#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# If this archive is complete, you will see the following message at the end:
#		"End of shell archive."
#
# Contents:
#   README.text test.text tkText-1.3.patch tkText.c
#
# Wrapped by mgc@cray.com on Mon Nov 25 10:29:57 1991
#
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f README.text -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"README.text\"
else
echo shar: Extracting \"README.text\" \(927 characters\)
sed "s/^X//" >README.text <<'END_OF_README.text'
XNotes on text widget:
X
Xadded the following options to configure:
X
X	-height			requested height of the widget (in lines)
X	-scrollhcommand		updates horizontal scrollbar
X	-scrollvcommand		updates vertical scrollbar
X	-wrapmode		one of none, char, word or fixed.
X				fixed means wrap on characters based on the
X				user supplied width even if the width of the
X				window changes.
X
Xadded following widget commands:
X
X	viewline		takes a lines number as argument.
X				positions line at the top of the window.
X	edit			takes a variety of editing commands.
X				(incomplete)
X
Xother changes:
X
X	the @ option for mapping the pointer to a text position takes both
X	x and y coordinates as @x,y
X
Xknown problems:
X
X	there is one case where word wrapping doesn't work exactly right.
X
X	could reclaim some memory when lines are shortened.
X
X	the cursor isn't always on the right line.
X
X	selecting to the bottom of the window will pick up the next line.
END_OF_README.text
if test 927 -ne `wc -c <README.text`; then
    echo shar: \"README.text\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f test.text -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"test.text\"
else
echo shar: Extracting \"test.text\" \(2693 characters\)
sed "s/^X//" >test.text <<'END_OF_test.text'
Xproc bind.text args {
X    foreach w $args {
X	bind $w <Any-KeyPress> "catch {%W insert cursor %A}"
X	bind $w <Enter> {focus %W}
X	bind $w <backslash> {%W insert cursor "\\"}
X	bind $w <braceleft> {%W insert cursor "\{"}
X	bind $w <braceright> {%W insert cursor "\}"}
X	bind $w <bracketleft> {%W insert cursor "\["}
X	bind $w <bracketright> {%W insert cursor "\]"}
X	bind $w <quotedbl> {%W insert cursor "\""}
X	bind $w <space> {%W insert cursor " "}
X	bind $w <Return> {%W insert cursor "\n"}
X	bind $w <2> {%W insert cursor [selection get]}
X	bind $w <Delete> {%W edit dnc}
X	bind $w <BackSpace> {%W edit dpc}
X	bind $w <Control-h> {%W edit dpc}
X	bind $w <1> {%W cursor @%x,%y; focus %W; %W select from @%x,%y}
X	bind $w <B1-Motion> {%W select to @%x,%y}
X	bind $w <Shift-1> {%W select adjust @%x,%y}
X	bind $w <Shift-B1-Motion> {%W select to @%x,%y}
X	bind $w <3> {%W scan mark %x,%y}
X	bind $w <B3-Motion> {%W scan dragto %x,%y}
X	bind $w <Any-Left> {%W edit bc}
X	bind $w <Any-Right> {%W edit fc}
X	bind $w <Any-Up> {%W edit pl}
X	bind $w <Any-Down> {%W edit nl}
X	bind $w <Control-a> {%W edit bol}
X	bind $w <Control-b> {%W edit bc}
X	bind $w <Control-c> {}
X	bind $w <Control-d> {%W edit dnc}
X	bind $w <Control-e> {%W edit eol}
X	bind $w <Control-f> {%W edit fc}
X	bind $w <Control-g> {}
X	bind $w <Control-h> {}
X	bind $w <Control-i> {}
X	bind $w <Control-j> {}
X	bind $w <Control-k> {%W delete cursor eol}
X	bind $w <Control-l> {}
X	bind $w <Control-m> {}
X	bind $w <Control-n> {%W edit nl}
X	bind $w <Control-o> {}
X	bind $w <Control-p> {%W edit pl}
X	bind $w <Control-q> {}
X	bind $w <Control-r> {}
X	bind $w <Control-s> {}
X	bind $w <Control-t> {}
X	bind $w <Control-u> {}
X	bind $w <Control-v> {%W insert cursor [selection get]}
X	bind $w <Control-w> {}
X	bind $w <Control-x> {}
X	bind $w <Control-y> {}
X	bind $w <Control-z> {}
X    }
X}
X
Xproc bind.text.readonly args {
X    foreach w $args {
X	bind $w <1> {%W cursor @%x,%y; focus %W; %W select from @%x,%y}
X	bind $w <B1-Motion> {%W select to @%x,%y}
X	bind $w <Shift-1> {%W select adjust @%x,%y}
X	bind $w <Shift-B1-Motion> {%W select to @%x,%y}
X	bind $w <3> {%W scan mark %x,%y}
X	bind $w <B3-Motion> {%W scan dragto %x,%y}
X    }
X}
X
Xframe .frame
Xpack append . .frame {top expand}
X
Xscrollbar .frame.vscrollbar -command ".frame.text viewline" -relief sunken
Xscrollbar .hscrollbar -relief sunken -orient horizontal
X
Xtext .frame.text -height 25 -width 16 \
X	-scrollvcommand ".frame.vscrollbar set" \
X	-scrollhcommand ".hscrollbar set" 
X
Xpack append .frame \
X	.frame.vscrollbar {left expand filly} \
X	.frame.text {left expand}
X
Xpack append . \
X	.hscrollbar {top expand fillx}
X
Xupdate
X
X.frame.text configure -wrapmode word
X.frame.text insert 0 [exec cat tkText.c]
X
Xbind.text .frame.text
X
END_OF_test.text
if test 2693 -ne `wc -c <test.text`; then
    echo shar: \"test.text\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f tkText-1.3.patch -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"tkText-1.3.patch\"
else
echo shar: Extracting \"tkText-1.3.patch\" \(6622 characters\)
sed "s/^X//" >tkText-1.3.patch <<'END_OF_tkText-1.3.patch'
Xdiff -c tk-1.3/Makefile tk/Makefile
X*** tk-1.3/Makefile	Tue Nov  5 13:49:30 1991
X--- tk/Makefile	Thu Nov 21 16:27:32 1991
X***************
X*** 24,36 ****
X  TCL_DIR		= tcl
X  XLIB		= -lX11
X  CC		= cc
X! CFLAGS		= -I. -I$(TCL_DIR) -g -DTK_VERSION=\"1.3/\"
X  
X  LIBS = libtk.a $(TCL_DIR)/libtcl.a
X  
X  WIDGOBJS = tkButton.o tkEntry.o tkFrame.o tkListbox.o \
X  	tkMenu.o tkMenubutton.o tkMessage.o tkScale.o \
X! 	tkScrollbar.o
X  
X  OBJS = tk3d.o tkArgv.o tkAtom.o tkBind.o tkBitmap.o \
X          tkCmds.o tkColor.o tkConfig.o tkCursor.o tkError.o \
X--- 24,36 ----
X  TCL_DIR		= tcl
X  XLIB		= -lX11
X  CC		= cc
X! CFLAGS		= -I. -I$(TCL_DIR) -g -DTK_VERSION=\"1.3\"
X  
X  LIBS = libtk.a $(TCL_DIR)/libtcl.a
X  
X  WIDGOBJS = tkButton.o tkEntry.o tkFrame.o tkListbox.o \
X  	tkMenu.o tkMenubutton.o tkMessage.o tkScale.o \
X! 	tkScrollbar.o tkText.o
X  
X  OBJS = tk3d.o tkArgv.o tkAtom.o tkBind.o tkBitmap.o \
X          tkCmds.o tkColor.o tkConfig.o tkCursor.o tkError.o \
X***************
X*** 40,46 ****
X  
X  WIDGSRCS = tkButton.c tkEntry.c tkFrame.c tkListbox.c \
X  	tkMenu.c tkMenubutton.c tkMessage.c tkScale.c \
X! 	tkScrollbar.c
X  
X  SRCS = tk3d.c tkArgv.c tkAtom.c tkBind.c tkBitmap.c \
X          tkCmds.o tkColor.c tkConfig.c tkCursor.c tkError.c \
X--- 40,46 ----
X  
X  WIDGSRCS = tkButton.c tkEntry.c tkFrame.c tkListbox.c \
X  	tkMenu.c tkMenubutton.c tkMessage.c tkScale.c \
X! 	tkScrollbar.c tkText.c
X  
X  SRCS = tk3d.c tkArgv.c tkAtom.c tkBind.c tkBitmap.c \
X          tkCmds.o tkColor.c tkConfig.c tkCursor.c tkError.c \
XCommon subdirectories: tk-1.3/bitmaps and tk/bitmaps
Xdiff -c tk-1.3/default.h tk/default.h
X*** tk-1.3/default.h	Sun Oct  6 16:29:12 1991
X--- tk/default.h	Fri Nov  8 12:28:50 1991
X***************
X*** 235,239 ****
X--- 235,267 ----
X  #define DEF_SCROLLBAR_REPEAT_INTERVAL	"100"
X  #define DEF_SCROLLBAR_WIDTH		"15"
X  
X+ /*
X+  * Defaults for text:
X+  */
X+ 
X+ #define DEF_TEXT_BG_COLOR		BISQUE1
X+ #define DEF_TEXT_BG_MONO		WHITE
X+ #define DEF_TEXT_BORDER_WIDTH		"2"
X+ #define DEF_TEXT_CURSOR_BG		BLACK
X+ #define DEF_TEXT_CURSOR_BD_COLOR	"1"
X+ #define DEF_TEXT_CURSOR_BD_MONO		"0"
X+ #define DEF_TEXT_CURSOR_OFF_TIME	"300"
X+ #define DEF_TEXT_CURSOR_ON_TIME		"600"
X+ #define DEF_TEXT_CURSOR_WIDTH		"2"
X+ #define DEF_TEXT_FONT			"*-Helvetica-Medium-R-Normal-*-120-*"
X+ #define DEF_TEXT_FG			BLACK
X+ #define DEF_TEXT_HEIGHT			"1"
X+ #define DEF_TEXT_RELIEF			"flat"
X+ #define DEF_TEXT_SCROLL_H_COMMAND	((char *) NULL)
X+ #define DEF_TEXT_SCROLL_V_COMMAND	((char *) NULL)
X+ #define DEF_TEXT_SELECT_COLOR		LIGHTBLUE2
X+ #define DEF_TEXT_SELECT_MONO		BLACK
X+ #define DEF_TEXT_SELECT_BD_COLOR	"1"
X+ #define DEF_TEXT_SELECT_BD_MONO		"0"
X+ #define DEF_TEXT_SELECT_FG_COLOR	BLACK
X+ #define DEF_TEXT_SELECT_FG_MONO		WHITE
X+ #define DEF_TEXT_WRAP			"word"
X+ #define DEF_TEXT_WIDTH			"20"
X+ 
X  
X  #endif /* _DEFAULT */
XCommon subdirectories: tk-1.3/demos and tk/demos
XCommon subdirectories: tk-1.3/doc and tk/doc
XCommon subdirectories: tk-1.3/tcl and tk/tcl
XOnly in tk: test.text
Xdiff -c tk-1.3/tk.h tk/tk.h
X*** tk-1.3/tk.h	Sun Oct 27 19:13:27 1991
X--- tk/tk.h	Mon Nov 11 15:37:40 1991
X***************
X*** 127,134 ****
X  #define TK_CONFIG_BORDER	9
X  #define TK_CONFIG_RELIEF	10
X  #define TK_CONFIG_CURSOR	11
X! #define TK_CONFIG_SYNONYM	12
X! #define TK_CONFIG_END		13
X  
X  /*
X   * Macro to use to fill in "offset" fields of Tk_ConfigInfos.
X--- 127,135 ----
X  #define TK_CONFIG_BORDER	9
X  #define TK_CONFIG_RELIEF	10
X  #define TK_CONFIG_CURSOR	11
X! #define TK_CONFIG_WRAP		12
X! #define TK_CONFIG_SYNONYM	13
X! #define TK_CONFIG_END		14
X  
X  /*
X   * Macro to use to fill in "offset" fields of Tk_ConfigInfos.
X***************
X*** 177,182 ****
X--- 178,192 ----
X  #define TK_RELIEF_SUNKEN	4
X  
X  /*
X+  * Wrap mode values returned by Tk_GetWrapMode:
X+  */
X+ 
X+ #define TK_WRAP_NONE		1
X+ #define TK_WRAP_CHAR		2
X+ #define TK_WRAP_WORD		3
X+ #define TK_WRAP_FIXED		4
X+ 
X+ /*
X   * Special EnterNotify/LeaveNotify "mode" for use in events
X   * generated by tkShare.c.  Pick a high enough value that it's
X   * unlikely to conflict with existing values (like NotifyNormal)
X***************
X*** 449,454 ****
X--- 459,467 ----
X  extern int		Tk_GetSelection _ANSI_ARGS_((Tcl_Interp *interp,
X  			    Tk_Window tkwin, Atom target, Tk_GetSelProc *proc,
X  			    ClientData clientData));
X+ extern int		Tk_GetWrapMode _ANSI_ARGS_((Tcl_Interp *interp,
X+ 						    char *name,
X+ 			    int *wrapPtr));
X  extern Tk_Uid		Tk_GetUid _ANSI_ARGS_((char *string));
X  extern void		Tk_HandleEvent _ANSI_ARGS_((XEvent *eventPtr));
X  extern Atom		Tk_InternAtom _ANSI_ARGS_((Tk_Window tkwin,
X***************
X*** 470,475 ****
X--- 483,489 ----
X  extern char *		Tk_NameOfFontStruct _ANSI_ARGS_((
X  			    XFontStruct *fontStructPtr));
X  extern char *		Tk_NameOfRelief _ANSI_ARGS_((int relief));
X+ extern char *		Tk_NameOfWrapMode _ANSI_ARGS_((int wrap));
X  extern Tk_Window	Tk_NameToWindow _ANSI_ARGS_((Tcl_Interp *interp,
X  			    char *pathName, Tk_Window tkwin));
X  extern void		Tk_OwnSelection _ANSI_ARGS_((Tk_Window tkwin,
X***************
X*** 552,557 ****
X--- 566,573 ----
X  extern int		Tk_SelectionCmd _ANSI_ARGS_((ClientData clientData,
X  			    Tcl_Interp *interp, int argc, char **argv));
X  extern int		Tk_SendCmd _ANSI_ARGS_((ClientData clientData,
X+ 			    Tcl_Interp *interp, int argc, char **argv));
X+ extern int		Tk_TextCmd _ANSI_ARGS_((ClientData clientData,
X  			    Tcl_Interp *interp, int argc, char **argv));
X  extern int		Tk_UpdateCmd _ANSI_ARGS_((ClientData clientData,
X  			    Tcl_Interp *interp, int argc, char **argv));
Xdiff -c tk-1.3/tkConfig.c tk/tkConfig.c
X*** tk-1.3/tkConfig.c	Thu Oct 31 13:49:22 1991
X--- tk/tkConfig.c	Fri Nov  8 12:29:08 1991
X***************
X*** 459,464 ****
X--- 459,469 ----
X  		    return TCL_ERROR;
X  		}
X  		break;
X+ 	    case TK_CONFIG_WRAP:
X+ 		if (Tk_GetWrapMode(interp, uid, (int *) ptr) != TCL_OK) {
X+ 		    return TCL_ERROR;
X+ 		}
X+ 		break;
X  	    case TK_CONFIG_CURSOR: {
X  		Cursor new, old;
X  
X***************
X*** 678,683 ****
X--- 683,691 ----
X  	}
X  	case TK_CONFIG_RELIEF:
X  	    argv[4] = Tk_NameOfRelief(*((int *) ptr));
X+ 	    break;
X+ 	case TK_CONFIG_WRAP:
X+ 	    argv[4] = Tk_NameOfWrapMode(*((int *) ptr));
X  	    break;
X  	case TK_CONFIG_CURSOR: {
X  	    Cursor cursor = *((Cursor *) ptr);
XOnly in tk: tkText.c
Xdiff -c tk-1.3/tkWindow.c tk/tkWindow.c
X*** tk-1.3/tkWindow.c	Sun Nov  3 14:07:39 1991
X--- tk/tkWindow.c	Fri Nov  8 12:29:12 1991
X***************
X*** 119,124 ****
X--- 119,125 ----
X      {"radiobutton",	Tk_ButtonCmd},
X      {"scale",		Tk_ScaleCmd},
X      {"scrollbar",	Tk_ScrollbarCmd},
X+     {"text",		Tk_TextCmd},
X      {"toplevel",	Tk_FrameCmd},
X      {(char *) NULL,	(int (*)()) NULL}
X  };
END_OF_tkText-1.3.patch
if test 6622 -ne `wc -c <tkText-1.3.patch`; then
    echo shar: \"tkText-1.3.patch\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f tkText.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"tkText.c\"
else
echo shar: Extracting \"tkText.c\" \(85975 characters\)
sed "s/^X//" >tkText.c <<'END_OF_tkText.c'
X/* 
X * tkText.c --
X *
X *	This module implements Text widgets for the Tk
X *	toolkit.  A Text widget displays a string and allows
X *	the string to be edited.
X *
X * Copyright 1990 Regents of the University of California.
X * Permission to use, copy, modify, and distribute this
X * software and its documentation for any purpose and without
X * fee is hereby granted, provided that the above copyright
X * notice appear in all copies.  The University of California
X * makes no representations about the suitability of this
X * software for any purpose.  It is provided "as is" without
X * express or implied warranty.
X */
X
X#ifndef lint
Xstatic char rcsid[] = "$Header: /user5/ouster/wish/RCS/tkText.c,v 1.17 91/04/05 17:37:41 ouster Exp $ SPRITE (Berkeley)";
X#endif not lint
X
X#include "default.h"
X#include "tkConfig.h"
X#include "tkInt.h"
X
X/*
X * A data structure of the following type is kept for each Text
X * widget managed by this file:
X */
X
Xtypedef struct {
X    int	total, window, first, last;
X} ScrollValues;
X
Xtypedef struct Line {
X    struct Line	*next;
X    struct Line	*prev;
X    int			flags;		/* see below */
X    int			index;		/* offset into text */
X    int			num;		/* line number in text */
X    int			length;		/* length of this line */
X    int			width;		/* width in pixels */
X    int			size;		/* the allocated length of this string.
X					 * Not necessarily equal to length */
X    char		*mem;
X    char		*text;		/* the string itself */
X} Line;
X
X#define TEXT_LINE_DIRTY 1
X
Xtypedef struct {
X    Tk_Window tkwin;		/* Window that embodies the Text. NULL
X				 * means that the window has been destroyed
X				 * but the data structures haven't yet been
X				 * cleaned up.*/
X    Tcl_Interp *interp;		/* Interpreter associated with Text. */
X    int numChars;		/* Number of non-NULL characters in
X				 * string (may be 0). */
X    int	numLines;
X    int displayedLines;
X    Line *firstLine;		/* Pointer to storage for text */
X    Line *lastLine;
X    Line *topLine;
X
X    /*
X     * Information used when displaying widget:
X     */
X
X    Tk_3DBorder normalBorder;	/* Used for drawing border around whole
X				 * window, plus used for background. */
X    int borderWidth;		/* Width of 3-D border around window. */
X    int relief;			/* 3-D effect: TK_RELIEF_RAISED, etc. */
X    XFontStruct *fontPtr;	/* Information about text font, or NULL. */
X    XColor *fgColorPtr;		/* Text color in normal mode. */
X    GC textGC;			/* For drawing normal text. */
X    Tk_3DBorder selBorder;	/* Border and background for selected
X				 * characters. */
X    int selBorderWidth;		/* Width of border around selection. */
X    XColor *selFgColorPtr;	/* Foreground color for selected text. */
X    GC selTextGC;		/* For drawing selected text. */
X    Tk_3DBorder cursorBorder;	/* Used to draw vertical bar for insertion
X				 * cursor. */
X    int cursorWidth;		/* Total width of insert cursor. */
X    int cursorBorderWidth;	/* Width of 3-D border around insert cursor. */
X    int cursorOnTime;		/* Number of milliseconds cursor should spend
X				 * in "on" state for each blink. */
X    int cursorOffTime;		/* Number of milliseconds cursor should spend
X				 * in "off" state for each blink. */
X    Tk_TimerToken cursorBlinkHandler;
X				/* Timer handler used to blink cursor on and
X				 * off. */
X    int avgWidth;		/* Width of average character. */
X    int prefWidth;		/* Desired width of window, measured in
X				 * average characters. */
X    int prefHeight;		/* Desired height of window, measured in
X				 * average characters. */
X    int offset;			/* 0 if window is flat, or borderWidth if
X				 * raised or sunken. */
X    int leftIndex;		/* Index of left-most character visible in
X				 * window. */
X    int cursorPos;		/* Index of character before which next
X				 * typed character will be inserted. */
X    Line *cursorLine;
X    int wrapMode;
X    Pixmap pm;			/* Used to do redisplays off-screen, then
X				 * blast on-screen for minimal flashing.
X				 * None means not allocated yet. */
X    Display *pmDisplay;		/* Display associated with pm;  needed to
X				 * delete it. */
X    int pmWidth, pmHeight;	/* Dimensions of pm. */
X
X    /*
X     * Information about what's selected, if any.
X     */
X
X    int selectFirst;		/* Index of first selected character (-1 means
X				 * nothing selected. */
X    int selectLast;		/* Index of last selected character (-1 means
X				 * nothing selected. */
X    int selectAnchor;		/* Fixed end of selection (i.e. "select to"
X				 * operation will use this as one end of the
X				     * selection). */
X
X    /*
X     * Information for scanning:
X     */
X
X    int scanMarkX;		/* X-position at which scan started (e.g.
X				 * button was pressed here). */
X    int scanMarkIndex;		/* Index of character that was at left of
X				 * window when scan started. */
X
X    /*
X     * Miscellaneous information:
X     */
X
X    Tk_Uid scrollHCmd;		/* Command prefix for communicating with
X				 * scrollbar(s).  NULL means no command
X				 * to issue. */
X    ScrollValues lastScrollH;
X    Tk_Uid scrollVCmd;		/* Command prefix for communicating with
X				 * scrollbar(s).  NULL means no command
X				 * to issue. */
X    ScrollValues lastScrollV;
X    int flags;			/* Miscellaneous flags;  see below for
X				 * definitions. */
X} Text;
X
X/*
X * Assigned bits of "flags" fields of Text structures, and what those
X * bits mean:
X *
X * REDRAW_PENDING:		Non-zero means a DoWhenIdle handler has
X *				already been queued to redisplay the Text.
X * CLEAR_NEEDED:		Non-zero means that the Text's window
X *				should be cleared during redraw (this is
X *				needed when things change like the size of
X *				the text).
X * BORDER_NEEDED:		Non-zero means 3-D border must be redrawn
X *				around window during redisplay.  Normally
X *				only text portion needs to be redrawn.
X *				CLEAR_NEEDED implies BORDER_NEEDED.
X * CURSOR_ON:			Non-zero means cursor is displayed at
X *				present.  0 means it isn't displayed.
X * GOT_FOCUS:			Non-zero means this window has the input
X *				focus.
X * REFRESH_NEEDED:		Non-zero means that the Text's window
X *				should be redrawn whether text lines have
X *				changed or not.  Needed when scrolling or
X *				inserting or deleting lines.
X */
X
X#define REDRAW_PENDING		1
X#define CLEAR_NEEDED		2
X#define BORDER_NEEDED		4
X#define CURSOR_ON		8
X#define GOT_FOCUS		16
X#define REFRESH_NEEDED		32
X
X/*
X * Information used for argv parsing.
X */
X
Xstatic Tk_ConfigSpec configSpecs[] = {
X    {TK_CONFIG_BORDER, "-background", "background", "Background",
X	DEF_TEXT_BG_COLOR, Tk_Offset(Text, normalBorder),
X	TK_CONFIG_COLOR_ONLY},
X    {TK_CONFIG_BORDER, "-background", "background", "Background",
X	DEF_TEXT_BG_MONO, Tk_Offset(Text, normalBorder),
X	TK_CONFIG_MONO_ONLY},
X    {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
X	(char *) NULL, 0, 0},
X    {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
X	(char *) NULL, 0, 0},
X    {TK_CONFIG_INT, "-borderwidth", "borderWidth", "BorderWidth",
X	DEF_TEXT_BORDER_WIDTH, Tk_Offset(Text, borderWidth), 0},
X    {TK_CONFIG_BORDER, "-cursorbackground", "cursorBackground", "Foreground",
X	DEF_TEXT_CURSOR_BG, Tk_Offset(Text, cursorBorder), 0},
X    {TK_CONFIG_INT, "-cursorborderwidth", "cursorBorderWidth", "BorderWidth",
X	DEF_TEXT_CURSOR_BD_COLOR, Tk_Offset(Text, cursorBorderWidth),
X	TK_CONFIG_COLOR_ONLY},
X    {TK_CONFIG_INT, "-cursorborderwidth", "cursorBorderWidth", "BorderWidth",
X	DEF_TEXT_CURSOR_BD_MONO, Tk_Offset(Text, cursorBorderWidth),
X	TK_CONFIG_MONO_ONLY},
X    {TK_CONFIG_INT, "-cursorofftime", "cursorOffTime", "OffTime",
X	DEF_TEXT_CURSOR_OFF_TIME, Tk_Offset(Text, cursorOffTime), 0},
X    {TK_CONFIG_INT, "-cursorontime", "cursorOnTime", "OnTime",
X	DEF_TEXT_CURSOR_ON_TIME, Tk_Offset(Text, cursorOnTime), 0},
X    {TK_CONFIG_INT, "-cursorwidth", "cursorWidth", "CursorWidth",
X	DEF_TEXT_CURSOR_WIDTH, Tk_Offset(Text, cursorWidth), 0},
X    {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
X	(char *) NULL, 0, 0},
X    {TK_CONFIG_FONT, "-font", "font", "Font",
X	DEF_TEXT_FONT, Tk_Offset(Text, fontPtr), 0},
X    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
X	DEF_TEXT_FG, Tk_Offset(Text, fgColorPtr), 0},
X    {TK_CONFIG_INT, "-height", "height", "Height",
X	DEF_TEXT_HEIGHT, Tk_Offset(Text, prefHeight), 0},
X    {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
X	DEF_TEXT_RELIEF, Tk_Offset(Text, relief), 0},
X    {TK_CONFIG_UID, "-scrollhcommand", "scrollHCommand", "ScrollHCommand",
X	DEF_TEXT_SCROLL_H_COMMAND, Tk_Offset(Text, scrollHCmd), 0},
X    {TK_CONFIG_UID, "-scrollvcommand", "scrollVCommand", "ScrollVCommand",
X	DEF_TEXT_SCROLL_V_COMMAND, Tk_Offset(Text, scrollVCmd), 0},
X    {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
X	DEF_TEXT_SELECT_COLOR, Tk_Offset(Text, selBorder),
X	TK_CONFIG_COLOR_ONLY},
X    {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
X	DEF_TEXT_SELECT_MONO, Tk_Offset(Text, selBorder),
X	TK_CONFIG_MONO_ONLY},
X    {TK_CONFIG_INT, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
X	DEF_TEXT_SELECT_BD_COLOR, Tk_Offset(Text, selBorderWidth),
X	TK_CONFIG_COLOR_ONLY},
X    {TK_CONFIG_INT, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
X	DEF_TEXT_SELECT_BD_MONO, Tk_Offset(Text, selBorderWidth),
X	TK_CONFIG_MONO_ONLY},
X    {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
X	DEF_TEXT_SELECT_FG_COLOR, Tk_Offset(Text, selFgColorPtr),
X	TK_CONFIG_COLOR_ONLY},
X    {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
X	DEF_TEXT_SELECT_FG_MONO, Tk_Offset(Text, selFgColorPtr),
X	TK_CONFIG_MONO_ONLY},
X    {TK_CONFIG_INT, "-width", "width", "Width",
X	DEF_TEXT_WIDTH, Tk_Offset(Text, prefWidth), 0},
X    {TK_CONFIG_WRAP, "-wrapmode", "wrapMode", "WrapMode",
X	DEF_TEXT_WRAP, Tk_Offset(Text, wrapMode), 0},
X    {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
X	(char *) NULL, 0, 0}
X};
X
X/*
X * Flags for GetTextIndex procedure:
X */
X
X#define ZERO_OK			1
X#define LAST_PLUS_ONE_OK	2
X
X/*
X * Forward declarations for procedures defined later in this file:
X */
X
Xstatic int		ConfigureText _ANSI_ARGS_((Tcl_Interp *interp,
X			    Text *TextPtr, int argc, char **argv,
X			    int flags));
Xstatic void		DeleteText _ANSI_ARGS_((Text *TextPtr, int index,
X			    int count));
Xstatic void		DestroyText _ANSI_ARGS_((ClientData clientData));
Xstatic void		DisplayText _ANSI_ARGS_((ClientData clientData));
Xstatic int		GetTextIndex _ANSI_ARGS_((Tcl_Interp *interp,
X			    Text *TextPtr, char *string, int *indexPtr));
Xstatic void		InsertText _ANSI_ARGS_((Text *TextPtr, int index,
X			    char *string));
Xstatic void		TextBlinkProc _ANSI_ARGS_((ClientData clientData));
Xstatic void		TextEventProc _ANSI_ARGS_((ClientData clientData,
X			    XEvent *eventPtr));
Xstatic void		TextFocusProc _ANSI_ARGS_ ((ClientData clientData,
X			    int gotFocus));
Xstatic int		TextFetchSelection _ANSI_ARGS_((ClientData clientData,
X			    int offset, char *buffer, int maxBytes));
Xstatic void		TextLostSelection _ANSI_ARGS_((ClientData clientData));
Xstatic void		EventuallyRedraw _ANSI_ARGS_((Text *TextPtr));
Xstatic void		TextScanTo _ANSI_ARGS_((Text *TextPtr, int y));
Xstatic void		TextSelectTo _ANSI_ARGS_((Text *TextPtr, int index));
Xstatic void		TextUpdateHScrollbar _ANSI_ARGS_((Text *TextPtr));
Xstatic void		TextUpdateVScrollbar _ANSI_ARGS_((Text *TextPtr));
Xstatic int		TextWidgetCmd _ANSI_ARGS_((ClientData clientData,
X			    Tcl_Interp *interp, int argc, char **argv));
Xstatic void		EnsureCursorVisible();
Xstatic Line *		FindLine _ANSI_ARGS_ ((Text *TextPtr, int num));
Xstatic Line *		FindChar _ANSI_ARGS_ ((Text *TextPtr, int index));
Xstatic char *		GetText _ANSI_ARGS_((Text *TextPtr, int index,
X			    int count));
Xstatic void		FixLines _ANSI_ARGS_((Text *TextPtr, Line *start,
X					int min));
Xint
XTk_GetWrapMode(interp, name, wrapPtr)
X    Tcl_Interp *interp;         /* For error messages. */
X    char *name;                 /* Name of a relief type. */
X    int *wrapPtr;               /* Where to store converted wrap mode. */
X{
X    char c;
X    int length;
X
X    c = name[0];
X    length = strlen(name);
X    if ((c == 'n') && (strncmp(name, "none", length) == 0)) {
X        *wrapPtr = TK_WRAP_NONE;
X    } else if ((c == 'c') && (strncmp(name, "char", length) == 0)) {
X        *wrapPtr = TK_WRAP_CHAR;
X    } else if ((c == 'w') && (strncmp(name, "word", length) == 0)) {
X        *wrapPtr = TK_WRAP_WORD;
X    } else if ((c == 'f') && (strncmp(name, "fixed", length) == 0)) {
X	*wrapPtr = TK_WRAP_FIXED;
X    } else {
X        sprintf(interp->result, "bad wrap mode \"%.50s\":  must be %s",
X                name, "none, char, word or fixed");
X        return TCL_ERROR;
X    }
X    return TCL_OK;
X}
X
Xchar *
XTk_NameOfWrapMode(wrap)
X    int wrap;
X
X{
X    if (wrap == TK_WRAP_NONE) {
X        return "flat";
X    } else if (wrap == TK_WRAP_CHAR) {
X        return "sunken";
X    } else if (wrap == TK_WRAP_WORD) {
X        return "raised";
X    } else if (wrap == TK_WRAP_FIXED) {
X	return "fixed";
X    } else {
X        return "unknown relief";
X    }
X}
X
X/*
X *--------------------------------------------------------------
X *
X * Tk_TextCmd --
X *
X *	This procedure is invoked to process the "Text" Tcl
X *	command.  See the user documentation for details on what
X *	it does.
X *
X * Results:
X *	A standard Tcl result.
X *
X * Side effects:
X *	See the user documentation.
X *
X *--------------------------------------------------------------
X */
X
Xint
XTk_TextCmd(clientData, interp, argc, argv)
X    ClientData clientData;	/* Main window associated with
X				 * interpreter. */
X    Tcl_Interp *interp;		/* Current interpreter. */
X    int argc;			/* Number of arguments. */
X    char **argv;		/* Argument strings. */
X{
X    Tk_Window tkwin = (Tk_Window) clientData;
X    register Text *TextPtr;
X    Tk_Window new;
X    static ScrollValues scrollInitial = { -1, -1, -1, -1 };
X
X    if (argc < 2) {
X	Tcl_AppendResult(interp, "wrong # args:  should be \"",
X		argv[0], " pathName ?options?\"", (char *) NULL);
X	return TCL_ERROR;
X    }
X
X    new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
X    if (new == NULL) {
X	return TCL_ERROR;
X    }
X
X    /*
X     * Initialize the fields of the structure that won't be initialized
X     * by ConfigureText, or that ConfigureText requires to be
X     * initialized already (e.g. resource pointers).
X     */
X
X    TextPtr = (Text *) ckalloc(sizeof(Text));
X    TextPtr->tkwin = new;
X    TextPtr->interp = interp;
X    TextPtr->numChars = 0;
X    TextPtr->numLines = 0;
X    TextPtr->displayedLines = 0;
X
X    TextPtr->firstLine = (Line *)ckalloc(sizeof(Line));
X    TextPtr->firstLine->next = TextPtr->firstLine->prev = NULL;
X    TextPtr->firstLine->flags = 0;
X    TextPtr->firstLine->index = 0;
X    TextPtr->firstLine->length = 0;
X    TextPtr->firstLine->width = 0;
X    TextPtr->firstLine->size = 0;
X    TextPtr->firstLine->mem = TextPtr->firstLine->text = NULL;
X
X    TextPtr->lastLine = TextPtr->topLine = TextPtr->firstLine;
X
X    TextPtr->normalBorder = NULL;
X    TextPtr->fontPtr = NULL;
X    TextPtr->fgColorPtr = NULL;
X    TextPtr->textGC = None;
X    TextPtr->selBorder = NULL;
X    TextPtr->selFgColorPtr = NULL;
X    TextPtr->selTextGC = NULL;
X    TextPtr->cursorBorder = NULL;
X    TextPtr->cursorBlinkHandler = (Tk_TimerToken) NULL;
X    TextPtr->leftIndex = 0;
X    TextPtr->cursorPos = 0;
X    TextPtr->cursorLine = TextPtr->firstLine;
X    TextPtr->wrapMode = TK_WRAP_NONE;
X    TextPtr->pm = None;
X    TextPtr->pmDisplay = NULL;
X    TextPtr->pmWidth = -1;
X    TextPtr->pmHeight = -1;
X    TextPtr->selectFirst = -1;
X    TextPtr->selectLast = -1;
X    TextPtr->selectAnchor = 0;
X    TextPtr->scanMarkX = 0;
X    TextPtr->scrollHCmd = NULL;
X    TextPtr->lastScrollH = scrollInitial;
X    TextPtr->scrollVCmd = NULL;
X    TextPtr->lastScrollV = scrollInitial;
X    TextPtr->flags = 0;
X
X    Tk_SetClass(TextPtr->tkwin, "Text");
X    Tk_CreateEventHandler(TextPtr->tkwin, ExposureMask|StructureNotifyMask,
X	    TextEventProc, (ClientData) TextPtr);
X    Tk_CreateSelHandler(TextPtr->tkwin, XA_STRING, TextFetchSelection,
X	    (ClientData) TextPtr, XA_STRING);
X    Tcl_CreateCommand(interp, Tk_PathName(TextPtr->tkwin), TextWidgetCmd,
X	    (ClientData) TextPtr, (void (*)()) NULL);
X    if (ConfigureText(interp, TextPtr, argc-2, argv+2, 0) != TCL_OK) {
X	goto error;
X    }
X    Tk_CreateFocusHandler(TextPtr->tkwin, TextFocusProc,
X	    (ClientData) TextPtr);
X
X    interp->result = Tk_PathName(TextPtr->tkwin);
X    return TCL_OK;
X
X    error:
X    Tk_DestroyWindow(TextPtr->tkwin);
X    return TCL_ERROR;
X}
X
X/*
X *--------------------------------------------------------------
X *
X * TextWidgetCmd --
X *
X *	This procedure is invoked to process the Tcl command
X *	that corresponds to a widget managed by this module.
X *	See the user documentation for details on what it does.
X *
X * Results:
X *	A standard Tcl result.
X *
X * Side effects:
X *	See the user documentation.
X *
X *--------------------------------------------------------------
X */
X
Xstatic int
XTextWidgetCmd(clientData, interp, argc, argv)
X    ClientData clientData;		/* Information about Text widget. */
X    Tcl_Interp *interp;			/* Current interpreter. */
X    int argc;				/* Number of arguments. */
X    char **argv;			/* Argument strings. */
X{
X    register Text *TextPtr = (Text *) clientData;
X    int result = TCL_OK;
X    int length;
X    char c;
X
X    if (argc < 2) {
X	Tcl_AppendResult(interp, "wrong # args: should be \"",
X		argv[0], " option ?arg arg ...?\"", (char *) NULL);
X	return TCL_ERROR;
X    }
X    Tk_Preserve((ClientData) TextPtr);
X    c = argv[1][0];
X    length = strlen(argv[1]);
X    if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
X	    && (length >= 2)) {
X	if (argc == 2) {
X	    result = Tk_ConfigureInfo(interp, TextPtr->tkwin, configSpecs,
X		    (char *) TextPtr, (char *) NULL, 0);
X	} else if (argc == 3) {
X	    result = Tk_ConfigureInfo(interp, TextPtr->tkwin, configSpecs,
X		    (char *) TextPtr, argv[2], 0);
X	} else {
X	    result = ConfigureText(interp, TextPtr, argc-2, argv+2,
X		    TK_CONFIG_ARGV_ONLY);
X	}
X    } else if ((c == 'c') && (strncmp(argv[1], "cursor", length) == 0)
X	    && (length >= 2)) {
X
X	if (argc != 3) {
X	    Tcl_AppendResult(interp, "wrong # args: should be \"",
X		    argv[0], " cursor pos\"",
X		    (char *) NULL);
X	    goto error;
X	}
X	if (GetTextIndex(interp, TextPtr, argv[2], &TextPtr->cursorPos)
X		!= TCL_OK) {
X	    goto error;
X	}
X	TextPtr->cursorLine->flags |= TEXT_LINE_DIRTY;
X	TextPtr->cursorLine = FindChar(TextPtr, TextPtr->cursorPos);
X	EnsureCursorVisible(TextPtr);
X	TextUpdateVScrollbar(TextPtr);
X	EventuallyRedraw(TextPtr);
X    } else if ((c == 'e') && (strncmp(argv[1], "edit", length) == 0)) {
X	if (argc > 2)
X	    result = TextEditCmd(TextPtr, interp, argc, argv);
X	else {
X	    Tcl_AppendResult(interp, "wrong # args: should be \"",
X		    argv[0], " edit option [arg arg ...]\"",
X		    (char *) NULL);
X	    goto error;
X	}
X    } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {
X	int first, last;
X
X	if ((argc < 3) || (argc > 4)) {
X	    Tcl_AppendResult(interp, "wrong # args: should be \"",
X		    argv[0], " delete firstIndex ?lastIndex?\"",
X		    (char *) NULL);
X	    goto error;
X	}
X	if (GetTextIndex(interp, TextPtr, argv[2], &first) != TCL_OK) {
X	    goto error;
X	}
X	if (argc == 3) {
X	    last = first;
X	} else {
X	    if (GetTextIndex(interp, TextPtr, argv[3], &last) != TCL_OK) {
X		goto error;
X	    }
X	}
X	if (last >= first) {
X	    DeleteText(TextPtr, first, last+1-first);
X	}
X    } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
X	int first = 0;
X	int last = TextPtr->numChars - 1;
X
X	if ((argc < 2) || (argc > 4)) {
X	    Tcl_AppendResult(interp, "wrong # args: should be \"",
X		    argv[0], " get [firstIndex [lastIndex]]\"",
X		    (char *) NULL);
X	    goto error;
X	}
X	if (argc > 2) {
X	    if (GetTextIndex(interp, TextPtr, argv[2], &first) != TCL_OK) {
X		goto error;
X	    }
X	    last = first;
X	}
X	if (argc > 3) {
X	    if (GetTextIndex(interp, TextPtr, argv[3], &last) != TCL_OK) {
X		goto error;
X	    }
X	}
X	if (last >= first) {
X	    char *text;
X
X	    text = GetText(TextPtr, first, last+1-first);
X
X	    if (text != NULL) {
X		Tcl_AppendResult(interp, text, NULL);
X		ckfree(text);
X	    }
X	}
X    } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
X	    && (length >= 2)) {
X	int index;
X
X	if (argc != 3) {
X	    Tcl_AppendResult(interp, "wrong # args: should be \"",
X		    argv[0], " index string\"", (char *) NULL);
X	    goto error;
X	}
X	if (GetTextIndex(interp, TextPtr, argv[2], &index) != TCL_OK) {
X	    goto error;
X	}
X	sprintf(interp->result, "%d", index);
X    } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
X	    && (length >= 2)) {
X	int index;
X
X	if (argc != 4) {
X	    Tcl_AppendResult(interp, "wrong # args: should be \"",
X		    argv[0], " insert index text\"",
X		    (char *) NULL);
X	    goto error;
X	}
X	if (GetTextIndex(interp, TextPtr, argv[2], &index) != TCL_OK) {
X	    goto error;
X	}
X	InsertText(TextPtr, index, argv[3]);
X    } else if ((c == 's') && (length >= 2)
X	    && (strncmp(argv[1], "scan", length) == 0)) {
X	int x;
X
X	if (argc != 4) {
X	    Tcl_AppendResult(interp, "wrong # args: should be \"",
X		    argv[0], " scan mark|dragto x\"", (char *) NULL);
X	    goto error;
X	}
X	if (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) {
X	    goto error;
X	}
X	if ((argv[2][0] == 'm')
X		&& (strncmp(argv[2], "mark", strlen(argv[2])) == 0)) {
X	    TextPtr->scanMarkX = x;
X	    TextPtr->scanMarkIndex = TextPtr->leftIndex;
X	} else if ((argv[2][0] == 'd')
X		&& (strncmp(argv[2], "dragto", strlen(argv[2])) == 0)) {
X	    TextScanTo(TextPtr, x);
X	} else {
X	    Tcl_AppendResult(interp, "bad scan option \"", argv[2],
X		    "\":  must be mark or dragto", (char *) NULL);
X	    goto error;
X	}
X    } else if ((c == 's') && (length >= 2)
X	    && (strncmp(argv[1], "select", length) == 0)) {
X	int index;
X
X	if (argc < 3) {
X	    Tcl_AppendResult(interp, "too few args: should be \"",
X		    argv[0], " select option ?index?\"", (char *) NULL);
X	    goto error;
X	}
X	length = strlen(argv[2]);
X	c = argv[2][0];
X	if ((c == 'c') && (argv[2] != NULL)
X		&& (strncmp(argv[2], "clear", length) == 0)) {
X	    if (argc != 3) {
X		Tcl_AppendResult(interp, "wrong # args: should be \"",
X			argv[0], " select clear\"", (char *) NULL);
X		goto error;
X	    }
X	    if (TextPtr->selectFirst != -1) {
X		TextPtr->selectFirst = TextPtr->selectLast = -1;
X		EventuallyRedraw(TextPtr);
X	    }
X	    goto error;
X	}
X	if (argc >= 4) {
X	    if (GetTextIndex(interp, TextPtr, argv[3], &index) != TCL_OK) {
X		goto error;
X	    }
X	}
X	if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
X	    if (argc != 4) {
X		Tcl_AppendResult(interp, "wrong # args: should be \"",
X			argv[0], " select adjust index\"",
X			(char *) NULL);
X		goto error;
X	    }
X	    if (TextPtr->selectFirst >= 0) {
X		if (index < (TextPtr->selectFirst + TextPtr->selectLast)/2) {
X		    TextPtr->selectAnchor = TextPtr->selectLast + 1;
X		} else {
X		    TextPtr->selectAnchor = TextPtr->selectFirst;
X		}
X	    }
X	    TextSelectTo(TextPtr, index);
X	} else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
X	    if (argc != 4) {
X		Tcl_AppendResult(interp, "wrong # args: should be \"",
X			argv[0], " select from index\"",
X			(char *) NULL);
X		goto error;
X	    }
X	    TextPtr->selectAnchor = index;
X	} else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
X	    if (argc != 4) {
X		Tcl_AppendResult(interp, "wrong # args: should be \"",
X			argv[0], " select to index\"",
X			(char *) NULL);
X		goto error;
X	    }
X	    TextSelectTo(TextPtr, index);
X	} else {
X	    Tcl_AppendResult(interp, "bad select option \"", argv[2],
X		    "\": must be adjust, clear, from, or to", (char *) NULL);
X	    goto error;
X	}
X    } else if (c == 'v') {
X	if (strncmp(argv[1], "view", length) == 0) {
X	    int index;
X
X	    if (argc != 3) {
X		Tcl_AppendResult(interp, "wrong # args: should be \"",
X				 argv[0], " view index\"", (char *) NULL);
X		goto error;
X	    }
X	    if (GetTextIndex(interp, TextPtr, argv[2], &index) != TCL_OK) {
X		goto error;
X	    }
X	    TextPtr->topLine = FindChar(TextPtr, index);
X	    EventuallyRedraw(TextPtr);
X	    TextUpdateVScrollbar(TextPtr);
X	} else if (strncmp(argv[1], "viewline", length) == 0) {
X	    Line *line;
X	    int num;
X
X	    if (argc != 3) {
X		Tcl_AppendResult(interp, "wrong # args: should be \"",
X				 argv[0], " viewline line\"", (char *) NULL);
X		goto error;
X	    }
X
X	    if (Tcl_GetInt(interp, argv[2], &num) != TCL_OK) {
X		goto error;
X	    }
X
X	    TextPtr->topLine = FindLine(TextPtr, num);
X
X	    TextPtr->flags |= REFRESH_NEEDED;
X	    EventuallyRedraw(TextPtr);
X	    TextUpdateVScrollbar(TextPtr);
X	} else
X	    goto error;
X    } else {
X	Tcl_AppendResult(interp, "bad option \"", argv[1],
X		"\": must be configure, cursor, delete, get, index, ",
X		"insert, scan, select, or view", (char *) NULL);
X	goto error;
X    }
X    Tk_Release((ClientData) TextPtr);
X    return result;
X
X    error:
X    Tk_Release((ClientData) TextPtr);
X    return TCL_ERROR;
X}
X
X
X/*
X *----------------------------------------------------------------------
X *
X * DestroyText --
X *
X *	This procedure is invoked by Tk_EventuallyFree or Tk_Release
X *	to clean up the internal structure of a Text at a safe time
X *	(when no-one is using it anymore).
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	Everything associated with the Text is freed up.
X *
X *----------------------------------------------------------------------
X */
X
Xstatic void
XDestroyText(clientData)
X    ClientData clientData;			/* Info about Text widget. */
X{
X    register Text *TextPtr = (Text *) clientData;
X    Line *line;
X
X    line = TextPtr->firstLine;
X    while (line != NULL) {
X	Line *victim = line;
X	line = victim->next;
X	if (victim->mem != NULL)
X	    ckfree(victim->mem);
X	ckfree(victim);
X    }
X    if (TextPtr->normalBorder != NULL) {
X	Tk_Free3DBorder(TextPtr->normalBorder);
X    }
X    if (TextPtr->fontPtr != NULL) {
X	Tk_FreeFontStruct(TextPtr->fontPtr);
X    }
X    if (TextPtr->fgColorPtr != NULL) {
X	Tk_FreeColor(TextPtr->fgColorPtr);
X    }
X    if (TextPtr->textGC != None) {
X	Tk_FreeGC(TextPtr->textGC);
X    }
X    if (TextPtr->selBorder != NULL) {
X	Tk_Free3DBorder(TextPtr->selBorder);
X    }
X    if (TextPtr->selFgColorPtr != NULL) {
X	Tk_FreeColor(TextPtr->selFgColorPtr);
X    }
X    if (TextPtr->selTextGC != None) {
X	Tk_FreeGC(TextPtr->selTextGC);
X    }
X    if (TextPtr->cursorBorder != NULL) {
X	Tk_Free3DBorder(TextPtr->cursorBorder);
X    }
X    Tk_DeleteTimerHandler(TextPtr->cursorBlinkHandler);
X    if (TextPtr->pm != None) {
X	XFreePixmap(TextPtr->pmDisplay, TextPtr->pm);
X    }
X    ckfree((char *) TextPtr);
X}
X
X/*
X *----------------------------------------------------------------------
X *
X * ConfigureText --
X *
X *	This procedure is called to process an argv/argc list, plus
X *	the Tk option database, in order to configure (or reconfigure)
X *	a Text widget.
X *
X * Results:
X *	The return value is a standard Tcl result.  If TCL_ERROR is
X *	returned, then interp->result contains an error message.
X *
X * Side effects:
X *	Configuration information, such as colors, border width,
X *	etc. get set for TextPtr;  old resources get freed,
X *	if there were any.
X *
X *----------------------------------------------------------------------
X */
X
Xstatic int
XConfigureText(interp, TextPtr, argc, argv, flags)
X    Tcl_Interp *interp;		/* Used for error reporting. */
X    register Text *TextPtr;	/* Information about widget;  may or may
X				 * not already have values for some fields. */
X    int argc;			/* Number of valid entries in argv. */
X    char **argv;		/* Arguments. */
X    int flags;			/* Flags to pass to Tk_ConfigureWidget. */
X{
X    XGCValues gcValues;
X    GC new;
X    int width, height, fontHeight;
X
X    if (Tk_ConfigureWidget(interp, TextPtr->tkwin, configSpecs,
X	    argc, argv, (char *) TextPtr, flags) != TCL_OK) {
X	return TCL_ERROR;
X    }
X
X    /*
X     * A few options need special processing, such as parsing the
X     * geometry and setting the background from a 3-D border.
X     */
X
X    Tk_SetBackgroundFromBorder(TextPtr->tkwin, TextPtr->normalBorder);
X
X    gcValues.foreground = TextPtr->fgColorPtr->pixel;
X    gcValues.font = TextPtr->fontPtr->fid;
X    new = Tk_GetGC(TextPtr->tkwin, GCForeground|GCFont, &gcValues);
X    if (TextPtr->textGC != None) {
X	Tk_FreeGC(TextPtr->textGC);
X    }
X    TextPtr->textGC = new;
X
X    gcValues.foreground = TextPtr->selFgColorPtr->pixel;
X    gcValues.font = TextPtr->fontPtr->fid;
X    new = Tk_GetGC(TextPtr->tkwin, GCForeground|GCFont, &gcValues);
X    if (TextPtr->selTextGC != None) {
X	Tk_FreeGC(TextPtr->selTextGC);
X    }
X    TextPtr->selTextGC = new;
X
X    if (TextPtr->cursorWidth > 2*TextPtr->fontPtr->min_bounds.width) {
X	TextPtr->cursorWidth = 2*TextPtr->fontPtr->min_bounds.width;
X    }
X    if (TextPtr->cursorBorderWidth > TextPtr->cursorWidth/2) {
X	TextPtr->cursorBorderWidth = TextPtr->cursorWidth/2;
X    }
X
X    /*
X     * Restart the cursor timing sequence in case the on-time or off-time
X     * just changed.
X     */
X
X    if (TextPtr->flags & GOT_FOCUS) {
X	TextFocusProc((ClientData) TextPtr, 1);
X    }
X
X    /*
X     * Register the desired geometry for the window, and arrange for
X     * the window to be redisplayed.
X     */
X
X    fontHeight = TextPtr->fontPtr->ascent + TextPtr->fontPtr->descent;
X    TextPtr->avgWidth = XTextWidth(TextPtr->fontPtr, "0m", 2)/2;
X    width = TextPtr->prefWidth*TextPtr->avgWidth + (15*fontHeight)/10;
X    height = fontHeight*TextPtr->prefHeight + 2*TextPtr->borderWidth + 2;
X    Tk_GeometryRequest(TextPtr->tkwin, width, height);
X    Tk_SetInternalBorder(TextPtr->tkwin, TextPtr->borderWidth);
X    if (TextPtr->relief != TK_RELIEF_FLAT) {
X	TextPtr->offset = TextPtr->borderWidth;
X    } else {
X	TextPtr->offset = 0;
X    }
X    FixLines(TextPtr, TextPtr->firstLine, TextPtr->numLines);
X    EventuallyRedraw(TextPtr);
X    TextPtr->flags |= CLEAR_NEEDED;
X    TextUpdateVScrollbar(TextPtr);
X    return TCL_OK;
X}
X
X
X/*
X *--------------------------------------------------------------
X *
X * TextEditCmd --
X *
X *	This procedure is invoked to process the an editing
X *	command for a text widget.
X *	See the user documentation for details on what it does.
X *
X * Results:
X *	A standard Tcl result.
X *
X * Side effects:
X *	See the user documentation.
X *
X *--------------------------------------------------------------
X */
X
Xstatic int
XTextEditCmd(TextPtr, interp, argc, argv)
X    Text *TextPtr;
X    Tcl_Interp *interp;			/* Current interpreter. */
X    int argc;				/* Number of arguments. */
X    char **argv;			/* Argument strings. */
X
X{
X    char *name = argv[0];
X    int result = TCL_OK;
X    int length;
X    char c;
X
X    argv += 2;
X    argc -= 2;
X
X    c = argv[0][0];
X    length = strlen(argv[0]);
X
X    if (c == 'b') {
X	if (strncmp(argv[0], "bkw", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "backword-kill-word", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "bc", length) == 0 ||
X		   strncmp(argv[0], "backward-character", length) == 0) {
X	    if (TextPtr->cursorPos > 0) {
X		if (TextPtr->cursorPos == TextPtr->cursorLine->index) {
X		    TextPtr->cursorLine->flags |= TEXT_LINE_DIRTY;
X		    TextPtr->cursorLine = TextPtr->cursorLine->prev;
X		    if (TextPtr->cursorLine->text[TextPtr->cursorLine->length
X						  - 1] == '\n')
X			TextPtr->cursorPos--;
X		} else
X		    TextPtr->cursorPos--;
X		EnsureCursorVisible(TextPtr);
X	    }
X	} else if (strncmp(argv[0], "bof", length) == 0 ||
X		   strncmp(argv[0], "beginning-of-file", length) == 0) {
X	    if (TextPtr->cursorPos != 0) {
X		TextPtr->cursorPos = 0;
X		TextPtr->cursorLine->flags |= TEXT_LINE_DIRTY;
X		TextPtr->cursorLine = TextPtr->firstLine;
X		EnsureCursorVisible(TextPtr);
X	    }
X	} else if (strncmp(argv[0], "bol", length) == 0 ||
X		   strncmp(argv[0], "beginning-of-line", length) == 0) {
X	    TextPtr->cursorPos = TextPtr->cursorLine->index;
X	    EnsureCursorVisible(TextPtr);
X	} else if (strncmp(argv[0], "bp", length) == 0 ||
X		   strncmp(argv[0], "backward-paragraph", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "bw", length) == 0 ||
X		   strncmp(argv[0], "backword-word", length) == 0) {
X	    goto not_implemented;
X	}
X    } else if (c == 'd') {
X	if (strncmp(argv[0], "display-caret", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "dnc", length) == 0 ||
X		   strncmp(argv[0], "delete-next-character", length) == 0) {
X	    if (TextPtr->cursorPos != TextPtr->numChars) {
X		DeleteText(TextPtr, TextPtr->cursorPos, 1);
X	    }
X	} else if (strncmp(argv[0], "dnw", length) == 0 ||
X		   strncmp(argv[0], "delete-next-word", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "dpc", length) == 0 ||
X		   strncmp(argv[0], "delete-previous-character",
X			   length) == 0) {
X	    if (TextPtr->cursorPos != 0) {
X		DeleteText(TextPtr, TextPtr->cursorPos - 1, 1);
X	    }
X	    EnsureCursorVisible(TextPtr);
X	} else if (strncmp(argv[0], "dpw", length) == 0 ||
X		   strncmp(argv[0], "delete-previous-word", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "ds", length) == 0 ||
X		   strncmp(argv[0], "delete-selection", length) == 0) {
X	    goto not_implemented;
X	}
X    } else if (c == 'e') {
X	if (strncmp(argv[0], "eof", length) == 0 ||
X	    strncmp(argv[0], "end-of-file", length) == 0) {
X	    TextPtr->cursorLine->flags |= TEXT_LINE_DIRTY;
X
X	    TextPtr->cursorPos = TextPtr->numChars;
X	    TextPtr->cursorLine = FindChar(TextPtr, TextPtr->cursorPos);
X	    EnsureCursorVisible(TextPtr);
X	} else if (strncmp(argv[0], "eol", length) == 0 ||
X		   strncmp(argv[0], "end-of-line", length) == 0) {
X	    int index = TextPtr->cursorLine->length;
X	    if (TextPtr->cursorLine->text[index - 1] == '\n')
X		TextPtr->cursorPos = TextPtr->cursorLine->index + index - 1;
X	    else
X		TextPtr->cursorPos = TextPtr->cursorLine->index + index;
X	    EnsureCursorVisible(TextPtr);
X	} else if (strncmp(argv[0], "extend-adjust", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "extend-end", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "extend-start", length) == 0) {
X	    goto not_implemented;
X	}
X    } else if (c == 'f') {
X	if (strncmp(argv[0], "fc", length) == 0 ||
X	    strncmp(argv[0], "forward-character", length) == 0) {
X	    if (TextPtr->cursorPos < TextPtr->numChars) {
X		int index = TextPtr->cursorPos - TextPtr->cursorLine->index;
X		if (index == TextPtr->cursorLine->length ||
X		    TextPtr->cursorLine->text[index] == '\n') {
X		    TextPtr->cursorLine->flags |= TEXT_LINE_DIRTY;
X		    if (index < TextPtr->cursorLine->length)
X			TextPtr->cursorPos++;
X		    TextPtr->cursorLine = TextPtr->cursorLine->next;
X		} else
X		    TextPtr->cursorPos++;
X		EnsureCursorVisible(TextPtr);
X	    }
X	} else if (strncmp(argv[0], "focus-in", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "focus-out", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "form-paragraph", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "fp", length) == 0 ||
X		   strncmp(argv[0], "forward-paragraph", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "fw", length) == 0 ||
X		   strncmp(argv[0], "forward-word", length) == 0) {
X	    goto not_implemented;
X	}
X    } else if (c == 'i') {
X	if (strncmp(argv[0], "ic", length) == 0 ||
X	    strncmp(argv[0], "insert-char", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "if", length) == 0 ||
X		   strncmp(argv[0], "insert-file", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "insert-selection", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "is", length) == 0 ||
X		   strncmp(argv[0], "insert-string", length) == 0) {
X	    goto not_implemented;
X	}
X    } else if (c == 'k') {
X	if (strncmp(argv[0], "kp", length) == 0 ||
X	    strncmp(argv[0], "kill-paragraph", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "ks", length) == 0 ||
X			   strncmp(argv[0], "kill-selection", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "kill", length) == 0 ||
X		   strncmp(argv[0], "kill-to-end-of-line", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "keop", length) == 0 ||
X		   strncmp(argv[0], "kill-to-end-of-paragraph", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "kill-word", length) == 0) {
X	    goto not_implemented;
X	}
X    } else if ((c == 'm') && (strncmp(argv[0], "multiply", length) == 0)) {
X	    goto not_implemented;
X    } else if (c == 'n') {
X	if (strncmp(argv[0], "new-line", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "newline-and-backup", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "newline-and-indent", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "nl", length) == 0 ||
X		   strncmp(argv[0], "next-line", length) == 0) {
X	    if (TextPtr->cursorLine->next != NULL) {
X		int index = TextPtr->cursorPos - TextPtr->cursorLine->index;
X		Line *line = TextPtr->cursorLine->next;
X
X		TextPtr->cursorLine->flags |= TEXT_LINE_DIRTY;
X
X		if (index >= line->length)
X		    index = line->length;
X
X		if (line->text &&
X		    index == line->length && line->text[index - 1] == '\n')
X		    index--;
X		TextPtr->cursorLine = line;
X		TextPtr->cursorPos = line->index + index;
X
X		EnsureCursorVisible(TextPtr);
X	    }
X	} else if (strncmp(argv[0], "no-op", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "np", length) == 0 ||
X		   strncmp(argv[0], "next-page", length) == 0) {
X	    goto not_implemented;
X	}
X    } else if (c == 'p') {
X	if (strncmp(argv[0], "pl", length) == 0 ||
X	    strncmp(argv[0], "previous-line", length) == 0) {
X	    if (TextPtr->cursorLine->prev != NULL) {
X		int index = TextPtr->cursorPos - TextPtr->cursorLine->index;
X		Line *line = TextPtr->cursorLine->prev;
X
X		TextPtr->cursorLine->flags |= TEXT_LINE_DIRTY;
X
X		if (index >= line->length)
X		    index = line->length;
X
X		if (line->text &&
X		    index == line->length && line->text[index - 1] == '\n')
X		    index--;
X		TextPtr->cursorLine = line;
X		TextPtr->cursorPos = line->index + index;
X
X		EnsureCursorVisible(TextPtr);
X	    }
X	} else if (strncmp(argv[0], "pp", length) == 0 ||
X		   strncmp(argv[0], "previous-page", length) == 0) {
X	    goto not_implemented;
X	}
X    } else if ((c == 'r') &&
X	       (strncmp(argv[0], "redraw-display", length) == 0)) {
X	    goto not_implemented;
X    } else if (c == 's') {
X	if (strncmp(argv[0], "sd", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "scroll-one-line-down", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "search", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "select-adjust", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "select-all", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "select-end", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "select-start", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "select-word", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "su", length) == 0) {
X	    goto not_implemented;
X	} else if (strncmp(argv[0], "scroll-one-line-up", length) == 0) {
X	    goto not_implemented;
X	}
X    } else if ((c == 't') &&
X	       (strncmp(argv[0], "transpose-characters", length) == 0)) {
X	    goto not_implemented;
X    } else {
X	Tcl_AppendResult(interp, "bad option \"", argv[0], "\"",
X			 (char *) NULL);
X	goto error;
X    }
X
X    EventuallyRedraw(TextPtr);
X    return result;
X
X    not_implemented:
X    Tcl_AppendResult(interp, "option \"", argv[0],
X		     "\" not implemented", (char *) NULL);
X    
X    error:
X    return TCL_ERROR;
X}
X
X
X/*
X *--------------------------------------------------------------
X *
X * DisplayLine --
X *
X *	This procedure redraws the contents of a line in a Text
X *	window.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	Information appears on the screen.
X *
X *--------------------------------------------------------------
X */
X
Xstatic void
XDisplayLine(TextPtr, line, where)
X    register Text *TextPtr;
X    Line *line;
X    int where;
X{
X    register Tk_Window tkwin = TextPtr->tkwin;
X    int startX, baseY, selStartX, selEndX, index, cursorX;
X    int leftIndex, leftOffset;
X    int selectFirst = -1, selectLast = -1;
X    int height, width, count;
X
X    /*
X     * In order to avoid screen flashes, this procedure redraws the
X     * textual area of the Text into off-screen memory, then copies
X     * it back on-screen in a single operation.  This means there's
X     * no point in time where the on-screen image has been cleared.
X     * First, make sure we've got a Pixmap large enough to do the
X     * job.
X     */
X
X    width = TextPtr->pmWidth;
X    height = TextPtr->pmHeight;
X
X    Tk_Fill3DRectangle(Tk_Display(tkwin), TextPtr->pm, TextPtr->normalBorder,
X	    0, 0, TextPtr->pmWidth, TextPtr->pmHeight, 0, TK_RELIEF_FLAT);
X
X    if (line == NULL)
X	goto copy;
X
X    /*
X     * Compute x-coordinate of the "leftIndex" character, plus
X     * vertical position of baseline of text.
X     */
X
X    startX = 0;
X    baseY = (TextPtr->pmHeight + TextPtr->fontPtr->ascent
X	    - TextPtr->fontPtr->descent)/2;
X
X    /* compute the left index */
X
X    if (TextPtr->leftIndex > 0) {
X	leftIndex = TkMeasureChars(TextPtr->fontPtr,
X				   line->text, line->length,
X				   0, TextPtr->leftIndex * TextPtr->avgWidth,
X				   0, &leftOffset);
X	leftOffset -= TextPtr->leftIndex * TextPtr->avgWidth;
X    } else {
X	leftIndex = 0;
X	leftOffset = 0;
X    }
X
X    /* compute selection offsets */
X
X    if (TextPtr->selectFirst < line->index + line->length &&
X	TextPtr->selectLast >= line->index) {
X	selectFirst = TextPtr->selectFirst - line->index;
X	if (selectFirst < 0)
X	    selectFirst = 0;
X	selectLast = TextPtr->selectLast - line->index;
X	if (selectLast >=  line->length)
X	    selectLast = line->length - 1;
X    }
X
X    /*
X     * Draw the background in three layers.  From bottom to top the
X     * layers are:  normal background, selection background, and
X     * insertion cursor background.
X     */
X
X    Tk_Fill3DRectangle(Tk_Display(tkwin), TextPtr->pm, TextPtr->normalBorder,
X	    0, 0, TextPtr->pmWidth, TextPtr->pmHeight, 0, TK_RELIEF_FLAT);
X
X    if (selectLast >= leftIndex) {
X	if (selectFirst <= leftIndex) {
X	    selStartX = startX;
X	    index = leftIndex;
X	} else {
X	    (void) TkMeasureChars(TextPtr->fontPtr,
X		    line->text + leftIndex,
X		    selectFirst - leftIndex, startX,
X		    TextPtr->pmWidth, TK_PARTIAL_OK, &selStartX);
X	    index = selectFirst;
X	}
X	if (selStartX < TextPtr->pmWidth) {
X	    (void) TkMeasureChars(TextPtr->fontPtr,
X		    line->text + index, selectLast + 1 - index,
X		    selStartX, TextPtr->pmWidth, TK_PARTIAL_OK, &selEndX);
X	    Tk_Fill3DRectangle(Tk_Display(tkwin), TextPtr->pm,
X		    TextPtr->selBorder,
X		    selStartX - TextPtr->selBorderWidth,
X		    baseY - TextPtr->fontPtr->ascent
X			    - TextPtr->selBorderWidth,
X		    (selEndX - selStartX) + 2*TextPtr->selBorderWidth,
X		    TextPtr->fontPtr->ascent + TextPtr->fontPtr->descent
X			    + 2*TextPtr->selBorderWidth,
X		    TextPtr->selBorderWidth, TK_RELIEF_RAISED);
X	} else {
X	    selEndX = TextPtr->pmWidth;
X	}
X    }
X
X    /*
X     * Draw a special background for the insertion cursor, overriding
X     * even the selection background.  As a special hack to keep the
X     * cursor visible on mono displays, write background in the cursor
X     * area (instead of nothing) when the cursor isn't on.  Otherwise
X     * the selection would hide the cursor.
X     */
X
X    if (line == TextPtr->cursorLine &&
X	TextPtr->cursorPos >= line->index &&
X	TextPtr->cursorPos <= (line->index + line->length)) {
X	(void) TkMeasureChars(TextPtr->fontPtr,
X			      line->text + leftIndex,
X			      TextPtr->cursorPos - line->index - leftIndex,
X			      startX, TextPtr->pmWidth, TK_PARTIAL_OK,
X			      &cursorX);
X	if (cursorX <= TextPtr->pmWidth) {
X	    if (TextPtr->flags & CURSOR_ON) {
X		Tk_Fill3DRectangle(Tk_Display(tkwin), TextPtr->pm,
X			TextPtr->cursorBorder,
X			cursorX - (TextPtr->cursorWidth)/2,
X			baseY - TextPtr->fontPtr->ascent,
X			TextPtr->cursorWidth,
X			TextPtr->fontPtr->ascent + TextPtr->fontPtr->descent,
X			TextPtr->cursorBorderWidth, TK_RELIEF_RAISED);
X	    } else if (DefaultDepthOfScreen(Tk_Screen(tkwin)) == 1) {
X		Tk_Fill3DRectangle(Tk_Display(tkwin), TextPtr->pm,
X			TextPtr->normalBorder,
X			cursorX - (TextPtr->cursorWidth)/2,
X			baseY - TextPtr->fontPtr->ascent,
X			TextPtr->cursorWidth,
X			TextPtr->fontPtr->ascent + TextPtr->fontPtr->descent,
X			0, TK_RELIEF_FLAT);
X	    }
X	}
X    }
X
X    /*
X     * Draw the text in three pieces:  first the piece to the left of
X     * the selection, then the selection, then the piece to the right
X     * of the selection.
X     */
X
X    if (selectLast < leftIndex) {
X	TkDisplayChars(Tk_Display(tkwin), TextPtr->pm, TextPtr->textGC,
X		TextPtr->fontPtr, line->text + leftIndex,
X		line->length - leftIndex, startX, baseY);
X    } else {
X	count = selectFirst - leftIndex;
X	if (count > 0) {
X	    TkDisplayChars(Tk_Display(tkwin), TextPtr->pm,
X		    TextPtr->textGC, TextPtr->fontPtr,
X		    line->text + leftIndex, count,
X		    startX, baseY);
X	    index = selectFirst;
X	} else {
X	    index = leftIndex;
X	}
X	count = selectLast + 1 - index;
X	if ((selStartX < TextPtr->pmWidth) && (count > 0)) {
X	    TkDisplayChars(Tk_Display(tkwin), TextPtr->pm,
X		    TextPtr->selTextGC, TextPtr->fontPtr,
X		    line->text + index, count, selStartX, baseY);
X	}
X	count = line->length - selectLast - 1;
X	if ((selEndX < TextPtr->pmWidth) && (count > 0)) {
X	    TkDisplayChars(Tk_Display(tkwin), TextPtr->pm,
X		    TextPtr->textGC, TextPtr->fontPtr,
X		    line->text + selectLast + 1,
X		    count, selEndX, baseY);
X	}
X    }
X
X    /*
X     * Everything's been redisplayed;  now copy the pixmap onto the screen.
X     */
X
X    line->flags &= ~TEXT_LINE_DIRTY;
X
Xcopy:
X    XCopyArea(Tk_Display(tkwin), TextPtr->pm, Tk_WindowId(tkwin),
X	      TextPtr->textGC, 0, 0, TextPtr->pmWidth, TextPtr->pmHeight,
X	      TextPtr->offset,
X	      TextPtr->borderWidth + where * TextPtr->pmHeight + 1);
X}
X
X
X/*
X *--------------------------------------------------------------
X *
X * DisplayText --
X *
X *	This procedure redraws the contents of a text widget.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	Information appears on the screen.
X *
X *--------------------------------------------------------------
X */
X
Xstatic void
XDisplayText(clientdata)
X    char *clientdata;
X
X{
X    register Text *TextPtr = (Text *)clientdata;
X    register Tk_Window tkwin = TextPtr->tkwin;
X    Line *line;
X    int height, width;
X    int	where = 0;
X    int visible;
X    int displayedLines = 0;
X    ScrollValues scrollValues;
X
X    TextPtr->flags &= ~REDRAW_PENDING;
X    if ((TextPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
X	return;
X    }
X
X    if (TextPtr->flags & CLEAR_NEEDED) {
X	XClearWindow(Tk_Display(tkwin), Tk_WindowId(tkwin));
X    }
X
X    height = TextPtr->fontPtr->ascent + TextPtr->fontPtr->descent
X	    + 2*TextPtr->selBorderWidth;
X    width = Tk_Width(tkwin) - 2*TextPtr->offset;
X    if ((TextPtr->pmWidth != width) || (TextPtr->pmHeight != height)) {
X	if (TextPtr->pm != None) {
X	    XFreePixmap(Tk_Display(tkwin), TextPtr->pm);
X	}
X	TextPtr->pm = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
X		width, height, DefaultDepthOfScreen(Tk_Screen(tkwin)));
X	TextPtr->pmDisplay = Tk_Display(tkwin);
X	TextPtr->pmWidth = width;
X	TextPtr->pmHeight = height;
X    }
X
X    visible = (Tk_Height(tkwin) - 2 * (TextPtr->borderWidth + 1)) /
X	(TextPtr->fontPtr->ascent + TextPtr->fontPtr->descent);
X
X    line = TextPtr->topLine;
X    scrollValues.total = 0;
X    scrollValues.window = Tk_Width(tkwin);
X    scrollValues.first = 0;
X    scrollValues.last = 0;
X    while (visible--) {
X	if ((TextPtr->flags & (REFRESH_NEEDED|CLEAR_NEEDED)) ||
X	    (line != NULL && ((line->flags & TEXT_LINE_DIRTY) ||
X			      (line == TextPtr->cursorLine))) ||
X	    ((line == NULL) && (displayedLines < TextPtr->displayedLines)))
X	    DisplayLine(TextPtr, line, where);
X	if (line != NULL) {
X	    if (line->width > scrollValues.total)
X		scrollValues.total = line->width;
X	    line = line->next;
X	    displayedLines++;
X	}
X	where++;
X    }
X    TextPtr->displayedLines = displayedLines;
X    if (scrollValues.total > scrollValues.window)
X	scrollValues.last = scrollValues.window - 1;
X    else
X	scrollValues.last = scrollValues.total - 1;
X
X    if ((scrollValues.first != TextPtr->lastScrollH.first) ||
X	(scrollValues.last != TextPtr->lastScrollH.last) ||
X	(scrollValues.total != TextPtr->lastScrollH.total) ||
X	(scrollValues.window != TextPtr->lastScrollH.window)) {
X	TextPtr->lastScrollH = scrollValues;
X	TextUpdateHScrollbar(TextPtr);
X    }
X
X    /*
X     * Draw the border last, so it will overwrite any text that extends
X     * past the viewable part of the window.
X     */
X
X    if ((TextPtr->relief != TK_RELIEF_FLAT)
X	    && (TextPtr->flags &
X		(CLEAR_NEEDED|BORDER_NEEDED|REFRESH_NEEDED))) {
X	Tk_Draw3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
X		TextPtr->normalBorder, 0, 0, Tk_Width(tkwin),
X		Tk_Height(tkwin), TextPtr->borderWidth,
X		TextPtr->relief);
X    }
X    TextPtr->flags &= ~(CLEAR_NEEDED | BORDER_NEEDED | REFRESH_NEEDED);
X}
X
X
X/*
X *--------------------------------------------------------------
X *
X * EnsureCursorVisible --
X *
X *	This procedure adjusts the top visible line in a text
X *	widget so the line containing the cursor is visible.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	The top line may be adjusted.
X *
X *--------------------------------------------------------------
X */
X
Xstatic void
XEnsureCursorVisible(TextPtr)
X    register Text *TextPtr;
X
X{
X    int distance = TextPtr->cursorLine->num - TextPtr->topLine->num;
X
X    if (distance < 0) {
X	TextPtr->topLine = TextPtr->cursorLine;
X	TextPtr->flags |= REFRESH_NEEDED;
X    } else {
X	int visible = (Tk_Height(TextPtr->tkwin) -
X		       2 * (TextPtr->borderWidth + 1)) /
X			   (TextPtr->fontPtr->ascent +
X			    TextPtr->fontPtr->descent);
X	if (distance >= visible) {
X	    int count = distance + 1 - visible;
X
X	    while (count--)
X		TextPtr->topLine = TextPtr->topLine->next;
X
X	    TextPtr->flags |= REFRESH_NEEDED;
X	}
X    }
X}
X
X
X/*
X *--------------------------------------------------------------
X *
X * FindLine --
X *
X *	This procedure locates a line in the linked list based
X *	on the line number.
X *
X * Results:
X *	A pointer to a line structure.
X *
X * Side effects:
X *	None.
X *
X *--------------------------------------------------------------
X */
X
Xstatic Line *
XFindLine(TextPtr, num)
X    register Text *TextPtr;
X    int num;
X
X{
X    Line *line;
X
X    if (TextPtr->topLine->num <= num)
X	line = TextPtr->topLine;
X    else
X	line = TextPtr->firstLine;
X
X    while (line->length != 0 && num > (line->num))
X	line = line->next;
X
X    if (line->length == 0 && line->prev != NULL &&
X	line->prev->text[line->prev->length - 1] != '\n')
X	line = line->prev;
X
X    return line;
X}
X
X
X/*
X *--------------------------------------------------------------
X *
X * FindLine --
X *
X *	This procedure locates a line in the linked list based
X *	on a character position in the text.
X *
X * Results:
X *	A pointer to a line structure.
X *
X * Side effects:
X *	None.
X *
X *--------------------------------------------------------------
X */
Xstatic Line *
XFindChar(TextPtr, index)
X    register Text *TextPtr;
X    int index;
X
X{
X    Line *line;
X
X    if (TextPtr->topLine->index <= index)
X	line = TextPtr->topLine;
X    else
X	line = TextPtr->firstLine;
X
X    while (line->length != 0 &&
X	   index >= (line->index + line->length))
X	line = line->next;
X
X    if (line->length == 0 && line->prev != NULL &&
X	line->prev->text[line->prev->length - 1] != '\n')
X	line = line->prev;
X
X    return line;
X}
X
X
X/*
X *--------------------------------------------------------------
X *
X * NewLine --
X *
X *	Allocates memory and initializes a new line structure.
X *
X * Results:
X *	A pointer to a line structure.
X *
X * Side effects:
X *	Memory is allocated.
X *
X *--------------------------------------------------------------
X */
X
Xstatic Line *
XNewLine()
X
X{
X    Line *new;
X
X    new = (Line *)ckalloc(sizeof(Line));
X
X    new->next = NULL;
X    new->prev = NULL;
X
X    new->flags = 0;
X    new->size = 0;
X    new->index = 0;
X    new->length = 0;
X    new->width = 0;
X    new->text = NULL;
X
X    return new;
X}
X
X
X/*
X *--------------------------------------------------------------
X *
X * DestroyLine --
X *
X *	Deallocates a line structure.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	Memory is deallocated.
X *
X *--------------------------------------------------------------
X */
X
Xstatic void
XDestroyLine(line)
X    Line *line;
X
X{
X    if (line->length == 0)
X	fprintf(stderr, "\nattempt to delete last line\n");
X
X    if (line->mem != NULL)
X	ckfree(line->mem);
X
X    ckfree(line);
X}
X
X
X/*
X *--------------------------------------------------------------
X *
X * LinkLine --
X *
X *	Inserts a line structure into a linked list.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	None.
X *
X *--------------------------------------------------------------
X */
X
Xstatic void
XLinkLine(TextPtr, line, before)
X    Text *TextPtr;
X    Line *line, *before;
X
X{
X    if (before == TextPtr->firstLine)
X	TextPtr->firstLine = line;
X    if (before == TextPtr->topLine)
X	TextPtr->topLine = line;
X
X    line->next = before;
X    line->prev = before->prev;
X    if (before->prev != NULL)
X	before->prev->next = line;
X    before->prev = line;
X
X    if (line->width == 0) {
X	(void) TkMeasureChars(TextPtr->fontPtr,
X			      line->text, line->length,
X			      0, 1<<30, 0,
X			      &line->width);
X    }
X
X    TextPtr->numLines++;
X}
X
X
X/*
X *--------------------------------------------------------------
X *
X * UnlinkLine --
X *
X *	Removes a line structure from a linked list.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	None.
X *
X *--------------------------------------------------------------
X */
X
Xstatic void
XUnlinkLine(TextPtr, line)
X    Text *TextPtr;
X    Line *line;
X
X{
X    if (line == TextPtr->firstLine)
X	TextPtr->firstLine = line->next;
X    if (line == TextPtr->topLine)
X	TextPtr->topLine = line->next;
X
X    if (line->prev != NULL)
X	line->prev->next = line->next;
X    if (line->next != NULL)
X	line->next->prev = line->prev;
X
X    TextPtr->numLines--;
X}
X
X
X/*
X *--------------------------------------------------------------
X *
X * AppendToLine --
X *
X *	Appends text to a line.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	None.
X *
X *--------------------------------------------------------------
X */
X
Xstatic void
XAppendToLine(line, text, length)
X    Line *line;
X    char *text;
X    int length;
X
X{
X    int space = line->size - ((line->text - line->mem) +
X			       line->length + 1);
X
X    if (space < length) {
X	char *old = line->text;
X
X	line->size = line->length + length + 40;
X	line->text = (char *)ckalloc(line->size);
X
X	strcpy(line->text, old);
X	ckfree(line->mem);
X	line->mem = line->text;
X    }
X
X    strncpy(line->text + line->length, text, length);
X    line->length += length;
X    line->text[line->length] = '\0';
X    line->width = 0;
X}
X
X
X/*
X *--------------------------------------------------------------
X *
X * JoinLines --
X *
X *	Joins two adjacent lines into one.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	Second line is unlinked and destroyed.
X *
X *--------------------------------------------------------------
X */
X
Xstatic void
XJoinLines(TextPtr, line1, line2)
X    Text *TextPtr;
X    Line *line1, *line2;
X
X{
X    int newwidth = 0;
X
X    if (line2->length == 0)
X	return;
X
X    if (line1->width > 0 && line2->width > 0)
X	newwidth = line1->width + line2->width;
X
X    AppendToLine(line1, line2->text, line2->length);
X
X    line1->flags |= TEXT_LINE_DIRTY;
X
X    if (newwidth == 0) {
X	(void) TkMeasureChars(TextPtr->fontPtr,
X			      line1->text, line1->length,
X			      0, 1<<30, 0,
X			      &line1->width);
X    } else 
X	line1->width = newwidth;
X
X    UnlinkLine(TextPtr, line2);
X    DestroyLine(line2);
X}
X
X
X/*
X *--------------------------------------------------------------
X *
X * SplitLine --
X *
X *	Splits a line into two lines.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	A new line is allocated and linked in.
X *
X *--------------------------------------------------------------
X */
X
Xstatic void
XSplitLine(TextPtr, line, where)
X    Text *TextPtr;
X    Line *line;
X    int where;
X
X{
X    Line *new;
X    int left, right;
X    int oldwidth = line->width;
X
X    left = where;
X    right = line->length - where;
X
X    if (left == 0 || right == 0)
X	return;
X
X    new = NewLine();
X
X    if (left > right) {
X	new->mem = new->text = (char *)strdup(line->text + where);
X	new->length = line->length - where;
X	new->size = new->length + 1;
X
X	line->length = where;
X	line->text[where] = '\0';
X
X	(void) TkMeasureChars(TextPtr->fontPtr,
X			      new->text, new->length,
X			      0, 1<<30, 0,
X			      &new->width);
X
X/*	if (line->text[where-1] != '\n')
X	    line->width -= new->width;
X	else */
X	    (void) TkMeasureChars(TextPtr->fontPtr,
X				  line->text, line->length,
X				  0, 1<<30, 0,
X				  &line->width);
X    } else {
X	new->mem = line->mem;
X	new->text = line->text;
X	new->length = line->length - where;
X	new->size = line->size;
X	new->width = line->width;
X
X	line->size = where + 1;
X	line->mem = line->text = (char *)ckalloc(line->size);
X	line->length = where;
X	strncpy(line->text, new->text, line->length);
X	line->text[line->length] = '\0';
X
X	(void) TkMeasureChars(TextPtr->fontPtr,
X			      line->text, line->length,
X			      0, 1<<30, 0,
X			      &line->width);
X
X	new->text = new->text + where;
X
X/*	if (line->text[where-1] != '\n')
X	    new->width -= line->width;
X	else {*/
X	    (void) TkMeasureChars(TextPtr->fontPtr,
X				  new->text, new->length,
X				  0, 1<<30, 0,
X				  &new->width);
X    }
X
X    line->flags |= TEXT_LINE_DIRTY;
X    new->flags |= TEXT_LINE_DIRTY;
X
X    LinkLine(TextPtr, new, line->next);
X}
X
Xstatic void
XDumpLines(start, count)
X    Line *start;
X
X{
X    while (start != NULL && count-- > 0) {
X	fprintf(stderr,
X		"mem = %x, text = %x, size = %d, length = %d, width = %d\n",
X		start->mem, start->text, start->size, start->length,
X		start->width);
X	if (start->length > 0)
X	    fprintf(stderr, "%.100s", start->text);
X	fprintf(stderr, "\n---\n");
X	start = start->next;
X    }
X}
X
X
X/*
X *--------------------------------------------------------------
X *
X * FixLines --
X *
X *	Adjusts lines according to the wrapping mode.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	Lines are changed.
X *
X *--------------------------------------------------------------
X */
X
Xstatic void
XFixLines(TextPtr, start, min)
X    register Text *TextPtr;
X    Line *start;
X    int min;
X
X{
X    char *cp;
X    int next;
X    int winwidth = Tk_Width(TextPtr->tkwin) - TextPtr->offset;
X    int changed = 0;
X
X    while ((start->length > 0) && (changed || (min-- > 0))) {
X	int measureflags = 0;
X
X	changed = 0;
X
X	switch (TextPtr->wrapMode) {
X	    int where;
X
X	  case TK_WRAP_NONE:
X	    cp = strchr(start->text, '\n');
X
X	    if (cp == NULL) {
X		/* too few */
X		Line *next = start->next;
X		/* get more */
X		while (next->length > 0 &&
X		       (cp = strchr(next->text, '\n')) == NULL) {
X		    JoinLines(TextPtr, start, next);
X		    next = start->next;
X		    changed++;
X		}
X		if (next->length > 0) {
X		    where = cp - next->text + 1;
X
X		    if (where == next->length) {
X			JoinLines(TextPtr, start, next);
X		    } else {
X			SplitLine(TextPtr, next, where);
X			JoinLines(TextPtr, start, next);
X		    }
X		    changed++;
X		}
X	    } else if ((where = cp - start->text + 1) < start->length) {
X		/* too many */
X		SplitLine(TextPtr, start, where);
X		changed++;
X	    }
X
X	    break;
X
X	  case TK_WRAP_WORD:
X	    measureflags = TK_WHOLE_WORDS;
X	    /* fall */
X
X	  case TK_WRAP_CHAR:
X	    if ((cp = strchr(start->text, '\n')) != NULL)
X		where = cp - start->text + 1;
X	    else
X		where = start->length;
X
X	    if (start->width < winwidth && cp == NULL) {
X		Line *next = start->next;
X		int remain = winwidth - start->width;
X
X		/* get more */
X		if (next->length > 0 &&
X		    (cp = strchr(next->text, '\n')) != NULL)
X		    where = cp - next->text + 1;
X		else
X		    where = next->length;
X
X		while (next->length > 0 && next->width < remain &&
X		       where == next->length && cp == NULL) {
X		    remain -= next->width;
X
X		    JoinLines(TextPtr, start, next);
X		    next = start->next;
X			
X		    if (next->length > 0 &&
X			(cp = strchr(next->text, '\n')) != NULL)
X			where = cp - next->text + 1;
X		    else
X			where = next->length;
X
X		    changed++;
X		}
X
X		if (next->length > 0) {
X		    int width;
X
X		    /* This bit doesn't quite work right for word wrap.
X		       Should really join the lines and then do the
X		       measuring.  The way it is now, some wrapped
X		       words which should be coalesced aren't */
X
X		    where = TkMeasureChars(TextPtr->fontPtr,
X					   next->text, next->length,
X					   0, remain,
X					   measureflags,
X					   &width);
X
X		    if (next->text[where] == '\n')
X			where++;
X		    /* else suck up white space? */
X
X		    if (where == next->length) {
X			JoinLines(TextPtr, start, next);
X			changed++;
X		    } else if (where > 0) {
X			SplitLine(TextPtr, next, where);
X			JoinLines(TextPtr, start, next);
X			changed++;
X		    }
X		}
X	    } else if (start->width > winwidth) {
X		int width;
X
X		where = TkMeasureChars(TextPtr->fontPtr,
X				       start->text, start->length,
X				       0, winwidth,
X				       measureflags,
X				       &width);
X
X		if (start->text[where] == '\n')
X		    where++;
X
X		if (where == 0) {
X		    /* degenerate case when wrap mode = WORD and
X		       leading whitespace extends beyond right border.
X		       We try again using character mode and require
X		       at least one */
X		    where = TkMeasureChars(TextPtr->fontPtr,
X				       start->text, start->length,
X				       0, winwidth,
X				       TK_AT_LEAST_ONE,
X				       &width);
X		    if (start->text[where] == '\n')
X			where++;
X		}
X
X		SplitLine(TextPtr, start, where);
X		changed++;
X	    } else if (where < start->length) {
X		SplitLine(TextPtr, start, where);
X		changed++;
X	    }
X
X	    break;
X
X	  case TK_WRAP_FIXED:
X	    if ((cp = strchr(start->text, '\n')) != NULL)
X		where = cp - start->text + 1;
X	    else
X		where = start->length;
X
X	    if (where < TextPtr->prefWidth && cp == NULL) {
X		Line *next = start->next;
X		int remain = TextPtr->prefWidth - where;
X
X		/* get more */
X		if (next->length > 0 &&
X		    (cp = strchr(next->text, '\n')) != NULL)
X		    where = cp - next->text + 1;
X		else
X		    where = next->length;
X
X		while (next->length > 0 && where < remain &&
X		       where == next->length && cp == NULL) {
X		    remain -= next->length;
X
X		    JoinLines(TextPtr, start, next);
X		    next = start->next;
X			
X		    if (next->length > 0 &&
X			(cp = strchr(next->text, '\n')) != NULL)
X			where = cp - next->text + 1;
X		    else
X			where = next->length;
X
X		    changed++;
X		}
X
X		if (next->length > 0) {
X		    if (where > remain)
X			where = remain;
X
X		    if (where == next->length) {
X			JoinLines(TextPtr, start, next);
X		    } else {
X			SplitLine(TextPtr, next, where);
X			JoinLines(TextPtr, start, next);
X		    }
X
X		    changed++;
X		}
X	    } else if (where > TextPtr->prefWidth) {
X		SplitLine(TextPtr, start, TextPtr->prefWidth);
X		changed++;
X	    } else if (where < start->length) {
X		SplitLine(TextPtr, start, where);
X		changed++;
X	    }
X
X	    break;
X	}
X	start = start->next;
X    }
X
X    /* fix the indexes */
X    TextPtr->firstLine->index = 0;
X    TextPtr->firstLine->num = next = 0;
X    start = TextPtr->firstLine->next;
X    while (start != NULL) {
X	int newindex = start->prev->index + start->prev->length;
X
X	next++;
X
X	if (start->index != newindex || start->num != next)
X	    start->flags |= TEXT_LINE_DIRTY;
X
X	start->index = start->prev->index + start->prev->length;
X	start->num = next;
X	start = start->next;
X    }
X}
X
X
X/*
X *----------------------------------------------------------------------
X *
X * InsertText --
X *
X *	Add new characters to a Text widget.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	New information gets added to TextPtr;  it will be redisplayed
X *	soon, but not necessarily immediately.
X *
X *----------------------------------------------------------------------
X */
X
Xstatic void
XInsertText(TextPtr, index, string)
X    register Text *TextPtr;	/* Text that is to get the new
X				 * elements. */
X    int index;			/* Add the new elements before this
X				 * element. */
X    char *string;		/* New characters to add (NULL-terminated
X				 * string). */
X{
X    int length;
X    int where;
X    register Line *line, *new;
X
X    length = strlen(string);
X
X    if (length == 0) {
X	return;
X    }
X
X    new = NewLine();
X    new->mem = new->text = (char *)strdup(string);
X    new->length = length;
X    new->size = new->length + 1;
X
X    new->flags |= TEXT_LINE_DIRTY;
X
X    line = FindChar(TextPtr, index);
X
X    where = index - line->index;
X
X    if (line->length == 0 || where == 0) {
X	LinkLine(TextPtr, new, line);
X	line = line->prev;
X    } else {
X	if (where < line->length)
X	    SplitLine(TextPtr, line, where);
X	LinkLine(TextPtr, new, line->next);
X    }
X
X    if (line->prev != NULL)
X	FixLines(TextPtr, line->prev, 4);
X    else
X	FixLines(TextPtr, line, 3);
X
X    TextPtr->numChars += length;
X
X    /*
X     * Inserting characters invalidates all indexes into the string.
X     * Touch up the indexes so that they still refer to the same
X     * characters (at new positions).
X     */
X
X    if (TextPtr->selectFirst >= index) {
X	TextPtr->selectFirst += length;
X    }
X    if (TextPtr->selectLast >= index) {
X	TextPtr->selectLast += length;
X    }
X    if (TextPtr->selectAnchor >= index) {
X	TextPtr->selectAnchor += length;
X    }
X    if (TextPtr->leftIndex > index) {
X	TextPtr->leftIndex += length;
X    }
X    if (TextPtr->cursorPos >= index) {
X	TextPtr->cursorPos += length;
X    }
X
X    TextPtr->cursorLine->flags |= TEXT_LINE_DIRTY;
X    TextPtr->cursorLine = FindChar(TextPtr, TextPtr->cursorPos);
X    EnsureCursorVisible(TextPtr);
X    EventuallyRedraw(TextPtr);
X    TextUpdateVScrollbar(TextPtr);
X}
X
X
X/*
X *----------------------------------------------------------------------
X *
X * DeleteText --
X *
X *	Remove one or more characters from a Text widget.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	Memory gets freed, the Text gets modified and (eventually)
X *	redisplayed.
X *
X *----------------------------------------------------------------------
X */
X
Xstatic void
XDeleteText(TextPtr, index, count)
X    register Text *TextPtr;	/* Text widget to modify. */
X    int index;			/* Index of first character to delete. */
X    int count;			/* How many characters to delete. */
X
X{
X    char *new;
X    Line *l, *prev;
X    int c, where;
X
X    if ((index + count) > TextPtr->numChars)
X	count = TextPtr->numChars - index;
X
X    if (count <= 0)
X	return;
X
X    l = FindChar(TextPtr, index);
X    prev = l->prev;
X
X    c = count;
X
X    /* first line */
X    where = index - l->index;
X    if (where > 0) {
X	if (c < l->length - where) {
X	    SplitLine(TextPtr, l, where + c);
X	    c = 0;
X	} else
X	    c -= l->length - where;
X	l->length = where;
X	l->text[l->length] = '\0';
X	(void) TkMeasureChars(TextPtr->fontPtr,
X			      l->text, l->length,
X			      0, 1<<30, 0,
X			      &l->width);
X
X	l->flags |= TEXT_LINE_DIRTY;
X
X	l = l->next;
X    }
X
X    /* intervening lines */
X    while (c > 0 && c >= l->length) {
X	Line *victim = l;
X
X	c -= l->length;
X	l = l->next;
X
X	UnlinkLine(TextPtr, victim);
X	DestroyLine(victim);
X    }
X
X    /* last line */
X    if (c > 0) {
X	l->text += c;
X	l->length -= c;
X	(void) TkMeasureChars(TextPtr->fontPtr,
X			      l->text, l->length,
X			      0, 1<<30, 0,
X			      &l->width);
X
X	l->flags |= TEXT_LINE_DIRTY;
X    }
X
X    TextPtr->numChars -= count;
X
X    if (prev != NULL)
X	FixLines(TextPtr, prev, 3);
X    else
X	FixLines(TextPtr, TextPtr->firstLine, 2);
X
X    /*
X     * Deleting characters results in the remaining characters being
X     * renumbered.  Update the various indexes into the string to reflect
X     * this change.
X     */
X    if (TextPtr->selectFirst >= index) {
X	if (TextPtr->selectFirst >= (index+count)) {
X	    TextPtr->selectFirst -= count;
X	} else {
X	    TextPtr->selectFirst = index;
X	}
X    }
X    if (TextPtr->selectLast >= index) {
X	if (TextPtr->selectLast >= (index+count)) {
X	    TextPtr->selectLast -= count;
X	} else {
X	    TextPtr->selectLast = index-1;
X	}
X    }
X    if (TextPtr->selectLast < TextPtr->selectFirst) {
X	TextPtr->selectFirst = TextPtr->selectLast = -1;
X    }
X    if (TextPtr->selectAnchor >= index) {
X	if (TextPtr->selectAnchor >= (index+count)) {
X	    TextPtr->selectAnchor -= count;
X	} else {
X	    TextPtr->selectAnchor = index;
X	}
X    }
X    if (TextPtr->leftIndex > index) {
X	if (TextPtr->leftIndex >= (index+count)) {
X	    TextPtr->leftIndex -= count;
X	} else {
X	    TextPtr->leftIndex = index;
X	}
X    }
X    if (TextPtr->cursorPos >= index) {
X	if (TextPtr->cursorPos >= (index+count)) {
X	    TextPtr->cursorPos -= count;
X	} else {
X	    TextPtr->cursorPos = index;
X	}
X    }
X
X    TextPtr->cursorLine->flags |= TEXT_LINE_DIRTY;
X    TextPtr->cursorLine = FindChar(TextPtr, TextPtr->cursorPos);
X    EnsureCursorVisible(TextPtr);
X    EventuallyRedraw(TextPtr);
X    TextUpdateVScrollbar(TextPtr);
X}
X
X
X/*
X *----------------------------------------------------------------------
X *
X * GetText --
X *
X *	Get some text.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	interpreter's result is set.
X *
X *----------------------------------------------------------------------
X */
X
Xstatic char *
XGetText(TextPtr, index, count)
X    register Text *TextPtr;	/* Text widget to modify. */
X    int index;			/* Index of first character to delete. */
X    int count;			/* How many characters to delete. */
X
X{
X    char *result, *cp;
X    Line *line, *l;
X    int c, where;
X
X    if ((index + count) > TextPtr->numChars)
X	count = TextPtr->numChars - index;
X
X    if (count <= 0)
X	return NULL;
X
X    cp = result = (char *)ckalloc(count + 1);
X
X    l = FindChar(TextPtr, index);
X
X    c = count;
X
X    /* first line */
X    where = index - l->index;
X    if (where > 0) {
X	int n;
X
X	if (c < l->length - where)
X	    n = c;
X	else
X	    n = l->length - where;
X
X	strncpy(cp, l->text + where, n);
X	cp += n;
X	c -= n;
X
X	l = l->next;
X    }
X
X    /* intervening lines */
X    while (c > 0 && c >= l->length) {
X	strcpy(cp, l->text);
X
X	c -= l->length;
X	cp += l->length;
X	l = l->next;
X    }
X
X    /* last line */
X    if (c > 0) {
X	strncpy(cp, l->text, c);
X
X	cp += c;
X	c = 0;
X    }
X
X    *cp = '\0';
X
X    return result;
X}
X
X
X/*
X *--------------------------------------------------------------
X *
X * TextEventProc --
X *
X *	This procedure is invoked by the Tk dispatcher for various
X *	events on Textes.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	When the window gets deleted, internal structures get
X *	cleaned up.  When it gets exposed, it is redisplayed.
X *
X *--------------------------------------------------------------
X */
X
Xstatic void
XTextEventProc(clientData, eventPtr)
X    ClientData clientData;	/* Information about window. */
X    XEvent *eventPtr;		/* Information about event. */
X{
X    Text *TextPtr = (Text *) clientData;
X    if (eventPtr->type == Expose) {
X	EventuallyRedraw(TextPtr);
X	TextPtr->flags |= (BORDER_NEEDED | REFRESH_NEEDED);
X    } else if (eventPtr->type == DestroyNotify) {
X	Tcl_DeleteCommand(TextPtr->interp, Tk_PathName(TextPtr->tkwin));
X	TextPtr->tkwin = NULL;
X	if (TextPtr->flags & REDRAW_PENDING) {
X	    Tk_CancelIdleCall(DisplayText, (ClientData) TextPtr);
X	}
X	Tk_EventuallyFree((ClientData) TextPtr, DestroyText);
X    } else if (eventPtr->type == ConfigureNotify) {
X	Tk_Preserve((ClientData) TextPtr);
X	EventuallyRedraw(TextPtr);
X	TextUpdateVScrollbar(TextPtr);
X	Tk_Release((ClientData) TextPtr);
X    }
X}
X
Xstatic int
XTextXYToIndex(TextPtr, x, y)
X    Text *TextPtr;
X    int x, y;
X
X{
X    Line *line = TextPtr->topLine;
X    int result, dummy;
X
X    /* locate the line */
X
X    y -= TextPtr->borderWidth + 1;
X
X    if (y > 0) {
X	int count = y / TextPtr->pmHeight;
X
X	while (line->length > 0 && count--)
X	    line = line->next;
X    }
X
X    if (line->length == 0)
X	result = TextPtr->numChars;
X    else
X	result = line->index +
X	    TkMeasureChars(TextPtr->fontPtr,
X			   line->text,
X			   line->length,
X			   TextPtr->offset,
X			   x + TextPtr->leftIndex * TextPtr->avgWidth,
X			   0, &dummy);
X
X    return result;
X}
X
X
X/*
X *--------------------------------------------------------------
X *
X * GetTextIndex --
X *
X *	Parse an index into a Text widget and return either its value
X *	or an error.
X *
X * Results:
X *	A standard Tcl result.  If all went well, then *indexPtr is
X *	filled in with the index (into TextPtr) corresponding to
X *	string.  Otherwise an error message is left in interp->result.
X *
X * Side effects:
X *	None.
X *
X *--------------------------------------------------------------
X */
X
Xstatic int
XGetTextIndex(interp, TextPtr, string, indexPtr)
X    Tcl_Interp *interp;		/* For error messages. */
X    Text *TextPtr;		/* Text for which the index is being
X				 * specified. */
X    char *string;		/* Numerical index into TextPtr's element
X				 * list, or "end" to refer to last element. */
X    int *indexPtr;		/* Where to store converted relief. */
X{
X    int length;
X
X    length = strlen(string);
X
X    if (string[0] == 'e') {
X	if (strncmp(string, "end", length) == 0) {
X	    *indexPtr = TextPtr->numChars;
X	} else {
X	    badIndex:
X
X	    /*
X	     * Some of the paths here leave messages in interp->result,
X	     * so we have to clear it out before storing our own message.
X	     */
X
X	    Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
X	    Tcl_AppendResult(interp, "bad Text index \"", string,
X		    "\"", (char *) NULL);
X	    return TCL_ERROR;
X	}
X    } else if (string[0] == 'c') {
X	if (strncmp(string, "cursor", length) == 0) {
X	    *indexPtr = TextPtr->cursorPos;
X	} else {
X	    goto badIndex;
X	}
X    } else if (string[0] == 's') {
X	if (TextPtr->selectFirst == -1) {
X	    interp->result = "selection isn't in Text";
X	    return TCL_ERROR;
X	}
X	if (length < 5) {
X	    goto badIndex;
X	}
X	if (strncmp(string, "sel.first", length) == 0) {
X	    *indexPtr = TextPtr->selectFirst;
X	} else if (strncmp(string, "sel.last", length) == 0) {
X	    *indexPtr = TextPtr->selectLast;
X	} else {
X	    goto badIndex;
X	}
X    } else if (string[0] == 't') {
X	if (strncmp(string, "top", length) == 0) {
X	    *indexPtr = TextPtr->topLine->index;
X	} else {
X	    goto badIndex;
X	}
X    } else if (string[0] == '@') {
X	char *y_string;
X	int x, y, dummy;
X
X	if ((y_string = strchr(string+1, ',')) == NULL)
X	    goto badIndex;
X	else
X	    *y_string++ = '\0';
X
X	if (Tcl_GetInt(interp, string+1, &x) != TCL_OK) {
X	    goto badIndex;
X	}
X
X	if (Tcl_GetInt(interp, y_string, &y) != TCL_OK) {
X	    goto badIndex;
X	}
X
X	if (TextPtr->numChars == 0) {
X	    *indexPtr = 0;
X	} else {
X	    *indexPtr = TextXYToIndex(TextPtr, x, y);
X	}
X    } else {
X	if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
X	    goto badIndex;
X	}
X	if (*indexPtr < 0){
X	    *indexPtr = 0;
X	} else if (*indexPtr > TextPtr->numChars) {
X	    *indexPtr = TextPtr->numChars;
X	}
X    }
X    return TCL_OK;
X}
X
X/*
X *----------------------------------------------------------------------
X *
X * TextScanTo --
X *
X *	Given a y-coordinate (presumably of the curent mouse location)
X *	drag the view in the window to implement the scan operation.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	The view in the window may change.
X *
X *----------------------------------------------------------------------
X */
X
Xstatic void
XTextScanTo(TextPtr, x)
X    register Text *TextPtr;		/* Information about widget. */
X    int x;				/* X-coordinate to use for scan
X					 * operation. */
X{
X    int newLeftIndex;
X
X    /*
X     * Compute new leftIndex for Text by amplifying the difference
X     * between the current position and the place where the scan
X     * started (the "mark" position).  If we run off the left or right
X     * side of the Text, then reset the mark point so that the current
X     * position continues to correspond to the edge of the window.
X     * This means that the picture will start dragging as soon as the
X     * mouse reverses direction (without this reset, might have to slide
X     * mouse a long ways back before the picture starts moving again).
X     */
X
X    newLeftIndex = TextPtr->scanMarkIndex
X	    - (10*(x - TextPtr->scanMarkX))/TextPtr->avgWidth;
X    if (newLeftIndex < 0) {
X	newLeftIndex = TextPtr->scanMarkIndex = 0;
X	TextPtr->scanMarkX = x;
X    } else if (newLeftIndex >= TextPtr->numChars) {
X	newLeftIndex = TextPtr->scanMarkIndex = TextPtr->numChars-1;
X	TextPtr->scanMarkX = x;
X    }
X    if (newLeftIndex != TextPtr->leftIndex) {
X	TextPtr->leftIndex = newLeftIndex;
X	EventuallyRedraw(TextPtr);
X	TextUpdateVScrollbar(TextPtr);
X    }
X}
X
X/*
X *----------------------------------------------------------------------
X *
X * TextSelectTo --
X *
X *	Modify the selection by moving its un-anchored end.  This could
X *	make the selection either larger or smaller.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	The selection changes.
X *
X *----------------------------------------------------------------------
X */
X
Xstatic void
XTextSelectTo(TextPtr, index)
X    register Text *TextPtr;		/* Information about widget. */
X    int index;				/* Index of element that is to
X					 * become the "other" end of the
X					 * selection. */
X{
X    int newFirst, newLast;
X
X    /*
X     * Grab the selection if we don't own it already.
X     */
X
X    if (TextPtr->selectFirst == -1) {
X	Tk_OwnSelection(TextPtr->tkwin, TextLostSelection,
X		(ClientData) TextPtr);
X    }
X
X    if (index < 0) {
X	index = 0;
X    }
X    if (index >= TextPtr->numChars) {
X	index = TextPtr->numChars-1;
X    }
X    if (TextPtr->selectAnchor > TextPtr->numChars) {
X	TextPtr->selectAnchor = TextPtr->numChars;
X    }
X    if (TextPtr->selectAnchor <= index) {
X	newFirst = TextPtr->selectAnchor;
X	newLast = index;
X    } else {
X	newFirst = index;
X	newLast = TextPtr->selectAnchor - 1;
X	if (newLast < 0) {
X	    newFirst = newLast = -1;
X	}
X    }
X    if ((TextPtr->selectFirst == newFirst)
X	    && (TextPtr->selectLast == newLast)) {
X	return;
X    }
X    TextPtr->selectFirst = newFirst;
X    TextPtr->selectLast = newLast;
X    TextPtr->flags |= REFRESH_NEEDED;
X    EventuallyRedraw(TextPtr);
X}
X
X/*
X *----------------------------------------------------------------------
X *
X * TextFetchSelection --
X *
X *	This procedure is called back by Tk when the selection is
X *	requested by someone.  It returns part or all of the selection
X *	in a buffer provided by the caller.
X *
X * Results:
X *	The return value is the number of non-NULL bytes stored
X *	at buffer.  Buffer is filled (or partially filled) with a
X *	NULL-terminated string containing part or all of the selection,
X *	as given by offset and maxBytes.
X *
X * Side effects:
X *	None.
X *
X *----------------------------------------------------------------------
X */
X
Xstatic int
XTextFetchSelection(clientData, offset, buffer, maxBytes)
X    ClientData clientData;		/* Information about Text widget. */
X    int offset;				/* Offset within selection of first
X					 * character to be returned. */
X    char *buffer;			/* Location in which to place
X					 * selection. */
X    int maxBytes;			/* Maximum number of bytes to place
X					 * at buffer, not including terminating
X					 * NULL character. */
X{
X    Text *TextPtr = (Text *) clientData;
X    char *text;
X    int count;
X
X    if (TextPtr->selectFirst < 0) {
X	return -1;
X    }
X    count = TextPtr->selectLast + 1 - TextPtr->selectFirst - offset;
X    if (count > maxBytes) {
X	count = maxBytes;
X    }
X    if (count <= 0) {
X	return 0;
X    }
X
X    text = GetText(TextPtr, TextPtr->selectFirst + offset, count);
X
X    if (text != NULL) {
X	strcpy(buffer, text);
X	ckfree(text);
X    } else
X	count = 0;
X
X    return count;
X}
X
X/*
X *----------------------------------------------------------------------
X *
X * TextLostSelection --
X *
X *	This procedure is called back by Tk when the selection is
X *	grabbed away from a Text widget.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	The existing selection is unhighlighted, and the window is
X *	marked as not containing a selection.
X *
X *----------------------------------------------------------------------
X */
X
Xstatic void
XTextLostSelection(clientData)
X    ClientData clientData;		/* Information about Text widget. */
X{
X    Text *TextPtr = (Text *) clientData;
X
X    TextPtr->selectFirst = -1;
X    TextPtr->selectLast = -1;
X    EventuallyRedraw(TextPtr);
X}
X
X/*
X *----------------------------------------------------------------------
X *
X * EventuallyRedraw --
X *
X *	Ensure that a Text is eventually redrawn on the display.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	Information gets redisplayed.  Right now we don't do selective
X *	redisplays:  the whole window will be redrawn.  This doesn't
X *	seem to hurt performance noticeably, but if it does then this
X *	could be changed.
X *
X *----------------------------------------------------------------------
X */
X
Xstatic void
XEventuallyRedraw(TextPtr)
X    register Text *TextPtr;		/* Information about widget. */
X{
X    if ((TextPtr->tkwin == NULL) || !Tk_IsMapped(TextPtr->tkwin)) {
X	return;
X    }
X
X    /*
X     * Right now we don't do selective redisplays:  the whole window
X     * will be redrawn.  This doesn't seem to hurt performance noticeably,
X     * but if it does then this could be changed.
X     */
X
X    if (!(TextPtr->flags & REDRAW_PENDING)) {
X	TextPtr->flags |= REDRAW_PENDING;
X	Tk_DoWhenIdle(DisplayText, (ClientData) TextPtr);
X    }
X}
X
X/*
X *----------------------------------------------------------------------
X *
X * TextUpdateHScrollbar --
X *
X *	This procedure is invoked whenever information has changed in
X *	a Text in a way that would invalidate a scrollbar display.
X *	If there is an associated scrollbar, then this command updates
X *	it by invoking a Tcl command.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	A Tcl command is invoked, and an additional command may be
X *	invoked to process errors in the command.
X *
X *----------------------------------------------------------------------
X */
X
Xstatic void
XTextUpdateHScrollbar(TextPtr)
X    register Text *TextPtr;		/* Information about widget. */
X{
X    char args[100];
X    int result;
X
X    if (TextPtr->scrollHCmd != NULL) {
X	sprintf(args, " %d %d %d %d",
X		TextPtr->lastScrollH.total, TextPtr->lastScrollH.window,
X		TextPtr->lastScrollH.first, TextPtr->lastScrollH.last);
X	result = Tcl_VarEval(TextPtr->interp, TextPtr->scrollHCmd, args,
X			     (char *) NULL);
X	if (result != TCL_OK) {
X	    TkBindError(TextPtr->interp);
X	}
X    }
X}
X
X
X/*
X *----------------------------------------------------------------------
X *
X * TextUpdateVScrollbar --
X *
X *	This procedure is invoked whenever information has changed in
X *	a Text in a way that would invalidate a scrollbar display.
X *	If there is an associated scrollbar, then this command updates
X *	it by invoking a Tcl command.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	A Tcl command is invoked, and an additional command may be
X *	invoked to process errors in the command.
X *
X *----------------------------------------------------------------------
X */
X
Xstatic void
XTextUpdateVScrollbar(TextPtr)
X    register Text *TextPtr;		/* Information about widget. */
X{
X    char args[100];
X    int result;
X
X    if (TextPtr->scrollVCmd != NULL) {
X	ScrollValues scrollValues;
X
X	scrollValues.window = (Tk_Height(TextPtr->tkwin) -
X			       2 * (TextPtr->borderWidth + 1)) /
X				   (TextPtr->fontPtr->ascent +
X				    TextPtr->fontPtr->descent);
X
X	scrollValues.first = TextPtr->topLine->num;
X	scrollValues.last = scrollValues.first + scrollValues.window - 1;
X
X	if (scrollValues.last >= TextPtr->lastLine->num)
X	    scrollValues.last = TextPtr->lastLine->num - 1;
X
X	if (scrollValues.last < 0)
X	    scrollValues.last = 0;
X
X	scrollValues.total = TextPtr->numLines;
X
X	if ((scrollValues.first != TextPtr->lastScrollV.first) ||
X	    (scrollValues.last != TextPtr->lastScrollV.last) ||
X	    (scrollValues.total != TextPtr->lastScrollV.total) ||
X	    (scrollValues.window != TextPtr->lastScrollV.window)) {
X	    sprintf(args, " %d %d %d %d",
X		    scrollValues.total, scrollValues.window,
X		    scrollValues.first, scrollValues.last);
X	    result = Tcl_VarEval(TextPtr->interp, TextPtr->scrollVCmd, args,
X				 (char *) NULL);
X	    if (result != TCL_OK) {
X		TkBindError(TextPtr->interp);
X	    }
X	    TextPtr->lastScrollV = scrollValues;
X	}
X    }
X
X    Tcl_SetResult(TextPtr->interp, (char *) NULL, TCL_STATIC);
X}
X
X/*
X *----------------------------------------------------------------------
X *
X * TextBlinkProc --
X *
X *	This procedure is called as a timer handler to blink the
X *	insertion cursor off and on.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	The cursor gets turned on or off, redisplay gets invoked,
X *	and this procedure reschedules itself.
X *
X *----------------------------------------------------------------------
X */
X
Xstatic void
XTextBlinkProc(clientData)
X    ClientData clientData;	/* Pointer to record describing Text. */
X{
X    register Text *TextPtr = (Text *) clientData;
X
X    if (!(TextPtr->flags & GOT_FOCUS) || (TextPtr->cursorOffTime == 0)) {
X	return;
X    }
X    if (TextPtr->flags & CURSOR_ON) {
X	TextPtr->flags &= ~CURSOR_ON;
X	TextPtr->cursorBlinkHandler = Tk_CreateTimerHandler(
X		TextPtr->cursorOffTime, TextBlinkProc, (ClientData) TextPtr);
X    } else {
X	TextPtr->flags |= CURSOR_ON;
X	TextPtr->cursorBlinkHandler = Tk_CreateTimerHandler(
X		TextPtr->cursorOnTime, TextBlinkProc, (ClientData) TextPtr);
X    }
X    EventuallyRedraw(TextPtr);
X}
X
X/*
X *----------------------------------------------------------------------
X *
X * TextFocusProc --
X *
X *	This procedure is called whenever the Text gets or loses the
X *	input focus.  It's also called whenever the window is reconfigured
X *	while it has the focus.
X *
X * Results:
X *	None.
X *
X * Side effects:
X *	The cursor gets turned on or off.
X *
X *----------------------------------------------------------------------
X */
X
Xstatic void
XTextFocusProc(clientData, gotFocus)
X    ClientData clientData;	/* Pointer to structure describing Text. */
X    int gotFocus;		/* 1 means window is getting focus, 0 means
X				 * it's losing it. */
X{
X    register Text *TextPtr = (Text *) clientData;
X
X    Tk_DeleteTimerHandler(TextPtr->cursorBlinkHandler);
X    if (gotFocus) {
X	TextPtr->flags |= GOT_FOCUS | CURSOR_ON;
X	if (TextPtr->cursorOffTime != 0) {
X	    TextPtr->cursorBlinkHandler = Tk_CreateTimerHandler(
X		    TextPtr->cursorOnTime, TextBlinkProc,
X		    (ClientData) TextPtr);
X	}
X    } else {
X	TextPtr->flags &= ~(GOT_FOCUS | CURSOR_ON);
X	TextPtr->cursorBlinkHandler = (Tk_TimerToken) NULL;
X    }
X    EventuallyRedraw(TextPtr);
X}
END_OF_tkText.c
if test 85975 -ne `wc -c <tkText.c`; then
    echo shar: \"tkText.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0
