head	1.78;
access;
symbols
	core-8-5-9:1.68.2.2
	core-8-5-8:1.68.2.1
	macosx-cocoa-merge:1.72
	macosx-cocoa-premerge:1.72
	macosx-carbon-freeze:1.72
	core-8-5-7:1.68
	core-8-5-6:1.68
	core-8-6-b1:1.72
	core-8-6-a3:1.69
	core-8-5-5:1.68
	core-8-6-a2:1.69
	core-8-5-4:1.68
	core-8-5-3:1.68
	core-8-6-a1:1.69
	core-8-4-19:1.14.2.5
	core-8-5-branch:1.68.0.2
	core-8-5-2:1.68
	core-8-4-18:1.14.2.5
	core-8-5-1:1.68
	core-8-4-17:1.14.2.5
	core-8-5-0:1.68
	core-8-5-b3:1.67
	core-8-5-b2:1.67
	core-8-4-16:1.14.2.5
	core-8-5-b1:1.66
	core-stablilizer-merge:1.64
	core-stabilizer-branch:1.63.0.2
	core-stabilizer-merge:1.68
	core-8-4-15:1.14.2.5
	core-8-5-a6:1.63
	core-8-5-a5:1.60
	core-8-4-14:1.14.2.4
	core-8-5-a4:1.58
	core-8-4-13:1.14.2.2
	core-8-4-12:1.14.2.1
	core-8-4-11:1.14
	core-8-5-a3:1.50
	core-8-4-10:1.14
	core-8-4-9-branch:1.14.0.4
	core-8-5-a2:1.45
	core-8-4-9:1.14
	core-8-4-8:1.14
	core-8-4-7:1.14
	core-8-5-a1:1.40
	core-8-4-6:1.14
	core-8-4-5:1.14
	mistachkin-dev-branch:1.17.0.2
	core-8-4-4:1.14
	core-8-4-3:1.14
	core-8-4-branch:1.14.0.2
	core-8-4-2:1.14
	core-8-4-1:1.13
	core-8-3-5:1.9.2.1
	core-8-4-0:1.13
	macosx-8-4-merge-2002-08-31-trunk:1.13
	macosx-8-4-premerge-2002-08-31-branch:1.9.8.2
	macosx-8-4-premerge-2002-08-31-trunk:1.13
	macosx-8-4-merge-2002-08-30-branch:1.9.8.2
	macosx-8-4-merge-2002-08-30-trunk:1.13
	macosx-8-4-merge-2002-08-20-branch:1.9.8.2
	macosx-8-4-merge-2002-08-20-trunk:1.13
	core-8-4-b2:1.13
	core-8-4-b1:1.12
	macosx-premerge-6-9-02:1.9
	core-8-4-a4:1.9
	core-8-3-4:1.9
	macosx-8-4-branch:1.9.0.8
	macosx-8-4-branchpoint:1.9
	core-8-4-a3:1.9
	core-8-3-3:1.9
	dgp-privates-into-namespace:1.9.0.6
	tclpro-1-5-0:1.9
	core-8-4-win-speedup:1.9.0.4
	core-8-4-a2:1.9
	tclpro-1-4-1:1.9
	tclpro-1-4-0:1.9
	core-8-3-2:1.9
	ajuba-ajuba2-2-1-base:1.9
	ajuba-ajuba2-2-0:1.9
	core-8-4-a1:1.9
	core-8-3-1-branch:1.9.0.2
	core-8-3-1:1.9
	scriptics-sc-2-0-b5:1.9
	scriptics-sc-2-0-fixed:1.9
	scriptics-sc-2-0-b2:1.9
	scriptics-sc-2-0-b1:1.9
	core-8-3-0:1.9
	core-8-3-b2:1.9
	core-8-3-b1:1.8
	scriptics-sc-1-1:1.7
	scriptics-sc-1-1-b1:1.7
	core-8-2-3:1.7
	scriptics-sc-1-1-branch:1.7.0.8
	scriptics-sc-1-1-base:1.7
	scriptics-sc-1-0:1.7
	scriptics-sc-1-0-branch:1.7.0.6
	scriptics-sc-1-0-base:1.7
	core-8-2-2:1.7
	core-8-2-1:1.7
	scriptics-bc-1-0-b1:1.7
	core-8-2-1-branch:1.7.0.4
	scriptics-tclpro-1-3-0:1.7
	core-8-2-0:1.7
	core-8-2-b3-branch:1.7.0.2
	core-8-2-b3-base:1.7
	scriptics-tclpro-1-3-b4:1.6
	core-8-2-b2:1.6
	scriptics-tclpro-1-3-b3:1.6
	core-8-2-b1:1.6
	scriptics-tclpro-1-3-b2:1.6
	merge-to-mainline-5/21/99:1.6
	scriptics-tclpro-1-3-b2-branch:1.6.0.4
	core-8-1-1:1.6
	scriptics-tclpro-1-3-b1:1.6
	core-8-1-1-base:1.6
	core-8-1-0:1.6
	merge-to-mainline-4-29-99:1.6
	merge-to-mainline-4-23-99:1.6
	core-8-1-branch-old:1.1.0.4
	core-8-0-6-branch:1.3.0.2
	core-8-0-6-base:1.3
	core-8-0-5:1.2
	core-8-1-b3:1.1.4.5
	core-8-1-b2:1.1.4.5
	dev-8-1-stubs-branch:1.1.4.4.0.2
	dev-stubs-branch:1.2.0.4
	scriptics-tclpro-1-2:1.2
	scriptics-tclpro-1-2-b2:1.2
	core-8-1-merge-2/10/99:1.2
	core-8-0-5-branch:1.2.0.2
	core-8-0-5-base:1.2
	scriptics-tclpro-1-2-b1:1.2
	scriptics-tclpro-1-2-a1:1.2
	core-8-1-b1:1.1.4.2
	core-8-0-4:1.2
	core-8-1-a2:1.1.4.1
	core-8-0-2:1.1
	core-8-0-3:1.2;
locks; strict;
comment	@ * @;


1.78
date	2010.12.06.10.30.49;	author nijtmans;	state Exp;
branches;
next	1.77;

1.77
date	2010.01.07.15.32.18;	author dkf;	state Exp;
branches;
next	1.76;

1.76
date	2010.01.02.22.52.38;	author dkf;	state Exp;
branches;
next	1.75;

1.75
date	2009.09.07.07.29.04;	author das;	state Exp;
branches;
next	1.74;

1.74
date	2009.08.04.21.19.27;	author dkf;	state Exp;
branches;
next	1.73;

1.73
date	2009.06.30.00.56.29;	author das;	state Exp;
branches;
next	1.72;

1.72
date	2008.12.09.21.22.56;	author dgp;	state Exp;
branches;
next	1.71;

1.71
date	2008.11.08.18.44.40;	author dkf;	state Exp;
branches;
next	1.70;

1.70
date	2008.10.17.23.18.37;	author nijtmans;	state Exp;
branches;
next	1.69;

1.69
date	2008.04.27.22.38.58;	author dkf;	state Exp;
branches;
next	1.68;

1.68
date	2007.12.13.15.24.17;	author dgp;	state Exp;
branches
	1.68.2.1;
next	1.67;

1.67
date	2007.10.12.03.06.08;	author das;	state Exp;
branches;
next	1.66;

1.66
date	2007.09.20.22.59.40;	author dkf;	state Exp;
branches;
next	1.65;

1.65
date	2007.09.07.00.34.54;	author dgp;	state Exp;
branches;
next	1.64;

1.64
date	2007.05.31.13.31.26;	author das;	state Exp;
branches;
next	1.63;

1.63
date	2007.04.23.21.15.19;	author das;	state Exp;
branches
	1.63.2.1;
next	1.62;

1.62
date	2007.02.22.13.56.33;	author dkf;	state Exp;
branches;
next	1.61;

1.61
date	2007.01.11.15.35.39;	author dkf;	state Exp;
branches;
next	1.60;

1.60
date	2006.10.17.23.44.45;	author hobbs;	state Exp;
branches;
next	1.59;

1.59
date	2006.09.10.17.06.32;	author das;	state Exp;
branches;
next	1.58;

1.58
date	2006.03.27.13.43.15;	author cc_benny;	state Exp;
branches;
next	1.57;

1.57
date	2006.03.24.14.58.00;	author das;	state Exp;
branches;
next	1.56;

1.56
date	2006.03.22.00.21.17;	author das;	state Exp;
branches;
next	1.55;

1.55
date	2005.11.27.02.36.14;	author das;	state Exp;
branches;
next	1.54;

1.54
date	2005.11.17.10.57.35;	author dkf;	state Exp;
branches;
next	1.53;

1.53
date	2005.10.10.10.36.35;	author vincentdarley;	state Exp;
branches;
next	1.52;

1.52
date	2005.07.28.17.20.41;	author dkf;	state Exp;
branches;
next	1.51;

1.51
date	2005.07.25.23.56.54;	author dkf;	state Exp;
branches;
next	1.50;

1.50
date	2005.05.13.13.46.12;	author vincentdarley;	state Exp;
branches;
next	1.49;

1.49
date	2005.05.10.21.36.37;	author dgp;	state Exp;
branches;
next	1.48;

1.48
date	2005.03.15.14.07.47;	author vincentdarley;	state Exp;
branches;
next	1.47;

1.47
date	2005.02.14.23.00.44;	author vincentdarley;	state Exp;
branches;
next	1.46;

1.46
date	2005.01.11.16.00.14;	author vincentdarley;	state Exp;
branches;
next	1.45;

1.45
date	2004.10.26.12.51.45;	author dkf;	state Exp;
branches;
next	1.44;

1.44
date	2004.09.10.12.13.41;	author vincentdarley;	state Exp;
branches;
next	1.43;

1.43
date	2004.06.08.20.28.19;	author dgp;	state Exp;
branches;
next	1.42;

1.42
date	2004.06.07.16.23.52;	author vincentdarley;	state Exp;
branches;
next	1.41;

1.41
date	2004.06.04.10.51.18;	author vincentdarley;	state Exp;
branches;
next	1.40;

1.40
date	2004.01.13.02.06.00;	author davygrvy;	state Exp;
branches;
next	1.39;

1.39
date	2004.01.07.16.28.23;	author vincentdarley;	state Exp;
branches;
next	1.38;

1.38
date	2003.12.15.11.51.06;	author vincentdarley;	state Exp;
branches;
next	1.37;

1.37
date	2003.12.05.17.19.06;	author vincentdarley;	state Exp;
branches;
next	1.36;

1.36
date	2003.12.04.12.28.37;	author vincentdarley;	state Exp;
branches;
next	1.35;

1.35
date	2003.11.21.18.51.18;	author vincentdarley;	state Exp;
branches;
next	1.34;

1.34
date	2003.11.21.17.29.13;	author vincentdarley;	state Exp;
branches;
next	1.33;

1.33
date	2003.11.16.14.13.09;	author vincentdarley;	state Exp;
branches;
next	1.32;

1.32
date	2003.11.15.16.57.57;	author vincentdarley;	state Exp;
branches;
next	1.31;

1.31
date	2003.11.15.12.47.15;	author vincentdarley;	state Exp;
branches;
next	1.30;

1.30
date	2003.11.15.02.49.36;	author vincentdarley;	state Exp;
branches;
next	1.29;

1.29
date	2003.11.15.02.33.50;	author vincentdarley;	state Exp;
branches;
next	1.28;

1.28
date	2003.11.14.17.21.03;	author dkf;	state Exp;
branches;
next	1.27;

1.27
date	2003.11.12.17.19.18;	author vincentdarley;	state Exp;
branches;
next	1.26;

1.26
date	2003.11.10.14.37.33;	author vincentdarley;	state Exp;
branches;
next	1.25;

1.25
date	2003.11.08.17.22.46;	author vincentdarley;	state Exp;
branches;
next	1.24;

1.24
date	2003.11.07.15.36.26;	author vincentdarley;	state Exp;
branches;
next	1.23;

1.23
date	2003.11.07.12.06.47;	author vincentdarley;	state Exp;
branches;
next	1.22;

1.22
date	2003.11.03.16.10.12;	author vincentdarley;	state Exp;
branches;
next	1.21;

1.21
date	2003.11.03.13.10.05;	author dkf;	state Exp;
branches;
next	1.20;

1.20
date	2003.10.31.19.14.27;	author vincentdarley;	state Exp;
branches;
next	1.19;

1.19
date	2003.10.31.14.21.49;	author vincentdarley;	state Exp;
branches;
next	1.18;

1.18
date	2003.10.31.09.02.10;	author vincentdarley;	state Exp;
branches;
next	1.17;

1.17
date	2003.07.07.20.39.50;	author hobbs;	state Exp;
branches;
next	1.16;

1.16
date	2003.05.27.15.35.53;	author vincentdarley;	state Exp;
branches;
next	1.15;

1.15
date	2003.05.19.13.04.23;	author vincentdarley;	state Exp;
branches;
next	1.14;

1.14
date	2002.11.22.23.25.19;	author hobbs;	state Exp;
branches
	1.14.2.1;
next	1.13;

1.13
date	2002.08.05.04.30.40;	author dgp;	state Exp;
branches;
next	1.12;

1.12
date	2002.06.15.04.44.02;	author dgp;	state Exp;
branches;
next	1.11;

1.11
date	2002.04.23.00.20.35;	author hobbs;	state Exp;
branches;
next	1.10;

1.10
date	2002.04.22.12.45.49;	author dkf;	state Exp;
branches;
next	1.9;

1.9
date	2000.01.06.02.18.59;	author hobbs;	state Exp;
branches
	1.9.2.1
	1.9.6.1
	1.9.8.1;
next	1.8;

1.8
date	99.12.14.06.52.32;	author hobbs;	state Exp;
branches;
next	1.7;

1.7
date	99.08.10.05.07.36;	author jingham;	state Exp;
branches;
next	1.6;

1.6
date	99.04.21.21.53.28;	author rjohnson;	state Exp;
branches;
next	1.5;

1.5
date	99.04.16.18.30.28;	author surles;	state Exp;
branches;
next	1.4;

1.4
date	99.04.16.01.51.23;	author stanton;	state Exp;
branches
	1.4.2.1;
next	1.3;

1.3
date	99.03.10.07.04.44;	author stanton;	state Exp;
branches;
next	1.2;

1.2
date	98.09.14.18.23.18;	author stanton;	state Exp;
branches
	1.2.4.1;
next	1.1;

1.1
date	98.04.01.09.52.04;	author rjohnson;	state Exp;
branches
	1.1.4.1;
next	;

1.1.4.1
date	98.09.29.00.25.27;	author stanton;	state Exp;
branches;
next	1.1.4.2;

1.1.4.2
date	98.09.30.02.17.23;	author stanton;	state Exp;
branches;
next	1.1.4.3;

1.1.4.3
date	99.01.07.02.42.52;	author lfb;	state Exp;
branches;
next	1.1.4.4;

1.1.4.4
date	99.02.16.11.39.32;	author lfb;	state Exp;
branches
	1.1.4.4.2.1;
next	1.1.4.5;

1.1.4.5
date	99.03.10.07.13.46;	author stanton;	state Exp;
branches;
next	;

1.1.4.4.2.1
date	99.03.09.17.50.35;	author redman;	state Exp;
branches;
next	;

1.2.4.1
date	99.03.06.00.08.07;	author redman;	state Exp;
branches;
next	;

1.4.2.1
date	99.04.21.02.31.53;	author rjohnson;	state Exp;
branches;
next	;

1.9.2.1
date	2002.06.15.03.02.09;	author dgp;	state Exp;
branches;
next	1.9.2.2;

1.9.2.2
date	2002.11.22.23.26.08;	author hobbs;	state Exp;
branches;
next	;

1.9.6.1
date	2001.02.28.23.29.55;	author dgp;	state Exp;
branches;
next	1.9.6.2;

1.9.6.2
date	2001.03.09.00.00.50;	author dgp;	state Exp;
branches;
next	;

1.9.8.1
date	2002.06.10.05.38.24;	author wolfsuit;	state Exp;
branches;
next	1.9.8.2;

1.9.8.2
date	2002.08.20.20.27.08;	author das;	state Exp;
branches;
next	;

1.14.2.1
date	2005.11.27.02.44.26;	author das;	state Exp;
branches;
next	1.14.2.2;

1.14.2.2
date	2006.04.05.19.48.07;	author hobbs;	state Exp;
branches;
next	1.14.2.3;

1.14.2.3
date	2006.09.06.22.01.26;	author hobbs;	state Exp;
branches;
next	1.14.2.4;

1.14.2.4
date	2006.09.10.17.07.35;	author das;	state Exp;
branches;
next	1.14.2.5;

1.14.2.5
date	2007.04.29.02.24.02;	author das;	state Exp;
branches;
next	1.14.2.6;

1.14.2.6
date	2011.01.25.08.49.59;	author nijtmans;	state Exp;
branches;
next	;

1.63.2.1
date	2007.06.01.04.01.32;	author dgp;	state Exp;
branches;
next	1.63.2.2;

1.63.2.2
date	2007.09.07.01.25.36;	author dgp;	state Exp;
branches;
next	1.63.2.3;

1.63.2.3
date	2007.10.02.20.43.53;	author dgp;	state Exp;
branches;
next	1.63.2.4;

1.63.2.4
date	2007.10.15.18.38.33;	author dgp;	state Exp;
branches;
next	;

1.68.2.1
date	2009.08.04.21.46.03;	author dkf;	state Exp;
branches;
next	1.68.2.2;

1.68.2.2
date	2010.01.07.15.35.04;	author dkf;	state Exp;
branches;
next	;


desc
@@


1.78
log
@[Bug 3129527]: Fix buffer overflow w/ GCC 4.5 and -D_FORTIFY_SOURCE=2
@
text
@/*
 * tkTextDisp.c --
 *
 *	This module provides facilities to display text widgets. It is the
 *	only place where information is kept about the screen layout of text
 *	widgets. (Well, strictly, each TkTextLine and B-tree node caches its
 *	last observed pixel height, but that information originates here).
 *
 * Copyright (c) 1992-1994 The Regents of the University of California.
 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.77 2010/01/07 15:32:18 dkf Exp $
 */

#include "tkInt.h"
#include "tkText.h"

#ifdef __WIN32__
#include "tkWinInt.h"
#endif

#ifdef MAC_OSX_TK
#include "tkMacOSXInt.h"
#endif

/*
 * "Calculations of line pixel heights and the size of the vertical
 * scrollbar."
 *
 * Given that tag, font and elide changes can happen to large numbers of
 * diverse chunks in a text widget containing megabytes of text, it is not
 * possible to recalculate all affected height information immediately any
 * such change takes place and maintain a responsive user-experience. Yet, for
 * an accurate vertical scrollbar to be drawn, we must know the total number
 * of vertical pixels shown on display versus the number available to be
 * displayed.
 *
 * The way the text widget solves this problem is by maintaining cached line
 * pixel heights (in the BTree for each logical line), and having asynchronous
 * timer callbacks (i) to iterate through the logical lines recalculating
 * their heights, and (ii) to recalculate the vertical scrollbar's position
 * and size.
 *
 * Typically this works well but there are some situations where the overall
 * functional design of this file causes some problems. These problems can
 * only arise because the calculations used to display lines on screen are not
 * connected to those in the iterating-line- recalculation-process.
 *
 * The reason for this disconnect is that the display calculations operate in
 * display lines, and the iteration and cache operates in logical lines.
 * Given that the display calculations both need not contain complete logical
 * lines (at top or bottom of display), and that they do not actually keep
 * track of logical lines (for simplicity of code and historical design), this
 * means a line may be known and drawn with a different pixel height to that
 * which is cached in the BTree, and this might cause some temporary
 * undesirable mismatch between display and the vertical scrollbar.
 *
 * All such mismatches should be temporary, however, since the asynchronous
 * height calculations will always catch up eventually.
 *
 * For further details see the comments before and within the following
 * functions below: LayoutDLine, AsyncUpdateLineMetrics, GetYView,
 * GetYPixelCount, TkTextUpdateOneLine, TkTextUpdateLineMetrics.
 *
 * For details of the way in which the BTree keeps track of pixel heights, see
 * tkTextBTree.c. Basically the BTree maintains two pieces of information: the
 * logical line indices and the pixel height cache.
 */

/*
 * TK_LAYOUT_WITH_BASE_CHUNKS:
 *
 *	With this macro set, collect all char chunks that have no holes
 *	between them, that are on the same line and use the same font and font
 *	size. Allocate the chars of all these chunks, the so-called "stretch",
 *	in a DString in the first chunk, the so-called "base chunk". Use the
 *	base chunk string for measuring and drawing, so that these actions are
 *	always performed with maximum context.
 *
 *	This is necessary for text rendering engines that provide ligatures
 *	and sub-pixel layout, like ATSU on Mac. If we don't do this, the
 *	measuring will change all the time, leading to an ugly "tremble and
 *	shiver" effect. This is because of the continuous splitting and
 *	re-merging of chunks that goes on in a text widget, when the cursor or
 *	the selection move.
 *
 * Side effects:
 *
 *	Memory management changes. Instead of attaching the character data to
 *	the clientData structures of the char chunks, an additional DString is
 *	used. The collection process will even lead to resizing this DString
 *	for large stretches (> TCL_DSTRING_STATIC_SIZE == 200). We could
 *	reduce the overall memory footprint by copying the result to a plain
 *	char array after the line breaking process, but that would complicate
 *	the code and make performance even worse speedwise. See also TODOs.
 *
 * TODOs:
 *
 *    -	Move the character collection process from the LayoutProc into
 *	LayoutDLine(), so that the collection can be done before actual
 *	layout. In this way measuring can look at the following text, too,
 *	right from the beginning. Memory handling can also be improved with
 *	this. Problem: We don't easily know which chunks are adjacent until
 *	all the other chunks have calculated their width. Apparently marks
 *	would return width==0. A separate char collection loop would have to
 *	know these things.
 *
 *    -	Use a new context parameter to pass the context from LayoutDLine() to
 *	the LayoutProc instead of using a global variable like now. Not
 *	pressing until the previous point gets implemented.
 */

/*
 * The following structure describes how to display a range of characters.
 * The information is generated by scanning all of the tags associated with
 * the characters and combining that with default information for the overall
 * widget. These structures form the hash keys for dInfoPtr->styleTable.
 */

typedef struct StyleValues {
    Tk_3DBorder border;		/* Used for drawing background under text.
				 * NULL means use widget background. */
    int borderWidth;		/* Width of 3-D border for background. */
    int relief;			/* 3-D relief for background. */
    Pixmap bgStipple;		/* Stipple bitmap for background. None means
				 * draw solid. */
    XColor *fgColor;		/* Foreground color for text. */
    Tk_Font tkfont;		/* Font for displaying text. */
    Pixmap fgStipple;		/* Stipple bitmap for text and other
				 * foreground stuff. None means draw solid.*/
    int justify;		/* Justification style for text. */
    int lMargin1;		/* Left margin, in pixels, for first display
				 * line of each text line. */
    int lMargin2;		/* Left margin, in pixels, for second and
				 * later display lines of each text line. */
    int offset;			/* Offset in pixels of baseline, relative to
				 * baseline of line. */
    int overstrike;		/* Non-zero means draw overstrike through
				 * text. */
    int rMargin;		/* Right margin, in pixels. */
    int spacing1;		/* Spacing above first dline in text line. */
    int spacing2;		/* Spacing between lines of dline. */
    int spacing3;		/* Spacing below last dline in text line. */
    TkTextTabArray *tabArrayPtr;/* Locations and types of tab stops (may be
				 * NULL). */
    int tabStyle;		/* One of TABULAR or WORDPROCESSOR. */
    int underline;		/* Non-zero means draw underline underneath
				 * text. */
    int elide;			/* Zero means draw text, otherwise not. */
    TkWrapMode wrapMode;	/* How to handle wrap-around for this tag.
				 * One of TEXT_WRAPMODE_CHAR,
				 * TEXT_WRAPMODE_NONE or TEXT_WRAPMODE_WORD.*/
} StyleValues;

/*
 * The following structure extends the StyleValues structure above with
 * graphics contexts used to actually draw the characters. The entries in
 * dInfoPtr->styleTable point to structures of this type.
 */

typedef struct TextStyle {
    int refCount;		/* Number of times this structure is
				 * referenced in Chunks. */
    GC bgGC;			/* Graphics context for background. None means
				 * use widget background. */
    GC fgGC;			/* Graphics context for foreground. */
    StyleValues *sValuePtr;	/* Raw information from which GCs were
				 * derived. */
    Tcl_HashEntry *hPtr;	/* Pointer to entry in styleTable. Used to
				 * delete entry. */
} TextStyle;

/*
 * The following macro determines whether two styles have the same background
 * so that, for example, no beveled border should be drawn between them.
 */

#define SAME_BACKGROUND(s1, s2) \
    (((s1)->sValuePtr->border == (s2)->sValuePtr->border) \
	&& ((s1)->sValuePtr->borderWidth == (s2)->sValuePtr->borderWidth) \
	&& ((s1)->sValuePtr->relief == (s2)->sValuePtr->relief) \
	&& ((s1)->sValuePtr->bgStipple == (s2)->sValuePtr->bgStipple))

/*
 * The following macro is used to compare two floating-point numbers to within
 * a certain degree of scale. Direct comparison fails on processors where the
 * processor and memory representations of FP numbers of a particular
 * precision is different (e.g. Intel)
 */

#define FP_EQUAL_SCALE(double1, double2, scaleFactor) \
    (fabs((double1)-(double2))*((scaleFactor)+1.0) < 0.3)

/*
 * Macro to make debugging/testing logging a little easier.
 */

#define LOG(toVar,what) \
    Tcl_SetVar2(textPtr->interp, toVar, NULL, (what), \
	    TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT)

/*
 * The following structure describes one line of the display, which may be
 * either part or all of one line of the text.
 */

typedef struct DLine {
    TkTextIndex index;		/* Identifies first character in text that is
				 * displayed on this line. */
    int byteCount;		/* Number of bytes accounted for by this
				 * display line, including a trailing space or
				 * newline that isn't actually displayed. */
    int logicalLinesMerged;	/* Number of extra logical lines merged into
				 * this one due to elided newlines. */
    int y;			/* Y-position at which line is supposed to be
				 * drawn (topmost pixel of rectangular area
				 * occupied by line). */
    int oldY;			/* Y-position at which line currently appears
				 * on display. This is used to move lines by
				 * scrolling rather than re-drawing. If
				 * 'flags' have the OLD_Y_INVALID bit set,
				 * then we will never examine this field
				 * (which means line isn't currently visible
				 * on display and must be redrawn). */
    int height;			/* Height of line, in pixels. */
    int baseline;		/* Offset of text baseline from y, in
				 * pixels. */
    int spaceAbove;		/* How much extra space was added to the top
				 * of the line because of spacing options.
				 * This is included in height and baseline. */
    int spaceBelow;		/* How much extra space was added to the
				 * bottom of the line because of spacing
				 * options. This is included in height. */
    int length;			/* Total length of line, in pixels. */
    TkTextDispChunk *chunkPtr;	/* Pointer to first chunk in list of all of
				 * those that are displayed on this line of
				 * the screen. */
    struct DLine *nextPtr;	/* Next in list of all display lines for this
				 * window. The list is sorted in order from
				 * top to bottom. Note: the next DLine doesn't
				 * always correspond to the next line of text:
				 * (a) can have multiple DLines for one text
				 * line, and (b) can have gaps where DLine's
				 * have been deleted because they're out of
				 * date. */
    int flags;			/* Various flag bits: see below for values. */
} DLine;

/*
 * Flag bits for DLine structures:
 *
 * HAS_3D_BORDER -		Non-zero means that at least one of the chunks
 *				in this line has a 3D border, so it
 *				potentially interacts with 3D borders in
 *				neighboring lines (see DisplayLineBackground).
 * NEW_LAYOUT -			Non-zero means that the line has been
 *				re-layed out since the last time the display
 *				was updated.
 * TOP_LINE -			Non-zero means that this was the top line in
 *				in the window the last time that the window
 *				was laid out. This is important because a line
 *				may be displayed differently if its at the top
 *				or bottom than if it's in the middle
 *				(e.g. beveled edges aren't displayed for
 *				middle lines if the adjacent line has a
 *				similar background).
 * BOTTOM_LINE -		Non-zero means that this was the bottom line
 *				in the window the last time that the window
 *				was laid out.
 * OLD_Y_INVALID -		The value of oldY in the structure is not
 *				valid or useful and should not be examined.
 *				'oldY' is only useful when the DLine is
 *				currently displayed at a different position
 *				and we wish to re-display it via scrolling, so
 *				this means the DLine needs redrawing.
 */

#define HAS_3D_BORDER	1
#define NEW_LAYOUT	2
#define TOP_LINE	4
#define BOTTOM_LINE	8
#define OLD_Y_INVALID  16

/*
 * Overall display information for a text widget:
 */

typedef struct TextDInfo {
    Tcl_HashTable styleTable;	/* Hash table that maps from StyleValues to
				 * TextStyles for this widget. */
    DLine *dLinePtr;		/* First in list of all display lines for this
				 * widget, in order from top to bottom. */
    int topPixelOffset;		/* Identifies first pixel in top display line
				 * to display in window. */
    int newTopPixelOffset;	/* Desired first pixel in top display line to
				 * display in window. */
    GC copyGC;			/* Graphics context for copying from off-
				 * screen pixmaps onto screen. */
    GC scrollGC;		/* Graphics context for copying from one place
				 * in the window to another (scrolling):
				 * differs from copyGC in that we need to get
				 * GraphicsExpose events. */
    int x;			/* First x-coordinate that may be used for
				 * actually displaying line information.
				 * Leaves space for border, etc. */
    int y;			/* First y-coordinate that may be used for
				 * actually displaying line information.
				 * Leaves space for border, etc. */
    int maxX;			/* First x-coordinate to right of available
				 * space for displaying lines. */
    int maxY;			/* First y-coordinate below available space
				 * for displaying lines. */
    int topOfEof;		/* Top-most pixel (lowest y-value) that has
				 * been drawn in the appropriate fashion for
				 * the portion of the window after the last
				 * line of the text. This field is used to
				 * figure out when to redraw part or all of
				 * the eof field. */

    /*
     * Information used for scrolling:
     */

    int newXPixelOffset;	/* Desired x scroll position, measured as the
				 * number of pixels off-screen to the left for
				 * a line with no left margin. */
    int curXPixelOffset;	/* Actual x scroll position, measured as the
				 * number of pixels off-screen to the left. */
    int maxLength;		/* Length in pixels of longest line that's
				 * visible in window (length may exceed window
				 * size). If there's no wrapping, this will be
				 * zero. */
    double xScrollFirst, xScrollLast;
				/* Most recent values reported to horizontal
				 * scrollbar; used to eliminate unnecessary
				 * reports. */
    double yScrollFirst, yScrollLast;
				/* Most recent values reported to vertical
				 * scrollbar; used to eliminate unnecessary
				 * reports. */

    /*
     * The following information is used to implement scanning:
     */

    int scanMarkXPixel;		/* Pixel index of left edge of the window when
				 * the scan started. */
    int scanMarkX;		/* X-position of mouse at time scan started. */
    int scanTotalYScroll;	/* Total scrolling (in screen pixels) that has
				 * occurred since scanMarkY was set. */
    int scanMarkY;		/* Y-position of mouse at time scan started. */

    /*
     * Miscellaneous information:
     */

    int dLinesInvalidated;	/* This value is set to 1 whenever something
				 * happens that invalidates information in
				 * DLine structures; if a redisplay is in
				 * progress, it will see this and abort the
				 * redisplay. This is needed because, for
				 * example, an embedded window could change
				 * its size when it is first displayed,
				 * invalidating the DLine that is currently
				 * being displayed. If redisplay continues, it
				 * will use freed memory and could dump
				 * core. */
    int flags;			/* Various flag values: see below for
				 * definitions. */
    /*
     * Information used to handle the asynchronous updating of the y-scrollbar
     * and the vertical height calculations:
     */

    int lineMetricUpdateEpoch;	/* Stores a number which is incremented each
				 * time the text widget changes in a
				 * significant way (e.g. resizing or
				 * geometry-influencing tag changes). */
    int currentMetricUpdateLine;/* Stores a counter which is used to iterate
				 * over the logical lines contained in the
				 * widget and update their geometry
				 * calculations, if they are out of date. */
    TkTextIndex metricIndex;	/* If the current metric update line wraps
				 * into very many display lines, then this is
				 * used to keep track of what index we've got
				 * to so far... */
    int metricPixelHeight;	/* ...and this is for the height calculation
				 * so far...*/
    int metricEpoch;		/* ...and this for the epoch of the partial
				 * calculation so it can be cancelled if
				 * things change once more. This field will be
				 * -1 if there is no long-line calculation in
				 * progress, and take a non-negative value if
				 * there is such a calculation in progress. */
    int lastMetricUpdateLine;	/* When the current update line reaches this
				 * line, we are done and should stop the
				 * asychronous callback mechanism. */
    Tcl_TimerToken lineUpdateTimer;
				/* A token pointing to the current line metric
				 * update callback. */
    Tcl_TimerToken scrollbarTimer;
				/* A token pointing to the current scrollbar
				 * update callback. */
} TextDInfo;

/*
 * In TkTextDispChunk structures for character segments, the clientData field
 * points to one of the following structures:
 */

#if !TK_LAYOUT_WITH_BASE_CHUNKS

typedef struct CharInfo {
    int numBytes;		/* Number of bytes to display. */
    char chars[1];		/* UTF characters to display. Actual size will
				 * be numBytes, not 1. THIS MUST BE THE LAST
				 * FIELD IN THE STRUCTURE. */
} CharInfo;

#else /* TK_LAYOUT_WITH_BASE_CHUNKS */

typedef struct CharInfo {
    TkTextDispChunk *baseChunkPtr;
    int baseOffset;		/* Starting offset in base chunk
				 * baseChars. */
    int numBytes;		/* Number of bytes that belong to this
				 * chunk. */
    const char *chars;		/* UTF characters to display. Actually points
				 * into the baseChars of the base chunk. Only
				 * valid after FinalizeBaseChunk(). */
} CharInfo;

/*
 * The BaseCharInfo is a CharInfo with some additional data added.
 */

typedef struct BaseCharInfo {
    CharInfo ci;
    Tcl_DString baseChars;	/* Actual characters for the stretch of text
				 * represented by this base chunk. */
    int width;			/* Width in pixels of the whole string, if
				 * known, else -1. Valid during
				 * LayoutDLine(). */
} BaseCharInfo;

/* TODO: Thread safety */
static TkTextDispChunk *baseCharChunkPtr = NULL;

#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */

/*
 * Flag values for TextDInfo structures:
 *
 * DINFO_OUT_OF_DATE:		Non-zero means that the DLine structures for
 *				this window are partially or completely out of
 *				date and need to be recomputed.
 * REDRAW_PENDING:		Means that a when-idle handler has been
 *				scheduled to update the display.
 * REDRAW_BORDERS:		Means window border or pad area has
 *				potentially been damaged and must be redrawn.
 * REPICK_NEEDED:		1 means that the widget has been modified in a
 *				way that could change the current character (a
 *				different character might be under the mouse
 *				cursor now). Need to recompute the current
 *				character before the next redisplay.
 */

#define DINFO_OUT_OF_DATE	1
#define REDRAW_PENDING		2
#define REDRAW_BORDERS		4
#define REPICK_NEEDED		8

/*
 * Action values for FreeDLines:
 *
 * DLINE_FREE:		Free the lines, but no need to unlink them from the
 *			current list of actual display lines.
 * DLINE_UNLINK:	Free and unlink from current display.
 * DLINE_FREE_TEMP:	Free, but don't unlink, and also don't set
 *			'dLinesInvalidated'.
 */

#define DLINE_FREE	  0
#define DLINE_UNLINK	  1
#define DLINE_FREE_TEMP	  2

/*
 * The following counters keep statistics about redisplay that can be checked
 * to see how clever this code is at reducing redisplays.
 */

static int numRedisplays;	/* Number of calls to DisplayText. */
static int linesRedrawn;	/* Number of calls to DisplayDLine. */
static int numCopies;		/* Number of calls to XCopyArea to copy part
				 * of the screen. */
static int lineHeightsRecalculated;
				/* Number of line layouts purely for height
				 * calculation purposes.*/
/*
 * Forward declarations for functions defined later in this file:
 */

static void		AdjustForTab(TkText *textPtr,
			    TkTextTabArray *tabArrayPtr, int index,
			    TkTextDispChunk *chunkPtr);
static void		CharBboxProc(TkText *textPtr,
			    TkTextDispChunk *chunkPtr, int index, int y,
			    int lineHeight, int baseline, int *xPtr,
			    int *yPtr, int *widthPtr, int *heightPtr);
static int		CharChunkMeasureChars(TkTextDispChunk *chunkPtr,
			    const char *chars, int charsLen,
			    int start, int end, int startX, int maxX,
			    int flags, int *nextX);
static void		CharDisplayProc(TkText *textPtr,
			    TkTextDispChunk *chunkPtr, int x, int y,
			    int height, int baseline, Display *display,
			    Drawable dst, int screenY);
static int		CharMeasureProc(TkTextDispChunk *chunkPtr, int x);
static void		CharUndisplayProc(TkText *textPtr,
			    TkTextDispChunk *chunkPtr);
#if TK_LAYOUT_WITH_BASE_CHUNKS
static void		FinalizeBaseChunk(TkTextDispChunk *additionalChunkPtr);
static void		FreeBaseChunk(TkTextDispChunk *baseChunkPtr);
static int		IsSameFGStyle(TextStyle *style1, TextStyle *style2);
static void		RemoveFromBaseChunk(TkTextDispChunk *chunkPtr);
#endif
/*
 * Definitions of elided procs. Compiler can't inline these since we use
 * pointers to these functions. ElideDisplayProc and ElideUndisplayProc are
 * special-cased for speed, as potentially many elided DLine chunks if large,
 * tag toggle-filled elided region.
 */
static void		ElideBboxProc(TkText *textPtr,
			    TkTextDispChunk *chunkPtr, int index, int y,
			    int lineHeight, int baseline, int *xPtr,
			    int *yPtr, int *widthPtr, int *heightPtr);
static int		ElideMeasureProc(TkTextDispChunk *chunkPtr, int x);
static void		DisplayDLine(TkText *textPtr, DLine *dlPtr,
			    DLine *prevPtr, Pixmap pixmap);
static void		DisplayLineBackground(TkText *textPtr, DLine *dlPtr,
			    DLine *prevPtr, Pixmap pixmap);
static void		DisplayText(ClientData clientData);
static DLine *		FindDLine(DLine *dlPtr, const TkTextIndex *indexPtr);
static void		FreeDLines(TkText *textPtr, DLine *firstPtr,
			    DLine *lastPtr, int action);
static void		FreeStyle(TkText *textPtr, TextStyle *stylePtr);
static TextStyle *	GetStyle(TkText *textPtr, const TkTextIndex *indexPtr);
static void		GetXView(Tcl_Interp *interp, TkText *textPtr,
			    int report);
static void		GetYView(Tcl_Interp *interp, TkText *textPtr,
			    int report);
static int		GetYPixelCount(TkText *textPtr, DLine *dlPtr);
static DLine *		LayoutDLine(TkText *textPtr,
			    const TkTextIndex *indexPtr);
static int		MeasureChars(Tk_Font tkfont, const char *source,
			    int maxBytes, int rangeStart, int rangeLength,
			    int startX, int maxX, int flags, int *nextXPtr);
static void		MeasureUp(TkText *textPtr,
			    const TkTextIndex *srcPtr, int distance,
			    TkTextIndex *dstPtr, int *overlap);
static int		NextTabStop(Tk_Font tkfont, int x, int tabOrigin);
static void		UpdateDisplayInfo(TkText *textPtr);
static void		YScrollByLines(TkText *textPtr, int offset);
static void		YScrollByPixels(TkText *textPtr, int offset);
static int		SizeOfTab(TkText *textPtr, int tabStyle,
			    TkTextTabArray *tabArrayPtr, int *indexPtr, int x,
			    int maxX);
static void		TextChanged(TkText *textPtr,
			    const TkTextIndex *index1Ptr,
			    const TkTextIndex *index2Ptr);
static void		TextInvalidateRegion(TkText *textPtr, TkRegion region);
static void		TextRedrawTag(TkText *textPtr,
			    TkTextIndex *index1Ptr, TkTextIndex *index2Ptr,
			    TkTextTag *tagPtr, int withTag);
static void		TextInvalidateLineMetrics(TkText *textPtr,
			    TkTextLine *linePtr, int lineCount, int action);
static int		CalculateDisplayLineHeight(TkText *textPtr,
			    const TkTextIndex *indexPtr, int *byteCountPtr,
			    int *mergedLinePtr);
static void		DlineIndexOfX(TkText *textPtr,
			    DLine *dlPtr, int x, TkTextIndex *indexPtr);
static int		DlineXOfIndex(TkText *textPtr,
			    DLine *dlPtr, int byteIndex);
static int		TextGetScrollInfoObj(Tcl_Interp *interp,
			    TkText *textPtr, int objc,
			    Tcl_Obj *const objv[], double *dblPtr,
			    int *intPtr);
static void		AsyncUpdateLineMetrics(ClientData clientData);
static void		AsyncUpdateYScrollbar(ClientData clientData);

/*
 * Result values returned by TextGetScrollInfoObj:
 */

#define TKTEXT_SCROLL_MOVETO	1
#define TKTEXT_SCROLL_PAGES	2
#define TKTEXT_SCROLL_UNITS	3
#define TKTEXT_SCROLL_ERROR	4
#define TKTEXT_SCROLL_PIXELS	5

/*
 *----------------------------------------------------------------------
 *
 * TkTextCreateDInfo --
 *
 *	This function is called when a new text widget is created. Its job is
 *	to set up display-related information for the widget.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	A TextDInfo data structure is allocated and initialized and attached
 *	to textPtr.
 *
 *----------------------------------------------------------------------
 */

void
TkTextCreateDInfo(
    TkText *textPtr)		/* Overall information for text widget. */
{
    register TextDInfo *dInfoPtr;
    XGCValues gcValues;

    dInfoPtr = (TextDInfo *) ckalloc(sizeof(TextDInfo));
    Tcl_InitHashTable(&dInfoPtr->styleTable, sizeof(StyleValues)/sizeof(int));
    dInfoPtr->dLinePtr = NULL;
    dInfoPtr->copyGC = None;
    gcValues.graphics_exposures = True;
    dInfoPtr->scrollGC = Tk_GetGC(textPtr->tkwin, GCGraphicsExposures,
	    &gcValues);
    dInfoPtr->topOfEof = 0;
    dInfoPtr->newXPixelOffset = 0;
    dInfoPtr->curXPixelOffset = 0;
    dInfoPtr->maxLength = 0;
    dInfoPtr->xScrollFirst = -1;
    dInfoPtr->xScrollLast = -1;
    dInfoPtr->yScrollFirst = -1;
    dInfoPtr->yScrollLast = -1;
    dInfoPtr->scanMarkXPixel = 0;
    dInfoPtr->scanMarkX = 0;
    dInfoPtr->scanTotalYScroll = 0;
    dInfoPtr->scanMarkY = 0;
    dInfoPtr->dLinesInvalidated = 0;
    dInfoPtr->flags = DINFO_OUT_OF_DATE;
    dInfoPtr->topPixelOffset = 0;
    dInfoPtr->newTopPixelOffset = 0;
    dInfoPtr->currentMetricUpdateLine = -1;
    dInfoPtr->lastMetricUpdateLine = -1;
    dInfoPtr->lineMetricUpdateEpoch = 1;
    dInfoPtr->metricEpoch = -1;
    dInfoPtr->metricIndex.textPtr = NULL;
    dInfoPtr->metricIndex.linePtr = NULL;

    /*
     * Add a refCount for each of the idle call-backs.
     */

    textPtr->refCount++;
    dInfoPtr->lineUpdateTimer = Tcl_CreateTimerHandler(0,
	    AsyncUpdateLineMetrics, textPtr);
    textPtr->refCount++;
    dInfoPtr->scrollbarTimer = Tcl_CreateTimerHandler(200,
	    AsyncUpdateYScrollbar, textPtr);

    textPtr->dInfoPtr = dInfoPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextFreeDInfo --
 *
 *	This function is called to free up all of the private display
 *	information kept by this file for a text widget.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Lots of resources get freed.
 *
 *----------------------------------------------------------------------
 */

void
TkTextFreeDInfo(
    TkText *textPtr)		/* Overall information for text widget. */
{
    register TextDInfo *dInfoPtr = textPtr->dInfoPtr;

    /*
     * Be careful to free up styleTable *after* freeing up all the DLines, so
     * that the hash table is still intact to free up the style-related
     * information from the lines. Once the lines are all free then styleTable
     * will be empty.
     */

    FreeDLines(textPtr, dInfoPtr->dLinePtr, NULL, DLINE_UNLINK);
    Tcl_DeleteHashTable(&dInfoPtr->styleTable);
    if (dInfoPtr->copyGC != None) {
	Tk_FreeGC(textPtr->display, dInfoPtr->copyGC);
    }
    Tk_FreeGC(textPtr->display, dInfoPtr->scrollGC);
    if (dInfoPtr->flags & REDRAW_PENDING) {
	Tcl_CancelIdleCall(DisplayText, textPtr);
    }
    if (dInfoPtr->lineUpdateTimer != NULL) {
	Tcl_DeleteTimerHandler(dInfoPtr->lineUpdateTimer);
	textPtr->refCount--;
	dInfoPtr->lineUpdateTimer = NULL;
    }
    if (dInfoPtr->scrollbarTimer != NULL) {
	Tcl_DeleteTimerHandler(dInfoPtr->scrollbarTimer);
	textPtr->refCount--;
	dInfoPtr->scrollbarTimer = NULL;
    }
    ckfree((char *) dInfoPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * GetStyle --
 *
 *	This function creates all the information needed to display text at a
 *	particular location.
 *
 * Results:
 *	The return value is a pointer to a TextStyle structure that
 *	corresponds to *sValuePtr.
 *
 * Side effects:
 *	A new entry may be created in the style table for the widget.
 *
 *----------------------------------------------------------------------
 */

static TextStyle *
GetStyle(
    TkText *textPtr,		/* Overall information about text widget. */
    const TkTextIndex *indexPtr)/* The character in the text for which display
				 * information is wanted. */
{
    TkTextTag **tagPtrs;
    register TkTextTag *tagPtr;
    StyleValues styleValues;
    TextStyle *stylePtr;
    Tcl_HashEntry *hPtr;
    int numTags, isNew, i;
    XGCValues gcValues;
    unsigned long mask;
    /*
     * The variables below keep track of the highest-priority specification
     * that has occurred for each of the various fields of the StyleValues.
     */
    int borderPrio, borderWidthPrio, reliefPrio, bgStipplePrio;
    int fgPrio, fontPrio, fgStipplePrio;
    int underlinePrio, elidePrio, justifyPrio, offsetPrio;
    int lMargin1Prio, lMargin2Prio, rMarginPrio;
    int spacing1Prio, spacing2Prio, spacing3Prio;
    int overstrikePrio, tabPrio, tabStylePrio, wrapPrio;

    /*
     * Find out what tags are present for the character, then compute a
     * StyleValues structure corresponding to those tags (scan through all of
     * the tags, saving information for the highest-priority tag).
     */

    tagPtrs = TkBTreeGetTags(indexPtr, textPtr, &numTags);
    borderPrio = borderWidthPrio = reliefPrio = bgStipplePrio = -1;
    fgPrio = fontPrio = fgStipplePrio = -1;
    underlinePrio = elidePrio = justifyPrio = offsetPrio = -1;
    lMargin1Prio = lMargin2Prio = rMarginPrio = -1;
    spacing1Prio = spacing2Prio = spacing3Prio = -1;
    overstrikePrio = tabPrio = tabStylePrio = wrapPrio = -1;
    memset(&styleValues, 0, sizeof(StyleValues));
    styleValues.relief = TK_RELIEF_FLAT;
    styleValues.fgColor = textPtr->fgColor;
    styleValues.tkfont = textPtr->tkfont;
    styleValues.justify = TK_JUSTIFY_LEFT;
    styleValues.spacing1 = textPtr->spacing1;
    styleValues.spacing2 = textPtr->spacing2;
    styleValues.spacing3 = textPtr->spacing3;
    styleValues.tabArrayPtr = textPtr->tabArrayPtr;
    styleValues.tabStyle = textPtr->tabStyle;
    styleValues.wrapMode = textPtr->wrapMode;
    styleValues.elide = 0;

    for (i = 0 ; i < numTags; i++) {
	Tk_3DBorder border;

	tagPtr = tagPtrs[i];
	border = tagPtr->border;

	/*
	 * If this is the selection tag, and inactiveSelBorder is NULL (the
	 * default on Windows), then we need to skip it if we don't have the
	 * focus.
	 */

	if ((tagPtr == textPtr->selTagPtr) && !(textPtr->flags & GOT_FOCUS)) {
	    if (textPtr->inactiveSelBorder == NULL
#ifdef MAC_OSX_TK
		    /* Don't show inactive selection in disabled widgets. */
		    || textPtr->state == TK_TEXT_STATE_DISABLED
#endif
	    ) {
		continue;
	    }
	    border = textPtr->inactiveSelBorder;
	}

	if ((border != NULL) && (tagPtr->priority > borderPrio)) {
	    styleValues.border = border;
	    borderPrio = tagPtr->priority;
	}
	if ((tagPtr->borderWidthPtr != NULL)
		&& (Tcl_GetString(tagPtr->borderWidthPtr)[0] != '\0')
		&& (tagPtr->priority > borderWidthPrio)) {
	    styleValues.borderWidth = tagPtr->borderWidth;
	    borderWidthPrio = tagPtr->priority;
	}
	if ((tagPtr->reliefString != NULL)
		&& (tagPtr->priority > reliefPrio)) {
	    if (styleValues.border == NULL) {
		styleValues.border = textPtr->border;
	    }
	    styleValues.relief = tagPtr->relief;
	    reliefPrio = tagPtr->priority;
	}
	if ((tagPtr->bgStipple != None)
		&& (tagPtr->priority > bgStipplePrio)) {
	    styleValues.bgStipple = tagPtr->bgStipple;
	    bgStipplePrio = tagPtr->priority;
	}
	if ((tagPtr->fgColor != None) && (tagPtr->priority > fgPrio)) {
	    styleValues.fgColor = tagPtr->fgColor;
	    fgPrio = tagPtr->priority;
	}
	if ((tagPtr->tkfont != None) && (tagPtr->priority > fontPrio)) {
	    styleValues.tkfont = tagPtr->tkfont;
	    fontPrio = tagPtr->priority;
	}
	if ((tagPtr->fgStipple != None)
		&& (tagPtr->priority > fgStipplePrio)) {
	    styleValues.fgStipple = tagPtr->fgStipple;
	    fgStipplePrio = tagPtr->priority;
	}
	if ((tagPtr->justifyString != NULL)
		&& (tagPtr->priority > justifyPrio)) {
	    styleValues.justify = tagPtr->justify;
	    justifyPrio = tagPtr->priority;
	}
	if ((tagPtr->lMargin1String != NULL)
		&& (tagPtr->priority > lMargin1Prio)) {
	    styleValues.lMargin1 = tagPtr->lMargin1;
	    lMargin1Prio = tagPtr->priority;
	}
	if ((tagPtr->lMargin2String != NULL)
		&& (tagPtr->priority > lMargin2Prio)) {
	    styleValues.lMargin2 = tagPtr->lMargin2;
	    lMargin2Prio = tagPtr->priority;
	}
	if ((tagPtr->offsetString != NULL)
		&& (tagPtr->priority > offsetPrio)) {
	    styleValues.offset = tagPtr->offset;
	    offsetPrio = tagPtr->priority;
	}
	if ((tagPtr->overstrikeString != NULL)
		&& (tagPtr->priority > overstrikePrio)) {
	    styleValues.overstrike = tagPtr->overstrike;
	    overstrikePrio = tagPtr->priority;
	}
	if ((tagPtr->rMarginString != NULL)
		&& (tagPtr->priority > rMarginPrio)) {
	    styleValues.rMargin = tagPtr->rMargin;
	    rMarginPrio = tagPtr->priority;
	}
	if ((tagPtr->spacing1String != NULL)
		&& (tagPtr->priority > spacing1Prio)) {
	    styleValues.spacing1 = tagPtr->spacing1;
	    spacing1Prio = tagPtr->priority;
	}
	if ((tagPtr->spacing2String != NULL)
		&& (tagPtr->priority > spacing2Prio)) {
	    styleValues.spacing2 = tagPtr->spacing2;
	    spacing2Prio = tagPtr->priority;
	}
	if ((tagPtr->spacing3String != NULL)
		&& (tagPtr->priority > spacing3Prio)) {
	    styleValues.spacing3 = tagPtr->spacing3;
	    spacing3Prio = tagPtr->priority;
	}
	if ((tagPtr->tabStringPtr != NULL)
		&& (tagPtr->priority > tabPrio)) {
	    styleValues.tabArrayPtr = tagPtr->tabArrayPtr;
	    tabPrio = tagPtr->priority;
	}
	if ((tagPtr->tabStyle != TK_TEXT_TABSTYLE_NONE)
		&& (tagPtr->priority > tabStylePrio)) {
	    styleValues.tabStyle = tagPtr->tabStyle;
	    tabStylePrio = tagPtr->priority;
	}
	if ((tagPtr->underlineString != NULL)
		&& (tagPtr->priority > underlinePrio)) {
	    styleValues.underline = tagPtr->underline;
	    underlinePrio = tagPtr->priority;
	}
	if ((tagPtr->elideString != NULL)
		&& (tagPtr->priority > elidePrio)) {
	    styleValues.elide = tagPtr->elide;
	    elidePrio = tagPtr->priority;
	}
	if ((tagPtr->wrapMode != TEXT_WRAPMODE_NULL)
		&& (tagPtr->priority > wrapPrio)) {
	    styleValues.wrapMode = tagPtr->wrapMode;
	    wrapPrio = tagPtr->priority;
	}
    }
    if (tagPtrs != NULL) {
	ckfree((char *) tagPtrs);
    }

    /*
     * Use an existing style if there's one around that matches.
     */

    hPtr = Tcl_CreateHashEntry(&textPtr->dInfoPtr->styleTable,
	    (char *) &styleValues, &isNew);
    if (!isNew) {
	stylePtr = Tcl_GetHashValue(hPtr);
	stylePtr->refCount++;
	return stylePtr;
    }

    /*
     * No existing style matched. Make a new one.
     */

    stylePtr = (TextStyle *) ckalloc(sizeof(TextStyle));
    stylePtr->refCount = 1;
    if (styleValues.border != NULL) {
	gcValues.foreground = Tk_3DBorderColor(styleValues.border)->pixel;
	mask = GCForeground;
	if (styleValues.bgStipple != None) {
	    gcValues.stipple = styleValues.bgStipple;
	    gcValues.fill_style = FillStippled;
	    mask |= GCStipple|GCFillStyle;
	}
	stylePtr->bgGC = Tk_GetGC(textPtr->tkwin, mask, &gcValues);
    } else {
	stylePtr->bgGC = None;
    }
    mask = GCFont;
    gcValues.font = Tk_FontId(styleValues.tkfont);
    mask |= GCForeground;
    gcValues.foreground = styleValues.fgColor->pixel;
    if (styleValues.fgStipple != None) {
	gcValues.stipple = styleValues.fgStipple;
	gcValues.fill_style = FillStippled;
	mask |= GCStipple|GCFillStyle;
    }
    stylePtr->fgGC = Tk_GetGC(textPtr->tkwin, mask, &gcValues);
    stylePtr->sValuePtr = (StyleValues *)
	    Tcl_GetHashKey(&textPtr->dInfoPtr->styleTable, hPtr);
    stylePtr->hPtr = hPtr;
    Tcl_SetHashValue(hPtr, stylePtr);
    return stylePtr;
}

/*
 *----------------------------------------------------------------------
 *
 * FreeStyle --
 *
 *	This function is called when a TextStyle structure is no longer
 *	needed. It decrements the reference count and frees up the space for
 *	the style structure if the reference count is 0.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The storage and other resources associated with the style are freed up
 *	if no-one's still using it.
 *
 *----------------------------------------------------------------------
 */

static void
FreeStyle(
    TkText *textPtr,		/* Information about overall widget. */
    register TextStyle *stylePtr)
				/* Information about style to free. */
{
    stylePtr->refCount--;
    if (stylePtr->refCount == 0) {
	if (stylePtr->bgGC != None) {
	    Tk_FreeGC(textPtr->display, stylePtr->bgGC);
	}
	if (stylePtr->fgGC != None) {
	    Tk_FreeGC(textPtr->display, stylePtr->fgGC);
	}
	Tcl_DeleteHashEntry(stylePtr->hPtr);
	ckfree((char *) stylePtr);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * LayoutDLine --
 *
 *	This function generates a single DLine structure for a display line
 *	whose leftmost character is given by indexPtr.
 *
 * Results:
 *	The return value is a pointer to a DLine structure desribing the
 *	display line. All fields are filled in and correct except for y and
 *	nextPtr.
 *
 * Side effects:
 *	Storage is allocated for the new DLine.
 *
 *	See the comments in 'GetYView' for some thoughts on what the side-
 *	effects of this call (or its callers) should be; the synchronisation
 *	of TkTextLine->pixelHeight with the sum of the results of this
 *	function operating on all display lines within each logical line.
 *	Ideally the code should be refactored to ensure the cached pixel
 *	height is never behind what is known when this function is called
 *	elsewhere.
 *
 *	Unfortunately, this function is currently called from many different
 *	places, not just to layout a display line for actual display, but also
 *	simply to calculate some metric or other of one or more display lines
 *	(typically the height). It would be a good idea to do some profiling
 *	of typical text widget usage and the way in which this is called and
 *	see if some optimization could or should be done.
 *
 *----------------------------------------------------------------------
 */

static DLine *
LayoutDLine(
    TkText *textPtr,		/* Overall information about text widget. */
    const TkTextIndex *indexPtr)/* Beginning of display line. May not
				 * necessarily point to a character
				 * segment. */
{
    register DLine *dlPtr;	/* New display line. */
    TkTextSegment *segPtr;	/* Current segment in text. */
    TkTextDispChunk *lastChunkPtr;
				/* Last chunk allocated so far for line. */
    TkTextDispChunk *chunkPtr;	/* Current chunk. */
    TkTextIndex curIndex;
    TkTextDispChunk *breakChunkPtr;
				/* Chunk containing best word break point, if
				 * any. */
    TkTextIndex breakIndex;	/* Index of first character in
				 * breakChunkPtr. */
    int breakByteOffset;	/* Byte offset of character within
				 * breakChunkPtr just to right of best break
				 * point. */
    int noCharsYet;		/* Non-zero means that no characters have been
				 * placed on the line yet. */
    int paragraphStart;		/* Non-zero means that we are on the first
				 * line of a paragraph (used to choose between
				 * lmargin1, lmargin2). */
    int justify;		/* How to justify line: taken from style for
				 * the first character in line. */
    int jIndent;		/* Additional indentation (beyond margins) due
				 * to justification. */
    int rMargin;		/* Right margin width for line. */
    TkWrapMode wrapMode;	/* Wrap mode to use for this line. */
    int x = 0, maxX = 0;	/* Initializations needed only to stop
				 * compiler warnings. */
    int wholeLine;		/* Non-zero means this display line runs to
				 * the end of the text line. */
    int tabIndex;		/* Index of the current tab stop. */
    int gotTab;			/* Non-zero means the current chunk contains a
				 * tab. */
    TkTextDispChunk *tabChunkPtr;
				/* Pointer to the chunk containing the
				 * previous tab stop. */
    int maxBytes;		/* Maximum number of bytes to include in this
				 * chunk. */
    TkTextTabArray *tabArrayPtr;/* Tab stops for line; taken from style for
				 * the first character on line. */
    int tabStyle;		/* One of TABULAR or WORDPROCESSOR. */
    int tabSize;		/* Number of pixels consumed by current tab
				 * stop. */
    TkTextDispChunk *lastCharChunkPtr;
				/* Pointer to last chunk in display lines with
				 * numBytes > 0. Used to drop 0-sized chunks
				 * from the end of the line. */
    int byteOffset, ascent, descent, code, elide, elidesize;
    StyleValues *sValuePtr;
    TkTextElideInfo info;	/* Keep track of elide state. */

    /*
     * Create and initialize a new DLine structure.
     */

    dlPtr = (DLine *) ckalloc(sizeof(DLine));
    dlPtr->index = *indexPtr;
    dlPtr->byteCount = 0;
    dlPtr->y = 0;
    dlPtr->oldY = 0;		/* Only set to avoid compiler warnings. */
    dlPtr->height = 0;
    dlPtr->baseline = 0;
    dlPtr->chunkPtr = NULL;
    dlPtr->nextPtr = NULL;
    dlPtr->flags = NEW_LAYOUT | OLD_Y_INVALID;
    dlPtr->logicalLinesMerged = 0;

    /*
     * This is not necessarily totally correct, where we have merged logical
     * lines. Fixing this would require a quite significant overhaul, though,
     * so currently we make do with this.
     */

    paragraphStart = (indexPtr->byteIndex == 0);

    /*
     * Special case entirely elide line as there may be 1000s or more.
     */

    elide = TkTextIsElided(textPtr, indexPtr, &info);
    if (elide && indexPtr->byteIndex == 0) {
	maxBytes = 0;
	for (segPtr = info.segPtr; segPtr != NULL; segPtr = segPtr->nextPtr) {
	    if (segPtr->size > 0) {
		if (elide == 0) {
		    /*
		     * We toggled a tag and the elide state changed to
		     * visible, and we have something of non-zero size.
		     * Therefore we must bail out.
		     */

		    break;
		}
		maxBytes += segPtr->size;

		/*
		 * Reset tag elide priority, since we're on a new character.
		 */

	    } else if ((segPtr->typePtr == &tkTextToggleOffType)
		    || (segPtr->typePtr == &tkTextToggleOnType)) {
		TkTextTag *tagPtr = segPtr->body.toggle.tagPtr;

		/*
		 * The elide state only changes if this tag is either the
		 * current highest priority tag (and is therefore being
		 * toggled off), or it's a new tag with higher priority.
		 */

		if (tagPtr->elideString != NULL) {
		    info.tagCnts[tagPtr->priority]++;
		    if (info.tagCnts[tagPtr->priority] & 1) {
			info.tagPtrs[tagPtr->priority] = tagPtr;
		    }
		    if (tagPtr->priority >= info.elidePriority) {
			if (segPtr->typePtr == &tkTextToggleOffType) {
			    /*
			     * If it is being toggled off, and it has an elide
			     * string, it must actually be the current highest
			     * priority tag, so this check is redundant:
			     */

			    if (tagPtr->priority != info.elidePriority) {
				Tcl_Panic("Bad tag priority being toggled off");
			    }

			    /*
			     * Find previous elide tag, if any (if not then
			     * elide will be zero, of course).
			     */

			    elide = 0;
			    while (--info.elidePriority > 0) {
				if (info.tagCnts[info.elidePriority] & 1) {
				    elide = info.tagPtrs[info.elidePriority]
					    ->elide;
				    break;
				}
			    }
			} else {
			    elide = tagPtr->elide;
			    info.elidePriority = tagPtr->priority;
			}
		    }
		}
	    }
	}

	if (elide) {
	    dlPtr->byteCount = maxBytes;
	    dlPtr->spaceAbove = dlPtr->spaceBelow = dlPtr->length = 0;
	    if (dlPtr->index.byteIndex == 0) {
		/*
		 * Elided state goes from beginning to end of an entire
		 * logical line. This means we can update the line's pixel
		 * height, and bring its pixel calculation up to date.
		 */

		TkBTreeLinePixelEpoch(textPtr, dlPtr->index.linePtr)
			= textPtr->dInfoPtr->lineMetricUpdateEpoch;

		if (TkBTreeLinePixelCount(textPtr,dlPtr->index.linePtr) != 0) {
		    TkBTreeAdjustPixelHeight(textPtr,
			    dlPtr->index.linePtr, 0, 0);
		}
	    }
	    TkTextFreeElideInfo(&info);
	    return dlPtr;
	}
    }
    TkTextFreeElideInfo(&info);

    /*
     * Each iteration of the loop below creates one TkTextDispChunk for the
     * new display line. The line will always have at least one chunk (for the
     * newline character at the end, if there's nothing else available).
     */

    curIndex = *indexPtr;
    lastChunkPtr = NULL;
    chunkPtr = NULL;
    noCharsYet = 1;
    elide = 0;
    breakChunkPtr = NULL;
    breakByteOffset = 0;
    justify = TK_JUSTIFY_LEFT;
    tabIndex = -1;
    tabChunkPtr = NULL;
    tabArrayPtr = NULL;
    tabStyle = TK_TEXT_TABSTYLE_TABULAR;
    rMargin = 0;
    wrapMode = TEXT_WRAPMODE_CHAR;
    tabSize = 0;
    lastCharChunkPtr = NULL;

    /*
     * Find the first segment to consider for the line. Can't call
     * TkTextIndexToSeg for this because it won't return a segment with zero
     * size (such as the insertion cursor's mark).
     */

  connectNextLogicalLine:
    byteOffset = curIndex.byteIndex;
    segPtr = curIndex.linePtr->segPtr;
    while ((byteOffset > 0) && (byteOffset >= segPtr->size)) {
	byteOffset -= segPtr->size;
	segPtr = segPtr->nextPtr;

	if (segPtr == NULL) {
	    /*
	     * Two logical lines merged into one display line through eliding
	     * of a newline.
	     */

	    TkTextLine *linePtr = TkBTreeNextLine(NULL, curIndex.linePtr);
	    if (linePtr == NULL) {
		break;
	    }

	    dlPtr->logicalLinesMerged++;
	    curIndex.byteIndex = 0;
	    curIndex.linePtr = linePtr;
	    segPtr = curIndex.linePtr->segPtr;
	}
    }

    while (segPtr != NULL) {
	/*
	 * Every logical line still gets at least one chunk due to
	 * expectations in the rest of the code, but we are able to skip
	 * elided portions of the line quickly.
	 *
	 * If current chunk is elided and last chunk was too, coalese.
	 *
	 * This also means that each logical line which is entirely elided
	 * still gets laid out into a DLine, but with zero height. This isn't
	 * particularly a problem, but it does seem somewhat unnecessary. We
	 * may wish to redesign the code to remove these zero height DLines in
	 * the future.
	 */

	if (elide && (lastChunkPtr != NULL)
		&& (lastChunkPtr->displayProc == NULL /*ElideDisplayProc*/)) {
	    elidesize = segPtr->size - byteOffset;
	    if (elidesize > 0) {
		curIndex.byteIndex += elidesize;
		lastChunkPtr->numBytes += elidesize;
		breakByteOffset = lastChunkPtr->breakIndex
			= lastChunkPtr->numBytes;

		/*
		 * If have we have a tag toggle, there is a chance that
		 * invisibility state changed, so bail out.
		 */
	    } else if ((segPtr->typePtr == &tkTextToggleOffType)
		    || (segPtr->typePtr == &tkTextToggleOnType)) {
		if (segPtr->body.toggle.tagPtr->elideString != NULL) {
		    elide = (segPtr->typePtr == &tkTextToggleOffType)
			    ^ segPtr->body.toggle.tagPtr->elide;
		}
	    }

	    byteOffset = 0;
	    segPtr = segPtr->nextPtr;

	    if (segPtr == NULL) {
		/*
		 * Two logical lines merged into one display line through
		 * eliding of a newline.
		 */

		TkTextLine *linePtr = TkBTreeNextLine(NULL, curIndex.linePtr);

		if (linePtr != NULL) {
		    dlPtr->logicalLinesMerged++;
		    curIndex.byteIndex = 0;
		    curIndex.linePtr = linePtr;
		    goto connectNextLogicalLine;
		}
	    }

	    /*
	     * Code no longer needed, now that we allow logical lines to merge
	     * into a single display line.
	     *
	    if (segPtr == NULL && chunkPtr != NULL) {
		ckfree((char *) chunkPtr);
		chunkPtr = NULL;
	    }
	     */

	    continue;
	}

	if (segPtr->typePtr->layoutProc == NULL) {
	    segPtr = segPtr->nextPtr;
	    byteOffset = 0;
	    continue;
	}
	if (chunkPtr == NULL) {
	    chunkPtr = (TkTextDispChunk *) ckalloc(sizeof(TkTextDispChunk));
	    chunkPtr->nextPtr = NULL;
	    chunkPtr->clientData = NULL;
	}
	chunkPtr->stylePtr = GetStyle(textPtr, &curIndex);
	elide = chunkPtr->stylePtr->sValuePtr->elide;

	/*
	 * Save style information such as justification and indentation, up
	 * until the first character is encountered, then retain that
	 * information for the rest of the line.
	 */

	if (!elide && noCharsYet) {
	    tabArrayPtr = chunkPtr->stylePtr->sValuePtr->tabArrayPtr;
	    tabStyle = chunkPtr->stylePtr->sValuePtr->tabStyle;
	    justify = chunkPtr->stylePtr->sValuePtr->justify;
	    rMargin = chunkPtr->stylePtr->sValuePtr->rMargin;
	    wrapMode = chunkPtr->stylePtr->sValuePtr->wrapMode;

	    /*
	     * See above - this test may not be entirely correct where we have
	     * partially elided lines (and therefore merged logical lines).
	     * In such a case a byteIndex of zero doesn't necessarily mean the
	     * beginning of a logical line.
	     */

	    if (paragraphStart) {
		/*
		 * Beginning of logical line.
		 */

		x = chunkPtr->stylePtr->sValuePtr->lMargin1;
	    } else {
		/*
		 * Beginning of display line.
		 */

		x = chunkPtr->stylePtr->sValuePtr->lMargin2;
	    }
	    if (wrapMode == TEXT_WRAPMODE_NONE) {
		maxX = -1;
	    } else {
		maxX = textPtr->dInfoPtr->maxX - textPtr->dInfoPtr->x
			- rMargin;
		if (maxX < x) {
		    maxX = x;
		}
	    }
	}

	gotTab = 0;
	maxBytes = segPtr->size - byteOffset;
	if (segPtr->typePtr == &tkTextCharType) {

	    /*
	     * See if there is a tab in the current chunk; if so, only layout
	     * characters up to (and including) the tab.
	     */

	    if (!elide && justify == TK_JUSTIFY_LEFT) {
		char *p;

		for (p = segPtr->body.chars + byteOffset; *p != 0; p++) {
		    if (*p == '\t') {
			maxBytes = (p + 1 - segPtr->body.chars) - byteOffset;
			gotTab = 1;
			break;
		    }
		}
	    }

#if TK_LAYOUT_WITH_BASE_CHUNKS
	    if (baseCharChunkPtr != NULL) {
		int expectedX =
			((BaseCharInfo *) baseCharChunkPtr->clientData)->width
			+ baseCharChunkPtr->x;

		if ((expectedX != x) || !IsSameFGStyle(
			baseCharChunkPtr->stylePtr, chunkPtr->stylePtr)) {
		    FinalizeBaseChunk(NULL);
		}
	    }
#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */
	}
	chunkPtr->x = x;
	if (elide /*&& maxBytes*/) {
	    /*
	     * Don't free style here, as other code expects to be able to do
	     * that.
	     */

	    /* breakByteOffset =*/
	    chunkPtr->breakIndex = chunkPtr->numBytes = maxBytes;
	    chunkPtr->width = 0;
	    chunkPtr->minAscent = chunkPtr->minDescent
		    = chunkPtr->minHeight = 0;

	    /*
	     * Would just like to point to canonical empty chunk.
	     */

	    chunkPtr->displayProc = NULL;
	    chunkPtr->undisplayProc = NULL;
	    chunkPtr->measureProc = ElideMeasureProc;
	    chunkPtr->bboxProc = ElideBboxProc;

	    code = 1;
	} else {
	    code = segPtr->typePtr->layoutProc(textPtr, &curIndex, segPtr,
		    byteOffset, maxX-tabSize, maxBytes, noCharsYet, wrapMode,
		    chunkPtr);
	}
	if (code <= 0) {
	    FreeStyle(textPtr, chunkPtr->stylePtr);
	    if (code < 0) {
		/*
		 * This segment doesn't wish to display itself (e.g. most
		 * marks).
		 */

		segPtr = segPtr->nextPtr;
		byteOffset = 0;
		continue;
	    }

	    /*
	     * No characters from this segment fit in the window: this means
	     * we're at the end of the display line.
	     */

	    if (chunkPtr != NULL) {
		ckfree((char *) chunkPtr);
	    }
	    break;
	}

	/*
	 * We currently say we have some characters (and therefore something
	 * from which to examine tag values for the first character of the
	 * line) even if those characters are actually elided. This behaviour
	 * is not well documented, and it might be more consistent to
	 * completely ignore such elided characters and their tags. To do so
	 * change this to:
	 *
	 * if (!elide && chunkPtr->numBytes > 0).
	 */

	if (!elide && chunkPtr->numBytes > 0) {
	    noCharsYet = 0;
	    lastCharChunkPtr = chunkPtr;
	}
	if (lastChunkPtr == NULL) {
	    dlPtr->chunkPtr = chunkPtr;
	} else {
	    lastChunkPtr->nextPtr = chunkPtr;
	}
	lastChunkPtr = chunkPtr;
	x += chunkPtr->width;
	if (chunkPtr->breakIndex > 0) {
	    breakByteOffset = chunkPtr->breakIndex;
	    breakIndex = curIndex;
	    breakChunkPtr = chunkPtr;
	}
	if (chunkPtr->numBytes != maxBytes) {
	    break;
	}

	/*
	 * If we're at a new tab, adjust the layout for all the chunks
	 * pertaining to the previous tab. Also adjust the amount of space
	 * left in the line to account for space that will be eaten up by the
	 * tab.
	 */

	if (gotTab) {
	    if (tabIndex >= 0) {
		AdjustForTab(textPtr, tabArrayPtr, tabIndex, tabChunkPtr);
		x = chunkPtr->x + chunkPtr->width;
	    }
	    tabChunkPtr = chunkPtr;
	    tabSize = SizeOfTab(textPtr, tabStyle, tabArrayPtr, &tabIndex, x,
		    maxX);
	    if ((maxX >= 0) && (tabSize >= maxX - x)) {
		break;
	    }
	}
	curIndex.byteIndex += chunkPtr->numBytes;
	byteOffset += chunkPtr->numBytes;
	if (byteOffset >= segPtr->size) {
	    byteOffset = 0;
	    segPtr = segPtr->nextPtr;
	    if (elide && segPtr == NULL) {
		/*
		 * An elided section started on this line, and carries on
		 * until the newline. Hence the newline is actually elided,
		 * and we want to merge the display of the next logical line
		 * with this one.
		 */

		TkTextLine *linePtr = TkBTreeNextLine(NULL, curIndex.linePtr);

		if (linePtr != NULL) {
		    dlPtr->logicalLinesMerged++;
		    curIndex.byteIndex = 0;
		    curIndex.linePtr = linePtr;
		    chunkPtr = NULL;
		    goto connectNextLogicalLine;
		}
	    }
	}

	chunkPtr = NULL;
    }
#if TK_LAYOUT_WITH_BASE_CHUNKS
    FinalizeBaseChunk(NULL);
#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */
    if (noCharsYet) {
	dlPtr->spaceAbove = 0;
	dlPtr->spaceBelow = 0;
	dlPtr->length = 0;

	/*
	 * We used to Tcl_Panic here, saying that LayoutDLine couldn't place
	 * any characters on a line, but I believe a more appropriate response
	 * is to return a DLine with zero height. With elided lines, tag
	 * transitions and asynchronous line height calculations, it is hard
	 * to avoid this situation ever arising with the current code design.
	 */

	return dlPtr;
    }
    wholeLine = (segPtr == NULL);

    /*
     * We're at the end of the display line. Throw away everything after the
     * most recent word break, if there is one; this may potentially require
     * the last chunk to be layed out again.
     */

    if (breakChunkPtr == NULL) {
	/*
	 * This code makes sure that we don't accidentally display chunks with
	 * no characters at the end of the line (such as the insertion
	 * cursor). These chunks belong on the next line. So, throw away
	 * everything after the last chunk that has characters in it.
	 */

	breakChunkPtr = lastCharChunkPtr;
	breakByteOffset = breakChunkPtr->numBytes;
    }
    if ((breakChunkPtr != NULL) && ((lastChunkPtr != breakChunkPtr)
	    || (breakByteOffset != lastChunkPtr->numBytes))) {
	while (1) {
	    chunkPtr = breakChunkPtr->nextPtr;
	    if (chunkPtr == NULL) {
		break;
	    }
	    FreeStyle(textPtr, chunkPtr->stylePtr);
	    breakChunkPtr->nextPtr = chunkPtr->nextPtr;
	    if (chunkPtr->undisplayProc != NULL) {
		chunkPtr->undisplayProc(textPtr, chunkPtr);
	    }
	    ckfree((char *) chunkPtr);
	}
	if (breakByteOffset != breakChunkPtr->numBytes) {
	    if (breakChunkPtr->undisplayProc != NULL) {
		breakChunkPtr->undisplayProc(textPtr, breakChunkPtr);
	    }
	    segPtr = TkTextIndexToSeg(&breakIndex, &byteOffset);
	    segPtr->typePtr->layoutProc(textPtr, &breakIndex, segPtr,
		    byteOffset, maxX, breakByteOffset, 0, wrapMode,
		    breakChunkPtr);
#if TK_LAYOUT_WITH_BASE_CHUNKS
	    FinalizeBaseChunk(NULL);
#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */
	}
	lastChunkPtr = breakChunkPtr;
	wholeLine = 0;
    }

    /*
     * Make tab adjustments for the last tab stop, if there is one.
     */

    if ((tabIndex >= 0) && (tabChunkPtr != NULL)) {
	AdjustForTab(textPtr, tabArrayPtr, tabIndex, tabChunkPtr);
    }

    /*
     * Make one more pass over the line to recompute various things like its
     * height, length, and total number of bytes. Also modify the x-locations
     * of chunks to reflect justification. If we're not wrapping, I'm not sure
     * what is the best way to handle left and center justification: should
     * the total length, for purposes of justification, be (a) the window
     * width, (b) the length of the longest line in the window, or (c) the
     * length of the longest line in the text? (c) isn't available, (b) seems
     * weird, since it can change with vertical scrolling, so (a) is what is
     * implemented below.
     */

    if (wrapMode == TEXT_WRAPMODE_NONE) {
	maxX = textPtr->dInfoPtr->maxX - textPtr->dInfoPtr->x - rMargin;
    }
    dlPtr->length = lastChunkPtr->x + lastChunkPtr->width;
    if (justify == TK_JUSTIFY_LEFT) {
	jIndent = 0;
    } else if (justify == TK_JUSTIFY_RIGHT) {
	jIndent = maxX - dlPtr->length;
    } else {
	jIndent = (maxX - dlPtr->length)/2;
    }
    ascent = descent = 0;
    for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL;
	    chunkPtr = chunkPtr->nextPtr) {
	chunkPtr->x += jIndent;
	dlPtr->byteCount += chunkPtr->numBytes;
	if (chunkPtr->minAscent > ascent) {
	    ascent = chunkPtr->minAscent;
	}
	if (chunkPtr->minDescent > descent) {
	    descent = chunkPtr->minDescent;
	}
	if (chunkPtr->minHeight > dlPtr->height) {
	    dlPtr->height = chunkPtr->minHeight;
	}
	sValuePtr = chunkPtr->stylePtr->sValuePtr;
	if ((sValuePtr->borderWidth > 0)
		&& (sValuePtr->relief != TK_RELIEF_FLAT)) {
	    dlPtr->flags |= HAS_3D_BORDER;
	}
    }
    if (dlPtr->height < (ascent + descent)) {
	dlPtr->height = ascent + descent;
	dlPtr->baseline = ascent;
    } else {
	dlPtr->baseline = ascent + (dlPtr->height - ascent - descent)/2;
    }
    sValuePtr = dlPtr->chunkPtr->stylePtr->sValuePtr;
    if (dlPtr->index.byteIndex == 0) {
	dlPtr->spaceAbove = sValuePtr->spacing1;
    } else {
	dlPtr->spaceAbove = sValuePtr->spacing2 - sValuePtr->spacing2/2;
    }
    if (wholeLine) {
	dlPtr->spaceBelow = sValuePtr->spacing3;
    } else {
	dlPtr->spaceBelow = sValuePtr->spacing2/2;
    }
    dlPtr->height += dlPtr->spaceAbove + dlPtr->spaceBelow;
    dlPtr->baseline += dlPtr->spaceAbove;

    /*
     * Recompute line length: may have changed because of justification.
     */

    dlPtr->length = lastChunkPtr->x + lastChunkPtr->width;

    return dlPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * UpdateDisplayInfo --
 *
 *	This function is invoked to recompute some or all of the DLine
 *	structures for a text widget. At the time it is called the DLine
 *	structures still left in the widget are guaranteed to be correct
 *	except that (a) the y-coordinates aren't necessarily correct, (b)
 *	there may be missing structures (the DLine structures get removed as
 *	soon as they are potentially out-of-date), and (c) DLine structures
 *	that don't start at the beginning of a line may be incorrect if
 *	previous information in the same line changed size in a way that moved
 *	a line boundary (DLines for any info that changed will have been
 *	deleted, but not DLines for unchanged info in the same text line).
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Upon return, the DLine information for textPtr correctly reflects the
 *	positions where characters will be displayed. However, this function
 *	doesn't actually bring the display up-to-date.
 *
 *----------------------------------------------------------------------
 */

static void
UpdateDisplayInfo(
    TkText *textPtr)		/* Text widget to update. */
{
    register TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    register DLine *dlPtr, *prevPtr;
    TkTextIndex index;
    TkTextLine *lastLinePtr;
    int y, maxY, xPixelOffset, maxOffset, lineHeight;

    if (!(dInfoPtr->flags & DINFO_OUT_OF_DATE)) {
	return;
    }
    dInfoPtr->flags &= ~DINFO_OUT_OF_DATE;

    /*
     * Delete any DLines that are now above the top of the window.
     */

    index = textPtr->topIndex;
    dlPtr = FindDLine(dInfoPtr->dLinePtr, &index);
    if ((dlPtr != NULL) && (dlPtr != dInfoPtr->dLinePtr)) {
	FreeDLines(textPtr, dInfoPtr->dLinePtr, dlPtr, DLINE_UNLINK);
    }
    if (index.byteIndex == 0) {
	lineHeight = 0;
    } else {
	lineHeight = -1;
    }

    /*
     * Scan through the contents of the window from top to bottom, recomputing
     * information for lines that are missing.
     */

    lastLinePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree, textPtr,
	    TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr));
    dlPtr = dInfoPtr->dLinePtr;
    prevPtr = NULL;
    y = dInfoPtr->y - dInfoPtr->newTopPixelOffset;
    maxY = dInfoPtr->maxY;
    while (1) {
	register DLine *newPtr;

	if (index.linePtr == lastLinePtr) {
	    break;
	}

	/*
	 * There are three possibilities right now:
	 * (a) the next DLine (dlPtr) corresponds exactly to the next
	 *     information we want to display: just use it as-is.
	 * (b) the next DLine corresponds to a different line, or to a segment
	 *     that will be coming later in the same line: leave this DLine
	 *     alone in the hopes that we'll be able to use it later, then
	 *     create a new DLine in front of it.
	 * (c) the next DLine corresponds to a segment in the line we want,
	 *     but it's a segment that has already been processed or will
	 *     never be processed. Delete the DLine and try again.
	 *
	 * One other twist on all this. It's possible for 3D borders to
	 * interact between lines (see DisplayLineBackground) so if a line is
	 * relayed out and has styles with 3D borders, its neighbors have to
	 * be redrawn if they have 3D borders too, since the interactions
	 * could have changed (the neighbors don't have to be relayed out,
	 * just redrawn).
	 */

	if ((dlPtr == NULL) || (dlPtr->index.linePtr != index.linePtr)) {
	    /*
	     * Case (b) -- must make new DLine.
	     */

	makeNewDLine:
	    if (tkTextDebug) {
		char string[TK_POS_CHARS];

		/*
		 * Debugging is enabled, so keep a log of all the lines that
		 * were re-layed out. The test suite uses this information.
		 */

		TkTextPrintIndex(textPtr, &index, string);
		LOG("tk_textRelayout", string);
	    }
	    newPtr = LayoutDLine(textPtr, &index);
	    if (prevPtr == NULL) {
		dInfoPtr->dLinePtr = newPtr;
	    } else {
		prevPtr->nextPtr = newPtr;
		if (prevPtr->flags & HAS_3D_BORDER) {
		    prevPtr->flags |= OLD_Y_INVALID;
		}
	    }
	    newPtr->nextPtr = dlPtr;
	    dlPtr = newPtr;
	} else {
	    /*
	     * DlPtr refers to the line we want. Next check the index within
	     * the line.
	     */

	    if (index.byteIndex == dlPtr->index.byteIndex) {
		/*
		 * Case (a) - can use existing display line as-is.
		 */

		if ((dlPtr->flags & HAS_3D_BORDER) && (prevPtr != NULL)
			&& (prevPtr->flags & (NEW_LAYOUT))) {
		    dlPtr->flags |= OLD_Y_INVALID;
		}
		goto lineOK;
	    }
	    if (index.byteIndex < dlPtr->index.byteIndex) {
		goto makeNewDLine;
	    }

	    /*
	     * Case (c) - dlPtr is useless. Discard it and start again with
	     * the next display line.
	     */

	    newPtr = dlPtr->nextPtr;
	    FreeDLines(textPtr, dlPtr, newPtr, DLINE_FREE);
	    dlPtr = newPtr;
	    if (prevPtr != NULL) {
		prevPtr->nextPtr = newPtr;
	    } else {
		dInfoPtr->dLinePtr = newPtr;
	    }
	    continue;
	}

	/*
	 * Advance to the start of the next line.
	 */

    lineOK:
	dlPtr->y = y;
	y += dlPtr->height;
	if (lineHeight != -1) {
	    lineHeight += dlPtr->height;
	}
	TkTextIndexForwBytes(textPtr, &index, dlPtr->byteCount, &index);
	prevPtr = dlPtr;
	dlPtr = dlPtr->nextPtr;

	/*
	 * If we switched text lines, delete any DLines left for the old text
	 * line.
	 */

	if (index.linePtr != prevPtr->index.linePtr) {
	    register DLine *nextPtr;

	    nextPtr = dlPtr;
	    while ((nextPtr != NULL)
		    && (nextPtr->index.linePtr == prevPtr->index.linePtr)) {
		nextPtr = nextPtr->nextPtr;
	    }
	    if (nextPtr != dlPtr) {
		FreeDLines(textPtr, dlPtr, nextPtr, DLINE_FREE);
		prevPtr->nextPtr = nextPtr;
		dlPtr = nextPtr;
	    }

	    if ((lineHeight != -1) && (TkBTreeLinePixelCount(textPtr,
		    prevPtr->index.linePtr) != lineHeight)) {
		/*
		 * The logical line height we just calculated is actually
		 * differnt to the currently cached height of the text line.
		 * That is fine (the text line heights are only calculated
		 * asynchronously), but we must update the cached height so
		 * that any counts made with DLine pointers are the same as
		 * counts made through the BTree. This helps to ensure that
		 * the scrollbar size corresponds accurately to that displayed
		 * contents, even as the window is re-sized.
		 */

		TkBTreeAdjustPixelHeight(textPtr, prevPtr->index.linePtr,
			lineHeight, 0);

		/*
		 * I believe we can be 100% sure that we started at the
		 * beginning of the logical line, so we can also adjust the
		 * 'pixelCalculationEpoch' to mark it as being up to date.
		 * There is a slight concern that we might not have got this
		 * right for the first line in the re-display.
		 */

		TkBTreeLinePixelEpoch(textPtr, prevPtr->index.linePtr) =
			dInfoPtr->lineMetricUpdateEpoch;
	    }
	    lineHeight = 0;
	}

	/*
	 * It's important to have the following check here rather than in the
	 * while statement for the loop, so that there's always at least one
	 * DLine generated, regardless of how small the window is. This keeps
	 * a lot of other code from breaking.
	 */

	if (y >= maxY) {
	    break;
	}
    }

    /*
     * Delete any DLine structures that don't fit on the screen.
     */

    FreeDLines(textPtr, dlPtr, NULL, DLINE_UNLINK);

    /*
     * If there is extra space at the bottom of the window (because we've hit
     * the end of the text), then bring in more lines at the top of the
     * window, if there are any, to fill in the view.
     *
     * Since the top line may only be partially visible, we try first to
     * simply show more pixels from that line (newTopPixelOffset). If that
     * isn't enough, we have to layout more lines.
     */

    if (y < maxY) {
	/*
	 * This counts how many vertical pixels we have left to fill by
	 * pulling in more display pixels either from the first currently
	 * displayed, or the lines above it.
	 */

	int spaceLeft = maxY - y;

	if (spaceLeft <= dInfoPtr->newTopPixelOffset) {
	    /*
	     * We can full up all the needed space just by showing more of the
	     * current top line.
	     */

	    dInfoPtr->newTopPixelOffset -= spaceLeft;
	    y += spaceLeft;
	    spaceLeft = 0;
	} else {
	    int lineNum, bytesToCount;
	    DLine *lowestPtr;

	    /*
	     * Add in all of the current top line, which won't be enough to
	     * bring y up to maxY (if it was we would be in the 'if' block
	     * above).
	     */

	    y += dInfoPtr->newTopPixelOffset;
	    dInfoPtr->newTopPixelOffset = 0;

	    /*
	     * Layout an entire text line (potentially > 1 display line), then
	     * link in as many display lines as fit without moving the bottom
	     * line out of the window. Repeat this until all the extra space
	     * has been used up or we've reached the beginning of the text.
	     */

	    spaceLeft = maxY - y;
	    if (dInfoPtr->dLinePtr == NULL) {
		/*
		 * No lines have been laid out. This must be an empty peer
		 * widget.
		 */

		lineNum = -1;
		bytesToCount = 0;	/* Stop compiler warning. */
	    } else {
		lineNum = TkBTreeLinesTo(textPtr,
			dInfoPtr->dLinePtr->index.linePtr);
		bytesToCount = dInfoPtr->dLinePtr->index.byteIndex;
		if (bytesToCount == 0) {
		    bytesToCount = INT_MAX;
		    lineNum--;
		}
	    }
	    for ( ; (lineNum >= 0) && (spaceLeft > 0); lineNum--) {
		int pixelHeight = 0;

		index.linePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree,
			textPtr, lineNum);
		index.byteIndex = 0;
		lowestPtr = NULL;

		do {
		    dlPtr = LayoutDLine(textPtr, &index);
		    pixelHeight += dlPtr->height;
		    dlPtr->nextPtr = lowestPtr;
		    lowestPtr = dlPtr;
		    if (dlPtr->length == 0 && dlPtr->height == 0) {
			bytesToCount--;
			break;
		    }	/* elide */
		    TkTextIndexForwBytes(textPtr, &index, dlPtr->byteCount,
			    &index);
		    bytesToCount -= dlPtr->byteCount;
		} while ((bytesToCount > 0)
			&& (index.linePtr == lowestPtr->index.linePtr));

		/*
		 * We may not have examined the entire line (depending on the
		 * value of 'bytesToCount', so we only want to set this if it
		 * is genuinely bigger).
		 */

		if (pixelHeight > TkBTreeLinePixelCount(textPtr,
			lowestPtr->index.linePtr)) {
		    TkBTreeAdjustPixelHeight(textPtr,
			    lowestPtr->index.linePtr, pixelHeight, 0);
		    if (index.linePtr != lowestPtr->index.linePtr) {
			/*
			 * We examined the entire line, so can update the
			 * epoch.
			 */

			TkBTreeLinePixelEpoch(textPtr,
				lowestPtr->index.linePtr) =
				dInfoPtr->lineMetricUpdateEpoch;
		    }
		}

		/*
		 * Scan through the display lines from the bottom one up to
		 * the top one.
		 */

		while (lowestPtr != NULL) {
		    dlPtr = lowestPtr;
		    spaceLeft -= dlPtr->height;
		    lowestPtr = dlPtr->nextPtr;
		    dlPtr->nextPtr = dInfoPtr->dLinePtr;
		    dInfoPtr->dLinePtr = dlPtr;
		    if (tkTextDebug) {
			char string[TK_POS_CHARS];

			TkTextPrintIndex(textPtr, &dlPtr->index, string);
			LOG("tk_textRelayout", string);
		    }
		    if (spaceLeft <= 0) {
			break;
		    }
		}
		FreeDLines(textPtr, lowestPtr, NULL, DLINE_FREE);
		bytesToCount = INT_MAX;
	    }

	    /*
	     * We've either filled in the space we wanted to or we've run out
	     * of display lines at the top of the text. Note that we already
	     * set dInfoPtr->newTopPixelOffset to zero above.
	     */

	    if (spaceLeft < 0) {
		/*
		 * We've laid out a few too many vertical pixels at or above
		 * the first line. Therefore we only want to show part of the
		 * first displayed line, so that the last displayed line just
		 * fits in the window.
		 */

		dInfoPtr->newTopPixelOffset = -spaceLeft;
		if (dInfoPtr->newTopPixelOffset>=dInfoPtr->dLinePtr->height) {
		    /*
		     * Somehow the entire first line we laid out is shorter
		     * than the new offset. This should not occur and would
		     * indicate a bad problem in the logic above.
		     */

		    Tcl_Panic("Error in pixel height consistency while filling in spacesLeft");
		}
	    }
	}

	/*
	 * Now we're all done except that the y-coordinates in all the DLines
	 * are wrong and the top index for the text is wrong. Update them.
	 */

	if (dInfoPtr->dLinePtr != NULL) {
	    textPtr->topIndex = dInfoPtr->dLinePtr->index;
	    y = dInfoPtr->y - dInfoPtr->newTopPixelOffset;
	    for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
		    dlPtr = dlPtr->nextPtr) {
		if (y > dInfoPtr->maxY) {
		    Tcl_Panic("Added too many new lines in UpdateDisplayInfo");
		}
		dlPtr->y = y;
		y += dlPtr->height;
	    }
	}
    }

    /*
     * If the old top or bottom line has scrolled elsewhere on the screen, we
     * may not be able to re-use its old contents by copying bits (e.g., a
     * beveled edge that was drawn when it was at the top or bottom won't be
     * drawn when the line is in the middle and its neighbor has a matching
     * background). Similarly, if the new top or bottom line came from
     * somewhere else on the screen, we may not be able to copy the old bits.
     */

    dlPtr = dInfoPtr->dLinePtr;
    if (dlPtr != NULL) {
	if ((dlPtr->flags & HAS_3D_BORDER) && !(dlPtr->flags & TOP_LINE)) {
	    dlPtr->flags |= OLD_Y_INVALID;
	}
	while (1) {
	    if ((dlPtr->flags & TOP_LINE) && (dlPtr != dInfoPtr->dLinePtr)
		    && (dlPtr->flags & HAS_3D_BORDER)) {
		dlPtr->flags |= OLD_Y_INVALID;
	    }

	    /*
	     * If the old top-line was not completely showing (i.e. the
	     * pixelOffset is non-zero) and is no longer the top-line, then we
	     * must re-draw it.
	     */

	    if ((dlPtr->flags & TOP_LINE) &&
		    dInfoPtr->topPixelOffset!=0 && dlPtr!=dInfoPtr->dLinePtr) {
		dlPtr->flags |= OLD_Y_INVALID;
	    }
	    if ((dlPtr->flags & BOTTOM_LINE) && (dlPtr->nextPtr != NULL)
		    && (dlPtr->flags & HAS_3D_BORDER)) {
		dlPtr->flags |= OLD_Y_INVALID;
	    }
	    if (dlPtr->nextPtr == NULL) {
		if ((dlPtr->flags & HAS_3D_BORDER)
			&& !(dlPtr->flags & BOTTOM_LINE)) {
		    dlPtr->flags |= OLD_Y_INVALID;
		}
		dlPtr->flags &= ~TOP_LINE;
		dlPtr->flags |= BOTTOM_LINE;
		break;
	    }
	    dlPtr->flags &= ~(TOP_LINE|BOTTOM_LINE);
	    dlPtr = dlPtr->nextPtr;
	}
	dInfoPtr->dLinePtr->flags |= TOP_LINE;
	dInfoPtr->topPixelOffset = dInfoPtr->newTopPixelOffset;
    }

    /*
     * Arrange for scrollbars to be updated.
     */

    textPtr->flags |= UPDATE_SCROLLBARS;

    /*
     * Deal with horizontal scrolling:
     * 1. If there's empty space to the right of the longest line, shift the
     *	  screen to the right to fill in the empty space.
     * 2. If the desired horizontal scroll position has changed, force a full
     *	  redisplay of all the lines in the widget.
     * 3. If the wrap mode isn't "none" then re-scroll to the base position.
     */

    dInfoPtr->maxLength = 0;
    for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
	    dlPtr = dlPtr->nextPtr) {
	if (dlPtr->length > dInfoPtr->maxLength) {
	    dInfoPtr->maxLength = dlPtr->length;
	}
    }
    maxOffset = dInfoPtr->maxLength - (dInfoPtr->maxX - dInfoPtr->x);

    xPixelOffset = dInfoPtr->newXPixelOffset;
    if (xPixelOffset > maxOffset) {
	xPixelOffset = maxOffset;
    }
    if (xPixelOffset < 0) {
	xPixelOffset = 0;
    }

    /*
     * Here's a problem: see the tests textDisp-29.2.1-4
     *
     * If the widget is being created, but has not yet been configured it will
     * have a maxY of 1 above, and we we won't have examined all the lines
     * (just the first line, in fact), and so maxOffset will not be a true
     * reflection of the widget's lines. Therefore we must not overwrite the
     * original newXPixelOffset in this case.
     */

    if (!(((Tk_FakeWin *) (textPtr->tkwin))->flags & TK_NEED_CONFIG_NOTIFY)) {
	dInfoPtr->newXPixelOffset = xPixelOffset;
    }

    if (xPixelOffset != dInfoPtr->curXPixelOffset) {
	dInfoPtr->curXPixelOffset = xPixelOffset;
	for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
		dlPtr = dlPtr->nextPtr) {
	    dlPtr->flags |= OLD_Y_INVALID;
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * FreeDLines --
 *
 *	This function is called to free up all of the resources associated
 *	with one or more DLine structures.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Memory gets freed and various other resources are released.
 *
 *----------------------------------------------------------------------
 */

static void
FreeDLines(
    TkText *textPtr,		/* Information about overall text widget. */
    register DLine *firstPtr,	/* Pointer to first DLine to free up. */
    DLine *lastPtr,		/* Pointer to DLine just after last one to
				 * free (NULL means everything starting with
				 * firstPtr). */
    int action)			/* DLINE_UNLINK means DLines are currently
				 * linked into the list rooted at
				 * textPtr->dInfoPtr->dLinePtr and they have
				 * to be unlinked. DLINE_FREE means just free
				 * without unlinking. DLINE_FREE_TEMP means
				 * the DLine given is just a temporary one and
				 * we shouldn't invalidate anything for the
				 * overall widget. */
{
    register TkTextDispChunk *chunkPtr, *nextChunkPtr;
    register DLine *nextDLinePtr;

    if (action == DLINE_FREE_TEMP) {
	lineHeightsRecalculated++;
	if (tkTextDebug) {
	    char string[TK_POS_CHARS];

	    /*
	     * Debugging is enabled, so keep a log of all the lines whose
	     * height was recalculated. The test suite uses this information.
	     */

	    TkTextPrintIndex(textPtr, &firstPtr->index, string);
	    LOG("tk_textHeightCalc", string);
	}
    } else if (action == DLINE_UNLINK) {
	if (textPtr->dInfoPtr->dLinePtr == firstPtr) {
	    textPtr->dInfoPtr->dLinePtr = lastPtr;
	} else {
	    register DLine *prevPtr;

	    for (prevPtr = textPtr->dInfoPtr->dLinePtr;
		    prevPtr->nextPtr != firstPtr; prevPtr = prevPtr->nextPtr) {
		/* Empty loop body. */
	    }
	    prevPtr->nextPtr = lastPtr;
	}
    }
    while (firstPtr != lastPtr) {
	nextDLinePtr = firstPtr->nextPtr;
	for (chunkPtr = firstPtr->chunkPtr; chunkPtr != NULL;
		chunkPtr = nextChunkPtr) {
	    if (chunkPtr->undisplayProc != NULL) {
		chunkPtr->undisplayProc(textPtr, chunkPtr);
	    }
	    FreeStyle(textPtr, chunkPtr->stylePtr);
	    nextChunkPtr = chunkPtr->nextPtr;
	    ckfree((char *) chunkPtr);
	}
	ckfree((char *) firstPtr);
	firstPtr = nextDLinePtr;
    }
    if (action != DLINE_FREE_TEMP) {
	textPtr->dInfoPtr->dLinesInvalidated = 1;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * DisplayDLine --
 *
 *	This function is invoked to draw a single line on the screen.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The line given by dlPtr is drawn at its correct position in textPtr's
 *	window. Note that this is one *display* line, not one *text* line.
 *
 *----------------------------------------------------------------------
 */

static void
DisplayDLine(
    TkText *textPtr,		/* Text widget in which to draw line. */
    register DLine *dlPtr,	/* Information about line to draw. */
    DLine *prevPtr,		/* Line just before one to draw, or NULL if
				 * dlPtr is the top line. */
    Pixmap pixmap)		/* Pixmap to use for double-buffering. Caller
				 * must make sure it's large enough to hold
				 * line. */
{
    register TkTextDispChunk *chunkPtr;
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    Display *display;
    int height, y_off;
#ifndef TK_NO_DOUBLE_BUFFERING
    const int y = 0;
#else
    const int y = dlPtr->y;
#endif /* TK_NO_DOUBLE_BUFFERING */

    if (dlPtr->chunkPtr == NULL) return;

    display = Tk_Display(textPtr->tkwin);

    height = dlPtr->height;
    if ((height + dlPtr->y) > dInfoPtr->maxY) {
	height = dInfoPtr->maxY - dlPtr->y;
    }
    if (dlPtr->y < dInfoPtr->y) {
	y_off = dInfoPtr->y - dlPtr->y;
	height -= y_off;
    } else {
	y_off = 0;
    }

#ifdef TK_NO_DOUBLE_BUFFERING
    TkpClipDrawableToRect(display, pixmap, dInfoPtr->x, y + y_off,
	    dInfoPtr->maxX - dInfoPtr->x, height);
#endif /* TK_NO_DOUBLE_BUFFERING */

    /*
     * First, clear the area of the line to the background color for the text
     * widget.
     */

    Tk_Fill3DRectangle(textPtr->tkwin, pixmap, textPtr->border, 0, y,
	    Tk_Width(textPtr->tkwin), dlPtr->height, 0, TK_RELIEF_FLAT);

    /*
     * Next, draw background information for the whole line.
     */

    DisplayLineBackground(textPtr, dlPtr, prevPtr, pixmap);

    /*
     * Make another pass through all of the chunks to redraw the insertion
     * cursor, if it is visible on this line. Must do it here rather than in
     * the foreground pass below because otherwise a wide insertion cursor
     * will obscure the character to its left.
     */

    if (textPtr->state == TK_TEXT_STATE_NORMAL) {
	for (chunkPtr = dlPtr->chunkPtr; (chunkPtr != NULL);
		chunkPtr = chunkPtr->nextPtr) {
	    if (chunkPtr->displayProc == TkTextInsertDisplayProc) {
		int x = chunkPtr->x + dInfoPtr->x - dInfoPtr->curXPixelOffset;

		chunkPtr->displayProc(textPtr, chunkPtr, x,
			y + dlPtr->spaceAbove,
			dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
			dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
			dlPtr->y + dlPtr->spaceAbove);
	    }
	}
    }

    /*
     * Make yet another pass through all of the chunks to redraw all of
     * foreground information. Note: we have to call the displayProc even for
     * chunks that are off-screen. This is needed, for example, so that
     * embedded windows can be unmapped in this case.
     */

    for (chunkPtr = dlPtr->chunkPtr; (chunkPtr != NULL);
	    chunkPtr = chunkPtr->nextPtr) {
	if (chunkPtr->displayProc == TkTextInsertDisplayProc) {
	    /*
	     * Already displayed the insertion cursor above. Don't do it again
	     * here.
	     */

	    continue;
	}

	/*
	 * Don't call if elide. This tax OK since not very many visible DLines
	 * in an area, but potentially many elide ones.
	 */

	if (chunkPtr->displayProc != NULL) {
	    int x = chunkPtr->x + dInfoPtr->x - dInfoPtr->curXPixelOffset;

	    if ((x + chunkPtr->width <= 0) || (x >= dInfoPtr->maxX)) {
		/*
		 * Note: we have to call the displayProc even for chunks that
		 * are off-screen. This is needed, for example, so that
		 * embedded windows can be unmapped in this case. Display the
		 * chunk at a coordinate that can be clearly identified by the
		 * displayProc as being off-screen to the left (the
		 * displayProc may not be able to tell if something is off to
		 * the right).
		 */

		x = -chunkPtr->width;
	    }
	    chunkPtr->displayProc(textPtr, chunkPtr, x,
		    y + dlPtr->spaceAbove, dlPtr->height - dlPtr->spaceAbove -
		    dlPtr->spaceBelow, dlPtr->baseline - dlPtr->spaceAbove,
		    display, pixmap, dlPtr->y + dlPtr->spaceAbove);
	}

	if (dInfoPtr->dLinesInvalidated) {
	    return;
	}
    }

#ifndef TK_NO_DOUBLE_BUFFERING
    /*
     * Copy the pixmap onto the screen. If this is the first or last line on
     * the screen then copy a piece of the line, so that it doesn't overflow
     * into the border area. Another special trick: copy the padding area to
     * the left of the line; this is because the insertion cursor sometimes
     * overflows onto that area and we want to get as much of the cursor as
     * possible.
     */

    XCopyArea(display, pixmap, Tk_WindowId(textPtr->tkwin), dInfoPtr->copyGC,
	    dInfoPtr->x, y + y_off, (unsigned) (dInfoPtr->maxX - dInfoPtr->x),
	    (unsigned) height, dInfoPtr->x, dlPtr->y + y_off);
#else
    TkpClipDrawableToRect(display, pixmap, 0, 0, -1, -1);
#endif /* TK_NO_DOUBLE_BUFFERING */
    linesRedrawn++;
}

/*
 *--------------------------------------------------------------
 *
 * DisplayLineBackground --
 *
 *	This function is called to fill in the background for a display line.
 *	It draws 3D borders cleverly so that adjacent chunks with the same
 *	style (whether on the same line or different lines) have a single 3D
 *	border around the whole region.
 *
 * Results:
 *	There is no return value. Pixmap is filled in with background
 *	information for dlPtr.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */

static void
DisplayLineBackground(
    TkText *textPtr,		/* Text widget containing line. */
    register DLine *dlPtr,	/* Information about line to draw. */
    DLine *prevPtr,		/* Line just above dlPtr, or NULL if dlPtr is
				 * the top-most line in the window. */
    Pixmap pixmap)		/* Pixmap to use for double-buffering. Caller
				 * must make sure it's large enough to hold
				 * line. Caller must also have filled it with
				 * the background color for the widget. */
{
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    TkTextDispChunk *chunkPtr;	/* Pointer to chunk in the current line. */
    TkTextDispChunk *chunkPtr2;	/* Pointer to chunk in the line above or below
				 * the current one. NULL if we're to the left
				 * of or to the right of the chunks in the
				 * line. */
    TkTextDispChunk *nextPtr2;	/* Next chunk after chunkPtr2 (it's not the
				 * same as chunkPtr2->nextPtr in the case
				 * where chunkPtr2 is NULL because the line is
				 * indented). */
    int leftX;			/* The left edge of the region we're currently
				 * working on. */
    int leftXIn;		/* 1 means beveled edge at leftX slopes right
				 * as it goes down, 0 means it slopes left as
				 * it goes down. */
    int rightX;			/* Right edge of chunkPtr. */
    int rightX2;		/* Right edge of chunkPtr2. */
    int matchLeft;		/* Does the style of this line match that of
				 * its neighbor just to the left of the
				 * current x coordinate? */
    int matchRight;		/* Does line's style match its neighbor just
				 * to the right of the current x-coord? */
    int minX, maxX, xOffset;
    StyleValues *sValuePtr;
    Display *display;
#ifndef TK_NO_DOUBLE_BUFFERING
    const int y = 0;
#else
    const int y = dlPtr->y;
#endif /* TK_NO_DOUBLE_BUFFERING */

    /*
     * Pass 1: scan through dlPtr from left to right. For each range of chunks
     * with the same style, draw the main background for the style plus the
     * vertical parts of the 3D borders (the left and right edges).
     */

    display = Tk_Display(textPtr->tkwin);
    minX = dInfoPtr->curXPixelOffset;
    xOffset = dInfoPtr->x - minX;
    maxX = minX + dInfoPtr->maxX - dInfoPtr->x;
    chunkPtr = dlPtr->chunkPtr;

    /*
     * Note A: in the following statement, and a few others later in this file
     * marked with "See Note A above", the right side of the assignment was
     * replaced with 0 on 6/18/97. This has the effect of highlighting the
     * empty space to the left of a line whenever the leftmost character of
     * the line is highlighted. This way, multi-line highlights always line up
     * along their left edges. However, this may look funny in the case where
     * a single word is highlighted. To undo the change, replace "leftX = 0"
     * with "leftX = chunkPtr->x" and "rightX2 = 0" with "rightX2 =
     * nextPtr2->x" here and at all the marked points below. This restores the
     * old behavior where empty space to the left of a line is not
     * highlighted, leaving a ragged left edge for multi-line highlights.
     */

    leftX = 0;
    for (; leftX < maxX; chunkPtr = chunkPtr->nextPtr) {
	if ((chunkPtr->nextPtr != NULL)
		&& SAME_BACKGROUND(chunkPtr->nextPtr->stylePtr,
		chunkPtr->stylePtr)) {
	    continue;
	}
	sValuePtr = chunkPtr->stylePtr->sValuePtr;
	rightX = chunkPtr->x + chunkPtr->width;
	if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
	    rightX = maxX;
	}
	if (chunkPtr->stylePtr->bgGC != None) {
	    /*
	     * Not visible - bail out now.
	     */

	    if (rightX + xOffset <= 0) {
		leftX = rightX;
		continue;
	    }

	    /*
	     * Trim the start position for drawing to be no further away than
	     * -borderWidth. The reason is that on many X servers drawing from
	     * -32768 (or less) to +something simply does not display
	     * correctly. [Patch #541999]
	     */

	    if ((leftX + xOffset) < -(sValuePtr->borderWidth)) {
		leftX = -sValuePtr->borderWidth - xOffset;
	    }
	    if ((rightX - leftX) > 32767) {
		rightX = leftX + 32767;
	    }

	    XFillRectangle(display, pixmap, chunkPtr->stylePtr->bgGC,
		    leftX + xOffset, y, (unsigned int) (rightX - leftX),
		    (unsigned int) dlPtr->height);
	    if (sValuePtr->relief != TK_RELIEF_FLAT) {
		Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
			leftX + xOffset, y, sValuePtr->borderWidth,
			dlPtr->height, 1, sValuePtr->relief);
		Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
			rightX - sValuePtr->borderWidth + xOffset,
			y, sValuePtr->borderWidth, dlPtr->height, 0,
			sValuePtr->relief);
	    }
	}
	leftX = rightX;
    }

    /*
     * Pass 2: draw the horizontal bevels along the top of the line. To do
     * this, scan through dlPtr from left to right while simultaneously
     * scanning through the line just above dlPtr. ChunkPtr2 and nextPtr2
     * refer to two adjacent chunks in the line above.
     */

    chunkPtr = dlPtr->chunkPtr;
    leftX = 0;				/* See Note A above. */
    leftXIn = 1;
    rightX = chunkPtr->x + chunkPtr->width;
    if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
	rightX = maxX;
    }
    chunkPtr2 = NULL;
    if (prevPtr != NULL && prevPtr->chunkPtr != NULL) {
	/*
	 * Find the chunk in the previous line that covers leftX.
	 */

	nextPtr2 = prevPtr->chunkPtr;
	rightX2 = 0;			/* See Note A above. */
	while (rightX2 <= leftX) {
	    chunkPtr2 = nextPtr2;
	    if (chunkPtr2 == NULL) {
		break;
	    }
	    nextPtr2 = chunkPtr2->nextPtr;
	    rightX2 = chunkPtr2->x + chunkPtr2->width;
	    if (nextPtr2 == NULL) {
		rightX2 = INT_MAX;
	    }
	}
    } else {
	nextPtr2 = NULL;
	rightX2 = INT_MAX;
    }

    while (leftX < maxX) {
	matchLeft = (chunkPtr2 != NULL)
		&& SAME_BACKGROUND(chunkPtr2->stylePtr, chunkPtr->stylePtr);
	sValuePtr = chunkPtr->stylePtr->sValuePtr;
	if (rightX <= rightX2) {
	    /*
	     * The chunk in our line is about to end. If its style changes
	     * then draw the bevel for the current style.
	     */

	    if ((chunkPtr->nextPtr == NULL)
		    || !SAME_BACKGROUND(chunkPtr->stylePtr,
		    chunkPtr->nextPtr->stylePtr)) {
		if (!matchLeft && (sValuePtr->relief != TK_RELIEF_FLAT)) {
		    Tk_3DHorizontalBevel(textPtr->tkwin, pixmap,
			    sValuePtr->border, leftX + xOffset, y,
			    rightX - leftX, sValuePtr->borderWidth, leftXIn,
			    1, 1, sValuePtr->relief);
		}
		leftX = rightX;
		leftXIn = 1;

		/*
		 * If the chunk in the line above is also ending at the same
		 * point then advance to the next chunk in that line.
		 */

		if ((rightX == rightX2) && (chunkPtr2 != NULL)) {
		    goto nextChunk2;
		}
	    }
	    chunkPtr = chunkPtr->nextPtr;
	    if (chunkPtr == NULL) {
		break;
	    }
	    rightX = chunkPtr->x + chunkPtr->width;
	    if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
		rightX = maxX;
	    }
	    continue;
	}

	/*
	 * The chunk in the line above is ending at an x-position where there
	 * is no change in the style of the current line. If the style above
	 * matches the current line on one side of the change but not on the
	 * other, we have to draw an L-shaped piece of bevel.
	 */

	matchRight = (nextPtr2 != NULL)
		&& SAME_BACKGROUND(nextPtr2->stylePtr, chunkPtr->stylePtr);
	if (matchLeft && !matchRight) {
	    if (sValuePtr->relief != TK_RELIEF_FLAT) {
		Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
			rightX2 - sValuePtr->borderWidth + xOffset, y,
			sValuePtr->borderWidth, sValuePtr->borderWidth, 0,
			sValuePtr->relief);
	    }
	    leftX = rightX2 - sValuePtr->borderWidth;
	    leftXIn = 0;
	} else if (!matchLeft && matchRight
		&& (sValuePtr->relief != TK_RELIEF_FLAT)) {
	    Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
		    rightX2 + xOffset, y, sValuePtr->borderWidth,
		    sValuePtr->borderWidth, 1, sValuePtr->relief);
	    Tk_3DHorizontalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
		    leftX + xOffset, y, rightX2 + sValuePtr->borderWidth -
		    leftX, sValuePtr->borderWidth, leftXIn, 0, 1,
		    sValuePtr->relief);
	}

    nextChunk2:
	chunkPtr2 = nextPtr2;
	if (chunkPtr2 == NULL) {
	    rightX2 = INT_MAX;
	} else {
	    nextPtr2 = chunkPtr2->nextPtr;
	    rightX2 = chunkPtr2->x + chunkPtr2->width;
	    if (nextPtr2 == NULL) {
		rightX2 = INT_MAX;
	    }
	}
    }

    /*
     * Pass 3: draw the horizontal bevels along the bottom of the line. This
     * uses the same approach as pass 2.
     */

    chunkPtr = dlPtr->chunkPtr;
    leftX = 0;				/* See Note A above. */
    leftXIn = 0;
    rightX = chunkPtr->x + chunkPtr->width;
    if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
	rightX = maxX;
    }
    chunkPtr2 = NULL;
    if (dlPtr->nextPtr != NULL && dlPtr->nextPtr->chunkPtr != NULL) {
	/*
	 * Find the chunk in the previous line that covers leftX.
	 */

	nextPtr2 = dlPtr->nextPtr->chunkPtr;
	rightX2 = 0;			/* See Note A above. */
	while (rightX2 <= leftX) {
	    chunkPtr2 = nextPtr2;
	    if (chunkPtr2 == NULL) {
		break;
	    }
	    nextPtr2 = chunkPtr2->nextPtr;
	    rightX2 = chunkPtr2->x + chunkPtr2->width;
	    if (nextPtr2 == NULL) {
		rightX2 = INT_MAX;
	    }
	}
    } else {
	nextPtr2 = NULL;
	rightX2 = INT_MAX;
    }

    while (leftX < maxX) {
	matchLeft = (chunkPtr2 != NULL)
		&& SAME_BACKGROUND(chunkPtr2->stylePtr, chunkPtr->stylePtr);
	sValuePtr = chunkPtr->stylePtr->sValuePtr;
	if (rightX <= rightX2) {
	    if ((chunkPtr->nextPtr == NULL)
		    || !SAME_BACKGROUND(chunkPtr->stylePtr,
		    chunkPtr->nextPtr->stylePtr)) {
		if (!matchLeft && (sValuePtr->relief != TK_RELIEF_FLAT)) {
		    Tk_3DHorizontalBevel(textPtr->tkwin, pixmap,
			    sValuePtr->border, leftX + xOffset,
			    y + dlPtr->height - sValuePtr->borderWidth,
			    rightX - leftX, sValuePtr->borderWidth, leftXIn,
			    0, 0, sValuePtr->relief);
		}
		leftX = rightX;
		leftXIn = 0;
		if ((rightX == rightX2) && (chunkPtr2 != NULL)) {
		    goto nextChunk2b;
		}
	    }
	    chunkPtr = chunkPtr->nextPtr;
	    if (chunkPtr == NULL) {
		break;
	    }
	    rightX = chunkPtr->x + chunkPtr->width;
	    if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
		rightX = maxX;
	    }
	    continue;
	}

	matchRight = (nextPtr2 != NULL)
		&& SAME_BACKGROUND(nextPtr2->stylePtr, chunkPtr->stylePtr);
	if (matchLeft && !matchRight) {
	    if (sValuePtr->relief != TK_RELIEF_FLAT) {
		Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
			rightX2 - sValuePtr->borderWidth + xOffset,
			y + dlPtr->height - sValuePtr->borderWidth,
			sValuePtr->borderWidth, sValuePtr->borderWidth, 0,
			sValuePtr->relief);
	    }
	    leftX = rightX2 - sValuePtr->borderWidth;
	    leftXIn = 1;
	} else if (!matchLeft && matchRight
		&& (sValuePtr->relief != TK_RELIEF_FLAT)) {
	    Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
		    rightX2 + xOffset, y + dlPtr->height -
		    sValuePtr->borderWidth, sValuePtr->borderWidth,
		    sValuePtr->borderWidth, 1, sValuePtr->relief);
	    Tk_3DHorizontalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
		    leftX + xOffset, y + dlPtr->height -
		    sValuePtr->borderWidth, rightX2 + sValuePtr->borderWidth -
		    leftX, sValuePtr->borderWidth, leftXIn, 1, 0,
		    sValuePtr->relief);
	}

    nextChunk2b:
	chunkPtr2 = nextPtr2;
	if (chunkPtr2 == NULL) {
	    rightX2 = INT_MAX;
	} else {
	    nextPtr2 = chunkPtr2->nextPtr;
	    rightX2 = chunkPtr2->x + chunkPtr2->width;
	    if (nextPtr2 == NULL) {
		rightX2 = INT_MAX;
	    }
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * AsyncUpdateLineMetrics --
 *
 *	This function is invoked as a background handler to update the pixel-
 *	height calculations of individual lines in an asychronous manner.
 *
 *	Currently a timer-handler is used for this purpose, which continuously
 *	reschedules itself. It may well be better to use some other approach
 *	(e.g., a background thread). We can't use an idle-callback because of
 *	a known bug in Tcl/Tk in which idle callbacks are not allowed to
 *	re-schedule themselves. This just causes an effective infinite loop.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Line heights may be recalculated.
 *
 *----------------------------------------------------------------------
 */

static void
AsyncUpdateLineMetrics(
    ClientData clientData)	/* Information about widget. */
{
    register TkText *textPtr = clientData;
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    int lineNum;

    dInfoPtr->lineUpdateTimer = NULL;

    if ((textPtr->tkwin == NULL) || (textPtr->flags & DESTROYED)) {
	/*
	 * The widget has been deleted. Don't do anything.
	 */

	if (--textPtr->refCount == 0) {
	    ckfree((char *) textPtr);
	}
	return;
    }

    if (dInfoPtr->flags & REDRAW_PENDING) {
	dInfoPtr->lineUpdateTimer = Tcl_CreateTimerHandler(1,
		AsyncUpdateLineMetrics, clientData);
	return;
    }

    /*
     * Reify where we end or all hell breaks loose with the calculations when
     * we try to update. [Bug 2677890]
     */

    lineNum = dInfoPtr->currentMetricUpdateLine;
    if (dInfoPtr->lastMetricUpdateLine == -1) {
	dInfoPtr->lastMetricUpdateLine =
		TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr);
    }

    /*
     * Update the lines in blocks of about 24 recalculations, or 250+ lines
     * examined, so we pass in 256 for 'doThisMuch'.
     */

    lineNum = TkTextUpdateLineMetrics(textPtr, lineNum,
	    dInfoPtr->lastMetricUpdateLine, 256);

    if (tkTextDebug) {
	char buffer[2 * TCL_INTEGER_SPACE + 1];

	sprintf(buffer, "%d %d", lineNum, dInfoPtr->lastMetricUpdateLine);
	LOG("tk_textInvalidateLine", buffer);
    }

    /*
     * If we're not in the middle of a long-line calculation (metricEpoch==-1)
     * and we've reached the last line, then we're done.
     */

    if (dInfoPtr->metricEpoch == -1
	    && lineNum == dInfoPtr->lastMetricUpdateLine) {
	/*
	 * We have looped over all lines, so we're done. We must release our
	 * refCount on the widget (the timer token was already set to NULL
	 * above).
	 */

	textPtr->refCount--;
	if (textPtr->refCount == 0) {
	    ckfree((char *) textPtr);
	}
	return;
    }
    dInfoPtr->currentMetricUpdateLine = lineNum;

    /*
     * Re-arm the timer. We already have a refCount on the text widget so no
     * need to adjust that.
     */

    dInfoPtr->lineUpdateTimer = Tcl_CreateTimerHandler(1,
	    AsyncUpdateLineMetrics, textPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextUpdateLineMetrics --
 *
 *	This function updates the pixel height calculations of a range of
 *	lines in the widget. The range is from lineNum to endLine, but, if
 *	doThisMuch is positive, then the function may return earlier, once a
 *	certain number of lines has been examined. The line counts are from 0.
 *
 *	If doThisMuch is -1, then all lines in the range will be updated. This
 *	will potentially take quite some time for a large text widget.
 *
 *	Note: with bad input for lineNum and endLine, this function can loop
 *	indefinitely.
 *
 * Results:
 *	The index of the last line examined (or -1 if we are about to wrap
 *	around from end to beginning of the widget, and the next line will be
 *	the first line).
 *
 * Side effects:
 *	Line heights may be recalculated.
 *
 *----------------------------------------------------------------------
 */

int
TkTextUpdateLineMetrics(
    TkText *textPtr,		/* Information about widget. */
    int lineNum,		/* Start at this line. */
    int endLine,		/* Go no further than this line. */
    int doThisMuch)		/* How many lines to check, or how many 10s of
				 * lines to recalculate. If '-1' then do
				 * everything in the range (which may take a
				 * while). */
{
    TkTextLine *linePtr = NULL;
    int count = 0;
    int totalLines = TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr);

    if (totalLines == 0) {
	/*
	 * Empty peer widget.
	 */

	return endLine;
    }

    while (1) {
	/*
	 * Get a suitable line.
	 */

	if (lineNum == -1 && linePtr == NULL) {
	    lineNum = 0;
	    linePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree, textPtr,
		    lineNum);
	} else {
	    if (lineNum == -1 || linePtr == NULL) {
		if (lineNum == -1) {
		    lineNum = 0;
		}
		linePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree,
			textPtr, lineNum);
	    } else {
		lineNum++;
		linePtr = TkBTreeNextLine(textPtr, linePtr);
	    }

	    /*
	     * If we're in the middle of a partial-line height calculation,
	     * then we can't be done.
	     */

	    if (textPtr->dInfoPtr->metricEpoch == -1 && lineNum == endLine) {
		/*
		 * We have looped over all lines, so we're done.
		 */

		break;
	    }
	}

	if (lineNum < totalLines) {
	    if (tkTextDebug) {
		char buffer[4 * TCL_INTEGER_SPACE + 3];

		sprintf(buffer, "%d %d %d %d",
			lineNum, endLine, totalLines, count);
		LOG("tk_textInvalidateLine", buffer);
	    }

	    /*
	     * Now update the line's metrics if necessary.
	     */

	    if (TkBTreeLinePixelEpoch(textPtr, linePtr)
		    == textPtr->dInfoPtr->lineMetricUpdateEpoch) {
		/*
		 * This line is already up to date. That means there's nothing
		 * to do here.
		 */
	    } else if (doThisMuch == -1) {
		count += 8 * TkTextUpdateOneLine(textPtr, linePtr, 0,NULL,0);
	    } else {
		TkTextIndex index;
		TkTextIndex *indexPtr;
		int pixelHeight;

		/*
		 * If the metric epoch is the same as the widget's epoch, then
		 * we know that indexPtrs are still valid, and if the cached
		 * metricIndex (if any) is for the same line as we wish to
		 * examine, then we are looking at a long line wrapped many
		 * times, which we will examine in pieces.
		 */

		if (textPtr->dInfoPtr->metricEpoch ==
			textPtr->sharedTextPtr->stateEpoch &&
			textPtr->dInfoPtr->metricIndex.linePtr==linePtr) {
		    indexPtr = &textPtr->dInfoPtr->metricIndex;
		    pixelHeight = textPtr->dInfoPtr->metricPixelHeight;
		} else {
		    /*
		     * We must reset the partial line height calculation data
		     * here, so we don't use it when it is out of date.
		     */

		    textPtr->dInfoPtr->metricEpoch = -1;
		    index.tree = textPtr->sharedTextPtr->tree;
		    index.linePtr = linePtr;
		    index.byteIndex = 0;
		    index.textPtr = NULL;
		    indexPtr = &index;
		    pixelHeight = 0;
		}

		/*
		 * Update the line and update the counter, counting 8 for each
		 * display line we actually re-layout.
		 */

		count += 8 * TkTextUpdateOneLine(textPtr, linePtr,
			pixelHeight, indexPtr, 1);

		if (indexPtr->linePtr == linePtr) {
		    /*
		     * We didn't complete the logical line, because it
		     * produced very many display lines, which must be because
		     * it must be a long line wrapped many times. So we must
		     * cache as far as we got for next time around.
		     */

		    if (pixelHeight == 0) {
			/*
			 * These have already been stored, unless we just
			 * started the new line.
			 */

			textPtr->dInfoPtr->metricIndex = index;
			textPtr->dInfoPtr->metricEpoch =
				textPtr->sharedTextPtr->stateEpoch;
		    }
		    textPtr->dInfoPtr->metricPixelHeight =
			    TkBTreeLinePixelCount(textPtr, linePtr);
		    break;
		}

		/*
		 * We're done with this long line.
		 */

		textPtr->dInfoPtr->metricEpoch = -1;
	    }
	} else {
	    /*
	     * We must never recalculate the height of the last artificial
	     * line. It must stay at zero, and if we recalculate it, it will
	     * change.
	     */

	    if (endLine >= totalLines) {
		lineNum = endLine;
		break;
	    }

	    /*
	     * Set things up for the next loop through.
	     */

	    lineNum = -1;
	}
	count++;

	if (doThisMuch != -1 && count >= doThisMuch) {
	    break;
	}
    }
    if (doThisMuch == -1) {
	/*
	 * If we were requested to provide a full update, then also update the
	 * scrollbar.
	 */

	GetYView(textPtr->interp, textPtr, 1);
    }
    return lineNum;
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextInvalidateLineMetrics, TextInvalidateLineMetrics --
 *
 *	Mark a number of text lines as having invalid line metric
 *	calculations. Never call this with linePtr as the last (artificial)
 *	line in the text. Depending on 'action' which indicates whether the
 *	given lines are simply invalid or have been inserted or deleted, the
 *	pre-existing asynchronous line update range may need to be adjusted.
 *
 *	If linePtr is NULL then 'lineCount' and 'action' are ignored and all
 *	lines are invalidated.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	May schedule an asychronous callback.
 *
 *----------------------------------------------------------------------
 */

void
TkTextInvalidateLineMetrics(
    TkSharedText *sharedTextPtr,/* Shared widget section for all peers, or
				 * NULL. */
    TkText *textPtr,		/* Widget record for text widget. */
    TkTextLine *linePtr,	/* Invalidation starts from this line. */
    int lineCount,		/* And includes this many following lines. */
    int action)			/* Indicates what type of invalidation
				 * occurred (insert, delete, or simple). */
{
    if (sharedTextPtr == NULL) {
	TextInvalidateLineMetrics(textPtr, linePtr, lineCount, action);
    } else {
	textPtr = sharedTextPtr->peers;
	while (textPtr != NULL) {
	    TextInvalidateLineMetrics(textPtr, linePtr, lineCount, action);
	    textPtr = textPtr->next;
	}
    }
}

static void
TextInvalidateLineMetrics(
    TkText *textPtr,		/* Widget record for text widget. */
    TkTextLine *linePtr,	/* Invalidation starts from this line. */
    int lineCount,		/* And includes this many following lines. */
    int action)			/* Indicates what type of invalidation
				 * occurred (insert, delete, or simple). */
{
    int fromLine;
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;

    if (linePtr != NULL) {
	int counter = lineCount;

	fromLine = TkBTreeLinesTo(textPtr, linePtr);

	/*
	 * Invalidate the height calculations of each line in the given range.
	 */

	TkBTreeLinePixelEpoch(textPtr, linePtr) = 0;
	while (counter > 0 && linePtr != 0) {
	    linePtr = TkBTreeNextLine(textPtr, linePtr);
	    if (linePtr != NULL) {
		TkBTreeLinePixelEpoch(textPtr, linePtr) = 0;
	    }
	    counter--;
	}

	/*
	 * Now schedule an examination of each line in the union of the old
	 * and new update ranges, including the (possibly empty) range in
	 * between. If that between range is not-empty, then we are examining
	 * more lines than is strictly necessary (but the examination of the
	 * extra lines should be quick, since their pixelCalculationEpoch will
	 * be up to date). However, to keep track of that would require more
	 * complex record-keeping that what we have.
	 */

	if (dInfoPtr->lineUpdateTimer == NULL) {
	    dInfoPtr->currentMetricUpdateLine = fromLine;
	    if (action == TK_TEXT_INVALIDATE_DELETE) {
		lineCount = 0;
	    }
	    dInfoPtr->lastMetricUpdateLine = fromLine + lineCount + 1;
	} else {
	    int toLine = fromLine + lineCount + 1;

	    if (action == TK_TEXT_INVALIDATE_DELETE) {
		if (toLine <= dInfoPtr->currentMetricUpdateLine) {
		    dInfoPtr->currentMetricUpdateLine = fromLine;
		    if (dInfoPtr->lastMetricUpdateLine != -1) {
			dInfoPtr->lastMetricUpdateLine -= lineCount;
		    }
		} else if (fromLine <= dInfoPtr->currentMetricUpdateLine) {
		    dInfoPtr->currentMetricUpdateLine = fromLine;
		    if (toLine <= dInfoPtr->lastMetricUpdateLine) {
			dInfoPtr->lastMetricUpdateLine -= lineCount;
		    }
		} else {
		    if (dInfoPtr->lastMetricUpdateLine != -1) {
			dInfoPtr->lastMetricUpdateLine = toLine;
		    }
		}
	    } else if (action == TK_TEXT_INVALIDATE_INSERT) {
		if (toLine <= dInfoPtr->currentMetricUpdateLine) {
		    dInfoPtr->currentMetricUpdateLine = fromLine;
		    if (dInfoPtr->lastMetricUpdateLine != -1) {
			dInfoPtr->lastMetricUpdateLine += lineCount;
		    }
		} else if (fromLine <= dInfoPtr->currentMetricUpdateLine) {
		    dInfoPtr->currentMetricUpdateLine = fromLine;
		    if (toLine <= dInfoPtr->lastMetricUpdateLine) {
			dInfoPtr->lastMetricUpdateLine += lineCount;
		    }
		    if (toLine > dInfoPtr->lastMetricUpdateLine) {
			dInfoPtr->lastMetricUpdateLine = toLine;
		    }
		} else {
		    if (dInfoPtr->lastMetricUpdateLine != -1) {
			dInfoPtr->lastMetricUpdateLine = toLine;
		    }
		}
	    } else {
		if (fromLine < dInfoPtr->currentMetricUpdateLine) {
		    dInfoPtr->currentMetricUpdateLine = fromLine;
		}
		if (dInfoPtr->lastMetricUpdateLine != -1
			&& toLine > dInfoPtr->lastMetricUpdateLine) {
		    dInfoPtr->lastMetricUpdateLine = toLine;
		}
	    }
	}
    } else {
	/*
	 * This invalidates the height of all lines in the widget.
	 */

	if ((++dInfoPtr->lineMetricUpdateEpoch) == 0) {
	    dInfoPtr->lineMetricUpdateEpoch++;
	}

	/*
	 * This has the effect of forcing an entire new loop of update checks
	 * on all lines in the widget.
	 */

	if (dInfoPtr->lineUpdateTimer == NULL) {
	    dInfoPtr->currentMetricUpdateLine = -1;
	}
	dInfoPtr->lastMetricUpdateLine = dInfoPtr->currentMetricUpdateLine;
    }

    /*
     * Now re-set the current update calculations.
     */

    if (dInfoPtr->lineUpdateTimer == NULL) {
	textPtr->refCount++;
	dInfoPtr->lineUpdateTimer = Tcl_CreateTimerHandler(1,
		AsyncUpdateLineMetrics, textPtr);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextFindDisplayLineEnd --
 *
 *	This function is invoked to find the index of the beginning or end of
 *	the particular display line on which the given index sits, whether
 *	that line is displayed or not.
 *
 *	If 'end' is zero, we look for the start, and if 'end' is one we look
 *	for the end.
 *
 *	If the beginning of the current display line is elided, and we are
 *	looking for the start of the line, then the returned index will be the
 *	first elided index on the display line.
 *
 *	Similarly if the end of the current display line is elided and we are
 *	looking for the end, then the returned index will be the last elided
 *	index on the display line.
 *
 * Results:
 *	Modifies indexPtr to point to the given end.
 *
 *	If xOffset is non-NULL, it is set to the x-pixel offset of the given
 *	original index within the given display line.
 *
 * Side effects:
 *	The combination of 'LayoutDLine' and 'FreeDLines' seems like a rather
 *	time-consuming way of gathering the information we need, so this would
 *	be a good place to look to speed up the calculations. In particular
 *	these calls will map and unmap embedded windows respectively, which I
 *	would hope isn't exactly necessary!
 *
 *----------------------------------------------------------------------
 */

void
TkTextFindDisplayLineEnd(
    TkText *textPtr,		/* Widget record for text widget. */
    TkTextIndex *indexPtr,	/* Index we will adjust to the display line
				 * start or end. */
    int end,			/* 0 = start, 1 = end. */
    int *xOffset)		/* NULL, or used to store the x-pixel offset
				 * of the original index within its display
				 * line. */
{
    TkTextIndex index;

    if (!end && indexPtr->byteIndex == 0) {
	/*
	 * Nothing to do.
	 */

	if (xOffset != NULL) {
	    *xOffset = 0;
	}
	return;
    }

    index = *indexPtr;
    index.byteIndex = 0;
    index.textPtr = NULL;

    while (1) {
	TkTextIndex endOfLastLine;

	if (TkTextIndexBackBytes(textPtr, &index, 1, &endOfLastLine)) {
	    /*
	     * Reached beginning of text.
	     */

	    break;
	}

	if (!TkTextIsElided(textPtr, &endOfLastLine, NULL)) {
	    /*
	     * The eol is not elided, so 'index' points to the start of a
	     * display line (as well as logical line).
	     */

	    break;
	}

	/*
	 * indexPtr's logical line is actually merged with the previous
	 * logical line whose eol is elided. Continue searching back to get a
	 * real line start.
	 */

	index = endOfLastLine;
	index.byteIndex = 0;
    }

    while (1) {
	DLine *dlPtr;
	int byteCount;
	TkTextIndex nextLineStart;

	dlPtr = LayoutDLine(textPtr, &index);
	byteCount = dlPtr->byteCount;

	TkTextIndexForwBytes(textPtr, &index, byteCount, &nextLineStart);

	/*
	 * 'byteCount' goes up to the beginning of the next display line, so
	 * equality here says we need one more line. We try to perform a quick
	 * comparison which is valid for the case where the logical line is
	 * the same, but otherwise fall back on a full TkTextIndexCmp.
	 */

	if (((index.linePtr == indexPtr->linePtr)
		&& (index.byteIndex + byteCount > indexPtr->byteIndex))
		|| (dlPtr->logicalLinesMerged > 0
		&& TkTextIndexCmp(&nextLineStart, indexPtr) > 0)) {
	    /*
	     * It's on this display line.
	     */

	    if (xOffset != NULL) {
		/*
		 * This call takes a byte index relative to the start of the
		 * current _display_ line, not logical line. We are about to
		 * overwrite indexPtr->byteIndex, so we must do this now.
		 */

		*xOffset = DlineXOfIndex(textPtr, dlPtr,
			indexPtr->byteIndex - dlPtr->index.byteIndex);
	    }
	    if (end) {
		/*
		 * The index we want is one less than the number of bytes in
		 * the display line.
		 */

		TkTextIndexBackBytes(textPtr, &nextLineStart, 1, indexPtr);
	    } else {
		*indexPtr = index;
	    }
	    FreeDLines(textPtr, dlPtr, NULL, DLINE_FREE_TEMP);
	    return;
	}

	FreeDLines(textPtr, dlPtr, NULL, DLINE_FREE_TEMP);
	index = nextLineStart;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * CalculateDisplayLineHeight --
 *
 *	This function is invoked to recalculate the height of the particular
 *	display line which starts with the given index, whether that line is
 *	displayed or not.
 *
 *	This function does not, in itself, update any cached information about
 *	line heights. That should be done, where necessary, by its callers.
 *
 *	The behaviour of this function is _undefined_ if indexPtr is not
 *	currently at the beginning of a display line.
 *
 * Results:
 *	The number of vertical pixels used by the display line.
 *
 *	If 'byteCountPtr' is non-NULL, then returns in that pointer the number
 *	of byte indices on the given display line (which can be used to update
 *	indexPtr in a loop).
 *
 *	If 'mergedLinePtr' is non-NULL, then returns in that pointer the
 *	number of extra logical lines merged into the given display line.
 *
 * Side effects:
 *	The combination of 'LayoutDLine' and 'FreeDLines' seems like a rather
 *	time-consuming way of gathering the information we need, so this would
 *	be a good place to look to speed up the calculations. In particular
 *	these calls will map and unmap embedded windows respectively, which I
 *	would hope isn't exactly necessary!
 *
 *----------------------------------------------------------------------
 */

static int
CalculateDisplayLineHeight(
    TkText *textPtr,		/* Widget record for text widget. */
    const TkTextIndex *indexPtr,/* The index at the beginning of the display
				 * line of interest. */
    int *byteCountPtr,		/* NULL or used to return the number of byte
				 * indices on the given display line. */
    int *mergedLinePtr)		/* NULL or used to return if the given display
				 * line merges with a following logical line
				 * (because the eol is elided). */
{
    DLine *dlPtr;
    int pixelHeight;

    /*
     * Special case for artificial last line. May be better to move this
     * inside LayoutDLine.
     */

    if (indexPtr->byteIndex == 0
	    && TkBTreeNextLine(textPtr, indexPtr->linePtr) == NULL) {
	if (byteCountPtr != NULL) {
	    *byteCountPtr = 0;
	}
	if (mergedLinePtr != NULL) {
	    *mergedLinePtr = 0;
	}
	return 0;
    }

    /*
     * Layout, find the information we need and then free the display-line we
     * laid-out. We must use 'FreeDLines' because it will actually call the
     * relevant code to unmap any embedded windows which were mapped in the
     * LayoutDLine call!
     */

    dlPtr = LayoutDLine(textPtr, indexPtr);
    pixelHeight = dlPtr->height;
    if (byteCountPtr != NULL) {
	*byteCountPtr = dlPtr->byteCount;
    }
    if (mergedLinePtr != NULL) {
	*mergedLinePtr = dlPtr->logicalLinesMerged;
    }
    FreeDLines(textPtr, dlPtr, NULL, DLINE_FREE_TEMP);

    return pixelHeight;
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextIndexYPixels --
 *
 *	This function is invoked to calculate the number of vertical pixels
 *	between the first index of the text widget and the given index. The
 *	range from first logical line to given logical line is determined
 *	using the cached values, and the range inside the given logical line
 *	is calculated on the fly.
 *
 * Results:
 *	The pixel distance between first pixel in the widget and the
 *	top of the index's current display line (could be zero).
 *
 * Side effects:
 *	Just those of 'CalculateDisplayLineHeight'.
 *
 *----------------------------------------------------------------------
 */

int
TkTextIndexYPixels(
    TkText *textPtr,		/* Widget record for text widget. */
    const TkTextIndex *indexPtr)/* The index of which we want the pixel
				 * distance from top of logical line to top of
				 * index. */
{
    int pixelHeight;
    TkTextIndex index;

    pixelHeight = TkBTreePixelsTo(textPtr, indexPtr->linePtr);

    /*
     * Iterate through all display-lines corresponding to the single logical
     * line belonging to indexPtr, adding up the pixel height of each such
     * display line as we go along, until we go past 'indexPtr'.
     */

    if (indexPtr->byteIndex == 0) {
	return pixelHeight;
    }

    index.tree = textPtr->sharedTextPtr->tree;
    index.linePtr = indexPtr->linePtr;
    index.byteIndex = 0;
    index.textPtr = NULL;

    while (1) {
	int bytes, height;

	/*
	 * Currently this call doesn't have many side-effects. However, if in
	 * the future we change the code so there are side-effects (such as
	 * adjusting linePtr->pixelHeight), then the code might not quite work
	 * as intended, specifically the 'linePtr->pixelHeight == pixelHeight'
	 * test below this while loop.
	 */

	height = CalculateDisplayLineHeight(textPtr, &index, &bytes, NULL);

	index.byteIndex += bytes;

	if (index.byteIndex > indexPtr->byteIndex) {
	    return pixelHeight;
	}

	if (height > 0) {
	    pixelHeight += height;
	}

	if (index.byteIndex == indexPtr->byteIndex) {
	    return pixelHeight;
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextUpdateOneLine --
 *
 *	This function is invoked to recalculate the height of a particular
 *	logical line, whether that line is displayed or not.
 *
 *	It must NEVER be called for the artificial last TkTextLine which is
 *	used internally for administrative purposes only. That line must
 *	retain its initial height of 0 otherwise the pixel height calculation
 *	maintained by the B-tree will be wrong.
 *
 * Results:
 *	The number of display lines in the logical line. This could be zero if
 *	the line is totally elided.
 *
 * Side effects:
 *	Line heights may be recalculated, and a timer to update the scrollbar
 *	may be installed. Also see the called function
 *	CalculateDisplayLineHeight for its side effects.
 *
 *----------------------------------------------------------------------
 */

int
TkTextUpdateOneLine(
    TkText *textPtr,		/* Widget record for text widget. */
    TkTextLine *linePtr,	/* The line of which to calculate the
				 * height. */
    int pixelHeight,		/* If indexPtr is non-NULL, then this is the
				 * number of pixels in the logical line
				 * linePtr, up to the index which has been
				 * given. */
    TkTextIndex *indexPtr,	/* Either NULL or an index at the start of a
				 * display line belonging to linePtr, at which
				 * we wish to start (e.g. up to which we have
				 * already calculated). On return this will be
				 * set to the first index on the next line. */
    int partialCalc)		/* Set to 1 if we are allowed to do partial
				 * height calculations of long-lines. In this
				 * case we'll only return what we know so
				 * far. */
{
    TkTextIndex index;
    int displayLines;
    int mergedLines;

    if (indexPtr == NULL) {
	index.tree = textPtr->sharedTextPtr->tree;
	index.linePtr = linePtr;
	index.byteIndex = 0;
	index.textPtr = NULL;
	indexPtr = &index;
	pixelHeight = 0;
    }

    /*
     * Iterate through all display-lines corresponding to the single logical
     * line 'linePtr', adding up the pixel height of each such display line as
     * we go along. The final total is, therefore, the height of the logical
     * line.
     */

    displayLines = 0;
    mergedLines = 0;

    while (1) {
	int bytes, height, logicalLines;

	/*
	 * Currently this call doesn't have many side-effects. However, if in
	 * the future we change the code so there are side-effects (such as
	 * adjusting linePtr->pixelHeight), then the code might not quite work
	 * as intended, specifically the 'linePtr->pixelHeight == pixelHeight'
	 * test below this while loop.
	 */

	height = CalculateDisplayLineHeight(textPtr, indexPtr, &bytes,
		&logicalLines);

	if (height > 0) {
	    pixelHeight += height;
	    displayLines++;
	}

	mergedLines += logicalLines;

	if (TkTextIndexForwBytes(textPtr, indexPtr, bytes, indexPtr)) {
	    break;
	}

	if (logicalLines == 0) {
	    if (indexPtr->linePtr != linePtr) {
		/*
		 * If we reached the end of the logical line, then either way
		 * we don't have a partial calculation.
		 */

		partialCalc = 0;
		break;
	    }
	} else if (indexPtr->byteIndex != 0) {
	    /*
	     * We must still be on the same wrapped line.
	     */
	} else {
	    /*
	     * Must check if indexPtr is really a new logical line which is
	     * not merged with the previous line. The only code that would
	     * really know this is LayoutDLine, which doesn't pass the
	     * information on, so we have to check manually here.
	     */

	    TkTextIndex idx;

	    TkTextIndexBackChars(textPtr, indexPtr, 1, &idx, COUNT_INDICES);
	    if (!TkTextIsElided(textPtr, &idx, NULL)) {
		/*
		 * We've ended a logical line.
		 */

		partialCalc = 0;
		break;
	    }

	    /*
	     * We must still be on the same wrapped line.
	     */
	}
	if (partialCalc && displayLines > 50 && mergedLines == 0) {
	    /*
	     * Only calculate 50 display lines at a time, to avoid huge
	     * delays. In any case it is very rare that a single line wraps 50
	     * times!
	     *
	     * If we have any merged lines, we must complete the full logical
	     * line layout here and now, because the partial-calculation code
	     * isn't designed to handle merged logical lines. Hence the
	     * 'mergedLines == 0' check.
	     */

	    break;
	}
    }

    if (!partialCalc) {
	int changed = 0;

	/*
	 * Cancel any partial line height calculation state.
	 */

	textPtr->dInfoPtr->metricEpoch = -1;

	/*
	 * Mark the logical line as being up to date (caution: it isn't yet up
	 * to date, that will happen in TkBTreeAdjustPixelHeight just below).
	 */

	TkBTreeLinePixelEpoch(textPtr, linePtr)
		= textPtr->dInfoPtr->lineMetricUpdateEpoch;
	if (TkBTreeLinePixelCount(textPtr, linePtr) != pixelHeight) {
	    changed = 1;
	}

	if (mergedLines > 0) {
	    int i = mergedLines;
	    TkTextLine *mergedLinePtr;

	    /*
	     * Loop over all merged logical lines, marking them up to date
	     * (again, the pixel count setting will actually happen in
	     * TkBTreeAdjustPixelHeight).
	     */

	    mergedLinePtr = linePtr;
	    while (i-- > 0) {
		mergedLinePtr = TkBTreeNextLine(textPtr, mergedLinePtr);
		TkBTreeLinePixelEpoch(textPtr, mergedLinePtr)
			= textPtr->dInfoPtr->lineMetricUpdateEpoch;
		if (TkBTreeLinePixelCount(textPtr, mergedLinePtr) != 0) {
		    changed = 1;
		}
	    }
	}

	if (!changed) {
	    /*
	     * If there's nothing to change, then we can already return.
	     */

	    return displayLines;
	}
    }

    /*
     * We set the line's height, but the return value is now the height of the
     * entire widget, which may be used just below for reporting/debugging
     * purposes.
     */

    pixelHeight = TkBTreeAdjustPixelHeight(textPtr, linePtr, pixelHeight,
	    mergedLines);

    if (tkTextDebug) {
	char buffer[2 * TCL_INTEGER_SPACE + 1];

	if (TkBTreeNextLine(textPtr, linePtr) == NULL) {
	    Tcl_Panic("Mustn't ever update line height of last artificial line");
	}

	sprintf(buffer, "%d %d", TkBTreeLinesTo(textPtr,linePtr), pixelHeight);
	LOG("tk_textNumPixels", buffer);
    }
    if (textPtr->dInfoPtr->scrollbarTimer == NULL) {
	textPtr->refCount++;
	textPtr->dInfoPtr->scrollbarTimer = Tcl_CreateTimerHandler(200,
		AsyncUpdateYScrollbar, textPtr);
    }
    return displayLines;
}

/*
 *----------------------------------------------------------------------
 *
 * DisplayText --
 *
 *	This function is invoked as a when-idle handler to update the display.
 *	It only redisplays the parts of the text widget that are out of date.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Information is redrawn on the screen.
 *
 *----------------------------------------------------------------------
 */

static void
DisplayText(
    ClientData clientData)	/* Information about widget. */
{
    register TkText *textPtr = clientData;
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    register DLine *dlPtr;
    DLine *prevPtr;
    Pixmap pixmap;
    int maxHeight, borders;
    int bottomY = 0;		/* Initialization needed only to stop compiler
				 * warnings. */
    Tcl_Interp *interp;

    if ((textPtr->tkwin == NULL) || (textPtr->flags & DESTROYED)) {
	/*
	 * The widget has been deleted.	 Don't do anything.
	 */

	return;
    }

    interp = textPtr->interp;
    Tcl_Preserve(interp);

    if (tkTextDebug) {
	Tcl_SetVar2(interp, "tk_textRelayout", NULL, "", TCL_GLOBAL_ONLY);
    }

    if ((textPtr->tkwin == NULL) || (textPtr->flags & DESTROYED)) {
	/*
	 * The widget has been deleted.	 Don't do anything.
	 */

	goto end;
    }

    if (!Tk_IsMapped(textPtr->tkwin) || (dInfoPtr->maxX <= dInfoPtr->x)
	    || (dInfoPtr->maxY <= dInfoPtr->y)) {
	UpdateDisplayInfo(textPtr);
	dInfoPtr->flags &= ~REDRAW_PENDING;
	goto doScrollbars;
    }
    numRedisplays++;
    if (tkTextDebug) {
	Tcl_SetVar2(interp, "tk_textRedraw", NULL, "", TCL_GLOBAL_ONLY);
    }

    if ((textPtr->tkwin == NULL) || (textPtr->flags & DESTROYED)) {
	/*
	 * The widget has been deleted. Don't do anything.
	 */

	goto end;
    }

    /*
     * Choose a new current item if that is needed (this could cause event
     * handlers to be invoked, hence the preserve/release calls and the loop,
     * since the handlers could conceivably necessitate yet another current
     * item calculation). The tkwin check is because the whole window could go
     * away in the Tcl_Release call.
     */

    while (dInfoPtr->flags & REPICK_NEEDED) {
	textPtr->refCount++;
	dInfoPtr->flags &= ~REPICK_NEEDED;
	TkTextPickCurrent(textPtr, &textPtr->pickEvent);
	if (--textPtr->refCount == 0) {
	    ckfree((char *) textPtr);
	    goto end;
	}
	if ((textPtr->tkwin == NULL) || (textPtr->flags & DESTROYED)) {
	    goto end;
	}
    }

    /*
     * First recompute what's supposed to be displayed.
     */

    UpdateDisplayInfo(textPtr);
    dInfoPtr->dLinesInvalidated = 0;

    /*
     * See if it's possible to bring some parts of the screen up-to-date by
     * scrolling (copying from other parts of the screen). We have to be
     * particularly careful with the top and bottom lines of the display,
     * since these may only be partially visible and therefore not helpful for
     * some scrolling purposes.
     */

    for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
	register DLine *dlPtr2;
	int offset, height, y, oldY;
	TkRegion damageRgn;

	/*
	 * These tests are, in order:
	 *
	 * 1. If the line is already marked as invalid
	 * 2. If the line hasn't moved
	 * 3. If the line overlaps the bottom of the window and we are
	 *    scrolling up.
	 * 4. If the line overlaps the top of the window and we are scrolling
	 *    down.
	 *
	 * If any of these tests are true, then we can't scroll this line's
	 * part of the display.
	 *
	 * Note that even if tests 3 or 4 aren't true, we may be able to
	 * scroll the line, but we still need to be sure to call embedded
	 * window display procs on top and bottom lines if they have any
	 * portion non-visible (see below).
	 */

	if ((dlPtr->flags & OLD_Y_INVALID)
		|| (dlPtr->y == dlPtr->oldY)
		|| (((dlPtr->oldY + dlPtr->height) > dInfoPtr->maxY)
		    && (dlPtr->y < dlPtr->oldY))
		|| ((dlPtr->oldY < dInfoPtr->y) && (dlPtr->y > dlPtr->oldY))) {
	    continue;
	}

	/*
	 * This line is already drawn somewhere in the window so it only needs
	 * to be copied to its new location. See if there's a group of lines
	 * that can all be copied together.
	 */

	offset = dlPtr->y - dlPtr->oldY;
	height = dlPtr->height;
	y = dlPtr->y;
	for (dlPtr2 = dlPtr->nextPtr; dlPtr2 != NULL;
		dlPtr2 = dlPtr2->nextPtr) {
	    if ((dlPtr2->flags & OLD_Y_INVALID)
		    || ((dlPtr2->oldY + offset) != dlPtr2->y)
		    || ((dlPtr2->oldY + dlPtr2->height) > dInfoPtr->maxY)) {
		break;
	    }
	    height += dlPtr2->height;
	}

	/*
	 * Reduce the height of the area being copied if necessary to avoid
	 * overwriting the border area.
	 */

	if ((y + height) > dInfoPtr->maxY) {
	    height = dInfoPtr->maxY -y;
	}
	oldY = dlPtr->oldY;
	if (y < dInfoPtr->y) {
	    /*
	     * Adjust if the area being copied is going to overwrite the top
	     * border of the window (so the top line is only half onscreen).
	     */

	    int y_off = dInfoPtr->y - dlPtr->y;
	    height -= y_off;
	    oldY += y_off;
	    y = dInfoPtr->y;
	}

	/*
	 * Update the lines we are going to scroll to show that they have been
	 * copied.
	 */

	while (1) {
	    /*
	     * The DLine already has OLD_Y_INVALID cleared.
	     */

	    dlPtr->oldY = dlPtr->y;
	    if (dlPtr->nextPtr == dlPtr2) {
		break;
	    }
	    dlPtr = dlPtr->nextPtr;
	}

	/*
	 * Scan through the lines following the copied ones to see if we are
	 * going to overwrite them with the copy operation. If so, mark them
	 * for redisplay.
	 */

	for ( ; dlPtr2 != NULL; dlPtr2 = dlPtr2->nextPtr) {
	    if ((!(dlPtr2->flags & OLD_Y_INVALID))
		    && ((dlPtr2->oldY + dlPtr2->height) > y)
		    && (dlPtr2->oldY < (y + height))) {
		dlPtr2->flags |= OLD_Y_INVALID;
	    }
	}

	/*
	 * Now scroll the lines. This may generate damage which we handle by
	 * calling TextInvalidateRegion to mark the display blocks as stale.
	 */

	damageRgn = TkCreateRegion();
	if (TkScrollWindow(textPtr->tkwin, dInfoPtr->scrollGC, dInfoPtr->x,
		oldY, dInfoPtr->maxX-dInfoPtr->x, height, 0, y-oldY,
		damageRgn)) {
	    TextInvalidateRegion(textPtr, damageRgn);
	}
	numCopies++;
	TkDestroyRegion(damageRgn);
    }

    /*
     * Clear the REDRAW_PENDING flag here. This is actually pretty tricky. We
     * want to wait until *after* doing the scrolling, since that could
     * generate more areas to redraw and don't want to reschedule a redisplay
     * for them. On the other hand, we can't wait until after all the
     * redisplaying, because the act of redisplaying could actually generate
     * more redisplays (e.g. in the case of a nested window with event
     * bindings triggered by redisplay).
     */

    dInfoPtr->flags &= ~REDRAW_PENDING;

    /*
     * Redraw the borders if that's needed.
     */

    if (dInfoPtr->flags & REDRAW_BORDERS) {
	if (tkTextDebug) {
	    LOG("tk_textRedraw", "borders");
	}

	if (textPtr->tkwin == NULL) {
	    /*
	     * The widget has been deleted. Don't do anything.
	     */

	    goto end;
	}

	Tk_Draw3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
		textPtr->border, textPtr->highlightWidth,
		textPtr->highlightWidth,
		Tk_Width(textPtr->tkwin) - 2*textPtr->highlightWidth,
		Tk_Height(textPtr->tkwin) - 2*textPtr->highlightWidth,
		textPtr->borderWidth, textPtr->relief);
	if (textPtr->highlightWidth != 0) {
	    GC fgGC, bgGC;

	    bgGC = Tk_GCForColor(textPtr->highlightBgColorPtr,
		    Tk_WindowId(textPtr->tkwin));
	    if (textPtr->flags & GOT_FOCUS) {
		fgGC = Tk_GCForColor(textPtr->highlightColorPtr,
			Tk_WindowId(textPtr->tkwin));
		TkpDrawHighlightBorder(textPtr->tkwin, fgGC, bgGC,
			textPtr->highlightWidth, Tk_WindowId(textPtr->tkwin));
	    } else {
		TkpDrawHighlightBorder(textPtr->tkwin, bgGC, bgGC,
			textPtr->highlightWidth, Tk_WindowId(textPtr->tkwin));
	    }
	}
	borders = textPtr->borderWidth + textPtr->highlightWidth;
	if (textPtr->padY > 0) {
	    Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
		    textPtr->border, borders, borders,
		    Tk_Width(textPtr->tkwin) - 2*borders, textPtr->padY,
		    0, TK_RELIEF_FLAT);
	    Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
		    textPtr->border, borders,
		    Tk_Height(textPtr->tkwin) - borders - textPtr->padY,
		    Tk_Width(textPtr->tkwin) - 2*borders,
		    textPtr->padY, 0, TK_RELIEF_FLAT);
	}
	if (textPtr->padX > 0) {
	    Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
		    textPtr->border, borders, borders + textPtr->padY,
		    textPtr->padX,
		    Tk_Height(textPtr->tkwin) - 2*borders -2*textPtr->padY,
		    0, TK_RELIEF_FLAT);
	    Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
		    textPtr->border,
		    Tk_Width(textPtr->tkwin) - borders - textPtr->padX,
		    borders + textPtr->padY, textPtr->padX,
		    Tk_Height(textPtr->tkwin) - 2*borders -2*textPtr->padY,
		    0, TK_RELIEF_FLAT);
	}
	dInfoPtr->flags &= ~REDRAW_BORDERS;
    }

    /*
     * Now we have to redraw the lines that couldn't be updated by scrolling.
     * First, compute the height of the largest line and allocate an off-
     * screen pixmap to use for double-buffered displays.
     */

    maxHeight = -1;
    for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
	    dlPtr = dlPtr->nextPtr) {
	if ((dlPtr->height > maxHeight) &&
		((dlPtr->flags&OLD_Y_INVALID) || (dlPtr->oldY != dlPtr->y))) {
	    maxHeight = dlPtr->height;
	}
	bottomY = dlPtr->y + dlPtr->height;
    }

    /*
     * There used to be a line here which restricted 'maxHeight' to be no
     * larger than 'dInfoPtr->maxY', but this is incorrect for the case where
     * individual lines may be taller than the widget _and_ we have smooth
     * scrolling. What we can do is restrict maxHeight to be no larger than
     * 'dInfoPtr->maxY + dInfoPtr->topPixelOffset'.
     */

    if (maxHeight > (dInfoPtr->maxY + dInfoPtr->topPixelOffset)) {
	maxHeight = (dInfoPtr->maxY + dInfoPtr->topPixelOffset);
    }

    if (maxHeight > 0) {
#ifndef TK_NO_DOUBLE_BUFFERING
	pixmap = Tk_GetPixmap(Tk_Display(textPtr->tkwin),
		Tk_WindowId(textPtr->tkwin), Tk_Width(textPtr->tkwin),
		maxHeight, Tk_Depth(textPtr->tkwin));
#else
	pixmap = Tk_WindowId(textPtr->tkwin);
#endif /* TK_NO_DOUBLE_BUFFERING */
	for (prevPtr = NULL, dlPtr = textPtr->dInfoPtr->dLinePtr;
		(dlPtr != NULL) && (dlPtr->y < dInfoPtr->maxY);
		prevPtr = dlPtr, dlPtr = dlPtr->nextPtr) {
	    if (dlPtr->chunkPtr == NULL) {
		continue;
	    }
	    if ((dlPtr->flags & OLD_Y_INVALID) || dlPtr->oldY != dlPtr->y) {
		if (tkTextDebug) {
		    char string[TK_POS_CHARS];

		    TkTextPrintIndex(textPtr, &dlPtr->index, string);
		    LOG("tk_textRedraw", string);
		}
		DisplayDLine(textPtr, dlPtr, prevPtr, pixmap);
		if (dInfoPtr->dLinesInvalidated) {
#ifndef TK_NO_DOUBLE_BUFFERING
		    Tk_FreePixmap(Tk_Display(textPtr->tkwin), pixmap);
#endif /* TK_NO_DOUBLE_BUFFERING */
		    return;
		}
		dlPtr->oldY = dlPtr->y;
		dlPtr->flags &= ~(NEW_LAYOUT | OLD_Y_INVALID);
	    } else if (dlPtr->chunkPtr != NULL && ((dlPtr->y < 0)
		    || (dlPtr->y + dlPtr->height > dInfoPtr->maxY))) {
		register TkTextDispChunk *chunkPtr;

		/*
		 * It's the first or last DLine which are also overlapping the
		 * top or bottom of the window, but we decided above it wasn't
		 * necessary to display them (we were able to update them by
		 * scrolling). This is fine, except that if the lines contain
		 * any embedded windows, we must still call the display proc
		 * on them because they might need to be unmapped or they
		 * might need to be moved to reflect their new position.
		 * Otherwise, everything else moves, but the embedded window
		 * doesn't!
		 *
		 * So, we loop through all the chunks, calling the display
		 * proc of embedded windows only.
		 */

		for (chunkPtr = dlPtr->chunkPtr; (chunkPtr != NULL);
			chunkPtr = chunkPtr->nextPtr) {
		    int x;
		    if (chunkPtr->displayProc != TkTextEmbWinDisplayProc) {
			continue;
		    }
		    x = chunkPtr->x + dInfoPtr->x - dInfoPtr->curXPixelOffset;
		    if ((x + chunkPtr->width <= 0) || (x >= dInfoPtr->maxX)) {
			/*
			 * Note: we have to call the displayProc even for
			 * chunks that are off-screen. This is needed, for
			 * example, so that embedded windows can be unmapped
			 * in this case. Display the chunk at a coordinate
			 * that can be clearly identified by the displayProc
			 * as being off-screen to the left (the displayProc
			 * may not be able to tell if something is off to the
			 * right).
			 */

			x = -chunkPtr->width;
		    }
		    TkTextEmbWinDisplayProc(textPtr, chunkPtr, x,
			    dlPtr->spaceAbove,
			    dlPtr->height-dlPtr->spaceAbove-dlPtr->spaceBelow,
			    dlPtr->baseline - dlPtr->spaceAbove, NULL,
			    (Drawable) None, dlPtr->y + dlPtr->spaceAbove);
		}

	    }
	}
#ifndef TK_NO_DOUBLE_BUFFERING
	Tk_FreePixmap(Tk_Display(textPtr->tkwin), pixmap);
#endif /* TK_NO_DOUBLE_BUFFERING */
    }

    /*
     * See if we need to refresh the part of the window below the last line of
     * text (if there is any such area). Refresh the padding area on the left
     * too, since the insertion cursor might have been displayed there
     * previously).
     */

    if (dInfoPtr->topOfEof > dInfoPtr->maxY) {
	dInfoPtr->topOfEof = dInfoPtr->maxY;
    }
    if (bottomY < dInfoPtr->topOfEof) {
	if (tkTextDebug) {
	    LOG("tk_textRedraw", "eof");
	}

	if ((textPtr->tkwin == NULL) || (textPtr->flags & DESTROYED)) {
	    /*
	     * The widget has been deleted. Don't do anything.
	     */

	    goto end;
	}

	Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
		textPtr->border, dInfoPtr->x - textPtr->padX, bottomY,
		dInfoPtr->maxX - (dInfoPtr->x - textPtr->padX),
		dInfoPtr->topOfEof-bottomY, 0, TK_RELIEF_FLAT);
    }
    dInfoPtr->topOfEof = bottomY;

    /*
     * Update the vertical scrollbar, if there is one. Note: it's important to
     * clear REDRAW_PENDING here, just in case the scroll function does
     * something that requires redisplay.
     */

  doScrollbars:
    if (textPtr->flags & UPDATE_SCROLLBARS) {
	textPtr->flags &= ~UPDATE_SCROLLBARS;
	if (textPtr->yScrollCmd != NULL) {
	    GetYView(textPtr->interp, textPtr, 1);
	}

	if ((textPtr->tkwin == NULL) || (textPtr->flags & DESTROYED)) {
	    /*
	     * The widget has been deleted. Don't do anything.
	     */

	    goto end;
	}

	/*
	 * Update the horizontal scrollbar, if any.
	 */

	if (textPtr->xScrollCmd != NULL) {
	    GetXView(textPtr->interp, textPtr, 1);
	}
    }

  end:
    Tcl_Release(interp);
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextEventuallyRepick --
 *
 *	This function is invoked whenever something happens that could change
 *	the current character or the tags associated with it.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	A repick is scheduled as an idle handler.
 *
 *----------------------------------------------------------------------
 */

void
TkTextEventuallyRepick(
    TkText *textPtr)		/* Widget record for text widget. */
{
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;

    dInfoPtr->flags |= REPICK_NEEDED;
    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
	dInfoPtr->flags |= REDRAW_PENDING;
	Tcl_DoWhenIdle(DisplayText, textPtr);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextRedrawRegion --
 *
 *	This function is invoked to schedule a redisplay for a given region of
 *	a text widget. The redisplay itself may not occur immediately: it's
 *	scheduled as a when-idle handler.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Information will eventually be redrawn on the screen.
 *
 *----------------------------------------------------------------------
 */

void
TkTextRedrawRegion(
    TkText *textPtr,		/* Widget record for text widget. */
    int x, int y,		/* Coordinates of upper-left corner of area to
				 * be redrawn, in pixels relative to textPtr's
				 * window. */
    int width, int height)	/* Width and height of area to be redrawn. */
{
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    TkRegion damageRgn = TkCreateRegion();
    XRectangle rect;

    rect.x = x;
    rect.y = y;
    rect.width = width;
    rect.height = height;
    TkUnionRectWithRegion(&rect, damageRgn, damageRgn);

    TextInvalidateRegion(textPtr, damageRgn);

    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
	dInfoPtr->flags |= REDRAW_PENDING;
	Tcl_DoWhenIdle(DisplayText, textPtr);
    }
    TkDestroyRegion(damageRgn);
}

/*
 *----------------------------------------------------------------------
 *
 * TextInvalidateRegion --
 *
 *	Mark a region of text as invalid.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Updates the display information for the text widget.
 *
 *----------------------------------------------------------------------
 */

static void
TextInvalidateRegion(
    TkText *textPtr,		/* Widget record for text widget. */
    TkRegion region)		/* Region of area to redraw. */
{
    register DLine *dlPtr;
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    int maxY, inset;
    XRectangle rect;

    /*
     * Find all lines that overlap the given region and mark them for
     * redisplay.
     */

    TkClipBox(region, &rect);
    maxY = rect.y + rect.height;
    for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
	    dlPtr = dlPtr->nextPtr) {
	if ((!(dlPtr->flags & OLD_Y_INVALID))
		&& (TkRectInRegion(region, rect.x, dlPtr->y,
		rect.width, (unsigned int) dlPtr->height) != RectangleOut)) {
	    dlPtr->flags |= OLD_Y_INVALID;
	}
    }
    if (dInfoPtr->topOfEof < maxY) {
	dInfoPtr->topOfEof = maxY;
    }

    /*
     * Schedule the redisplay operation if there isn't one already scheduled.
     */

    inset = textPtr->borderWidth + textPtr->highlightWidth;
    if ((rect.x < (inset + textPtr->padX))
	    || (rect.y < (inset + textPtr->padY))
	    || ((int) (rect.x + rect.width) > (Tk_Width(textPtr->tkwin)
		    - inset - textPtr->padX))
	    || (maxY > (Tk_Height(textPtr->tkwin) - inset - textPtr->padY))) {
	dInfoPtr->flags |= REDRAW_BORDERS;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextChanged, TextChanged --
 *
 *	This function is invoked when info in a text widget is about to be
 *	modified in a way that changes how it is displayed (e.g. characters
 *	were inserted or deleted, or tag information was changed). This
 *	function must be called *before* a change is made, so that indexes in
 *	the display information are still valid.
 *
 *	Note: if the range of indices may change geometry as well as simply
 *	requiring redisplay, then the caller should also call
 *	TkTextInvalidateLineMetrics.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The range of character between index1Ptr (inclusive) and index2Ptr
 *	(exclusive) will be redisplayed at some point in the future (the
 *	actual redisplay is scheduled as a when-idle handler).
 *
 *----------------------------------------------------------------------
 */

void
TkTextChanged(
    TkSharedText *sharedTextPtr,/* Shared widget section, or NULL. */
    TkText *textPtr,		/* Widget record for text widget, or NULL. */
    const TkTextIndex*index1Ptr,/* Index of first character to redisplay. */
    const TkTextIndex*index2Ptr)/* Index of character just after last one to
				 * redisplay. */
{
    if (sharedTextPtr == NULL) {
	TextChanged(textPtr, index1Ptr, index2Ptr);
    } else {
	textPtr = sharedTextPtr->peers;
	while (textPtr != NULL) {
	    TextChanged(textPtr, index1Ptr, index2Ptr);
	    textPtr = textPtr->next;
	}
    }
}

static void
TextChanged(
    TkText *textPtr,		/* Widget record for text widget, or NULL. */
    const TkTextIndex*index1Ptr,/* Index of first character to redisplay. */
    const TkTextIndex*index2Ptr)/* Index of character just after last one to
				 * redisplay. */
{
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    DLine *firstPtr, *lastPtr;
    TkTextIndex rounded;

    /*
     * Schedule both a redisplay and a recomputation of display information.
     * It's done here rather than the end of the function for two reasons:
     *
     * 1. If there are no display lines to update we'll want to return
     *	  immediately, well before the end of the function.
     * 2. It's important to arrange for the redisplay BEFORE calling
     *	  FreeDLines. The reason for this is subtle and has to do with
     *	  embedded windows. The chunk delete function for an embedded window
     *	  will schedule an idle handler to unmap the window. However, we want
     *	  the idle handler for redisplay to be called first, so that it can
     *	  put the embedded window back on the screen again (if appropriate).
     *	  This will prevent the window from ever being unmapped, and thereby
     *	  avoid flashing.
     */

    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
	Tcl_DoWhenIdle(DisplayText, textPtr);
    }
    dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;

    /*
     * Find the DLines corresponding to index1Ptr and index2Ptr. There is one
     * tricky thing here, which is that we have to relayout in units of whole
     * text lines: round index1Ptr back to the beginning of its text line, and
     * include all the display lines after index2, up to the end of its text
     * line. This is necessary because the indices stored in the display lines
     * will no longer be valid. It's also needed because any edit could change
     * the way lines wrap.
     */

    rounded = *index1Ptr;
    rounded.byteIndex = 0;
    firstPtr = FindDLine(dInfoPtr->dLinePtr, &rounded);
    if (firstPtr == NULL) {
	return;
    }
    lastPtr = FindDLine(dInfoPtr->dLinePtr, index2Ptr);
    while ((lastPtr != NULL)
	    && (lastPtr->index.linePtr == index2Ptr->linePtr)) {
	lastPtr = lastPtr->nextPtr;
    }

    /*
     * Delete all the DLines from firstPtr up to but not including lastPtr.
     */

    FreeDLines(textPtr, firstPtr, lastPtr, DLINE_UNLINK);
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextRedrawTag, TextRedrawTag --
 *
 *	This function is invoked to request a redraw of all characters in a
 *	given range that have a particular tag on or off. It's called, for
 *	example, when tag options change.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Information on the screen may be redrawn, and the layout of the screen
 *	may change.
 *
 *----------------------------------------------------------------------
 */

void
TkTextRedrawTag(
    TkSharedText *sharedTextPtr,/* Shared widget section, or NULL. */
    TkText *textPtr,		/* Widget record for text widget. */
    TkTextIndex *index1Ptr,	/* First character in range to consider for
				 * redisplay. NULL means start at beginning of
				 * text. */
    TkTextIndex *index2Ptr,	/* Character just after last one to consider
				 * for redisplay. NULL means process all the
				 * characters in the text. */
    TkTextTag *tagPtr,		/* Information about tag. */
    int withTag)		/* 1 means redraw characters that have the
				 * tag, 0 means redraw those without. */
{
    if (sharedTextPtr == NULL) {
	TextRedrawTag(textPtr, index1Ptr, index2Ptr, tagPtr, withTag);
    } else {
	textPtr = sharedTextPtr->peers;
	while (textPtr != NULL) {
	    TextRedrawTag(textPtr, index1Ptr, index2Ptr, tagPtr, withTag);
	    textPtr = textPtr->next;
	}
    }
}

static void
TextRedrawTag(
    TkText *textPtr,		/* Widget record for text widget. */
    TkTextIndex *index1Ptr,	/* First character in range to consider for
				 * redisplay. NULL means start at beginning of
				 * text. */
    TkTextIndex *index2Ptr,	/* Character just after last one to consider
				 * for redisplay. NULL means process all the
				 * characters in the text. */
    TkTextTag *tagPtr,		/* Information about tag. */
    int withTag)		/* 1 means redraw characters that have the
				 * tag, 0 means redraw those without. */
{
    register DLine *dlPtr;
    DLine *endPtr;
    int tagOn;
    TkTextSearch search;
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    TkTextIndex *curIndexPtr;
    TkTextIndex endOfText, *endIndexPtr;

    /*
     * Invalidate the pixel calculation of all lines in the given range. This
     * may be a bit over-aggressive, so we could consider more subtle
     * techniques here in the future. In particular, when we create a tag for
     * the first time with '.t tag configure foo -font "Arial 20"', say, even
     * though that obviously can't apply to anything at all (the tag didn't
     * exist a moment ago), we invalidate every single line in the widget.
     */

    if (tagPtr->affectsDisplayGeometry) {
	TkTextLine *startLine, *endLine;
	int lineCount;

	if (index2Ptr == NULL) {
	    endLine = NULL;
	    lineCount = TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr);
	} else {
	    endLine = index2Ptr->linePtr;
	    lineCount = TkBTreeLinesTo(textPtr, endLine);
	}
	if (index1Ptr == NULL) {
	    startLine = NULL;
	} else {
	    startLine = index1Ptr->linePtr;
	    lineCount -= TkBTreeLinesTo(textPtr, startLine);
	}
	TkTextInvalidateLineMetrics(NULL, textPtr, startLine, lineCount,
		TK_TEXT_INVALIDATE_ONLY);
    }

    /*
     * Round up the starting position if it's before the first line visible on
     * the screen (we only care about what's on the screen).
     */

    dlPtr = dInfoPtr->dLinePtr;
    if (dlPtr == NULL) {
	return;
    }
    if ((index1Ptr == NULL) || (TkTextIndexCmp(&dlPtr->index, index1Ptr)>0)) {
	index1Ptr = &dlPtr->index;
    }

    /*
     * Set the stopping position if it wasn't specified.
     */

    if (index2Ptr == NULL) {
	int lastLine = TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr);

	index2Ptr = TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
		lastLine, 0, &endOfText);
    }

    /*
     * Initialize a search through all transitions on the tag, starting with
     * the first transition where the tag's current state is different from
     * what it will eventually be.
     */

    TkBTreeStartSearch(index1Ptr, index2Ptr, tagPtr, &search);

    /*
     * Make our own curIndex because at this point search.curIndex may not
     * equal index1Ptr->curIndex in the case the first tag toggle comes after
     * index1Ptr (See the use of FindTagStart in TkBTreeStartSearch).
     */

    curIndexPtr = index1Ptr;
    tagOn = TkBTreeCharTagged(index1Ptr, tagPtr);
    if (tagOn != withTag) {
	if (!TkBTreeNextTag(&search)) {
	    return;
	}
	curIndexPtr = &search.curIndex;
    }

    /*
     * Schedule a redisplay and layout recalculation if they aren't already
     * pending. This has to be done before calling FreeDLines, for the reason
     * given in TkTextChanged.
     */

    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
	Tcl_DoWhenIdle(DisplayText, textPtr);
    }
    dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;

    /*
     * Each loop through the loop below is for one range of characters where
     * the tag's current state is different than its eventual state. At the
     * top of the loop, search contains information about the first character
     * in the range.
     */

    while (1) {
	/*
	 * Find the first DLine structure in the range. Note: if the desired
	 * character isn't the first in its text line, then look for the
	 * character just before it instead. This is needed to handle the case
	 * where the first character of a wrapped display line just got
	 * smaller, so that it now fits on the line before: need to relayout
	 * the line containing the previous character.
	 */

	if (curIndexPtr->byteIndex == 0) {
	    dlPtr = FindDLine(dlPtr, curIndexPtr);
	} else {
	    TkTextIndex tmp;

	    tmp = *curIndexPtr;
	    tmp.byteIndex -= 1;
	    dlPtr = FindDLine(dlPtr, &tmp);
	}
	if (dlPtr == NULL) {
	    break;
	}

	/*
	 * Find the first DLine structure that's past the end of the range.
	 */

	if (!TkBTreeNextTag(&search)) {
	    endIndexPtr = index2Ptr;
	} else {
	    curIndexPtr = &search.curIndex;
	    endIndexPtr = curIndexPtr;
	}
	endPtr = FindDLine(dlPtr, endIndexPtr);
	if ((endPtr != NULL) && (endPtr->index.linePtr == endIndexPtr->linePtr)
		&& (endPtr->index.byteIndex < endIndexPtr->byteIndex)) {
	    endPtr = endPtr->nextPtr;
	}

	/*
	 * Delete all of the display lines in the range, so that they'll be
	 * re-layed out and redrawn.
	 */

	FreeDLines(textPtr, dlPtr, endPtr, DLINE_UNLINK);
	dlPtr = endPtr;

	/*
	 * Find the first text line in the next range.
	 */

	if (!TkBTreeNextTag(&search)) {
	    break;
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextRelayoutWindow --
 *
 *	This function is called when something has happened that invalidates
 *	the whole layout of characters on the screen, such as a change in a
 *	configuration option for the overall text widget or a change in the
 *	window size. It causes all display information to be recomputed and
 *	the window to be redrawn.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	All the display information will be recomputed for the window and the
 *	window will be redrawn.
 *
 *----------------------------------------------------------------------
 */

void
TkTextRelayoutWindow(
    TkText *textPtr,		/* Widget record for text widget. */
    int mask)			/* OR'd collection of bits showing what has
				 * changed. */
{
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    GC newGC;
    XGCValues gcValues;

    /*
     * Schedule the window redisplay. See TkTextChanged for the reason why
     * this has to be done before any calls to FreeDLines.
     */

    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
	Tcl_DoWhenIdle(DisplayText, textPtr);
    }
    dInfoPtr->flags |= REDRAW_PENDING|REDRAW_BORDERS|DINFO_OUT_OF_DATE
	    |REPICK_NEEDED;

    /*
     * (Re-)create the graphics context for drawing the traversal highlight.
     */

    gcValues.graphics_exposures = False;
    newGC = Tk_GetGC(textPtr->tkwin, GCGraphicsExposures, &gcValues);
    if (dInfoPtr->copyGC != None) {
	Tk_FreeGC(textPtr->display, dInfoPtr->copyGC);
    }
    dInfoPtr->copyGC = newGC;

    /*
     * Throw away all the current layout information.
     */

    FreeDLines(textPtr, dInfoPtr->dLinePtr, NULL, DLINE_UNLINK);
    dInfoPtr->dLinePtr = NULL;

    /*
     * Recompute some overall things for the layout. Even if the window gets
     * very small, pretend that there's at least one pixel of drawing space in
     * it.
     */

    if (textPtr->highlightWidth < 0) {
	textPtr->highlightWidth = 0;
    }
    dInfoPtr->x = textPtr->highlightWidth + textPtr->borderWidth
	    + textPtr->padX;
    dInfoPtr->y = textPtr->highlightWidth + textPtr->borderWidth
	    + textPtr->padY;
    dInfoPtr->maxX = Tk_Width(textPtr->tkwin) - textPtr->highlightWidth
	    - textPtr->borderWidth - textPtr->padX;
    if (dInfoPtr->maxX <= dInfoPtr->x) {
	dInfoPtr->maxX = dInfoPtr->x + 1;
    }

    /*
     * This is the only place where dInfoPtr->maxY is set.
     */

    dInfoPtr->maxY = Tk_Height(textPtr->tkwin) - textPtr->highlightWidth
	    - textPtr->borderWidth - textPtr->padY;
    if (dInfoPtr->maxY <= dInfoPtr->y) {
	dInfoPtr->maxY = dInfoPtr->y + 1;
    }
    dInfoPtr->topOfEof = dInfoPtr->maxY;

    /*
     * If the upper-left character isn't the first in a line, recompute it.
     * This is necessary because a change in the window's size or options
     * could change the way lines wrap.
     */

    if (textPtr->topIndex.byteIndex != 0) {
	TkTextFindDisplayLineEnd(textPtr, &textPtr->topIndex, 0, NULL);
    }

    /*
     * Invalidate cached scrollbar positions, so that scrollbars sliders will
     * be udpated.
     */

    dInfoPtr->xScrollFirst = dInfoPtr->xScrollLast = -1;
    dInfoPtr->yScrollFirst = dInfoPtr->yScrollLast = -1;

    if (mask & TK_TEXT_LINE_GEOMETRY) {
	/*
	 * Set up line metric recalculation.
	 *
	 * Avoid the special zero value, since that is used to mark individual
	 * lines as being out of date.
	 */

	if ((++dInfoPtr->lineMetricUpdateEpoch) == 0) {
	    dInfoPtr->lineMetricUpdateEpoch++;
	}

	dInfoPtr->currentMetricUpdateLine = -1;

	/*
	 * Also cancel any partial line-height calculations (for long-wrapped
	 * lines) in progress.
	 */

	dInfoPtr->metricEpoch = -1;

	if (dInfoPtr->lineUpdateTimer == NULL) {
	    textPtr->refCount++;
	    dInfoPtr->lineUpdateTimer = Tcl_CreateTimerHandler(1,
		    AsyncUpdateLineMetrics, textPtr);
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextSetYView --
 *
 *	This function is called to specify what lines are to be displayed in a
 *	text widget.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The display will (eventually) be updated so that the position given by
 *	"indexPtr" is visible on the screen at the position determined by
 *	"pickPlace".
 *
 *----------------------------------------------------------------------
 */

void
TkTextSetYView(
    TkText *textPtr,		/* Widget record for text widget. */
    TkTextIndex *indexPtr,	/* Position that is to appear somewhere in the
				 * view. */
    int pickPlace)		/* 0 means the given index must appear exactly
				 * at the top of the screen. TK_TEXT_PICKPLACE
				 * (-1) means we get to pick where it appears:
				 * minimize screen motion or else display line
				 * at center of screen. TK_TEXT_NOPIXELADJUST
				 * (-2) indicates to make the given index the
				 * top line, but if it is already the top
				 * line, don't nudge it up or down by a few
				 * pixels just to make sure it is entirely
				 * displayed. Positive numbers indicate the
				 * number of pixels of the index's line which
				 * are to be off the top of the screen. */
{
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    register DLine *dlPtr;
    int bottomY, close, lineIndex;
    TkTextIndex tmpIndex, rounded;
    int lineHeight;

    /*
     * If the specified position is the extra line at the end of the text,
     * round it back to the last real line.
     */

    lineIndex = TkBTreeLinesTo(textPtr, indexPtr->linePtr);
    if (lineIndex == TkBTreeNumLines(indexPtr->tree, textPtr)) {
	TkTextIndexBackChars(textPtr, indexPtr, 1, &rounded, COUNT_INDICES);
	indexPtr = &rounded;
    }

    if (pickPlace == TK_TEXT_NOPIXELADJUST) {
	if (textPtr->topIndex.linePtr == indexPtr->linePtr
		&& textPtr->topIndex.byteIndex == indexPtr->byteIndex) {
	    pickPlace = dInfoPtr->topPixelOffset;
	} else {
	    pickPlace = 0;
	}
    }

    if (pickPlace != TK_TEXT_PICKPLACE) {
	/*
	 * The specified position must go at the top of the screen. Just leave
	 * all the DLine's alone: we may be able to reuse some of the
	 * information that's currently on the screen without redisplaying it
	 * all.
	 */

	textPtr->topIndex = *indexPtr;
	if (indexPtr->byteIndex != 0) {
	    TkTextFindDisplayLineEnd(textPtr, &textPtr->topIndex, 0, NULL);
	}
	dInfoPtr->newTopPixelOffset = pickPlace;
	goto scheduleUpdate;
    }

    /*
     * We have to pick where to display the index. First, bring the display
     * information up to date and see if the index will be completely visible
     * in the current screen configuration. If so then there's nothing to do.
     */

    if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
	UpdateDisplayInfo(textPtr);
    }
    dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
    if (dlPtr != NULL) {
	if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
	    /*
	     * Part of the line hangs off the bottom of the screen; pretend
	     * the whole line is off-screen.
	     */

	    dlPtr = NULL;
	} else if ((dlPtr->index.linePtr == indexPtr->linePtr)
		&& (dlPtr->index.byteIndex <= indexPtr->byteIndex)) {
	    if (dInfoPtr->dLinePtr == dlPtr && dInfoPtr->topPixelOffset != 0) {
		/*
		 * It is on the top line, but that line is hanging off the top
		 * of the screen. Change the top overlap to zero and update.
		 */

		dInfoPtr->newTopPixelOffset = 0;
		goto scheduleUpdate;
	    }
	    return;
	}
    }

    /*
     * The desired line isn't already on-screen. Figure out what it means to
     * be "close" to the top or bottom of the screen. Close means within 1/3
     * of the screen height or within three lines, whichever is greater.
     *
     * If the line is not close, place it in the center of the window.
     */

    lineHeight = CalculateDisplayLineHeight(textPtr, indexPtr, NULL, NULL);

    /*
     * It would be better if 'bottomY' were calculated using the actual height
     * of the given line, not 'textPtr->charHeight'.
     */

    bottomY = (dInfoPtr->y + dInfoPtr->maxY + lineHeight)/2;
    close = (dInfoPtr->maxY - dInfoPtr->y)/3;
    if (close < 3*textPtr->charHeight) {
	close = 3*textPtr->charHeight;
    }
    if (dlPtr != NULL) {
	int overlap;

	/*
	 * The desired line is above the top of screen. If it is "close" to
	 * the top of the window then make it the top line on the screen.
	 * MeasureUp counts from the bottom of the given index upwards, so we
	 * add an extra half line to be sure we count far enough.
	 */

	MeasureUp(textPtr, &textPtr->topIndex, close + textPtr->charHeight/2,
		&tmpIndex, &overlap);
	if (TkTextIndexCmp(&tmpIndex, indexPtr) <= 0) {
	    textPtr->topIndex = *indexPtr;
	    TkTextFindDisplayLineEnd(textPtr, &textPtr->topIndex, 0, NULL);
	    dInfoPtr->newTopPixelOffset = 0;
	    goto scheduleUpdate;
	}
    } else {
	int overlap;

	/*
	 * The desired line is below the bottom of the screen. If it is
	 * "close" to the bottom of the screen then position it at the bottom
	 * of the screen.
	 */

	MeasureUp(textPtr, indexPtr, close + lineHeight
		- textPtr->charHeight/2, &tmpIndex, &overlap);
	if (FindDLine(dInfoPtr->dLinePtr, &tmpIndex) != NULL) {
	    bottomY = dInfoPtr->maxY - dInfoPtr->y;
	}
    }

    /*
     * Our job now is to arrange the display so that indexPtr appears as low
     * on the screen as possible but with its bottom no lower than bottomY.
     * BottomY is the bottom of the window if the desired line is just below
     * the current screen, otherwise it is a half-line lower than the center
     * of the window.
     */

    MeasureUp(textPtr, indexPtr, bottomY, &textPtr->topIndex,
	    &dInfoPtr->newTopPixelOffset);

  scheduleUpdate:
    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
	Tcl_DoWhenIdle(DisplayText, textPtr);
    }
    dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
}

/*
 *--------------------------------------------------------------
 *
 * TkTextMeasureDown --
 *
 *	Given one index, find the index of the first character on the highest
 *	display line that would be displayed no more than "distance" pixels
 *	below the top of the given index.
 *
 * Results:
 *	The srcPtr is manipulated in place to reflect the new position. We
 *	return the number of pixels by which 'distance' overlaps the srcPtr.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */

int
TkTextMeasureDown(
    TkText *textPtr,		/* Text widget in which to measure. */
    TkTextIndex *srcPtr,	/* Index of character from which to start
				 * measuring. */
    int distance)		/* Vertical distance in pixels measured from
				 * the top pixel in srcPtr's logical line. */
{
    TkTextLine *lastLinePtr;
    DLine *dlPtr;
    TkTextIndex loop;

    lastLinePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree, textPtr,
	    TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr));

    do {
	dlPtr = LayoutDLine(textPtr, srcPtr);
	dlPtr->nextPtr = NULL;

	if (distance < dlPtr->height) {
	    FreeDLines(textPtr, dlPtr, NULL, DLINE_FREE_TEMP);
	    break;
	}
	distance -= dlPtr->height;
	TkTextIndexForwBytes(textPtr, srcPtr, dlPtr->byteCount, &loop);
	FreeDLines(textPtr, dlPtr, NULL, DLINE_FREE_TEMP);
	if (loop.linePtr == lastLinePtr) {
	    break;
	}
	*srcPtr = loop;
    } while (distance > 0);

    return distance;
}

/*
 *--------------------------------------------------------------
 *
 * MeasureUp --
 *
 *	Given one index, find the index of the first character on the highest
 *	display line that would be displayed no more than "distance" pixels
 *	above the given index.
 *
 *	If this function is called with distance=0, it simply finds the first
 *	index on the same display line as srcPtr. However, there is a another
 *	function TkTextFindDisplayLineEnd designed just for that task which is
 *	probably better to use.
 *
 * Results:
 *	*dstPtr is filled in with the index of the first character on a
 *	display line. The display line is found by measuring up "distance"
 *	pixels above the pixel just below an imaginary display line that
 *	contains srcPtr. If the display line that covers this coordinate
 *	actually extends above the coordinate, then return any excess pixels
 *	in *overlap, if that is non-NULL.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */

static void
MeasureUp(
    TkText *textPtr,		/* Text widget in which to measure. */
    const TkTextIndex *srcPtr,	/* Index of character from which to start
				 * measuring. */
    int distance,		/* Vertical distance in pixels measured from
				 * the pixel just below the lowest one in
				 * srcPtr's line. */
    TkTextIndex *dstPtr,	/* Index to fill in with result. */
    int *overlap)		/* Used to store how much of the final index
				 * returned was not covered by 'distance'. */
{
    int lineNum;		/* Number of current line. */
    int bytesToCount;		/* Maximum number of bytes to measure in
				 * current line. */
    TkTextIndex index;
    DLine *dlPtr, *lowestPtr;

    bytesToCount = srcPtr->byteIndex + 1;
    index.tree = srcPtr->tree;
    for (lineNum = TkBTreeLinesTo(textPtr, srcPtr->linePtr); lineNum >= 0;
	    lineNum--) {
	/*
	 * Layout an entire text line (potentially > 1 display line).
	 *
	 * For the first line, which contains srcPtr, only layout the part up
	 * through srcPtr (bytesToCount is non-infinite to accomplish this).
	 * Make a list of all the display lines in backwards order (the lowest
	 * DLine on the screen is first in the list).
	 */

	index.linePtr = TkBTreeFindLine(srcPtr->tree, textPtr, lineNum);
	index.byteIndex = 0;
	lowestPtr = NULL;
	do {
	    dlPtr = LayoutDLine(textPtr, &index);
	    dlPtr->nextPtr = lowestPtr;
	    lowestPtr = dlPtr;
	    TkTextIndexForwBytes(textPtr, &index, dlPtr->byteCount, &index);
	    bytesToCount -= dlPtr->byteCount;
	} while (bytesToCount>0 && index.linePtr==dlPtr->index.linePtr);

	/*
	 * Scan through the display lines to see if we've covered enough
	 * vertical distance. If so, save the starting index for the line at
	 * the desired location. If distance was zero to start with then we
	 * simply get the first index on the same display line as the original
	 * index.
	 */

	for (dlPtr = lowestPtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
	    distance -= dlPtr->height;
	    if (distance <= 0) {
		*dstPtr = dlPtr->index;
		if (overlap != NULL) {
		    *overlap = -distance;
		}
		break;
	    }
	}

	/*
	 * Discard the display lines, then either return or prepare for the
	 * next display line to lay out.
	 */

	FreeDLines(textPtr, lowestPtr, NULL, DLINE_FREE);
	if (distance <= 0) {
	    return;
	}
	bytesToCount = INT_MAX;		/* Consider all chars. in next line. */
    }

    /*
     * Ran off the beginning of the text. Return the first character in the
     * text.
     */

    TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr, 0, 0, dstPtr);
    if (overlap != NULL) {
	*overlap = 0;
    }
}

/*
 *--------------------------------------------------------------
 *
 * TkTextSeeCmd --
 *
 *	This function is invoked to process the "see" option for the widget
 *	command for text widgets. See the user documentation for details on
 *	what it does.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the user documentation.
 *
 *--------------------------------------------------------------
 */

int
TkTextSeeCmd(
    TkText *textPtr,		/* Information about text widget. */
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. Someone else has already
				 * parsed this command enough to know that
				 * objv[1] is "see". */
{
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    TkTextIndex index;
    int x, y, width, height, lineWidth, byteCount, oneThird, delta;
    DLine *dlPtr;
    TkTextDispChunk *chunkPtr;

    if (objc != 3) {
	Tcl_WrongNumArgs(interp, 2, objv, "index");
	return TCL_ERROR;
    }
    if (TkTextGetObjIndex(interp, textPtr, objv[2], &index) != TCL_OK) {
	return TCL_ERROR;
    }

    /*
     * If the specified position is the extra line at the end of the text,
     * round it back to the last real line.
     */

    if (TkBTreeLinesTo(textPtr, index.linePtr)
	    == TkBTreeNumLines(index.tree, textPtr)) {
	TkTextIndexBackChars(textPtr, &index, 1, &index, COUNT_INDICES);
    }

    /*
     * First get the desired position into the vertical range of the window.
     */

    TkTextSetYView(textPtr, &index, TK_TEXT_PICKPLACE);

    /*
     * Now make sure that the character is in view horizontally.
     */

    if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
	UpdateDisplayInfo(textPtr);
    }
    lineWidth = dInfoPtr->maxX - dInfoPtr->x;
    if (dInfoPtr->maxLength < lineWidth) {
	return TCL_OK;
    }

    /*
     * Find the chunk that contains the desired index. dlPtr may be NULL if
     * the widget is not mapped. [Bug #641778]
     */

    dlPtr = FindDLine(dInfoPtr->dLinePtr, &index);
    if (dlPtr == NULL) {
	return TCL_OK;
    }

    byteCount = index.byteIndex - dlPtr->index.byteIndex;
    for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL ;
	    chunkPtr = chunkPtr->nextPtr) {
	if (byteCount < chunkPtr->numBytes) {
	    break;
	}
	byteCount -= chunkPtr->numBytes;
    }

    /*
     * Call a chunk-specific function to find the horizontal range of the
     * character within the chunk. chunkPtr is NULL if trying to see in elided
     * region.
     */

    if (chunkPtr != NULL) {
	chunkPtr->bboxProc(textPtr, chunkPtr, byteCount,
		dlPtr->y + dlPtr->spaceAbove,
		dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
		dlPtr->baseline - dlPtr->spaceAbove, &x, &y, &width,
		&height);
	delta = x - dInfoPtr->curXPixelOffset;
	oneThird = lineWidth/3;
	if (delta < 0) {
	    if (delta < -oneThird) {
		dInfoPtr->newXPixelOffset = x - lineWidth/2;
	    } else {
		dInfoPtr->newXPixelOffset += delta;
	    }
	} else {
	    delta -= lineWidth - width;
	    if (delta <= 0) {
		return TCL_OK;
	    } else if (delta > oneThird) {
		dInfoPtr->newXPixelOffset = x - lineWidth/2;
	    } else {
		dInfoPtr->newXPixelOffset += delta;
	    }
	}
    }
    dInfoPtr->flags |= DINFO_OUT_OF_DATE;
    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
	dInfoPtr->flags |= REDRAW_PENDING;
	Tcl_DoWhenIdle(DisplayText, textPtr);
    }
    return TCL_OK;
}

/*
 *--------------------------------------------------------------
 *
 * TkTextXviewCmd --
 *
 *	This function is invoked to process the "xview" option for the widget
 *	command for text widgets. See the user documentation for details on
 *	what it does.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the user documentation.
 *
 *--------------------------------------------------------------
 */

int
TkTextXviewCmd(
    TkText *textPtr,		/* Information about text widget. */
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. Someone else has already
				 * parsed this command enough to know that
				 * objv[1] is "xview". */
{
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    int type, count;
    double fraction;

    if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
	UpdateDisplayInfo(textPtr);
    }

    if (objc == 2) {
	GetXView(interp, textPtr, 0);
	return TCL_OK;
    }

    type = TextGetScrollInfoObj(interp, textPtr, objc, objv,
	    &fraction, &count);
    switch (type) {
    case TKTEXT_SCROLL_ERROR:
	return TCL_ERROR;
    case TKTEXT_SCROLL_MOVETO:
	if (fraction > 1.0) {
	    fraction = 1.0;
	}
	if (fraction < 0) {
	    fraction = 0;
	}
	dInfoPtr->newXPixelOffset = (int)
		(fraction * dInfoPtr->maxLength + 0.5);
	break;
    case TKTEXT_SCROLL_PAGES: {
	int pixelsPerPage;

	pixelsPerPage = (dInfoPtr->maxX-dInfoPtr->x) - 2*textPtr->charWidth;
	if (pixelsPerPage < 1) {
	    pixelsPerPage = 1;
	}
	dInfoPtr->newXPixelOffset += pixelsPerPage * count;
	break;
    }
    case TKTEXT_SCROLL_UNITS:
	dInfoPtr->newXPixelOffset += count * textPtr->charWidth;
	break;
    case TKTEXT_SCROLL_PIXELS:
	dInfoPtr->newXPixelOffset += count;
	break;
    }

    dInfoPtr->flags |= DINFO_OUT_OF_DATE;
    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
	dInfoPtr->flags |= REDRAW_PENDING;
	Tcl_DoWhenIdle(DisplayText, textPtr);
    }
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * YScrollByPixels --
 *
 *	This function is called to scroll a text widget up or down by a given
 *	number of pixels.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The view in textPtr's window changes to reflect the value of "offset".
 *
 *----------------------------------------------------------------------
 */

static void
YScrollByPixels(
    TkText *textPtr,		/* Widget to scroll. */
    int offset)			/* Amount by which to scroll, in pixels.
				 * Positive means that information later in
				 * text becomes visible, negative means that
				 * information earlier in the text becomes
				 * visible. */
{
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;

    if (offset < 0) {
	/*
	 * Now we want to measure up this number of pixels from the top of the
	 * screen. But the top line may not be totally visible. Note that
	 * 'count' is negative here.
	 */

	offset -= CalculateDisplayLineHeight(textPtr,
		&textPtr->topIndex, NULL, NULL) - dInfoPtr->topPixelOffset;
	MeasureUp(textPtr, &textPtr->topIndex, -offset,
		&textPtr->topIndex, &dInfoPtr->newTopPixelOffset);
    } else if (offset > 0) {
	DLine *dlPtr;
	TkTextLine *lastLinePtr;
	TkTextIndex newIdx;

	/*
	 * Scrolling down by pixels. Layout lines starting at the top index
	 * and count through the desired vertical distance.
	 */

	lastLinePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree, textPtr,
		TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr));
	offset += dInfoPtr->topPixelOffset;
	dInfoPtr->newTopPixelOffset = 0;
	while (offset > 0) {
	    dlPtr = LayoutDLine(textPtr, &textPtr->topIndex);
	    dlPtr->nextPtr = NULL;
	    TkTextIndexForwBytes(textPtr, &textPtr->topIndex,
		    dlPtr->byteCount, &newIdx);
	    if (offset <= dlPtr->height) {
		/*
		 * Adjust the top overlap accordingly.
		 */

		dInfoPtr->newTopPixelOffset = offset;
	    }
	    offset -= dlPtr->height;
	    FreeDLines(textPtr, dlPtr, NULL, DLINE_FREE_TEMP);
	    if (newIdx.linePtr == lastLinePtr || offset <= 0) {
		break;
	    }
	    textPtr->topIndex = newIdx;
	}
    } else {
	/*
	 * offset = 0, so no scrolling required.
	 */

	return;
    }
    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
	Tcl_DoWhenIdle(DisplayText, textPtr);
    }
    dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
}

/*
 *----------------------------------------------------------------------
 *
 * YScrollByLines --
 *
 *	This function is called to scroll a text widget up or down by a given
 *	number of lines.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The view in textPtr's window changes to reflect the value of "offset".
 *
 *----------------------------------------------------------------------
 */

static void
YScrollByLines(
    TkText *textPtr,		/* Widget to scroll. */
    int offset)			/* Amount by which to scroll, in display
				 * lines. Positive means that information
				 * later in text becomes visible, negative
				 * means that information earlier in the text
				 * becomes visible. */
{
    int i, bytesToCount, lineNum;
    TkTextIndex newIdx, index;
    TkTextLine *lastLinePtr;
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    DLine *dlPtr, *lowestPtr;

    if (offset < 0) {
	/*
	 * Must scroll up (to show earlier information in the text). The code
	 * below is similar to that in MeasureUp, except that it counts lines
	 * instead of pixels.
	 */

	bytesToCount = textPtr->topIndex.byteIndex + 1;
	index.tree = textPtr->sharedTextPtr->tree;
	offset--;		/* Skip line containing topIndex. */
	for (lineNum = TkBTreeLinesTo(textPtr, textPtr->topIndex.linePtr);
		lineNum >= 0; lineNum--) {
	    index.linePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree,
		    textPtr, lineNum);
	    index.byteIndex = 0;
	    lowestPtr = NULL;
	    do {
		dlPtr = LayoutDLine(textPtr, &index);
		dlPtr->nextPtr = lowestPtr;
		lowestPtr = dlPtr;
		TkTextIndexForwBytes(textPtr, &index, dlPtr->byteCount,&index);
		bytesToCount -= dlPtr->byteCount;
	    } while ((bytesToCount > 0)
		    && (index.linePtr == dlPtr->index.linePtr));

	    for (dlPtr = lowestPtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
		offset++;
		if (offset == 0) {
		    textPtr->topIndex = dlPtr->index;
		    break;
		}
	    }

	    /*
	     * Discard the display lines, then either return or prepare for
	     * the next display line to lay out.
	     */

	    FreeDLines(textPtr, lowestPtr, NULL, DLINE_FREE);
	    if (offset >= 0) {
		goto scheduleUpdate;
	    }
	    bytesToCount = INT_MAX;
	}

	/*
	 * Ran off the beginning of the text. Return the first character in
	 * the text, and make sure we haven't left anything overlapping the
	 * top window border.
	 */

	TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr, 0, 0,
		&textPtr->topIndex);
	dInfoPtr->newTopPixelOffset = 0;
    } else {
	/*
	 * Scrolling down, to show later information in the text. Just count
	 * lines from the current top of the window.
	 */

	lastLinePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree, textPtr,
		TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr));
	for (i = 0; i < offset; i++) {
	    dlPtr = LayoutDLine(textPtr, &textPtr->topIndex);
	    if (dlPtr->length == 0 && dlPtr->height == 0) {
		offset++;
	    }
	    dlPtr->nextPtr = NULL;
	    TkTextIndexForwBytes(textPtr, &textPtr->topIndex,
		    dlPtr->byteCount, &newIdx);
	    FreeDLines(textPtr, dlPtr, NULL, DLINE_FREE);
	    if (newIdx.linePtr == lastLinePtr) {
		break;
	    }
	    textPtr->topIndex = newIdx;
	}
    }

  scheduleUpdate:
    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
	Tcl_DoWhenIdle(DisplayText, textPtr);
    }
    dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
}

/*
 *--------------------------------------------------------------
 *
 * TkTextYviewCmd --
 *
 *	This function is invoked to process the "yview" option for the widget
 *	command for text widgets. See the user documentation for details on
 *	what it does.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the user documentation.
 *
 *--------------------------------------------------------------
 */

int
TkTextYviewCmd(
    TkText *textPtr,		/* Information about text widget. */
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. Someone else has already
				 * parsed this command enough to know that
				 * objv[1] is "yview". */
{
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    int pickPlace, type;
    int pixels, count;
    int switchLength;
    double fraction;
    TkTextIndex index;

    if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
	UpdateDisplayInfo(textPtr);
    }

    if (objc == 2) {
	GetYView(interp, textPtr, 0);
	return TCL_OK;
    }

    /*
     * Next, handle the old syntax: "pathName yview ?-pickplace? where"
     */

    pickPlace = 0;
    if (Tcl_GetString(objv[2])[0] == '-') {
	register const char *switchStr =
		Tcl_GetStringFromObj(objv[2], &switchLength);

	if ((switchLength >= 2) && (strncmp(switchStr, "-pickplace",
		(unsigned) switchLength) == 0)) {
	    pickPlace = 1;
	    if (objc != 4) {
		Tcl_WrongNumArgs(interp, 3, objv, "lineNum|index");
		return TCL_ERROR;
	    }
	}
    }
    if ((objc == 3) || pickPlace) {
	int lineNum;

	if (Tcl_GetIntFromObj(interp, objv[2+pickPlace], &lineNum) == TCL_OK) {
	    TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
		    lineNum, 0, &index);
	    TkTextSetYView(textPtr, &index, 0);
	    return TCL_OK;
	}

	/*
	 * The argument must be a regular text index.
	 */

	Tcl_ResetResult(interp);
	if (TkTextGetObjIndex(interp, textPtr, objv[2+pickPlace],
		&index) != TCL_OK) {
	    return TCL_ERROR;
	}
	TkTextSetYView(textPtr, &index, (pickPlace ? TK_TEXT_PICKPLACE : 0));
	return TCL_OK;
    }

    /*
     * New syntax: dispatch based on objv[2].
     */

    type = TextGetScrollInfoObj(interp, textPtr, objc,objv, &fraction, &count);
    switch (type) {
    case TKTEXT_SCROLL_ERROR:
	return TCL_ERROR;
    case TKTEXT_SCROLL_MOVETO: {
	int numPixels = TkBTreeNumPixels(textPtr->sharedTextPtr->tree,
		textPtr);
	int topMostPixel;

	if (numPixels == 0) {
	    /*
	     * If the window is totally empty no scrolling is needed, and the
	     * TkTextMakePixelIndex call below will fail.
	     */

	    break;
	}
	if (fraction > 1.0) {
	    fraction = 1.0;
	}
	if (fraction < 0) {
	    fraction = 0;
	}

	/*
	 * Calculate the pixel count for the new topmost pixel in the topmost
	 * line of the window. Note that the interpretation of 'fraction' is
	 * that it counts from 0 (top pixel in buffer) to 1.0 (one pixel past
	 * the last pixel in buffer).
	 */

	topMostPixel = (int) (0.5 + fraction * numPixels);
	if (topMostPixel >= numPixels) {
	    topMostPixel = numPixels -1;
	}

	/*
	 * This function returns the number of pixels by which the given line
	 * should overlap the top of the visible screen.
	 *
	 * This is then used to provide smooth scrolling.
	 */

	pixels = TkTextMakePixelIndex(textPtr, topMostPixel, &index);
	TkTextSetYView(textPtr, &index, pixels);
	break;
    }
    case TKTEXT_SCROLL_PAGES: {
	/*
	 * Scroll up or down by screenfuls. Actually, use the window height
	 * minus two lines, so that there's some overlap between adjacent
	 * pages.
	 */

	int height = dInfoPtr->maxY - dInfoPtr->y;

	if (textPtr->charHeight * 4 >= height) {
	    /*
	     * A single line is more than a quarter of the display. We choose
	     * to scroll by 3/4 of the height instead.
	     */

	    pixels = 3*height/4;
	    if (pixels < textPtr->charHeight) {
		/*
		 * But, if 3/4 of the height is actually less than a single
		 * typical character height, then scroll by the minimum of the
		 * linespace or the total height.
		 */

		if (textPtr->charHeight < height) {
		    pixels = textPtr->charHeight;
		} else {
		    pixels = height;
		}
	    }
	    pixels *= count;
	} else {
	    pixels = (height - 2*textPtr->charHeight)*count;
	}
	YScrollByPixels(textPtr, pixels);
	break;
    }
    case TKTEXT_SCROLL_PIXELS:
	YScrollByPixels(textPtr, count);
	break;
    case TKTEXT_SCROLL_UNITS:
	YScrollByLines(textPtr, count);
	break;
    }
    return TCL_OK;
}

/*
 *--------------------------------------------------------------
 *
 * TkTextScanCmd --
 *
 *	This function is invoked to process the "scan" option for the widget
 *	command for text widgets. See the user documentation for details on
 *	what it does.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the user documentation.
 *
 *--------------------------------------------------------------
 */

int
TkTextScanCmd(
    register TkText *textPtr,	/* Information about text widget. */
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. Someone else has already
				 * parsed this command enough to know that
				 * objv[1] is "scan". */
{
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    TkTextIndex index;
    int c, x, y, totalScroll, gain=10;
    size_t length;

    if ((objc != 5) && (objc != 6)) {
	Tcl_WrongNumArgs(interp, 2, objv, "mark x y");
	Tcl_AppendResult(interp, " or \"", Tcl_GetString(objv[0]),
		" scan dragto x y ?gain?\"", NULL);
	/*
	 * Ought to be:
	 * Tcl_WrongNumArgs(interp, 2, objc, "dragto x y ?gain?");
	 */
	return TCL_ERROR;
    }
    if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) {
	return TCL_ERROR;
    }
    if (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) {
	return TCL_ERROR;
    }
    if ((objc == 6) && (Tcl_GetIntFromObj(interp, objv[5], &gain) != TCL_OK)) {
	return TCL_ERROR;
    }
    c = Tcl_GetString(objv[2])[0];
    length = strlen(Tcl_GetString(objv[2]));
    if (c=='d' && strncmp(Tcl_GetString(objv[2]), "dragto", length)==0) {
	int newX, maxX;

	/*
	 * Amplify the difference between the current position and the mark
	 * position to compute how much the view should shift, then update the
	 * mark position to correspond to the new view. If we run off the edge
	 * of the text, reset the mark point so that the current position
	 * continues to correspond to the edge of the window. This means that
	 * the picture will start dragging as soon as the mouse reverses
	 * direction (without this reset, might have to slide mouse a long
	 * ways back before the picture starts moving again).
	 */

	newX = dInfoPtr->scanMarkXPixel + gain*(dInfoPtr->scanMarkX - x);
	maxX = 1 + dInfoPtr->maxLength - (dInfoPtr->maxX - dInfoPtr->x);
	if (newX < 0) {
	    newX = 0;
	    dInfoPtr->scanMarkXPixel = 0;
	    dInfoPtr->scanMarkX = x;
	} else if (newX > maxX) {
	    newX = maxX;
	    dInfoPtr->scanMarkXPixel = maxX;
	    dInfoPtr->scanMarkX = x;
	}
	dInfoPtr->newXPixelOffset = newX;

	totalScroll = gain*(dInfoPtr->scanMarkY - y);
	if (totalScroll != dInfoPtr->scanTotalYScroll) {
	    index = textPtr->topIndex;
	    YScrollByPixels(textPtr, totalScroll-dInfoPtr->scanTotalYScroll);
	    dInfoPtr->scanTotalYScroll = totalScroll;
	    if ((index.linePtr == textPtr->topIndex.linePtr) &&
		    (index.byteIndex == textPtr->topIndex.byteIndex)) {
		dInfoPtr->scanTotalYScroll = 0;
		dInfoPtr->scanMarkY = y;
	    }
	}
	dInfoPtr->flags |= DINFO_OUT_OF_DATE;
	if (!(dInfoPtr->flags & REDRAW_PENDING)) {
	    dInfoPtr->flags |= REDRAW_PENDING;
	    Tcl_DoWhenIdle(DisplayText, textPtr);
	}
    } else if (c=='m' && strncmp(Tcl_GetString(objv[2]), "mark", length)==0) {
	dInfoPtr->scanMarkXPixel = dInfoPtr->newXPixelOffset;
	dInfoPtr->scanMarkX = x;
	dInfoPtr->scanTotalYScroll = 0;
	dInfoPtr->scanMarkY = y;
    } else {
	Tcl_AppendResult(interp, "bad scan option \"", Tcl_GetString(objv[2]),
		"\": must be mark or dragto", NULL);
	return TCL_ERROR;
    }
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * GetXView --
 *
 *	This function computes the fractions that indicate what's visible in a
 *	text window and, optionally, evaluates a Tcl script to report them to
 *	the text's associated scrollbar.
 *
 * Results:
 *	If report is zero, then the interp's result is filled in with two real
 *	numbers separated by a space, giving the position of the left and
 *	right edges of the window as fractions from 0 to 1, where 0 means the
 *	left edge of the text and 1 means the right edge. If report is
 *	non-zero, then the interp's result isn't modified directly, but
 *	instead a script is evaluated in interp to report the new horizontal
 *	scroll position to the scrollbar (if the scroll position hasn't
 *	changed then no script is invoked).
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static void
GetXView(
    Tcl_Interp *interp,		/* If "report" is FALSE, string describing
				 * visible range gets stored in the interp's
				 * result. */
    TkText *textPtr,		/* Information about text widget. */
    int report)			/* Non-zero means report info to scrollbar if
				 * it has changed. */
{
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    double first, last;
    int code;
    Tcl_Obj *listObj;

    if (dInfoPtr->maxLength > 0) {
	first = ((double) dInfoPtr->curXPixelOffset)
		/ dInfoPtr->maxLength;
	last = ((double) (dInfoPtr->curXPixelOffset + dInfoPtr->maxX
		- dInfoPtr->x))/dInfoPtr->maxLength;
	if (last > 1.0) {
	    last = 1.0;
	}
    } else {
	first = 0;
	last = 1.0;
    }
    if (!report) {
	listObj = Tcl_NewListObj(0, NULL);
	Tcl_ListObjAppendElement(interp, listObj, Tcl_NewDoubleObj(first));
	Tcl_ListObjAppendElement(interp, listObj, Tcl_NewDoubleObj(last));
	Tcl_SetObjResult(interp, listObj);
	return;
    }
    if (FP_EQUAL_SCALE(first, dInfoPtr->xScrollFirst, dInfoPtr->maxLength) &&
	    FP_EQUAL_SCALE(last, dInfoPtr->xScrollLast, dInfoPtr->maxLength)) {
	return;
    }
    dInfoPtr->xScrollFirst = first;
    dInfoPtr->xScrollLast = last;
    if (textPtr->xScrollCmd != NULL) {
	char buf1[TCL_DOUBLE_SPACE+1];
	char buf2[TCL_DOUBLE_SPACE+1];

	buf1[0] = ' ';
	buf2[0] = ' ';
	Tcl_PrintDouble(NULL, first, buf1+1);
	Tcl_PrintDouble(NULL, last, buf2+1);
	code = Tcl_VarEval(interp, textPtr->xScrollCmd, buf1, buf2, NULL);
	if (code != TCL_OK) {
	    Tcl_AddErrorInfo(interp,
		    "\n    (horizontal scrolling command executed by text)");
	    Tcl_BackgroundException(interp, code);
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * GetYPixelCount --
 *
 *	How many pixels are there between the absolute top of the widget and
 *	the top of the given DLine.
 *
 *	While this function will work for any valid DLine, it is only ever
 *	called when dlPtr is the first display line in the widget (by
 *	'GetYView'). This means that usually this function is a very quick
 *	calculation, since it can use the pre-calculated linked-list of DLines
 *	for height information.
 *
 *	The only situation where this breaks down is if dlPtr's logical line
 *	wraps enough times to fill the text widget's current view - in this
 *	case we won't have enough dlPtrs in the linked list to be able to
 *	subtract off what we want.
 *
 * Results:
 *	The number of pixels.
 *
 *	This value has a valid range between '0' (the very top of the widget)
 *	and the number of pixels in the total widget minus the pixel-height of
 *	the last line.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
GetYPixelCount(
    TkText *textPtr,		/* Information about text widget. */
    DLine *dlPtr)		/* Information about the layout of a given
				 * index. */
{
    TkTextLine *linePtr = dlPtr->index.linePtr;
    int count;

    /*
     * Get the pixel count to the top of dlPtr's logical line. The rest of the
     * function is then concerned with updating 'count' for any difference
     * between the top of the logical line and the display line.
     */

    count = TkBTreePixelsTo(textPtr, linePtr);

    /*
     * For the common case where this dlPtr is also the start of the logical
     * line, we can return right away. Note the implicit assumption here that
     * the start of a logical line is always the start of a display line (if
     * the 'elide won't elide first newline' bug is fixed, this will no longer
     * necessarily be true).
     */

    if (dlPtr->index.byteIndex == 0) {
	return count;
    }

    /*
     * Add on the logical line's height to reach one pixel beyond the bottom
     * of the logical line. And then subtract off the heights of all the
     * display lines from dlPtr to the end of its logical line.
     *
     * A different approach would be to lay things out from the start of the
     * logical line until we reach dlPtr, but since none of those are
     * pre-calculated, it'll usually take a lot longer. (But there are cases
     * where it would be more efficient: say if we're on the second of 1000
     * wrapped lines all from a single logical line - but that sort of
     * optimization is left for the future).
     */

    count += TkBTreeLinePixelCount(textPtr, linePtr);

    do {
	count -= dlPtr->height;
	if (dlPtr->nextPtr == NULL) {
	    /*
	     * We've run out of pre-calculated display lines, so we have to
	     * lay them out ourselves until the end of the logical line. Here
	     * is where we could be clever and ask: what's faster, to layout
	     * all lines from here to line-end, or all lines from the original
	     * dlPtr to the line-start? We just assume the former.
	     */

	    TkTextIndex index;
	    int notFirst = 0;

	    while (1) {
		TkTextIndexForwBytes(textPtr, &dlPtr->index,
			dlPtr->byteCount, &index);
		if (notFirst) {
		    FreeDLines(textPtr, dlPtr, NULL, DLINE_FREE_TEMP);
		}
		if (index.linePtr != linePtr) {
		    break;
		}
		dlPtr = LayoutDLine(textPtr, &index);

		if (tkTextDebug) {
		    char string[TK_POS_CHARS];

		    /*
		     * Debugging is enabled, so keep a log of all the lines
		     * whose height was recalculated. The test suite uses this
		     * information.
		     */

		    TkTextPrintIndex(textPtr, &index, string);
		    LOG("tk_textHeightCalc", string);
		}
		count -= dlPtr->height;
		notFirst = 1;
	    }
	    break;
	}
	dlPtr = dlPtr->nextPtr;
    } while (dlPtr->index.linePtr == linePtr);

    return count;
}

/*
 *----------------------------------------------------------------------
 *
 * GetYView --
 *
 *	This function computes the fractions that indicate what's visible in a
 *	text window and, optionally, evaluates a Tcl script to report them to
 *	the text's associated scrollbar.
 *
 * Results:
 *	If report is zero, then the interp's result is filled in with two real
 *	numbers separated by a space, giving the position of the top and
 *	bottom of the window as fractions from 0 to 1, where 0 means the
 *	beginning of the text and 1 means the end. If report is non-zero, then
 *	the interp's result isn't modified directly, but a script is evaluated
 *	in interp to report the new scroll position to the scrollbar (if the
 *	scroll position hasn't changed then no script is invoked).
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static void
GetYView(
    Tcl_Interp *interp,		/* If "report" is FALSE, string describing
				 * visible range gets stored in the interp's
				 * result. */
    TkText *textPtr,		/* Information about text widget. */
    int report)			/* Non-zero means report info to scrollbar if
				 * it has changed. */
{
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    double first, last;
    DLine *dlPtr;
    int totalPixels, code, count;
    Tcl_Obj *listObj;

    dlPtr = dInfoPtr->dLinePtr;

    if (dlPtr == NULL) {
	return;
    }

    totalPixels = TkBTreeNumPixels(textPtr->sharedTextPtr->tree, textPtr);

    if (totalPixels == 0) {
	first = 0.0;
	last = 1.0;
    } else {
	/*
	 * Get the pixel count for the first visible pixel of the first
	 * visible line. If the first visible line is only partially visible,
	 * then we use 'topPixelOffset' to get the difference.
	 */

	count = GetYPixelCount(textPtr, dlPtr);
	first = (count + dInfoPtr->topPixelOffset) / (double) totalPixels;

	/*
	 * Add on the total number of visible pixels to get the count to one
	 * pixel _past_ the last visible pixel. This is how the 'yview'
	 * command is documented, and also explains why we are dividing by
	 * 'totalPixels' and not 'totalPixels-1'.
	 */

	while (1) {
	    int extra;

	    count += dlPtr->height;
	    extra = dlPtr->y + dlPtr->height - dInfoPtr->maxY;
	    if (extra > 0) {
		/*
		 * This much of the last line is not visible, so don't count
		 * these pixels. Since we've reached the bottom of the window,
		 * we break out of the loop.
		 */

		count -= extra;
		break;
	    }
	    if (dlPtr->nextPtr == NULL) {
		break;
	    }
	    dlPtr = dlPtr->nextPtr;
	}

	if (count > totalPixels) {
	    /*
	     * It can be possible, if we do not update each line's pixelHeight
	     * cache when we lay out individual DLines that the count
	     * generated here is more up-to-date than that maintained by the
	     * BTree. In such a case, the best we can do here is to fix up
	     * 'count' and continue, which might result in small, temporary
	     * perturbations to the size of the scrollbar. This is basically
	     * harmless, but in a perfect world we would not have this
	     * problem.
	     *
	     * For debugging purposes, if anyone wishes to improve the text
	     * widget further, the following 'panic' can be activated. In
	     * principle it should be possible to ensure the BTree is always
	     * at least as up to date as the display, so in the future we
	     * might be able to leave the 'panic' in permanently when we
	     * believe we have resolved the cache synchronisation issue.
	     *
	     * However, to achieve that goal would, I think, require a fairly
	     * substantial refactorisation of the code in this file so that
	     * there is much more obvious and explicit coordination between
	     * calls to LayoutDLine and updating of each TkTextLine's
	     * pixelHeight. The complicated bit is that LayoutDLine deals with
	     * individual display lines, but pixelHeight is for a logical
	     * line.
	     */

#if 0
	    Tcl_Panic("Counted more pixels (%d) than expected (%d) total "
		    "pixels in text widget scroll bar calculation.", count,
		    totalPixels);
#endif
	    count = totalPixels;
	}

	last = ((double) count)/((double)totalPixels);
    }

    if (!report) {
	listObj = Tcl_NewListObj(0,NULL);
	Tcl_ListObjAppendElement(interp, listObj, Tcl_NewDoubleObj(first));
	Tcl_ListObjAppendElement(interp, listObj, Tcl_NewDoubleObj(last));
	Tcl_SetObjResult(interp, listObj);
	return;
    }

    if (FP_EQUAL_SCALE(first, dInfoPtr->yScrollFirst, totalPixels) &&
	    FP_EQUAL_SCALE(last, dInfoPtr->yScrollLast, totalPixels)) {
	return;
    }

    dInfoPtr->yScrollFirst = first;
    dInfoPtr->yScrollLast = last;
    if (textPtr->yScrollCmd != NULL) {
	char buf1[TCL_DOUBLE_SPACE+1];
	char buf2[TCL_DOUBLE_SPACE+1];

	buf1[0] = ' ';
	buf2[0] = ' ';
	Tcl_PrintDouble(NULL, first, buf1+1);
	Tcl_PrintDouble(NULL, last, buf2+1);
	code = Tcl_VarEval(interp, textPtr->yScrollCmd, buf1, buf2, NULL);
	if (code != TCL_OK) {
	    Tcl_AddErrorInfo(interp,
		    "\n    (vertical scrolling command executed by text)");
	    Tcl_BackgroundException(interp, code);
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * AsyncUpdateYScrollbar --
 *
 *	This function is called to update the vertical scrollbar asychronously
 *	as the pixel height calculations progress for lines in the widget.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	See 'GetYView'. In particular the scrollbar position and size may be
 *	changed.
 *
 *----------------------------------------------------------------------
 */

static void
AsyncUpdateYScrollbar(
    ClientData clientData)	/* Information about widget. */
{
    register TkText *textPtr = clientData;

    textPtr->dInfoPtr->scrollbarTimer = NULL;

    if (!(textPtr->flags & DESTROYED)) {
	GetYView(textPtr->interp, textPtr, 1);
    }

    if (--textPtr->refCount == 0) {
	ckfree((char *) textPtr);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * FindDLine --
 *
 *	This function is called to find the DLine corresponding to a given
 *	text index.
 *
 * Results:
 *	The return value is a pointer to the first DLine found in the list
 *	headed by dlPtr that displays information at or after the specified
 *	position. If there is no such line in the list then NULL is returned.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static DLine *
FindDLine(
    register DLine *dlPtr,	/* Pointer to first in list of DLines to
				 * search. */
    const TkTextIndex *indexPtr)/* Index of desired character. */
{
    TkTextLine *linePtr;

    if (dlPtr == NULL) {
	return NULL;
    }
    if (TkBTreeLinesTo(NULL, indexPtr->linePtr)
	    < TkBTreeLinesTo(NULL, dlPtr->index.linePtr)) {
	/*
	 * The first display line is already past the desired line.
	 */

	return dlPtr;
    }

    /*
     * Find the first display line that covers the desired text line.
     */

    linePtr = dlPtr->index.linePtr;
    while (linePtr != indexPtr->linePtr) {
	while (dlPtr->index.linePtr == linePtr) {
	    dlPtr = dlPtr->nextPtr;
	    if (dlPtr == NULL) {
		return NULL;
	    }
	}

	/*
	 * VMD: some concern here as to whether this logic, or the caller's
	 * logic will work well with partial peer widgets.
	 */

	linePtr = TkBTreeNextLine(NULL, linePtr);
	if (linePtr == NULL) {
	    Tcl_Panic("FindDLine reached end of text");
	}
    }
    if (indexPtr->linePtr != dlPtr->index.linePtr) {
	return dlPtr;
    }

    /*
     * Now get to the right position within the text line.
     */

    while (indexPtr->byteIndex >= (dlPtr->index.byteIndex+dlPtr->byteCount)) {
	dlPtr = dlPtr->nextPtr;
	if ((dlPtr == NULL) || (dlPtr->index.linePtr != indexPtr->linePtr)) {
	    break;
	}
    }
    return dlPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextPixelIndex --
 *
 *	Given an (x,y) coordinate on the screen, find the location of the
 *	character closest to that location.
 *
 * Results:
 *	The index at *indexPtr is modified to refer to the character on the
 *	display that is closest to (x,y).
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void
TkTextPixelIndex(
    TkText *textPtr,		/* Widget record for text widget. */
    int x, int y,		/* Pixel coordinates of point in widget's
				 * window. */
    TkTextIndex *indexPtr,	/* This index gets filled in with the index of
				 * the character nearest to (x,y). */
    int *nearest)		/* If non-NULL then gets set to 0 if (x,y) is
				 * actually over the returned index, and 1 if
				 * it is just nearby (e.g. if x,y is on the
				 * border of the widget). */
{
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    register DLine *dlPtr, *validDlPtr;
    int nearby = 0;

    /*
     * Make sure that all of the layout information about what's displayed
     * where on the screen is up-to-date.
     */

    if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
	UpdateDisplayInfo(textPtr);
    }

    /*
     * If the coordinates are above the top of the window, then adjust them to
     * refer to the upper-right corner of the window. If they're off to one
     * side or the other, then adjust to the closest side.
     */

    if (y < dInfoPtr->y) {
	y = dInfoPtr->y;
	x = dInfoPtr->x;
	nearby = 1;
    }
    if (x >= dInfoPtr->maxX) {
	x = dInfoPtr->maxX - 1;
	nearby = 1;
    }
    if (x < dInfoPtr->x) {
	x = dInfoPtr->x;
	nearby = 1;
    }

    /*
     * Find the display line containing the desired y-coordinate.
     */

    if (dInfoPtr->dLinePtr == NULL) {
	if (nearest != NULL) {
	    *nearest = 1;
	}
	*indexPtr = textPtr->topIndex;
	return;
    }
    for (dlPtr = validDlPtr = dInfoPtr->dLinePtr;
	    y >= (dlPtr->y + dlPtr->height);
	    dlPtr = dlPtr->nextPtr) {
	if (dlPtr->chunkPtr != NULL) {
	    validDlPtr = dlPtr;
	}
	if (dlPtr->nextPtr == NULL) {
	    /*
	     * Y-coordinate is off the bottom of the displayed text. Use the
	     * last character on the last line.
	     */

	    x = dInfoPtr->maxX - 1;
	    nearby = 1;
	    break;
	}
    }
    if (dlPtr->chunkPtr == NULL) {
	dlPtr = validDlPtr;
    }

    if (nearest != NULL) {
	*nearest = nearby;
    }

    DlineIndexOfX(textPtr, dlPtr, x, indexPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * DlineIndexOfX --
 *
 *	Given an x coordinate in a display line, find the index of the
 *	character closest to that location.
 *
 *	This is effectively the opposite of DlineXOfIndex.
 *
 * Results:
 *	The index at *indexPtr is modified to refer to the character on the
 *	display line that is closest to x.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static void
DlineIndexOfX(
    TkText *textPtr,		/* Widget record for text widget. */
    DLine *dlPtr,		/* Display information for this display
				 * line. */
    int x,			/* Pixel x coordinate of point in widget's
				 * window. */
    TkTextIndex *indexPtr)	/* This index gets filled in with the index of
				 * the character nearest to x. */
{
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    register TkTextDispChunk *chunkPtr;

    /*
     * Scan through the line's chunks to find the one that contains the
     * desired x-coordinate. Before doing this, translate the x-coordinate
     * from the coordinate system of the window to the coordinate system of
     * the line (to take account of x-scrolling).
     */

    *indexPtr = dlPtr->index;
    x = x - dInfoPtr->x + dInfoPtr->curXPixelOffset;
    chunkPtr = dlPtr->chunkPtr;

    if (chunkPtr == NULL || x == 0) {
	/*
	 * This may occur if everything is elided, or if we're simply already
	 * at the beginning of the line.
	 */

	return;
    }

    while (x >= (chunkPtr->x + chunkPtr->width)) {
	/*
	 * Note that this forward then backward movement of the index can be
	 * problematic at the end of the buffer (we can't move forward, and
	 * then when we move backward, we do, leading to the wrong position).
	 * Hence when x == 0 we take special action above.
	 */

	if (TkTextIndexForwBytes(NULL,indexPtr,chunkPtr->numBytes,indexPtr)) {
	    /*
	     * We've reached the end of the text.
	     */

	    return;
	}
	if (chunkPtr->nextPtr == NULL) {
	    TkTextIndexBackChars(NULL, indexPtr, 1, indexPtr, COUNT_INDICES);
	    return;
	}
	chunkPtr = chunkPtr->nextPtr;
    }

    /*
     * If the chunk has more than one byte in it, ask it which character is at
     * the desired location. In this case we can manipulate
     * 'indexPtr->byteIndex' directly, because we know we're staying inside a
     * single logical line.
     */

    if (chunkPtr->numBytes > 1) {
	indexPtr->byteIndex += chunkPtr->measureProc(chunkPtr, x);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextIndexOfX --
 *
 *	Given a logical x coordinate (i.e. distance in pixels from the
 *	beginning of the display line, not taking into account any information
 *	about the window, scrolling etc.) on the display line starting with
 *	the given index, adjust that index to refer to the object under the x
 *	coordinate.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void
TkTextIndexOfX(
    TkText *textPtr,		/* Widget record for text widget. */
    int x,			/* The x coordinate for which we want the
				 * index. */
    TkTextIndex *indexPtr)	/* Index of display line start, which will be
				 * adjusted to the index under the given x
				 * coordinate. */
{
    DLine *dlPtr = LayoutDLine(textPtr, indexPtr);
    DlineIndexOfX(textPtr, dlPtr, x + textPtr->dInfoPtr->x
		- textPtr->dInfoPtr->curXPixelOffset, indexPtr);
    FreeDLines(textPtr, dlPtr, NULL, DLINE_FREE_TEMP);
}

/*
 *----------------------------------------------------------------------
 *
 * DlineXOfIndex --
 *
 *	Given a relative byte index on a given display line (i.e. the number
 *	of byte indices from the beginning of the given display line), find
 *	the x coordinate of that index within the abstract display line,
 *	without adjusting for the x-scroll state of the line.
 *
 *	This is effectively the opposite of DlineIndexOfX.
 *
 *	NB. The 'byteIndex' is relative to the display line, NOT the logical
 *	line.
 *
 * Results:
 *	The x coordinate.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
DlineXOfIndex(
    TkText *textPtr,		/* Widget record for text widget. */
    DLine *dlPtr,		/* Display information for this display
				 * line. */
    int byteIndex)		/* The byte index for which we want the
				 * coordinate. */
{
    register TkTextDispChunk *chunkPtr = dlPtr->chunkPtr;
    int x = 0;

    if (byteIndex == 0 || chunkPtr == NULL) {
	return x;
    }

    /*
     * Scan through the line's chunks to find the one that contains the
     * desired byte index.
     */

    chunkPtr = dlPtr->chunkPtr;
    while (byteIndex > 0) {
	if (byteIndex < chunkPtr->numBytes) {
	    int y, width, height;

	    chunkPtr->bboxProc(textPtr, chunkPtr, byteIndex,
		    dlPtr->y + dlPtr->spaceAbove,
		    dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
		    dlPtr->baseline - dlPtr->spaceAbove, &x, &y, &width,
		    &height);
	    break;
	}
	byteIndex -= chunkPtr->numBytes;
	if (chunkPtr->nextPtr == NULL || byteIndex == 0) {
	    x = chunkPtr->x + chunkPtr->width;
	    break;
	}
	chunkPtr = chunkPtr->nextPtr;
    }

    return x;
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextIndexBbox --
 *
 *	Given an index, find the bounding box of the screen area occupied by
 *	the entity (character, window, image) at that index.
 *
 * Results:
 *	Zero is returned if the index is on the screen. -1 means the index is
 *	not on the screen. If the return value is 0, then the bounding box of
 *	the part of the index that's visible on the screen is returned to
 *	*xPtr, *yPtr, *widthPtr, and *heightPtr.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
TkTextIndexBbox(
    TkText *textPtr,		/* Widget record for text widget. */
    const TkTextIndex *indexPtr,/* Index whose bounding box is desired. */
    int *xPtr, int *yPtr,	/* Filled with index's upper-left
				 * coordinate. */
    int *widthPtr, int *heightPtr,
				/* Filled in with index's dimensions. */
    int *charWidthPtr)		/* If the 'index' is at the end of a display
				 * line and therefore takes up a very large
				 * width, this is used to return the smaller
				 * width actually desired by the index. */
{
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    DLine *dlPtr;
    register TkTextDispChunk *chunkPtr;
    int byteIndex;

    /*
     * Make sure that all of the screen layout information is up to date.
     */

    if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
	UpdateDisplayInfo(textPtr);
    }

    /*
     * Find the display line containing the desired index.
     */

    dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
    if ((dlPtr == NULL) || (TkTextIndexCmp(&dlPtr->index, indexPtr) > 0)) {
	return -1;
    }

    /*
     * Find the chunk within the line that contains the desired index.
     */

    byteIndex = indexPtr->byteIndex - dlPtr->index.byteIndex;
    for (chunkPtr = dlPtr->chunkPtr; ; chunkPtr = chunkPtr->nextPtr) {
	if (chunkPtr == NULL) {
	    return -1;
	}
	if (byteIndex < chunkPtr->numBytes) {
	    break;
	}
	byteIndex -= chunkPtr->numBytes;
    }

    /*
     * Call a chunk-specific function to find the horizontal range of the
     * character within the chunk, then fill in the vertical range. The
     * x-coordinate returned by bboxProc is a coordinate within a line, not a
     * coordinate on the screen. Translate it to reflect horizontal scrolling.
     */

    chunkPtr->bboxProc(textPtr, chunkPtr, byteIndex,
	    dlPtr->y + dlPtr->spaceAbove,
	    dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
	    dlPtr->baseline - dlPtr->spaceAbove, xPtr, yPtr, widthPtr,
	    heightPtr);
    *xPtr = *xPtr + dInfoPtr->x - dInfoPtr->curXPixelOffset;
    if ((byteIndex == chunkPtr->numBytes-1) && (chunkPtr->nextPtr == NULL)) {
	/*
	 * Last character in display line. Give it all the space up to the
	 * line.
	 */

	if (charWidthPtr != NULL) {
	    *charWidthPtr = dInfoPtr->maxX - *xPtr;
	}
	if (*xPtr > dInfoPtr->maxX) {
	    *xPtr = dInfoPtr->maxX;
	}
	*widthPtr = dInfoPtr->maxX - *xPtr;
    } else {
	if (charWidthPtr != NULL) {
	    *charWidthPtr = *widthPtr;
	}
    }
    if (*widthPtr == 0) {
	/*
	 * With zero width (e.g. elided text) we just need to make sure it is
	 * onscreen, where the '=' case here is ok.
	 */

	if (*xPtr < dInfoPtr->x) {
	    return -1;
	}
    } else {
	if ((*xPtr + *widthPtr) <= dInfoPtr->x) {
	    return -1;
	}
    }
    if ((*xPtr + *widthPtr) > dInfoPtr->maxX) {
	*widthPtr = dInfoPtr->maxX - *xPtr;
	if (*widthPtr <= 0) {
	    return -1;
	}
    }
    if ((*yPtr + *heightPtr) > dInfoPtr->maxY) {
	*heightPtr = dInfoPtr->maxY - *yPtr;
	if (*heightPtr <= 0) {
	    return -1;
	}
    }
    return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextDLineInfo --
 *
 *	Given an index, return information about the display line containing
 *	that character.
 *
 * Results:
 *	Zero is returned if the character is on the screen. -1 means the
 *	character isn't on the screen. If the return value is 0, then
 *	information is returned in the variables pointed to by xPtr, yPtr,
 *	widthPtr, heightPtr, and basePtr.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
TkTextDLineInfo(
    TkText *textPtr,		/* Widget record for text widget. */
    const TkTextIndex *indexPtr,/* Index of character whose bounding box is
				 * desired. */
    int *xPtr, int *yPtr,	/* Filled with line's upper-left
				 * coordinate. */
    int *widthPtr, int *heightPtr,
				/* Filled in with line's dimensions. */
    int *basePtr)		/* Filled in with the baseline position,
				 * measured as an offset down from *yPtr. */
{
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    DLine *dlPtr;
    int dlx;

    /*
     * Make sure that all of the screen layout information is up to date.
     */

    if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
	UpdateDisplayInfo(textPtr);
    }

    /*
     * Find the display line containing the desired index.
     */

    dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
    if ((dlPtr == NULL) || (TkTextIndexCmp(&dlPtr->index, indexPtr) > 0)) {
	return -1;
    }

    dlx = (dlPtr->chunkPtr != NULL? dlPtr->chunkPtr->x: 0);
    *xPtr = dInfoPtr->x - dInfoPtr->curXPixelOffset + dlx;
    *widthPtr = dlPtr->length - dlx;
    *yPtr = dlPtr->y;
    if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
	*heightPtr = dInfoPtr->maxY - dlPtr->y;
    } else {
	*heightPtr = dlPtr->height;
    }
    *basePtr = dlPtr->baseline;
    return 0;
}

/*
 * Get bounding-box information about an elided chunk.
 */

static void
ElideBboxProc(
    TkText *textPtr,
    TkTextDispChunk *chunkPtr,	/* Chunk containing desired char. */
    int index,			/* Index of desired character within the
				 * chunk. */
    int y,			/* Topmost pixel in area allocated for this
				 * line. */
    int lineHeight,		/* Height of line, in pixels. */
    int baseline,		/* Location of line's baseline, in pixels
				 * measured down from y. */
    int *xPtr, int *yPtr,	/* Gets filled in with coords of character's
				 * upper-left pixel. X-coord is in same
				 * coordinate system as chunkPtr->x. */
    int *widthPtr,		/* Gets filled in with width of character, in
				 * pixels. */
    int *heightPtr)		/* Gets filled in with height of character, in
				 * pixels. */
{
    *xPtr = chunkPtr->x;
    *yPtr = y;
    *widthPtr = *heightPtr = 0;
}

/*
 * Measure an elided chunk.
 */

static int
ElideMeasureProc(
    TkTextDispChunk *chunkPtr,	/* Chunk containing desired coord. */
    int x)			/* X-coordinate, in same coordinate system as
				 * chunkPtr->x. */
{
    return 0 /*chunkPtr->numBytes - 1*/;
}

/*
 *--------------------------------------------------------------
 *
 * TkTextCharLayoutProc --
 *
 *	This function is the "layoutProc" for character segments.
 *
 * Results:
 *	If there is something to display for the chunk then a non-zero value
 *	is returned and the fields of chunkPtr will be filled in (see the
 *	declaration of TkTextDispChunk in tkText.h for details). If zero is
 *	returned it means that no characters from this chunk fit in the
 *	window. If -1 is returned it means that this segment just doesn't need
 *	to be displayed (never happens for text).
 *
 * Side effects:
 *	Memory is allocated to hold additional information about the chunk.
 *
 *--------------------------------------------------------------
 */

int
TkTextCharLayoutProc(
    TkText *textPtr,		/* Text widget being layed out. */
    TkTextIndex *indexPtr,	/* Index of first character to lay out
				 * (corresponds to segPtr and offset). */
    TkTextSegment *segPtr,	/* Segment being layed out. */
    int byteOffset,		/* Byte offset within segment of first
				 * character to consider. */
    int maxX,			/* Chunk must not occupy pixels at this
				 * position or higher. */
    int maxBytes,		/* Chunk must not include more than this many
				 * characters. */
    int noCharsYet,		/* Non-zero means no characters have been
				 * assigned to this display line yet. */
    TkWrapMode wrapMode,	/* How to handle line wrapping:
				 * TEXT_WRAPMODE_CHAR, TEXT_WRAPMODE_NONE, or
				 * TEXT_WRAPMODE_WORD. */
    register TkTextDispChunk *chunkPtr)
				/* Structure to fill in with information about
				 * this chunk. The x field has already been
				 * set by the caller. */
{
    Tk_Font tkfont;
    int nextX, bytesThatFit, count;
    CharInfo *ciPtr;
    char *p;
    TkTextSegment *nextPtr;
    Tk_FontMetrics fm;
#if TK_LAYOUT_WITH_BASE_CHUNKS
    const char *line;
    int lineOffset;
    BaseCharInfo *bciPtr;
    Tcl_DString *baseString;
#endif

    /*
     * Figure out how many characters will fit in the space we've got. Include
     * the next character, even though it won't fit completely, if any of the
     * following is true:
     *	 (a) the chunk contains no characters and the display line contains no
     *	     characters yet (i.e. the line isn't wide enough to hold even a
     *	     single character).
     *	 (b) at least one pixel of the character is visible, we have not
     *	     already exceeded the character limit, and the next character is a
     *	     white space character.
     */

    p = segPtr->body.chars + byteOffset;
    tkfont = chunkPtr->stylePtr->sValuePtr->tkfont;

#if TK_LAYOUT_WITH_BASE_CHUNKS
    if (baseCharChunkPtr == NULL) {
	baseCharChunkPtr = chunkPtr;
	bciPtr = (BaseCharInfo *) ckalloc(sizeof(BaseCharInfo));
	baseString = &bciPtr->baseChars;
	Tcl_DStringInit(baseString);
	bciPtr->width = 0;

	ciPtr = &bciPtr->ci;
    } else {
	bciPtr = baseCharChunkPtr->clientData;
	ciPtr = (CharInfo *) ckalloc(sizeof(CharInfo));
	baseString = &bciPtr->baseChars;
    }

    lineOffset = Tcl_DStringLength(baseString);
    line = Tcl_DStringAppend(baseString,p,maxBytes);

    chunkPtr->clientData = ciPtr;
    ciPtr->baseChunkPtr = baseCharChunkPtr;
    ciPtr->baseOffset = lineOffset;
    ciPtr->chars = NULL;
    ciPtr->numBytes = 0;

    bytesThatFit = CharChunkMeasureChars(chunkPtr, line,
	    lineOffset + maxBytes, lineOffset, -1, chunkPtr->x, maxX,
	    TK_ISOLATE_END, &nextX);
#else /* !TK_LAYOUT_WITH_BASE_CHUNKS */
    bytesThatFit = CharChunkMeasureChars(chunkPtr, p, maxBytes, 0, -1,
	    chunkPtr->x, maxX, TK_ISOLATE_END, &nextX);
#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */

    if (bytesThatFit < maxBytes) {
	if ((bytesThatFit == 0) && noCharsYet) {
	    Tcl_UniChar ch;
	    int chLen = Tcl_UtfToUniChar(p, &ch);

#if TK_LAYOUT_WITH_BASE_CHUNKS
	    bytesThatFit = CharChunkMeasureChars(chunkPtr, line,
		    lineOffset+chLen, lineOffset, -1, chunkPtr->x, -1, 0,
		    &nextX);
#else /* !TK_LAYOUT_WITH_BASE_CHUNKS */
	    bytesThatFit = CharChunkMeasureChars(chunkPtr, p, chLen, 0, -1,
		    chunkPtr->x, -1, 0, &nextX);
#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */
	}
	if ((nextX < maxX) && ((p[bytesThatFit] == ' ')
		|| (p[bytesThatFit] == '\t'))) {
	    /*
	     * Space characters are funny, in that they are considered to fit
	     * if there is at least one pixel of space left on the line. Just
	     * give the space character whatever space is left.
	     */

	    nextX = maxX;
	    bytesThatFit++;
	}
	if (p[bytesThatFit] == '\n') {
	    /*
	     * A newline character takes up no space, so if the previous
	     * character fits then so does the newline.
	     */

	    bytesThatFit++;
	}
	if (bytesThatFit == 0) {
#if TK_LAYOUT_WITH_BASE_CHUNKS
	    chunkPtr->clientData = NULL;
	    if (chunkPtr == baseCharChunkPtr) {
		baseCharChunkPtr = NULL;
		Tcl_DStringFree(baseString);
	    } else {
		Tcl_DStringSetLength(baseString,lineOffset);
	    }
	    ckfree((char *) ciPtr);
#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */
	    return 0;
	}
    }

    Tk_GetFontMetrics(tkfont, &fm);

    /*
     * Fill in the chunk structure and allocate and initialize a CharInfo
     * structure. If the last character is a newline then don't bother to
     * display it.
     */

    chunkPtr->displayProc = CharDisplayProc;
    chunkPtr->undisplayProc = CharUndisplayProc;
    chunkPtr->measureProc = CharMeasureProc;
    chunkPtr->bboxProc = CharBboxProc;
    chunkPtr->numBytes = bytesThatFit;
    chunkPtr->minAscent = fm.ascent + chunkPtr->stylePtr->sValuePtr->offset;
    chunkPtr->minDescent = fm.descent - chunkPtr->stylePtr->sValuePtr->offset;
    chunkPtr->minHeight = 0;
    chunkPtr->width = nextX - chunkPtr->x;
    chunkPtr->breakIndex = -1;

#if !TK_LAYOUT_WITH_BASE_CHUNKS
    ciPtr = (CharInfo *)
	    ckalloc((unsigned) ((Tk_Offset(CharInfo, chars) + 1) + bytesThatFit));
    chunkPtr->clientData = ciPtr;
    memcpy(ciPtr->chars, p, (unsigned) bytesThatFit);
#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */

    ciPtr->numBytes = bytesThatFit;
    if (p[bytesThatFit - 1] == '\n') {
	ciPtr->numBytes--;
    }

#if TK_LAYOUT_WITH_BASE_CHUNKS
    /*
     * Final update for the current base chunk data.
     */

    Tcl_DStringSetLength(baseString,lineOffset+ciPtr->numBytes);
    bciPtr->width = nextX - baseCharChunkPtr->x;

    /*
     * Finalize the base chunk if this chunk ends in a tab, which definitly
     * breaks the context and needs to be handled on a higher level.
     */

    if (ciPtr->numBytes > 0 && p[ciPtr->numBytes - 1] == '\t') {
	FinalizeBaseChunk(chunkPtr);
    }
#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */

    /*
     * Compute a break location. If we're in word wrap mode, a break can occur
     * after any space character, or at the end of the chunk if the next
     * segment (ignoring those with zero size) is not a character segment.
     */

    if (wrapMode != TEXT_WRAPMODE_WORD) {
	chunkPtr->breakIndex = chunkPtr->numBytes;
    } else {
	for (count = bytesThatFit, p += bytesThatFit - 1; count > 0;
		count--, p--) {
	    /*
	     * Don't use isspace(); effects are unpredictable and can lead to
	     * odd word-wrapping problems on some platforms. Also don't use
	     * Tcl_UniCharIsSpace here either, as it identifies non-breaking
	     * spaces as places to break. What we actually want is only the
	     * ASCII space characters, so use them explicitly...
	     */

	    switch (*p) {
	    case '\t': case '\n': case '\v': case '\f': case '\r': case ' ':
		chunkPtr->breakIndex = count;
		goto checkForNextChunk;
	    }
	}
    checkForNextChunk:
	if ((bytesThatFit + byteOffset) == segPtr->size) {
	    for (nextPtr = segPtr->nextPtr; nextPtr != NULL;
		    nextPtr = nextPtr->nextPtr) {
		if (nextPtr->size != 0) {
		    if (nextPtr->typePtr != &tkTextCharType) {
			chunkPtr->breakIndex = chunkPtr->numBytes;
		    }
		    break;
		}
	    }
	}
    }
    return 1;
}

/*
 *---------------------------------------------------------------------------
 *
 * CharChunkMeasureChars --
 *
 *	Determine the number of characters from a char chunk that will fit in
 *	the given horizontal span.
 *
 *	This is the same as MeasureChars (which see), but in the context of a
 *	char chunk, i.e. on a higher level of abstraction. Use this function
 *	whereever possible instead of plain MeasureChars, so that the right
 *	context is used automatically.
 *
 * Results:
 *	The return value is the number of bytes from the range of start to end
 *	in source that fit in the span given by startX and maxX. *nextXPtr is
 *	filled in with the x-coordinate at which the first character that
 *	didn't fit would be drawn, if it were to be drawn.
 *
 * Side effects:
 *	None.
 *--------------------------------------------------------------
 */

static int
CharChunkMeasureChars(
    TkTextDispChunk *chunkPtr,	/* Chunk from which to measure. */
    const char *chars,		/* Chars to use, instead of the chunk's own.
				 * Used by the layoutproc during chunk setup.
				 * All other callers use NULL. Not
				 * NUL-terminated. */
    int charsLen,		/* Length of the "chars" parameter. */
    int start, int end,		/* The range of chars to measure inside the
				 * chunk (or inside the additional chars). */
    int startX,			/* Starting x coordinate where the measured
				 * span will begin. */
    int maxX,			/* Maximum pixel width of the span. May be -1
				 * for unlimited. */
    int flags,			/* Flags to pass to MeasureChars. */
    int *nextXPtr)		/* The function puts the newly calculated
				 * right border x-position of the span
				 * here. */
{
    Tk_Font tkfont = chunkPtr->stylePtr->sValuePtr->tkfont;
    CharInfo *ciPtr = chunkPtr->clientData;

#if !TK_LAYOUT_WITH_BASE_CHUNKS
    if (chars == NULL) {
	chars = ciPtr->chars;
	charsLen = ciPtr->numBytes;
    }
    if (end == -1) {
	end = charsLen;
    }

    return MeasureChars(tkfont, chars, charsLen, start, end-start,
	    startX, maxX, flags, nextXPtr);
#else
    {
	int xDisplacement;
	int fit, bstart = start, bend = end;

	if (chars == NULL) {
	    Tcl_DString *baseChars = &((BaseCharInfo *)
		    ciPtr->baseChunkPtr->clientData)->baseChars;

	    chars = Tcl_DStringValue(baseChars);
	    charsLen = Tcl_DStringLength(baseChars);
	    bstart += ciPtr->baseOffset;
	    if (bend == -1) {
		bend = ciPtr->baseOffset + ciPtr->numBytes;
	    } else {
		bend += ciPtr->baseOffset;
	    }
	} else if (bend == -1) {
	    bend = charsLen;
	}

	if (bstart == ciPtr->baseOffset) {
	    xDisplacement = startX - chunkPtr->x;
	} else {
	    int widthUntilStart = 0;

	    MeasureChars(tkfont, chars, charsLen, 0, bstart,
		    0, -1, 0, &widthUntilStart);
	    xDisplacement = startX - widthUntilStart - chunkPtr->x;
	}

	fit = MeasureChars(tkfont, chars, charsLen, 0, bend,
		ciPtr->baseChunkPtr->x + xDisplacement, maxX, flags, nextXPtr);

	if (fit < bstart) {
	    return 0;
	} else {
	    return fit - bstart;
	}
    }
#endif
}

/*
 *--------------------------------------------------------------
 *
 * CharDisplayProc --
 *
 *	This function is called to display a character chunk on the screen or
 *	in an off-screen pixmap.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Graphics are drawn.
 *
 *--------------------------------------------------------------
 */

static void
CharDisplayProc(
    TkText *textPtr,
    TkTextDispChunk *chunkPtr,	/* Chunk that is to be drawn. */
    int x,			/* X-position in dst at which to draw this
				 * chunk (may differ from the x-position in
				 * the chunk because of scrolling). */
    int y,			/* Y-position at which to draw this chunk in
				 * dst. */
    int height,			/* Total height of line. */
    int baseline,		/* Offset of baseline from y. */
    Display *display,		/* Display to use for drawing. */
    Drawable dst,		/* Pixmap or window in which to draw chunk. */
    int screenY)		/* Y-coordinate in text window that
				 * corresponds to y. */
{
    CharInfo *ciPtr = chunkPtr->clientData;
    const char *string;
    TextStyle *stylePtr;
    StyleValues *sValuePtr;
    int numBytes, offsetBytes, offsetX;
#if TK_DRAW_IN_CONTEXT
    BaseCharInfo *bciPtr;
#endif /* TK_DRAW_IN_CONTEXT */

    if ((x + chunkPtr->width) <= 0) {
	/*
	 * The chunk is off-screen.
	 */

	return;
    }

#if TK_DRAW_IN_CONTEXT
    bciPtr = ciPtr->baseChunkPtr->clientData;
    numBytes = Tcl_DStringLength(&bciPtr->baseChars);
    string = Tcl_DStringValue(&bciPtr->baseChars);

#elif TK_LAYOUT_WITH_BASE_CHUNKS
    if (ciPtr->baseChunkPtr != chunkPtr) {
	/*
	 * Without context drawing only base chunks display their foreground.
	 */

	return;
    }

    numBytes = Tcl_DStringLength(&((BaseCharInfo *) ciPtr)->baseChars);
    string = ciPtr->chars;

#else /* !TK_LAYOUT_WITH_BASE_CHUNKS */
    numBytes = ciPtr->numBytes;
    string = ciPtr->chars;
#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */

    stylePtr = chunkPtr->stylePtr;
    sValuePtr = stylePtr->sValuePtr;

    /*
     * If the text sticks out way to the left of the window, skip over the
     * characters that aren't in the visible part of the window. This is
     * essential if x is very negative (such as less than 32K); otherwise
     * overflow problems will occur in servers that use 16-bit arithmetic,
     * like X.
     */

    offsetX = x;
    offsetBytes = 0;
    if (x < 0) {
	offsetBytes = CharChunkMeasureChars(chunkPtr, NULL, 0, 0, -1,
		x, 0, 0, &offsetX);
    }

    /*
     * Draw the text, underline, and overstrike for this chunk.
     */

    if (!sValuePtr->elide && (numBytes > offsetBytes)
	    && (stylePtr->fgGC != None)) {
#if TK_DRAW_IN_CONTEXT
	int start = ciPtr->baseOffset + offsetBytes;
	int len = ciPtr->numBytes - offsetBytes;
	int xDisplacement = x - chunkPtr->x;

	if ((len > 0) && (string[start + len - 1] == '\t')) {
	    len--;
	}
	if (len <= 0) {
	    return;
	}

	TkpDrawCharsInContext(display, dst, stylePtr->fgGC, sValuePtr->tkfont,
		string, numBytes, start, len,
		ciPtr->baseChunkPtr->x + xDisplacement,
		y + baseline - sValuePtr->offset);

	if (sValuePtr->underline) {
	    TkUnderlineCharsInContext(display, dst, stylePtr->fgGC,
		    sValuePtr->tkfont, string, numBytes,
		    ciPtr->baseChunkPtr->x + xDisplacement,
		    y + baseline - sValuePtr->offset,
		    start, start+len);
	}
	if (sValuePtr->overstrike) {
	    Tk_FontMetrics fm;

	    Tk_GetFontMetrics(sValuePtr->tkfont, &fm);
	    TkUnderlineCharsInContext(display, dst, stylePtr->fgGC,
		    sValuePtr->tkfont, string, numBytes,
		    ciPtr->baseChunkPtr->x + xDisplacement,
		    y + baseline - sValuePtr->offset
			    - fm.descent - (fm.ascent * 3) / 10,
		    start, start+len);
	}
#else /* !TK_DRAW_IN_CONTEXT */
	string += offsetBytes;
	numBytes -= offsetBytes;

	if ((numBytes > 0) && (string[numBytes - 1] == '\t')) {
	    numBytes--;
	}
	Tk_DrawChars(display, dst, stylePtr->fgGC, sValuePtr->tkfont, string,
		numBytes, offsetX, y + baseline - sValuePtr->offset);
	if (sValuePtr->underline) {
	    Tk_UnderlineChars(display, dst, stylePtr->fgGC, sValuePtr->tkfont,
		    string, offsetX,
		    y + baseline - sValuePtr->offset,
		    0, numBytes);

	}
	if (sValuePtr->overstrike) {
	    Tk_FontMetrics fm;

	    Tk_GetFontMetrics(sValuePtr->tkfont, &fm);
	    Tk_UnderlineChars(display, dst, stylePtr->fgGC, sValuePtr->tkfont,
		    string, offsetX,
		    y + baseline - sValuePtr->offset
			    - fm.descent - (fm.ascent * 3) / 10,
		    0, numBytes);
	}
#endif /* TK_DRAW_IN_CONTEXT */
    }
}

/*
 *--------------------------------------------------------------
 *
 * CharUndisplayProc --
 *
 *	This function is called when a character chunk is no longer going to
 *	be displayed. It frees up resources that were allocated to display the
 *	chunk.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Memory and other resources get freed.
 *
 *--------------------------------------------------------------
 */

static void
CharUndisplayProc(
    TkText *textPtr,		/* Overall information about text widget. */
    TkTextDispChunk *chunkPtr)	/* Chunk that is about to be freed. */
{
    CharInfo *ciPtr = chunkPtr->clientData;

    if (ciPtr) {
#if TK_LAYOUT_WITH_BASE_CHUNKS
	if (chunkPtr == ciPtr->baseChunkPtr) {
	    /*
	     * Basechunks are undisplayed first, when DLines are freed or
	     * partially freed, so this makes sure we don't access their data
	     * any more.
	     */

	    FreeBaseChunk(chunkPtr);
	} else if (ciPtr->baseChunkPtr != NULL) {
	    /*
	     * When other char chunks are undisplayed, drop their characters
	     * from the base chunk. This usually happens, when they are last
	     * in a line and need to be re-layed out.
	     */

	    RemoveFromBaseChunk(chunkPtr);
	}

	ciPtr->baseChunkPtr = NULL;
	ciPtr->chars = NULL;
	ciPtr->numBytes = 0;
#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */

	ckfree((char *) ciPtr);
	chunkPtr->clientData = NULL;
    }
}

/*
 *--------------------------------------------------------------
 *
 * CharMeasureProc --
 *
 *	This function is called to determine which character in a character
 *	chunk lies over a given x-coordinate.
 *
 * Results:
 *	The return value is the index *within the chunk* of the character that
 *	covers the position given by "x".
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */

static int
CharMeasureProc(
    TkTextDispChunk *chunkPtr,	/* Chunk containing desired coord. */
    int x)			/* X-coordinate, in same coordinate system as
				 * chunkPtr->x. */
{
    int endX;

    return CharChunkMeasureChars(chunkPtr, NULL, 0, 0, chunkPtr->numBytes-1,
	    chunkPtr->x, x, 0, &endX); /* CHAR OFFSET */
}

/*
 *--------------------------------------------------------------
 *
 * CharBboxProc --
 *
 *	This function is called to compute the bounding box of the area
 *	occupied by a single character.
 *
 * Results:
 *	There is no return value. *xPtr and *yPtr are filled in with the
 *	coordinates of the upper left corner of the character, and *widthPtr
 *	and *heightPtr are filled in with the dimensions of the character in
 *	pixels. Note: not all of the returned bbox is necessarily visible on
 *	the screen (the rightmost part might be off-screen to the right, and
 *	the bottommost part might be off-screen to the bottom).
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */

static void
CharBboxProc(
    TkText *textPtr,
    TkTextDispChunk *chunkPtr,	/* Chunk containing desired char. */
    int byteIndex,		/* Byte offset of desired character within the
				 * chunk. */
    int y,			/* Topmost pixel in area allocated for this
				 * line. */
    int lineHeight,		/* Height of line, in pixels. */
    int baseline,		/* Location of line's baseline, in pixels
				 * measured down from y. */
    int *xPtr, int *yPtr,	/* Gets filled in with coords of character's
				 * upper-left pixel. X-coord is in same
				 * coordinate system as chunkPtr->x. */
    int *widthPtr,		/* Gets filled in with width of character, in
				 * pixels. */
    int *heightPtr)		/* Gets filled in with height of character, in
				 * pixels. */
{
    CharInfo *ciPtr = chunkPtr->clientData;
    int maxX;

    maxX = chunkPtr->width + chunkPtr->x;
    CharChunkMeasureChars(chunkPtr, NULL, 0, 0, byteIndex,
	    chunkPtr->x, -1, 0, xPtr);

    if (byteIndex == ciPtr->numBytes) {
	/*
	 * This situation only happens if the last character in a line is a
	 * space character, in which case it absorbs all of the extra space in
	 * the line (see TkTextCharLayoutProc).
	 */

	*widthPtr = maxX - *xPtr;
    } else if ((ciPtr->chars[byteIndex] == '\t')
	    && (byteIndex == ciPtr->numBytes - 1)) {
	/*
	 * The desired character is a tab character that terminates a chunk;
	 * give it all the space left in the chunk.
	 */

	*widthPtr = maxX - *xPtr;
    } else {
	CharChunkMeasureChars(chunkPtr, NULL, 0, byteIndex, byteIndex+1,
		*xPtr, -1, 0, widthPtr);
	if (*widthPtr > maxX) {
	    *widthPtr = maxX - *xPtr;
	} else {
	    *widthPtr -= *xPtr;
	}
    }
    *yPtr = y + baseline - chunkPtr->minAscent;
    *heightPtr = chunkPtr->minAscent + chunkPtr->minDescent;
}

/*
 *----------------------------------------------------------------------
 *
 * AdjustForTab --
 *
 *	This function is called to move a series of chunks right in order to
 *	align them with a tab stop.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The width of chunkPtr gets adjusted so that it absorbs the extra space
 *	due to the tab. The x locations in all the chunks after chunkPtr are
 *	adjusted rightward to align with the tab stop given by tabArrayPtr and
 *	index.
 *
 *----------------------------------------------------------------------
 */

static void
AdjustForTab(
    TkText *textPtr,		/* Information about the text widget as a
				 * whole. */
    TkTextTabArray *tabArrayPtr,/* Information about the tab stops that apply
				 * to this line. May be NULL to indicate
				 * default tabbing (every 8 chars). */
    int index,			/* Index of current tab stop. */
    TkTextDispChunk *chunkPtr)	/* Chunk whose last character is the tab; the
				 * following chunks contain information to be
				 * shifted right. */
{
    int x, desired, delta, width, decimal, i, gotDigit;
    TkTextDispChunk *chunkPtr2, *decimalChunkPtr;
    CharInfo *ciPtr;
    int tabX, spaceWidth;
    const char *p;
    TkTextTabAlign alignment;

    if (chunkPtr->nextPtr == NULL) {
	/*
	 * Nothing after the actual tab; just return.
	 */

	return;
    }

    x = chunkPtr->nextPtr->x;

    /*
     * If no tab information has been given, assuming tab stops are at 8
     * average-sized characters. Still ensure we respect the tabular versus
     * wordprocessor tab style.
     */

    if ((tabArrayPtr == NULL) || (tabArrayPtr->numTabs == 0)) {
	/*
	 * No tab information has been given, so use the default
	 * interpretation of tabs.
	 */

	if (textPtr->tabStyle == TK_TEXT_TABSTYLE_TABULAR) {
	    int tabWidth = Tk_TextWidth(textPtr->tkfont, "0", 1) * 8;
	    if (tabWidth == 0) {
		tabWidth = 1;
	    }

	    desired = tabWidth * (index + 1);
	} else {
	    desired = NextTabStop(textPtr->tkfont, x, 0);
	}

	goto update;
    }

    if (index < tabArrayPtr->numTabs) {
	alignment = tabArrayPtr->tabs[index].alignment;
	tabX = tabArrayPtr->tabs[index].location;
    } else {
	/*
	 * Ran out of tab stops; compute a tab position by extrapolating from
	 * the last two tab positions.
	 */

	tabX = (int) (tabArrayPtr->lastTab +
		(index + 1 - tabArrayPtr->numTabs)*tabArrayPtr->tabIncrement +
		0.5);
	alignment = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].alignment;
    }

    if (alignment == LEFT) {
	desired = tabX;
	goto update;
    }

    if ((alignment == CENTER) || (alignment == RIGHT)) {
	/*
	 * Compute the width of all the information in the tab group, then use
	 * it to pick a desired location.
	 */

	width = 0;
	for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
		chunkPtr2 = chunkPtr2->nextPtr) {
	    width += chunkPtr2->width;
	}
	if (alignment == CENTER) {
	    desired = tabX - width/2;
	} else {
	    desired = tabX - width;
	}
	goto update;
    }

    /*
     * Must be numeric alignment. Search through the text to be tabbed,
     * looking for the last , or . before the first character that isn't a
     * number, comma, period, or sign.
     */

    decimalChunkPtr = NULL;
    decimal = gotDigit = 0;
    for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
	    chunkPtr2 = chunkPtr2->nextPtr) {
	if (chunkPtr2->displayProc != CharDisplayProc) {
	    continue;
	}
	ciPtr = chunkPtr2->clientData;
	for (p = ciPtr->chars, i = 0; i < ciPtr->numBytes; p++, i++) {
	    if (isdigit(UCHAR(*p))) {
		gotDigit = 1;
	    } else if ((*p == '.') || (*p == ',')) {
		decimal = p-ciPtr->chars;
		decimalChunkPtr = chunkPtr2;
	    } else if (gotDigit) {
		if (decimalChunkPtr == NULL) {
		    decimal = p-ciPtr->chars;
		    decimalChunkPtr = chunkPtr2;
		}
		goto endOfNumber;
	    }
	}
    }

  endOfNumber:
    if (decimalChunkPtr != NULL) {
	int curX;

	ciPtr = decimalChunkPtr->clientData;
	CharChunkMeasureChars(decimalChunkPtr, NULL, 0, 0, decimal,
		decimalChunkPtr->x, -1, 0, &curX);
	desired = tabX - (curX - x);
	goto update;
    }

    /*
     * There wasn't a decimal point. Right justify the text.
     */

    width = 0;
    for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
	    chunkPtr2 = chunkPtr2->nextPtr) {
	width += chunkPtr2->width;
    }
    desired = tabX - width;

    /*
     * Shift all of the chunks to the right so that the left edge is at the
     * desired location, then expand the chunk containing the tab. Be sure
     * that the tab occupies at least the width of a space character.
     */

  update:
    delta = desired - x;
    MeasureChars(textPtr->tkfont, " ", 1, 0, 1, 0, -1, 0, &spaceWidth);
    if (delta < spaceWidth) {
	delta = spaceWidth;
    }
    for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
	    chunkPtr2 = chunkPtr2->nextPtr) {
	chunkPtr2->x += delta;
    }
    chunkPtr->width += delta;
}

/*
 *----------------------------------------------------------------------
 *
 * SizeOfTab --
 *
 *	This returns an estimate of the amount of white space that will be
 *	consumed by a tab.
 *
 * Results:
 *	The return value is the minimum number of pixels that will be occupied
 *	by the next tab of tabArrayPtr, assuming that the current position on
 *	the line is x and the end of the line is maxX. The 'next tab' is
 *	determined by a combination of the current position (x) which it must
 *	be equal to or beyond, and the tab count in indexPtr.
 *
 *	For numeric tabs, this is a conservative estimate. The return value is
 *	always >= 0.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
SizeOfTab(
    TkText *textPtr,		/* Information about the text widget as a
				 * whole. */
    int tabStyle,		/* One of TK_TEXT_TABSTYLE_TABULAR
				 * or TK_TEXT_TABSTYLE_WORDPROCESSOR. */
    TkTextTabArray *tabArrayPtr,/* Information about the tab stops that apply
				 * to this line. NULL means use default
				 * tabbing (every 8 chars.) */
    int *indexPtr,		/* Contains index of previous tab stop, will
				 * be updated to reflect the number of stops
				 * used. */
    int x,			/* Current x-location in line. */
    int maxX)			/* X-location of pixel just past the right
				 * edge of the line. */
{
    int tabX, result, index, spaceWidth, tabWidth;
    TkTextTabAlign alignment;

    index = *indexPtr;

    if ((tabArrayPtr == NULL) || (tabArrayPtr->numTabs == 0)) {
	/*
	 * We're using a default tab spacing of 8 characters.
	 */

	tabWidth = Tk_TextWidth(textPtr->tkfont, "0", 1) * 8;
	if (tabWidth == 0) {
	    tabWidth = 1;
	}
    } else {
	tabWidth = 0;		/* Avoid compiler error. */
    }

    do {
	/*
	 * We were given the count before this tab, so increment it first.
	 */

	index++;

	if ((tabArrayPtr == NULL) || (tabArrayPtr->numTabs == 0)) {
	    /*
	     * We're using a default tab spacing calculated above.
	     */

	    tabX = tabWidth * (index + 1);
	    alignment = LEFT;
	} else if (index < tabArrayPtr->numTabs) {
	    tabX = tabArrayPtr->tabs[index].location;
	    alignment = tabArrayPtr->tabs[index].alignment;
	} else {
	    /*
	     * Ran out of tab stops; compute a tab position by extrapolating.
	     */

	    tabX = (int) (tabArrayPtr->lastTab
		    + (index + 1 - tabArrayPtr->numTabs)
		    * tabArrayPtr->tabIncrement + 0.5);
	    alignment = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].alignment;
	}

	/*
	 * If this tab stop is before the current x position, then we have two
	 * cases:
	 *
	 * With 'wordprocessor' style tabs, we must obviously continue until
	 * we reach the text tab stop.
	 *
	 * With 'tabular' style tabs, we always use the index'th tab stop.
	 */
    } while (tabX <= x && (tabStyle == TK_TEXT_TABSTYLE_WORDPROCESSOR));

    /*
     * Inform our caller of how many tab stops we've used up.
     */

    *indexPtr = index;

    if (alignment == CENTER) {
	/*
	 * Be very careful in the arithmetic below, because maxX may be the
	 * largest positive number: watch out for integer overflow.
	 */

	if ((maxX-tabX) < (tabX - x)) {
	    result = (maxX - x) - 2*(maxX - tabX);
	} else {
	    result = 0;
	}
	goto done;
    }
    if (alignment == RIGHT) {
	result = 0;
	goto done;
    }

    /*
     * Note: this treats NUMERIC alignment the same as LEFT alignment, which
     * is somewhat conservative. However, it's pretty tricky at this point to
     * figure out exactly where the damn decimal point will be.
     */

    if (tabX > x) {
	result = tabX - x;
    } else {
	result = 0;
    }

  done:
    MeasureChars(textPtr->tkfont, " ", 1, 0, 1, 0, -1, 0, &spaceWidth);
    if (result < spaceWidth) {
	result = spaceWidth;
    }
    return result;
}

/*
 *---------------------------------------------------------------------------
 *
 * NextTabStop --
 *
 *	Given the current position, determine where the next default tab stop
 *	would be located. This function is called when the current chunk in
 *	the text has no tabs defined and so the default tab spacing for the
 *	font should be used, provided we are using wordprocessor style tabs.
 *
 * Results:
 *	The location in pixels of the next tab stop.
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

static int
NextTabStop(
    Tk_Font tkfont,		/* Font in which chunk that contains tab stop
				 * will be drawn. */
    int x,			/* X-position in pixels where last character
				 * was drawn. The next tab stop occurs
				 * somewhere after this location. */
    int tabOrigin)		/* The origin for tab stops. May be non-zero
				 * if text has been scrolled. */
{
    int tabWidth, rem;

    tabWidth = Tk_TextWidth(tkfont, "0", 1) * 8;
    if (tabWidth == 0) {
	tabWidth = 1;
    }

    x += tabWidth;
    rem = (x - tabOrigin) % tabWidth;
    if (rem < 0) {
	rem += tabWidth;
    }
    x -= rem;
    return x;
}

/*
 *---------------------------------------------------------------------------
 *
 * MeasureChars --
 *
 *	Determine the number of characters from the string that will fit in
 *	the given horizontal span. The measurement is done under the
 *	assumption that Tk_DrawChars will be used to actually display the
 *	characters.
 *
 *	If tabs are encountered in the string, they will be ignored (they
 *	should only occur as last character of the string anyway).
 *
 *	If a newline is encountered in the string, the line will be broken at
 *	that point.
 *
 * Results:
 *	The return value is the number of bytes from the range of start to end
 *	in source that fit in the span given by startX and maxX. *nextXPtr is
 *	filled in with the x-coordinate at which the first character that
 *	didn't fit would be drawn, if it were to be drawn.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */

static int
MeasureChars(
    Tk_Font tkfont,		/* Font in which to draw characters. */
    const char *source,		/* Characters to be displayed. Need not be
				 * NULL-terminated. */
    int maxBytes,		/* Maximum # of bytes to consider from
				 * source. */
    int rangeStart, int rangeLength,
				/* Range of bytes to consider in source.*/
    int startX,			/* X-position at which first character will be
				 * drawn. */
    int maxX,			/* Don't consider any character that would
				 * cross this x-position. */
    int flags,			/* Flags to pass to Tk_MeasureChars. */
    int *nextXPtr)		/* Return x-position of terminating character
				 * here. */
{
    int curX, width, ch;
    const char *special, *end, *start;

    ch = 0;			/* lint. */
    curX = startX;
    start = source + rangeStart;
    end = start + rangeLength;
    special = start;
    while (start < end) {
	if (start >= special) {
	    /*
	     * Find the next special character in the string.
	     */

	    for (special = start; special < end; special++) {
		ch = *special;
		if ((ch == '\t') || (ch == '\n')) {
		    break;
		}
	    }
	}

	/*
	 * Special points at the next special character (or the end of the
	 * string). Process characters between start and special.
	 */

	if ((maxX >= 0) && (curX >= maxX)) {
	    break;
	}
#if TK_DRAW_IN_CONTEXT
	start += TkpMeasureCharsInContext(tkfont, source, maxBytes,
		start - source, special - start,
		maxX >= 0 ? maxX - curX : -1, flags, &width);
#else
	(void) maxBytes;
	start += Tk_MeasureChars(tkfont, start, special - start,
		maxX >= 0 ? maxX - curX : -1, flags, &width);
#endif /* TK_DRAW_IN_CONTEXT */
	curX += width;
	if (start < special) {
	    /*
	     * No more chars fit in line.
	     */

	    break;
	}
	if (special < end) {
	    if (ch != '\t') {
		break;
	    }
	    start++;
	}
    }

    *nextXPtr = curX;
    return start - (source+rangeStart);
}

/*
 *----------------------------------------------------------------------
 *
 * TextGetScrollInfoObj --
 *
 *	This function is invoked to parse "xview" and "yview" scrolling
 *	commands for text widgets using the new scrolling command syntax
 *	("moveto" or "scroll" options). It extends the public
 *	Tk_GetScrollInfoObj function with the addition of "pixels" as a valid
 *	unit alongside "pages" and "units". It is a shame the core API isn't
 *	more flexible in this regard.
 *
 * Results:
 *	The return value is either TKTEXT_SCROLL_MOVETO, TKTEXT_SCROLL_PAGES,
 *	TKTEXT_SCROLL_UNITS, TKTEXT_SCROLL_PIXELS or TKTEXT_SCROLL_ERROR. This
 *	indicates whether the command was successfully parsed and what form
 *	the command took. If TKTEXT_SCROLL_MOVETO, *dblPtr is filled in with
 *	the desired position; if TKTEXT_SCROLL_PAGES, TKTEXT_SCROLL_PIXELS or
 *	TKTEXT_SCROLL_UNITS, *intPtr is filled in with the number of
 *	pages/pixels/lines to move (may be negative); if TKTEXT_SCROLL_ERROR,
 *	the interp's result contains an error message.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
TextGetScrollInfoObj(
    Tcl_Interp *interp,		/* Used for error reporting. */
    TkText *textPtr,		/* Information about the text widget. */
    int objc,			/* # arguments for command. */
    Tcl_Obj *const objv[],	/* Arguments for command. */
    double *dblPtr,		/* Filled in with argument "moveto" option, if
				 * any. */
    int *intPtr)		/* Filled in with number of pages or lines or
				 * pixels to scroll, if any. */
{
    static const char *const subcommands[] = {
	"moveto", "scroll", NULL
    };
    enum viewSubcmds {
	VIEW_MOVETO, VIEW_SCROLL
    };
    static const char *const units[] = {
	"units", "pages", "pixels", NULL
    };
    enum viewUnits {
	VIEW_SCROLL_UNITS, VIEW_SCROLL_PAGES, VIEW_SCROLL_PIXELS
    };
    int index;

    if (Tcl_GetIndexFromObj(interp, objv[2], subcommands, "option", 0,
	    &index) != TCL_OK) {
	return TKTEXT_SCROLL_ERROR;
    }

    switch ((enum viewSubcmds) index) {
    case VIEW_MOVETO:
	if (objc != 4) {
	    Tcl_WrongNumArgs(interp, 3, objv, "fraction");
	    return TKTEXT_SCROLL_ERROR;
	}
	if (Tcl_GetDoubleFromObj(interp, objv[3], dblPtr) != TCL_OK) {
	    return TKTEXT_SCROLL_ERROR;
	}
	return TKTEXT_SCROLL_MOVETO;
    case VIEW_SCROLL:
	if (objc != 5) {
	    Tcl_WrongNumArgs(interp, 3, objv, "number units|pages|pixels");
	    return TKTEXT_SCROLL_ERROR;
	}
	if (Tcl_GetIndexFromObj(interp, objv[4], units, "argument", 0,
		&index) != TCL_OK) {
	    return TKTEXT_SCROLL_ERROR;
	}
	switch ((enum viewUnits) index) {
	case VIEW_SCROLL_PAGES:
	    if (Tcl_GetIntFromObj(interp, objv[3], intPtr) != TCL_OK) {
		return TKTEXT_SCROLL_ERROR;
	    }
	    return TKTEXT_SCROLL_PAGES;
	case VIEW_SCROLL_PIXELS:
	    if (Tk_GetPixelsFromObj(interp, textPtr->tkwin, objv[3],
		    intPtr) != TCL_OK) {
		return TKTEXT_SCROLL_ERROR;
	    }
	    return TKTEXT_SCROLL_PIXELS;
	case VIEW_SCROLL_UNITS:
	    if (Tcl_GetIntFromObj(interp, objv[3], intPtr) != TCL_OK) {
		return TKTEXT_SCROLL_ERROR;
	    }
	    return TKTEXT_SCROLL_UNITS;
	}
    }
    Tcl_Panic("unexpected switch fallthrough");
    return TKTEXT_SCROLL_ERROR;
}

#if TK_LAYOUT_WITH_BASE_CHUNKS
/*
 *----------------------------------------------------------------------
 *
 * FinalizeBaseChunk --
 *
 *	This procedure makes sure that all the chunks of the stretch are
 *	up-to-date. It is invoked when the LayoutProc has been called for all
 *	chunks and the base chunk is stable.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The CharInfo.chars of all dependent chunks point into
 *	BaseCharInfo.baseChars for easy access (and compatibility).
 *
 *----------------------------------------------------------------------
 */

static void
FinalizeBaseChunk(
    TkTextDispChunk *addChunkPtr)
				/* An additional chunk to add to the stretch,
				 * even though it may not be in the linked
				 * list yet. Used by the LayoutProc, otherwise
				 * NULL. */
{
    const char *baseChars;
    TkTextDispChunk *chunkPtr;
    CharInfo *ciPtr;
#if TK_DRAW_IN_CONTEXT
    int widthAdjust = 0;
    int newwidth;
#endif /* TK_DRAW_IN_CONTEXT */

    if (baseCharChunkPtr == NULL) {
	return;
    }

    baseChars = Tcl_DStringValue(
	    &((BaseCharInfo *) baseCharChunkPtr->clientData)->baseChars);

    for (chunkPtr = baseCharChunkPtr; chunkPtr != NULL;
	    chunkPtr = chunkPtr->nextPtr) {
#if TK_DRAW_IN_CONTEXT
	chunkPtr->x += widthAdjust;
#endif /* TK_DRAW_IN_CONTEXT */

	if (chunkPtr->displayProc != CharDisplayProc) {
	    continue;
	}
	ciPtr = chunkPtr->clientData;
	if (ciPtr->baseChunkPtr != baseCharChunkPtr) {
	    break;
	}
	ciPtr->chars = baseChars + ciPtr->baseOffset;

#if TK_DRAW_IN_CONTEXT
	newwidth = 0;
	CharChunkMeasureChars(chunkPtr, NULL, 0, 0, -1, 0, -1, 0, &newwidth);
	if (newwidth != chunkPtr->width) {
	    widthAdjust += newwidth - chunkPtr->width;
	    chunkPtr->width = newwidth;
	}
#endif /* TK_DRAW_IN_CONTEXT */
    }

    if (addChunkPtr != NULL) {
	ciPtr = addChunkPtr->clientData;
	ciPtr->chars = baseChars + ciPtr->baseOffset;

#if TK_DRAW_IN_CONTEXT
	addChunkPtr->x += widthAdjust;
	CharChunkMeasureChars(addChunkPtr, NULL, 0, 0, -1, 0, -1, 0,
		&addChunkPtr->width);
#endif /* TK_DRAW_IN_CONTEXT */
    }

    baseCharChunkPtr = NULL;
}

/*
 *----------------------------------------------------------------------
 *
 * FreeBaseChunk --
 *
 *	This procedure makes sure that all the chunks of the stretch are
 *	disconnected from the base chunk and the base chunk specific data is
 *	freed. It is invoked from the UndisplayProc. The procedure doesn't
 *	ckfree the base chunk clientData itself, that's up to the main
 *	UndisplayProc.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The CharInfo.chars of all dependent chunks are set to NULL. Memory
 *	that belongs specifically to the base chunk is freed.
 *
 *----------------------------------------------------------------------
 */

static void
FreeBaseChunk(
    TkTextDispChunk *baseChunkPtr)
				/* The base chunk of the stretch and head of
				 * the linked list. */
{
    TkTextDispChunk *chunkPtr;
    CharInfo *ciPtr;

    if (baseCharChunkPtr == baseChunkPtr) {
	baseCharChunkPtr = NULL;
    }

    for (chunkPtr=baseChunkPtr; chunkPtr!=NULL; chunkPtr=chunkPtr->nextPtr) {
	if (chunkPtr->undisplayProc != CharUndisplayProc) {
	    continue;
	}
	ciPtr = chunkPtr->clientData;
	if (ciPtr->baseChunkPtr != baseChunkPtr) {
	    break;
	}

	ciPtr->baseChunkPtr = NULL;
	ciPtr->chars = NULL;
    }

    if (baseChunkPtr) {
	Tcl_DStringFree(&((BaseCharInfo *) baseChunkPtr->clientData)->baseChars);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * IsSameFGStyle --
 *
 *	Compare the foreground attributes of two styles. Specifically must
 *	consider: foreground color, font, font style and font decorations,
 *	elide, "offset" and foreground stipple. Do *not* consider: background
 *	color, border, relief or background stipple.
 *
 *	If we use TkpDrawCharsInContext(), we also don't need to check
 *	foreground color, font decorations, elide, offset and foreground
 *	stipple, so all that is left is font (including font size and font
 *	style) and "offset".
 *
 * Results:
 *	1 if the two styles match, 0 otherwise.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
IsSameFGStyle(
    TextStyle *style1,
    TextStyle *style2)
{
    StyleValues *sv1;
    StyleValues *sv2;

    if (style1 == style2) {
	return 1;
    }

#if !TK_DRAW_IN_CONTEXT
    if (
#ifdef MAC_OSX_TK
	    !TkMacOSXCompareColors(style1->fgGC->foreground,
		    style2->fgGC->foreground)
#else
	    style1->fgGC->foreground != style2->fgGC->foreground
#endif
	    ) {
	return 0;
    }
#endif /* !TK_DRAW_IN_CONTEXT */

    sv1 = style1->sValuePtr;
    sv2 = style2->sValuePtr;

#if TK_DRAW_IN_CONTEXT
    return sv1->tkfont == sv2->tkfont && sv1->offset == sv2->offset;
#else
    return sv1->tkfont == sv2->tkfont
	    && sv1->underline == sv2->underline
	    && sv1->overstrike == sv2->overstrike
	    && sv1->elide == sv2->elide
	    && sv1->offset == sv2->offset
	    && sv1->fgStipple == sv1->fgStipple;
#endif /* TK_DRAW_IN_CONTEXT */
}

/*
 *----------------------------------------------------------------------
 *
 * RemoveFromBaseChunk --
 *
 *	This procedure removes a chunk from the stretch as a result of
 *	UndisplayProc. The chunk in question should be the last in a stretch.
 *	This happens during re-layouting of the break position.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The characters that belong to this chunk are removed from the base
 *	chunk. It is assumed that LayoutProc and FinalizeBaseChunk are called
 *	next to repair any damage that this causes to the integrity of the
 *	stretch and the other chunks. For that reason the base chunk is also
 *	put into baseCharChunkPtr automatically, so that LayoutProc can resume
 *	correctly.
 *
 *----------------------------------------------------------------------
 */

static void
RemoveFromBaseChunk(
    TkTextDispChunk *chunkPtr)	/* The chunk to remove from the end of the
				 * stretch. */
{
    CharInfo *ciPtr;
    BaseCharInfo *bciPtr;

    if (chunkPtr->displayProc != CharDisplayProc) {
#ifdef DEBUG_LAYOUT_WITH_BASE_CHUNKS
	fprintf(stderr,"RemoveFromBaseChunk called with wrong chunk type\n");
#endif
	return;
    }

    /*
     * Reinstitute this base chunk for re-layout.
     */

    ciPtr = chunkPtr->clientData;
    baseCharChunkPtr = ciPtr->baseChunkPtr;

    /*
     * Remove the chunk data from the base chunk data.
     */

    bciPtr = baseCharChunkPtr->clientData;

    if ((ciPtr->baseOffset + ciPtr->numBytes)
	    != Tcl_DStringLength(&bciPtr->baseChars)) {
#ifdef DEBUG_LAYOUT_WITH_BASE_CHUNKS
	fprintf(stderr,"RemoveFromBaseChunk called with wrong chunk "
		"(not last)\n");
#endif
    }

    Tcl_DStringSetLength(&bciPtr->baseChars, ciPtr->baseOffset);

    /*
     * Invalidate the stored pixel width of the base chunk.
     */

    bciPtr->width = -1;
}
#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */
@


1.77
log
@[Bug 2677890]: Fix odd text widget update problem that had scrollbars being
unable to cover the whole widget. Fix is to reify the range to update sooner.
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.76 2010/01/02 22:52:38 dkf Exp $
d418 2
a419 2
    char chars[4];		/* UTF characters to display. Actual size will
				 * be numBytes, not 4. THIS MUST BE THE LAST
d7173 1
a7173 1
	    ckalloc((unsigned) bytesThatFit + Tk_Offset(CharInfo, chars) + 1);
@


1.76
log
@SMALL FIX: Remove useless explicit casts now that we require C89 or later.
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.75 2009/09/07 07:29:04 das Exp $
d2905 5
d2911 3
a2913 2
    if (lineNum == -1) {
	dInfoPtr->lastMetricUpdateLine = 0;
@


1.75
log
@	* generic/tkFocus.c:	fix potential null dereference flagged by clang
	* generic/tkMenu.c:	static analyzer.
	* generic/tkTextBTree.c:
	* generic/tkTextDisp.c:
	* generic/tkTextIndex.c:

	* generic/tkConsole.c:	silence false positives from clang static
	* generic/tkTest.c:	analyzer about potential null dereference.
	* generic/tkText.c:
	* generic/tkTextBTree.c:
	* generic/tkTextTag.c:
	* generic/tkVisual.c:
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.74 2009/08/04 21:19:27 dkf Exp $
d936 1
a936 1
	stylePtr = (TextStyle *) Tcl_GetHashValue(hPtr);
@


1.74
log
@Fix word-wrapping of non-breaking spaces in the text widget to work reliably.
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.73 2009/06/30 00:56:29 das Exp $
d8363 3
a8365 1
    Tcl_DStringFree(&((BaseCharInfo *) baseChunkPtr->clientData)->baseChars);
@


1.73
log
@	* generic/tkInt.h:		add assert macros for clang static
					analyzer and redefine Tcl_Panic to
					assert after panic in clang PURIFY
					builds.

	* generic/tkImgPhInstance.c:	small fixes to make clang static
	* generic/tkTextDisp.c:		analyzer happier.

	* generic/tkConfig.c:		add clang assert for false positives
	* generic/tkUndo.c:		from static analyzer.
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.72 2008/12/09 21:22:56 dgp Exp $
d5394 1
a5394 1
		dInfoPtr->newXPixelOffset = (x - lineWidth/2);
d5396 1
a5396 1
		dInfoPtr->newXPixelOffset -= ((-delta) );
d5403 1
a5403 1
		dInfoPtr->newXPixelOffset = (x - lineWidth/2);
d5405 1
a5405 1
		dInfoPtr->newXPixelOffset += (delta );
d7206 10
a7215 1
	    if (isspace(UCHAR(*p))) {
d7217 1
a7217 1
		break;
d7220 1
@


1.72
log
@        TIP 337

        * generic/tkBind.c:     Updated callers of Tcl_BackgroundError()
        * generic/tkCanvas.c:   to use the new routine
        * generic/tkEntry.c:    Tcl_BackgroundException() as appropriate.
        * generic/tkImgBmap.c:
        * generic/tkListbox.c:
        * generic/tkSelect.c:
        * generic/tkTextDisp.c:
        * generic/tkTextWind.c:
        * macosx/tkMacOSXHLEvents.c:
        * macosx/tkMacOSXMenu.c:
        * macosx/tkMacOSXMenus.c:
        * macosx/tkMacOSXScale.c:
        * macosx/tkMacOSXWindowEvent.c:
        * unix/tkUnixScale.c:
        * unix/tkUnixWm.c:
        * win/tkWinButton.c:
        * win/tkWinMenu.c:
        * win/tkWinScrlbr.c:
        * win/tkWinWm.c:
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.71 2008/11/08 18:44:40 dkf Exp $
d6724 1
a6724 1
    int x;
d6727 1
a6727 1
	return 0;
@


1.71
log
@Lots of small changes to make code more pretty and C89-like.
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.70 2008/10/17 23:18:37 nijtmans Exp $
d6066 1
a6066 1
	    Tcl_BackgroundError(interp);
d6348 1
a6348 1
	    Tcl_BackgroundError(interp);
@


1.70
log
@Add "const" to many internal
const tables, so those will be
put by the C-compiler in the
TEXT segment in stead of the
DATA segment. This makes those
table sharable in shared libraries.
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.69 2008/04/27 22:38:58 dkf Exp $
d449 1
d665 1
a665 1
	    AsyncUpdateLineMetrics, (ClientData) textPtr);
d668 1
a668 1
	    AsyncUpdateYScrollbar, (ClientData) textPtr);
d710 1
a710 1
	Tcl_CancelIdleCall(DisplayText, (ClientData) textPtr);
d1269 1
a1269 6
	    if (linePtr != NULL) {
		dlPtr->logicalLinesMerged++;
		curIndex.byteIndex = 0;
		curIndex.linePtr = linePtr;
		segPtr = curIndex.linePtr->segPtr;
	    } else {
d1272 5
d1463 1
a1463 1
	    code = (*segPtr->typePtr->layoutProc)(textPtr, &curIndex, segPtr,
d1615 1
a1615 1
		(*chunkPtr->undisplayProc)(textPtr, chunkPtr);
d1621 1
a1621 1
		(*breakChunkPtr->undisplayProc)(textPtr, breakChunkPtr);
d1624 3
a1626 3
	    (*segPtr->typePtr->layoutProc)(textPtr, &breakIndex,
		    segPtr, byteOffset, maxX, breakByteOffset, 0,
		    wrapMode, breakChunkPtr);
d2310 1
a2310 1
		(*chunkPtr->undisplayProc)(textPtr, chunkPtr);
d2408 1
a2408 1
		(*chunkPtr->displayProc)(textPtr, chunkPtr, x,
d2456 1
a2456 1
	    (*chunkPtr->displayProc)(textPtr, chunkPtr, x,
d2882 1
a2882 1
    register TkText *textPtr = (TkText *) clientData;
d2952 1
a2952 1
	    AsyncUpdateLineMetrics, (ClientData) textPtr);
d3053 25
a3077 4
		!= textPtr->dInfoPtr->lineMetricUpdateEpoch) {
		if (doThisMuch == -1) {
		    count += 8 * TkTextUpdateOneLine(textPtr, linePtr, 0,
			    NULL, 0);
a3078 4
		    TkTextIndex index;
		    TkTextIndex *indexPtr;
		    int pixelHeight;

d3080 2
a3081 5
		     * If the metric epoch is the same as the widget's epoch,
		     * then we know that indexPtrs are still valid, and if the
		     * cached metricIndex (if any) is for the same line as we
		     * wish to examine, then we are looking at a long line
		     * wrapped many times, which we will examine in pieces.
d3084 13
a3096 11
		    if (textPtr->dInfoPtr->metricEpoch ==
			    textPtr->sharedTextPtr->stateEpoch &&
			    textPtr->dInfoPtr->metricIndex.linePtr==linePtr) {
			indexPtr = &textPtr->dInfoPtr->metricIndex;
			pixelHeight = textPtr->dInfoPtr->metricPixelHeight;
		    } else {
			/*
			 * We must reset the partial line height calculation
			 * data here, so we don't use it when it is out of
			 * date.
			 */
d3098 2
a3099 8
			textPtr->dInfoPtr->metricEpoch = -1;
			index.tree = textPtr->sharedTextPtr->tree;
			index.linePtr = linePtr;
			index.byteIndex = 0;
			index.textPtr = NULL;
			indexPtr = &index;
			pixelHeight = 0;
		    }
d3101 1
d3103 4
a3106 2
		     * Update the line and update the counter, counting 8 for
		     * each display line we actually re-layout.
d3109 1
a3109 25
		    count += 8 * TkTextUpdateOneLine(textPtr, linePtr,
			    pixelHeight, indexPtr, 1);

		    if (indexPtr->linePtr == linePtr) {
			/*
			 * We didn't complete the logical line, because it
			 * produced very many display lines - it must be a
			 * long line wrapped many times. So we must cache as
			 * far as we got for next time around.
			 */

			if (pixelHeight == 0) {
			    /*
			     * These have already been stored, unless we just
			     * started the new line.
			     */

			    textPtr->dInfoPtr->metricIndex = index;
			    textPtr->dInfoPtr->metricEpoch =
				    textPtr->sharedTextPtr->stateEpoch;
			}
			textPtr->dInfoPtr->metricPixelHeight =
				TkBTreeLinePixelCount(textPtr, linePtr);
			break;
		    } else {
d3111 2
a3112 1
			 * We're done with this long line.
d3115 3
a3117 1
			textPtr->dInfoPtr->metricEpoch = -1;
d3119 3
d3123 1
a3123 1
	    } else {
d3125 1
a3125 2
		 * This line is already up to date. That means there's nothing
		 * to do here.
d3127 2
d3329 1
a3329 1
		AsyncUpdateLineMetrics, (ClientData) textPtr);
d3379 2
d3390 1
a3390 2
    } else {
	TkTextIndex index = *indexPtr;
d3392 3
a3394 2
	index.byteIndex = 0;
	index.textPtr = NULL;
d3396 2
a3397 2
	while (1) {
	    TkTextIndex endOfLastLine;
d3399 4
a3402 4
	    if (TkTextIndexBackBytes(textPtr, &index, 1, &endOfLastLine)) {
		/*
		 * Reached beginning of text.
		 */
d3404 2
a3405 11
		break;
	    }

	    if (!TkTextIsElided(textPtr, &endOfLastLine, NULL)) {
		/*
		 * The eol is not elided, so 'index' points to the start of a
		 * display line (as well as logical line).
		 */

		break;
	    }
d3407 1
d3409 2
a3410 3
	     * indexPtr's logical line is actually merged with the previous
	     * logical line whose eol is elided. Continue searching back to
	     * get a real line start.
d3413 1
a3413 2
	    index = endOfLastLine;
	    index.byteIndex = 0;
d3416 17
a3432 4
	while (1) {
	    DLine *dlPtr;
	    int byteCount;
	    TkTextIndex nextLineStart;
d3434 1
a3434 2
	    dlPtr = LayoutDLine(textPtr, &index);
	    byteCount = dlPtr->byteCount;
d3436 6
a3441 1
	    TkTextIndexForwBytes(textPtr, &index, byteCount, &nextLineStart);
d3443 4
d3448 1
a3448 5
	     * 'byteCount' goes up to the beginning of the next display line,
	     * so equality here says we need one more line. We try to perform
	     * a quick comparison which is valid for the case where the
	     * logical line is the same, but otherwise fall back on a full
	     * TkTextIndexCmp.
d3451 1
a3451 4
	    if (((index.linePtr == indexPtr->linePtr)
		    && (index.byteIndex + byteCount > indexPtr->byteIndex))
		    || (dlPtr->logicalLinesMerged > 0
		    && TkTextIndexCmp(&nextLineStart, indexPtr) > 0)) {
d3453 3
a3455 1
		 * It's on this display line.
d3458 8
a3465 7
		if (xOffset != NULL) {
		    /*
		     * This call takes a byte index relative to the start of
		     * the current _display_ line, not logical line. We are
		     * about to overwrite indexPtr->byteIndex, so we must do
		     * this now.
		     */
d3467 3
a3469 15
		    *xOffset = DlineXOfIndex(textPtr, dlPtr,
			    indexPtr->byteIndex - dlPtr->index.byteIndex);
		}
		if (end) {
		    /*
		     * The index we want is one less than the number of bytes
		     * in the display line.
		     */

		    TkTextIndexBackBytes(textPtr, &nextLineStart, 1, indexPtr);
		} else {
		    *indexPtr = index;
		}
		FreeDLines(textPtr, dlPtr, NULL, DLINE_FREE_TEMP);
		return;
d3472 1
a3472 1
	    index = nextLineStart;
d3474 3
d3860 1
a3860 1
		AsyncUpdateYScrollbar, (ClientData) textPtr);
d3886 1
a3886 1
    register TkText *textPtr = (TkText *) clientData;
d3905 1
a3905 1
    Tcl_Preserve((ClientData) interp);
d4343 1
a4343 1
    Tcl_Release((ClientData) interp);
d4372 1
a4372 1
	Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
d4416 1
a4416 1
	Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
d4553 1
a4553 1
	Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
d4735 1
a4735 1
	Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
d4840 1
a4840 1
	Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
d4935 1
a4935 1
		    AsyncUpdateLineMetrics, (ClientData) textPtr);
d5119 1
a5119 1
	Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
d5385 1
a5385 1
	(*chunkPtr->bboxProc)(textPtr, chunkPtr, byteCount,
d5399 5
a5403 7
	    delta -= (lineWidth - width);
	    if (delta > 0) {
		if (delta > oneThird) {
		    dInfoPtr->newXPixelOffset = (x - lineWidth/2);
		} else {
		    dInfoPtr->newXPixelOffset += (delta );
		}
d5405 1
a5405 1
		return TCL_OK;
d5412 1
a5412 1
	Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
d5493 1
a5493 1
	Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
d5578 1
a5578 1
	Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
d5695 1
a5695 1
	Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
d5975 1
a5975 1
	    Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
a6187 2
	} else {
	    dlPtr = dlPtr->nextPtr;
d6189 1
d6375 1
a6375 1
    register TkText *textPtr = (TkText *) clientData;
d6540 12
a6551 12
    } else {
	for (dlPtr = validDlPtr = dInfoPtr->dLinePtr;
		y >= (dlPtr->y + dlPtr->height);
		dlPtr = dlPtr->nextPtr) {
	    if (dlPtr->chunkPtr != NULL) {
		validDlPtr = dlPtr;
	    }
	    if (dlPtr->nextPtr == NULL) {
		/*
		 * Y-coordinate is off the bottom of the displayed text. Use
		 * the last character on the last line.
		 */
d6553 3
a6555 4
		x = dInfoPtr->maxX - 1;
		nearby = 1;
		break;
	    }
d6557 3
a6559 1
	if (dlPtr->chunkPtr == NULL) dlPtr = validDlPtr;
d6652 1
a6652 1
	indexPtr->byteIndex += (*chunkPtr->measureProc)(chunkPtr, x);
d6740 1
a6740 1
	    (*chunkPtr->bboxProc)(textPtr, chunkPtr, byteIndex,
a6745 2
	} else {
	    byteIndex -= chunkPtr->numBytes;
d6747 1
d6835 1
a6835 1
    (*chunkPtr->bboxProc)(textPtr, chunkPtr, byteIndex,
d7076 1
a7076 1
	bciPtr = (BaseCharInfo *) baseCharChunkPtr->clientData;
d7084 1
a7084 1
    chunkPtr->clientData = (ClientData) ciPtr;
d7168 1
a7168 1
    chunkPtr->clientData = (ClientData) ciPtr;
d7270 1
a7270 1
    CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
d7359 1
a7359 1
    CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
d7377 1
a7377 1
    bciPtr = (BaseCharInfo *) ciPtr->baseChunkPtr->clientData;
d7510 1
a7510 1
    CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
d7613 1
a7613 1
    CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
d7776 1
a7776 1
	ciPtr = (CharInfo *) chunkPtr2->clientData;
d7797 1
a7797 1
	ciPtr = (CharInfo *) decimalChunkPtr->clientData;
d7802 5
a7806 4
    } else {
	/*
	 * There wasn't a decimal point. Right justify the text.
	 */
d7808 4
a7811 6
	width = 0;
	for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
		chunkPtr2 = chunkPtr2->nextPtr) {
	    width += chunkPtr2->width;
	}
	desired = tabX - width;
d7813 1
d8113 1
a8113 3
	    if (ch == '\t') {
		start++;
	    } else {
d8116 1
d8276 1
a8276 1
	ciPtr = (CharInfo *)chunkPtr->clientData;
d8293 1
a8293 1
	ciPtr = (CharInfo *)addChunkPtr->clientData;
d8344 1
a8344 1
	ciPtr = (CharInfo *) chunkPtr->clientData;
d8462 1
a8462 1
    ciPtr = (CharInfo *) chunkPtr->clientData;
d8469 1
a8469 1
    bciPtr = (BaseCharInfo *) baseCharChunkPtr->clientData;
@


1.69
log
@Get rid of pre-C89-isms (esp. CONST vs const).
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.68 2007/12/13 15:24:17 dgp Exp $
d2729 1
a2729 1
		    leftX + xOffset, y, rightX2 + sValuePtr->borderWidth - 
d8170 1
a8170 1
    static const char *subcommands[] = {
d8176 1
a8176 1
    static const char *units[] = {
@


1.68
log
@merge stable branch onto HEAD
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.63.2.4 2007/10/15 18:38:33 dgp Exp $
d545 1
a545 1
static DLine *		FindDLine(DLine *dlPtr, CONST TkTextIndex *indexPtr);
d549 1
a549 1
static TextStyle *	GetStyle(TkText *textPtr, CONST TkTextIndex *indexPtr);
d556 2
a557 2
			    CONST TkTextIndex *indexPtr);
static int		MeasureChars(Tk_Font tkfont, CONST char *source,
d561 1
a561 1
			    CONST TkTextIndex *srcPtr, int distance,
d571 2
a572 2
			    CONST TkTextIndex *index1Ptr,
			    CONST TkTextIndex *index2Ptr);
d580 1
a580 1
			    CONST TkTextIndex *indexPtr, int *byteCountPtr,
d588 1
a588 1
			    Tcl_Obj *CONST objv[], double *dblPtr,
d745 1
a745 1
    CONST TkTextIndex *indexPtr)/* The character in the text for which display
d1050 1
a1050 1
    CONST TkTextIndex *indexPtr)/* Beginning of display line. May not
d3521 1
a3521 1
    CONST TkTextIndex *indexPtr,/* The index at the beginning of the display
d3592 1
a3592 1
    CONST TkTextIndex *indexPtr)/* The index of which we want the pixel
d4513 2
a4514 2
    CONST TkTextIndex*index1Ptr,/* Index of first character to redisplay. */
    CONST TkTextIndex*index2Ptr)/* Index of character just after last one to
d4531 2
a4532 2
    CONST TkTextIndex*index1Ptr,/* Index of first character to redisplay. */
    CONST TkTextIndex*index2Ptr)/* Index of character just after last one to
d5212 1
a5212 1
    CONST TkTextIndex *srcPtr,	/* Index of character from which to start
d5316 1
a5316 1
    Tcl_Obj *CONST objv[])	/* Argument objects. Someone else has already
d5445 1
a5445 1
    Tcl_Obj *CONST objv[])	/* Argument objects. Someone else has already
d5728 1
a5728 1
    Tcl_Obj *CONST objv[])	/* Argument objects. Someone else has already
d5754 1
a5754 1
	register CONST char *switchStr =
d5909 1
a5909 1
    Tcl_Obj *CONST objv[])	/* Argument objects. Someone else has already
d6417 1
a6417 1
    CONST TkTextIndex *indexPtr)/* Index of desired character. */
d6787 1
a6787 1
    CONST TkTextIndex *indexPtr,/* Index whose bounding box is desired. */
d6917 1
a6917 1
    CONST TkTextIndex *indexPtr,/* Index of character whose bounding box is
d8057 1
a8057 1
    CONST char *source,		/* Characters to be displayed. Need not be
d8072 1
a8072 1
    CONST char *special, *end, *start;
d8164 1
a8164 1
    Tcl_Obj *CONST objv[],	/* Arguments for command. */
d8170 1
a8170 1
    static CONST char *subcommands[] = {
d8176 1
a8176 1
    static CONST char *units[] = {
@


1.68.2.1
log
@Fix word-wrapping of non-breaking spaces in the text widget to work reliably.
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.68 2007/12/13 15:24:17 dgp Exp $
d7212 1
a7212 1
	    if (UCHAR(*p) < 0x80 && isspace(UCHAR(*p))) {
@


1.68.2.2
log
@[Bug 2677890]: Fix odd text widget update problem that had scrollbars being
unable to cover the whole widget. Fix is to reify the range to update sooner.
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.68.2.1 2009/08/04 21:46:03 dkf Exp $
d2905 2
a2906 3
    if (dInfoPtr->lastMetricUpdateLine == -1) {
	dInfoPtr->lastMetricUpdateLine =
		TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr);
@


1.67
log
@	* generic/tkTextDisp.c (CharUndisplayProc): fix textDisp.test crash.
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.66 2007/09/20 22:59:40 dkf Exp $
@


1.66
log
@Only call callbacks that are not NULL. [Bug 1791052]
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.65 2007/09/07 00:34:54 dgp Exp $
d7518 1
d7520 6
a7525 6
    if (chunkPtr == ciPtr->baseChunkPtr) {
	/*
	 * Basechunks are undisplayed first, when DLines are freed or
	 * partially freed, so this makes sure we don't access their data any
	 * more.
	 */
d7527 7
a7533 7
	FreeBaseChunk(chunkPtr);
    } else if (ciPtr->baseChunkPtr != NULL) {
	/*
	 * When other char chunks are undisplayed, drop their characters from
	 * the base chunk. This usually happens, when they are last in a line
	 * and need to be re-layed out.
	 */
d7535 2
a7536 2
	RemoveFromBaseChunk(chunkPtr);
    }
d7538 3
a7540 3
    ciPtr->baseChunkPtr = NULL;
    ciPtr->chars = NULL;
    ciPtr->numBytes = 0;
d7543 3
a7545 2
    ckfree((char *) ciPtr);
    chunkPtr->clientData = NULL;
@


1.65
log
@header cleanup
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.64 2007/05/31 13:31:26 das Exp $
d1554 1
d1613 3
a1615 1
	    (*chunkPtr->undisplayProc)(textPtr, chunkPtr);
d1619 3
a1621 1
	    (*breakChunkPtr->undisplayProc)(textPtr, breakChunkPtr);
@


1.64
log
@	* generic/tkFont.c:	#ifdef out debug msg printing to stderr.
	* generic/tkTextDisp.c:
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.63 2007/04/23 21:15:19 das Exp $
a17 1
#include "tkPort.h"
@


1.63
log
@	* generic/tkCanvas.c:	allow -selectforeground option to be None; add
	* generic/tkCanvText.c:	fallback to fgColor when selFgColor is None
	* generic/tkEntry.c:	(new default on aqua to match native L&F).
	* generic/tkListbox.c:
	* generic/tkText.c:

	* generic/tkCanvas.c:	   add support for bypassing all of Tk's double
	* generic/tkEntry.c:	   buffered drawing into intermediate pixmaps
	* generic/tkFrame.c:	   (via TK_NO_DOUBLE_BUFFERING #define), it is
	* generic/tkListbox.c:	   unnecessary & wasteful on aqua where all
	* generic/tkPanedWindow.c: drawing is already double-buffered by the
	* generic/tkTextDisp.c:	   window server. (Use of this on other
	* generic/ttk/ttkWidget.c: platforms would only require implementation
	* unix/tkUnixScale.c:	   of TkpClipDrawableToRect()).
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.62 2007/02/22 13:56:33 dkf Exp $
d8453 1
d8455 1
d8474 1
d8477 1
d8480 1
a8480 1
    Tcl_DStringSetLength(&bciPtr->baseChars,ciPtr->baseOffset);
@


1.63.2.1
log
@merge updates from HEAD
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.63 2007/04/23 21:15:19 das Exp $
a8452 1
#ifdef DEBUG_LAYOUT_WITH_BASE_CHUNKS
a8453 1
#endif
a8471 1
#ifdef DEBUG_LAYOUT_WITH_BASE_CHUNKS
a8473 1
#endif
d8476 1
a8476 1
    Tcl_DStringSetLength(&bciPtr->baseChars, ciPtr->baseOffset);
@


1.63.2.2
log
@merge updates from HEAD
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.63.2.1 2007/06/01 04:01:32 dgp Exp $
d18 1
@


1.63.2.3
log
@Merge updates from HEAD, then

        [core-stabilizer-branch]

        * README:               Bump version number to 8.5.0
        * generic/tk.h:
        * library/tk.tcl:
        * unix/configure.in:    Updated LOCALES.
        * unix/tk.spec:
        * win/configure.in:

        * unix/configure:       autoconf (2.59)
        * win/configure:
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.63.2.2 2007/09/07 01:25:36 dgp Exp $
a1553 1

d1612 1
a1612 3
	    if (chunkPtr->undisplayProc != NULL) {
		(*chunkPtr->undisplayProc)(textPtr, chunkPtr);
	    }
d1616 1
a1616 3
	    if (breakChunkPtr->undisplayProc != NULL) {
		(*breakChunkPtr->undisplayProc)(textPtr, breakChunkPtr);
	    }
@


1.63.2.4
log
@merge updates from HEAD
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.63.2.3 2007/10/02 20:43:53 dgp Exp $
a7517 1
    if (ciPtr) {
d7519 6
a7524 6
	if (chunkPtr == ciPtr->baseChunkPtr) {
	    /*
	     * Basechunks are undisplayed first, when DLines are freed or
	     * partially freed, so this makes sure we don't access their data
	     * any more.
	     */
d7526 7
a7532 7
	    FreeBaseChunk(chunkPtr);
	} else if (ciPtr->baseChunkPtr != NULL) {
	    /*
	     * When other char chunks are undisplayed, drop their characters
	     * from the base chunk. This usually happens, when they are last
	     * in a line and need to be re-layed out.
	     */
d7534 2
a7535 2
	    RemoveFromBaseChunk(chunkPtr);
	}
d7537 3
a7539 3
	ciPtr->baseChunkPtr = NULL;
	ciPtr->chars = NULL;
	ciPtr->numBytes = 0;
d7542 2
a7543 3
	ckfree((char *) ciPtr);
	chunkPtr->clientData = NULL;
    }
@


1.62
log
@Less of that K&R or wrongly indented function header style!
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.61 2007/01/11 15:35:39 dkf Exp $
d2350 5
d2358 18
d2381 1
a2381 2
    display = Tk_Display(textPtr->tkwin);
    Tk_Fill3DRectangle(textPtr->tkwin, pixmap, textPtr->border, 0, 0,
d2404 1
a2404 1
			dlPtr->spaceAbove,
d2451 4
a2454 4
	    (*chunkPtr->displayProc)(textPtr, chunkPtr, x, dlPtr->spaceAbove,
		    dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
		    dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
		    dlPtr->y + dlPtr->spaceAbove);
d2462 1
a2471 10
    height = dlPtr->height;
    if ((height + dlPtr->y) > dInfoPtr->maxY) {
	height = dInfoPtr->maxY - dlPtr->y;
    }
    if (dlPtr->y < dInfoPtr->y) {
	y_off = dInfoPtr->y - dlPtr->y;
	height -= y_off;
    } else {
	y_off = 0;
    }
d2473 1
a2473 1
	    dInfoPtr->x, y_off, (unsigned) (dInfoPtr->maxX - dInfoPtr->x),
d2475 3
d2537 5
a2541 1

d2606 1
a2606 1
		    leftX + xOffset, 0, (unsigned int) (rightX - leftX),
d2610 1
a2610 1
			leftX + xOffset, 0, sValuePtr->borderWidth,
d2614 1
a2614 1
			0, sValuePtr->borderWidth, dlPtr->height, 0,
d2674 1
a2674 1
			    sValuePtr->border, leftX + xOffset, 0,
d2713 1
a2713 1
			rightX2 - sValuePtr->borderWidth + xOffset, 0,
d2722 1
a2722 1
		    rightX2 + xOffset, 0, sValuePtr->borderWidth,
d2725 2
a2726 2
		    leftX + xOffset,0, rightX2 + sValuePtr->borderWidth -leftX,
		    sValuePtr->borderWidth, leftXIn, 0, 1,
d2790 1
a2790 1
			    dlPtr->height - sValuePtr->borderWidth,
d2817 1
a2817 1
			dlPtr->height - sValuePtr->borderWidth,
d2826 1
a2826 1
		    rightX2 + xOffset, dlPtr->height - sValuePtr->borderWidth,
d2828 1
a2828 1
		    1, sValuePtr->relief);
d2830 4
a2833 3
		    leftX + xOffset, dlPtr->height - sValuePtr->borderWidth,
		    rightX2 + sValuePtr->borderWidth - leftX,
		    sValuePtr->borderWidth, leftXIn, 1, 0, sValuePtr->relief);
d4198 1
d4202 3
d4220 1
d4222 1
d4276 1
d4278 1
@


1.61
log
@Many minute fixes to reduce number of minor warnings from GCC.
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.60 2006/10/17 23:44:45 hobbs Exp $
d77 13
a89 13
 *   With this macro set, collect all char chunks that have no holes between
 *   them, that are on the same line and use the same font and font size.
 *   Allocate the chars of all these chunks, the so-called "stretch", in a
 *   DString in the first chunk, the so-called "base chunk".  Use the base
 *   chunk string for measuring and drawing, so that these actions are always
 *   performed with maximum context.
 *
 *   This is necessary for text rendering engines that provide ligatures and
 *   sub-pixel layout, like ATSU on Mac.  If we don't do this, the measuring
 *   will change all the time, leading to an ugly "tremble and shiver"
 *   effect.  This is because of the continuous splitting and re-merging of
 *   chunks that goes on in a text widget, when the cursor or the selection
 *   move.
d93 7
a99 7
 *   Memory management changes.	 Instead of attaching the character data to
 *   the clientData structures of the char chunks, an additional DString is
 *   used.  The collection process will even lead to resizing this DString
 *   for large stretches (> TCL_DSTRING_STATIC_SIZE == 200).  We could reduce
 *   the overall memory footprint by copying the result to a plain char array
 *   after the line breaking process, but that would complicate the code and
 *   make performance even worse speedwise.  See also TODOs.
d103 12
a114 12
 *   - Move the character collection process from the LayoutProc into
 *     LayoutDLine(), so that the collection can be done before actual
 *     layout.	In this way measuring can look at the following text, too,
 *     right from the beginning.  Memory handling can also be improved with
 *     this.  Problem: We don't easily know which chunks are adjacent until
 *     all the other chunks have calculated their width.  Apparently marks
 *     would return width==0.  A separate char collection loop would have to
 *     know these things.
 *
 *   - Use a new context parameter to pass the context from LayoutDLine() to
 *     the LayoutProc instead of using a global variable like now.  Not
 *     pressing until the previous point gets implemented.
d153 1
a153 1
    int elide;			/* Zero means draw text, otherwise not */
d432 3
a434 4
    const char *chars;		/* UTF characters to display.  Actually
				 * points into the baseChars of the base
				 * chunk.  Only valid after
				 * FinalizeBaseChunk().	 */
d446 1
a446 1
				 * known, else -1.  Valid during
d510 4
a513 6
			    TkTextDispChunk *chunkPtr,
			    int index, int y, int lineHeight, int baseline,
			    int *xPtr, int *yPtr, int *widthPtr,
			    int *heightPtr);
static int		CharChunkMeasureChars(
			    TkTextDispChunk *chunkPtr,
d518 3
a520 3
			    TkTextDispChunk *chunkPtr,
			    int x, int y, int height, int baseline,
			    Display *display, Drawable dst, int screenY);
a523 2


d525 4
a528 10

static void		FinalizeBaseChunk(
			    TkTextDispChunk *additionalChunkPtr);
static void		FreeBaseChunk(
			    TkTextDispChunk *baseChunkPtr);
static int		IsSameFGStyle(
			    TextStyle *style1, TextStyle *style2);
static void		RemoveFromBaseChunk(
			    TkTextDispChunk *chunkPtr);

a529 1

d532 1
a532 1
 * pointers to these functions. ElideDisplayProc, ElideUndisplayProc
a535 1

d537 3
a539 4
			    TkTextDispChunk *chunkPtr,
			    int index, int y, int lineHeight, int baseline,
			    int *xPtr, int *yPtr, int *widthPtr,
			    int *heightPtr);
d541 4
a544 4
static void		DisplayDLine(TkText *textPtr,
			    DLine *dlPtr, DLine *prevPtr, Pixmap pixmap);
static void		DisplayLineBackground(TkText *textPtr,
			    DLine *dlPtr, DLine *prevPtr, Pixmap pixmap);
d551 4
a554 4
static void		GetXView(Tcl_Interp *interp,
			    TkText *textPtr, int report);
static void		GetYView(Tcl_Interp *interp,
			    TkText *textPtr, int report);
a756 1

a760 1

d781 1
a781 1
    memset((VOID *) &styleValues, 0, sizeof(StyleValues));
d802 2
a803 2
	 * default on Windows), then we need to skip it if we don't
	 * have focus.
d1103 1
a1103 1
    TkTextElideInfo info;	/* Keep track of elide state */
d1113 1
a1113 1
    dlPtr->oldY = 0;		/* Only set to avoid compiler warnings */
d1130 1
a1130 1
     * Special case entirely elide line as there may be 1000s or more
d1429 1
a1429 1
			((BaseCharInfo*) baseCharChunkPtr->clientData)->width
a1629 1

d2007 1
a2007 1
		bytesToCount = 0;	/* Stop compiler warning */
d2409 2
a2410 2
	 * Don't call if elide. This tax ok since not very many visible
	 * DLine's in an area, but potentially many elide ones
d2959 2
a2960 2
    int lineNum,		/* Start at this line */
    int endLine,		/* Go no further than this line */
d2971 4
a2974 1
	/* Empty peer widget */
d3301 1
a3301 1
     * Now re-set the current update calculations
d3735 1
d4061 3
a4063 4
	if (TkScrollWindow(textPtr->tkwin, dInfoPtr->scrollGC,
		dInfoPtr->x, oldY,
		(dInfoPtr->maxX - dInfoPtr->x), height,
		0, y - oldY, damageRgn)) {
d4183 3
a4185 1
	    if (dlPtr->chunkPtr == NULL) continue;
d4243 2
a4244 3
			    dlPtr->baseline - dlPtr->spaceAbove,
			    NULL, (Drawable) None,
			    dlPtr->y + dlPtr->spaceAbove);
a4281 2
  doScrollbars:

d4288 1
a4332 1
	/* ARGSUSED */
a4363 1
	/* ARGSUSED */
d4478 1
a4478 1
    TkSharedText *sharedTextPtr,/* Shared widget section, or NULL */
d4577 1
a4577 1
    TkSharedText *sharedTextPtr,/* Shared widget section, or NULL */
d4798 1
a4798 1
				 * changed */
d4966 1
a4966 1
	    && textPtr->topIndex.byteIndex == indexPtr->byteIndex) {
d5009 1
a5009 2
	    if (dInfoPtr->dLinePtr == dlPtr
		    && dInfoPtr->topPixelOffset != 0) {
d5341 1
a5341 1
	 chunkPtr = chunkPtr->nextPtr) {
d5638 2
a5639 2
	TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr,
			    0, 0, &textPtr->topIndex);
d6014 1
a6014 1
	listObj = Tcl_NewListObj(0,NULL);
d6079 1
a6079 1
				 * index */
d6082 1
d6090 1
a6090 1
    int count = TkBTreePixelsTo(textPtr, linePtr);
d6124 4
a6127 4
	     * lay them out ourselves until the end of the logical line.
	     * Here's where we could be clever and ask: what's faster, to
	     * layout all lines from here to line-end, or all lines from the
	     * original dlPtr to the line-start? We just assume the former.
d6237 1
a6238 1

d6515 5
a6519 3
	 y >= (dlPtr->y + dlPtr->height);
	 dlPtr = dlPtr->nextPtr) {
	    if (dlPtr->chunkPtr !=NULL) validDlPtr = dlPtr;
d6652 1
a6652 1
				 * index */
d6698 3
a6700 1
    if (byteIndex == 0 || chunkPtr == NULL) return 0;
d6740 3
a6742 3
 *	Zero is returned if the index is on the screen. -1 means the index
 *	isn't on the screen. If the return value is 0, then the bounding box
 *	of the part of the index that's visible on the screen is returned to
d6928 1
a6928 1
 * Get bounding-box information about an elided chunk
d6956 1
a6956 1
 * Measure an elided chunk
a7040 1

d7042 1
a7042 1
	bciPtr = (BaseCharInfo*) ckalloc(sizeof(BaseCharInfo));
d7049 2
a7050 2
	bciPtr = (BaseCharInfo*) baseCharChunkPtr->clientData;
	ciPtr = (CharInfo*) ckalloc(sizeof(CharInfo));
d7066 1
a7066 1
#else  /* !TK_LAYOUT_WITH_BASE_CHUNKS */
d7077 6
a7082 7
	    bytesThatFit = CharChunkMeasureChars(
		chunkPtr, line, lineOffset + chLen, lineOffset, -1,
		chunkPtr->x, -1, 0, &nextX);
#else  /* !TK_LAYOUT_WITH_BASE_CHUNKS */
	    bytesThatFit = CharChunkMeasureChars(
		chunkPtr, p, chLen, 0, -1,
		chunkPtr->x, -1, 0, &nextX);
d7113 1
a7113 1
	    ckfree((char*)ciPtr);
a7150 1

a7165 1

d7202 1
a7202 1
 *  CharChunkMeasureChars --
d7208 1
a7208 1
 *	char chunk, i.e. on a higher level of abstraction.  Use this function
d7213 4
a7216 4
 *	The return value is the number of bytes from the range of start to
 *	end in source that fit in the span given by startX and maxX.
 *	*nextXPtr is filled in with the x-coordinate at which the first
 *	character that didn't fit would be drawn, if it were to be drawn.
d7228 1
a7228 1
				 * All other callers use NULL.	Not
d7235 2
a7236 2
    int maxX,			/* Maximum pixel width of the span.  May be
				 * -1 for unlimited. */
d7262 3
a7264 2
	    Tcl_DString *baseChars =
		&((BaseCharInfo *)ciPtr->baseChunkPtr->clientData)->baseChars;
d7273 2
a7274 4
	} else {
	    if (bend == -1) {
		bend = charsLen;
	    }
d7281 1
d7339 1
a7339 1
#endif
d7350 1
a7350 2

    bciPtr = (BaseCharInfo*) ciPtr->baseChunkPtr->clientData;
a7354 1

d7363 1
a7363 1
    numBytes = Tcl_DStringLength(&((BaseCharInfo*) ciPtr)->baseChars);
a7366 1

a7368 1

d7393 2
a7394 2
    if (!sValuePtr->elide && (numBytes > offsetBytes) 
	&& (stylePtr->fgGC != None)) {
d7421 1
a7421 1
	    
d7430 1
a7430 1
#else
d7456 1
a7456 1
#endif
a7486 1

a7493 1

a7494 1

d7497 2
a7498 2
	 * the base chunk.  This usually happens, when they are last in a
	 * line and need to be re-layed out.
d7539 2
a7540 3
    return CharChunkMeasureChars(
	chunkPtr, NULL, 0, 0, chunkPtr->numBytes - 1, chunkPtr->x, x, 0,
	&endX); /* CHAR OFFSET */
d7860 1
a7860 1
	tabWidth = 0; /* Avoid compiler error */
d8008 4
a8011 4
 *	The return value is the number of bytes from the range of start to
 *	end in source that fit in the span given by startX and maxX.
 *	*nextXPtr is filled in with the x-coordinate at which the first
 *	character that didn't fit would be drawn, if it were to be drawn.
d8074 1
a8074 1
#endif
d8203 2
a8204 2
 *	up-to-date.  It is invoked when the LayoutProc has been called for
 *	all chunks and the base chunk is stable.
d8218 5
a8222 4
    TkTextDispChunk *addChunkPtr)	/* An additional chunk to add to the
					 * stretch, even though it may not be
					 * in the linked list yet.  Used by
					 * the LayoutProc, otherwise NULL. */
d8226 1
a8226 1
    CharInfo * ciPtr;
d8230 1
a8230 1
#endif
d8237 1
a8237 5
	&((BaseCharInfo*) baseCharChunkPtr->clientData)->baseChars);

    for (chunkPtr = baseCharChunkPtr;
	 chunkPtr != NULL;
	 chunkPtr = chunkPtr->nextPtr) {
d8239 2
d8243 1
a8243 1
#endif
d8256 1
a8256 2
	CharChunkMeasureChars(
	    chunkPtr, NULL, 0, 0, -1, 0, -1, 0, &newwidth);
d8261 1
a8261 2
#endif

a8264 1

d8270 3
a8272 4
	CharChunkMeasureChars(
	    addChunkPtr, NULL, 0, 0, -1, 0, -1, 0, &addChunkPtr->width);
#endif

d8285 1
a8285 1
 *	freed.	It is invoked from the UndisplayProc.  The procedure doesn't
d8293 1
a8293 1
 *	The CharInfo.chars of all dependent chunks are set to NULL.  Memory
d8301 3
a8303 2
    TkTextDispChunk * baseChunkPtr)	/* The base chunk of the stretch and
					 * head of the linked list. */
d8312 1
a8312 4
    for (chunkPtr = baseChunkPtr;
	 chunkPtr != NULL;
	 chunkPtr = chunkPtr->nextPtr) {

d8325 1
a8325 1
    Tcl_DStringFree(&((BaseCharInfo*)baseChunkPtr->clientData)->baseChars);
d8333 3
a8335 3
 *	Compare the foreground attributes of two styles.  Specifically
 *	consider: Foreground color, font, font style and font decorations,
 *	elide, "offset" and foreground stipple.	 *Don't* consider: Background
d8355 1
a8355 1
    TextStyle *style2) 
a8364 1

d8368 1
a8368 1
				   style2->fgGC->foreground)
d8375 1
a8375 2

#endif
d8381 1
a8381 3
    return sv1->tkfont	   == sv2->tkfont
	&& sv1->offset	   == sv2->offset
	;
d8383 7
a8389 8
    return sv1->tkfont	   == sv2->tkfont
	&& sv1->underline  == sv2->underline
	&& sv1->overstrike == sv2->overstrike
	&& sv1->elide	   == sv2->elide
	&& sv1->offset	   == sv2->offset
	&& sv1->fgStipple  == sv1->fgStipple
	;
#endif
d8398 2
a8399 2
 *	UndisplayProc.	The chunk in question should be the last in a
 *	stretch.  This happens during re-layouting of the break position.
d8406 5
a8410 5
 *	chunk.	It is assumed that LayoutProc and FinalizeBaseChunk are
 *	called next to repair any damage that this causes to the integrity of
 *	the stretch and the other chunks.  For that reason the base chunk is
 *	also put into baseCharChunkPtr automatically, so that LayoutProc can
 *	resume correctly.
d8417 2
a8418 2
    TkTextDispChunk *chunkPtr)		/* The chunk to remove from the end
					 * of the stretch. */
d8420 1
a8420 1
    CharInfo *ciPtr; 
d8432 1
a8432 1
    ciPtr = (CharInfo*) chunkPtr->clientData;
d8439 1
a8439 1
    bciPtr = (BaseCharInfo*) baseCharChunkPtr->clientData;
@


1.60
log
@	* generic/tkTextDisp.c (SizeOfTab): fix -tabstyle wordprocessor
	tab alignment to correct tab edge case. [Bug 1578858]
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.59 2006/09/10 17:06:32 das Exp $
d5741 2
a5742 2
	if ((switchLength >= 2)
		&& (strncmp(switchStr, "-pickplace", switchLength) == 0)) {
a7060 1

a7061 1

a7064 1

d7076 3
a7078 3
    bytesThatFit = CharChunkMeasureChars(
	chunkPtr, line, lineOffset + maxBytes, lineOffset, -1,
	chunkPtr->x, maxX, TK_ISOLATE_END, &nextX);
d7080 2
a7081 3
    bytesThatFit = CharChunkMeasureChars(
	chunkPtr, p, maxBytes, 0, -1,
	chunkPtr->x, maxX, TK_ISOLATE_END, &nextX);
d7088 1
a7088 1
	    
d7153 2
a7154 2
    ciPtr = (CharInfo *) ckalloc(
	bytesThatFit + Tk_Offset(CharInfo,chars) +1);
d7156 1
a7156 1
    memcpy(ciPtr->chars, p, bytesThatFit);
@


1.59
log
@	* macosx/tkMacOSXColor.c (TkSetMacColor, TkpGetColor): use AppearanceMgr
	* macosx/tkMacOSXDefault.h: to retrieve platform std colors for text
	* macosx/tkMacOSXPort.h:    selections, add "systemHighlightSecondary"
	color name for standard color of inactive selections, use this color as
	default for text widget -inactiveselectbackground to implement platform
	standard look for inactive text selections.

	* library/text.tcl (aqua): remove focus bindings to set selection color.

	* generic/tkTextBTree.c (TkTextIsElided): on TkAqua, don't show inactive
	* generic/tkTextDisp.c (GetStyle):        text selection when text
	                                          widget is in disabled state.

	* generic/tkEntry.c (DisplayEntry): change default TkAqua selection
	* macosx/tkMacOSXDefault.h:         relief to "flat" (platform std).

	* generic/tkText.c (CreateWidget): fix bug leading to default text
	selection relief string DEF_TEXT_SELECT_RELIEF being ignored.

	* macosx/tkMacOSXMouseEvent.c (TkMacOSXProcessMouseEvent): allow mouse
	event delivery to background windows with kWindowNoActivatesAttribute
	(e.g. overrideredirect windows), as these never come to the foreground
	they would not receive any mouse events otherwise. [Bug 1472624]

	* macosx/tkMacOSXWindowEvent.c (TkMacOSXGenerateFocusEvent): do not
	send focus events to any windows with kWindowNoActivatesAttribute.

	* macosx/tkMacOSXXStubs.c (XQueryColor, XQueryColors): implement basic
	XColor computation from pixel values, enough to make tkImg's window.c
	happy, fixes img::window failures reported on tcl-mac.

	* macosx/tkMacOSXMenu.c (DrawMenuEntryLabel): fix leak. [Bug 1554672]

	* macosx/GNUmakefile: workaround bug in 'cp -pRH' on Darwin 6 and
	earlier, fixes 'make embedded' failure reported on tcl-mac; fix error
	from 'make deploy' with same build tree as previous 'make embedded'.

	* macosx/Wish.xcodeproj/project.pbxproj: add new tclUnixCompat.c file.

	* macosx/tkMacOSXEntry.c (TkpDrawEntryBorderAndFocus): fix typo.

	* unix/tcl.m4: sync with tcl/unix/tcl.m4.
	* unix/configure: autoconf-2.59
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.58 2006/03/27 13:43:15 cc_benny Exp $
d7928 1
a7928 1
    } while (tabX < x && (tabStyle == TK_TEXT_TABSTYLE_WORDPROCESSOR));
@


1.58
log
@	* generic/tkTextDisp.c (MeasureChars): Fix calculations of start and
	end of string.  [Bugs 1325998, 1456157]
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.57 2006/03/24 14:58:00 das Exp $
d818 1
a818 1
	 * default on Windows and Mac), then we need to skip it if we don't
d823 6
a828 1
	    if (textPtr->inactiveSelBorder == NULL) {
@


1.57
log
@	* generic/tkTextDisp.c:   moved #ifdef MAC_OSX_TK code added by
	* macosx/tkMacOSXColor.c: Patch #638966 into platform specific
	* macosx/tkMacOSXInt.h:   files.

	* macosx/tkMacOSX.h:             cleaned up & rationalized order of
	* macosx/tkMacOSXBitmap.c:       #includes of tk and carbon headers.
	* macosx/tkMacOSXButton.c:
	* macosx/tkMacOSXCarbonEvents.c:
	* macosx/tkMacOSXClipboard.c:
	* macosx/tkMacOSXColor.c:
	* macosx/tkMacOSXConfig.c:
	* macosx/tkMacOSXCursor.c:
	* macosx/tkMacOSXDialog.c:
	* macosx/tkMacOSXDraw.c:
	* macosx/tkMacOSXEmbed.c:
	* macosx/tkMacOSXEntry.c:
	* macosx/tkMacOSXEvent.c:
	* macosx/tkMacOSXEvent.h:
	* macosx/tkMacOSXFont.h:
	* macosx/tkMacOSXHLEvents.c:
	* macosx/tkMacOSXInit.c:
	* macosx/tkMacOSXInt.h:
	* macosx/tkMacOSXKeyEvent.c:
	* macosx/tkMacOSXKeyboard.c:
	* macosx/tkMacOSXMenu.c:
	* macosx/tkMacOSXMenubutton.c:
	* macosx/tkMacOSXMenus.c:
	* macosx/tkMacOSXMouseEvent.c:
	* macosx/tkMacOSXRegion.c:
	* macosx/tkMacOSXScale.c:
	* macosx/tkMacOSXScrlbr.c:
	* macosx/tkMacOSXSend.c:
	* macosx/tkMacOSXSubwindows.c:
	* macosx/tkMacOSXWindowEvent.c:
	* macosx/tkMacOSXWm.c:
	* macosx/tkMacOSXWm.h:
	* macosx/tkMacOSXXStubs.c:
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.56 2006/03/22 00:21:17 das Exp $
d8064 4
a8067 3
    special = source + rangeStart;
    end = source + rangeLength;
    for (start = source; start < end; ) {
@


1.56
log
@	* generic/tkFont.c:             implementation of ATSUI text rendering
	* generic/tkInt.h:              in TkAqua provided by Benjamin
	* generic/tkTextDisp.c:         Riefenstahl. [Patch 638966]
	* library/demos/unicodeout.tcl:
	* macosx/tkMacOSXFont.h (new file):
	* macosx/tkMacOSXFont.c:
	* tests/font.test:
	* unix/tkUnixFont.c:
	* win/tkWinFont.c:

	* generic/tkFont.c:             moved MODULE_SCOPE declarations of
	* generic/tkFont.h:             font helper procs into header files.
	* macosx/tkMacOSXButton.c:
	* macosx/tkMacOSXFont.h:
	* macosx/tkMacOSXMenubutton.c:

	* macosx/Wish.xcode/project.pbxproj:     add new tkMacOSXFont.h file,
	* macosx/Wish.xcodeproj/project.pbxproj: turn off dead code stripping
	as it interferes with -sectcreate (rdar://4486223).

	* macosx/Wish.xcode/default.pbxuser:     add TCLLIBPATH=/Library/Tcl
	* macosx/Wish.xcodeproj/default.pbxuser: env var setting to tktest.

	* unix/configure.in: fix detection of symbols build when enabling
	TkAqua debug code; filter nm output of libtclstub better to avoid
	error on intel macs [Bug 1415789].
	* unix/configure: autoconf-2.59
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.55 2005/11/27 02:36:14 das Exp $
d26 4
a116 15
#ifdef MAC_OSX_TK
#define TK_LAYOUT_WITH_BASE_CHUNKS 1
#define TK_DRAW_IN_CONTEXT 1
#endif

#if TK_LAYOUT_WITH_BASE_CHUNKS && !TK_DRAW_IN_CONTEXT

#ifdef MAC_OSX_TK
#define TextStyle MacTextStyle
#include "tkMacOSXInt.h"     /* TkSetMacColor() */
#undef TextStyle
#endif

#endif /* TK_LAYOUT_WITH_BASE_CHUNKS */

d8394 2
d8397 2
a8398 18

    /*
     * On Mac, color codes may specify symbolic values like "highlight
     * foreground", but we really need the actual values here to compare.
     * Maybe see also: "TIP #154: Add Named Colors to Tk".
     *
     * FIXME: We should have and use a generic function for this.
     */

    {
	RGBColor col1, col2;
	TkSetMacColor(style1->fgGC->foreground,&col1);
	TkSetMacColor(style2->fgGC->foreground,&col2);
	if (memcmp(&col1,&col2,sizeof(col1)) != 0) {
	    return 0;
	}
    }

d8400 3
a8402 2

    if (style1->fgGC->foreground != style2->fgGC->foreground) {
a8406 1
#endif
@


1.55
log
@	* unix/tcl.m4 (Darwin): add 64bit support, check for Tiger copyfile(),
	add CFLAGS to SHLIB_LD to support passing -isysroot in env(CFLAGS) to
	configure (flag can't be present twice, so can't be in both CFLAGS and
	LDFLAGS during configure), don't use -prebind when deploying on 10.4,
	define TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING (rdar://3171542).
	(SC_ENABLE_LANGINFO, SC_TIME_HANDLER): add/fix caching, fix obsolete
	autoconf macros. Sync with tcl/unix/tcl.m4.

	* unix/configure.in: fix obsolete autoconf macros, sync gratuitous
	formatting/ordering differences with tcl/unix/configure.in.

	* unix/Makefile.in: add CFLAGS to wish/tktest link to make executable
	linking the same as during configure (needed to avoid loosing any linker
	relevant flags in CFLAGS, in particular flags that can't be in LDFLAGS).
	Avoid concurrent linking of wish and compiling of tkTestInit.o during
	parallel make, fix dependencies and flags for building tkMacOSXInit.o
	(checkstubs, checkexports): dependency and Darwin fixes
	(dist): add new macosx files.

	* macosx/tkMacOSXEvent.c (TkMacOSXProcessEvent):
	* macosx/tkMacOSXEvent.h:
	* macosx/tkMacOSXMouseEvent.c (TkMacOSXProcessMouseEvent):
	* macosx/tkMacOSXCarbonEvents.c: install standard application event
	handler, add & call functions to start and stop carbon even timer that
	runs the tcl event loop periodically during a nested carbon event loop
	in the toolbox (e.g. during menutracking) to ensure tcl timers etc
	continue to fire, register app event handler for menu tracking and HI
	command carbon events, move menu event handling to new handlers for
	those carbon events, no longer register for/handle appleevent carbon
	event (now dealt with by standard application event handler), event
	debugging code dynamically acquires carbon event debugging functions to
	allow use on Tiger where they are no longer exported from HIToolbox.

	* macosx/tkMacOSXFont.c (TkMacOSXUseAntialiasedText):
	* macosx/tkMacOSXKeyEvent.c (GetKeyboardLayout):
	* macosx/tkMacOSXCarbonEvents.c (TkMacOSXInitCarbonEvents):
	* macosx/tkMacOSXInit.c:
	* macosx/tkMacOSXInt.h: abstract common code to dynamically acquire
	address of a named symbol (from a loaded dynamic library) into new
	function TkMacOSXGetNamedSymbol() and macro TkMacOSXInitNamedSymbol.

	* macosx/tkMacOSXMenu.c (TkpNewMenu):
	* macosx/tkMacOSXMenubutton.c (MenuButtonInitControl):
	* macosx/tkMacOSXMenus.c (TkMacOSXHandleMenuSelect): switch to  modern
	utf8 aware menu manager API, remove obsolete code, add error handling.

	* macosx/tkMacOSXMouseEvent.c: define OSX 10.3 or later only constants
	if necessary to allow compilation on OSX 10.2

	* macosx/tkMacOSXWm.c (UpdateSizeHints): remove code that is never
	executed.

	* xlib/xgc.c (XCreateGC): sync with core-8-4-branch change.

	* generic/tk.h: add/correct location of version numbers in macosx files.

	* generic/tkInt.h: clarify fat compile comment.

	* macosx/Wish.pbproj/default.pbxuser (new file):
	* macosx/Wish.pbproj/jingham.pbxuser:
	* macosx/Wish.pbproj/project.pbxproj:
	* macosx/Wish.xcode/default.pbxuser:
	* macosx/Wish.xcode/project.pbxproj:
	* macosx/Wish.xcodeproj/default.pbxuser (new file):
	* macosx/Wish.xcodeproj/project.pbxproj (new file): new/updated projects
	for Xcode 2.2 on 10.4, Xcode 1.5 on 10.3 & ProjectBuilder on 10.2, with
	native tktest targets and support for universal (fat) compiles.

	* macosx/Tk-Info.plist (removed):
	* macosx/Wish-Info.plist (removed):
	* macosx/buildTkConfig.tcl (removed): remove obsolete build files.

	* macosx/README: clarification/cleanup, document new Xcode projects and
	universal (fat) builds via CFLAGS (i.e. ppc and i386 at the same time).

	* unix/Makefile.in:
	* unix/aclocal.m4:
	* unix/configure.in:
	* macosx/configure.ac (new file): add support for inclusion of
	unix/configure.in by macosx/configure.ac, allows generation of a config
	headers enabled configure script in macosx (required by Xcode projects).

	* macosx/GNUmakefile: rename from Makefile to avoid overwriting by
	configure run in tk/macosx, add support for reusing configure cache,
	build target fixes.

	* generic/tk3d.h:
	* generic/tkButton.h:
	* generic/tkCanvas.c:
	* generic/tkCanvas.h:
	* generic/tkColor.h:
	* generic/tkEntry.h:
	* generic/tkFileFilter.h:
	* generic/tkFont.c:
	* generic/tkFont.h:
	* generic/tkImage.c:
	* generic/tkImgPhoto.c:
	* generic/tkInt.h:
	* generic/tkMenu.c:
	* generic/tkMenu.h:
	* generic/tkMenubutton.h:
	* generic/tkScale.h:
	* generic/tkScrollbar.h:
	* generic/tkSelect.h:
	* generic/tkStubInit.c:
	* generic/tkStubLib.c:
	* generic/tkText.h:
	* generic/tkUndo.h:
	* macosx/tkMacOSXButton.c:
	* macosx/tkMacOSXDebug.c:
	* macosx/tkMacOSXDebug.h:
	* macosx/tkMacOSXDialog.c:
	* macosx/tkMacOSXDraw.c:
	* macosx/tkMacOSXEntry.c:
	* macosx/tkMacOSXFont.c:
	* macosx/tkMacOSXInt.h:
	* macosx/tkMacOSXMenu.c:
	* macosx/tkMacOSXMenubutton.c:
	* macosx/tkMacOSXMouseEvent.c:
	* macosx/tkMacOSXSend.c:
	* macosx/tkMacOSXSubwindows.c:
	* macosx/tkMacOSXWindowEvent.c:
	* macosx/tkMacOSXWm.c:
	* macosx/tkMacOSXXStubs.c:
	* unix/tkUnixButton.c:
	* unix/tkUnixMenu.c:
	* xlib/xgc.c: ensure externally visible symbols not contained in stubs
	table are declared as MODULE_SCOPE (or as static if not used outside of
	own source file), #ifdef out a few Xlib and aqua functions that are
	never called. These changes allow 'make checkstubs' to complete without
	error on Darwin with gcc 4.

	* macosx/tkMacOSXTest.c:
	* macosx/tkMacOSXPort.h:
	* win/tkWinTest.c:
	* generic/tkInt.decls: add functions needed by tktest to internal stubs
	table, correct signature of TkMacOSXHandleMenuSelect, add XSync to aqua
	Xlib stubs.

	* unix/tkUnixSend.c:
	* generic/tkText.c:
	* generic/tkTest.c: #ifdef unix only declarations.
	(TestmetricsCmd): unify win and mac implementation.
	(TestsendCmd): move to tkUnixSend.c to avoid access to global var.
	(TesttextCmd): move to tkText.c to avoid having to put all the internal
	text functions it uses into the stubs table.

	* generic/tkTextDisp.c:
	* macosx/tkMacOSXInit.c:
	* macosx/tkMacOSXKeyEvent.c:
	* macosx/tkMacOSXWindowEvent.c:
	* macosx/tkMacOSXXStubs.c: fix gcc 4 warnings.

	* macosx/tkMacOSXNotify.c:
	* macosx/tkMacOSXScrlbr.c: sync with core-8-4-branch.

	* generic/tkIntDecls.h:
	* generic/tkIntPlatDecls.h:
	* generic/tkIntXlibDecls.h:
	* generic/tkStubInit.c:
	* unix/configure:
	* unix/tkConfig.h.in: regen.
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.54 2005/11/17 10:57:35 dkf Exp $
d71 58
d426 2
d435 31
d526 5
d539 14
d584 2
a585 1
			    int maxBytes, int startX, int maxX, int *nextXPtr);
d1378 1
a1426 5
	/*
	 * See if there is a tab in the current chunk; if so, only layout
	 * characters up to (and including) the tab.
	 */

d1429 8
a1436 2
	if (!elide && justify == TK_JUSTIFY_LEFT) {
	    if (segPtr->typePtr == &tkTextCharType) {
d1447 13
d1589 3
d1644 3
d7035 6
d7056 38
a7093 2
    bytesThatFit =
	    MeasureChars(tkfont, p, maxBytes, chunkPtr->x, maxX, &nextX);
d7097 11
a7107 3

	    bytesThatFit = MeasureChars(tkfont, p, Tcl_UtfToUniChar(p, &ch),
		    chunkPtr->x, -1, &nextX);
d7129 10
d7161 4
a7164 2
    ciPtr = (CharInfo *) ckalloc((unsigned)
	    (sizeof(CharInfo) - 3 + bytesThatFit));
d7166 3
a7169 1
    strncpy(ciPtr->chars, p, (size_t) bytesThatFit);
d7174 20
d7226 100
d7359 1
d7362 4
a7365 1
    int offsetBytes, offsetX;
d7375 26
d7415 2
a7416 2
	offsetBytes = MeasureChars(sValuePtr->tkfont, ciPtr->chars,
		ciPtr->numBytes, x, 0, &offsetX);
d7423 40
a7462 4
    if (!sValuePtr->elide && (ciPtr->numBytes > offsetBytes)
	    && (stylePtr->fgGC != None)) {
	int numBytes = ciPtr->numBytes - offsetBytes;
	char *string = ciPtr->chars + offsetBytes;
d7471 3
a7473 2
		    ciPtr->chars + offsetBytes, offsetX,
		    y + baseline - sValuePtr->offset, 0, numBytes);
d7481 1
a7481 1
		    ciPtr->chars + offsetBytes, offsetX,
d7486 1
d7515 27
d7543 1
a7569 1
    CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
d7572 3
a7574 2
    return MeasureChars(chunkPtr->stylePtr->sValuePtr->tkfont, ciPtr->chars,
	    chunkPtr->numBytes - 1, chunkPtr->x, x, &endX); /* CHAR OFFSET */
d7622 2
a7623 2
    MeasureChars(chunkPtr->stylePtr->sValuePtr->tkfont, ciPtr->chars,
	    byteIndex, chunkPtr->x, -1, xPtr);
d7642 2
a7643 2
	MeasureChars(chunkPtr->stylePtr->sValuePtr->tkfont,
		ciPtr->chars + byteIndex, 1, *xPtr, -1, widthPtr);
d7690 1
a7690 1
    char *p;
d7803 2
a7804 2
	MeasureChars(decimalChunkPtr->stylePtr->sValuePtr->tkfont,
		ciPtr->chars, decimal, decimalChunkPtr->x, -1, &curX);
d7828 1
a7828 1
    MeasureChars(textPtr->tkfont, " ", 1, 0, -1, &spaceWidth);
d7973 1
a7973 1
    MeasureChars(textPtr->tkfont, " ", 1, 0, -1, &spaceWidth);
d8035 2
a8036 2
 *	If tabs are encountered in the string, they will be expanded to the
 *	next tab stop.
d8042 4
a8045 4
 *	The return value is the number of bytes from source that fit in the
 *	span given by startX and maxX. *nextXPtr is filled in with the
 *	x-coordinate at which the first character that didn't fit would be
 *	drawn, if it were to be drawn.
d8060 2
d8066 1
d8075 2
a8076 2
    special = source;
    end = source + maxBytes;
d8099 9
a8107 2
	start += Tk_MeasureChars(tkfont, start, special - start, maxX - curX,
		0, &width);
d8126 1
a8126 1
    return start - source;
d8229 287
@


1.54
log
@Lots of ANSIfying of function decls.
Also a few spots where code has been cleaned up more completely.
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.53 2005/10/10 10:36:35 vincentdarley Exp $
d5593 1
a5593 1
    size_t switchLength;
@


1.53
log
@tip256 implementation
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.52 2005/07/28 17:20:41 dkf Exp $
d103 1
a103 1
    int tabStyle;               /* One of TABULAR or WORDPROCESSOR. */
d152 8
d203 1
a203 1
    int flags;			/* Various flag bits:  see below for values. */
d427 1
a427 1
static void		AdjustForTab _ANSI_ARGS_((TkText *textPtr,
d429 2
a430 2
			    TkTextDispChunk *chunkPtr));
static void		CharBboxProc _ANSI_ARGS_((TkText *textPtr,
d434 2
a435 2
			    int *heightPtr));
static void		CharDisplayProc _ANSI_ARGS_((TkText *textPtr,
d438 4
a441 5
			    Display *display, Drawable dst, int screenY));
static int		CharMeasureProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
			    int x));
static void		CharUndisplayProc _ANSI_ARGS_((TkText *textPtr,
			    TkTextDispChunk *chunkPtr));
d450 1
a450 1
static void		ElideBboxProc _ANSI_ARGS_((TkText *textPtr,
d454 22
a475 29
			    int *heightPtr));
static int		ElideMeasureProc _ANSI_ARGS_((
			    TkTextDispChunk *chunkPtr, int x));

static void		DisplayDLine _ANSI_ARGS_((TkText *textPtr,
			    DLine *dlPtr, DLine *prevPtr, Pixmap pixmap));
static void		DisplayLineBackground _ANSI_ARGS_((TkText *textPtr,
			    DLine *dlPtr, DLine *prevPtr, Pixmap pixmap));
static void		DisplayText _ANSI_ARGS_((ClientData clientData));
static DLine *		FindDLine _ANSI_ARGS_((DLine *dlPtr,
			    CONST TkTextIndex *indexPtr));
static void		FreeDLines _ANSI_ARGS_((TkText *textPtr,
			    DLine *firstPtr, DLine *lastPtr, int action));
static void		FreeStyle _ANSI_ARGS_((TkText *textPtr,
			    TextStyle *stylePtr));
static TextStyle *	GetStyle _ANSI_ARGS_((TkText *textPtr,
			    CONST TkTextIndex *indexPtr));
static void		GetXView _ANSI_ARGS_((Tcl_Interp *interp,
			    TkText *textPtr, int report));
static void		GetYView _ANSI_ARGS_((Tcl_Interp *interp,
			    TkText *textPtr, int report));
static int		GetYPixelCount _ANSI_ARGS_((TkText *textPtr,
			    DLine *dlPtr));
static DLine *		LayoutDLine _ANSI_ARGS_((TkText *textPtr,
			    CONST TkTextIndex *indexPtr));
static int		MeasureChars _ANSI_ARGS_((Tk_Font tkfont,
			    CONST char *source, int maxBytes, int startX,
			    int maxX, int *nextXPtr));
static void		MeasureUp _ANSI_ARGS_((TkText *textPtr,
d477 9
a485 12
			    TkTextIndex *dstPtr, int *overlap));
static int		NextTabStop _ANSI_ARGS_((Tk_Font tkfont, int x,
			    int tabOrigin));
static void		UpdateDisplayInfo _ANSI_ARGS_((TkText *textPtr));
static void		YScrollByLines _ANSI_ARGS_((TkText *textPtr,
			    int offset));
static void		YScrollByPixels _ANSI_ARGS_((TkText *textPtr,
			    int offset));
static int		SizeOfTab _ANSI_ARGS_((TkText *textPtr,
			    int tabStyle, TkTextTabArray *tabArrayPtr, 
			    int *indexPtr, int x, int maxX));
static void		TextChanged _ANSI_ARGS_((TkText *textPtr,
d487 3
a489 4
			    CONST TkTextIndex *index2Ptr));
static void		TextInvalidateRegion _ANSI_ARGS_((TkText *textPtr,
			    TkRegion region));
static void		TextRedrawTag _ANSI_ARGS_((TkText *textPtr,
d491 11
a501 11
			    TkTextTag *tagPtr, int withTag));
static void		TextInvalidateLineMetrics _ANSI_ARGS_((TkText *textPtr,
			    TkTextLine *linePtr, int lineCount, int action));
static int		CalculateDisplayLineHeight _ANSI_ARGS_((
			    TkText *textPtr, CONST TkTextIndex *indexPtr,
			    int *byteCountPtr, int *mergedLinePtr));
static void		DlineIndexOfX _ANSI_ARGS_((TkText *textPtr,
			    DLine *dlPtr, int x, TkTextIndex *indexPtr));
static int		DlineXOfIndex _ANSI_ARGS_((TkText *textPtr,
			    DLine *dlPtr, int byteIndex));
static int		TextGetScrollInfoObj _ANSI_ARGS_((Tcl_Interp *interp,
d504 3
a506 5
			    int *intPtr));
static void		AsyncUpdateLineMetrics _ANSI_ARGS_((ClientData
			    clientData));
static void		AsyncUpdateYScrollbar _ANSI_ARGS_((ClientData
			    clientData));
a516 1

d537 2
a538 2
TkTextCreateDInfo(textPtr)
    TkText *textPtr;		/* Overall information for text widget. */
d605 2
a606 2
TkTextFreeDInfo(textPtr)
    TkText *textPtr;		/* Overall information for text widget. */
d617 1
a617 1
    FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, DLINE_UNLINK);
d658 3
a660 3
GetStyle(textPtr, indexPtr)
    TkText *textPtr;		/* Overall information about text widget. */
    CONST TkTextIndex *indexPtr;/* The character in the text for which display
d668 1
a668 1
    int numTags, new, i;
d845 2
a846 2
	    (char *) &styleValues, &new);
    if (!new) {
d907 4
a910 4
FreeStyle(textPtr, stylePtr)
    TkText *textPtr;			/* Information about overall widget. */
    register TextStyle *stylePtr;	/* Information about style to free. */

d942 1
a942 1
 *	effects of this call (or its callers) should be - the synchronisation
d960 3
a962 3
LayoutDLine(textPtr, indexPtr)
    TkText *textPtr;		/* Overall information about text widget. */
    CONST TkTextIndex *indexPtr;/* Beginning of display line. May not
d966 5
a970 5
    register DLine *dlPtr;		/* New display line. */
    TkTextSegment *segPtr;		/* Current segment in text. */
    TkTextDispChunk *lastChunkPtr;	/* Last chunk allocated so far for
					 * line. */
    TkTextDispChunk *chunkPtr;		/* Current chunk. */
d972 40
a1011 43
    TkTextDispChunk *breakChunkPtr;	/* Chunk containing best word break
					 * point, if any. */
    TkTextIndex breakIndex;		/* Index of first character in
					 * breakChunkPtr. */
    int breakByteOffset;		/* Byte offset of character within
					 * breakChunkPtr just to right of best
					 * break point. */
    int noCharsYet;			/* Non-zero means that no characters
					 * have been placed on the line
					 * yet. */
    int paragraphStart;                 /* Non-zero means that we are 
                                         * on the first line of a 
                                         * paragraph  (Used to choose 
                                         * between lmargin1, lmargin2). */
    int justify;			/* How to justify line: taken from
					 * style for the first character in
					 * line. */
    int jIndent;			/* Additional indentation (beyond
					 * margins) due to justification. */
    int rMargin;			/* Right margin width for line. */
    TkWrapMode wrapMode;		/* Wrap mode to use for this line. */
    int x = 0, maxX = 0;		/* Initializations needed only to stop
					 * compiler warnings. */
    int wholeLine;			/* Non-zero means this display line
					 * runs to the end of the text
					 * line. */
    int tabIndex;			/* Index of the current tab stop. */
    int gotTab;				/* Non-zero means the current chunk
					 * contains a tab. */
    TkTextDispChunk *tabChunkPtr;	/* Pointer to the chunk containing the
					 * previous tab stop. */
    int maxBytes;			/* Maximum number of bytes to include
					 * in this chunk. */
    TkTextTabArray *tabArrayPtr;	/* Tab stops for line; taken from
					 * style for the first character on
					 * line. */
    int tabStyle;                       /* One of TABULAR or WORDPROCESSOR. */
    int tabSize;			/* Number of pixels consumed by
					 * current tab stop. */
    TkTextDispChunk *lastCharChunkPtr;	/* Pointer to last chunk in display
					 * lines with numBytes > 0. Used to
					 * drop 0-sized chunks from the end of
					 * the line. */
d1014 1
a1014 1
    TkTextElideInfo info;		/* Keep track of elide state */
d1024 1
a1024 1
    dlPtr->oldY = 0;  /* Only setting this to avoid compiler warnings */
d1032 4
a1035 4
    /* 
     * This is not necessarily totally correct, where we have merged
     * logical lines.  Fixing this would require a quite significant
     * overhaul, though, so currently we make do with this.
d1037 1
d1039 1
a1039 1
    
d1283 6
a1288 6
	    
	    /* 
	     * See above - this test may not be entirely correct where
	     * we have partially elided lines (and therefore merged
	     * logical lines).  In such a case a byteIndex of zero
	     * doesn't necessarily mean the beginning of a logical line.
d1290 1
d1292 5
a1296 2
		/* Beginning of logical line */
	        x = chunkPtr->stylePtr->sValuePtr->lMargin1;
d1298 4
a1301 1
		/* Beginning of display line */
d1326 1
a1326 1
		for (p = segPtr->body.chars  + byteOffset; *p != 0; p++) {
d1348 6
a1353 3
	    /* Would just like to point to canonical empty chunk */
	    chunkPtr->displayProc = (Tk_ChunkDisplayProc *) NULL;
	    chunkPtr->undisplayProc = (Tk_ChunkUndisplayProc *) NULL;
d1386 9
a1394 8
	/* 
	 * We currently say we have some characters (and therefore
	 * something from which to examine tag values for the first
	 * character of the line) even if those characters are actually
	 * elided.  This behaviour is not well documented, and it might
	 * be more consistent to completely ignore such elided
	 * characters and their tags.  To do so change this to:
	 * 
d1397 1
d1431 2
a1432 2
	    tabSize = SizeOfTab(textPtr, tabStyle, tabArrayPtr, 
				&tabIndex, x, maxX);
d1593 1
a1593 1
     * Recompute line length:  may have changed because of justification.
d1629 2
a1630 2
UpdateDisplayInfo(textPtr)
    TkText *textPtr;			/* Text widget to update. */
a1658 1
     *--------------------------------------------------------------
a1660 1
     *--------------------------------------------------------------
a1696 1

d1711 1
a1711 3
		Tcl_SetVar2(textPtr->interp, "tk_textRelayout", (char *) NULL,
			string,
			TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
d1840 1
a1840 1
    FreeDLines(textPtr, dlPtr, (DLine *) NULL, DLINE_UNLINK);
a1842 1
     *--------------------------------------------------------------
a1849 1
     *--------------------------------------------------------------
d1898 1
a1898 1
		bytesToCount = 0; /* Stop compiler warning */
d1922 2
a1923 1
			bytesToCount--; break;
d1968 1
a1968 3
			Tcl_SetVar2(textPtr->interp, "tk_textRelayout",
			    (char *) NULL, string,
			    TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
d1974 1
a1974 1
		FreeDLines(textPtr, lowestPtr, (DLine *) NULL, DLINE_FREE);
a2024 1
     *--------------------------------------------------------------
a2030 1
     *--------------------------------------------------------------
a2080 1
     *--------------------------------------------------------------
a2086 1
     *--------------------------------------------------------------
d2147 4
a2150 4
FreeDLines(textPtr, firstPtr, lastPtr, action)
    TkText *textPtr;		/* Information about overall text widget. */
    register DLine *firstPtr;	/* Pointer to first DLine to free up. */
    DLine *lastPtr;		/* Pointer to DLine just after last one to
d2153 1
a2153 1
    int action;			/* DLINE_UNLINK means DLines are currently
d2176 1
a2176 2
	    Tcl_SetVar2(textPtr->interp, "tk_textHeightCalc", (char *) NULL,
		string, TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
d2183 1
d2228 4
a2231 4
DisplayDLine(textPtr, dlPtr, prevPtr, pixmap)
    TkText *textPtr;		/* Text widget in which to draw line. */
    register DLine *dlPtr;	/* Information about line to draw. */
    DLine *prevPtr;		/* Line just before one to draw, or NULL if
d2233 1
a2233 1
    Pixmap pixmap;		/* Pixmap to use for double-buffering. Caller
d2271 3
a2273 1
		(*chunkPtr->displayProc)(textPtr, chunkPtr, x, dlPtr->spaceAbove,
d2377 4
a2380 4
DisplayLineBackground(textPtr, dlPtr, prevPtr, pixmap)
    TkText *textPtr;		/* Text widget containing line. */
    register DLine *dlPtr;	/* Information about line to draw. */
    DLine *prevPtr;		/* Line just above dlPtr, or NULL if dlPtr is
d2382 1
a2382 1
    Pixmap pixmap;		/* Pixmap to use for double-buffering. Caller
d2725 2
a2726 3
 *	This function is invoked as a background handler to update the
 *	pixel-height calculations of individual lines in an asychronous
 *	manner.
d2730 2
a2731 2
 *	(e.g. a background thread). We can't use an idle-callback because of a
 *	known bug in Tcl/Tk in which idle callbacks are not allowed to
d2744 2
a2745 2
AsyncUpdateLineMetrics(clientData)
    ClientData clientData;	/* Information about widget. */
d2755 1
a2755 1
	 * The widget has been deleted.	 Don't do anything.
d2787 1
a2787 2
	Tcl_SetVar2(textPtr->interp, "tk_textInvalidateLine", (char *) NULL,
		    buffer, TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
d2830 2
a2831 2
 *	If doThisMuch is -1, then all lines in the range will be updated.
 *	This will potentially take quite some time for a large text widget.
d2848 8
a2855 7
TkTextUpdateLineMetrics(textPtr, lineNum, endLine, doThisMuch)
    TkText *textPtr;	/* Information about widget. */
    int lineNum;	/* Start at this line */
    int endLine;	/* Go no further than this line */
    int doThisMuch;	/* How many lines to check, or how many 10s of lines
			 * to recalculate. If '-1' then do everything in the
			 * range (which may take a while). */
d2905 3
a2907 5
		sprintf(buffer, "%d %d %d %d", lineNum, endLine,
			totalLines, count);
		Tcl_SetVar2(textPtr->interp, "tk_textInvalidateLine",
			(char *) NULL, buffer,
			TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
d3055 2
a3056 2
TkTextInvalidateLineMetrics(sharedTextPtr, textPtr, linePtr, lineCount, action)
    TkSharedText *sharedTextPtr;/* Shared widget section for all peers, or
d3058 4
a3061 4
    TkText *textPtr;		/* Widget record for text widget. */
    TkTextLine *linePtr;	/* Invalidation starts from this line. */
    int lineCount;		/* And includes this many following lines. */
    int action;			/* Indicates what type of invalidation
d3076 5
a3080 5
TextInvalidateLineMetrics(textPtr, linePtr, lineCount, action)
    TkText *textPtr;		/* Widget record for text widget. */
    TkTextLine *linePtr;	/* Invalidation starts from this line. */
    int lineCount;		/* And includes this many following lines. */
    int action;			/* Indicates what type of invalidation
d3163 1
a3163 1
		  && toLine > dInfoPtr->lastMetricUpdateLine) {
d3236 3
a3238 3
TkTextFindDisplayLineEnd(textPtr, indexPtr, end, xOffset)
    TkText *textPtr;		/* Widget record for text widget. */
    TkTextIndex *indexPtr;	/* Index we will adjust to the display line
d3240 2
a3241 2
    int end;			/* 0 = start, 1 = end. */
    int *xOffset;		/* NULL, or used to store the x-pixel offset
d3382 9
a3390 10
CalculateDisplayLineHeight(textPtr, indexPtr, byteCountPtr, mergedLinePtr)
    TkText *textPtr;		 /* Widget record for text widget. */
    CONST TkTextIndex *indexPtr; /* The index at the beginning of the display
				  * line of interest. */
    int *byteCountPtr;		 /* NULL or used to return the number of byte
				  * indices on the given display line. */
    int *mergedLinePtr;		 /* NULL or used to return if the given
				  * display line merges with a following
				  * logical line (because the eol is
				  * elided). */
d3453 5
a3457 5
TkTextIndexYPixels(textPtr, indexPtr)
    TkText *textPtr;		  /* Widget record for text widget. */
    CONST TkTextIndex *indexPtr;  /* The index of which we want the pixel
				   * distance from top of logical line to top
				   * of index. */
d3528 1
a3528 1
 *	'CalculateDisplayLineHeight' for its side effects.
d3534 17
a3550 14
TkTextUpdateOneLine(textPtr, linePtr, pixelHeight, indexPtr, partialCalc)
    TkText *textPtr;	    /* Widget record for text widget. */
    TkTextLine *linePtr;    /* The line of which to calculate the height. */
    int pixelHeight;	    /* If indexPtr is non-NULL, then this is the
			     * number of pixels in the logical line linePtr,
			     * up to the index which has been given. */
    TkTextIndex *indexPtr;  /* Either NULL or an index at the start of a
			     * display line belonging to linePtr, at which we
			     * wish to start (e.g. up to which we have already
			     * calculated). On return this will be set to the
			     * first index on the next line. */
    int partialCalc;	    /* Set to 1 if we are allowed to do partial height
			     * calculations of long-lines. In this case we'll
			     * only return what we know so far. */
d3719 2
a3720 3
	sprintf(buffer, "%d %d", TkBTreeLinesTo(textPtr, linePtr), pixelHeight);
	Tcl_SetVar2(textPtr->interp, "tk_textNumPixels", (char *) NULL,
		    buffer, TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
d3748 2
a3749 2
DisplayText(clientData)
    ClientData clientData;	/* Information about widget. */
d3773 1
a3773 2
	Tcl_SetVar2(interp, "tk_textRelayout", (char *) NULL, "",
		TCL_GLOBAL_ONLY);
d3792 1
a3792 2
	Tcl_SetVar2(interp, "tk_textRedraw", (char *) NULL, "",
		TCL_GLOBAL_ONLY);
d3797 1
a3797 1
	 * The widget has been deleted.	 Don't do anything.
d3976 1
a3976 2
	    Tcl_SetVar2(interp, "tk_textRedraw", (char *) NULL, "borders",
		    TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
d3997 1
a3997 1
			Tk_WindowId(textPtr->tkwin));
d4038 2
a4039 2
     * First, compute the height of the largest line and allocate an
     * off-screen pixmap to use for double-buffered displays.
d4045 2
a4046 2
	if ((dlPtr->height > maxHeight)
	  && ((dlPtr->flags & OLD_Y_INVALID) || (dlPtr->oldY != dlPtr->y))) {
d4077 1
a4077 3
		    Tcl_SetVar2(textPtr->interp, "tk_textRedraw",
			    (char *) NULL, string,
			    TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
d4130 1
a4130 1
			    (Display *) NULL, (Drawable) None,
d4151 1
a4151 3
	    Tcl_SetVar2(textPtr->interp, "tk_textRedraw",
		    (char *) NULL, "eof",
		    TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
d4223 2
a4224 2
TkTextEventuallyRepick(textPtr)
    TkText *textPtr;		/* Widget record for text widget. */
d4255 3
a4257 3
TkTextRedrawRegion(textPtr, x, y, width, height)
    TkText *textPtr;		/* Widget record for text widget. */
    int x, y;			/* Coordinates of upper-left corner of area to
d4260 1
a4260 1
    int width, height;		/* Width and height of area to be redrawn. */
d4298 3
a4300 3
TextInvalidateRegion(textPtr, region)
    TkText *textPtr;		/* Widget record for text widget. */
    TkRegion region;		/* Region of area to redraw. */
d4367 6
a4372 7
TkTextChanged(sharedTextPtr, textPtr, index1Ptr, index2Ptr)
    TkSharedText *sharedTextPtr;  /* Shared widget section, or NULL */
    TkText *textPtr;		  /* Widget record for text widget, or
				   * NULL. */
    CONST TkTextIndex *index1Ptr; /* Index of first character to redisplay. */
    CONST TkTextIndex *index2Ptr; /* Index of character just after last one to
				   * redisplay. */
d4386 5
a4390 6
TextChanged(textPtr, index1Ptr, index2Ptr)
    TkText *textPtr;		  /* Widget record for text widget, or
				   * NULL. */
    CONST TkTextIndex *index1Ptr; /* Index of first character to redisplay. */
    CONST TkTextIndex *index2Ptr; /* Index of character just after last one to
				   * redisplay. */
d4466 4
a4469 4
TkTextRedrawTag(sharedTextPtr, textPtr, index1Ptr, index2Ptr, tagPtr, withTag)
    TkSharedText *sharedTextPtr;/* Shared widget section, or NULL */
    TkText *textPtr;		/* Widget record for text widget. */
    TkTextIndex *index1Ptr;	/* First character in range to consider for
d4472 1
a4472 1
    TkTextIndex *index2Ptr;	/* Character just after last one to consider
d4475 2
a4476 2
    TkTextTag *tagPtr;		/* Information about tag. */
    int withTag;		/* 1 means redraw characters that have the
d4491 3
a4493 3
TextRedrawTag(textPtr, index1Ptr, index2Ptr, tagPtr, withTag)
    TkText *textPtr;		/* Widget record for text widget. */
    TkTextIndex *index1Ptr;	/* First character in range to consider for
d4496 1
a4496 1
    TkTextIndex *index2Ptr;	/* Character just after last one to consider
d4499 2
a4500 2
    TkTextTag *tagPtr;		/* Information about tag. */
    int withTag;		/* 1 means redraw characters that have the
d4685 3
a4687 3
TkTextRelayoutWindow(textPtr, mask)
    TkText *textPtr;		/* Widget record for text widget. */
    int mask;			/* OR'd collection of bits showing what has
d4691 1
a4691 1
    GC new;
d4710 1
a4710 1
    new = Tk_GetGC(textPtr->tkwin, GCGraphicsExposures, &gcValues);
d4714 1
a4714 1
    dInfoPtr->copyGC = new;
d4720 1
a4720 1
    FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, DLINE_UNLINK);
d4805 2
a4806 2
 *	This function is called to specify what lines are to be displayed in
 *	a text widget.
d4820 3
a4822 3
TkTextSetYView(textPtr, indexPtr, pickPlace)
    TkText *textPtr;		/* Widget record for text widget. */
    TkTextIndex *indexPtr;	/* Position that is to appear somewhere in the
d4824 12
a4835 13
    int pickPlace;		/* 0 means the given index must appear exactly
				 * at the top of the screen.
				 * TK_TEXT_PICKPLACE (-1) means we get to pick
				 * where it appears: minimize screen motion or
				 * else display line at center of screen.
				 * TK_TEXT_NOPIXELADJUST (-2) indicates to
				 * make the given index the top line, but if
				 * it is already the top line, don't nudge it
				 * up or down by a few pixels just to make
				 * sure it is entirely displayed. Positive
				 * numbers indicate the number of pixels of
				 * the index's line which are to be off the
				 * top of the screen. */
d5005 3
a5007 3
TkTextMeasureDown(textPtr, srcPtr, distance)
    TkText *textPtr;		/* Text widget in which to measure. */
    TkTextIndex *srcPtr;	/* Index of character from which to start
d5009 1
a5009 1
    int distance;		/* Vertical distance in pixels measured from
d5024 1
a5024 1
	    FreeDLines(textPtr, dlPtr, (DLine *) NULL, DLINE_FREE_TEMP);
d5029 1
a5029 1
	FreeDLines(textPtr, dlPtr, (DLine *) NULL, DLINE_FREE_TEMP);
d5068 3
a5070 3
MeasureUp(textPtr, srcPtr, distance, dstPtr, overlap)
    TkText *textPtr;		/* Text widget in which to measure. */
    CONST TkTextIndex *srcPtr;	/* Index of character from which to start
d5072 1
a5072 1
    int distance;		/* Vertical distance in pixels measured from
d5075 2
a5076 2
    TkTextIndex *dstPtr;	/* Index to fill in with result. */
    int *overlap;		/* Used to store how much of the final index
d5133 1
a5133 1
	FreeDLines(textPtr, lowestPtr, (DLine *) NULL, DLINE_FREE);
d5170 5
a5174 5
TkTextSeeCmd(textPtr, interp, objc, objv)
    TkText *textPtr;		/* Information about text widget. */
    Tcl_Interp *interp;		/* Current interpreter. */
    int objc;			/* Number of arguments. */
    Tcl_Obj *CONST objv[];	/* Argument objects. Someone else has already
d5299 5
a5303 5
TkTextXviewCmd(textPtr, interp, objc, objv)
    TkText *textPtr;		/* Information about text widget. */
    Tcl_Interp *interp;		/* Current interpreter. */
    int objc;			/* Number of arguments. */
    Tcl_Obj *CONST objv[];	/* Argument objects. Someone else has already
d5337 1
d5373 1
a5373 2
 *	The view in textPtr's window changes to reflect the value
 *	of "offset".
d5379 3
a5381 3
YScrollByPixels(textPtr, offset)
    TkText *textPtr;		/* Widget to scroll. */
    int offset;			/* Amount by which to scroll, in pixels.
d5403 1
a5403 1
	TkTextIndex new;
d5418 1
a5418 1
		    dlPtr->byteCount, &new);
d5427 2
a5428 2
	    FreeDLines(textPtr, dlPtr, (DLine *) NULL, DLINE_FREE_TEMP);
	    if (new.linePtr == lastLinePtr || offset <= 0) {
d5431 1
a5431 1
	    textPtr->topIndex = new;
d5464 3
a5466 3
YScrollByLines(textPtr, offset)
    TkText *textPtr;		/* Widget to scroll. */
    int offset;			/* Amount by which to scroll, in display
d5473 1
a5473 1
    TkTextIndex new, index;
d5516 1
a5516 1
	    FreeDLines(textPtr, lowestPtr, (DLine *) NULL, DLINE_FREE);
d5547 3
a5549 3
		    dlPtr->byteCount, &new);
	    FreeDLines(textPtr, dlPtr, (DLine *) NULL, DLINE_FREE);
	    if (new.linePtr == lastLinePtr) {
d5552 1
a5552 1
	    textPtr->topIndex = new;
d5582 5
a5586 5
TkTextYviewCmd(textPtr, interp, objc, objv)
    TkText *textPtr;		/* Information about text widget. */
    Tcl_Interp *interp;		/* Current interpreter. */
    int objc;			/* Number of arguments. */
    Tcl_Obj *CONST objv[];	/* Argument objects. Someone else has already
d5612 3
a5614 1
	switchLength = strlen(Tcl_GetString(objv[2]));
d5616 1
a5616 2
		&& (strncmp(Tcl_GetString(objv[2]),
			"-pickplace", switchLength) == 0)) {
d5626 1
d5629 1
a5629 1
				lineNum, 0, &index);
d5706 1
d5763 5
a5767 5
TkTextScanCmd(textPtr, interp, objc, objv)
    register TkText *textPtr;	/* Information about text widget. */
    Tcl_Interp *interp;		/* Current interpreter. */
    int objc;			/* Number of arguments. */
    Tcl_Obj *CONST objv[];	/* Argument objects. Someone else has already
d5777 7
a5783 4
	Tcl_AppendResult(interp, "wrong # args: should be \"",
		Tcl_GetString(objv[0]), " scan mark x y\" or \"",
		Tcl_GetString(objv[0]), " scan dragto x y ?gain?\"",
		(char *) NULL);
d5847 1
a5847 1
		"\": must be mark or dragto", (char *) NULL);
d5879 2
a5880 2
GetXView(interp, textPtr, report)
    Tcl_Interp *interp;		/* If "report" is FALSE, string describing
d5883 2
a5884 2
    TkText *textPtr;		/* Information about text widget. */
    int report;			/* Non-zero means report info to scrollbar if
d5967 3
a5969 3
GetYPixelCount(textPtr, dlPtr)
    TkText *textPtr;		/* Information about text widget. */
    DLine *dlPtr;		/* Information about the layout of a given
d6027 1
a6027 1
		    FreeDLines(textPtr, dlPtr, (DLine *)NULL, DLINE_FREE_TEMP);
d6044 1
a6044 3
		    Tcl_SetVar2(textPtr->interp, "tk_textHeightCalc",
			    (char *) NULL, string,
			    TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
d6063 3
a6065 3
 *	This function computes the fractions that indicate what's visible in
 *	a text window and, optionally, evaluates a Tcl script to report them
 *	to the text's associated scrollbar.
d6083 2
a6084 2
GetYView(interp, textPtr, report)
    Tcl_Interp *interp;		/* If "report" is FALSE, string describing
d6087 2
a6088 2
    TkText *textPtr;		/* Information about text widget. */
    int report;			/* Non-zero means report info to scrollbar if
d6193 1
a6193 1
	FP_EQUAL_SCALE(last,  dInfoPtr->yScrollLast,  totalPixels)) {
d6235 2
a6236 2
AsyncUpdateYScrollbar(clientData)
    ClientData clientData;	/* Information about widget. */
d6271 2
a6272 2
FindDLine(dlPtr, indexPtr)
    register DLine *dlPtr;	/* Pointer to first in list of DLines to
d6274 1
a6274 1
    CONST TkTextIndex *indexPtr;/* Index of desired character. */
d6349 3
a6351 3
TkTextPixelIndex(textPtr, x, y, indexPtr, nearest)
    TkText *textPtr;		/* Widget record for text widget. */
    int x, y;			/* Pixel coordinates of point in widget's
d6353 1
a6353 1
    TkTextIndex *indexPtr;	/* This index gets filled in with the index of
d6355 1
a6355 1
    int *nearest;		/* If non-NULL then gets set to 0 if (x,y) is
d6399 1
a6399 1
	   *nearest = 1;
d6450 3
a6452 3
DlineIndexOfX(textPtr, dlPtr, x, indexPtr)
    TkText *textPtr;		/* Widget record for text widget. */
    DLine *dlPtr;		/* Display information for this display
d6454 1
a6454 1
    int x;			/* Pixel x coordinate of point in widget's
d6456 1
a6456 1
    TkTextIndex *indexPtr;	/* This index gets filled in with the index of
d6537 3
a6539 3
TkTextIndexOfX(textPtr, x, indexPtr)
    TkText *textPtr;		/* Widget record for text widget. */
    int x;			/* The x coordinate for which we want the
d6541 1
a6541 1
    TkTextIndex *indexPtr;	/* Index of display line start, which will be
d6576 3
a6578 3
DlineXOfIndex(textPtr, dlPtr, byteIndex)
    TkText *textPtr;		/* Widget record for text widget. */
    DLine *dlPtr;		/* Display information for this display
d6580 1
a6580 1
    int byteIndex;		/* The byte index for which we want the
d6626 4
a6629 4
 *	Zero is returned if the index is on the screen. -1 means the
 *	index isn't on the screen. If the return value is 0, then the
 *	bounding box of the part of the index that's visible on the screen
 *	is returned to *xPtr, *yPtr, *widthPtr, and *heightPtr.
d6638 4
a6641 4
TkTextIndexBbox(textPtr, indexPtr, xPtr, yPtr, widthPtr, heightPtr, charWidthPtr)
    TkText *textPtr;		/* Widget record for text widget. */
    CONST TkTextIndex *indexPtr;/* Index whose bounding box is desired. */
    int *xPtr, *yPtr;		/* Filled with index's upper-left
d6643 6
a6648 6
    int *widthPtr, *heightPtr;	/* Filled in with index's dimensions. */
    int *charWidthPtr;		/* If the 'index' is at the end of
                      		 * a display line and therefore takes
                      		 * up a very large width, this is
                      		 * used to return the smaller width
                      		 * actually desired by the index.  */
d6719 3
a6721 4
	/* 
	 * With zero width (e.g. elided text) we just need to
	 * make sure it is onscreen, where the '=' case here is
	 * ok.
d6723 1
d6768 3
a6770 3
TkTextDLineInfo(textPtr, indexPtr, xPtr, yPtr, widthPtr, heightPtr, basePtr)
    TkText *textPtr;		/* Widget record for text widget. */
    CONST TkTextIndex *indexPtr;/* Index of character whose bounding box is
d6772 1
a6772 1
    int *xPtr, *yPtr;		/* Filled with line's upper-left
d6774 3
a6776 2
    int *widthPtr, *heightPtr;	/* Filled in with line's dimensions. */
    int *basePtr;		/* Filled in with the baseline position,
d6818 4
a6821 5
ElideBboxProc(textPtr, chunkPtr, index, y, lineHeight, baseline, xPtr, yPtr,
	widthPtr, heightPtr)
    TkText *textPtr;
    TkTextDispChunk *chunkPtr;	/* Chunk containing desired char. */
    int index;			/* Index of desired character within the
d6823 1
a6823 1
    int y;			/* Topmost pixel in area allocated for this
d6825 2
a6826 2
    int lineHeight;		/* Height of line, in pixels. */
    int baseline;		/* Location of line's baseline, in pixels
d6828 1
a6828 1
    int *xPtr, *yPtr;		/* Gets filled in with coords of character's
d6831 1
a6831 1
    int *widthPtr;		/* Gets filled in with width of character, in
d6833 1
a6833 1
    int *heightPtr;		/* Gets filled in with height of character, in
d6846 3
a6848 3
ElideMeasureProc(chunkPtr, x)
    TkTextDispChunk *chunkPtr;	/* Chunk containing desired coord. */
    int x;			/* X-coordinate, in same coordinate system as
d6876 3
a6878 4
TkTextCharLayoutProc(textPtr, indexPtr, segPtr, byteOffset, maxX, maxBytes,
	noCharsYet, wrapMode, chunkPtr)
    TkText *textPtr;		/* Text widget being layed out. */
    TkTextIndex *indexPtr;	/* Index of first character to lay out
d6880 2
a6881 2
    TkTextSegment *segPtr;	/* Segment being layed out. */
    int byteOffset;		/* Byte offset within segment of first
d6883 1
a6883 1
    int maxX;			/* Chunk must not occupy pixels at this
d6885 1
a6885 1
    int maxBytes;		/* Chunk must not include more than this many
d6887 1
a6887 1
    int noCharsYet;		/* Non-zero means no characters have been
d6889 1
a6889 1
    TkWrapMode wrapMode;	/* How to handle line wrapping:
d6892 1
a6892 1
    register TkTextDispChunk *chunkPtr;
d6905 3
a6907 3
     * Figure out how many characters will fit in the space we've got.
     * Include the next character, even though it won't fit completely, if any
     * of the following is true:
d6911 1
a6911 1
     *	 (b) at least one pixel of the character is visible, we haven't
d7027 4
a7030 4
CharDisplayProc(textPtr, chunkPtr, x, y, height, baseline, display, dst, screenY)
    TkText *textPtr;
    TkTextDispChunk *chunkPtr;	/* Chunk that is to be drawn. */
    int x;			/* X-position in dst at which to draw this
d7033 1
a7033 1
    int y;			/* Y-position at which to draw this chunk in
d7035 5
a7039 5
    int height;			/* Total height of line. */
    int baseline;		/* Offset of baseline from y. */
    Display *display;		/* Display to use for drawing. */
    Drawable dst;		/* Pixmap or window in which to draw chunk. */
    int screenY;		/* Y-coordinate in text window that
d7125 3
a7127 3
CharUndisplayProc(textPtr, chunkPtr)
    TkText *textPtr;		/* Overall information about text widget. */
    TkTextDispChunk *chunkPtr;	/* Chunk that is about to be freed. */
d7153 3
a7155 3
CharMeasureProc(chunkPtr, x)
    TkTextDispChunk *chunkPtr;	/* Chunk containing desired coord. */
    int x;			/* X-coordinate, in same coordinate system as
d7188 4
a7191 5
CharBboxProc(textPtr, chunkPtr, byteIndex, y, lineHeight, baseline, xPtr, yPtr,
	widthPtr, heightPtr)
    TkText *textPtr;
    TkTextDispChunk *chunkPtr;	/* Chunk containing desired char. */
    int byteIndex;		/* Byte offset of desired character within the
d7193 1
a7193 1
    int y;			/* Topmost pixel in area allocated for this
d7195 2
a7196 2
    int lineHeight;		/* Height of line, in pixels. */
    int baseline;		/* Location of line's baseline, in pixels
d7198 1
a7198 1
    int *xPtr, *yPtr;		/* Gets filled in with coords of character's
d7201 1
a7201 1
    int *widthPtr;		/* Gets filled in with width of character, in
d7203 1
a7203 1
    int *heightPtr;		/* Gets filled in with height of character, in
d7263 2
a7264 2
AdjustForTab(textPtr, tabArrayPtr, index, chunkPtr)
    TkText *textPtr;		/* Information about the text widget as a
d7266 1
a7266 1
    TkTextTabArray *tabArrayPtr;/* Information about the tab stops that apply
d7269 2
a7270 2
    int index;			/* Index of current tab stop. */
    TkTextDispChunk *chunkPtr;	/* Chunk whose last character is the tab; the
d7283 1
a7283 1
	 * Nothing after the actual tab;  just return.
d7292 3
a7294 3
     * If no tab information has been given, assuming tab stops are
     * at 8 average-sized characters.  Still ensure we respect the
     * tabular versus wordprocessor tab style.
d7313 1
a7313 1
	
d7452 2
a7453 2
SizeOfTab(textPtr, tabStyle, tabArrayPtr, indexPtr, x, maxX)
    TkText *textPtr;		/* Information about the text widget as a
d7455 3
a7457 3
    int tabStyle;               /* One of TK_TEXT_TABSTYLE_TABULAR 
                                 * or TK_TEXT_TABSTYLE_WORDPROCESSOR. */
    TkTextTabArray *tabArrayPtr;/* Information about the tab stops that apply
d7460 1
a7460 1
    int *indexPtr;		/* Contains index of previous tab stop, will
d7463 2
a7464 2
    int x;			/* Current x-location in line. */
    int maxX;			/* X-location of pixel just past the right
d7473 4
a7476 1
	/* We're using a default tab spacing of 8 characters */
d7484 1
a7484 1
    
d7491 1
a7491 1
	
d7493 4
a7496 1
	    /* We're using a default tab spacing calculated above */
d7514 7
a7520 8
	 * If this tab stop is before the current x position, then we
	 * have two cases:
	 * 
	 * With 'wordprocessor' style tabs, we must obviously continue
	 * until we reach the text tab stop.
	 * 
	 * With 'tabular' style tabs, we always use the index'th tab
	 * stop.
d7576 1
a7576 2
 *	font should be used, provided we are using wordprocessor 
 *	style tabs.
d7588 2
a7589 2
NextTabStop(tkfont, x, tabOrigin)
    Tk_Font tkfont;		/* Font in which chunk that contains tab stop
d7591 1
a7591 1
    int x;			/* X-position in pixels where last character
d7594 1
a7594 1
    int tabOrigin;		/* The origin for tab stops. May be non-zero
d7616 1
a7616 1
 *  MeasureChars --
d7642 3
a7644 3
MeasureChars(tkfont, source, maxBytes, startX, maxX, nextXPtr)
    Tk_Font tkfont;		/* Font in which to draw characters. */
    CONST char *source;		/* Characters to be displayed. Need not be
d7646 1
a7646 1
    int maxBytes;		/* Maximum # of bytes to consider from
d7648 1
a7648 1
    int startX;			/* X-position at which first character will be
d7650 1
a7650 1
    int maxX;			/* Don't consider any character that would
d7652 1
a7652 1
    int *nextXPtr;		/* Return x-position of terminating character
d7684 2
a7685 2
	start += Tk_MeasureChars(tkfont, start, special - start,
				 maxX - curX, 0, &width);
d7736 6
a7741 6
TextGetScrollInfoObj(interp, textPtr, objc, objv, dblPtr, intPtr)
    Tcl_Interp *interp;		/* Used for error reporting. */
    TkText *textPtr;		/* Information about the text widget. */
    int objc;			/* # arguments for command. */
    Tcl_Obj *CONST objv[];	/* Arguments for command. */
    double *dblPtr;		/* Filled in with argument "moveto" option, if
d7743 1
a7743 1
    int *intPtr;		/* Filled in with number of pages or lines or
d7746 21
a7766 7
    char c;
    int length;
    CONST char *arg2;

    arg2 = Tcl_GetStringFromObj(objv[2], &length);
    c = arg2[0];
    if (c=='m' && strncmp(arg2, "moveto", (unsigned) length)==0) {
d7768 1
a7768 1
	    Tcl_WrongNumArgs(interp, 2, objv, "moveto fraction");
d7775 1
a7775 4
    } else if (c=='s' && strncmp(arg2, "scroll", (unsigned) length)==0) {
	CONST char *arg4;
	size_t argLen;

d7777 1
a7777 2
	    Tcl_WrongNumArgs(interp, 2, objv,
	    "scroll number units|pages|pixels");
d7780 2
a7781 6
	arg4 = Tcl_GetStringFromObj(objv[4], &length);
	argLen = (size_t) length;
	c = arg4[0];
	if (c=='p' && length==1) {
	    Tcl_AppendResult(interp, "ambiguous argument \"", arg4,
		    "\": must be units, pages or pixels", (char *) NULL);
d7783 3
a7785 1
	} else if (c=='p' && strncmp(arg4, "pages", argLen)==0) {
d7790 1
a7790 1
	} else if (c=='p' && strncmp(arg4, "pixels", argLen)==0) {
d7796 1
a7796 1
	} else if (c=='u' && strncmp(arg4, "units", argLen)==0) {
a7800 4
	} else {
	    Tcl_AppendResult(interp, "bad argument \"", arg4,
		    "\": must be units, pages or pixels", (char *) NULL);
	    return TKTEXT_SCROLL_ERROR;
d7803 1
a7803 2
    Tcl_AppendResult(interp, "unknown option \"", arg2,
	    "\": must be moveto or scroll", (char *) NULL);
d7806 8
@


1.52
log
@Remove unwanted tab from error info trace accidentally inserted during cleanup
Fixes [Bug 1246936]
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.51 2005/07/25 23:56:54 dkf Exp $
d103 1
d486 2
a487 2
			    TkTextTabArray *tabArrayPtr, int *indexPtr,
			    int x, int maxX));
d689 1
a689 1
    int overstrikePrio, tabPrio, wrapPrio;
d703 1
a703 1
    overstrikePrio = tabPrio = wrapPrio = -1;
d713 1
d822 5
d989 4
d1015 1
d1042 7
d1054 1
a1054 1
    if (elide && indexPtr->byteIndex==0) {
d1163 1
d1217 2
a1218 1
	    if ((elidesize = segPtr->size - byteOffset) > 0) {
d1286 1
a1286 1
	if (noCharsYet) {
d1288 1
d1292 14
a1305 3
	    x = ((curIndex.byteIndex == 0)
		    ? chunkPtr->stylePtr->sValuePtr->lMargin1
		    : chunkPtr->stylePtr->sValuePtr->lMargin2);
d1338 1
a1338 1
	if (elide && maxBytes) {
d1385 10
d1428 2
a1429 1
	    tabSize = SizeOfTab(textPtr, tabArrayPtr, &tabIndex, x, maxX);
d6635 1
a6635 1
 * TkTextCharBbox --
d6638 1
a6638 1
 *	that character.
d6641 3
a6643 3
 *	Zero is returned if the character is on the screen. -1 means the
 *	character isn't on the screen. If the return value is 0, then the
 *	bounding box of the part of the character that's visible on the screen
d6653 1
a6653 1
TkTextCharBbox(textPtr, indexPtr, xPtr, yPtr, widthPtr, heightPtr, charWidthPtr)
d6655 2
a6656 3
    CONST TkTextIndex *indexPtr;/* Index of character whose bounding box is
				 * desired. */
    int *xPtr, *yPtr;		/* Filled with character's upper-left
d6658 6
a6663 5
    int *widthPtr, *heightPtr;	/* Filled in with character's dimensions. */
    int *charWidthPtr;		/* If the 'character' isn't really a character
				 * (e.g. end of a line) and therefore takes up
				 * a very large width, this is used to return
				 * a smaller width */
d6733 13
a6745 2
    if ((*xPtr + *widthPtr) <= dInfoPtr->x) {
	return -1;
d7306 2
d7309 3
a7311 2
     * If no tab information has been given, do the usual thing: round up to
     * the next boundary of 8 average-sized characters.
a7313 1
    x = chunkPtr->nextPtr->x;
d7320 11
a7330 1
	desired = NextTabStop(textPtr->tkfont, x, 0);
d7469 1
a7469 1
SizeOfTab(textPtr, tabArrayPtr, indexPtr, x, maxX)
d7472 2
d7484 1
a7484 1
    int tabX, result, index, spaceWidth;
d7490 7
a7496 8
	tabX = NextTabStop(textPtr->tkfont, x, 0);

	/*
	 * We used up one tab stop.
	 */

	*indexPtr = index+1;
	return tabX - x;
d7498 1
a7498 1

d7505 6
a7510 1
	if (index < tabArrayPtr->numTabs) {
d7525 8
a7532 2
	 * If this tab stop is before the current x position, then we must
	 * obviously continue until we reach the text tab stop.
d7534 1
a7534 1
    } while (tabX < x);
d7588 2
a7589 1
 *	font should be used.
a7815 8

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */
@


1.51
log
@Getting more systematic about style
@
text
@d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.50 2005/05/13 13:46:12 vincentdarley Exp $
d6181 1
a6181 1
		    "\n	   (vertical scrolling command executed by text)");
@


1.50
log
@fix to two text widget bugs
@
text
@d1 1
a1 1
/* 
d4 4
a7 5
 *	This module provides facilities to display text widgets.  It is
 *	the only place where information is kept about the screen layout
 *	of text widgets.  (Well, strictly, each TkTextLine and B-tree
 *	node caches its last observed pixel height, but that information
 *	originates here).
d12 2
a13 2
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
d15 1
a15 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.49 2005/05/10 21:36:37 dgp Exp $
d29 1
a29 1
 * 
d31 30
a60 32
 * diverse chunks in a text widget containing megabytes of text, it is
 * not possible to recalculate all affected height information
 * immediately any such change takes place and maintain a responsive
 * user-experience.  Yet, for an accurate vertical scrollbar to be
 * drawn, we must know the total number of vertical pixels shown on
 * display versus the number available to be displayed.
 * 
 * The way the text widget solves this problem is by maintaining cached
 * line pixel heights (in the BTree for each logical line), and having
 * asynchronous timer callbacks (i) to iterate through the logical
 * lines recalculating their heights, and (ii) to recalculate the
 * vertical scrollbar's position and size.
 * 
 * Typically this works well but there are some situations where the
 * overall functional design of this file causes some problems.  These
 * problems can only arise because the calculations used to display
 * lines on screen are not connected to those in the iterating-line-
 * recalculation-process.
 * 
 * The reason for this disconnect is that the display calculations
 * operate in display lines, and the iteration and cache operates in
 * logical lines.  Given that the display calculations both need not
 * contain complete logical lines (at top or bottom of display), and
 * that they do not actually keep track of logical lines (for simplicity
 * of code and historical design), this means a line may be known and
 * drawn with a different pixel height to that which is cached in the
 * BTree, and this might cause some temporary undesirable mismatch
 * between display and the vertical scrollbar.
 * 
 * All such mismatches should be temporary, however, since the
 * asynchronous height calculations will always catch up eventually.
 * 
d65 3
a67 4
 * For details of the way in which the BTree keeps track of pixel
 * heights, see tkTextBTree.c.  Basically the BTree maintains two
 * pieces of information: the logical line indices and the pixel
 * height cache.
d72 3
a74 4
 * The information is generated by scanning all of the tags associated
 * with the characters and combining that with default information for
 * the overall widget.  These structures form the hash keys for
 * dInfoPtr->styleTable.
d82 2
a83 2
    Pixmap bgStipple;		/* Stipple bitmap for background.  None
				 * means draw solid. */
d87 1
a87 2
				 * foreground stuff.   None means draw
				 * solid.*/
d101 2
a102 2
    TkTextTabArray *tabArrayPtr;/* Locations and types of tab stops (may
				 * be NULL). */
d113 2
a114 2
 * graphics contexts used to actually draw the characters.  The entries
 * in dInfoPtr->styleTable point to structures of this type.
d120 2
a121 2
    GC bgGC;			/* Graphics context for background.  None
				 * means use widget background. */
d125 2
a126 2
    Tcl_HashEntry *hPtr;	/* Pointer to entry in styleTable.  Used
				 * to delete entry. */
d130 2
a131 3
 * The following macro determines whether two styles have the same
 * background so that, for example, no beveled border should be drawn
 * between them.
d136 3
a138 3
        && ((s1)->sValuePtr->borderWidth == (s2)->sValuePtr->borderWidth) \
        && ((s1)->sValuePtr->relief == (s2)->sValuePtr->relief) \
        && ((s1)->sValuePtr->bgStipple == (s2)->sValuePtr->bgStipple))
d141 4
a144 4
 * The following macro is used to compare two floating-point numbers
 * to within a certain degree of scale.  Direct comparison fails on
 * processors where the processor and memory representations of FP
 * numbers of a particular precision is different (e.g. Intel)
d151 2
a152 2
 * The following structure describes one line of the display, which may
 * be either part or all of one line of the text.
d156 2
a157 2
    TkTextIndex index;		/* Identifies first character in text
				 * that is displayed on this line. */
d159 14
a172 16
				 * display line, including a trailing space
				 * or newline that isn't actually displayed. */
    int logicalLinesMerged;     /* Number of extra logical lines merged 
                                 * into this one due to elided newlines. */
    int y;			/* Y-position at which line is supposed to
				 * be drawn (topmost pixel of rectangular
				 * area occupied by line). */
    int oldY;			/* Y-position at which line
				 * currently appears on display.  This
				 * is used to move lines by scrolling
				 * rather than re-drawing.  If 'flags'
				 * have the OLD_Y_INVALID bit set, then
				 * we will never examine this field
				 * (which means line isn't currently
				 * visible on display and must be
				 * redrawn).  */
d176 3
a178 4
    int spaceAbove;		/* How much extra space was added to the
				 * top of the line because of spacing
				 * options.  This is included in height
				 * and baseline. */
d181 1
a181 1
				 * options.  This is included in height. */
d183 11
a193 11
    TkTextDispChunk *chunkPtr;	/* Pointer to first chunk in list of all
				 * of those that are displayed on this
				 * line of the screen. */
    struct DLine *nextPtr;	/* Next in list of all display lines for
				 * this window.   The list is sorted in
				 * order from top to bottom.  Note:  the
				 * next DLine doesn't always correspond
				 * to the next line of text:  (a) can have
				 * multiple DLines for one text line, and
				 * (b) can have gaps where DLine's have been
				 * deleted because they're out of date. */
d200 4
a203 5
 * HAS_3D_BORDER -		Non-zero means that at least one of the
 *				chunks in this line has a 3D border, so
 *				it potentially interacts with 3D borders
 *				in neighboring lines (see
 *				DisplayLineBackground).
d205 3
a207 3
 *				re-layed out since the last time the
 *				display was updated.
 * TOP_LINE -			Non-zero means that this was the top line
d209 6
a214 6
 *				was laid out.  This is important because
 *				a line may be displayed differently if its
 *				at the top or bottom than if it's in the
 *				middle (e.g. beveled edges aren't displayed
 *				for middle lines if the adjacent line has
 *				a similar background).
d218 6
a223 7
 * OLD_Y_INVALID -              The value of oldY in the structure is
 *                              not valid or useful and should not be 
 *                              examined.  'oldY' is only useful when the
 *                              DLine is currently displayed at a
 *                              different position and we wish to
 *                              re-display it via scrolling, so this
 *                              means the DLine needs redrawing.
d237 8
a244 8
    Tcl_HashTable styleTable;	/* Hash table that maps from StyleValues
				 * to TextStyles for this widget. */
    DLine *dLinePtr;		/* First in list of all display lines for
				 * this widget, in order from top to bottom. */
    int topPixelOffset; 	/* Identifies first pixel in top display
				 * line to display in window. */
    int newTopPixelOffset; 	/* Desired first pixel in top display
				 * line to display in window. */
d259 2
a260 2
    int maxY;			/* First y-coordinate below available
				 * space for displaying lines. */
d264 1
a264 1
				 * line of the text.  This field is used to
d273 2
a274 2
				 * number of pixels off-screen to the left 
				 * for a line with no left margin. */
d279 2
a280 2
				 * size).  If there's no wrapping, this will
				 * be zero. */
d283 1
a283 1
				 * scrollbar;  used to eliminate unnecessary
d287 1
a287 1
				 * scrollbar;  used to eliminate unnecessary
d294 2
a295 2
    int scanMarkXPixel;		/* Pixel index of left edge of the window 
                       		 * when the scan started. */
d307 10
a316 10
				 * DLine structures;  if a redisplay
				 * is in progress, it will see this and
				 * abort the redisplay.  This is needed
				 * because, for example, an embedded window
				 * could change its size when it is first
				 * displayed, invalidating the DLine that
				 * is currently being displayed.  If redisplay
				 * continues, it will use freed memory and
				 * could dump core. */
    int flags;			/* Various flag values:  see below for
d319 2
a320 2
     * Information used to handle the asynchronous updating of the
     * y-scrollbar and the vertical height calculations:
d323 3
a325 3
    int lineMetricUpdateEpoch;  /* Stores a number which is incremented
				 * each time the text widget changes in
				 * a significant way (e.g. resizing or 
d327 25
a351 27
    int currentMetricUpdateLine;/* Stores a counter which is used to 
				 * iterate over the logical lines
				 * contained in the widget and update
				 * their geometry calculations, if they
				 * are out of date.  */
    TkTextIndex metricIndex;    /* If the current metric update line
                                 * wraps into very many display lines, then
                                 * this is used to keep track of what
                                 * index we've got to so far... */
    int metricPixelHeight;      /* ...and this is for the height
                                 * calculation so far...*/
    int metricEpoch;            /* ...and this for the epoch of the
                                 * partial calculation so it can be
                                 * cancelled if things change once more.
                                 * This field will be -1 if there is no
                                 * long-line calculation in progress, 
                                 * and take a non-negative value if 
                                 * there is such a calculation in 
                                 * progress. */
    int lastMetricUpdateLine;   /* When the current update line reaches
				 * this line, we are done and should
				 * stop the asychronous callback
				 * mechanism. */
    Tcl_TimerToken lineUpdateTimer; /* A token pointing to the current
				     * line metric update callback. */
    Tcl_TimerToken scrollbarTimer;  /* A token pointing to the current
				     * scrollbar update callback. */
d355 2
a356 2
 * In TkTextDispChunk structures for character segments, the clientData
 * field points to one of the following structures:
d361 3
a363 3
    char chars[4];		/* UTF characters to display.  Actual size
				 * will be numBytes, not 4.  THIS MUST BE
				 * THE LAST FIELD IN THE STRUCTURE. */
d369 3
a371 3
 * DINFO_OUT_OF_DATE:		Non-zero means that the DLine structures
 *				for this window are partially or completely
 *				out of date and need to be recomputed.
d376 5
a380 6
 * REPICK_NEEDED:		1 means that the widget has been modified
 *				in a way that could change the current
 *				character (a different character might be
 *				under the mouse cursor now).  Need to
 *				recompute the current character before
 *				the next redisplay.
d391 2
a392 2
 * DLINE_FREE:		Free the lines, but no need to unlink them
 *                      from the current list of actual display lines.
d394 2
a395 2
 * DLINE_FREE_TEMP:	Free, but don't unlink, and also don't
 *                      set 'dLinesInvalidated'.
d398 3
a400 3
#define DLINE_FREE        0
#define DLINE_UNLINK      1
#define DLINE_FREE_TEMP   2
d403 2
a404 2
 * The following counters keep statistics about redisplay that can be
 * checked to see how clever this code is at reducing redisplays.
d407 7
a413 6
static int numRedisplays;	    /* Number of calls to DisplayText. */
static int linesRedrawn;	    /* Number of calls to DisplayDLine. */
static int numCopies;		    /* Number of calls to XCopyArea
				     * to copy part of the screen.  */
static int lineHeightsRecalculated; /* Number of line layouts purely
                                     * for height calculation purposes.*/
d415 1
a415 1
 * Forward declarations for procedures defined later in this file:
d421 1
a421 1
static void		CharBboxProc _ANSI_ARGS_((TkText *textPtr, 
d426 1
a426 1
static void		CharDisplayProc _ANSI_ARGS_((TkText *textPtr, 
d436 7
a442 7
   Definitions of elided procs.
   Compiler can't inline these since we use pointers to these functions.
   ElideDisplayProc, ElideUndisplayProc special-cased for speed,
   as potentially many elided DLine chunks if large, tag toggle-filled
   elided region.
*/
static void		ElideBboxProc _ANSI_ARGS_((TkText *textPtr, 
d447 2
a448 2
static int		ElideMeasureProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
			    int x));
d467 1
a467 1
static int              GetYPixelCount _ANSI_ARGS_((TkText *textPtr, 
d485 1
a485 1
			    TkTextTabArray *tabArrayPtr, int *indexPtr, 
d488 1
a488 1
			    CONST TkTextIndex *index1Ptr, 
d495 1
a495 1
static void             TextInvalidateLineMetrics _ANSI_ARGS_((TkText *textPtr,
d497 2
a498 2
static int              CalculateDisplayLineHeight _ANSI_ARGS_((
			    TkText *textPtr, CONST TkTextIndex *indexPtr, 
d500 1
a500 1
static void             DlineIndexOfX _ANSI_ARGS_((TkText *textPtr, 
d502 1
a502 1
static int              DlineXOfIndex _ANSI_ARGS_((TkText *textPtr, 
d504 2
a505 2
static int              TextGetScrollInfoObj _ANSI_ARGS_((Tcl_Interp *interp, 
			    TkText *textPtr, int objc, 
d508 1
a508 1
static void	        AsyncUpdateLineMetrics _ANSI_ARGS_((ClientData 
d510 1
a510 1
static void	        AsyncUpdateYScrollbar _ANSI_ARGS_((ClientData 
d517 5
a521 5
#define TKTEXT_SCROLL_MOVETO    	1
#define TKTEXT_SCROLL_PAGES		2
#define TKTEXT_SCROLL_UNITS		3
#define TKTEXT_SCROLL_ERROR		4
#define TKTEXT_SCROLL_PIXELS		5
d529 2
a530 2
 *	This procedure is called when a new text widget is created.
 *	Its job is to set up display-related information for the widget.
d578 5
a582 2
    
    /* Add a refCount for each of the idle call-backs */
d584 1
a584 1
    dInfoPtr->lineUpdateTimer = Tcl_CreateTimerHandler(0, 
d587 1
a587 1
    dInfoPtr->scrollbarTimer = Tcl_CreateTimerHandler(200, 
d598 1
a598 1
 *	This procedure is called to free up all of the private display
d617 4
a620 4
     * Be careful to free up styleTable *after* freeing up all the
     * DLines, so that the hash table is still intact to free up the
     * style-related information from the lines.  Once the lines are
     * all free then styleTable will be empty.
d650 2
a651 2
 *	This procedure creates all the information needed to display
 *	text at a particular location.
d666 2
a667 2
    CONST TkTextIndex *indexPtr;/* The character in the text for which
				 * display information is wanted. */
d691 3
a693 4
     * Find out what tags are present for the character, then compute
     * a StyleValues structure corresponding to those tags (scan
     * through all of the tags, saving information for the highest-
     * priority tag).
d714 1
d717 1
d720 1
a720 1
	
d722 3
a724 3
	 * If this is the selection tag, and inactiveSelBorder is NULL
	 * (the default on Windows and Mac), then we need to skip it if
	 * we don't have focus.
d729 1
a729 1
	        continue;
d738 2
a739 2
	if ((tagPtr->borderWidthPtr != NULL) 
	        && (Tcl_GetString(tagPtr->borderWidthPtr)[0] != '\0')
d853 1
a853 1
     * No existing style matched.  Make a new one.
d892 3
a894 3
 *	This procedure is called when a TextStyle structure is no longer
 *	needed.  It decrements the reference count and frees up the
 *	space for the style structure if the reference count is 0.
d900 2
a901 2
 *	The storage and other resources associated with the style
 *	are freed up if no-one's still using it.
d930 3
a932 3
 *	This procedure generates a single DLine structure for a display
 *	line whose leftmost character is given by indexPtr.
 *	
d935 2
a936 2
 *	display line.  All fields are filled in and correct except for
 *	y and nextPtr.
d940 15
a954 16
 *	
 *	See the comments in 'GetYView' for some thoughts on what the
 *	side-effects of this call (or its callers) should be -- the
 *	synchronisation of TkTextLine->pixelHeight with the sum of 
 *	the results of this function operating on all display lines
 *	within each logical line.  Ideally the code should be
 *	refactored to ensure the cached pixel height is never behind
 *	what is known when this function is called elsewhere.
 *	
 *	Unfortunately, this function is currently called from many
 *	different places, not just to layout a display line for actual
 *	display, but also simply to calculate some metric or other of one
 *	or more display lines (typically the height).  It would be a good
 *	idea to do some profiling of typical text widget usage and the
 *	way in which this is called and see if some optimization could or
 *	should be done.
d962 3
a964 2
    CONST TkTextIndex *indexPtr;/* Beginning of display line.  May not
				 * necessarily point to a character segment. */
d968 2
a969 2
    TkTextDispChunk *lastChunkPtr;	/* Last chunk allocated so far
					 * for line. */
d980 2
a981 1
					 * have been placed on the line yet. */
d989 2
a990 2
    int x = 0, maxX = 0;		/* Initializations needed only to
					 * stop compiler warnings. */
d992 2
a993 1
					 * runs to the end of the text line. */
d997 4
a1000 4
    TkTextDispChunk *tabChunkPtr;	/* Pointer to the chunk containing
					 * the previous tab stop. */
    int maxBytes;			/* Maximum number of bytes to
					 * include in this chunk. */
d1004 2
a1005 2
    int tabSize;			/* Number of pixels consumed by current
					 * tab stop. */
d1007 3
a1009 3
					 * lines with numBytes > 0.  Used to
					 * drop 0-sized chunks from the end
					 * of the line. */
d1012 2
a1013 2
    TkTextElideInfo info;               /* Keep track of elide state */
    
d1029 1
a1029 1
    
d1033 1
d1042 2
a1043 2
		     * visible, and we have something of non-zero 
		     * size.  Therefore we must bail out.
d1045 1
d1049 3
a1051 3
		/* 
		 * Reset tag elide priority, since we're on a new
		 * character.
d1053 1
d1055 1
a1055 1
		       || (segPtr->typePtr == &tkTextToggleOnType)) {
d1057 1
d1059 3
a1061 4
		 * The elide state only changes if this tag is
		 * either the current highest priority tag
		 * (and is therefore being toggled off), or it's
		 * a new tag with higher priority.
d1063 1
d1071 4
a1074 5
			    /* 
			     * If it is being toggled off, and it has
			     * an elide string, it must actually be the
			     * current highest priority tag, so this
			     * check is redundant:
d1076 1
d1080 4
a1083 4
			    
			    /* 
			     * Find previous elide tag, if any (if not
			     * then elide will be zero, of course).
d1085 1
d1090 1
a1090 1
								     ->elide;
d1107 4
a1110 5
		/* 
		 * Elided state goes from beginning to end of 
		 * an entire logical line.  This means we can
		 * update the line's pixel height, and bring
		 * its pixel calculation up to date.
d1112 1
d1114 1
a1114 1
		  = textPtr->dInfoPtr->lineMetricUpdateEpoch;
d1116 3
a1118 3
		if (TkBTreeLinePixelCount(textPtr, dlPtr->index.linePtr) != 0) {
		    TkBTreeAdjustPixelHeight(textPtr, 
					     dlPtr->index.linePtr, 0, 0);
d1126 1
a1126 1
    
d1128 3
a1130 4
     * Each iteration of the loop below creates one TkTextDispChunk for
     * the new display line.  The line will always have at least one
     * chunk (for the newline character at the end, if there's nothing
     * else available).
d1148 1
a1148 1
    
d1150 3
a1152 3
     * Find the first segment to consider for the line.  Can't call
     * TkTextIndexToSeg for this because it won't return a segment
     * with zero size (such as the insertion cursor's mark).
d1161 1
a1161 1
	
d1163 3
a1165 3
	    /* 
	     * Two logical lines merged into one display line 
	     * through eliding of a newline
d1167 1
d1185 1
a1185 1
	 * 
d1187 6
a1192 6
	 * 
	 * This also means that each logical line which is entirely
	 * elided still gets laid out into a DLine, but with zero
	 * height.  This isn't particularly a problem, but it does seem
	 * somewhat unnecessary.  We may wish to redesign the code to
	 * remove these zero height DLines in the future.
d1194 1
d1200 3
a1202 2
		breakByteOffset = lastChunkPtr->breakIndex 
		  = lastChunkPtr->numBytes;
d1204 2
a1205 2
		 * If have we have a tag toggle, there is a chance
		 * that invisibility state changed, so bail out
d1217 1
a1217 1
	    
d1219 3
a1221 3
		/* 
		 * Two logical lines merged into one display line 
		 * through eliding of a newline
d1223 1
d1225 1
d1233 5
a1237 2
	    /* Code no longer needed, now that we allow logical lines
	     * to merge into a single display line.
d1241 3
a1243 1
	    }*/
d1260 2
a1261 2
	 * Save style information such as justification and indentation,
	 * up until the first character is encountered, then retain that
d1285 2
a1286 2
	 * See if there is a tab in the current chunk; if so, only
	 * layout characters up to (and including) the tab.
d1306 3
a1308 3
	    /* 
	     * Don't free style here, as other code expects to be able to
	     * do that.
d1310 2
a1311 1
	    /* breakByteOffset =*/ 
d1314 2
a1315 1
	    chunkPtr->minAscent = chunkPtr->minDescent = chunkPtr->minHeight = 0;
d1326 2
a1327 2
		byteOffset, maxX-tabSize, maxBytes, noCharsYet, wrapMode,
		chunkPtr);
d1343 2
a1344 2
	     * No characters from this segment fit in the window: this
	     * means we're at the end of the display line.
d1374 3
a1376 3
	 * pertaining to the previous tab.  Also adjust the amount of
	 * space left in the line to account for space that will be eaten
	 * up by the tab.
d1396 1
a1396 1
		/* 
d1398 3
a1400 3
		 * until the newline.  Hence the newline is actually
		 * elided, and we want to merge the display of the next
		 * logical line with this one.
d1402 1
d1420 7
a1426 7
	/* 
	 * We used to Tcl_Panic here, saying that LayoutDLine couldn't
	 * place any characters on a line, but I believe a more
	 * appropriate response is to return a DLine with zero height.
	 * With elided lines, tag transitions and asynchronous line
	 * height calculations, it is hard to avoid this situation ever
	 * arising with the current code design.
d1428 1
d1434 3
a1436 3
     * We're at the end of the display line.  Throw away everything
     * after the most recent word break, if there is one;  this may
     * potentially require the last chunk to be layed out again.
d1441 4
a1444 5
	 * This code makes sure that we don't accidentally display
	 * chunks with no characters at the end of the line (such as
	 * the insertion cursor).  These chunks belong on the next
	 * line.  So, throw away everything after the last chunk that
	 * has characters in it.
d1466 1
a1466 1
		    segPtr, byteOffset, maxX, breakByteOffset, 0, 
d1483 9
a1491 10
     * Make one more pass over the line to recompute various things
     * like its height, length, and total number of bytes.  Also
     * modify the x-locations of chunks to reflect justification.
     * If we're not wrapping, I'm not sure what is the best way to
     * handle left and center justification:  should the total length,
     * for purposes of justification, be (a) the window width, (b)
     * the length of the longest line in the window, or (c) the length
     * of the longest line in the text?  (c) isn't available, (b) seems
     * weird, since it can change with vertical scrolling, so (a) is
     * what is implemented below.
d1550 1
a1550 1
    
d1559 10
a1568 11
 *	This procedure is invoked to recompute some or all of the
 *	DLine structures for a text widget.  At the time it is called
 *	the DLine structures still left in the widget are guaranteed
 *	to be correct except that (a) the y-coordinates aren't
 *	necessarily correct, (b) there may be missing structures
 *	(the DLine structures get removed as soon as they are potentially
 *	out-of-date), and (c) DLine structures that don't start at the
 *	beginning of a line may be incorrect if previous information in
 *	the same line changed size in a way that moved a line boundary
 *	(DLines for any info that changed will have been deleted, but
 *	not DLines for unchanged info in the same text line).
d1574 3
a1576 3
 *	Upon return, the DLine information for textPtr correctly reflects
 *	the positions where characters will be displayed.  However, this
 *	procedure doesn't actually bring the display up-to-date.
d1613 2
a1614 2
     * Scan through the contents of the window from top to bottom,
     * recomputing information for lines that are missing.
d1618 1
a1618 1
    lastLinePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree, textPtr, 
d1635 7
a1641 9
	 * (b) the next DLine corresponds to a different line, or to
	 *     a segment that will be coming later in the same line:
	 *     leave this DLine alone in the hopes that we'll be able
	 *     to use it later, then create a new DLine in front of
	 *     it.
	 * (c) the next DLine corresponds to a segment in the line we
	 *     want, but it's a segment that has already been processed
	 *     or will never be processed.  Delete the DLine and try
	 *     again.
d1643 6
a1648 6
	 * One other twist on all this.  It's possible for 3D borders
	 * to interact between lines (see DisplayLineBackground) so if
	 * a line is relayed out and has styles with 3D borders, its
	 * neighbors have to be redrawn if they have 3D borders too,
	 * since the interactions could have changed (the neighbors
	 * don't have to be relayed out, just redrawn).
d1652 1
a1652 1
	    
d1657 1
a1657 1
	    makeNewDLine:
d1662 2
a1663 3
		 * Debugging is enabled, so keep a log of all the lines
		 * that were re-layed out.  The test suite uses this
		 * information.
d1684 2
a1685 2
	     * DlPtr refers to the line we want.  Next check the
	     * index within the line.
d1690 1
a1690 1
		 * Case (a) -- can use existing display line as-is.
d1704 2
a1705 2
	     * Case (c) -- dlPtr is useless.  Discard it and start
	     * again with the next display line.
d1723 1
a1723 1
	lineOK:
d1734 2
a1735 2
	 * If we switched text lines, delete any DLines left for the
	 * old text line.
d1751 12
a1762 14
	    
	    if ((lineHeight != -1) 
	      && (lineHeight != TkBTreeLinePixelCount(textPtr, 
						     prevPtr->index.linePtr))) {
		/* 
		 * The logical line height we just calculated is
		 * actually differnt to the currently cached height of
		 * the text line.  That is fine (the text line heights
		 * are only calculated asynchronously), but we must
		 * update the cached height so that any counts made with
		 * DLine pointers are the same as counts made through the
		 * BTree.  This helps to ensure that the scrollbar size
		 * corresponds accurately to that displayed contents,
		 * even as the window is re-sized.
d1764 5
a1768 4
		TkBTreeAdjustPixelHeight(textPtr, 
					 prevPtr->index.linePtr, 
					 lineHeight, 0);
		/* 
d1770 4
a1773 5
		 * beginning of the logical line, so we can also adjust
		 * the 'pixelCalculationEpoch' to mark it as being up to
		 * date.  There is a slight concern that we might not
		 * have got this right for the first line in the
		 * re-display.
d1775 1
d1783 4
a1786 4
	 * It's important to have the following check here rather than in
	 * the while statement for the loop, so that there's always at least
	 * one DLine generated, regardless of how small the window is.  This
	 * keeps a lot of other code from breaking.
d1802 7
a1808 7
     * If there is extra space at the bottom of the window (because
     * we've hit the end of the text), then bring in more lines at
     * the top of the window, if there are any, to fill in the view.
     * 
     * Since the top line may only be partially visible, we try first
     * to simply show more pixels from that line (newTopPixelOffset).
     * If that isn't enough, we have to layout more lines.
d1814 3
a1816 3
	 * This counts how many vertical pixels we have left to fill
	 * by pulling in more display pixels either from the first
	 * currently displayed, or the lines above it.
d1818 1
d1820 1
a1820 1
	
d1822 3
a1824 3
	    /* 
	     * We can full up all the needed space just by showing
	     * more of the current top line
d1826 1
d1833 5
a1837 5
    
	    /* 
	     * Add in all of the current top line, which won't
	     * be enough to bring y up to maxY (if it was we
	     * would be in the 'if' block above).
d1839 1
d1844 4
a1847 5
	     * Layout an entire text line (potentially > 1 display line),
	     * then link in as many display lines as fit without moving
	     * the bottom line out of the window.  Repeat this until
	     * all the extra space has been used up or we've reached the
	     * beginning of the text.
d1852 3
a1854 3
		/* 
		 * No lines have been laid out.  This must be an empty
		 * peer widget.
d1856 1
d1860 2
a1861 1
		lineNum = TkBTreeLinesTo(textPtr, dInfoPtr->dLinePtr->index.linePtr);
d1870 3
a1872 3
		
		index.linePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree, 
						textPtr, lineNum);
d1881 2
a1882 2
		    if (dlPtr->length == 0 && dlPtr->height == 0) { 
			bytesToCount--; break; 
d1884 2
a1885 1
		    TkTextIndexForwBytes(textPtr, &index, dlPtr->byteCount, &index);
d1889 5
a1893 5
		
		/* 
		 * We may not have examined the entire line (depending
		 * on the value of 'bytesToCount', so we only want
		 * to set this if it is genuinely bigger).  
d1895 5
a1899 5
		if (pixelHeight > TkBTreeLinePixelCount(textPtr, 
							lowestPtr->index.linePtr)) {
		    TkBTreeAdjustPixelHeight(textPtr, 
					     lowestPtr->index.linePtr, 
					     pixelHeight, 0);
d1901 3
a1903 3
			/* 
			 * We examined the entire line, so can update
			 * the epoch.
d1905 3
a1907 1
			TkBTreeLinePixelEpoch(textPtr, lowestPtr->index.linePtr) =
d1938 1
d1940 3
a1942 4
	     * We've either filled in the space we wanted to or we've
	     * run out of display lines at the top of the text.  Note
	     * that we already set dInfoPtr->newTopPixelOffset to zero
	     * above.
d1944 1
d1946 5
a1950 5
		/* 
		 * We've laid out a few too many vertical pixels at or
		 * above the first line.  Therefore we only want to show
		 * part of the first displayed line, so that the last
		 * displayed line just fits in the window.
d1952 1
d1954 5
a1958 6
		if (dInfoPtr->newTopPixelOffset >= dInfoPtr->dLinePtr->height) {
		    /* 
		     * Somehow the entire first line we laid out is
		     * shorter than the new offset.  This should not
		     * occur and would indicate a bad problem in the
		     * logic above.
d1960 1
d1967 2
a1968 3
	 * Now we're all done except that the y-coordinates in all the
	 * DLines are wrong and the top index for the text is wrong.
	 * Update them.
d1980 1
a1980 1
		y += dlPtr->height; 
d1987 6
a1992 7
     * If the old top or bottom line has scrolled elsewhere on the
     * screen, we may not be able to re-use its old contents by
     * copying bits (e.g., a beveled edge that was drawn when it was
     * at the top or bottom won't be drawn when the line is in the
     * middle and its neighbor has a matching background).  Similarly,
     * if the new top or bottom line came from somewhere else on the
     * screen, we may not be able to copy the old bits.
d2006 1
d2009 2
a2010 2
	     * pixelOffset is non-zero) and is no longer the top-line, then
	     * we must re-draw it.
d2012 3
a2014 2
	    if ((dlPtr->flags & TOP_LINE) 
		    && dInfoPtr->topPixelOffset!=0 && dlPtr!=dInfoPtr->dLinePtr) {
d2036 1
a2036 1
    
d2046 5
a2050 6
     * 1. If there's empty space to the right of the longest line,
     *    shift the screen to the right to fill in the empty space.
     * 2. If the desired horizontal scroll position has changed,
     *    force a full redisplay of all the lines in the widget.
     * 3. If the wrap mode isn't "none" then re-scroll to the base
     *    position.
d2070 2
a2071 2
    
    /* 
d2073 6
a2078 6
     * 
     * If the widget is being created, but has not yet been configured
     * it will have a maxY of 1 above, and we we won't have examined
     * all the lines (just the first line, in fact), and so maxOffset
     * will not be a true reflection of the widget's lines.  Therefore
     * we must not overwrite the original newXPixelOffset in this case.
d2080 1
d2084 1
a2084 1
    
d2099 2
a2100 2
 *	This procedure is called to free up all of the resources
 *	associated with one or more DLine structures.
d2113 1
a2113 2
    TkText *textPtr;		/* Information about overall text
				 * widget. */
d2115 10
a2124 13
    DLine *lastPtr;		/* Pointer to DLine just after last
				 * one to free (NULL means everything
				 * starting with firstPtr). */
    int action;                 /* DLINE_UNLINK means DLines are
				 * currently linked into the list
				 * rooted at
				 * textPtr->dInfoPtr->dLinePtr and
				 * they have to be unlinked.
				 * DLINE_FREE means just free without
				 * unlinking.  DLINE_FREE_TEMP means
				 * the DLine given is just a
				 * temporary one and we shouldn't
				 * invalidate anything for the
d2136 2
a2137 3
	     * Debugging is enabled, so keep a log of all the lines
	     * whose height was recalculated.  The test suite uses this
	     * information.
d2171 1
a2171 1
        textPtr->dInfoPtr->dLinesInvalidated = 1;
d2180 1
a2180 2
 *	This procedure is invoked to draw a single line on the
 *	screen.
d2186 2
a2187 3
 *	The line given by dlPtr is drawn at its correct position in
 *	textPtr's window.  Note that this is one *display* line, not
 *	one *text* line.
d2196 5
a2200 5
    DLine *prevPtr;		/* Line just before one to draw, or NULL
				 * if dlPtr is the top line. */
    Pixmap pixmap;		/* Pixmap to use for double-buffering.
				 * Caller must make sure it's large enough
				 * to hold line. */
d2210 2
a2211 2
     * First, clear the area of the line to the background color for the
     * text widget.
d2225 4
a2228 5
     * Make another pass through all of the chunks to redraw the
     * insertion cursor, if it is visible on this line.  Must do
     * it here rather than in the foreground pass below because
     * otherwise a wide insertion cursor will obscure the character
     * to its left.
d2246 3
a2248 3
     * foreground information.  Note:  we have to call the displayProc
     * even for chunks that are off-screen.  This is needed, for
     * example, so that embedded windows can be unmapped in this case.
d2255 2
a2256 2
	     * Already displayed the insertion cursor above.  Don't
	     * do it again here.
d2261 4
a2264 4
	/* 
	 * Don't call if elide.  This tax ok since not very many
	 * visible DLine's in an area, but potentially many elide
	 * ones
d2266 1
d2272 7
a2278 7
		 * Note:  we have to call the displayProc even for chunks
		 * that are off-screen.  This is needed, for example, so
		 * that embedded windows can be unmapped in this case.
		 * Display the chunk at a coordinate that can be clearly
		 * identified by the displayProc as being off-screen to
		 * the left (the displayProc may not be able to tell if
		 * something is off to the right).
d2280 1
d2288 1
a2288 1
	
d2295 6
a2300 6
     * Copy the pixmap onto the screen.  If this is the first or last
     * line on the screen then copy a piece of the line, so that it
     * doesn't overflow into the border area.  Another special trick:
     * copy the padding area to the left of the line; this is because the
     * insertion cursor sometimes overflows onto that area and we want to
     * get as much of the cursor as possible.
d2324 4
a2327 5
 *	This procedure is called to fill in the background for
 *	a display line.  It draws 3D borders cleverly so that
 *	adjacent chunks with the same style (whether on the same
 *	line or different lines) have a single 3D border around
 *	the whole region.
d2330 1
a2330 1
 *	There is no return value.  Pixmap is filled in with background
d2343 6
a2348 7
    DLine *prevPtr;		/* Line just above dlPtr, or NULL if dlPtr
				 * is the top-most line in the window. */
    Pixmap pixmap;		/* Pixmap to use for double-buffering.
				 * Caller must make sure it's large enough
				 * to hold line.  Caller must also have
				 * filled it with the background color for
				 * the widget. */
d2351 5
a2355 5
    TkTextDispChunk *chunkPtr;  /* Pointer to chunk in the current line. */
    TkTextDispChunk *chunkPtr2;	/* Pointer to chunk in the line above or
				 * below the current one.  NULL if we're to
				 * the left of or to the right of the chunks
				 * in the line. */
d2358 4
a2361 4
				 * where chunkPtr2 is NULL because the line
				 * is indented). */
    int leftX;			/* The left edge of the region we're
				 * currently working on. */
d2363 2
a2364 2
				 * as it goes down, 0 means it slopes left
				 * as it goes down. */
d2367 5
a2371 5
    int matchLeft;		/* Does the style of this line match that
				 * of its neighbor just to the left of
				 * the current x coordinate? */
    int matchRight;		/* Does line's style match its neighbor
				 * just to the right of the current x-coord? */
d2378 3
a2380 4
     * Pass 1: scan through dlPtr from left to right.  For each range of
     * chunks with the same style, draw the main background for the style
     * plus the vertical parts of the 3D borders (the left and right
     * edges).
d2390 11
a2400 13
     * Note A: in the following statement, and a few others later in
     * this file marked with "See Note A above", the right side of the
     * assignment was replaced with 0 on 6/18/97.  This has the effect
     * of highlighting the empty space to the left of a line whenever
     * the leftmost character of the line is highlighted.  This way,
     * multi-line highlights always line up along their left edges. 
     * However, this may look funny in the case where a single word is
     * highlighted. To undo the change, replace "leftX = 0" with "leftX
     * = chunkPtr->x" and "rightX2 = 0" with "rightX2 = nextPtr2->x"
     * here and at all the marked points below.  This restores the old
     * behavior where empty space to the left of a line is not
     * highlighted, leaving a ragged left edge for multi-line
     * highlights.
d2416 4
a2419 1
	    /* Not visible - bail out now */
d2421 1
a2421 1
	        leftX = rightX;
d2431 1
d2433 1
a2433 1
	        leftX = -sValuePtr->borderWidth - xOffset;
d2436 1
a2436 1
	        rightX = leftX + 32767;
d2456 3
a2458 3
     * Pass 2: draw the horizontal bevels along the top of the line.  To
     * do this, scan through dlPtr from left to right while simultaneously
     * scanning through the line just above dlPtr.  ChunkPtr2 and nextPtr2
d2499 2
a2500 2
	     * The chunk in our line is about to end.  If its style
	     * changes then draw the bevel for the current style.
d2516 2
a2517 3
		 * If the chunk in the line above is also ending at
		 * the same point then advance to the next chunk in
		 * that line.
d2536 4
a2539 5
	 * The chunk in the line above is ending at an x-position where
	 * there is no change in the style of the current line.  If the
	 * style above matches the current line on one side of the change
	 * but not on the other, we have to draw an L-shaped piece of
	 * bevel.
d2559 1
a2559 1
		    leftX + xOffset, 0, rightX2 + sValuePtr->borderWidth -leftX,
d2564 1
a2564 1
	nextChunk2:
d2576 1
d2578 2
a2579 2
     * Pass 3: draw the horizontal bevels along the bottom of the line.
     * This uses the same approach as pass 2.
d2669 1
a2669 1
	nextChunk2b:
d2688 9
a2696 10
 *	This procedure is invoked as a background handler to update the
 *	pixel-height calculations of individual lines in an
 *	asychronous manner.
 *	
 *	Currently a timer-handler is used for this purpose, which
 *	continuously reschedules itself.  It may well be better to
 *	use some other approach (e.g. a background thread).  We can't
 *	use an idle-callback because of a known bug in Tcl/Tk in
 *	which idle callbacks are not allowed to re-schedule
 *	themselves.  This just causes an effective infinite loop.
d2714 1
a2714 1
    
a2717 1

d2719 1
a2719 1
	 * The widget has been deleted.  Don't do anything.
d2729 1
a2729 1
	dInfoPtr->lineUpdateTimer = Tcl_CreateTimerHandler(1, 
d2738 1
d2740 2
a2741 2
     * Update the lines in blocks of about 24 recalculations,
     * or 250+ lines examined, so we pass in 256 for 'doThisMuch'.
d2743 2
a2744 1
    lineNum = TkTextUpdateLineMetrics(textPtr, lineNum, 
d2751 1
a2751 1
	Tcl_SetVar2(textPtr->interp, "tk_textInvalidateLine", (char *) NULL, 
d2754 12
a2765 12
    
    /* 
     * If we're not in the middle of a long-line calculation 
     * (metricEpoch == -1) and we've reached the last line, then
     * we're done.
     */
    if (dInfoPtr->metricEpoch == -1 
      && lineNum == dInfoPtr->lastMetricUpdateLine) {
	/*
	 * We have looped over all lines, so we're done.  We must
	 * release our refCount on the widget (the timer token
	 * was already set to NULL above).
d2767 1
d2776 3
a2778 3
    /* 
     * Re-arm the timer.  We already have a refCount on the text widget
     * so no need to adjust that.
d2780 2
a2781 1
    dInfoPtr->lineUpdateTimer = Tcl_CreateTimerHandler(1, 
d2790 15
a2804 17
 *	This procedure updates the pixel height calculations of a
 *	range of lines in the widget.  The range is from lineNum to
 *	endLine, but, if doThisMuch is positive, then the procedure
 *	may return earlier, once a certain number of lines has been
 *	examined.  The line counts are from 0.
 *	
 *	If doThisMuch is -1, then all lines in the range will be
 *	updated.  This will potentially take quite some time for
 *	a large text widget.
 *	
 *	Note: with bad input for lineNum and endLine, this procedure can
 *	loop indefinitely.
 *
 * Results:
 *	The index of the last line examined (or -1 if we are about to
 *	wrap around from end to beginning of the widget, and the next
 *	line will be the first line).
d2815 5
a2819 6
    int lineNum;        /* Start at this line */
    int endLine;        /* Go no further than this line */
    int doThisMuch;     /* How many lines to check, or how many 10s of
                         * lines to recalculate.  If '-1' then do
                         * everything in the range (which may take a
                         * while). */
d2824 1
a2824 1
    
d2827 1
a2827 1
        return endLine;
d2829 1
a2829 1
    
d2831 4
a2834 1
	/* Get a suitable line */
d2837 2
a2838 2
	    linePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree, 
				      textPtr, lineNum);
d2844 2
a2845 2
		linePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree, 
					  textPtr, lineNum);
d2850 2
a2851 1
	    /* 
d2855 1
d2857 1
a2857 1
		/* 
d2859 2
a2860 1
		 */ 
d2869 1
a2869 1
		sprintf(buffer, "%d %d %d %d", lineNum, endLine, 
d2871 3
a2873 3
		Tcl_SetVar2(textPtr->interp, "tk_textInvalidateLine", 
			    (char *) NULL, buffer, 
			    TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
d2875 5
a2879 2
	    
	    /* Now update the line's metrics if necessary */
d2883 2
a2884 2
		    count += 8 * TkTextUpdateOneLine(textPtr, linePtr, 
						     0, NULL, 0);
d2889 7
a2895 8
		    
		    /* 
		     * If the metric epoch is the same as the widget's 
		     * epoch, then we know that indexPtrs are still
		     * valid, and if the cached metricIndex (if any) is
		     * for the same line as we wish to examine, then
		     * we are looking at a long line wrapped many
		     * times, which we will examine in pieces.
d2897 4
a2900 3
		    if (textPtr->dInfoPtr->metricEpoch == 
			                textPtr->sharedTextPtr->stateEpoch 
		      && textPtr->dInfoPtr->metricIndex.linePtr == linePtr) {
d2904 4
a2907 4
			/* 
			 * We must reset the partial line height
			 * calculation data here, so we don't use
			 * it when it is out of date.
d2909 1
d2918 4
a2921 3
		    /* 
		     * Update the line and update the counter, counting
		     * 8 for each display line we actually re-layout.
d2923 4
a2926 3
		    count += 8 * TkTextUpdateOneLine(textPtr, linePtr, 
						     pixelHeight, indexPtr, 1);
		    
d2928 1
a2928 1
			/* 
d2930 3
a2932 3
			 * produced very many display lines -- it must be a
			 * long line wrapped many times.  So we must
			 * cache as far as we got for next time around.
d2934 1
d2937 2
a2938 2
			     * These have already been stored, unless 
			     * we just started the new line
d2940 1
d2942 2
a2943 2
			    textPtr->dInfoPtr->metricEpoch = 
			              textPtr->sharedTextPtr->stateEpoch;
d2945 2
a2946 2
			textPtr->dInfoPtr->metricPixelHeight = 
			              TkBTreeLinePixelCount(textPtr, linePtr);
d2949 4
a2952 1
			/* We're done with this long line */
d2957 3
a2959 3
		/* 
		 * This line is already up to date.   That means there's
		 * nothing to do here.
d2963 4
a2966 4
	    /* 
	     * We must never recalculate the height of the
	     * last artificial line.  It must stay at zero, and
	     * if we recalculate it, it will change.
d2968 1
a2968 1
	    
d2973 5
a2977 1
	    /* Set things up for the next loop through */
d2981 1
a2981 1
	
d2987 3
a2989 3
	/* 
	 * If we were requested to provide a full update,
	 * then also update the scrollbar.
d2991 1
d3003 7
a3009 8
 *	calculations.  Never call this with linePtr as the last
 *	(artificial) line in the text.  Depending on 'action' which
 *	indicates whether the given lines are simply invalid or have
 *	been inserted or deleted, the pre-existing asynchronous line
 *	update range may need to be adjusted.
 *	
 *	If linePtr is NULL then 'lineCount' and 'action' are ignored and
 *	all lines are invalidated.
d3015 1
a3015 1
 *	May schedule an asychronous callback. 
d3022 2
a3023 2
    TkSharedText *sharedTextPtr;/* Shared widget section for all peers, 
                                 * or NULL. */
d3025 4
a3028 6
    TkTextLine *linePtr;        /* Invalidation starts from this line. */
    int lineCount;              /* And includes this many following 
                                 * lines. */
    int action;                 /* Indicates what type of invalidation
                                 * occurred (insert, delete, or
                                 * simple). */ 
d3044 4
a3047 6
    TkTextLine *linePtr;        /* Invalidation starts from this line. */
    int lineCount;              /* And includes this many following 
				 * lines. */
    int action;                 /* Indicates what type of invalidation
				 * occurred (insert, delete, or
				 * simple). */ 
d3054 1
a3054 1
	
d3057 2
a3058 3
	/* 
	 * Invalidate the height calculations of each line in the
	 * given range.
d3060 1
d3069 9
a3077 9
	/* 
	 * Now schedule an examination of each line in the union of the
	 * old and new update ranges, including the (possibly empty)
	 * range in between.  If that between range is not-empty, then we
	 * are examining more lines than is strictly necessary (but the
	 * examination of the extra lines should be quick, since their
	 * pixelCalculationEpoch will be up to date).  However, to keep
	 * track of that would require more complex record-keeping that
	 * what we have.
d3079 1
d3088 1
a3088 1
	    
d3138 1
d3142 4
a3145 3
	/* 
	 * This has the effect of forcing an entire new loop
	 * of update checks on all lines in the widget.
d3147 1
d3153 3
a3155 3
    
    /* 
     * Now re-set the current update calculations 
d3157 1
d3160 1
a3160 1
	dInfoPtr->lineUpdateTimer = Tcl_CreateTimerHandler(1, 
d3170 15
a3184 15
 *	This procedure is invoked to find the index of the beginning or
 *	end of the particular display line on which the given index sits,
 *	whether that line is displayed or not.
 *	
 *	If 'end' is zero, we look for the start, and if 'end' is one 
 *	we look for the end.
 *	
 *	If the beginning of the current display line is elided, and we
 *	are looking for the start of the line, then the returned index
 *	will be the first elided index on the display line. 
 *	
 *	Similarly if the end of the current display line is elided
 *	and we are looking for the end, then the returned index will
 *	be the last elided index on the display line.
 *	
d3187 3
a3189 3
 *	
 *	If xOffset is non-NULL, it is set to the x-pixel offset of the
 *	given original index within the given display line.
d3192 5
a3196 6
 *	The combination of 'LayoutDLine' and 'FreeDLines' seems
 *	like a rather time-consuming way of gathering the information
 *	we need, so this would be a good place to look to speed up
 *	the calculations.  In particular these calls will map and
 *	unmap embedded windows respectively, which I would hope isn't
 *	exactly necessary!
d3203 7
a3209 7
    TkText *textPtr;         /* Widget record for text widget. */
    TkTextIndex *indexPtr;   /* Index we will adjust to the display
                              * line start or end. */
    int end;                 /* 0 = start, 1 = end. */
    int *xOffset;            /* NULL, or used to store the x-pixel
                              * offset of the original index within
                              * its display line. */
d3212 4
a3215 1
	/* Nothing to do */
d3219 1
a3219 1
        return;
d3222 1
d3225 1
a3225 1
	
d3228 1
a3228 1
	
d3230 4
a3233 1
		/* Reached beginning of text */
d3239 2
a3240 3
		 * The eol is not elided, so 'index' points to
		 * the start of a display line (as well as logical
		 * line).
d3242 1
d3245 5
a3249 4
	    /* 
	     * indexPtr's logical line is actually merged with
	     * the previous logical line whose eol is elided.
	     * Continue searching back to get a real line start.
d3251 1
d3260 1
a3260 1
	    
d3263 1
a3263 1
	    
d3265 12
a3276 11
	    
	    /* 
	     * 'byteCount' goes up to the beginning of the next display
	     * line, so equality here says we need one more line.  We
	     * try to perform a quick comparison which is valid for 
	     * the case where the logical line is the same, but 
	     * otherwise fall back on a full TkTextIndexCmp.
	     */
	    if (((index.linePtr == indexPtr->linePtr) 
		 && (index.byteIndex + byteCount > indexPtr->byteIndex))
		|| (dlPtr->logicalLinesMerged > 0 
d3278 4
a3281 1
		/* It's on this display line */
d3283 5
a3287 5
		    /* 
		     * This call takes a byte index relative to the
		     * start of the current _display_ line, not
		     * logical line.  We are about to overwrite
		     * indexPtr->byteIndex, so we must do this now.
d3289 3
a3291 2
		    *xOffset = DlineXOfIndex(textPtr, dlPtr, 
				indexPtr->byteIndex - dlPtr->index.byteIndex);
d3294 3
a3296 3
		    /* 
		     * The index we want is one less than the number
		     * of bytes in the display line.
d3298 1
d3317 10
a3326 11
 *	This procedure is invoked to recalculate the height of the
 *	particular display line which starts with the given index, 
 *	whether that line is displayed or not.
 *	
 *	This procedure does not, in itself, update any cached
 *	information about line heights.  That should be done, where
 *	necessary, by its callers.
 *	
 *	The behaviour of this procedure is _undefined_ if indexPtr
 *	is not currently at the beginning of a display line.
 *	
d3329 14
a3342 16
 *	
 *	If 'byteCountPtr' is non-NULL, then returns in that pointer
 *	the number of byte indices on the given display line (which
 *	can be used to update indexPtr in a loop).
 *	
 *	If 'mergedLinePtr' is non-NULL, then returns in that pointer
 *	the number of extra logical lines merged into the given
 *	display line.
 *
 * Side effects:
 *	The combination of 'LayoutDLine' and 'FreeDLines' seems
 *	like a rather time-consuming way of gathering the information
 *	we need, so this would be a good place to look to speed up
 *	the calculations.  In particular these calls will map and
 *	unmap embedded windows respectively, which I would hope isn't
 *	exactly necessary!
d3349 9
a3357 10
    TkText *textPtr;             /* Widget record for text widget. */
    CONST TkTextIndex *indexPtr; /* The index at the beginning of the
				  * display line of interest. */
    int *byteCountPtr;           /* NULL or used to return the number of
				  * byte indices on the given display
				  * line. */
    int *mergedLinePtr;          /* NULL or used to return if the given
                                  * display line merges with a following
                                  * logical line (because the eol is 
                                  * elided). */
d3361 4
a3364 4
    
    /* 
     * Special case for artificial last line.  May be better to move
     * this inside LayoutDLine.
d3366 3
a3368 2
    if (indexPtr->byteIndex == 0 
      && TkBTreeNextLine(textPtr, indexPtr->linePtr) == NULL) {
d3378 5
a3382 5
    /* 
     * Layout, find the information we need and then free the
     * display-line we laid-out.  We must use 'FreeDLines' because it
     * will actually call the relevant code to unmap any embedded windows
     * which were mapped in the LayoutDLine call!
d3384 1
d3388 1
a3388 1
        *byteCountPtr = dlPtr->byteCount;
d3391 1
a3391 1
        *mergedLinePtr = dlPtr->logicalLinesMerged;
d3403 6
a3408 6
 *	This procedure is invoked to calculate the number of vertical
 *	pixels between the first index of the text widget and the given
 *	index.  The range from first logical line to given logical
 *	line is determined using the cached values, and the range
 *	inside the given logical line is calculated on the fly.
 *	
d3421 1
a3421 1
    TkText *textPtr;              /* Widget record for text widget. */
d3423 2
a3424 2
				   * distance from top of logical line to
				   * top of index. */
d3428 1
a3428 1
    
d3430 5
a3434 6
    
    /* 
     * Iterate through all display-lines corresponding to the single
     * logical line belonging to indexPtr, adding up the pixel height of
     * each such display line as we go along, until we go past
     * 'indexPtr'.
d3438 1
a3438 1
        return pixelHeight;
d3440 1
a3440 1
    
d3445 1
a3445 1
    
d3448 7
a3454 8
	
	/* 
	 * Currently this call doesn't have many side-effects. 
	 * However, if in the future we change the code so there
	 * are side-effects (such as adjusting linePtr->pixelHeight),
	 * then the code might not quite work as intended,
	 * specifically the 'linePtr->pixelHeight == pixelHeight' test 
	 * below this while loop.
d3456 1
d3458 1
a3458 1
	
d3460 1
a3460 1
	
d3464 1
a3464 1
	
d3480 16
a3495 18
 *	This procedure is invoked to recalculate the height of a
 *	particular logical line, whether that line is displayed or not.
 *	
 *	It must NEVER be called for the artificial last TkTextLine
 *	which is used internally for administrative purposes only.
 *	That line must retain its initial height of 0 otherwise
 *	the pixel height calculation maintained by the B-tree will
 *	be wrong.
 *	
 * Results:
 *	The number of display lines in the logical line.  This could
 *	be zero if the line is totally elided.
 *
 * Side effects:
 *	Line heights may be recalculated, and a timer to update
 *	the scrollbar may be installed.  Also see the called 
 *	function 'CalculateDisplayLineHeight' for its side
 *	effects.
d3502 13
a3514 17
    TkText *textPtr;        /* Widget record for text widget. */
    TkTextLine *linePtr;    /* The line of which to calculate the
                             * height. */
    int pixelHeight;        /* If indexPtr is non-NULL, then this
                             * is the number of pixels in the logical
                             * line linePtr, up to the index which
                             * has been given. */
    TkTextIndex *indexPtr;  /* Either NULL or an index at the start of
                             * a display line belonging to linePtr,
                             * at which we wish to start (e.g. up to 
                             * which we have already calculated). On
                             * return this will be set to the first
                             * index on the next line. */
    int partialCalc;        /* Set to 1 if we are allowed to do 
                             * partial height calculations of 
                             * long-lines.  In this case we'll only
                             * return what we know so far. */
d3519 1
a3519 1
    
d3528 6
a3533 6
    
    /* 
     * Iterate through all display-lines corresponding to the
     * single logical line 'linePtr', adding up the pixel height
     * of each such display line as we go along.  The final
     * total is, therefore, the height of the logical line.
d3538 1
a3538 1
    
d3541 12
a3552 12
	
	/* 
	 * Currently this call doesn't have many side-effects. 
	 * However, if in the future we change the code so there
	 * are side-effects (such as adjusting linePtr->pixelHeight),
	 * then the code might not quite work as intended,
	 * specifically the 'linePtr->pixelHeight == pixelHeight' test 
	 * below this while loop.
	 */
	height = CalculateDisplayLineHeight(textPtr, indexPtr, &bytes, 
					    &logicalLines);
	
d3557 1
a3557 1
	
d3559 1
a3559 1
	
d3561 2
a3562 2
            break;
        }
d3566 3
a3568 3
		/* 
		 * If we reached the end of the logical line, then
		 * either way we don't have a partial calculation.
d3570 1
d3575 3
a3577 1
	    /* We must still be on the same wrapped line */
d3580 4
a3583 5
	     * Must check if indexPtr is really a new logical line
	     * which is not merged with the previous line.  The only
	     * code that would really know this is LayoutDLine, which
	     * doesn't pass the information on, so we have to check
	     * manually here.
d3585 1
d3589 4
a3592 1
		/* We've ended a logical line */
d3596 4
a3599 1
	    /* We must still be on the same wrapped line */
d3602 9
a3610 9
	    /* 
	     * Only calculate 50 display lines at a time, to 
	     * avoid huge delays.  In any case it is very rare
	     * that a single line wraps 50 times!
	     * 
	     * If we have any merged lines, we must complete the full
	     * logical line layout here and now, because the
	     * partial-calculation code isn't designed to handle merged
	     * logical lines.  Hence the 'mergedLines == 0' check.
d3612 1
d3616 1
a3616 1
    
d3619 2
a3620 2
	
	/* 
d3623 1
d3626 3
a3628 4
	/* 
	 * Mark the logical line as being up to date (caution: it isn't
	 * yet up to date, that will happen in TkBTreeAdjustPixelHeight
	 * just below).
d3630 3
a3632 2
	TkBTreeLinePixelEpoch(textPtr, linePtr) 
	                   = textPtr->dInfoPtr->lineMetricUpdateEpoch;
d3636 1
a3636 1
	
d3640 2
a3641 1
	    /* 
d3643 1
a3643 1
	     * (again, the pixel count setting will actually happen in 
d3646 1
d3650 2
a3651 2
		TkBTreeLinePixelEpoch(textPtr, mergedLinePtr) 
				   = textPtr->dInfoPtr->lineMetricUpdateEpoch;
d3657 1
a3657 1
	
d3659 1
a3659 1
	    /* 
d3662 1
d3666 10
a3675 9
    
    /* 
     * We set the line's height, but the return value is now the height
     * of the entire widget, which may be used just below for
     * reporting/debugging purposes.
     */
    pixelHeight = TkBTreeAdjustPixelHeight(textPtr, linePtr, 
					   pixelHeight, mergedLines);
    
d3684 1
a3684 1
	Tcl_SetVar2(textPtr->interp, "tk_textNumPixels", (char *) NULL, 
d3689 1
a3689 1
        textPtr->dInfoPtr->scrollbarTimer = Tcl_CreateTimerHandler(200, 
d3700 2
a3701 3
 *	This procedure is invoked as a when-idle handler to update the
 *	display.  It only redisplays the parts of the text widget that
 *	are out of date.
d3722 2
a3723 2
    int bottomY = 0;		/* Initialization needed only to stop
				 * compiler warnings. */
a3726 1

d3728 1
a3728 1
	 * The widget has been deleted.  Don't do anything.
d3739 1
a3739 1
                TCL_GLOBAL_ONLY);
a3742 1

d3744 1
a3744 1
	 * The widget has been deleted.  Don't do anything.
d3747 1
a3747 1
        goto end;
d3759 1
a3759 1
                TCL_GLOBAL_ONLY);
a3762 1

d3764 1
a3764 1
	 * The widget has been deleted.  Don't do anything.
d3771 5
a3775 5
     * Choose a new current item if that is needed (this could cause
     * event handlers to be invoked, hence the preserve/release calls
     * and the loop, since the handlers could conceivably necessitate
     * yet another current item calculation).  The tkwin check is because
     * the whole window could go away in the Tcl_Release call.
d3799 5
a3803 5
     * See if it's possible to bring some parts of the screen up-to-date
     * by scrolling (copying from other parts of the screen).  We have
     * to be particularly careful with the top and bottom lines of the
     * display, since these may only be partially visible and therefore
     * not helpful for some scrolling purposes.
d3811 1
a3811 1
	/* 
d3813 1
a3813 1
	 * 
d3816 19
a3834 19
	 * 3. If the line overlaps the bottom of the window and we
	 * are scrolling up
	 * 4. If the line overlaps the top of the window and we are
	 * scrolling down
	 * 
	 * If any of these tests are true, then we can't scroll this
	 * line's part of the display.
	 * 
	 * Note that even if tests 3 or 4 aren't true, we may be
	 * able to scroll the line, but we still need to be sure
	 * to call embedded window display procs on top and bottom
	 * lines if they have any portion non-visible (see below).
	 */
	if ((dlPtr->flags & OLD_Y_INVALID) 
	    || (dlPtr->y == dlPtr->oldY)
	    || (((dlPtr->oldY + dlPtr->height) > dInfoPtr->maxY) 
		&& (dlPtr->y < dlPtr->oldY))
	    || ((dlPtr->oldY < dInfoPtr->y) 
		&& (dlPtr->y > dlPtr->oldY))) {
d3839 3
a3841 3
	 * This line is already drawn somewhere in the window so it only
	 * needs to be copied to its new location.  See if there's a group
	 * of lines that can all be copied together.
d3858 2
a3859 2
	 * Reduce the height of the area being copied if necessary to
	 * avoid overwriting the border area.
d3861 1
d3867 3
a3869 4
	    /* 
	     * Adjust if the area being copied is going to overwrite
	     * the top border of the window (so the top line is only
	     * half onscreen).
d3871 1
d3877 1
a3877 1
	
d3879 2
a3880 2
	 * Update the lines we are going to scroll to show that they
	 * have been copied.
d3884 4
a3887 1
	    /* The DLine already has OLD_Y_INVALID cleared */
d3896 3
a3898 3
	 * Scan through the lines following the copied ones to see if
	 * we are going to overwrite them with the copy operation.
	 * If so, mark them for redisplay.
d3910 2
a3911 3
	 * Now scroll the lines.  This may generate damage which we
	 * handle by calling TextInvalidateRegion to mark the display
	 * blocks as stale.
d3926 7
a3932 8
     * Clear the REDRAW_PENDING flag here.  This is actually pretty
     * tricky.  We want to wait until *after* doing the scrolling,
     * since that could generate more areas to redraw and don't
     * want to reschedule a redisplay for them.  On the other hand,
     * we can't wait until after all the redisplaying, because the
     * act of redisplaying could actually generate more redisplays
     * (e.g. in the case of a nested window with event bindings triggered
     * by redisplay).
d3947 1
a3947 2
        if (textPtr->tkwin == NULL) {

d3949 2
a3950 2
             * The widget has been deleted.  Don't do anything.
             */
d3952 2
a3953 2
            goto end;
        }
d3963 1
a3963 1
    
d3969 2
a3970 2
	        TkpDrawHighlightBorder(textPtr->tkwin, fgGC, bgGC, 
		        textPtr->highlightWidth, Tk_WindowId(textPtr->tkwin));
d3972 2
a3973 2
	        TkpDrawHighlightBorder(textPtr->tkwin, bgGC, bgGC, 
		        textPtr->highlightWidth, Tk_WindowId(textPtr->tkwin));
d4005 3
a4007 4
     * Now we have to redraw the lines that couldn't be updated by
     * scrolling.  First, compute the height of the largest line and
     * allocate an off-screen pixmap to use for double-buffered
     * displays.
d4013 1
a4013 1
	if ((dlPtr->height > maxHeight) 
d4019 2
a4020 1
    /* 
d4022 4
a4025 4
     * larger than 'dInfoPtr->maxY', but this is incorrect for the case
     * where individual lines may be taller than the widget _and_ we have
     * smooth scrolling.  What we can do is restrict maxHeight to be
     * no larger than 'dInfoPtr->maxY + dInfoPtr->topPixelOffset'.
d4027 1
d4031 1
a4031 1
    
d4043 1
d4056 2
a4057 2
	    } else if (dlPtr->chunkPtr != NULL && ((dlPtr->y < 0) 
		       || (dlPtr->y + dlPtr->height > dInfoPtr->maxY))) {
d4060 9
a4068 10
		/* 
		 * It's the first or last DLine which are also
		 * overlapping the top or bottom of the window, but we
		 * decided above it wasn't necessary to display them (we
		 * were able to update them by scrolling).  This is fine,
		 * except that if the lines contain any embedded windows,
		 * we must still call the display proc on them because
		 * they might need to be unmapped or they might need to
		 * be moved to reflect their new position.  Otherwise,
		 * everything else moves, but the embedded window
d4070 3
a4072 3
		 * 
		 * So, we loop through all the chunks, calling the
		 * display proc of embedded windows only.
d4074 1
d4085 7
a4091 7
			 * chunks that are off-screen.  This is needed,
			 * for example, so that embedded windows can be
			 * unmapped in this case.  Display the chunk at a
			 * coordinate that can be clearly identified by
			 * the displayProc as being off-screen to the
			 * left (the displayProc may not be able to tell
			 * if something is off to the right).
d4093 1
d4096 6
a4101 5
		    TkTextEmbWinDisplayProc(textPtr, chunkPtr, 
			x, dlPtr->spaceAbove,
			dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
			dlPtr->baseline - dlPtr->spaceAbove, (Display *) NULL, 
			(Drawable) None, dlPtr->y + dlPtr->spaceAbove);
d4103 1
a4103 1
 	       
d4110 4
a4113 4
     * See if we need to refresh the part of the window below the
     * last line of text (if there is any such area).  Refresh the
     * padding area on the left too, since the insertion cursor might
     * have been displayed there previously).
d4126 1
a4126 2
        if ((textPtr->tkwin == NULL) || (textPtr->flags & DESTROYED)) {

d4128 2
a4129 2
             * The widget has been deleted.  Don't do anything.
             */
d4131 2
a4132 2
            goto end;
        }
d4141 1
a4141 1
    doScrollbars:
d4144 3
a4146 3
     * Update the vertical scrollbar, if there is one.  Note:  it's
     * important to clear REDRAW_PENDING here, just in case the
     * scroll procedure does something that requires redisplay.
d4148 1
a4148 1
    
d4155 1
a4155 2
        if ((textPtr->tkwin == NULL) || (textPtr->flags & DESTROYED)) {

d4157 2
a4158 2
             * The widget has been deleted.  Don't do anything.
             */
d4160 2
a4161 2
            goto end;
        }
d4172 1
a4172 1
end:
d4181 2
a4182 3
 *	This procedure is invoked whenever something happens that
 *	could change the current character or the tags associated
 *	with it.
d4212 3
a4214 3
 *	This procedure is invoked to schedule a redisplay for a given
 *	region of a text widget.  The redisplay itself may not occur
 *	immediately:  it's scheduled as a when-idle handler.
d4229 3
a4231 3
    int x, y;			/* Coordinates of upper-left corner of area
				 * to be redrawn, in pixels relative to
				 * textPtr's window. */
d4288 3
a4290 4
	if ((!(dlPtr->flags & OLD_Y_INVALID)) 
	    && (TkRectInRegion(region, rect.x, dlPtr->y, 
			       rect.width, (unsigned int) dlPtr->height) 
		!= RectangleOut)) {
d4299 1
a4299 2
     * Schedule the redisplay operation if there isn't one already
     * scheduled.
d4317 9
a4325 10
 *	This procedure is invoked when info in a text widget is about
 *	to be modified in a way that changes how it is displayed (e.g.
 *	characters were inserted or deleted, or tag information was
 *	changed).  This procedure must be called *before* a change is
 *	made, so that indexes in the display information are still
 *	valid.
 *	
 *	Note: if the range of indices may change geometry as well
 *	as simply requiring redisplay, then the caller should also
 *	call TkTextInvalidateLineMetrics.
d4331 3
a4333 3
 *	The range of character between index1Ptr (inclusive) and
 *	index2Ptr (exclusive) will be redisplayed at some point in the
 *	future (the actual redisplay is scheduled as a when-idle handler).
d4341 5
a4345 6
    TkText *textPtr;		  /* Widget record for text widget,
                    		   * or NULL. */
    CONST TkTextIndex *index1Ptr; /* Index of first character to 
                                   * redisplay. */
    CONST TkTextIndex *index2Ptr; /* Index of character just after last one
				   * to redisplay. */
d4360 4
a4363 3
    TkText *textPtr;		  /* Widget record for text widget,
				   * or NULL. */
    CONST TkTextIndex *index1Ptr; /* Index of first character to 
a4364 2
    CONST TkTextIndex *index2Ptr; /* Index of character just after last one
				   * to redisplay. */
d4372 1
a4372 1
     * It's done here rather than the end of the procedure for two reasons:
d4375 1
a4375 1
     *    immediately, well before the end of the procedure.
d4377 7
a4383 7
     *    FreeDLines.  The reason for this is subtle and has to do with
     *    embedded windows.  The chunk delete procedure for an embedded
     *    window will schedule an idle handler to unmap the window.
     *    However, we want the idle handler for redisplay to be called
     *    first, so that it can put the embedded window back on the screen
     *    again (if appropriate).  This will prevent the window from ever
     *    being unmapped, and thereby avoid flashing.
d4392 7
a4398 7
     * Find the DLines corresponding to index1Ptr and index2Ptr.  There
     * is one tricky thing here, which is that we have to relayout in
     * units of whole text lines:  round index1Ptr back to the beginning
     * of its text line, and include all the display lines after index2,
     * up to the end of its text line.  This is necessary because the
     * indices stored in the display lines will no longer be valid.  It's
     * also needed because any edit could change the way lines wrap.
d4425 3
a4427 3
 *	This procedure is invoked to request a redraw of all characters
 *	in a given range that have a particular tag on or off.  It's
 *	called, for example, when tag options change.
d4433 2
a4434 2
 *	Information on the screen may be redrawn, and the layout of
 *	the screen may change.
d4441 1
a4441 1
    TkSharedText *sharedTextPtr;  /* Shared widget section, or NULL */
d4443 3
a4445 3
    TkTextIndex *index1Ptr;	/* First character in range to consider
				 * for redisplay.  NULL means start at
				 * beginning of text. */
d4447 2
a4448 2
				 * for redisplay.  NULL means process all
				 * the characters in the text. */
d4467 3
a4469 3
    TkTextIndex *index1Ptr;	/* First character in range to consider
				 * for redisplay.  NULL means start at
				 * beginning of text. */
d4471 2
a4472 2
				 * for redisplay.  NULL means process all
				 * the characters in the text. */
d4485 7
a4491 8
    /* 
     * Invalidate the pixel calculation of all lines in the given range.
     * This may be a bit over-aggressive, so we could consider more
     * subtle techniques here in the future.  In particular, when we
     * create a tag for the first time with '.t tag configure foo -font
     * "Arial 20"', say, even though that obviously can't apply to
     * anything at all (the tag didn't exist a moment ago), we invalidate
     * every single line in the widget.
d4493 1
d4495 1
a4495 1
        TkTextLine *startLine, *endLine;
d4497 1
d4511 2
a4512 2
	TkTextInvalidateLineMetrics(NULL, textPtr, startLine, lineCount, 
				    TK_TEXT_INVALIDATE_ONLY);
d4516 2
a4517 2
     * Round up the starting position if it's before the first line
     * visible on the screen (we only care about what's on the screen).
d4524 1
a4524 1
    if ((index1Ptr == NULL) || (TkTextIndexCmp(&dlPtr->index, index1Ptr) > 0)) {
d4534 1
d4536 1
a4536 1
					lastLine, 0, &endOfText);
d4539 4
a4542 4
    /* 
     * Initialize a search through all transitions on the tag, starting
     * with the first transition where the tag's current state is different
     * from what it will eventually be.
d4546 1
d4548 3
a4550 3
     * Make our own curIndex because at this point search.curIndex
     * may not equal index1Ptr->curIndex in the case the first tag toggle
     * comes after index1Ptr (See the use of FindTagStart in TkBTreeStartSearch)
d4552 1
d4563 3
a4565 3
     * Schedule a redisplay and layout recalculation if they aren't
     * already pending.  This has to be done before calling FreeDLines,
     * for the reason given in TkTextChanged.
d4574 4
a4577 4
     * Each loop through the loop below is for one range of characters
     * where the tag's current state is different than its eventual
     * state.  At the top of the loop, search contains information about
     * the first character in the range.
d4582 6
a4587 7
	 * Find the first DLine structure in the range.  Note: if the
	 * desired character isn't the first in its text line, then look
	 * for the character just before it instead.  This is needed to
	 * handle the case where the first character of a wrapped
	 * display line just got smaller, so that it now fits on the
	 * line before:  need to relayout the line containing the
	 * previous character.
d4620 2
a4621 2
	 * Delete all of the display lines in the range, so that they'll
	 * be re-layed out and redrawn.
d4642 5
a4646 5
 *	This procedure is called when something has happened that
 *	invalidates the whole layout of characters on the screen, such
 *	as a change in a configuration option for the overall text
 *	widget or a change in the window size.  It causes all display
 *	information to be recomputed and the window to be redrawn.
d4652 2
a4653 2
 *	All the display information will be recomputed for the window
 *	and the window will be redrawn.
d4661 2
a4662 2
    int mask;                   /* OR'd collection of bits showing what
                                 * has changed */
d4669 2
a4670 2
     * Schedule the window redisplay.  See TkTextChanged for the
     * reason why this has to be done before any calls to FreeDLines.
d4680 1
a4680 2
     * (Re-)create the graphics context for drawing the traversal
     * highlight.
d4698 3
a4700 3
     * Recompute some overall things for the layout.  Even if the
     * window gets very small, pretend that there's at least one
     * pixel of drawing space in it.
d4715 2
a4716 1
    /* 
d4719 1
d4728 3
a4730 3
     * If the upper-left character isn't the first in a line, recompute
     * it.  This is necessary because a change in the window's size
     * or options could change the way lines wrap.
d4738 2
a4739 2
     * Invalidate cached scrollbar positions, so that scrollbars
     * sliders will be udpated.
d4744 1
a4744 1
    
d4746 1
a4746 1
        /* 
d4748 3
a4750 3
	 * 
	 * Avoid the special zero value, since that is used to
	 * mark individual lines as being out of date.
d4752 1
d4758 4
a4761 4
	
	/* 
	 * Also cancel any partial line-height calculations (for
	 * long-wrapped lines) in progress 
d4763 1
d4768 1
a4768 1
	    dInfoPtr->lineUpdateTimer = Tcl_CreateTimerHandler(1, 
d4779 2
a4780 2
 *	This procedure is called to specify what lines are to be
 *	displayed in a text widget.
d4786 3
a4788 3
 *	The display will (eventually) be updated so that the position
 *	given by "indexPtr" is visible on the screen at the position
 *	determined by "pickPlace".
d4796 15
a4810 16
    TkTextIndex *indexPtr;	/* Position that is to appear somewhere
				 * in the view. */
    int pickPlace;		/* 0 means the given index must appear
                  		 * exactly at the top of the screen.
                  		 * TK_TEXT_PICKPLACE (-1) means we get to
                  		 * pick where it appears: minimize screen
                  		 * motion or else display line at center
                  		 * of screen.  TK_TEXT_NOPIXELADJUST (-2)
                  		 * indicates to make the given index the
                  		 * top line, but if it is already the top
                  		 * line, don't nudge it up or down by a
                  		 * few pixels just to make sure it is
                  		 * entirely displayed.  Positive numbers
                  		 * indicate the number of pixels of the
                  		 * index's line which are to be off the
                  		 * top of the screen.  */ 
d4817 1
a4817 1
    
d4819 2
a4820 2
     * If the specified position is the extra line at the end of the
     * text, round it back to the last real line.
d4830 1
a4830 1
	if (textPtr->topIndex.linePtr == indexPtr->linePtr 
d4837 1
a4837 1
    
d4840 4
a4843 4
	 * The specified position must go at the top of the screen.
	 * Just leave all the DLine's alone: we may be able to reuse
	 * some of the information that's currently on the screen
	 * without redisplaying it all.
d4855 3
a4857 4
     * We have to pick where to display the index.  First, bring
     * the display information up to date and see if the index will be
     * completely visible in the current screen configuration.  If so
     * then there's nothing to do.
d4867 2
a4868 2
	     * Part of the line hangs off the bottom of the screen;
	     * pretend the whole line is off-screen.
d4874 5
a4878 6
	    if (dInfoPtr->dLinePtr == dlPtr 
	      && dInfoPtr->topPixelOffset != 0) {
		/* 
		 * It is on the top line, but that line is hanging
		 * off the top of the screen.  Change the top
		 * overlap to zero and update.
d4880 1
d4889 5
a4893 7
     * The desired line isn't already on-screen.  Figure out what
     * it means to be "close" to the top or bottom of the screen.
     * Close means within 1/3 of the screen height or within three
     * lines, whichever is greater. 
     * 
     * If the line is not close, place it in the center of the
     * window.  
d4897 4
a4900 3
    /* 
     * It would be better if 'bottomY' were calculated using the
     * actual height of the given line, not 'textPtr->charHeight'.
d4902 1
d4910 1
d4912 4
a4915 5
	 * The desired line is above the top of screen.  If it is
	 * "close" to the top of the window then make it the top
	 * line on the screen.  MeasureUp counts from the bottom
	 * of the given index upwards, so we add an extra half line
	 * to be sure we count far enough.
d4918 1
a4918 1
	MeasureUp(textPtr, &textPtr->topIndex, close + textPtr->charHeight/2, 
d4928 1
d4930 3
a4932 3
	 * The desired line is below the bottom of the screen.  If it is
	 * "close" to the bottom of the screen then position it at the
	 * bottom of the screen.
d4935 1
a4935 1
	MeasureUp(textPtr, indexPtr, close + lineHeight 
d4943 5
a4947 5
     * Our job now is to arrange the display so that indexPtr appears
     * as low on the screen as possible but with its bottom no lower
     * than bottomY.  BottomY is the bottom of the window if the
     * desired line is just below the current screen, otherwise it
     * is a half-line lower than the center of the window.
d4950 1
a4950 1
    MeasureUp(textPtr, indexPtr, bottomY, &textPtr->topIndex, 
d4953 1
a4953 1
    scheduleUpdate:
d4965 3
a4967 3
 *	Given one index, find the index of the first character
 *	on the highest display line that would be displayed no more
 *	than "distance" pixels below the top of the given index.
d4970 2
a4971 3
 *	The srcPtr is manipulated in place to reflect the new
 *	position.  We return the number of pixels by which 'distance'
 *	overlaps the srcPtr.
d4984 2
a4985 3
    int distance;		/* Vertical distance in pixels measured
				 * from the top pixel in srcPtr's 
                                   logical line. */
d4991 2
a4992 2
    lastLinePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree, textPtr, 
			TkBTreeNumLines(textPtr->sharedTextPtr->tree, textPtr));
d5019 16
a5034 17
 *	Given one index, find the index of the first character on the
 *	highest display line that would be displayed no more than
 *	"distance" pixels above the given index.
 *	
 *	If this function is called with distance=0, it simply finds the
 *	first index on the same display line as srcPtr.  However, there
 *	is a another function TkTextFindDisplayLineEnd designed just for
 *	that task which is probably better to use.
 *
 * Results:
 *	*dstPtr is filled in with the index of the first character
 *	on a display line.  The display line is found by measuring
 *	up "distance" pixels above the pixel just below an imaginary
 *	display line that contains srcPtr.  If the display line
 *	that covers this coordinate actually extends above the 
 *	coordinate, then return any excess pixels in *overlap, if
 *	that is non-NULL.
d5047 3
a5049 3
    int distance;		/* Vertical distance in pixels measured
				 * from the pixel just below the lowest
				 * one in srcPtr's line. */
d5051 2
a5052 3
    int *overlap;               /* Used to store how much of the final
                                 * index returned was not covered by
                                 * 'distance'. */
d5066 5
a5070 6
	 * 
	 * For the first line, which contains srcPtr, only layout the
	 * part up through srcPtr (bytesToCount is non-infinite to
	 * accomplish this).  Make a list of all the display lines
	 * in backwards order (the lowest DLine on the screen is first
	 * in the list).
d5086 4
a5089 4
	 * vertical distance.  If so, save the starting index for the
	 * line at the desired location.  If distance was zero to start
	 * with then we simply get the first index on the same display
	 * line as the original index.
d5104 2
a5105 2
	 * Discard the display lines, then either return or prepare
	 * for the next display line to lay out.
d5116 2
a5117 2
     * Ran off the beginning of the text.  Return the first character
     * in the text.
d5122 1
a5122 1
        *overlap = 0;
d5131 3
a5133 3
 *	This procedure is invoked to process the "see" option for
 *	the widget command for text widgets. See the user documentation
 *	for details on what it does.
d5168 2
a5169 2
     * If the specified position is the extra line at the end of the
     * text, round it back to the last real line.
d5172 2
a5173 2
    if (TkBTreeLinesTo(textPtr, index.linePtr) 
      == TkBTreeNumLines(index.tree, textPtr)) {
d5196 2
a5197 2
     * Find the chunk that contains the desired index.
     * dlPtr may be NULL if the widget is not mapped. [Bug #641778]
d5215 3
a5217 3
     * Call a chunk-specific procedure to find the horizontal range of
     * the character within the chunk.
     * chunkPtr is NULL if trying to see in elided region.
d5260 3
a5262 3
 *	This procedure is invoked to process the "xview" option for
 *	the widget command for text widgets. See the user documentation
 *	for details on what it does.
d5295 2
a5296 2
    type = TextGetScrollInfoObj(interp, textPtr, objc, objv, 
				&fraction, &count);
d5298 27
a5324 27
	case TKTEXT_SCROLL_ERROR:
	    return TCL_ERROR;
	case TKTEXT_SCROLL_MOVETO:
	    if (fraction > 1.0) {
		fraction = 1.0;
	    }
	    if (fraction < 0) {
		fraction = 0;
	    }
	    dInfoPtr->newXPixelOffset = (int) (fraction 
					       * dInfoPtr->maxLength + 0.5);
	    break;
	case TKTEXT_SCROLL_PAGES: {
	    int pixelsPerPage;
	    pixelsPerPage = (dInfoPtr->maxX-dInfoPtr->x) - 2*textPtr->charWidth;
	    if (pixelsPerPage < 1) {
		pixelsPerPage = 1;
	    }
	    dInfoPtr->newXPixelOffset += pixelsPerPage * count;
	    break;
	}
	case TKTEXT_SCROLL_UNITS:
	    dInfoPtr->newXPixelOffset += count * textPtr->charWidth;
	    break;
	case TKTEXT_SCROLL_PIXELS:
	    dInfoPtr->newXPixelOffset += count;
	    break;
d5340 2
a5341 2
 *	This procedure is called to scroll a text widget up or down
 *	by a given number of pixels.
d5356 5
a5360 5
    int offset;			/* Amount by which to scroll, in 
               			 * pixels. Positive means that information
				 * later in text becomes visible, negative
				 * means that information earlier in the
				 * text becomes visible. */
d5365 4
a5368 5
	/* 
	 * Now we want to measure up this number of pixels
	 * from the top of the screen.  But the top line may
	 * not be totally visible.  Note that 'count' is 
	 * negative here.
d5370 2
a5371 1
	offset -= CalculateDisplayLineHeight(textPtr, 
d5373 1
a5373 1
	MeasureUp(textPtr, &textPtr->topIndex, -offset, 
d5379 1
d5381 2
a5382 3
	 * Scrolling down by pixels.  Layout lines starting at
	 * the top index and count through the desired vertical
	 * distance.
d5392 2
a5393 2
	    TkTextIndexForwBytes(textPtr, &textPtr->topIndex, 
				 dlPtr->byteCount, &new);
d5395 4
a5398 1
		/* Adjust the top overlap accordingly */
d5409 4
a5412 1
	/* offset = 0, so no scrolling required */
d5426 2
a5427 2
 *	This procedure is called to scroll a text widget up or down
 *	by a given number of lines.
d5433 1
a5433 2
 *	The view in textPtr's window changes to reflect the value
 *	of "offset".
d5441 2
a5442 2
    int offset;			/* Amount by which to scroll, in display 
				 * lines.  Positive means that information
d5444 2
a5445 2
				 * means that information earlier in the
				 * text becomes visible. */
d5455 3
a5457 3
	 * Must scroll up (to show earlier information in the text).
	 * The code below is similar to that in MeasureUp, except that
	 * it counts lines instead of pixels.
d5462 1
a5462 1
	offset--;			/* Skip line containing topIndex. */
d5465 2
a5466 2
	    index.linePtr = TkBTreeFindLine(textPtr->sharedTextPtr->tree, 
					    textPtr, lineNum);
d5473 1
a5473 1
		TkTextIndexForwBytes(textPtr, &index, dlPtr->byteCount, &index);
d5487 2
a5488 2
	     * Discard the display lines, then either return or prepare
	     * for the next display line to lay out.
d5490 1
a5490 1
    
d5497 1
a5497 1
    
d5499 3
a5501 3
	 * Ran off the beginning of the text.  Return the first character
	 * in the text, and make sure we haven't left anything
	 * overlapping the top window border.
d5504 1
a5504 1
	TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr, 
d5509 2
a5510 2
	 * Scrolling down, to show later information in the text.
	 * Just count lines from the current top of the window.
d5517 3
a5519 1
	    if (dlPtr->length == 0 && dlPtr->height == 0) offset++;
d5521 2
a5522 2
	    TkTextIndexForwBytes(textPtr, &textPtr->topIndex, 
				 dlPtr->byteCount, &new);
d5531 1
a5531 1
    scheduleUpdate:
d5543 3
a5545 3
 *	This procedure is invoked to process the "yview" option for
 *	the widget command for text widgets. See the user documentation
 *	for details on what it does.
d5589 1
a5589 1
		&& (strncmp(Tcl_GetString(objv[2]), 
d5601 1
a5601 1
	    TkTextMakeByteIndex(textPtr->sharedTextPtr->tree, textPtr, 
d5606 1
a5606 1
    
d5610 1
a5610 1
    
d5626 11
a5636 36
	case TKTEXT_SCROLL_ERROR:
	    return TCL_ERROR;
	case TKTEXT_SCROLL_MOVETO: {
	    int numPixels = TkBTreeNumPixels(textPtr->sharedTextPtr->tree,
					     textPtr);
	    int topMostPixel;
	    if (numPixels == 0) {
		/* 
		 * If the window is totally empty no scrolling is
		 * needed, and the TkTextMakePixelIndex call
		 * below will fail.
		 */
		break;
	    }
	    if (fraction > 1.0) {
		fraction = 1.0;
	    }
	    if (fraction < 0) {
		fraction = 0;
	    }
	    /* 
	     * Calculate the pixel count for the new topmost pixel
	     * in the topmost line of the window.  Note that the
	     * interpretation of 'fraction' is that it counts from
	     * 0 (top pixel in buffer) to 1.0 (one pixel past the
	     * last pixel in buffer).
	     */
	    topMostPixel = (int) (0.5 + fraction * numPixels);
	    if (topMostPixel >= numPixels) {
		topMostPixel = numPixels -1;
	    }
	    /*
	     * This function returns the number of pixels by which the
	     * given line should overlap the top of the visible screen.
	     * 
	     * This is then used to provide smooth scrolling.
d5638 1
a5638 2
	    pixels = TkTextMakePixelIndex(textPtr, topMostPixel, &index);
	    TkTextSetYView(textPtr, &index, pixels);
d5641 39
a5679 1
	case TKTEXT_SCROLL_PAGES: {
d5681 10
a5690 10
	     * Scroll up or down by screenfuls.  Actually, use the
	     * window height minus two lines, so that there's some
	     * overlap between adjacent pages.
	     */
	    int height = dInfoPtr->maxY - dInfoPtr->y;
	    if (textPtr->charHeight * 4 >= height) {
		/* 
		 * A single line is more than a quarter of the
		 * display.  We choose to scroll by 3/4 of the
		 * height instead.
d5692 5
a5696 12
		pixels = 3*height/4;
		if (pixels < textPtr->charHeight) {
		    /* 
		     * But, if 3/4 of the height is actually less than a
		     * single typical character height, then scroll by
		     * the minimum of the linespace or the total height.
		     */
		    if (textPtr->charHeight < height) {
			pixels = textPtr->charHeight;
		    } else {
			pixels = height;
		    }
a5697 3
		pixels *= count;
	    } else {
		pixels = (height - 2*textPtr->charHeight)*count;
d5699 3
a5701 2
	    YScrollByPixels(textPtr, pixels);
	    break;
d5703 9
a5711 6
	case TKTEXT_SCROLL_PIXELS:
	    YScrollByPixels(textPtr, count);
	    break;
	case TKTEXT_SCROLL_UNITS:
	    YScrollByLines(textPtr, count);
	    break;
d5721 3
a5723 3
 *	This procedure is invoked to process the "scan" option for
 *	the widget command for text widgets. See the user documentation
 *	for details on what it does.
d5751 1
a5751 1
		Tcl_GetString(objv[0]), " scan dragto x y ?gain?\"", 
d5762 1
a5762 1
        return TCL_ERROR;
d5768 1
a5768 1
	
d5770 8
a5777 9
	 * Amplify the difference between the current position and the
	 * mark position to compute how much the view should shift, then
	 * update the mark position to correspond to the new view.  If we
	 * run off the edge of the text, reset the mark point so that the
	 * current position continues to correspond to the edge of the
	 * window.  This means that the picture will start dragging as
	 * soon as the mouse reverses direction (without this reset, might
	 * have to slide mouse a long ways back before the picture starts
	 * moving again).
d5827 3
a5829 3
 *	This procedure computes the fractions that indicate what's
 *	visible in a text window and, optionally, evaluates a
 *	Tcl script to report them to the text's associated scrollbar.
d5832 8
a5839 8
 *	If report is zero, then the interp's result is filled in with
 *	two real numbers separated by a space, giving the position of
 *	the left and right edges of the window as fractions from 0 to
 *	1, where 0 means the left edge of the text and 1 means the right
 *	edge.  If report is non-zero, then the interp's result isn't modified
 *	directly, but instead a script is evaluated in interp to report
 *	the new horizontal scroll position to the scrollbar (if the scroll
 *	position hasn't changed then no script is invoked).
d5849 6
a5854 6
    Tcl_Interp *interp;			/* If "report" is FALSE, string
					 * describing visible range gets
					 * stored in the interp's result. */
    TkText *textPtr;			/* Information about text widget. */
    int report;				/* Non-zero means report info to
					 * scrollbar if it has changed. */
d5860 1
a5860 1
    
d5881 1
a5881 1
	FP_EQUAL_SCALE(last,  dInfoPtr->xScrollLast,  dInfoPtr->maxLength)) {
d5889 1
d5897 1
a5897 1
		"\n    (horizontal scrolling command executed by text)");
d5908 13
a5920 14
 *	How many pixels are there between the absolute top of the
 *	widget and the top of the given DLine.
 *	
 *	While this function will work for any valid DLine, it is
 *	only ever called when dlPtr is the first display
 *	line in the widget (by 'GetYView').  This means that
 *	usually this function is a very quick calculation, since
 *	it can use the pre-calculated linked-list of DLines for
 *	height information.
 *	
 *	The only situation where this breaks down is if dlPtr's logical
 *	line wraps enough times to fill the text widget's current view
 *	-- in this case we won't have enough dlPtrs in the linked list
 *	to be able to subtract off what we want.
d5924 4
a5927 4
 *	
 *	This value has a valid range between '0' (the very top of the
 *	widget) and the number of pixels in the total widget minus the
 *	pixel-height of the last line.
d5934 1
a5934 1
    
d5937 3
a5939 3
    TkText *textPtr;			/* Information about text widget. */
    DLine *dlPtr;                       /* Information about the layout
                                         * of a given index */
d5942 5
a5946 5
    /* 
     * Get the pixel count to the top of dlPtr's logical line.  The
     * rest of the function is then concerned with updating 'count'
     * for any difference between the top of the logical line and
     * the display line.
d5948 1
d5950 7
a5956 7
   
    /* 
     * For the common case where this dlPtr is also the start of the
     * logical line, we can return right away.  Note the implicit
     * assumption here that the start of a logical line is always the
     * start of a display line (if the 'elide won't elide first newline'
     * bug is fixed, this will no longer necessarily be true).
d5958 1
d5960 1
a5960 1
        return count;
d5963 11
a5973 11
    /* 
     * Add on the logical line's height to reach one pixel beyond the
     * bottom of the logical line.  And then subtract off the heights of
     * all the display lines from dlPtr to the end of its logical line.
     * 
     * A different approach would be to lay things out from the start of
     * the logical line until we reach dlPtr, but since none of those are
     * pre-calculated, it'll usually take a lot longer.  (But there are
     * cases where it would be more efficient: say if we're on the second
     * of 1000 wrapped lines all from a single logical line -- but that
     * sort of optimization is left for the future).
d5975 1
d5981 6
a5986 7
	    /* 
	     * We've run out of pre-calculated display lines, so we
	     * have to lay them out ourselves until the end of the
	     * logical line.  Here's where we could be clever and ask:
	     * what's faster, to layout all lines from here to line-end,
	     * or all lines from the original dlPtr to the line-start?
	     * We just assume the former.
d5988 1
d5991 1
d5993 2
a5994 2
		TkTextIndexForwBytes(textPtr, &dlPtr->index, 
				     dlPtr->byteCount, &index);
d6002 1
a6002 1
		
d6007 3
a6009 3
		     * Debugging is enabled, so keep a log of all the
		     * lines whose height was recalculated.  The test
		     * suite uses this information.
d6013 1
a6013 1
		    Tcl_SetVar2(textPtr->interp, "tk_textHeightCalc", 
d6025 1
a6025 1
    
d6034 3
a6036 3
 *	This procedure computes the fractions that indicate what's
 *	visible in a text window and, optionally, evaluates a
 *	Tcl script to report them to the text's associated scrollbar.
d6039 7
a6045 8
 *	If report is zero, then the interp's result is filled in with
 *	two real numbers separated by a space, giving the position of
 *	the top and bottom of the window as fractions from 0 to 1, where
 *	0 means the beginning of the text and 1 means the end.  If
 *	report is non-zero, then the interp's result isn't modified directly,
 *	but a script is evaluated in interp to report the new scroll
 *	position to the scrollbar (if the scroll position hasn't changed
 *	then no script is invoked).
d6052 1
a6052 1
    
d6055 6
a6060 6
    Tcl_Interp *interp;			/* If "report" is FALSE, string
					 * describing visible range gets
					 * stored in the interp's result. */
    TkText *textPtr;			/* Information about text widget. */
    int report;				/* Non-zero means report info to
					 * scrollbar if it has changed. */
d6067 1
a6067 1
    
d6074 2
a6075 3
    totalPixels = TkBTreeNumPixels(textPtr->sharedTextPtr->tree,
				   textPtr);
    
d6080 4
a6083 5
	/* 
	 * Get the pixel count for the first visible pixel of the
	 * first visible line.  If the first visible line is only
	 * partially visible, then we use 'topPixelOffset' to get the
	 * difference.
d6085 1
d6089 5
a6093 5
	/* 
	 * Add on the total number of visible pixels to get the count to
	 * one pixel _past_ the last visible pixel.  This is how the
	 * 'yview' command is documented, and also explains why we are
	 * dividing by 'totalPixels' and not 'totalPixels-1'.
d6095 1
d6103 3
a6105 3
		 * This much of the last line is not visible, so don't
		 * count these pixels.  Since we've reached the bottom
		 * of the window, we break out of the loop.
d6107 1
d6116 1
a6116 1
	
d6118 24
a6141 25
	    /* 
	     * It can be possible, if we do not update each line's
	     * pixelHeight cache when we lay out individual DLines that
	     * the count generated here is more up-to-date than that
	     * maintained by the BTree.  In such a case, the best we can
	     * do here is to fix up 'count' and continue, which might
	     * result in small, temporary perturbations to the size of
	     * the scrollbar.  This is basically harmless, but in a
	     * perfect world we would not have this problem.
	     * 
	     * For debugging purposes, if anyone wishes to improve the
	     * text widget further, the following 'panic' can be
	     * activated.  In principle it should be possible to ensure
	     * the BTree is always at least as up to date as the display,
	     * so in the future we might be able to leave the 'panic' in
	     * permanently when we believe we have resolved the cache
	     * synchronisation issue.
	     * 
	     * However, to achieve that goal would, I think, require a
	     * fairly substantial refactorisation of the code in this
	     * file so that there is much more obvious and explicit
	     * coordination between calls to LayoutDLine and updating of
	     * each TkTextLine's pixelHeight.  The complicated bit is
	     * that LayoutDLine deals with individual display lines, but
	     * pixelHeight is for a logical line.
d6143 2
a6144 1
    #if 0
d6148 1
a6148 1
    #endif
d6151 1
a6151 1
	
d6162 1
d6167 1
d6173 1
d6181 1
a6181 1
		    "\n    (vertical scrolling command executed by text)");
d6192 2
a6193 3
 *	This procedure is called to update the vertical scrollbar
 *	asychronously as the pixel height calculations progress for
 *	lines in the widget.
d6199 2
a6200 2
 *	See 'GetYView'. In particular the scrollbar position and size
 *	may be changed.
d6210 1
a6210 1
    
d6216 1
a6216 1
    
d6227 2
a6228 2
 *	This procedure is called to find the DLine corresponding to a
 *	given text index.
d6231 3
a6233 4
 *	The return value is a pointer to the first DLine found in the
 *	list headed by dlPtr that displays information at or after the
 *	specified position.  If there is no such line in the list then
 *	NULL is returned.
d6243 2
a6244 2
    register DLine *dlPtr;	/* Pointer to first in list of DLines
				 * to search. */
d6257 1
d6273 1
d6275 2
a6276 3
	 * VMD: some concern here as to whether this logic,
	 * or the caller's logic will work well with 
	 * partial peer widgets.
d6278 1
d6292 1
a6292 1
    while (indexPtr->byteIndex >= (dlPtr->index.byteIndex + dlPtr->byteCount)) {
d6306 2
a6307 2
 *	Given an (x,y) coordinate on the screen, find the location of
 *	the character closest to that location.
d6310 2
a6311 2
 *	The index at *indexPtr is modified to refer to the character
 *	on the display that is closest to (x,y).
d6324 6
a6329 7
    TkTextIndex *indexPtr;	/* This index gets filled in with the
				 * index of the character nearest to (x,y). */
    int *nearest;               /* If non-NULL then gets set to 0 if
                                 * (x,y) is actually over the returned 
                                 * index, and 1 if it is just nearby
                                 * (e.g. if x,y is on the border of the
                                 * widget). */
d6336 2
a6337 2
     * Make sure that all of the layout information about what's
     * displayed where on the screen is up-to-date.
d6345 3
a6347 3
     * If the coordinates are above the top of the window, then adjust
     * them to refer to the upper-right corner of the window.  If they're
     * off to one side or the other, then adjust to the closest side.
d6363 1
a6363 1
    
d6375 1
a6375 1
	for (dlPtr = validDlPtr = dInfoPtr->dLinePtr; 
d6381 2
a6382 2
		 * Y-coordinate is off the bottom of the displayed text.
		 * Use the last character on the last line.
d6392 1
a6392 1
        
d6405 4
a6408 4
 *	Given an x coordinate in a display line, find the index of
 *	the character closest to that location.
 *	
 *	This is effectively the opposite of DlineXOfIndex. 
d6411 2
a6412 2
 *	The index at *indexPtr is modified to refer to the character
 *	on the display line that is closest to x.
d6423 2
a6424 2
    DLine *dlPtr;		/* Display information for this
                 		 * display line. */
d6427 2
a6428 2
    TkTextIndex *indexPtr;	/* This index gets filled in with the
				 * index of the character nearest to x. */
d6434 4
a6437 4
     * Scan through the line's chunks to find the one that contains
     * the desired x-coordinate.  Before doing this, translate the
     * x-coordinate from the coordinate system of the window to the
     * coordinate system of the line (to take account of x-scrolling).
d6445 3
a6447 3
	/* 
	 * This may occur if everything is elided, or if we're simply
	 * already at the beginning of the line.
d6449 1
d6454 5
a6458 6
	/* 
	 * Note that this forward then backward movement of the index
	 * can be problematic at the end of the buffer (we can't move
	 * forward, and then when we move backward, we do, leading to
	 * the wrong position).  Hence when x == 0 we take special
	 * action above.
d6460 6
a6465 2
	if (TkTextIndexForwBytes(NULL, indexPtr, chunkPtr->numBytes, indexPtr)) {
	    /* We've reached the end of the text */
d6476 4
a6479 4
     * If the chunk has more than one byte in it, ask it which
     * character is at the desired location.  In this case we
     * can manipulate 'indexPtr->byteIndex' directly, because
     * we know we're staying inside a single logical line.
d6493 4
a6496 4
 *	beginning of the display line, not taking into account any
 *	information about the window, scrolling etc.)  on the display
 *	line starting with the given index, adjust that index to refer to
 *	the object under the x coordinate.
d6510 5
a6514 5
    int x;                      /* The x coordinate for which we want
                                 * the index */
    TkTextIndex *indexPtr;	/* Index of display line start, which
                          	 * will be adjusted to the index under the
                          	 * given x coordinate. */
d6517 1
a6517 1
    DlineIndexOfX(textPtr, dlPtr, x + textPtr->dInfoPtr->x 
d6527 8
a6534 4
 *	Given a relative byte index on a given display line (i.e. the
 *	number of byte indices from the beginning of the given display
 *	line), find the x coordinate of that index within the abstract
 *	display line, without adjusting for the x-scroll state of the
a6536 5
 *	This is effectively the opposite of DlineIndexOfX. 
 *	
 *	NB. The 'byteIndex' is relative to the display line, NOT the
 *	logical line.
 *
d6549 4
a6552 4
    DLine *dlPtr;		/* Display information for this
				 * display line. */
    int byteIndex;              /* The byte index for which we want the
                                 * coordinate. */
d6556 1
a6556 1
    
d6560 2
a6561 2
     * Scan through the line's chunks to find the one that contains
     * the desired byte index.
d6568 1
d6584 1
a6584 1
    
d6593 2
a6594 2
 *	Given an index, find the bounding box of the screen area
 *	occupied by that character.
d6597 4
a6600 5
 *	Zero is returned if the character is on the screen.  -1
 *	means the character isn't on the screen.  If the return value
 *	is 0, then the bounding box of the part of the character that's
 *	visible on the screen is returned to *xPtr, *yPtr, *widthPtr,
 *	and *heightPtr.
d6611 2
a6612 2
    CONST TkTextIndex *indexPtr;/* Index of character whose bounding
				 * box is desired. */
d6616 4
a6619 5
    int *charWidthPtr;          /* If the 'character' isn't really a
                                 * character (e.g. end of a line) and 
                                 * therefore takes up a very large
                                 * width, this is used to return a
                                 * smaller width */
d6644 1
a6644 2
     * Find the chunk within the line that contains the desired
     * index.
d6659 4
a6662 5
     * Call a chunk-specific procedure to find the horizontal range of
     * the character within the chunk, then fill in the vertical range.
     * The x-coordinate returned by bboxProc is a coordinate within a
     * line, not a coordinate on the screen.  Translate it to reflect
     * horizontal scrolling.
d6665 1
a6665 1
    (*chunkPtr->bboxProc)(textPtr, chunkPtr, byteIndex, 
d6671 1
a6671 1
    if ((byteIndex == (chunkPtr->numBytes - 1)) && (chunkPtr->nextPtr == NULL)) {
d6673 2
a6674 2
	 * Last character in display line.  Give it all the space up to
	 * the line.
d6712 2
a6713 2
 *	Given an index, return information about the display line
 *	containing that character.
d6716 4
a6719 4
 *	Zero is returned if the character is on the screen.  -1
 *	means the character isn't on the screen.  If the return value
 *	is 0, then information is returned in the variables pointed
 *	to by xPtr, yPtr, widthPtr, heightPtr, and basePtr.
d6730 2
a6731 2
    CONST TkTextIndex *indexPtr;/* Index of character whose bounding
				 * box is desired. */
d6775 1
d6780 15
a6794 16
    TkTextDispChunk *chunkPtr;		/* Chunk containing desired char. */
    int index;				/* Index of desired character within
					 * the chunk. */
    int y;				/* Topmost pixel in area allocated
					 * for this line. */
    int lineHeight;			/* Height of line, in pixels. */
    int baseline;			/* Location of line's baseline, in
					 * pixels measured down from y. */
    int *xPtr, *yPtr;			/* Gets filled in with coords of
					 * character's upper-left pixel. 
					 * X-coord is in same coordinate
					 * system as chunkPtr->x. */
    int *widthPtr;			/* Gets filled in with width of
					 * character, in pixels. */
    int *heightPtr;			/* Gets filled in with height of
					 * character, in pixels. */
d6804 1
d6807 3
a6809 3
    TkTextDispChunk *chunkPtr;		/* Chunk containing desired coord. */
    int x;				/* X-coordinate, in same coordinate
					 * system as chunkPtr->x. */
d6819 1
a6819 1
 *	This procedure is the "layoutProc" for character segments.
d6822 6
a6827 7
 *	If there is something to display for the chunk then a
 *	non-zero value is returned and the fields of chunkPtr
 *	will be filled in (see the declaration of TkTextDispChunk
 *	in tkText.h for details).  If zero is returned it means
 *	that no characters from this chunk fit in the window.
 *	If -1 is returned it means that this segment just doesn't
 *	need to be displayed (never happens for text).
d6830 1
a6830 2
 *	Memory is allocated to hold additional information about
 *	the chunk.
d6846 2
a6847 2
    int maxBytes;		/* Chunk must not include more than this
				 * many characters. */
d6850 3
a6852 3
    TkWrapMode wrapMode;	/* How to handle line wrapping: 
                        	 * TEXT_WRAPMODE_CHAR, * TEXT_WRAPMODE_NONE, 
                        	 * or TEXT_WRAPMODE_WORD. */
d6854 3
a6856 3
				/* Structure to fill in with information
				 * about this chunk.  The x field has already
				 * been set by the caller. */
d6867 8
a6874 8
     * Include the next character, even though it won't fit completely,
     * if any of the following is true:
     *   (a) the chunk contains no characters and the display line contains
     *	     no characters yet (i.e. the line isn't wide enough to hold
     *	     even a single character).
     *   (b) at least one pixel of the character is visible, we haven't
     *	     already exceeded the character limit, and the next character
     *	     is a white space character.
d6879 2
a6880 2
    bytesThatFit = MeasureChars(tkfont, p, maxBytes, chunkPtr->x, maxX,
				&nextX);
d6884 1
a6884 1
	    
d6891 3
a6893 3
	     * Space characters are funny, in that they are considered
	     * to fit if there is at least one pixel of space left on the
	     * line.  Just give the space character whatever space is left.
d6911 1
a6911 1
	
d6915 3
a6917 3
     * Fill in the chunk structure and allocate and initialize a
     * CharInfo structure.  If the last character is a newline
     * then don't bother to display it.
d6940 3
a6942 4
     * Compute a break location.  If we're in word wrap mode, a
     * break can occur after any space character, or at the end of
     * the chunk if the next segment (ignoring those with zero size)
     * is not a character segment.
d6975 2
a6976 2
 *	This procedure is called to display a character chunk on
 *	the screen or in an off-screen pixmap.
d6990 12
a7001 14
    TkTextDispChunk *chunkPtr;		/* Chunk that is to be drawn. */
    int x;				/* X-position in dst at which to
					 * draw this chunk (may differ from
					 * the x-position in the chunk because
					 * of scrolling). */
    int y;				/* Y-position at which to draw this
					 * chunk in dst. */
    int height;				/* Total height of line. */
    int baseline;			/* Offset of baseline from y. */
    Display *display;			/* Display to use for drawing. */
    Drawable dst;			/* Pixmap or window in which to draw
					 * chunk. */
    int screenY;			/* Y-coordinate in text window that
					 * corresponds to y. */
d7020 5
a7024 5
     * If the text sticks out way to the left of the window, skip
     * over the characters that aren't in the visible part of the
     * window.  This is essential if x is very negative (such as
     * less than 32K);  otherwise overflow problems will occur
     * in servers that use 16-bit arithmetic, like X.
d7038 2
a7039 2
    if (!sValuePtr->elide && (ciPtr->numBytes > offsetBytes) 
      && (stylePtr->fgGC != None)) {
d7056 1
a7056 1
	    
d7072 3
a7074 3
 *	This procedure is called when a character chunk is no
 *	longer going to be displayed.  It frees up resources
 *	that were allocated to display the chunk.
d7087 2
a7088 3
    TkText *textPtr;			/* Overall information about text
					 * widget. */
    TkTextDispChunk *chunkPtr;		/* Chunk that is about to be freed. */
d7100 2
a7101 2
 *	This procedure is called to determine which character in
 *	a character chunk lies over a given x-coordinate.
d7104 2
a7105 2
 *	The return value is the index *within the chunk* of the
 *	character that covers the position given by "x".
d7115 3
a7117 3
    TkTextDispChunk *chunkPtr;		/* Chunk containing desired coord. */
    int x;				/* X-coordinate, in same coordinate
					 * system as chunkPtr->x. */
d7123 1
a7123 2
	    chunkPtr->numBytes - 1, chunkPtr->x, x, &endX);
						/* CHAR OFFSET */
d7131 2
a7132 2
 *	This procedure is called to compute the bounding box of
 *	the area occupied by a single character.
d7135 6
a7140 7
 *	There is no return value.  *xPtr and *yPtr are filled in
 *	with the coordinates of the upper left corner of the
 *	character, and *widthPtr and *heightPtr are filled in with
 *	the dimensions of the character in pixels.  Note:  not all
 *	of the returned bbox is necessarily visible on the screen
 *	(the rightmost part might be off-screen to the right,
 *	and the bottommost part might be off-screen to the bottom).
d7152 15
a7166 16
    TkTextDispChunk *chunkPtr;		/* Chunk containing desired char. */
    int byteIndex;			/* Byte offset of desired character
					 * within the chunk. */
    int y;				/* Topmost pixel in area allocated
					 * for this line. */
    int lineHeight;			/* Height of line, in pixels. */
    int baseline;			/* Location of line's baseline, in
					 * pixels measured down from y. */
    int *xPtr, *yPtr;			/* Gets filled in with coords of
					 * character's upper-left pixel. 
					 * X-coord is in same coordinate
					 * system as chunkPtr->x. */
    int *widthPtr;			/* Gets filled in with width of
					 * character, in pixels. */
    int *heightPtr;			/* Gets filled in with height of
					 * character, in pixels. */
d7177 3
a7179 3
	 * This situation only happens if the last character in a line
	 * is a space character, in which case it absorbs all of the
	 * extra space in the line (see TkTextCharLayoutProc).
d7186 2
a7187 2
	 * The desired character is a tab character that terminates a
	 * chunk;  give it all the space left in the chunk.
d7192 1
a7192 1
	MeasureChars(chunkPtr->stylePtr->sValuePtr->tkfont, 
d7209 2
a7210 2
 *	This procedure is called to move a series of chunks right
 *	in order to align them with a tab stop.
d7216 4
a7219 4
 *	The width of chunkPtr gets adjusted so that it absorbs the
 *	extra space due to the tab.  The x locations in all the chunks
 *	after chunkPtr are adjusted rightward to align with the tab
 *	stop given by tabArrayPtr and index.
d7226 9
a7234 12
    TkText *textPtr;			/* Information about the text widget as
					 * a whole. */
    TkTextTabArray *tabArrayPtr;	/* Information about the tab stops
					 * that apply to this line.  May be
					 * NULL to indicate default tabbing
					 * (every 8 chars). */
    int index;				/* Index of current tab stop. */
    TkTextDispChunk *chunkPtr;		/* Chunk whose last character is
					 * the tab;  the following chunks
					 * contain information to be shifted
					 * right. */

d7252 2
a7253 2
     * If no tab information has been given, do the usual thing:
     * round up to the next boundary of 8 average-sized characters.
d7272 2
a7273 2
	 * Ran out of tab stops;  compute a tab position by extrapolating
	 * from the last two tab positions.
d7276 3
a7278 3
	tabX = (int) (tabArrayPtr->lastTab 
		      + (index + 1 - tabArrayPtr->numTabs) 
		      * tabArrayPtr->tabIncrement + 0.5);
d7289 2
a7290 2
	 * Compute the width of all the information in the tab group,
	 * then use it to pick a desired location.
d7307 3
a7309 3
     * Must be numeric alignment.  Search through the text to be
     * tabbed, looking for the last , or . before the first character
     * that isn't a number, comma, period, or sign.
d7335 2
a7336 1
    endOfNumber:
d7347 1
a7347 1
	 * There wasn't a decimal point.  Right justify the text.
d7349 1
a7349 1
    
d7359 3
a7361 4
     * Shift all of the chunks to the right so that the left edge is
     * at the desired location, then expand the chunk containing the
     * tab.  Be sure that the tab occupies at least the width of a
     * space character.
d7364 1
a7364 1
    update:
d7382 2
a7383 2
 *	This returns an estimate of the amount of white space that will
 *	be consumed by a tab.
d7386 8
a7393 9
 *	The return value is the minimum number of pixels that will
 *	be occupied by the next tab of tabArrayPtr, assuming that
 *	the current position on the line is x and the end of the
 *	line is maxX.  The 'next tab' is determined by a combination
 *	of the current position (x) which it must be equal to or
 *	beyond, and the tab count in indexPtr.
 *	
 *	For numeric tabs, this is a conservative estimate.  The return
 *	value is always >= 0.
d7403 11
a7413 13
    TkText *textPtr;			/* Information about the text widget as
					 * a whole. */
    TkTextTabArray *tabArrayPtr;	/* Information about the tab stops
					 * that apply to this line.  NULL
					 * means use default tabbing (every
					 * 8 chars.) */
    int *indexPtr;			/* Contains index of previous tab 
                  			 * stop, will be updated to
                  			 * reflect the number of stops
                  			 * used. */
    int x;				/* Current x-location in line. */
    int maxX;				/* X-location of pixel just past the
					 * right edge of the line. */
d7417 1
a7417 1
    
d7422 2
a7423 1
	/* 
d7426 1
d7432 2
a7433 3
	/* 
	 * We were given the count before this tab, so increment it
	 * first.
d7435 1
d7442 1
a7442 2
	     * Ran out of tab stops; compute a tab position by
	     * extrapolating.
d7444 4
a7447 3
	    tabX = (int) (tabArrayPtr->lastTab 
			  + (index + 1 - tabArrayPtr->numTabs) 
			  * tabArrayPtr->tabIncrement + 0.5);
d7450 4
a7453 3
	/* 
	 * If this tab stop is before the current x position, then we
	 * must obviously continue until we reach the text tab stop.
d7456 2
a7457 2
    
    /* 
d7460 1
d7462 1
a7462 1
    
d7465 2
a7466 3
	 * Be very careful in the arithmetic below, because maxX may
	 * be the largest positive number:  watch out for integer
	 * overflow.
d7482 3
a7484 4
     * Note: this treats NUMERIC alignment the same as LEFT
     * alignment, which is somewhat conservative.  However, it's
     * pretty tricky at this point to figure out exactly where
     * the damn decimal point will be.
d7493 1
a7493 1
    done:
d7506 4
a7509 4
 *	Given the current position, determine where the next default
 *	tab stop would be located.  This procedure is called when the
 *	current chunk in the text has no tabs defined and so the default
 *	tab spacing for the font should be used.
d7522 7
a7528 7
    Tk_Font tkfont;		/* Font in which chunk that contains tab
				 * stop will be drawn. */
    int x;			/* X-position in pixels where last
				 * character was drawn.  The next tab stop
				 * occurs somewhere after this location. */
    int tabOrigin;		/* The origin for tab stops.  May be
				 * non-zero if text has been scrolled. */
d7531 1
a7531 1
    
d7551 16
a7566 17
 *	Determine the number of characters from the string that will fit
 *	in the given horizontal span.  The measurement is done under the
 *	assumption that Tk_DrawChars will be used to actually display
 *	the characters.
 *
 *	If tabs are encountered in the string, they will be expanded
 *	to the next tab stop.
 *
 *	If a newline is encountered in the string, the line will be
 *	broken at that point.
 *
 * Results:
 *	The return value is the number of bytes from source
 *	that fit in the span given by startX and maxX.  *nextXPtr
 *	is filled in with the x-coordinate at which the first
 *	character that didn't fit would be drawn, if it were to
 *	be drawn.
d7577 2
a7578 2
    CONST char *source;		/* Characters to be displayed.  Need not
				 * be NULL-terminated. */
d7581 2
a7582 2
    int startX;			/* X-position at which first character will
				 * be drawn. */
d7585 2
a7586 2
    int *nextXPtr;		/* Return x-position of terminating
				 * character here. */
d7611 1
a7611 1
	 * string).  Process characters between start and special.
d7617 1
a7617 1
	start += Tk_MeasureChars(tkfont, start, special - start, 
d7645 1
a7645 1
 *	This procedure is invoked to parse "xview" and "yview" scrolling
d7647 4
a7650 4
 *	("moveto" or "scroll" options).  It extends the public
 *	Tk_GetScrollInfoObj function with the addition of "pixels" as a
 *	valid unit alongside "pages" and "units".  It is a shame the core
 *	API isn't more flexible in this regard.
d7653 5
a7657 6
 *	The return value is either TKTEXT_SCROLL_MOVETO,
 *	TKTEXT_SCROLL_PAGES, TKTEXT_SCROLL_UNITS, TKTEXT_SCROLL_PIXELS or
 *	TKTEXT_SCROLL_ERROR. This indicates whether the command was
 *	successfully parsed and what form the command took.  If
 *	TKTEXT_SCROLL_MOVETO, *dblPtr is filled in with the desired
 *	position; if TKTEXT_SCROLL_PAGES, TKTEXT_SCROLL_PIXELS or
d7659 2
a7660 3
 *	pages/pixels/lines to move (may be negative); if
 *	TKTEXT_SCROLL_ERROR, the interp's result contains an error
 *	message.
d7670 8
a7677 10
    Tcl_Interp *interp;			/* Used for error reporting. */
    TkText *textPtr;			/* Information about the text
                			 * widget. */
    int objc;				/* # arguments for command. */
    Tcl_Obj *CONST objv[];		/* Arguments for command. */
    double *dblPtr;			/* Filled in with argument "moveto"
					 * option, if any. */
    int *intPtr;			/* Filled in with number of pages
					 * or lines or pixels to scroll, 
					 * if any. */
d7697 1
a7697 1
	
d7699 1
a7699 1
	    Tcl_WrongNumArgs(interp, 2, objv, 
d7716 1
a7716 1
	    if (Tk_GetPixelsFromObj(interp, textPtr->tkwin, objv[3], 
d7736 8
a7743 1

@


1.49
log
@        * generic/tkTextDisp.c (GetXView):      Improved numerical precision
        of calculation of [.t xview] return values.
        * tests/textDisp.test:  Match greater precisions of [.t xview] and
        [.t yview] values in tests.
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.48 2005/03/15 14:07:47 vincentdarley Exp $
d1542 1
d1749 1
a1749 1
	      && (lineHeight > TkBTreeLinePixelCount(textPtr, 
d1752 3
a1754 3
		 * The logical line height we just calculated is actually
		 * larger than the currently cached height of the
		 * text line.  That is fine (the text line heights
d1756 5
a1760 3
		 * update the cached height so that any counts made
		 * with DLine pointers do not exceed counts made
		 * through the BTree.  
@


1.48
log
@fix for display lines bug and panic
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.47 2005/02/14 23:00:44 vincentdarley Exp $
d5811 2
a5812 2
	last = first + ((double) (dInfoPtr->maxX - dInfoPtr->x))
		/ dInfoPtr->maxLength;
@


1.47
log
@fix to newline eliding in text widget
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.46 2005/01/11 16:00:14 vincentdarley Exp $
d1807 5
d1926 4
a1929 3
	     * We've filled in the space we wanted to, and we
	     * need to store any extra overlap we've just
	     * created for the top line.
d1931 7
a1937 1
	    if (lineNum >= 0) {
d1939 8
a1946 4
		if (spaceLeft > 0 ||
		  dInfoPtr->newTopPixelOffset >= dInfoPtr->dLinePtr->height) {
		    /* Bad situation */
		    Tcl_Panic("Pixel height problem while laying out text widget");
@


1.46
log
@fix to text widget bug
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.45 2004/10/26 12:51:45 dkf Exp $
d168 2
d513 1
a513 1
			    int *byteCountPtr));
d1036 2
a1037 1

d1121 1
a1121 1
					     dlPtr->index.linePtr, 0);
d1152 1
a1152 1

d1159 22
a1180 4
    for (byteOffset = curIndex.byteIndex, segPtr = curIndex.linePtr->segPtr;
	 (byteOffset > 0) && (byteOffset >= segPtr->size);
	 byteOffset -= segPtr->size, segPtr = segPtr->nextPtr) {
	/* Empty loop body. */
d1192 4
a1195 5
	 * elided still gets laid out into a DLine, but with zero height.
	 * This isn't particularly a problem, but it does seem somewhat
	 * unnecessary.  If/when we fix [Tk Bug 443848] (see below)
	 * then we will probably have to remove such zero height DLines
	 * too. 
d1218 16
d1236 2
a1237 1
	    }
d1390 3
a1392 5
		 * until the newline.  Currently this forces a new line
		 * anyway (i.e. even though the newline is elided it
		 * still takes effect).  This is because the code 
		 * currently doesn't allow two or more logical lines to
		 * appear on the same display line. [Tk Bug #443848]
d1394 8
d1408 12
a1419 1
	Tcl_Panic("LayoutDLine couldn't place any characters on a line");
d1761 1
a1761 1
					 lineHeight);
d1883 1
a1883 1
					     pixelHeight);
d2853 1
a2853 1
						     0, NULL);
d2891 1
a2891 1
						     pixelHeight, indexPtr);
d3139 1
a3139 5
 *	be the last elided index on the display line.  (NB. This also
 *	highlights a current bug in the text widget that we cannot
 *	place two logical lines on a single display line -- even
 *	though the newline in this case is elided, it still causes
 *	a line break to be shown).
d3180 25
d3207 1
d3211 3
a3213 1

d3215 5
a3219 2
	     * 'byteCount' goes up to the beginning of the next line,
	     * so equality here says we need one more line
d3221 4
a3224 1
	    if (index.byteIndex + byteCount > indexPtr->byteIndex) {
a3235 1
		indexPtr->byteIndex = index.byteIndex;
d3241 3
a3243 1
		    indexPtr->byteIndex += byteCount - sizeof(char);
d3249 1
a3249 1
	    TkTextIndexForwBytes(textPtr, &index, byteCount, &index);
d3276 4
d3293 1
a3293 1
CalculateDisplayLineHeight(textPtr, indexPtr, byteCountPtr)
d3300 4
d3309 15
d3334 3
d3402 1
a3402 1
	height = CalculateDisplayLineHeight(textPtr, &index, &bytes);
d3448 1
a3448 1
TkTextUpdateOneLine(textPtr, linePtr, pixelHeight, indexPtr)
d3458 8
a3465 1
                             * up to which we have already calculated. */
d3468 2
a3469 1
    int displayLines, partialCalc;
a3477 3
	partialCalc = 0; 
    } else {
	partialCalc = 1; 
d3488 1
d3491 1
a3491 1
	int bytes, height;
d3501 2
a3502 1
	height = CalculateDisplayLineHeight(textPtr, indexPtr, &bytes);
d3509 2
d3515 18
a3532 4
	if (indexPtr->linePtr != linePtr) {
	    /* 
	     * If we reached the end of the logical line, then
	     * either way we don't have a partial calculation.
d3534 8
a3541 2
	    partialCalc = 0;
	    break;
d3543 1
a3543 1
	if (partialCalc && displayLines > 50) {
d3548 5
d3559 7
d3573 27
a3599 6
	/* 
	 * Also cancel any partial line height calculation state.
	 */
	textPtr->dInfoPtr->metricEpoch = -1;

	if (TkBTreeLinePixelCount(textPtr, linePtr) == pixelHeight) {
d3609 2
a3610 2
    pixelHeight = TkBTreeAdjustPixelHeight(textPtr,  
					   linePtr, pixelHeight);
d4837 1
a4837 1
    lineHeight = CalculateDisplayLineHeight(textPtr, indexPtr, NULL);
d5315 1
a5315 1
		&textPtr->topIndex, NULL) - dInfoPtr->topPixelOffset;
d6369 1
a6369 1
    if (chunkPtr == NULL) {
d6371 2
a6372 1
	 * This may occur if everything is elided
d6377 12
a6388 3
    for (; x >= (chunkPtr->x + chunkPtr->width);
	    indexPtr->byteIndex += chunkPtr->numBytes,
	    chunkPtr = chunkPtr->nextPtr) {
a6389 1
	    indexPtr->byteIndex += chunkPtr->numBytes;
d6393 1
d6398 3
a6400 1
     * character is at the desired location.
@


1.45
log
@Stop compiler warning
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.44 2004/09/10 12:13:41 vincentdarley Exp $
d350 6
a355 1
                                 * cancelled if things change once more. */
d2678 7
a2684 1
    if (lineNum == dInfoPtr->lastMetricUpdateLine) {
d2771 5
a2775 1
	    if (lineNum == endLine) {
d2847 4
d2858 3
@


1.44
log
@text widget 'peer' subcommand -- TIP#169 implementation
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.43 2004/06/08 20:28:19 dgp Exp $
d1785 1
@


1.43
log
@Silence compiler warnings
@
text
@d6 3
a8 3
 *	of text widgets.  (Well, strictly, each TkTextLine caches its
 *	last observed pixel height, but that information is
 *	calculated here).
d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.42 2004/06/07 16:23:52 vincentdarley Exp $
d428 2
a429 1
static void		CharBboxProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
d433 2
a434 1
static void		CharDisplayProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
d449 2
a450 1
static void		ElideBboxProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
d494 3
d499 5
d521 1
a521 1
 * Result values returned by TextGetScrollInfo:
d701 1
a701 1
    tagPtrs = TkBTreeGetTags(indexPtr, &numTags);
d720 1
d722 2
a723 1

d725 2
a726 1
	 * On Windows and Mac, we need to skip the selection tag if
a729 1
#ifndef ALWAYS_SHOW_SELECTION
d731 4
a734 1
	    continue;
a735 1
#endif
d737 2
a738 2
	if ((tagPtr->border != NULL) && (tagPtr->priority > borderPrio)) {
	    styleValues.border = tagPtr->border;
d1108 1
a1108 1
		dlPtr->index.linePtr->pixelCalculationEpoch 
d1111 3
a1113 2
		if (dlPtr->index.linePtr->pixelHeight != 0) {
		    TkBTreeAdjustPixelHeight(dlPtr->index.linePtr, 0);
d1551 2
a1552 2
    lastLinePtr = TkBTreeFindLine(textPtr->tree,
	    TkBTreeNumLines(textPtr->tree));
d1602 1
a1602 1
		TkTextPrintIndex(&index, string);
d1665 1
a1665 1
	TkTextIndexForwBytes(&index, dlPtr->byteCount, &index);
d1689 2
a1690 1
	      && (lineHeight > prevPtr->index.linePtr->pixelHeight)) {
d1700 3
a1702 1
		TkBTreeAdjustPixelHeight(prevPtr->index.linePtr, lineHeight);
d1711 1
a1711 1
		prevPtr->index.linePtr->pixelCalculationEpoch = 
d1779 13
a1791 5
	    lineNum = TkBTreeLineIndex(dInfoPtr->dLinePtr->index.linePtr);
	    bytesToCount = dInfoPtr->dLinePtr->index.byteIndex;
	    if (bytesToCount == 0) {
		bytesToCount = INT_MAX;
		lineNum--;
d1796 2
a1797 1
		index.linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
d1809 1
a1809 1
		    TkTextIndexForwBytes(&index, dlPtr->byteCount, &index);
d1819 5
a1823 3
		if (pixelHeight > lowestPtr->index.linePtr->pixelHeight) {
		    TkBTreeAdjustPixelHeight(lowestPtr->index.linePtr, 
			    pixelHeight);
d1829 1
a1829 1
			lowestPtr->index.linePtr->pixelCalculationEpoch = 
d1848 1
a1848 1
			TkTextPrintIndex(&dlPtr->index, string);
d1868 1
a1868 1
			dInfoPtr->newTopPixelOffset >= dInfoPtr->dLinePtr->height) {
d1881 10
a1890 6
	textPtr->topIndex = dInfoPtr->dLinePtr->index;
	y = dInfoPtr->y - dInfoPtr->newTopPixelOffset;
	for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
		dlPtr = dlPtr->nextPtr) {
	    if (y > dInfoPtr->maxY) {
		Tcl_Panic("Added too many new lines in UpdateDisplayInfo");
a1891 2
	    dlPtr->y = y;
	    y += dlPtr->height; 
d1908 2
a1909 6
    if ((dlPtr->flags & HAS_3D_BORDER) && !(dlPtr->flags & TOP_LINE)) {
	dlPtr->flags |= OLD_Y_INVALID;
    }
    while (1) {
	if ((dlPtr->flags & TOP_LINE) && (dlPtr != dInfoPtr->dLinePtr)
		&& (dlPtr->flags & HAS_3D_BORDER)) {
d1912 16
a1927 16
	/*
	 * If the old top-line was not completely showing (i.e. the
	 * pixelOffset is non-zero) and is no longer the top-line, then
	 * we must re-draw it.
	 */
	if ((dlPtr->flags & TOP_LINE) 
		&& dInfoPtr->topPixelOffset!=0 && dlPtr!=dInfoPtr->dLinePtr) {
	    dlPtr->flags |= OLD_Y_INVALID;
	}
	if ((dlPtr->flags & BOTTOM_LINE) && (dlPtr->nextPtr != NULL)
		&& (dlPtr->flags & HAS_3D_BORDER)) {
	    dlPtr->flags |= OLD_Y_INVALID;
	}
	if (dlPtr->nextPtr == NULL) {
	    if ((dlPtr->flags & HAS_3D_BORDER)
		    && !(dlPtr->flags & BOTTOM_LINE)) {
d1930 11
a1940 3
	    dlPtr->flags &= ~TOP_LINE;
	    dlPtr->flags |= BOTTOM_LINE;
	    break;
d1942 2
a1943 2
	dlPtr->flags &= ~(TOP_LINE|BOTTOM_LINE);
	dlPtr = dlPtr->nextPtr;
d1945 1
a1945 3
    dInfoPtr->dLinePtr->flags |= TOP_LINE;
    dInfoPtr->topPixelOffset = dInfoPtr->newTopPixelOffset;

d2054 1
a2054 1
	    TkTextPrintIndex(&firstPtr->index, string);
d2153 1
a2153 1
		(*chunkPtr->displayProc)(chunkPtr, x, dlPtr->spaceAbove,
d2198 1
a2198 1
	    (*chunkPtr->displayProc)(chunkPtr, x, dlPtr->spaceAbove,
d2735 6
a2740 1
    int totalLines = TkBTreeNumLines(textPtr->tree);
d2746 2
a2747 1
	    linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
d2753 2
a2754 1
		linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
d2757 1
a2757 1
		linePtr = TkBTreeNextLine(linePtr);
d2779 1
a2779 1
	    if (linePtr->pixelCalculationEpoch 
d2797 2
a2798 1
		    if (textPtr->dInfoPtr->metricEpoch == textPtr->stateEpoch 
d2809 1
a2809 1
			index.tree = textPtr->tree;
d2832 2
a2833 1
			    textPtr->dInfoPtr->metricEpoch = textPtr->stateEpoch;
d2835 2
a2836 1
			textPtr->dInfoPtr->metricPixelHeight = linePtr->pixelHeight;
d2879 1
a2879 1
 * TkTextInvalidateLineMetrics --
d2901 3
a2903 1
TkTextInvalidateLineMetrics(textPtr, linePtr, lineCount, action)
d2912 21
d2939 1
a2939 1
	fromLine = TkBTreeLineIndex(linePtr);
d2945 1
a2945 1
	linePtr->pixelCalculationEpoch = 0;
d2947 1
a2947 1
	    linePtr = TkBTreeNextLine(linePtr);
d2949 1
a2949 1
		linePtr->pixelCalculationEpoch = 0;
d3141 1
a3141 1
	    TkTextIndexForwBytes(&index, byteCount, &index);
d3239 1
a3239 1
    pixelHeight = TkBTreePixels(indexPtr->linePtr);
d3252 1
a3252 1
    index.tree = textPtr->tree;
d3330 1
a3330 1
	index.tree = textPtr->tree;
d3368 1
a3368 1
	if (TkTextIndexForwBytes(indexPtr, bytes, indexPtr)) {
d3396 2
a3397 1
	linePtr->pixelCalculationEpoch = textPtr->dInfoPtr->lineMetricUpdateEpoch;
d3403 1
a3403 1
	if (linePtr->pixelHeight == pixelHeight) {
d3413 2
a3414 1
    pixelHeight = TkBTreeAdjustPixelHeight(linePtr, pixelHeight);
d3419 1
a3419 1
	if (TkBTreeNextLine(linePtr) == NULL) {
d3423 1
a3423 1
	sprintf(buffer, "%d %d", TkBTreeLineIndex(linePtr), pixelHeight);
d3785 1
a3785 1
		    TkTextPrintIndex(&dlPtr->index, string);
d3836 2
a3837 1
		    TkTextEmbWinDisplayProc(chunkPtr, x, dlPtr->spaceAbove,
d4059 1
a4059 1
 * TkTextChanged --
d4084 4
a4087 2
TkTextChanged(textPtr, index1Ptr, index2Ptr)
    TkText *textPtr;		  /* Widget record for text widget. */
d4093 20
d4170 1
a4170 1
 * TkTextRedrawTag --
d4187 26
a4212 1
TkTextRedrawTag(textPtr, index1Ptr, index2Ptr, tagPtr, withTag)
d4246 1
a4246 1
	    lineCount = TkBTreeNumLines(textPtr->tree);
d4249 1
a4249 1
	    lineCount = TkBTreeLineIndex(endLine);
d4255 1
a4255 1
	    lineCount -= TkBTreeLineIndex(startLine);
d4257 1
a4257 1
	TkTextInvalidateLineMetrics(textPtr, startLine, lineCount, 
d4279 3
a4281 2
	index2Ptr = TkTextMakeByteIndex(textPtr->tree,
		TkBTreeNumLines(textPtr->tree), 0, &endOfText);
d4501 6
d4565 3
a4567 3
    lineIndex = TkBTreeLineIndex(indexPtr->linePtr);
    if (lineIndex == TkBTreeNumLines(indexPtr->tree)) {
	TkTextIndexBackChars(NULL,indexPtr, 1, &rounded, COUNT_INDICES);
d4735 2
a4736 2
    lastLinePtr =
	    TkBTreeFindLine(textPtr->tree, TkBTreeNumLines(textPtr->tree));
d4747 1
a4747 1
	TkTextIndexForwBytes(srcPtr, dlPtr->byteCount, &loop);
d4808 1
a4808 1
    for (lineNum = TkBTreeLineIndex(srcPtr->linePtr); lineNum >= 0;
d4820 1
a4820 1
	index.linePtr = TkBTreeFindLine(srcPtr->tree, lineNum);
d4827 1
a4827 1
	    TkTextIndexForwBytes(&index, dlPtr->byteCount, &index);
d4867 1
a4867 1
    TkTextMakeByteIndex(textPtr->tree, 0, 0, dstPtr);
d4919 3
a4921 2
    if (TkBTreeLineIndex(index.linePtr) == TkBTreeNumLines(index.tree)) {
	TkTextIndexBackChars(NULL,&index, 1, &index, COUNT_INDICES);
d4968 1
a4968 1
	(*chunkPtr->bboxProc)(chunkPtr, byteCount,
d5132 2
a5133 2
	lastLinePtr = TkBTreeFindLine(textPtr->tree,
		TkBTreeNumLines(textPtr->tree));
d5139 2
a5140 1
	    TkTextIndexForwBytes(&textPtr->topIndex, dlPtr->byteCount, &new);
d5203 1
a5203 1
	index.tree = textPtr->tree;
d5205 1
a5205 1
	for (lineNum = TkBTreeLineIndex(textPtr->topIndex.linePtr);
d5207 2
a5208 1
	    index.linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
d5215 1
a5215 1
		TkTextIndexForwBytes(&index, dlPtr->byteCount, &index);
d5246 2
a5247 1
	TkTextMakeByteIndex(textPtr->tree, 0, 0, &textPtr->topIndex);
d5255 2
a5256 2
	lastLinePtr = TkBTreeFindLine(textPtr->tree,
		TkBTreeNumLines(textPtr->tree));
d5261 2
a5262 1
	    TkTextIndexForwBytes(&textPtr->topIndex, dlPtr->byteCount, &new);
d5341 2
a5342 1
	    TkTextMakeByteIndex(textPtr->tree, lineNum, 0, &index);
d5369 2
a5370 1
	    int numPixels = TkBTreeNumPixels(textPtr->tree);
d5683 1
a5683 1
    int count = TkBTreePixels(linePtr);
d5708 1
a5708 1
    count += linePtr->pixelHeight;
d5724 2
a5725 1
		TkTextIndexForwBytes(&dlPtr->index, dlPtr->byteCount, &index);
d5743 1
a5743 1
		    TkTextPrintIndex(&index, string);
d5806 2
a5807 1
    totalPixels = TkBTreeNumPixels(textPtr->tree);
d5982 2
a5983 2
    if (TkBTreeLineIndex(indexPtr->linePtr)
	    < TkBTreeLineIndex(dlPtr->index.linePtr)) {
d6002 6
a6007 1
	linePtr = TkBTreeNextLine(linePtr);
d6097 8
a6104 1
    for (dlPtr = validDlPtr = dInfoPtr->dLinePtr; 
d6107 6
a6112 6
	if (dlPtr->chunkPtr !=NULL) validDlPtr = dlPtr;
	if (dlPtr->nextPtr == NULL) {
	    /*
	     * Y-coordinate is off the bottom of the displayed text.
	     * Use the last character on the last line.
	     */
d6114 4
a6117 3
	    x = dInfoPtr->maxX - 1;
	    nearby = 1;
	    break;
d6119 1
d6121 1
a6121 2
    if (dlPtr->chunkPtr == NULL) dlPtr = validDlPtr;
    
d6282 1
a6282 1
	    (*chunkPtr->bboxProc)(chunkPtr, byteIndex,
d6382 2
a6383 1
    (*chunkPtr->bboxProc)(chunkPtr, byteIndex, dlPtr->y + dlPtr->spaceAbove,
d6493 1
a6493 1
ElideBboxProc(chunkPtr, index, y, lineHeight, baseline, xPtr, yPtr,
d6495 1
d6707 2
a6708 1
CharDisplayProc(chunkPtr, x, y, height, baseline, display, dst, screenY)
d6873 1
a6873 1
CharBboxProc(chunkPtr, byteIndex, y, lineHeight, baseline, xPtr, yPtr,
d6875 1
d7470 1
@


1.42
log
@text widget partial line height calculations fix
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.41 2004/06/04 10:51:18 vincentdarley Exp $
a6122 1
    TkTextIndex index;
a6131 1
    index = dlPtr->index;
@


1.41
log
@text widget fixes
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.40 2004/01/13 02:06:00 davygrvy Exp $
d342 9
a350 3
    TkTextIndex metricIndex;
    int metricPixelHeight;
    int metricEpoch;
d2629 9
d2651 1
d2727 10
d2761 6
d2796 5
d3330 4
d3348 1
a3348 1
	char buffer[TCL_INTEGER_SPACE + 1];
d3354 1
a3354 1
	sprintf(buffer, "%d", pixelHeight);
d3356 1
a3356 1
		    buffer, TCL_GLOBAL_ONLY);
d4380 1
@


1.40
log
@	* generic/tk3d.c:	All uses of 'panic' (the macro) changed
	* generic/tkBind.c:	to 'Tcl_Panic' (the function).  The #define
	* generic/tkBitmap.c:	of panic in tcl.h clearly states it is
	* generic/tkCanvArc.c:	deprecated in the comments.
	* generic/tkCanvBmap.c: [Tcl Patch 865264]
	* generic/tkCanvImg.c:
	* generic/tkCanvLine.c:
	* generic/tkCanvPoly.c:
	* generic/tkCanvText.c:
	* generic/tkCanvWind.c:
	* generic/tkColor.c:
	* generic/tkConfig.c:
	* generic/tkCursor.c:
	* generic/tkError.c:
	* generic/tkEvent.c:
	* generic/tkFocus.c:
	* generic/tkFont.c:
	* generic/tkFrame.c:
	* generic/tkGC.c:
	* generic/tkGrid.c:
	* generic/tkImgBmap.c:
	* generic/tkImgPhoto.c:
	* generic/tkImgUtil.c:
	* generic/tkMenu.c:
	* generic/tkObj.c:
	* generic/tkPack.c:
	* generic/tkPlace.c:
	* generic/tkRectOval.c:
	* generic/tkSelect.c:
	* generic/tkText.c:
	* generic/tkTextBTree.c:
	* generic/tkTextDisp.c:
	* generic/tkTextImage.c:
	* generic/tkTextIndex.c:
	* generic/tkTextMark.c:
	* generic/tkTextWind.c:
	* generic/tkVisual.c:
	* generic/tkWindow.c:
	* mac/tkMacAppInit.c:
	* mac/tkMacAppearanceStubs.c:
	* mac/tkMacButton.c:
	* mac/tkMacDraw.c:
	* mac/tkMacEmbed.c:
	* mac/tkMacFont.c:
	* mac/tkMacInit.c:
	* mac/tkMacMenus.c:
	* mac/tkMacPort.h:
	* mac/tkMacSubwindows.c:
	* mac/tkMacWm.c:
	* mac/tkMacXStubs.c:
	* macosx/tkMacOSXEmbed.c:
	* macosx/tkMacOSXFont.c:
	* macosx/tkMacOSXMenus.c:
	* macosx/tkMacOSXNotify.c:
	* macosx/tkMacOSXPort.h:
	* macosx/tkMacOSXSubwindows.c:
	* macosx/tkMacOSXWm.c:
	* macosx/tkMacOSXXStubs.c:
	* unix/tkUnix3d.c:
	* unix/tkUnixColor.c:
	* unix/tkUnixEmbed.c:
	* unix/tkUnixEvent.c:
	* unix/tkUnixFocus.c:
	* unix/tkUnixFont.c:
	* unix/tkUnixSelect.c:
	* unix/tkUnixSend.c:
	* unix/tkUnixWm.c:
	* win/tkWin3d.c:
	* win/tkWinButton.c:
	* win/tkWinColor.c:
	* win/tkWinDialog.c:
	* win/tkWinDraw.c:
	* win/tkWinEmbed.c:
	* win/tkWinFont.c:
	* win/tkWinPixmap.c:
	* win/tkWinPointer.c:
	* win/tkWinScrlbr.c:
	* win/tkWinWm.c:
	* win/tkWinX.c:
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.39 2004/01/07 16:28:23 vincentdarley Exp $
d4405 1
a4405 1
    if (pickPlace == -2) {
d4414 1
a4414 1
    if (pickPlace != -1) {
d5198 1
d5213 11
a5223 1
	    fraction *= (numPixels - 1);
d5230 1
a5230 2
	    pixels = TkTextMakePixelIndex(textPtr,
		    (int) (0.5 + fraction), &index);
d5486 4
d5649 3
a5651 1
	 * the last visible pixel.
d5708 1
a5708 1
	last = ((double) (count))/((double)totalPixels);
@


1.39
log
@fix to text widget yview bug
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.38 2003/12/15 11:51:06 vincentdarley Exp $
d1053 1
a1053 1
				panic("Bad tag priority being toggled off");
d1338 1
a1338 1
	panic("LayoutDLine couldn't place any characters on a line");
d1834 1
a1834 1
		    panic("Pixel height problem while laying out text widget");
d1850 1
a1850 1
		panic("Added too many new lines in UpdateDisplayInfo");
d3310 1
a3310 1
	    panic("Mustn't ever update line height of last artificial line");
d5685 3
a5687 1
	    panic("Counted more pixels (%d) than expected (%d) total pixels in text widget scroll bar calculation.", count, totalPixels);
d5813 1
a5813 1
	    panic("FindDLine reached end of text");
@


1.38
log
@text widget more extensive documentation, and two small code improvements
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.37 2003/12/05 17:19:06 vincentdarley Exp $
d5196 10
a5205 1
	case TKTEXT_SCROLL_MOVETO:
d5212 1
a5212 1
	    fraction *= (TkBTreeNumPixels(textPtr->tree)-1);
d5223 1
@


1.37
log
@performance of lines containing 10000+ characters
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.36 2003/12/04 12:28:37 vincentdarley Exp $
d28 47
d465 1
a465 1
static int              GetPixelCount _ANSI_ARGS_((TkText *textPtr, 
d1080 15
d1098 2
a1099 1

d5447 1
a5447 1
 * GetPixelCount --
d5452 11
a5462 6
 *	This is only ever called when dlPtr is the first display
 *	line in the widget (by 'GetYView').  We have to be careful
 *	if dlPtr's logical line wraps enough times to fill the
 *	text widget's current view -- in this case we won't have
 *	enough dlPtrs in the linked list to be able to subtract off
 *	what we want.
d5474 1
a5474 1
GetPixelCount(textPtr, dlPtr)
d5481 4
a5484 1
     * Get the pixel count to the top of dlPtr's logical line.
d5490 4
a5493 1
     * logical line, we can return right away.
d5501 2
a5502 2
     * bottom of the line.  And then subtract off the heights of all the
     * display lines from dlPtr to the end of its logical line.  
d5620 1
a5620 1
	count = GetPixelCount(textPtr, dlPtr);
@


1.36
log
@fix to text widget tabs and xy-scroll-command
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.35 2003/11/21 18:51:18 vincentdarley Exp $
d295 3
d518 4
a521 1

d2651 50
a2700 6
		/* 
		 * Update the line and update the counter, counting
		 * 10 for each line we actually re-layout.
		 */
		TkTextUpdateOneLine(textPtr, linePtr);
		count += 10;
d3147 1
a3147 1
TkTextUpdateOneLine(textPtr, linePtr)
d3151 7
d3160 1
a3160 1
    int pixelHeight, displayLines;
d3162 11
a3172 4
    index.tree = textPtr->tree;
    index.linePtr = linePtr;
    index.byteIndex = 0;
    index.textPtr = NULL;
a3180 1
    pixelHeight = 0;
d3194 1
a3194 1
	height = CalculateDisplayLineHeight(textPtr, &index, &bytes);
d3201 1
a3201 1
	if (TkTextIndexForwBytes(&index, bytes, &index)) {
d3205 14
a3218 1
	if (index.linePtr != linePtr) {
d3223 7
a3229 6
    /* 
     * Mark the logical line as being up to date (caution: it isn't
     * yet up to date, that will happen in TkBTreeAdjustPixelHeight
     * just below).
     */
    linePtr->pixelCalculationEpoch = textPtr->dInfoPtr->lineMetricUpdateEpoch;
d3231 3
a3233 2
    if (linePtr->pixelHeight == pixelHeight) {
	return displayLines;
d3235 1
a3235 1

d3237 3
a3239 3
     * We now use the resulting 'pixelHeight' to refer to the
     * height of the entire widget, which may be used just below
     * for reporting/debugging purposes
@


1.35
log
@correct handling of interpolated tabs using fractional pixel widths
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.34 2003/11/21 17:29:13 vincentdarley Exp $
d421 1
a421 1
			    int maxX, int tabOrigin, int *nextXPtr));
d5288 7
a5294 8
	listObj = Tcl_NewStringObj(textPtr->xScrollCmd, -1);
	code = Tcl_ListObjAppendElement(interp, listObj, 
					Tcl_NewDoubleObj(first));
	if (code == TCL_OK) {
	    Tcl_ListObjAppendElement(interp, listObj, Tcl_NewDoubleObj(last));
	    code = Tcl_EvalObjEx(interp, listObj, 
				 TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL);
	}
d5545 7
a5551 8
        listObj = Tcl_NewStringObj(textPtr->yScrollCmd, -1);
	code = Tcl_ListObjAppendElement(interp, listObj, 
		Tcl_NewDoubleObj(first));
	if (code == TCL_OK) {
	    Tcl_ListObjAppendElement(interp, listObj, Tcl_NewDoubleObj(last));
	    code = Tcl_EvalObjEx(interp, listObj, 
		    TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL);
	}
d6229 2
a6230 2
    bytesThatFit = MeasureChars(tkfont, p, maxBytes, chunkPtr->x, maxX, 0,
	    &nextX);
d6236 1
a6236 1
		    chunkPtr->x, -1, 0, &nextX);
d6383 1
a6383 1
		ciPtr->numBytes, x, 0, x - chunkPtr->x, &offsetX);
d6476 1
a6476 1
	    chunkPtr->numBytes - 1, chunkPtr->x, x, 0, &endX);
d6528 1
a6528 1
	    byteIndex, chunkPtr->x, -1, 0, xPtr);
d6548 1
a6548 1
		ciPtr->chars + byteIndex, 1, *xPtr, -1, 0, widthPtr);
d6699 1
a6699 1
		ciPtr->chars, decimal, decimalChunkPtr->x, -1, 0, &curX);
d6724 1
a6724 1
    MeasureChars(textPtr->tkfont, " ", 1, 0, -1, 0, &spaceWidth);
d6853 1
a6853 1
    MeasureChars(textPtr->tkfont, " ", 1, 0, -1, 0, &spaceWidth);
d6912 1
a6912 1
 *	assumption that Tk_DrawTextLayout will be used to actually display
d6916 1
a6916 1
 *	to the next tab stop, unless the TK_IGNORE_TABS flag is specified.
d6919 1
a6919 2
 *	broken at that point, unless the TK_NEWSLINES_NOT_SPECIAL flag
 *	is specified.  
d6935 1
a6935 1
MeasureChars(tkfont, source, maxBytes, startX, maxX, tabOrigin, nextXPtr)
a6944 2
    int tabOrigin;		/* X-location that serves as "origin" for
				 * tab stops. */
d6977 2
a6978 2
	start += Tk_MeasureChars(tkfont, start, special - start, maxX - curX,
		0, &width);
@


1.34
log
@fix to two test suite bugs
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.33 2003/11/16 14:13:09 vincentdarley Exp $
d6599 1
a6599 1
    int tabX, prev, spaceWidth;
d6636 3
a6638 5
	if (tabArrayPtr->numTabs > 1) {
	    prev = tabArrayPtr->tabs[tabArrayPtr->numTabs-2].location;
	} else {
	    prev = 0;
	}
a6639 3
	tabX = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location
		+ (index + 1 - tabArrayPtr->numTabs)
		* (tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location - prev);
d6778 1
a6778 1
    int tabX, prev, result, index, spaceWidth;
d6794 1
a6794 1
	 * We were given the count before this tabs, so increment it
d6803 2
a6804 2
	     * Ran out of tab stops;  compute a tab position by extrapolating
	     * from the last two tab positions.
d6806 3
a6808 9
	    
	    if (tabArrayPtr->numTabs > 1) {
		prev = tabArrayPtr->tabs[tabArrayPtr->numTabs-2].location;
	    } else {
		prev = 0;
	    }
	    tabX = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location
	      + (index + 1 - tabArrayPtr->numTabs)
	      * (tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location - prev);
@


1.33
log
@documentation and cleanup
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.32 2003/11/15 16:57:57 vincentdarley Exp $
d359 6
a364 5
static int numRedisplays;	/* Number of calls to DisplayText. */
static int linesRedrawn;	/* Number of calls to DisplayDLine. */
static int numCopies;		/* Number of calls to XCopyArea to copy part
				 * of the screen. */

d437 1
a437 1
static int              TextCalculateDisplayLineHeight _ANSI_ARGS_((
d1934 16
a1949 1
    if (action == DLINE_UNLINK) {
d2538 6
d2932 1
a2932 1
 * TextCalculateDisplayLineHeight --
d2964 1
a2964 1
TextCalculateDisplayLineHeight(textPtr, indexPtr, byteCountPtr)
d3007 1
a3007 1
 *	Just those of 'TextCalculateDisplayLineHeight'.
d3051 1
a3051 1
	height = TextCalculateDisplayLineHeight(textPtr, &index, &bytes);
d3090 1
a3090 1
 *	function 'TextCalculateDisplayLineHeight' for its side
d3131 1
a3131 1
	height = TextCalculateDisplayLineHeight(textPtr, &index, &bytes);
d4334 1
a4334 1
    lineHeight = TextCalculateDisplayLineHeight(textPtr, indexPtr, NULL);
d4810 1
a4810 1
	offset -= TextCalculateDisplayLineHeight(textPtr, 
d5384 15
@


1.32
log
@single line wraps to whole display fix
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.31 2003/11/15 12:47:15 vincentdarley Exp $
d64 1
a64 1
    int elide;			/* Non-zero means draw text */
d5183 5
a5197 5
    dInfoPtr->flags |= DINFO_OUT_OF_DATE;
    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
	dInfoPtr->flags |= REDRAW_PENDING;
	Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
    }
d5314 1
a5314 2
     * Get the pixel count of one pixel beyond the 
     * botton of the given line.
d5319 2
a5320 2
     * For the common case where this dlPtr is also the start of 
     * the logical line, we can return right away.
a5324 2
    
    count += linePtr->pixelHeight;
d5327 10
a5336 2
     * Now we have to subtract off the distance between the top of this
     * dlPtr and the next logical line.
d5338 2
d5343 8
a5350 1
	    /* We've run out of lines */
d5352 1
a5352 1
	    DLine *dlPrev = NULL;
d5355 3
a5360 4
		if (dlPrev != NULL) {
		    FreeDLines(textPtr, dlPrev, (DLine *) NULL, 
			       DLINE_FREE_TEMP);
		}
a5361 1
		dlPrev = dlPtr;
d5363 1
a5363 4
	    }
	    if (dlPrev != NULL) {
		FreeDLines(textPtr, dlPrev, (DLine *) NULL, 
			   DLINE_FREE_TEMP);
d6749 2
d6756 1
a6756 1
	*indexPtr = (*indexPtr)+1;
a6759 1
    index = *indexPtr;
@


1.31
log
@fixed tab interpretation inconsistency
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.30 2003/11/15 02:49:36 vincentdarley Exp $
d5289 7
d5317 11
a5327 1
    int count = TkBTreePixels(linePtr) + linePtr->pixelHeight;
d5335 26
a5360 2
	dlPtr = dlPtr->nextPtr;
    } while (dlPtr != NULL && (dlPtr->index.linePtr == linePtr));
d5418 1
a5418 1
	 * Get the pixel count for the first display visible pixel of the
@


1.30
log
@cleanup
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.29 2003/11/15 02:33:50 vincentdarley Exp $
d432 2
a433 2
			    TkTextTabArray *tabArrayPtr, int index, int x,
			    int maxX));
a1241 1
	    tabIndex++;
d1243 1
a1243 1
	    tabSize = SizeOfTab(textPtr, tabArrayPtr, tabIndex, x, maxX);
d6665 1
a6665 1
 *	be occupied by the index'th tab of tabArrayPtr, assuming that
d6667 6
a6672 2
 *	line is maxX.  For numeric tabs, this is a conservative
 *	estimate.  The return value is always >= 0.
d6681 1
a6681 1
SizeOfTab(textPtr, tabArrayPtr, index, x, maxX)
d6688 5
a6692 3
    int index;				/* Index of current tab stop. */
    int x;				/* Current x-location in line. Only
					 * used if tabArrayPtr == NULL. */
d6696 1
a6696 1
    int tabX, prev, result, spaceWidth;
d6698 1
a6698 1

d6701 4
d6707 6
a6712 7
    if (index < tabArrayPtr->numTabs) {
	tabX = tabArrayPtr->tabs[index].location;
	alignment = tabArrayPtr->tabs[index].alignment;
    } else {
	/*
	 * Ran out of tab stops;  compute a tab position by extrapolating
	 * from the last two tab positions.
d6714 4
a6717 3

	if (tabArrayPtr->numTabs > 1) {
	    prev = tabArrayPtr->tabs[tabArrayPtr->numTabs-2].location;
d6719 14
a6732 1
	    prev = 0;
d6734 11
a6744 5
	tabX = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location
		+ (index + 1 - tabArrayPtr->numTabs)
		* (tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location - prev);
	alignment = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].alignment;
    }
@


1.29
log
@fix two more old text widget bugs
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.28 2003/11/14 17:21:03 dkf Exp $
d4701 1
a4701 1
    int type, charsPerPage, count, newOffset;
a4712 1
    newOffset = dInfoPtr->newXPixelOffset;
d4725 2
a4726 1
	    newOffset = (int) (fraction * dInfoPtr->maxLength + 0.5);
d4728 5
a4732 4
	case TKTEXT_SCROLL_PAGES:
	    charsPerPage = (dInfoPtr->maxX-dInfoPtr->x)/textPtr->charWidth - 2;
	    if (charsPerPage < 1) {
		charsPerPage = 1;
d4734 1
a4734 1
	    newOffset += charsPerPage * count * textPtr->charWidth;
d4736 1
d4738 1
a4738 1
	    newOffset += count * textPtr->charWidth;
d4741 1
a4741 1
	    newOffset += count;
d4744 1
a4744 1
    dInfoPtr->newXPixelOffset = newOffset;
d6905 6
a6910 6
 *	This procedure is invoked to parse "yview" scrolling commands for
 *	text widgets using the new scrolling command syntax ("moveto" or
 *	"scroll" options).  It extends the public Tk_GetScrollInfoObj
 *	function with the addition of "pixels" as a valid unit alongside
 *	"pages" and "units".  It is a shame the core API isn't more
 *	flexible in this regard.
@


1.28
log
@Fixes to prevent infinite event sequences in 'make test' on slow connections
Also a bit of whitespace/style police work.  [Bug 837300]
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.27 2003/11/12 17:19:18 vincentdarley Exp $
d235 3
a237 4
    int newXByteOffset;		/* Desired x scroll position, measured as the
				 * number of average-size characters off-screen
				 * to the left for a line with no left
				 * margin. */
d257 2
a258 3
    int scanMarkIndex;		/* Byte index of character that was at the
				 * left edge of the window when the scan
				 * started. */
d496 1
a496 1
    dInfoPtr->newXByteOffset = 0;
d503 1
a503 1
    dInfoPtr->scanMarkIndex = 0;
d1862 8
a1869 4
    maxOffset = (dInfoPtr->maxLength - (dInfoPtr->maxX - dInfoPtr->x)
	    + textPtr->charWidth - 1)/textPtr->charWidth;
    if (dInfoPtr->newXByteOffset > maxOffset) {
	dInfoPtr->newXByteOffset = maxOffset;
d1871 12
a1882 2
    if (dInfoPtr->newXByteOffset < 0) {
	dInfoPtr->newXByteOffset = 0;
d1884 1
a1884 1
    xPixelOffset = dInfoPtr->newXByteOffset * textPtr->charWidth;
d2704 1
a2704 1
	 * Invalid the height calculations of each line in the
d3938 1
a3938 1
		TK_TEXT_INVALIDATE_ONLY);
d4139 3
d4648 1
a4648 2
		dInfoPtr->newXByteOffset = (x - lineWidth/2)
			/ textPtr->charWidth;
d4650 1
a4650 2
		dInfoPtr->newXByteOffset -= ((-delta) + textPtr->charWidth - 1)
			/ textPtr->charWidth;
d4656 1
a4656 2
		    dInfoPtr->newXByteOffset = (x - lineWidth/2)
			    / textPtr->charWidth;
d4658 1
a4658 2
		    dInfoPtr->newXByteOffset +=
			     (delta+textPtr->charWidth-1) / textPtr->charWidth;
d4713 3
a4715 2
    newOffset = dInfoPtr->newXByteOffset;
    type = Tk_GetScrollInfoObj(interp, objc, objv, &fraction, &count);
d4717 1
a4717 1
	case TK_SCROLL_ERROR:
d4719 1
a4719 1
	case TK_SCROLL_MOVETO:
d4726 1
a4726 2
	    newOffset = (int) (((fraction * dInfoPtr->maxLength) 
				/ textPtr->charWidth) + 0.5);
d4728 1
a4728 1
	case TK_SCROLL_PAGES:
d4733 4
a4736 1
	    newOffset += charsPerPage * count;
d4738 1
a4738 1
	case TK_SCROLL_UNITS:
d4742 1
a4742 2

    dInfoPtr->newXByteOffset = newOffset;
d5122 1
a5122 1
    int c, x, y, totalScroll, newByte, maxByte, gain=10;
d5144 2
d5158 5
a5162 7
	newByte = dInfoPtr->scanMarkIndex + (gain*(dInfoPtr->scanMarkX - x))
		/ (textPtr->charWidth);
	maxByte = 1 + (dInfoPtr->maxLength - (dInfoPtr->maxX - dInfoPtr->x)
		+ textPtr->charWidth - 1)/textPtr->charWidth;
	if (newByte < 0) {
	    newByte = 0;
	    dInfoPtr->scanMarkIndex = 0;
d5164 3
a5166 3
	} else if (newByte > maxByte) {
	    newByte = maxByte;
	    dInfoPtr->scanMarkIndex = maxByte;
d5169 1
a5169 1
	dInfoPtr->newXByteOffset = newByte;
d5183 1
a5183 1
	dInfoPtr->scanMarkIndex = dInfoPtr->newXByteOffset;
@


1.27
log
@old tk text widget bugs fixed
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.26 2003/11/10 14:37:33 vincentdarley Exp $
d496 1
a496 1
				  &gcValues);
d520 1
a520 1
			AsyncUpdateLineMetrics, (ClientData) textPtr);
d523 1
a523 2
			AsyncUpdateYScrollbar, (ClientData) textPtr);

d1097 1
a1097 1
			^ segPtr->body.toggle.tagPtr->elide;
d1463 1
a1463 1
				  TkBTreeNumLines(textPtr->tree));
d1610 1
a1610 2
		TkBTreeAdjustPixelHeight(prevPtr->index.linePtr, 
					 lineHeight);
d1620 1
a1620 1
		                       dInfoPtr->lineMetricUpdateEpoch;
d1720 1
a1720 1
					     pixelHeight);
d1727 1
a1727 1
					   dInfoPtr->lineMetricUpdateEpoch;
d1764 2
a1765 2
		if (spaceLeft > 0 
		  || dInfoPtr->newTopPixelOffset >= dInfoPtr->dLinePtr->height) {
d1817 1
a1817 1
	  && (dInfoPtr->topPixelOffset != 0) && (dlPtr != dInfoPtr->dLinePtr)) {
d2515 1
a2515 1
    /* 
d2520 1
a2520 1
				      dInfoPtr->lastMetricUpdateLine, 256);
d2522 1
a2522 1
	/* 
d2539 1
a2539 1
			AsyncUpdateLineMetrics, (ClientData) textPtr);
d2627 1
d2790 1
a2790 1
			AsyncUpdateLineMetrics, (ClientData) textPtr);
d3911 8
d3923 1
d3925 2
a3926 7
	if (index2Ptr == NULL) {
	    endLine = NULL;
	} else {
	    endLine = index2Ptr->linePtr;
	}
	TkTextInvalidateLineMetrics(textPtr, startLine, endLine - startLine, 
				    TK_TEXT_INVALIDATE_ONLY);
d4167 1
a4167 1
			AsyncUpdateLineMetrics, (ClientData) textPtr);
d4319 1
a4319 1
		  &tmpIndex, &overlap);
d4335 1
a4335 1
		  - textPtr->charHeight/2, &tmpIndex, &overlap);
d4350 1
a4350 1
	      &dInfoPtr->newTopPixelOffset);
d4392 2
a4393 2
    lastLinePtr = TkBTreeFindLine(textPtr->tree,
				  TkBTreeNumLines(textPtr->tree));
d4486 1
a4486 1
	} while ((bytesToCount > 0) && (index.linePtr == dlPtr->index.linePtr));
d4634 1
a4634 1
		    / textPtr->charWidth;
d4637 1
a4637 1
		    / textPtr->charWidth;
d4644 1
a4644 1
			/ textPtr->charWidth;
d4646 2
a4647 2
		    dInfoPtr->newXByteOffset += (delta + textPtr->charWidth - 1)
			/ textPtr->charWidth;
d4718 1
a4718 2
	    charsPerPage = ((dInfoPtr->maxX - dInfoPtr->x) / textPtr->charWidth)
		    - 2;
d4777 1
a4777 1
		  &textPtr->topIndex, &dInfoPtr->newTopPixelOffset);
d4795 1
a4795 2
	    TkTextIndexForwBytes(&textPtr->topIndex, dlPtr->byteCount,
		    &new);
d4981 1
a4981 1
	    && (strncmp(Tcl_GetString(objv[2]), 
d5015 1
a5015 2
    type = TextGetScrollInfoObj(interp, textPtr, objc, objv, 
				&fraction, &count);
d5033 2
a5034 2
	    pixels = TkTextMakePixelIndex(textPtr, (int) (0.5 + fraction), 
					  &index);
d5114 3
a5116 3
			 Tcl_GetString(objv[0]), " scan mark x y\" or \"",
			 Tcl_GetString(objv[0]), " scan dragto x y ?gain?\"", 
			 (char *) NULL);
d5130 1
a5130 1
    if ((c == 'd') && (strncmp(Tcl_GetString(objv[2]), "dragto", length) == 0)) {
d5169 1
a5169 2
    } else if ((c == 'm') 
	       && (strncmp(Tcl_GetString(objv[2]), "mark", length) == 0)) {
d5369 1
a5369 2
	first = ((double) (count + dInfoPtr->topPixelOffset))
	                                  /((double)totalPixels);
d5423 1
a5423 6
	    char buffer[200];

	    sprintf(buffer, 
		    "Counted more pixels (%d) than expected (%d) total pixels in text widget scroll bar calculation.", 
		    count, totalPixels);
	    panic(buffer);
d5447 1
a5447 1
					Tcl_NewDoubleObj(first));
d5451 1
a5451 1
				 TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL);
d6023 3
d6050 4
a6053 2


d6284 1
a6284 1
	    ciPtr->numBytes, x, 0, x - chunkPtr->x, &offsetX);
d6966 1
a6966 1
				    intPtr) != TCL_OK) {
d6982 1
a6982 1
		     "\": must be moveto or scroll", (char *) NULL);
@


1.26
log
@handling of zero-pixel text widgets
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.25 2003/11/08 17:22:46 vincentdarley Exp $
d449 4
d520 1
a520 1
			TkTextAsyncUpdateLineMetrics, (ClientData) textPtr);
d523 1
a523 1
			TkTextUpdateYScrollbar, (ClientData) textPtr);
d2469 1
a2469 1
 * TkTextAsyncUpdateLineMetrics --
d2491 2
a2492 2
void
TkTextAsyncUpdateLineMetrics(clientData)
d2541 1
a2541 1
			TkTextAsyncUpdateLineMetrics, (ClientData) textPtr);
d2791 1
a2791 1
			TkTextAsyncUpdateLineMetrics, (ClientData) textPtr);
d3147 1
a3147 1
		TkTextUpdateYScrollbar, (ClientData) textPtr);
d4164 1
a4164 1
			TkTextAsyncUpdateLineMetrics, (ClientData) textPtr);
d5471 1
a5471 1
 * TkTextUpdateYScrollbar --
d5487 2
a5488 2
void
TkTextUpdateYScrollbar(clientData)
d5596 1
a5596 1
TkTextPixelIndex(textPtr, x, y, indexPtr)
d5602 5
d5610 1
d5630 1
d5634 1
d5638 1
d5640 1
a5640 1

d5656 1
d5662 4
@


1.25
log
@elide tag handling, once more
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.24 2003/11/07 15:36:26 vincentdarley Exp $
d5355 13
a5367 7
    /* 
     * Get the pixel count for the first display visible pixel of the
     * first visible line.  If the first visible line is only partially
     * visible, then we use 'topPixelOffset' to get the difference.
     */
    count = GetPixelCount(textPtr, dlPtr);
    first = ((double) (count + dInfoPtr->topPixelOffset))/((double)totalPixels);
d5369 7
a5375 7
    /* 
     * Add on the total number of visible pixels to get the count to
     * the last visible pixel.
     */
    while (1) {
	int extra;
	count += dlPtr->height;
d5377 42
a5418 6
	extra = dlPtr->y + dlPtr->height - dInfoPtr->maxY;
	if (extra > 0) {
	    /*
	     * This much of the last line is not visible, so don't
	     * count these pixels.  Since we've reached the bottom
	     * of the window, we break out of the loop.
d5420 9
a5428 2
	    count -= extra;
	    break;
d5430 2
a5431 4
	if (dlPtr->nextPtr == NULL) {
	    break;
	}
	dlPtr = dlPtr->nextPtr;
a5432 39
    
    if (count > totalPixels) {
	/* 
	 * It can be possible, if we do not update each line's
	 * pixelHeight cache when we lay out individual DLines that
	 * the count generated here is more up-to-date than that
	 * maintained by the BTree.  In such a case, the best we can
	 * do here is to fix up 'count' and continue, which might
	 * result in small, temporary perturbations to the size of
	 * the scrollbar.  This is basically harmless, but in a 
	 * perfect world we would not have this problem.
	 * 
	 * For debugging purposes, if anyone wishes to improve the text
	 * widget further, the following 'panic' can be activated.  In
	 * principle it should be possible to ensure the BTree is always
	 * at least as up to date as the display, so in the future we
	 * might be able to leave the 'panic' in permanently when we
	 * believe we have resolved the cache synchronisation issue.
	 * 
	 * However, to achieve that goal would, I think, require a 
	 * fairly substantial refactorisation of the code in this
	 * file so that there is much more obvious and explicit
	 * coordination between calls to LayoutDLine and updating
	 * of each TkTextLine's pixelHeight.  The complicated bit
	 * is that LayoutDLine deals with individual display lines,
	 * but pixelHeight is for a logical line.
	 */
#if 0
	char buffer[200];

	sprintf(buffer, 
		"Counted more pixels (%d) than expected (%d) total pixels in text widget scroll bar calculation.", 
		count, totalPixels);
	panic(buffer);
#endif
	count = totalPixels;
    }
    
    last = ((double) (count))/((double)totalPixels);
@


1.24
log
@better elide tag handling
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.23 2003/11/07 12:06:47 vincentdarley Exp $
d960 1
a960 3
	for (segPtr = indexPtr->linePtr->segPtr;
	     segPtr != NULL;
	     segPtr = segPtr->nextPtr) {
d978 21
a998 9
		if (tagPtr->elideString != NULL 
		  && (tagPtr->priority >= info.elidePriority)) {
		    elide = ((segPtr->typePtr == &tkTextToggleOffType)
			     ^ tagPtr->elide);
		    if (!elide && tagPtr->priority == info.elidePriority) {
			/* Find previous elide tag, if any */
			while (--info.elidePriority > 0) {
			    if (info.tagCnts[info.elidePriority] & 1) {
				break;
d1000 16
a1016 2
		    } else {
			info.elidePriority = tagPtr->priority;
d5250 2
a5251 1
	code = Tcl_ListObjAppendElement(interp, listObj, Tcl_NewDoubleObj(first));
@


1.23
log
@another elide/tkchat/tkTextDisp.c fix
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.22 2003/11/03 16:10:12 vincentdarley Exp $
d937 2
a938 1

d957 1
a957 1
    elide = TkTextIsElided(textPtr, indexPtr);		/* save a malloc */
a958 1
	int tagElidePriority = -1;
a976 1
		tagElidePriority = -1;
d980 13
a992 10
		if (tagPtr->elideString != NULL) {
		    /* 
		     * Only update the elide status if this tag has
		     * higher priority than any other we've so far
		     * seen at this index.
		     */
		    if (tagPtr->priority > tagElidePriority) {
			elide = ((segPtr->typePtr == &tkTextToggleOffType)
				 ^ tagPtr->elide);
			tagElidePriority = tagPtr->priority;
d4193 1
a4193 1
	TkTextIndexBackChars(indexPtr, 1, &rounded, COUNT_INDICES);
d4546 1
a4546 1
	TkTextIndexBackChars(&index, 1, &index, COUNT_INDICES);
d5674 1
a5674 1
	    TkTextIndexBackChars(indexPtr, 1, indexPtr, COUNT_INDICES);
@


1.22
log
@text widget and test suite fixes
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.21 2003/11/03 13:10:05 dkf Exp $
d958 1
d973 5
d979 13
a991 4
		    || (segPtr->typePtr == &tkTextToggleOnType)) {
		if (segPtr->body.toggle.tagPtr->elideString != NULL) {
		    elide = (segPtr->typePtr == &tkTextToggleOffType)
			^ segPtr->body.toggle.tagPtr->elide;
@


1.21
log
@Stop some complaints about signed/unsigned
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.20 2003/10/31 19:14:27 vincentdarley Exp $
d960 1
a960 1
	     elide && (segPtr != NULL);
d962 10
a971 6
	    if ((elidesize = segPtr->size) > 0) {
		maxBytes += elidesize;
		/*
		 * If have we have a tag toggle, there is a chance
		 * that invisibility state changed, so bail out
		 */
d5740 1
a5740 2
    TextDInfo *dInfoPtr = textPtr->dInfoPtr;
    register TkTextDispChunk *chunkPtr;
d5744 1
a5744 1
    if (byteIndex == 0) return 0;
@


1.20
log
@size_t vs int fix
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.19 2003/10/31 14:21:49 vincentdarley Exp $
d6865 1
a6865 1
    if ((c == 'm') && (strncmp(arg2, "moveto", length) == 0)) {
d6874 1
a6874 1
    } else if ((c == 's') && (strncmp(arg2, "scroll", length) == 0)) {
d6886 1
a6886 1
	if ((c == 'p') && (length == 1)) {
d6890 1
a6890 1
	} else if ((c == 'p') && (strncmp(arg4, "pages", argLen) == 0)) {
d6895 1
a6895 1
	} else if ((c == 'p') && (strncmp(arg4, "pixels", argLen) == 0)) {
d6901 1
a6901 1
	} else if ((c == 'u') && (strncmp(arg4, "units", argLen) == 0)) {
@


1.19
log
@text widget bug fix
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.18 2003/10/31 09:02:10 vincentdarley Exp $
d6860 1
a6860 1
    size_t length;
d6876 1
d6884 1
d6890 1
a6890 1
	} else if ((c == 'p') && (strncmp(arg4, "pages", length) == 0)) {
d6895 1
a6895 1
	} else if ((c == 'p') && (strncmp(arg4, "pixels", length) == 0)) {
d6901 1
a6901 1
	} else if ((c == 'u') && (strncmp(arg4, "units", length) == 0)) {
@


1.18
log
@TIP 155 implementation
@
text
@d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.17 2003/07/07 20:39:50 hobbs Exp $
d5639 10
a5648 1
    for (chunkPtr = dlPtr->chunkPtr; x >= (chunkPtr->x + chunkPtr->width);
@


1.17
log
@	* generic/tkTextDisp.c (DisplayText): correct use of textPtr data
	with respect to Tcl_Release time. [Bug #767009]
@
text
@d6 3
a8 1
 *	of text widgets.
d16 1
a16 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.16 2003/05/27 15:35:53 vincentdarley Exp $
d124 9
a132 5
    int oldY;			/* Y-position at which line currently
				 * appears on display.  -1 means line isn't
				 * currently visible on display and must be
				 * redrawn.  This is used to move lines by
				 * scrolling rather than re-drawing. */
d180 7
a186 1
 * IS_DISABLED -		This Dline cannot be edited.
d193 1
a193 1
#define IS_DISABLED    16
d204 4
d235 1
a235 1
    int newByteOffset;		/* Desired x scroll position, measured as the
d239 1
a239 1
    int curPixelOffset;		/* Actual x scroll position, measured as the
d262 1
a262 1
    int scanTotalScroll;	/* Total scrolling (in screen lines) that has
d283 22
d343 14
d407 1
a407 1
			    DLine *firstPtr, DLine *lastPtr, int unlink));
d411 1
a411 1
			    TkTextIndex *indexPtr));
d416 2
d419 1
a419 1
			    TkTextIndex *indexPtr));
d424 2
a425 2
			    TkTextIndex *srcPtr, int distance,
			    TkTextIndex *dstPtr));
d429 3
a431 1
static void		ScrollByLines _ANSI_ARGS_((TkText *textPtr,
d438 21
d492 1
a492 1
	    &gcValues);
d494 2
a495 2
    dInfoPtr->newByteOffset = 0;
    dInfoPtr->curPixelOffset = 0;
d503 1
a503 1
    dInfoPtr->scanTotalScroll = 0;
d507 15
d555 1
a555 1
    FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
d564 10
d598 1
a598 1
    TkTextIndex *indexPtr;	/* The character in the text for which
d867 16
d890 1
a890 1
    TkTextIndex *indexPtr;	/* Beginning of display line.  May not
d946 1
a946 1
    dlPtr->oldY = -1;
d951 1
a951 1
    dlPtr->flags = NEW_LAYOUT;
d1021 12
a1032 4
	 * Every line still gets at least one chunk due to expectations
	 * in the rest of the code, but we are able to skip elided portions
	 * of the line quickly.
	 * If current chunk is elided and last chunk was too, coalese
d1039 2
a1040 1
		breakByteOffset = lastChunkPtr->breakIndex = lastChunkPtr->numBytes;
d1120 6
a1125 2
	    /* don't free style here, as other code expects to be able to do that */
	    /*breakByteOffset =*/ chunkPtr->breakIndex = chunkPtr->numBytes = maxBytes;
d1129 1
a1129 1
	    /* would just like to point to canonical empty chunk */
d1136 2
a1137 2
	} else
	code = (*segPtr->typePtr->layoutProc)(textPtr, &curIndex, segPtr,
d1140 1
d1164 1
a1164 1
	if (chunkPtr->numBytes > 0) {
d1208 10
d1385 1
a1385 1
    int y, maxY, pixelOffset, maxOffset;
d1399 6
a1404 1
	FreeDLines(textPtr, dInfoPtr->dLinePtr, dlPtr, 1);
d1415 1
a1415 1
	    TkBTreeNumLines(textPtr->tree));
d1418 1
a1418 1
    y = dInfoPtr->y;
d1450 1
d1476 1
a1476 1
		    prevPtr->oldY = -1;
d1494 1
a1494 1
		    dlPtr->oldY = -1;
d1508 1
a1508 1
	    FreeDLines(textPtr, dlPtr, newPtr, 0);
d1525 3
d1546 1
a1546 1
		FreeDLines(textPtr, dlPtr, nextPtr, 0);
d1550 26
d1594 1
a1594 1
    FreeDLines(textPtr, dlPtr, (DLine *) NULL, 1);
d1601 4
d1609 21
a1629 2
	int lineNum, spaceLeft, bytesToCount;
	DLine *lowestPtr;
d1631 7
a1637 7
	/*
	 * Layout an entire text line (potentially > 1 display line),
	 * then link in as many display lines as fit without moving
	 * the bottom line out of the window.  Repeat this until
	 * all the extra space has been used up or we've reached the
	 * beginning of the text.
	 */
d1639 13
a1651 11
	spaceLeft = maxY - y;
	lineNum = TkBTreeLineIndex(dInfoPtr->dLinePtr->index.linePtr);
	bytesToCount = dInfoPtr->dLinePtr->index.byteIndex;
	if (bytesToCount == 0) {
	    bytesToCount = INT_MAX;
	    lineNum--;
	}
	for ( ; (lineNum >= 0) && (spaceLeft > 0); lineNum--) {
	    index.linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
	    index.byteIndex = 0;
	    lowestPtr = NULL;
d1653 30
a1682 9
	    do {
		dlPtr = LayoutDLine(textPtr, &index);
		dlPtr->nextPtr = lowestPtr;
		lowestPtr = dlPtr;
		if (dlPtr->length == 0 && dlPtr->height == 0) { bytesToCount--; break; }	/* elide */
		TkTextIndexForwBytes(&index, dlPtr->byteCount, &index);
		bytesToCount -= dlPtr->byteCount;
	    } while ((bytesToCount > 0)
		    && (index.linePtr == lowestPtr->index.linePtr));
d1684 4
a1687 4
	    /*
	     * Scan through the display lines from the bottom one up to
	     * the top one.
	     */
d1689 8
a1696 11
	    while (lowestPtr != NULL) {
		dlPtr = lowestPtr;
		spaceLeft -= dlPtr->height;
		if (spaceLeft < 0) {
		    break;
		}
		lowestPtr = dlPtr->nextPtr;
		dlPtr->nextPtr = dInfoPtr->dLinePtr;
		dInfoPtr->dLinePtr = dlPtr;
		if (tkTextDebug) {
		    char string[TK_POS_CHARS];
d1698 2
a1699 2
		    TkTextPrintIndex(&dlPtr->index, string);
		    Tcl_SetVar2(textPtr->interp, "tk_textRelayout",
d1702 19
a1722 2
	    FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
	    bytesToCount = INT_MAX;
d1732 1
a1732 1
	y = dInfoPtr->y;
d1757 1
a1757 1
	dlPtr->oldY = -1;
d1762 10
a1771 1
	    dlPtr->oldY = -1;
d1775 1
a1775 1
	    dlPtr->oldY = -1;
d1780 1
a1780 1
		dlPtr->oldY = -1;
d1790 1
d1819 2
a1820 2
    if (dInfoPtr->newByteOffset > maxOffset) {
	dInfoPtr->newByteOffset = maxOffset;
d1822 2
a1823 2
    if (dInfoPtr->newByteOffset < 0) {
	dInfoPtr->newByteOffset = 0;
d1825 3
a1827 3
    pixelOffset = dInfoPtr->newByteOffset * textPtr->charWidth;
    if (pixelOffset != dInfoPtr->curPixelOffset) {
	dInfoPtr->curPixelOffset = pixelOffset;
d1830 1
a1830 1
	    dlPtr->oldY = -1;
d1853 18
a1870 12
FreeDLines(textPtr, firstPtr, lastPtr, unlink)
    TkText *textPtr;			/* Information about overall text
					 * widget. */
    register DLine *firstPtr;		/* Pointer to first DLine to free up. */
    DLine *lastPtr;			/* Pointer to DLine just after last
					 * one to free (NULL means everything
					 * starting with firstPtr). */
    int unlink;				/* 1 means DLines are currently linked
					 * into the list rooted at
					 * textPtr->dInfoPtr->dLinePtr and
					 * they have to be unlinked.  0 means
					 * just free without unlinking. */
d1875 1
a1875 1
    if (unlink) {
d1901 3
a1903 1
    textPtr->dInfoPtr->dLinesInvalidated = 1;
d1938 1
a1938 1
    int height, x;
a1967 1
	    x = chunkPtr->x + dInfoPtr->x - dInfoPtr->curPixelOffset;
d1969 1
a1982 1
     * Conve
d1995 7
a2001 11
	x = chunkPtr->x + dInfoPtr->x - dInfoPtr->curPixelOffset;
	if ((x + chunkPtr->width <= 0) || (x >= dInfoPtr->maxX)) {
	    /*
	     * Note:  we have to call the displayProc even for chunks
	     * that are off-screen.  This is needed, for example, so
	     * that embedded windows can be unmapped in this case.
	     * Display the chunk at a coordinate that can be clearly
	     * identified by the displayProc as being off-screen to
	     * the left (the displayProc may not be able to tell if
	     * something is off to the right).
	     */
d2003 12
a2014 10
	    if (chunkPtr->displayProc != NULL)
	    (*chunkPtr->displayProc)(chunkPtr, -chunkPtr->width,
		    dlPtr->spaceAbove,
		    dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
		    dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
		    dlPtr->y + dlPtr->spaceAbove);
	} else {
	    /* don't call if elide.  This tax ok since not very many visible DLine's in
		  an area, but potentially many elide ones */
	    if (chunkPtr->displayProc != NULL)
d2020 1
d2027 6
a2032 6
     * Copy the pixmap onto the screen.  If this is the last line on
     * the screen then copy a piece of the line, so that it doesn't
     * overflow into the border area.  Another special trick:  copy the
     * padding area to the left of the line;  this is because the
     * insertion cursor sometimes overflows onto that area and we want
     * to get as much of the cursor as possible.
d2039 6
d2046 2
a2047 2
	    dInfoPtr->x, 0, (unsigned) (dInfoPtr->maxX - dInfoPtr->x),
	    (unsigned) height, dInfoPtr->x, dlPtr->y);
d2119 1
a2119 1
    minX = dInfoPtr->curPixelOffset;
d2420 1
a2420 1
 * DisplayText --
d2422 10
a2431 3
 *	This procedure is invoked as a when-idle handler to update the
 *	display.  It only redisplays the parts of the text widget that
 *	are out of date.
d2437 1
a2437 1
 *	Information is redrawn on the screen.
d2442 2
a2443 2
static void
DisplayText(clientData)
d2448 3
a2450 7
    register DLine *dlPtr;
    DLine *prevPtr;
    Pixmap pixmap;
    int maxHeight, borders;
    int bottomY = 0;		/* Initialization needed only to stop
				 * compiler warnings. */
    Tcl_Interp *interp;
d2458 3
d2464 21
a2484 6
    interp = textPtr->interp;
    Tcl_Preserve((ClientData) interp);

    if (tkTextDebug) {
	Tcl_SetVar2(interp, "tk_textRelayout", (char *) NULL, "",
                TCL_GLOBAL_ONLY);
d2486 37
d2524 36
a2559 1
    if ((textPtr->tkwin == NULL) || (textPtr->flags & DESTROYED)) {
d2561 34
a2594 2
	/*
	 * The widget has been deleted.  Don't do anything.
d2596 1
a2596 2

        goto end;
d2598 26
d2625 12
a2636 11
    if (!Tk_IsMapped(textPtr->tkwin) || (dInfoPtr->maxX <= dInfoPtr->x)
	    || (dInfoPtr->maxY <= dInfoPtr->y)) {
	UpdateDisplayInfo(textPtr);
	dInfoPtr->flags &= ~REDRAW_PENDING;
	goto doScrollbars;
    }
    numRedisplays++;
    if (tkTextDebug) {
	Tcl_SetVar2(interp, "tk_textRedraw", (char *) NULL, "",
                TCL_GLOBAL_ONLY);
    }
d2638 4
a2641 1
    if ((textPtr->tkwin == NULL) || (textPtr->flags & DESTROYED)) {
d2643 77
d2721 8
a2728 1
	 * The widget has been deleted.  Don't do anything.
d2730 4
a2733 2

	goto end;
d2735 3
a2737 7

    /*
     * Choose a new current item if that is needed (this could cause
     * event handlers to be invoked, hence the preserve/release calls
     * and the loop, since the handlers could conceivably necessitate
     * yet another current item calculation).  The tkwin check is because
     * the whole window could go away in the Tcl_Release call.
d2739 47
d2787 413
a3199 7
    while (dInfoPtr->flags & REPICK_NEEDED) {
	Tcl_Preserve((ClientData) textPtr);
	dInfoPtr->flags &= ~REPICK_NEEDED;
	TkTextPickCurrent(textPtr, &textPtr->pickEvent);
	if ((textPtr->tkwin == NULL) || (textPtr->flags & DESTROYED)) {
	    Tcl_Release((ClientData) textPtr);
	    goto end;
a3200 1
	Tcl_Release((ClientData) textPtr);
d3212 4
a3215 1
     * by scrolling (copying from other parts of the screen).
d3223 24
a3246 2
	if ((dlPtr->oldY == -1) || (dlPtr->y == dlPtr->oldY)
		|| ((dlPtr->oldY + dlPtr->height) > dInfoPtr->maxY)) {
d3261 1
a3261 1
	    if ((dlPtr2->oldY == -1)
a3272 1

d3277 12
a3288 1

d3295 1
d3310 1
a3310 1
	    if ((dlPtr2->oldY != -1)
d3313 1
a3313 1
		dlPtr2->oldY = -1;
d3425 2
a3426 1
	if ((dlPtr->height > maxHeight) && (dlPtr->oldY != dlPtr->y)) {
d3431 9
a3439 2
    if (maxHeight > dInfoPtr->maxY) {
	maxHeight = dInfoPtr->maxY;
d3441 1
d3450 1
a3450 1
	    if (dlPtr->oldY != dlPtr->y) {
d3464 46
a3509 1
		dlPtr->flags &= ~NEW_LAYOUT;
a3510 1
	    /*prevPtr = dlPtr;*/
d3697 5
a3701 3
	if ((dlPtr->oldY != -1) && (TkRectInRegion(region, rect.x, dlPtr->y,
		rect.width, (unsigned int) dlPtr->height) != RectangleOut)) {
	    dlPtr->oldY = -1;
d3734 4
d3752 5
a3756 4
    TkText *textPtr;		/* Widget record for text widget. */
    TkTextIndex *index1Ptr;	/* Index of first character to redisplay. */
    TkTextIndex *index2Ptr;	/* Index of character just after last one
				 * to redisplay. */
d3809 1
a3809 1
    FreeDLines(textPtr, firstPtr, lastPtr, 1);
d3852 25
d3983 1
a3983 1
	FreeDLines(textPtr, dlPtr, endPtr, 1);
d4018 1
a4018 1
TkTextRelayoutWindow(textPtr)
d4020 2
d4054 1
a4054 1
    FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
d4089 1
a4089 1
	MeasureUp(textPtr, &textPtr->topIndex, 0, &textPtr->topIndex);
d4099 19
d4144 14
a4157 4
    int pickPlace;		/* 0 means topLine must appear at top of
				 * screen.  1 means we get to pick where it
				 * appears:  minimize screen motion or else
				 * display line at center of screen. */
d4163 2
a4164 2
    Tk_FontMetrics fm;

d4172 1
a4172 1
	TkTextIndexBackChars(indexPtr, 1, &rounded);
d4176 10
a4185 1
    if (!pickPlace) {
d4193 3
a4195 4
	if (indexPtr->byteIndex == 0) {
	    textPtr->topIndex = *indexPtr;
	} else {
	    MeasureUp(textPtr, indexPtr, 0, &textPtr->topIndex);
d4197 1
d4222 10
d4240 4
a4243 2
     * lines, whichever is greater.  Add one extra line also, to
     * account for the way MeasureUp rounds.
d4246 6
a4251 2
    Tk_GetFontMetrics(textPtr->tkfont, &fm);
    bottomY = (dInfoPtr->y + dInfoPtr->maxY + fm.linespace)/2;
d4253 2
a4254 2
    if (close < 3*fm.linespace) {
	close = 3*fm.linespace;
a4255 1
    close += fm.linespace;
d4257 1
d4261 3
a4263 1
	 * line on the screen.
d4266 2
a4267 1
	MeasureUp(textPtr, &textPtr->topIndex, close, &tmpIndex);
d4269 3
a4271 1
	    MeasureUp(textPtr, indexPtr, 0, &textPtr->topIndex);
d4275 1
d4282 2
a4283 1
	MeasureUp(textPtr, indexPtr, close, &tmpIndex);
d4297 2
a4298 1
    MeasureUp(textPtr, indexPtr, bottomY, &textPtr->topIndex);
d4310 1
a4310 1
 * MeasureUp --
d4314 62
a4375 1
 *	than "distance" pixels above the given index.
d4383 2
a4384 3
 *	coordinate, then return the index of the next lower line
 *	instead (i.e. the returned index will be completely visible
 *	at or below the given y-coordinate).
d4393 1
a4393 1
MeasureUp(textPtr, srcPtr, distance, dstPtr)
d4395 1
a4395 1
    TkTextIndex *srcPtr;	/* Index of character from which to start
d4401 3
a4407 1
    TkTextIndex bestIndex;	/* Best candidate seen so far for result. */
a4409 1
    int noBestYet;		/* 1 means bestIndex hasn't been set. */
a4410 1
    noBestYet = 1;
d4417 1
d4439 3
a4441 1
	 * line at the desired location.
d4446 5
a4450 2
	    if (distance < 0) {
		*dstPtr = (noBestYet) ? dlPtr->index : bestIndex;
a4452 2
	    bestIndex = dlPtr->index;
	    noBestYet = 0;
d4460 2
a4461 2
	FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
	if (distance < 0) {
d4473 3
d4512 1
a4512 2
	Tcl_AppendResult(interp, "wrong # args: should be \"",
		Tcl_GetString(objv[0]), " see index\"", (char *) NULL);
d4525 1
a4525 1
	TkTextIndexBackChars(&index, 1, &index);
d4532 1
a4532 1
    TkTextSetYView(textPtr, &index, 1);
d4577 1
a4577 1
	delta = x - dInfoPtr->curPixelOffset;
d4581 1
a4581 1
		dInfoPtr->newByteOffset = (x - lineWidth/2)
d4584 1
a4584 1
		dInfoPtr->newByteOffset -= ((-delta) + textPtr->charWidth - 1)
d4591 1
a4591 1
		    dInfoPtr->newByteOffset = (x - lineWidth/2)
d4594 1
a4594 1
		    dInfoPtr->newByteOffset += (delta + textPtr->charWidth - 1)
d4648 88
a4735 1
    }
d4737 17
a4753 8
    newOffset = dInfoPtr->newByteOffset;
    type = Tk_GetScrollInfoObj(interp, objc, objv, &fraction, &count);
    switch (type) {
	case TK_SCROLL_ERROR:
	    return TCL_ERROR;
	case TK_SCROLL_MOVETO:
	    if (fraction > 1.0) {
		fraction = 1.0;
d4755 5
a4759 17
	    if (fraction < 0) {
		fraction = 0;
	    }
	    newOffset = (int) (((fraction * dInfoPtr->maxLength) / textPtr->charWidth)
		    + 0.5);
	    break;
	case TK_SCROLL_PAGES:
	    charsPerPage = ((dInfoPtr->maxX - dInfoPtr->x) / textPtr->charWidth)
		    - 2;
	    if (charsPerPage < 1) {
		charsPerPage = 1;
	    }
	    newOffset += charsPerPage * count;
	    break;
	case TK_SCROLL_UNITS:
	    newOffset += count;
	    break;
a4760 3

    dInfoPtr->newByteOffset = newOffset;
    dInfoPtr->flags |= DINFO_OUT_OF_DATE;
a4761 1
	dInfoPtr->flags |= REDRAW_PENDING;
d4764 1
a4764 1
    return TCL_OK;
d4770 1
a4770 1
 * ScrollByLines --
d4786 1
a4786 1
ScrollByLines(textPtr, offset)
d4788 1
a4788 1
    int offset;			/* Amount by which to scroll, in *screen*
d4837 1
a4837 1
	    FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
d4846 2
a4847 1
	 * in the text.
d4851 1
d4865 1
a4865 1
	    FreeDLines(textPtr, dlPtr, (DLine *) NULL, 0);
d4908 1
a4908 2
    int pickPlace, lineNum, type, bytesInLine;
    Tk_FontMetrics fm;
d4912 1
a4912 3
    TkTextIndex index, new;
    TkTextLine *lastLinePtr;
    DLine *dlPtr;
d4931 2
a4932 1
		&& (strncmp(Tcl_GetString(objv[2]), "-pickplace", switchLength) == 0)) {
d4935 1
a4935 4
		Tcl_AppendResult(interp, "wrong # args: should be \"",
				 Tcl_GetString(objv[0]), 
				 " yview -pickplace lineNum|index\"",
			(char *) NULL);
d4941 1
d4957 1
a4957 1
	TkTextSetYView(textPtr, &index, pickPlace);
d4965 2
a4966 1
    type = Tk_GetScrollInfoObj(interp, objc, objv, &fraction, &count);
d4968 1
a4968 1
	case TK_SCROLL_ERROR:
d4970 1
a4970 1
	case TK_SCROLL_MOVETO:
d4977 10
a4986 9
	    fraction *= TkBTreeNumLines(textPtr->tree);
	    lineNum = (int) fraction;
	    TkTextMakeByteIndex(textPtr->tree, lineNum, 0, &index);
	    bytesInLine = TkBTreeBytesInLine(index.linePtr);
	    index.byteIndex = (int)((bytesInLine * (fraction-lineNum)) + 0.5);
	    if (index.byteIndex >= bytesInLine) {
		TkTextMakeByteIndex(textPtr->tree, lineNum + 1, 0, &index);
	    }
	    TkTextSetYView(textPtr, &index, 0);
d4988 1
a4988 1
	case TK_SCROLL_PAGES:
d4994 13
a5006 10

	    Tk_GetFontMetrics(textPtr->tkfont, &fm);
	    if (count < 0) {
		pixels = (dInfoPtr->maxY - 2*fm.linespace - dInfoPtr->y)*(-count)
			+ fm.linespace;
		MeasureUp(textPtr, &textPtr->topIndex, pixels, &new);
		if (TkTextIndexCmp(&textPtr->topIndex, &new) == 0) {
		    /*
		     * A page of scrolling ended up being less than one line.
		     * Scroll one line anyway.
d5008 5
a5012 3

		    count = -1;
		    goto scrollByLines;
d5014 1
a5014 1
		textPtr->topIndex = new;
d5016 1
a5016 20
		/*
		 * Scrolling down by pages.  Layout lines starting at the
		 * top index and count through the desired vertical distance.
		 */

		pixels = (dInfoPtr->maxY - 2*fm.linespace - dInfoPtr->y)*count;
		lastLinePtr = TkBTreeFindLine(textPtr->tree,
			TkBTreeNumLines(textPtr->tree));
		do {
		    dlPtr = LayoutDLine(textPtr, &textPtr->topIndex);
		    dlPtr->nextPtr = NULL;
		    TkTextIndexForwBytes(&textPtr->topIndex, dlPtr->byteCount,
			    &new);
		    pixels -= dlPtr->height;
		    FreeDLines(textPtr, dlPtr, (DLine *) NULL, 0);
		    if (new.linePtr == lastLinePtr) {
			break;
		    }
		    textPtr->topIndex = new;
		} while (pixels > 0);
d5018 5
a5022 4
	    if (!(dInfoPtr->flags & REDRAW_PENDING)) {
		Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
	    }
	    dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
d5024 2
a5025 3
	case TK_SCROLL_UNITS:
	    scrollByLines:
	    ScrollByLines(textPtr, count);
a5060 1
    Tk_FontMetrics fm;
d5066 2
a5067 1
			 Tcl_GetString(objv[0]), " scan dragto x y ?gain?\"", (char *) NULL);
d5076 3
a5078 2
    if ((objc == 6) && (Tcl_GetIntFromObj(interp, objv[5], &gain) != TCL_OK))
	return TCL_ERROR;
d5107 1
a5107 1
	dInfoPtr->newByteOffset = newByte;
d5109 2
a5110 3
	Tk_GetFontMetrics(textPtr->tkfont, &fm);
	totalScroll = (gain*(dInfoPtr->scanMarkY - y)) / fm.linespace;
	if (totalScroll != dInfoPtr->scanTotalScroll) {
d5112 2
a5113 2
	    ScrollByLines(textPtr, totalScroll-dInfoPtr->scanTotalScroll);
	    dInfoPtr->scanTotalScroll = totalScroll;
d5116 1
a5116 1
		dInfoPtr->scanTotalScroll = 0;
d5120 3
a5122 2
    } else if ((c == 'm') && (strncmp(Tcl_GetString(objv[2]), "mark", length) == 0)) {
	dInfoPtr->scanMarkIndex = dInfoPtr->newByteOffset;
d5124 1
a5124 1
	dInfoPtr->scanTotalScroll = 0;
a5173 1
    char buffer[TCL_DOUBLE_SPACE * 2 + 1];
d5176 2
a5177 1

d5179 1
a5179 1
	first = ((double) dInfoPtr->curPixelOffset)
d5191 4
a5194 2
	sprintf(buffer, "%g %g", first, last);
	Tcl_SetResult(interp, buffer, TCL_VOLATILE);
d5203 10
a5212 5
    sprintf(buffer, " %g %g", first, last);
    code = Tcl_VarEval(interp, textPtr->xScrollCmd,
	    buffer, (char *) NULL);
    if (code != TCL_OK) {
	Tcl_AddErrorInfo(interp,
d5214 2
a5215 1
	Tcl_BackgroundError(interp);
d5222 42
d5285 1
a5285 1

a5295 1
    char buffer[TCL_DOUBLE_SPACE * 2 + 1];
d5298 8
a5305 1
    int totalLines, code, count;
d5307 14
a5320 6
    dlPtr = dInfoPtr->dLinePtr;
    totalLines = TkBTreeNumLines(textPtr->tree);
    first = (double) TkBTreeLineIndex(dlPtr->index.linePtr)
	    + (double) dlPtr->index.byteIndex
		    / TkBTreeBytesInLine(dlPtr->index.linePtr);
    first /= totalLines;
d5322 5
a5326 1
	if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
d5328 3
a5330 2
	     * The last line is only partially visible, so don't
	     * count its characters in what's visible.
d5332 1
a5332 1
	    count = 0;
a5335 1
	    count = dlPtr->byteCount;
d5340 40
a5379 4
    last = ((double) TkBTreeLineIndex(dlPtr->index.linePtr))
	    + ((double) (dlPtr->index.byteIndex + count))
		    / (TkBTreeBytesInLine(dlPtr->index.linePtr));
    last /= totalLines;
d5381 4
a5384 2
	sprintf(buffer, "%g %g", first, last);
	Tcl_SetResult(interp, buffer, TCL_VOLATILE);
d5387 2
a5388 2
    if (FP_EQUAL_SCALE(first, dInfoPtr->yScrollFirst, totalLines) &&
	FP_EQUAL_SCALE(last,  dInfoPtr->yScrollLast,  totalLines)) {
d5393 50
a5442 6
    sprintf(buffer, " %g %g", first, last);
    code = Tcl_VarEval(interp, textPtr->yScrollCmd, buffer, (char *) NULL);
    if (code != TCL_OK) {
	Tcl_AddErrorInfo(interp,
		"\n    (vertical scrolling command executed by text)");
	Tcl_BackgroundError(interp);
d5546 1
a5546 2
    register DLine *dlPtr, *validdlPtr;
    register TkTextDispChunk *chunkPtr;
d5578 4
a5581 3
    for (dlPtr = validdlPtr = dInfoPtr->dLinePtr; y >= (dlPtr->y + dlPtr->height);
	    dlPtr = dlPtr->nextPtr) {
	if (dlPtr->chunkPtr !=NULL) validdlPtr = dlPtr;
d5592 24
a5615 1
    if (dlPtr->chunkPtr == NULL) dlPtr = validdlPtr;
d5617 12
d5638 1
a5638 1
    x = x - dInfoPtr->x + dInfoPtr->curPixelOffset;
d5644 1
a5644 1
	    TkTextIndexBackChars(indexPtr, 1, indexPtr);
d5662 104
d5785 1
a5785 1
TkTextCharBbox(textPtr, indexPtr, xPtr, yPtr, widthPtr, heightPtr)
d5792 5
d5848 1
a5848 1
    *xPtr = *xPtr + dInfoPtr->x - dInfoPtr->curPixelOffset;
d5855 3
d5862 4
d5938 1
a5938 1
    *xPtr = dInfoPtr->x - dInfoPtr->curPixelOffset + dlx;
d6023 3
a6025 2
    TkWrapMode wrapMode;	/* How to handle line wrapping: TEXT_WRAPMODE_CHAR,
				 * TEXT_WRAPMODE_NONE, or TEXT_WRAPMODE_WORD. */
d6213 2
a6214 1
    if (!sValuePtr->elide && (ciPtr->numBytes > offsetBytes) && (stylePtr->fgGC != None)) {
d6330 1
a6330 1
    int byteIndex;				/* Byte offset of desired character
d6805 99
@


1.16
log
@text widget cleanup fix
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.15 2003/05/19 13:04:23 vincentdarley Exp $
a2181 1
    Tk_Window tkwin;
a2245 2
	int flags = textPtr->flags;
	
a2248 2
	tkwin = textPtr->tkwin;
	Tcl_Release((ClientData) textPtr);
d2250 1
d2253 1
@


1.15
log
@tip 113 implementation
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.14 2002/11/22 23:25:19 hobbs Exp $
d2191 1
a2191 1
    if (textPtr->tkwin == NULL) {
d2208 1
a2208 1
    if (textPtr->tkwin == NULL) {
d2229 1
a2229 1
    if (textPtr->tkwin == NULL) {
d2247 2
d2254 1
a2254 1
	if (tkwin == NULL) {
d2499 1
a2499 1
        if (textPtr->tkwin == NULL) {
d2529 1
a2529 1
        if (textPtr->tkwin == NULL) {
@


1.14
log
@	* tests/textDisp.test:
	* generic/tkTextDisp.c (TkTextSeeCmd): handle see for unmapped
	text displays. [Bug #641778]
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.13 2002/08/05 04:30:40 dgp Exp $
d353 1
a353 1
			    TkTextIndex *indexPtr));
d563 2
a564 1
	if ((tagPtr->bdString != NULL)
d640 1
a640 1
	if ((tagPtr->tabString != NULL)
d1710 1
a1710 1
    if (textPtr->state == TK_STATE_NORMAL) {
d3299 1
a3299 1
TkTextSeeCmd(textPtr, interp, argc, argv)
d3302 2
a3303 2
    int argc;			/* Number of arguments. */
    CONST char **argv;		/* Argument strings.  Someone else has already
d3305 1
a3305 1
				 * argv[1] is "see". */
d3313 1
a3313 1
    if (argc != 3) {
d3315 1
a3315 1
		argv[0], " see index\"", (char *) NULL);
d3318 1
a3318 1
    if (TkTextGetIndex(interp, textPtr, argv[2], &index) != TCL_OK) {
d3432 1
a3432 1
TkTextXviewCmd(textPtr, interp, argc, argv)
d3435 2
a3436 2
    int argc;			/* Number of arguments. */
    CONST char **argv;		/* Argument strings.  Someone else has already
d3438 1
a3438 1
				 * argv[1] is "xview". */
d3448 1
a3448 1
    if (argc == 2) {
d3454 1
a3454 1
    type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
d3620 1
a3620 1
TkTextYviewCmd(textPtr, interp, argc, argv)
d3623 2
a3624 2
    int argc;			/* Number of arguments. */
    CONST char **argv;		/* Argument strings.  Someone else has already
d3626 1
a3626 1
				 * argv[1] is "yview". */
d3642 1
a3642 1
    if (argc == 2) {
d3652 2
a3653 2
    if (argv[2][0] == '-') {
	switchLength = strlen(argv[2]);
d3655 1
a3655 1
		&& (strncmp(argv[2], "-pickplace", switchLength) == 0)) {
d3657 1
a3657 1
	    if (argc != 4) {
d3659 2
a3660 1
			argv[0], " yview -pickplace lineNum|index\"",
d3666 2
a3667 2
    if ((argc == 3) || pickPlace) {
	if (Tcl_GetInt(interp, argv[2+pickPlace], &lineNum) == TCL_OK) {
d3678 1
a3678 1
	if (TkTextGetIndex(interp, textPtr, argv[2+pickPlace],
d3687 1
a3687 1
     * New syntax: dispatch based on argv[2].
d3690 1
a3690 1
    type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
d3787 1
a3787 1
TkTextScanCmd(textPtr, interp, argc, argv)
d3790 2
a3791 2
    int argc;			/* Number of arguments. */
    CONST char **argv;		/* Argument strings.  Someone else has already
d3793 1
a3793 1
				 * argv[1] is "scan". */
d3801 1
a3801 1
    if ((argc != 5) && (argc != 6)) {
d3803 2
a3804 2
		argv[0], " scan mark x y\" or \"",
		argv[0], " scan dragto x y ?gain?\"", (char *) NULL);
d3807 1
a3807 1
    if (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) {
d3810 1
a3810 1
    if (Tcl_GetInt(interp, argv[4], &y) != TCL_OK) {
d3813 1
a3813 1
    if ((argc == 6) && (Tcl_GetInt(interp, argv[5], &gain) != TCL_OK))
d3815 3
a3817 3
    c = argv[2][0];
    length = strlen(argv[2]);
    if ((c == 'd') && (strncmp(argv[2], "dragto", length) == 0)) {
d3857 1
a3857 1
    } else if ((c == 'm') && (strncmp(argv[2], "mark", length) == 0)) {
d3863 1
a3863 1
	Tcl_AppendResult(interp, "bad scan option \"", argv[2],
d4056 1
a4056 1
    TkTextIndex *indexPtr;	/* Index of desired character. */
d4234 1
a4234 1
    TkTextIndex *indexPtr;	/* Index of character whose bounding
d4343 1
a4343 1
    TkTextIndex *indexPtr;	/* Index of character whose bounding
@


1.14.2.1
log
@	* unix/tcl.m4 (Darwin): add 64bit support, check for Tiger copyfile(),
	add CFLAGS to SHLIB_LD to support passing -isysroot in env(CFLAGS) to
	configure (flag can't be present twice, so can't be in both CFLAGS and
	LDFLAGS during configure), don't use -prebind when deploying on 10.4,
	define TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING (rdar://3171542).
	(SC_ENABLE_LANGINFO, SC_TIME_HANDLER): add/fix caching, fix obsolete
	autoconf macros. Sync with tk/unix/tcl.m4, sync whitespace with HEAD.

	* unix/configure.in: fix obsolete autoconf macros, sync gratuitous
	formatting/ordering differences with tcl/unix/configure.in.

	* unix/Makefile.in: add CFLAGS to wish/tktest link to make executable
	linking the same as during configure (needed to avoid loosing any linker
	relevant flags in CFLAGS, in particular flags that can't be in LDFLAGS).
	Avoid concurrent linking of wish and compiling of tkTestInit.o during
	parallel make, fix dependencies and flags for building tkMacOSXInit.o
	(checkstubs, checkexports): dependency and Darwin fixes

	* macosx/tkMacOSXEvent.c (TkMacOSXProcessEvent):
	* macosx/tkMacOSXEvent.h:
	* macosx/tkMacOSXMouseEvent.c (TkMacOSXProcessMouseEvent):
	* macosx/tkMacOSXCarbonEvents.c: install standard application event
	handler, add & call functions to start and stop carbon even timer that
	runs the tcl event loop periodically during a nested carbon event loop
	in the toolbox (e.g. during menutracking) to ensure tcl timers etc
	continue to fire, register app event handler for menu tracking and HI
	command carbon events, move menu event handling to new handlers for
	those carbon events, no longer register for/handle appleevent carbon
	event (now dealt with by standard application event handler), event
	debugging code dynamically acquires carbon event debugging functions to
	allow use on Tiger where they are no longer exported from HIToolbox.

	* macosx/tkMacOSXFont.c (TkMacOSXUseAntialiasedText):
	* macosx/tkMacOSXKeyEvent.c (GetKeyboardLayout):
	* macosx/tkMacOSXCarbonEvents.c (TkMacOSXInitCarbonEvents):
	* macosx/tkMacOSXInit.c:
	* macosx/tkMacOSXInt.h: abstract common code to dynamically acquire
	address of a named symbol (from a loaded dynamic library) into new
	function TkMacOSXGetNamedSymbol() and macro TkMacOSXInitNamedSymbol.

	* macosx/tkMacOSXMenu.c (TkpNewMenu):
	* macosx/tkMacOSXMenubutton.c (MenuButtonInitControl):
	* macosx/tkMacOSXMenus.c (TkMacOSXHandleMenuSelect): switch to  modern
	utf8 aware menu manager API, remove obsolete code, add error handling.

	* macosx/tkMacOSXMouseEvent.c: define OSX 10.3 or later only constants
	if necessary to allow compilation on OSX 10.2

	* macosx/tkMacOSXWm.c (UpdateSizeHints): remove code that is never
	executed.

	* generic/tk.h: add/correct location of version numbers in macosx files.

	* generic/tkInt.h: clarify fat compile comment.

	* macosx/Wish.pbproj/default.pbxuser (new file):
	* macosx/Wish.pbproj/jingham.pbxuser:
	* macosx/Wish.pbproj/project.pbxproj: sync with HEAD.

	* macosx/buildTkConfig.tcl (removed): remove obsolete build files.

	* macosx/README: clarification/cleanup, sync with HEAD, document
	universal (fat) builds via CFLAGS (i.e. ppc and i386 at the same time).

	* macosx/Makefile: add support for reusing configure cache, build target
	fixes.

	* generic/tkMenu.c:
	* macosx/tkMacOSXButton.c:
	* macosx/tkMacOSXDebug.c:
	* macosx/tkMacOSXDebug.h:
	* macosx/tkMacOSXDialog.c:
	* macosx/tkMacOSXDraw.c:
	* macosx/tkMacOSXEntry.c:
	* macosx/tkMacOSXMenu.c:
	* macosx/tkMacOSXMouseEvent.c:
	* macosx/tkMacOSXXStubs.c:
	* macosx/tkMacOSXSubwindows.c:
	* xlib/xgc.c: declare functions and globals used only in own file as
	static, #ifdef out a few Xlib and aqua functions that are never called
	(sync with HEAD).

	* macosx/tkMacOSXPort.h:
	* generic/tkInt.decls: correct signature of TkMacOSXHandleMenuSelect,
	add XSync to aqua Xlib stubs.

	* generic/tkTest.c: #ifdef unix only declarations.
	(TestmetricsCmd): unify win and mac implementation.

	* generic/tkTextDisp.c:
	* generic/tkTextBTree.c:
	* macosx/tkMacOSXInit.c:
	* macosx/tkMacOSXKeyEvent.c:
	* macosx/tkMacOSXWindowEvent.c:
	* macosx/tkMacOSXXStubs.c: fix gcc 4 warnings.

	* macosx/tkMacOSXNotify.c:
	* macosx/tkMacOSXTest.c: sync with HEAD.

	* generic/tkIntPlatDecls.h:
	* generic/tkIntXlibDecls.h:
	* generic/tkStubInit.c:
	* unix/configure: regen.
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.14 2002/11/22 23:25:19 hobbs Exp $
d3213 1
a3213 2
    TkTextIndex bestIndex = {NULL, NULL, 0}; /* Best candidate seen so far for
					      * result. Silence gcc 4 warning */
@


1.14.2.2
log
@	* generic/tkTextDisp.c (TkTextPixelIndex): Don't segfault on full
	-elide text. [Bug 1442102]
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.14.2.1 2005/11/27 02:44:26 das Exp $
a4178 9
    *indexPtr = dlPtr->index;

    /*
     * If it is still empty, we have nothing to access. [Bug 1442102]
     */

    if (dlPtr->chunkPtr == NULL) {
	return;
    }
d4187 1
@


1.14.2.3
log
@	* generic/tkEntry.c:     move hard-coded ALWAYS_SHOW_SELECTION
	* generic/tkInt.h:       control of entry/text selection display
	* generic/tkText.c:      based on focus to the Tcl level,
	* generic/tkTextBTree.c: controlled by ::tk::AlwaysShowSelection
	* generic/tkTextDisp.c:  (boolean, private).  [Bug 1553691]
	* generic/tkWindow.c:
	* library/tk.tcl
	* unix/tkUnixPort.h:
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.14.2.2 2006/04/05 19:48:07 hobbs Exp $
d549 2
a550 2
	 * Skip the selection tag if we don't have focus,
	 * unless we always want to show the selection.
d553 2
a554 3
	if (!TkpAlwaysShowSelection(textPtr->tkwin)
		&& (tagPtr == textPtr->selTagPtr)
		&& !(textPtr->flags & GOT_FOCUS)) {
d557 1
@


1.14.2.4
log
@	* macosx/tkMacOSXColor.c (TkSetMacColor, TkpGetColor): use AppearanceMgr
	* macosx/tkMacOSXDefault.h: to retrieve platform std colors for text
	* macosx/tkMacOSXPort.h:    selections, add "systemHighlightSecondary"
	color name for standard color of inactive selections.

	* library/text.tcl (aqua): change focus bindings to implement platform
	standard look for inactive text selections using this new color.

	* generic/tkTextBTree.c (TkTextIsElided): on TkAqua, don't show text
	* generic/tkTextDisp.c (GetStyle):        selection when text widget
	* generic/tkText.c (TextEventProc):       is in disabled state.

	* generic/tkEntry.c (DisplayEntry): change default TkAqua selection
	* macosx/tkMacOSXDefault.h:         relief to "flat" (platform std).

	* generic/tkText.c (Tk_TextCmd): fix bug leading to default text
	selection relief string DEF_TEXT_SELECT_RELIEF being ignored.

	* macosx/tkMacOSXMouseEvent.c (TkMacOSXProcessMouseEvent): allow mouse
	event delivery to background windows with kWindowNoActivatesAttribute
	(e.g. overrideredirect windows), as these never come to the foreground
	they would not receive any mouse events otherwise. [Bug 1472624]

	* macosx/tkMacOSXWindowEvent.c (TkMacOSXGenerateFocusEvent): do not
	send focus events to any windows with kWindowNoActivatesAttribute.

	* macosx/tkMacOSXXStubs.c (XQueryColor, XQueryColors): implement basic
	XColor computation from pixel values, enough to make tkImg's window.c
	happy, fixes img::window failures reported on tcl-mac.

	* macosx/tkMacOSXMenu.c (DrawMenuEntryLabel): fix leak. [Bug 1554672]

	* macosx/Makefile: workaround bug in 'cp -pRH' on Darwin 6 and earlier,
	fixes 'make embedded' failure reported on tcl-mac; fix error from 'make
	deploy' with same build tree as previous 'make embedded'.

	* macosx/tkMacOSXEntry.c (TkpDrawEntryBorderAndFocus): fix typo.

	* unix/tcl.m4: sync with tcl/unix/tcl.m4.
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.14.2.3 2006/09/06 22:01:26 hobbs Exp $
d553 1
a553 7
	if (
#ifndef MAC_OSX_TK
		!TkpAlwaysShowSelection(textPtr->tkwin)
#else
		/* Don't show inactive selection in disabled widgets. */
		textPtr->state == TK_STATE_DISABLED
#endif
@


1.14.2.5
log
@	* generic/tkCanvas.c:	allow -selectforeground option to be None; add
	* generic/tkCanvText.c:	fallback to fgColor when selFgColor is None
	* generic/tkEntry.c:	(new default on aqua to match native L&F).
	* generic/tkListbox.c:
	* generic/tkText.c:

	* generic/tkCanvas.c:	   add support for bypassing all of Tk's double
	* generic/tkEntry.c:	   buffered drawing into intermediate pixmaps
	* generic/tkFrame.c:	   (via TK_NO_DOUBLE_BUFFERING #define), it is
	* generic/tkListbox.c:	   unnecessary & wasteful on aqua where all
	* generic/tkPanedWindow.c: drawing is already double-buffered by the
	* generic/tkTextDisp.c:	   window server. (Use of this on other
	* unix/tkUnixScale.c:	   platforms would only require implementation
	of TkpClipDrawableToRect()).
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.14.2.4 2006/09/10 17:07:35 das Exp $
a24 6
#ifdef TK_NO_DOUBLE_BUFFERING
#ifdef MAC_OSX_TK
#include "tkMacOSXInt.h"
#endif
#endif /* TK_NO_DOUBLE_BUFFERING */

a1688 5
#ifndef TK_NO_DOUBLE_BUFFERING
    const int y = 0;
#else
    const int y = dlPtr->y;
#endif /* TK_NO_DOUBLE_BUFFERING */
a1691 12
    display = Tk_Display(textPtr->tkwin);

    height = dlPtr->height;
    if ((height + dlPtr->y) > dInfoPtr->maxY) {
	height = dInfoPtr->maxY - dlPtr->y;
    }

#ifdef TK_NO_DOUBLE_BUFFERING
    TkpClipDrawableToRect(display, pixmap, dInfoPtr->x, y,
	    dInfoPtr->maxX - dInfoPtr->x, height);
#endif /* TK_NO_DOUBLE_BUFFERING */

d1697 2
a1698 1
    Tk_Fill3DRectangle(textPtr->tkwin, pixmap, textPtr->border, 0, y,
d1720 1
a1720 1
		(*chunkPtr->displayProc)(chunkPtr, x, y + dlPtr->spaceAbove,
d1760 1
a1760 1
		    y + dlPtr->spaceAbove,
d1768 1
a1768 1
	    (*chunkPtr->displayProc)(chunkPtr, x, y + dlPtr->spaceAbove,
a1777 1
#ifndef TK_NO_DOUBLE_BUFFERING
d1787 4
d1792 1
a1792 1
	    dInfoPtr->x, y, (unsigned) (dInfoPtr->maxX - dInfoPtr->x),
a1793 3
#else
    TkpClipDrawableToRect(display, pixmap, 0, 0, -1, -1);
#endif /* TK_NO_DOUBLE_BUFFERING */
d1855 1
a1855 5
#ifndef TK_NO_DOUBLE_BUFFERING
    const int y = 0;
#else
    const int y = dlPtr->y;
#endif /* TK_NO_DOUBLE_BUFFERING */
d1919 1
a1919 1
		    leftX + xOffset, y, (unsigned int) (rightX - leftX),
d1923 1
a1923 1
			leftX + xOffset, y, sValuePtr->borderWidth,
d1927 1
a1927 1
			y, sValuePtr->borderWidth, dlPtr->height, 0,
d1987 1
a1987 1
			    sValuePtr->border, leftX + xOffset, y,
d2028 1
a2028 1
			rightX2 - sValuePtr->borderWidth + xOffset, y,
d2037 1
a2037 1
		    rightX2 + xOffset, y, sValuePtr->borderWidth,
d2040 2
a2041 2
		    leftX + xOffset, y, rightX2 + sValuePtr->borderWidth -
		    leftX, sValuePtr->borderWidth, leftXIn, 0, 1,
d2104 1
a2104 1
			    y + dlPtr->height - sValuePtr->borderWidth,
d2131 1
a2131 1
			y + dlPtr->height - sValuePtr->borderWidth,
d2140 1
a2140 1
		    rightX2 + xOffset, y + dlPtr->height -
d2142 1
a2142 1
		    sValuePtr->borderWidth, 1, sValuePtr->relief);
d2144 3
a2146 4
		    leftX + xOffset, y + dlPtr->height -
		    sValuePtr->borderWidth, rightX2 + sValuePtr->borderWidth -
		    leftX, sValuePtr->borderWidth, leftXIn, 1, 0,
		    sValuePtr->relief);
a2456 1
#ifndef TK_NO_DOUBLE_BUFFERING
a2459 3
#else
	pixmap = Tk_WindowId(textPtr->tkwin);
#endif /* TK_NO_DOUBLE_BUFFERING */
a2473 1
#ifndef TK_NO_DOUBLE_BUFFERING
a2474 1
#endif /* TK_NO_DOUBLE_BUFFERING */
a2481 1
#ifndef TK_NO_DOUBLE_BUFFERING
a2482 1
#endif /* TK_NO_DOUBLE_BUFFERING */
@


1.14.2.6
log
@[Patch #3129527]: Fix buffer overflow w/ GCC 4.5 and -D_FORTIFY_SOURCE=2.
Just the strcpy->memcpy part, to prevent anything like [Bug #3164879]
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.14.2.5 2007/04/29 02:24:02 das Exp $
d4589 1
a4589 1
    memcpy(ciPtr->chars, p, (size_t) bytesThatFit);
@


1.13
log
@Applied companion patch for Tcl Patch 585105,
updating Tk to use Tcl 8.4's fully CONST-ified
interface, and fully CONSTifying Tk at the
same time.

This patch includes purging Tk of its last
direct access to interp->result.  [Bug 589853]

The substantial changes include copying
event sequence strings into Tcl_DStrings
in tkBind.c, and copying [text] indices into
Tcl_DStrings because parsing them involved
overwriting them.  If this causes performance
trouble, that can be resolved by further
converting them to Tcl_Obj's.

The #defines USE_NON_CONST and USE_COMPAT_CONST
have the same effect for Tk as they do for Tcl.
(They actually change tcl.h)
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.12 2002/06/15 04:44:02 dgp Exp $
d3350 1
d3354 4
d3359 2
a3360 1
    for (chunkPtr = dlPtr->chunkPtr; chunkPtr!=NULL ; chunkPtr = chunkPtr->nextPtr) {
d3370 1
d3373 12
a3384 19
    if (chunkPtr!=NULL) {       /* chunkPtr==NULL iff trying to see in elided region */
    (*chunkPtr->bboxProc)(chunkPtr, byteCount, dlPtr->y + dlPtr->spaceAbove,
	    dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
	    dlPtr->baseline - dlPtr->spaceAbove, &x, &y, &width,
	    &height);
    delta = x - dInfoPtr->curPixelOffset;
    oneThird = lineWidth/3;
    if (delta < 0) {
	if (delta < -oneThird) {
	    dInfoPtr->newByteOffset = (x - lineWidth/2)/textPtr->charWidth;
	} else {
	    dInfoPtr->newByteOffset -= ((-delta) + textPtr->charWidth - 1)
		/ textPtr->charWidth;
	}
    } else {
	delta -= (lineWidth - width);
	if (delta > 0) {
	    if (delta > oneThird) {
		dInfoPtr->newByteOffset = (x - lineWidth/2)/textPtr->charWidth;
d3386 1
a3386 1
		dInfoPtr->newByteOffset += (delta + textPtr->charWidth - 1)
d3390 12
a3401 1
	    return TCL_OK;
d3403 1
a3403 1
    }}
@


1.12
log
@removed stray character
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.11 2002/04/23 00:20:35 hobbs Exp $
d3302 1
a3302 1
    char **argv;		/* Argument strings.  Someone else has already
d3424 1
a3424 1
    char **argv;		/* Argument strings.  Someone else has already
d3612 1
a3612 1
    char **argv;		/* Argument strings.  Someone else has already
d3778 1
a3778 1
    char **argv;		/* Argument strings.  Someone else has already
@


1.11
log
@	* generic/tkTextDisp.c (DisplayLineBackground):
	* unix/tkUnix3d.c (Tk_3DHorizontalBevel):
	* unix/tkUnixFont.c (Tk_DrawChars): applied fixes to not overrun
	the X window 16-bit size limit. [Patch #541999] (bonfield)
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.10 2002/04/22 12:45:49 dkf Exp $
d4414 1
a4414 1
n * Results:
@


1.10
log
@Fix for Bug 223739 to get rid of floating point equality test.

Sorry for delaying this fix for months; I hadn't noticed that it had been reviewed!
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.9 2000/01/06 02:18:59 hobbs Exp $
d1893 19
@


1.9
log
@	* doc/text.n:
	* generic/tkText.c:
	* generic/tkText.h:
	* generic/tkTextBTree.c:
	* generic/tkTextDisp.c:
	* generic/tkTextTag.c: removed the -state option for text tags,
	and reoriented it around -elide, as -state disabled was never
	implemented, and -state hidden is better as -elide.
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.8 1999/12/14 06:52:32 hobbs Exp $
d99 10
d3878 1
a3878 1
    char buffer[TCL_DOUBLE_SPACE * 2];
d3899 2
a3900 1
    if ((first == dInfoPtr->xScrollFirst) && (last == dInfoPtr->xScrollLast)) {
d3950 1
a3950 1
    char buffer[TCL_DOUBLE_SPACE * 2];
d3985 2
a3986 1
    if ((first == dInfoPtr->yScrollFirst) && (last == dInfoPtr->yScrollLast)) {
@


1.9.2.1
log
@deleted stray character
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.9 2000/01/06 02:18:59 hobbs Exp $
d4383 1
a4383 1
 * Results:
@


1.9.2.2
log
@	* generic/tkTextDisp.c (TkTextSeeCmd): handle see for unmapped
	text displays. [Bug #641778]
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.9.2.1 2002/06/15 03:02:09 dgp Exp $
a3320 1
     * dlPtr may be NULL if the widget is not mapped. [Bug #641778]
a3323 4
    if (dlPtr == NULL) {
	return TCL_OK;
    }

d3325 1
a3325 2
    for (chunkPtr = dlPtr->chunkPtr; chunkPtr!=NULL ;
	 chunkPtr = chunkPtr->nextPtr) {
a3334 1
     * chunkPtr is NULL if trying to see in elided region
d3337 18
a3354 9
    if (chunkPtr != NULL) {
	(*chunkPtr->bboxProc)(chunkPtr, byteCount, dlPtr->y + dlPtr->spaceAbove,
		dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
		dlPtr->baseline - dlPtr->spaceAbove, &x, &y, &width,
		&height);
	delta = x - dInfoPtr->curPixelOffset;
	oneThird = lineWidth/3;
	if (delta < 0) {
	    if (delta < -oneThird) {
d3357 1
a3357 1
		dInfoPtr->newByteOffset -= ((-delta) + textPtr->charWidth - 1)
d3361 1
a3361 11
	    delta -= (lineWidth - width);
	    if (delta > 0) {
		if (delta > oneThird) {
		    dInfoPtr->newByteOffset = (x - lineWidth/2)/textPtr->charWidth;
		} else {
		    dInfoPtr->newByteOffset += (delta + textPtr->charWidth - 1)
			/ textPtr->charWidth;
		}
	    } else {
		return TCL_OK;
	    }
d3363 1
a3363 1
    }
@


1.9.8.1
log
@Merging with TOT as of 06/09/2002.
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id$
a98 10
 * The following macro is used to compare two floating-point numbers
 * to within a certain degree of scale.  Direct comparison fails on
 * processors where the processor and memory representations of FP
 * numbers of a particular precision is different (e.g. Intel)
 */

#define FP_EQUAL_SCALE(double1, double2, scaleFactor) \
    (fabs((double1)-(double2))*((scaleFactor)+1.0) < 0.3)

/*
a1882 19
	    /* Not visible - bail out now */
	    if (rightX + xOffset <= 0) {
	        leftX = rightX;
		continue;
	    }

	    /*
	     * Trim the start position for drawing to be no further away than
	     * -borderWidth. The reason is that on many X servers drawing from
	     * -32768 (or less) to +something simply does not display
	     * correctly. [Patch #541999]
	     */
	    if ((leftX + xOffset) < -(sValuePtr->borderWidth)) {
	        leftX = -sValuePtr->borderWidth - xOffset;
	    }
	    if ((rightX - leftX) > 32767) {
	        rightX = leftX + 32767;
	    }

d3868 1
a3868 1
    char buffer[TCL_DOUBLE_SPACE * 2 + 1];
d3889 1
a3889 2
    if (FP_EQUAL_SCALE(first, dInfoPtr->xScrollFirst, dInfoPtr->maxLength) &&
	FP_EQUAL_SCALE(last,  dInfoPtr->xScrollLast,  dInfoPtr->maxLength)) {
d3939 1
a3939 1
    char buffer[TCL_DOUBLE_SPACE * 2 + 1];
d3974 1
a3974 2
    if (FP_EQUAL_SCALE(first, dInfoPtr->yScrollFirst, totalLines) &&
	FP_EQUAL_SCALE(last,  dInfoPtr->yScrollLast,  totalLines)) {
@


1.9.8.2
log
@merged with trunk at tag macosx-8-4-merge-2002-08-20-trunk
@
text
@d3302 1
a3302 1
    CONST char **argv;		/* Argument strings.  Someone else has already
d3424 1
a3424 1
    CONST char **argv;		/* Argument strings.  Someone else has already
d3612 1
a3612 1
    CONST char **argv;		/* Argument strings.  Someone else has already
d3778 1
a3778 1
    CONST char **argv;		/* Argument strings.  Someone else has already
d4414 1
a4414 1
 * Results:
@


1.9.6.1
log
@2001-02-28  Don Porter  <dgp@@users.sourceforge.net>

        BRANCH dgp-privates-into-namespace:  Feature branch to move all
        of Tk's private commands and variable into the ::tk namespace
        and its children.

        * doc/tkvars.n:  Documented private variable tkPriv renamed tk::Priv.

        * generic/tkBind.c:
        * generic/tkMenu.c:
        * generic/tkTextDisp.c:
        * library/bgerror.tcl:
        * library/button.tcl:
        * library/choosedir.tcl:
        * library/clrpick.tcl:
        * library/comdlg.tcl:
        * library/console.tcl:
        * library/dialog.tcl:
        * library/entry.tcl:
        * library/focus.tcl:
        * library/listbox.tcl:
        * library/menu.tcl:
        * library/msgbox.tcl:
        * library/optMenu.tcl:
        * library/palette.tcl:
        * library/scale.tcl:
        * library/scrlbar.tcl:
        * library/spinbox.tcl:
        * library/tclIndex:
        * library/tearoff.tcl:
        * library/text.tcl:
        * library/tk.tcl:
        * library/tkfbox.tcl:
        * library/xmfbox.tcl:
        * mac/tkMacMenu.c:
        * tests/clrpick.test:
        * tests/filebox.test:
        * tests/macMenu.test:
        * tests/menu.test:
        * tests/menuDraw.test:
        * tests/msgbox.test:
        * tests/text.test:
        * tests/textDisp.test:
        * tests/unixMenu.test:
        * tests/winMenu.test:
        * tests/xmfbox.test:
        * unix/tkUnixDialog.c:  All Tk commands matching ::tk[A-Z]* and
        all Tk private variables in the global namespace were renamed to
        live in the namespace ::tk or one of its children.
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.9 2000/01/06 02:18:59 hobbs Exp $
d1307 1
a1307 1
		Tcl_SetVar2(textPtr->interp, "tk::textRelayout", (char *) NULL,
d1468 1
a1468 1
		    Tcl_SetVar2(textPtr->interp, "tk::textRelayout",
d2174 1
a2174 1
	Tcl_SetVar2(interp, "tk::textRelayout", (char *) NULL, "",
d2195 1
a2195 1
	Tcl_SetVar2(interp, "tk::textRedraw", (char *) NULL, "",
d2341 1
a2341 1
	    Tcl_SetVar2(interp, "tk::textRedraw", (char *) NULL, "borders",
d2433 1
a2433 1
		    Tcl_SetVar2(textPtr->interp, "tk::textRedraw",
d2462 1
a2462 1
	    Tcl_SetVar2(textPtr->interp, "tk::textRedraw",
@


1.9.6.2
log
@	BRANCH dgp-privates-into-namespace:
	* generic/tkTextDisp.c:
	* library/unsupported.tcl:
	* tests/textDisp.test: Restored the global variables tk_textRedraw
	and tk_textRelayout.  Since they match tk_*, they should remain
	publicly available until at least Tk 9.
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.9.6.1 2001/02/28 23:29:55 dgp Exp $
d1307 1
a1307 1
		Tcl_SetVar2(textPtr->interp, "tk_textRelayout", (char *) NULL,
d1468 1
a1468 1
		    Tcl_SetVar2(textPtr->interp, "tk_textRelayout",
d2174 1
a2174 1
	Tcl_SetVar2(interp, "tk_textRelayout", (char *) NULL, "",
d2195 1
a2195 1
	Tcl_SetVar2(interp, "tk_textRedraw", (char *) NULL, "",
d2341 1
a2341 1
	    Tcl_SetVar2(interp, "tk_textRedraw", (char *) NULL, "borders",
d2433 1
a2433 1
		    Tcl_SetVar2(textPtr->interp, "tk_textRedraw",
d2462 1
a2462 1
	    Tcl_SetVar2(textPtr->interp, "tk_textRedraw",
@


1.8
log
@	* generic/tkStubInit.c:
	* generic/tkDecls.h:
	* generic/tkIntXlibDecls.h:
	* generic/tkInt.decls: added XSetDashes and XWarpPointer
	* generic/tk.decls: added Tk_CreateSmoothMethod, and reserved
	two spots
	* generic/tk.h: added Tk_SmoothMethod struct,
	state item to canvas record, #defines for item state,
	support for using old char*-based canvas item C creation
	procedures with -DUSE_OLD_CANVAS,
	Tk_Dash, Tk_TSOffset (-offsets) & Tk_Outline structs and #defs,
	decls for dash, outline and postscript routines
	* generic/tkBind.c: added support for Quadruple clicks, and added
	the -warp option to 'event' with pointer warping routines
	* xlib/xgc.c:
	* generic/tkRectOval.c:
	* generic/tkCanvArc.c:
	* generic/tkCanvBmap.c:
	* generic/tkCanvImg.c:
	* generic/tkCanvLine.c:
	* generic/tkCanvPoly.c:
	* generic/tkCanvPs.c:
	* generic/tkCanvText.c:
	* generic/tkCanvUtil.c:
	* generic/tkCanvWind.c:
	* generic/tkCanvas.c:
	* generic/tkCanvas.h: Canvas and items received overhaul to with
	the addition of the dash patch (Nijtmans, et al)  This includes
	objectification of the 'canvas' command, as well as support for
	(where appropriate) dashes in items, extended stipple support,
	state for all items, and postscript generation of images and
	windows.  See the new canvas man page for related docs.
	* generic/tkEntry.c: added entry widget validation, see entry.n
	* generic/tkEvent.c: on simulated events, ButtonPress should
	be matched with ButtonRelease to be correct
	* generic/tkFont.c: corrected possible null reference
	* generic/tkFrame.c: made frame a Tcl_Obj based command
	* generic/tkGet.c: added TkGetDoublePixels
	* generic/tkImage.c: bug fixes from Img patch and new
	Tk_PostscriptImage and Tk_SetTSOrigin functions
	* generic/tkImgBmap.c: new ImgBmapPostscript function
	* generic/tkImgPhoto.c: new Tk_CreatePhotoOption, Tk_DitherPhoto
	* generic/tkInt.h: declarations for some new functions
	* generic/tkMessage.c: reworked relief drawing
	* generic/tkOldConfig.c: added TK_CONFIG_OBJS so old style
	ConfigureWidget calls can pass in Tcl_Obj arrays
	* generic/tkScrollbar.c:
	* generic/tkScrollbar.h: made -orient use an option table
	* generic/tkText.c:
	* generic/tkText.h: made -wrap and -state use option tables
	* generic/tkTextBTree.c:
	* generic/tkTextDisp.c:
	* generic/tkTextImage.c:
	* generic/tkTextMark.c:
	* generic/tkTextTag.c:
	* generic/tkTextWind.c: added support for -elide and -state hidden
	* generic/tkTrig.c: changed TkMakeBezierCurve to support returning
	the upper limit of points needed for spline
	* generic/tkUtil.c: new option table parsing routines
	* generic/tkWindow.c: init'ing of warp stuff, mouseButtonState
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.7 1999/08/10 05:07:36 jingham Exp $
d505 1
a505 1
    int underlinePrio, statePrio, justifyPrio, offsetPrio;
d520 1
a520 1
    underlinePrio = statePrio = justifyPrio = offsetPrio = -1;
d534 1
a534 1
    styleValues.elide = (textPtr->state == TK_STATE_HIDDEN);
d639 4
a642 4
	if ((tagPtr->state != TK_STATE_NULL)
		&& (tagPtr->priority > statePrio)) {
	    styleValues.elide = (tagPtr->state == TK_STATE_HIDDEN);
	    statePrio = tagPtr->priority;
a823 1

d825 19
a843 15
	* special case entirely elide line as there may be 1000s or more
	*/
	elide = TkTextIsElided(textPtr, indexPtr);		/* save a malloc */
	if (elide && indexPtr->byteIndex==0) {
		maxBytes = 0;
		for (segPtr = indexPtr->linePtr->segPtr; elide && segPtr!=NULL; segPtr = segPtr->nextPtr) {
			if ((elidesize = segPtr->size) > 0) {
				maxBytes += elidesize;

			/* if have tag toggle, chance that invisibility state changed, so bail out */
			} else if (segPtr->typePtr == &tkTextToggleOffType || segPtr->typePtr == &tkTextToggleOnType) {
				if (segPtr->body.toggle.tagPtr->state!=TK_STATE_NULL) {
					elide = (segPtr->typePtr == &tkTextToggleOffType) ^ (segPtr->body.toggle.tagPtr->state==TK_STATE_HIDDEN);
				}
			}
d845 2
d848 4
a851 5
		if (elide) {
		    dlPtr->byteCount = maxBytes;
		    dlPtr->spaceAbove = dlPtr->spaceBelow = dlPtr->length = 0;
		    return dlPtr;
		}
d853 1
a853 2


d885 2
a886 2
	    (byteOffset > 0) && (byteOffset >= segPtr->size);
	    byteOffset -= segPtr->size, segPtr = segPtr->nextPtr) {
d891 8
a898 5

	/* every line still gets at least one chunk due to expectations in rest of code,
	   but able to skip elided portions of line quickly */
	/* if current chunk elided and last chunk was too, coalese */
	if (elide && lastChunkPtr!=NULL && lastChunkPtr->displayProc == NULL/*ElideDisplayProc*/) {
d900 13
a912 10
		   curIndex.byteIndex += elidesize;
		   lastChunkPtr->numBytes += elidesize;
		   breakByteOffset = lastChunkPtr->breakIndex = lastChunkPtr->numBytes;

	    /* if have tag toggle, chance that invisibility state changed */
	    } else if (segPtr->typePtr == &tkTextToggleOffType || segPtr->typePtr == &tkTextToggleOnType) {
		   if (segPtr->body.toggle.tagPtr->state!=TK_STATE_NULL) {
			  elide = (segPtr->typePtr == &tkTextToggleOffType) ^
 (segPtr->body.toggle.tagPtr->state==TK_STATE_HIDDEN);
		   }
d917 3
a919 2
	    if (segPtr == NULL && chunkPtr != NULL) ckfree((char *) chunkPtr);

a921 1

@


1.7
log
@New Function: TkpDrawHighlightBorder.  Use this in place of Tk_DrawFocusHighlight.  The latter did not work on the Mac, since you need to know both foreground & background color when you draw the active focus ring.
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.6 1999/04/21 21:53:28 rjohnson Exp $
d62 4
a65 2
    Tk_Uid wrapMode;		/* How to handle wrap-around for this tag.
				 * One of char, none, or text. */
d164 1
d171 1
d322 15
d505 1
a505 1
    int underlinePrio, justifyPrio, offsetPrio;
d520 1
a520 1
    underlinePrio = justifyPrio = offsetPrio = -1;
d534 1
d639 6
a644 1
	if ((tagPtr->wrapMode != NULL)
d684 3
a686 1
    mask = GCForeground|GCFont;
a687 1
    gcValues.font = Tk_FontId(styleValues.tkfont);
d731 3
a733 1
	Tk_FreeGC(textPtr->display, stylePtr->fgGC);
d785 1
a785 1
    Tk_Uid wrapMode;			/* Wrap mode to use for this line. */
d806 1
a806 1
    int byteOffset, ascent, descent, code;
d824 28
d863 1
d871 1
a871 1
    wrapMode = Tk_GetUid("char");
d888 26
d924 1
d940 1
a940 1
	    if (wrapMode == Tk_GetUid("none")) {
d958 1
a958 1
	if (justify == TK_JUSTIFY_LEFT) {
d971 12
d984 2
a985 1
	chunkPtr->x = x;
d1057 1
d1106 1
d1128 1
a1128 1
    if (wrapMode == Tk_GetUid("none")) {
d1430 1
d1435 1
d1665 2
d1690 1
a1690 1
    if (textPtr->state == Tk_GetUid("normal")) {
d1733 1
d1740 3
d1831 1
d1905 1
a1905 1
    if (prevPtr != NULL) {
d2026 1
a2026 1
    if (dlPtr->nextPtr != NULL) {
d2419 1
d2436 1
d3316 1
a3316 1
    for (chunkPtr = dlPtr->chunkPtr; ; chunkPtr = chunkPtr->nextPtr) {
d3328 1
d3354 1
a3354 1
    }
d3504 1
a3504 1
    
d3533 1
d3746 1
a3746 1
    int c, x, y, totalScroll, newByte, maxByte;
d3750 1
a3750 1
    if (argc != 5) {
d3752 2
a3753 1
		argv[0], " scan mark|dragto x y\"", (char *) NULL);
d3762 2
d3779 1
a3779 1
	newByte = dInfoPtr->scanMarkIndex + (10*(dInfoPtr->scanMarkX - x))
d3795 1
a3795 1
	totalScroll = (10*(dInfoPtr->scanMarkY - y)) / fm.linespace;
d4079 1
a4079 1
    register DLine *dlPtr;
d4112 1
a4112 1
    for (dlPtr = dInfoPtr->dLinePtr; y >= (dlPtr->y + dlPtr->height);
d4114 1
d4125 2
d4300 1
d4319 3
a4321 2
    *xPtr = dInfoPtr->x - dInfoPtr->curPixelOffset + dlPtr->chunkPtr->x;
    *widthPtr = dlPtr->length - dlPtr->chunkPtr->x;
d4332 35
d4405 2
a4406 2
    Tk_Uid wrapMode;		/* How to handle line wrapping: char, 
				 * none, or text. */
d4500 1
a4500 1
    if (wrapMode != Tk_GetUid("word")) {
d4594 1
a4594 1
    if (ciPtr->numBytes > offsetBytes) {
@


1.6
log
@
Merged 8-1-0 into mainline.
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.5 1999/04/16 18:30:28 surles Exp $
d2244 1
a2244 1
	    GC gc;
d2246 2
d2249 1
a2249 1
		gc = Tk_GCForColor(textPtr->highlightColorPtr,
d2251 2
d2254 2
a2255 2
		gc = Tk_GCForColor(textPtr->highlightBgColorPtr,
			Tk_WindowId(textPtr->tkwin));
a2256 2
	    Tk_DrawFocusHighlight(textPtr->tkwin, gc, textPtr->highlightWidth,
		    Tk_WindowId(textPtr->tkwin));
@


1.5
log
@fixed Tk comment errors
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.4 1999/04/16 01:51:23 stanton Exp $
@


1.4
log
@* Merged 8.1 branch into the main trunk
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id$
d4939 1
a4939 1
 *	assumption that Tk_DisplayChars will be used to actually display
@


1.4.2.1
log
@
Merged with mainline.
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.5 1999/04/16 18:30:28 surles Exp $
d4939 1
a4939 1
 *	assumption that Tk_DrawTextLayout will be used to actually display
@


1.3
log
@integrated stubs into 8.0 main branch
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.2.4.1 1999/03/06 00:08:07 redman Exp $
d63 1
a63 2
				 * One of tkTextCharUid, tkTextNoneUid,
				 * or tkTextWordUid. */
d104 1
a104 1
    int count;			/* Number of characters accounted for by this
d205 1
a205 1
    int newCharOffset;		/* Desired x scroll position, measured as the
d228 3
a230 2
    int scanMarkChar;		/* Character that was at the left edge of
				 * the window when the scan started. */
d261 3
a263 3
    int numChars;		/* Number of characters to display. */
    char chars[4];		/* Characters to display.  Actual size
				 * will be numChars, not 4.  THIS MUST BE
d338 1
a338 1
			    CONST char *source, int maxChars, int startX,
d388 1
a388 1
    dInfoPtr->newCharOffset = 0;
d395 1
a395 1
    dInfoPtr->scanMarkChar = 0;
d746 3
a748 2
    int breakCharOffset;		/* Character within breakChunkPtr just
					 * to right of best break point. */
d752 2
a753 1
					 * style for first character in line. */
d767 1
a767 1
    int maxChars;			/* Maximum number of characters to
d769 3
a771 2
    TkTextTabArray *tabArrayPtr;	/* Tab stops for line;  taken from
					 * style for first character on line. */
d775 1
a775 1
					 * lines with numChars > 0.  Used to
d778 1
a778 1
    int offset, ascent, descent, code;
d787 1
a787 1
    dlPtr->count = 0;
d808 1
a808 1
    breakCharOffset = 0;
d814 1
a814 1
    wrapMode = tkTextCharUid;
d824 3
a826 3
    for (offset = curIndex.charIndex, segPtr = curIndex.linePtr->segPtr;
	    (offset > 0) && (offset >= segPtr->size);
	    offset -= segPtr->size, segPtr = segPtr->nextPtr) {
d833 1
a833 1
	    offset = 0;
d853 1
a853 1
	    x = ((curIndex.charIndex == 0)
d856 2
a857 2
	    if (wrapMode == tkTextNoneUid) {
		maxX = INT_MAX;
d873 1
a873 1
	maxChars = segPtr->size - offset;
d878 1
a878 1
		for (p = segPtr->body.chars  + offset; *p != 0; p++) {
d880 1
a880 1
			maxChars = (p + 1 - segPtr->body.chars) - offset;
d890 1
a890 1
		offset, maxX-tabSize, maxChars, noCharsYet, wrapMode,
d901 1
a901 1
		offset = 0;
d915 1
a915 1
	if (chunkPtr->numChars > 0) {
d927 1
a927 1
	    breakCharOffset = chunkPtr->breakIndex;
d931 1
a931 1
	if (chunkPtr->numChars != maxChars) {
d950 1
a950 1
	    if (tabSize >= (maxX - x)) {
d954 4
a957 4
	curIndex.charIndex += chunkPtr->numChars;
	offset += chunkPtr->numChars;
	if (offset >= segPtr->size) {
	    offset = 0;
d983 1
a983 1
	breakCharOffset = breakChunkPtr->numChars;
d986 1
a986 1
	    || (breakCharOffset != lastChunkPtr->numChars))) {
d997 1
a997 1
	if (breakCharOffset != breakChunkPtr->numChars) {
d999 1
a999 1
	    segPtr = TkTextIndexToSeg(&breakIndex, &offset);
d1001 1
a1001 1
		    segPtr, offset, maxX, breakCharOffset, 0, 
d1018 1
a1018 1
     * like its height, length, and total number of characters.  Also
d1029 1
a1029 1
    if (wrapMode == tkTextNoneUid) {
d1044 1
a1044 1
	dlPtr->count += chunkPtr->numChars;
d1067 1
a1067 1
    if (dlPtr->index.charIndex == 0) {
d1220 1
a1220 1
	    if (index.charIndex == dlPtr->index.charIndex) {
d1231 1
a1231 1
	    if (index.charIndex < dlPtr->index.charIndex) {
d1258 1
a1258 1
	TkTextIndexForwChars(&index, dlPtr->count, &index);
d1309 1
a1309 1
	int lineNum, spaceLeft, charsToCount;
d1322 3
a1324 3
	charsToCount = dInfoPtr->dLinePtr->index.charIndex;
	if (charsToCount == 0) {
	    charsToCount = INT_MAX;
d1329 1
a1329 1
	    index.charIndex = 0;
d1335 3
a1337 3
		TkTextIndexForwChars(&index, dlPtr->count, &index);
		charsToCount -= dlPtr->count;
	    } while ((charsToCount > 0)
d1364 1
a1364 1
	    charsToCount = INT_MAX;
d1451 2
a1452 2
    if (dInfoPtr->newCharOffset > maxOffset) {
	dInfoPtr->newCharOffset = maxOffset;
d1454 2
a1455 2
    if (dInfoPtr->newCharOffset < 0) {
	dInfoPtr->newCharOffset = 0;
d1457 1
a1457 1
    pixelOffset = dInfoPtr->newCharOffset * textPtr->charWidth;
d1587 1
a1587 1
    if (textPtr->state == tkNormalUid) {
d2601 1
a2601 1
    rounded.charIndex = 0;
d2677 1
a2677 1
	index2Ptr = TkTextMakeIndex(textPtr->tree,
d2731 1
a2731 1
	if (curIndexPtr->charIndex == 0) {
d2737 1
a2737 1
	    tmp.charIndex -= 1;
d2756 1
a2756 1
		&& (endPtr->index.charIndex < endIndexPtr->charIndex)) {
d2868 1
a2868 1
    if (textPtr->topIndex.charIndex != 0) {
d2935 1
a2935 1
	if (indexPtr->charIndex == 0) {
d2963 1
a2963 1
		&& (dlPtr->index.charIndex <= indexPtr->charIndex)) {
d3061 2
a3062 2
    int charsToCount;		/* Maximum number of characters to measure
				 * in current line. */
d3069 1
a3069 1
    charsToCount = srcPtr->charIndex + 1;
d3076 1
a3076 1
	 * part up through srcPtr (charsToCount is non-infinite to
d3083 1
a3083 1
	index.charIndex = 0;
d3089 3
a3091 3
	    TkTextIndexForwChars(&index, dlPtr->count, &index);
	    charsToCount -= dlPtr->count;
	} while ((charsToCount > 0) && (index.linePtr == dlPtr->index.linePtr));
d3118 1
a3118 1
	charsToCount = INT_MAX;		/* Consider all chars. in next line. */
d3126 1
a3126 1
    TkTextMakeIndex(textPtr->tree, 0, 0, dstPtr);
d3158 1
a3158 1
    int x, y, width, height, lineWidth, charCount, oneThird, delta;
d3203 1
a3203 1
    charCount = index.charIndex - dlPtr->index.charIndex;
d3205 1
a3205 1
	if (charCount < chunkPtr->numChars) {
d3208 1
a3208 1
	charCount -= chunkPtr->numChars;
d3216 1
a3216 1
    (*chunkPtr->bboxProc)(chunkPtr, charCount, dlPtr->y + dlPtr->spaceAbove,
d3224 1
a3224 1
	    dInfoPtr->newCharOffset = (x - lineWidth/2)/textPtr->charWidth;
d3226 1
a3226 1
	    dInfoPtr->newCharOffset -= ((-delta) + textPtr->charWidth - 1)
d3233 1
a3233 1
		dInfoPtr->newCharOffset = (x - lineWidth/2)/textPtr->charWidth;
d3235 1
a3235 1
		dInfoPtr->newCharOffset += (delta + textPtr->charWidth - 1)
d3290 1
a3290 1
    newOffset = dInfoPtr->newCharOffset;
d3311 1
a3311 1
	    newOffset += charsPerPage*count;
d3318 1
a3318 1
    dInfoPtr->newCharOffset = newOffset;
d3354 1
a3354 1
    int i, charsToCount, lineNum;
d3367 1
a3367 1
	charsToCount = textPtr->topIndex.charIndex + 1;
d3373 1
a3373 1
	    index.charIndex = 0;
d3379 3
a3381 3
		TkTextIndexForwChars(&index, dlPtr->count, &index);
		charsToCount -= dlPtr->count;
	    } while ((charsToCount > 0)
d3401 1
a3401 1
	    charsToCount = INT_MAX;
d3409 1
a3409 1
	TkTextMakeIndex(textPtr->tree, 0, 0, &textPtr->topIndex);
d3421 1
a3421 1
	    TkTextIndexForwChars(&textPtr->topIndex, dlPtr->count, &new);
d3465 1
a3465 1
    int pickPlace, lineNum, type, charsInLine;
d3503 1
a3503 1
	    TkTextMakeIndex(textPtr->tree, lineNum, 0, &index);
d3538 5
a3542 5
	    TkTextMakeIndex(textPtr->tree, lineNum, 0, &index);
	    charsInLine = TkBTreeCharsInLine(index.linePtr);
	    index.charIndex = (int)((charsInLine * (fraction-lineNum)) + 0.5);
	    if (index.charIndex >= charsInLine) {
		TkTextMakeIndex(textPtr->tree, lineNum+1, 0, &index);
d3580 1
a3580 1
		    TkTextIndexForwChars(&textPtr->topIndex, dlPtr->count,
d3632 1
a3632 1
    int c, x, y, totalScroll, newChar, maxChar;
d3662 1
a3662 1
	newChar = dInfoPtr->scanMarkChar + (10*(dInfoPtr->scanMarkX - x))
d3664 1
a3664 1
	maxChar = 1 + (dInfoPtr->maxLength - (dInfoPtr->maxX - dInfoPtr->x)
d3666 3
a3668 2
	if (newChar < 0) {
	    dInfoPtr->scanMarkChar = newChar = 0;
d3670 3
a3672 2
	} else if (newChar > maxChar) {
	    dInfoPtr->scanMarkChar = newChar = maxChar;
d3675 1
a3675 1
	dInfoPtr->newCharOffset = newChar;
d3684 1
a3684 1
		    (index.charIndex == textPtr->topIndex.charIndex)) {
d3690 1
a3690 1
	dInfoPtr->scanMarkChar = dInfoPtr->newCharOffset;
d3717 1
a3717 1
 *	If report is zero, then interp->result is filled in with
d3721 1
a3721 1
 *	edge.  If report is non-zero, then interp->result isn't modified
d3736 1
a3736 1
					 * stored in interp->result. */
d3742 1
a3742 1
    char buffer[200];
d3759 2
a3760 1
	sprintf(interp->result, "%g %g", first, last);
d3788 1
a3788 1
 *	If report is zero, then interp->result is filled in with
d3792 1
a3792 1
 *	report is non-zero, then interp->result isn't modified directly,
d3807 1
a3807 1
					 * stored in interp->result. */
d3813 1
a3813 1
    char buffer[200];
d3820 3
a3822 3
    first = ((double) TkBTreeLineIndex(dlPtr->index.linePtr))
	    + ((double) dlPtr->index.charIndex)
	    / (TkBTreeCharsInLine(dlPtr->index.linePtr));
d3834 1
a3834 1
	    count = dlPtr->count;
d3840 2
a3841 2
	    + ((double) (dlPtr->index.charIndex + count))
	    / (TkBTreeCharsInLine(dlPtr->index.linePtr));
d3844 2
a3845 1
	sprintf(interp->result, "%g %g", first, last);
d3854 1
a3854 2
    code = Tcl_VarEval(interp, textPtr->yScrollCmd,
	    buffer, (char *) NULL);
d3926 1
a3926 1
    while (indexPtr->charIndex >= (dlPtr->index.charIndex + dlPtr->count)) {
d4018 1
a4018 1
	    indexPtr->charIndex += chunkPtr->numChars,
d4021 2
a4022 1
	    indexPtr->charIndex += chunkPtr->numChars - 1;
d4028 1
a4028 1
     * If the chunk has more than one character in it, ask it which
d4032 2
a4033 2
    if (chunkPtr->numChars > 1) {
	indexPtr->charIndex += (*chunkPtr->measureProc)(chunkPtr, x);
d4070 1
a4070 1
    int index;
d4094 1
a4094 1
    index = indexPtr->charIndex - dlPtr->index.charIndex;
d4099 1
a4099 1
	if (index < chunkPtr->numChars) {
d4102 1
a4102 1
	index -= chunkPtr->numChars;
d4113 1
a4113 1
    (*chunkPtr->bboxProc)(chunkPtr, index, dlPtr->y + dlPtr->spaceAbove,
d4118 1
a4118 1
    if ((index == (chunkPtr->numChars-1)) && (chunkPtr->nextPtr == NULL)) {
d4217 1
a4217 1
 * Results:
d4234 1
a4234 1
TkTextCharLayoutProc(textPtr, indexPtr, segPtr, offset, maxX, maxChars,
d4240 2
a4241 2
    int offset;			/* Offset within segment of first character
				 * to consider. */
d4244 1
a4244 1
    int maxChars;		/* Chunk must not include more than this
d4248 2
a4249 2
    Tk_Uid wrapMode;		/* How to handle line wrapping: tkTextCharUid,
				 * tkTextNoneUid, or tkTextWordUid. */
d4256 1
a4256 1
    int nextX, charsThatFit, count;
d4274 1
a4274 1
    p = segPtr->body.chars + offset;
d4276 1
a4276 1
    charsThatFit = MeasureChars(tkfont, p, maxChars, chunkPtr->x, maxX, 0,
d4278 6
a4283 4
    if (charsThatFit < maxChars) {
	if ((charsThatFit == 0) && noCharsYet) {
	    charsThatFit = 1;
	    MeasureChars(tkfont, p, 1, chunkPtr->x, INT_MAX, 0, &nextX);
d4285 2
a4286 2
	if ((nextX < maxX) && ((p[charsThatFit] == ' ')
		|| (p[charsThatFit] == '\t'))) {
d4294 1
a4294 1
	    charsThatFit++;
d4296 1
a4296 1
	if (p[charsThatFit] == '\n') {
d4302 1
a4302 1
	    charsThatFit++;
d4304 1
a4304 1
	if (charsThatFit == 0) {
d4321 1
a4321 1
    chunkPtr->numChars = charsThatFit;
d4328 1
a4328 1
	    (sizeof(CharInfo) - 3 + charsThatFit));
d4330 4
a4333 4
    ciPtr->numChars = charsThatFit;
    strncpy(ciPtr->chars, p, (size_t) charsThatFit);
    if (p[charsThatFit-1] == '\n') {
	ciPtr->numChars--;
d4343 2
a4344 2
    if (wrapMode != tkTextWordUid) {
	chunkPtr->breakIndex = chunkPtr->numChars;
d4346 1
a4346 1
	for (count = charsThatFit, p += charsThatFit-1; count > 0;
d4353 1
a4353 1
	if ((charsThatFit+offset) == segPtr->size) {
d4358 1
a4358 1
			chunkPtr->breakIndex = chunkPtr->numChars;
d4405 1
a4405 1
    int offsetChars, offsetX;
d4427 1
a4427 1
    offsetChars = 0;
d4429 2
a4430 2
	offsetChars = MeasureChars(sValuePtr->tkfont, ciPtr->chars,
	    ciPtr->numChars, x, 0, x - chunkPtr->x, &offsetX);
d4437 3
a4439 3
    if (ciPtr->numChars > offsetChars) {
	int numChars = ciPtr->numChars - offsetChars;
	char *string = ciPtr->chars + offsetChars;
d4441 2
a4442 2
	if ((numChars > 0) && (string[numChars - 1] == '\t')) {
	    numChars--;
d4445 1
a4445 1
		numChars, offsetX, y + baseline - sValuePtr->offset);
d4448 2
a4449 3
		    ciPtr->chars + offsetChars, offsetX,
		    y + baseline - sValuePtr->offset,
		    0, numChars);
d4457 1
a4457 1
		    ciPtr->chars + offsetChars, offsetX,
d4460 1
a4460 1
		    0, numChars);
d4522 2
a4523 1
	    chunkPtr->numChars-1, chunkPtr->x, x, 0, &endX);
d4550 1
a4550 1
CharBboxProc(chunkPtr, index, y, lineHeight, baseline, xPtr, yPtr,
d4553 2
a4554 2
    int index;				/* Index of desired character within
					 * the chunk. */
d4573 2
a4574 2
    MeasureChars(chunkPtr->stylePtr->sValuePtr->tkfont, ciPtr->chars, index,
	    chunkPtr->x, 1000000, 0, xPtr);
d4576 1
a4576 1
    if (index == ciPtr->numChars) {
d4584 2
a4585 2
    } else if ((ciPtr->chars[index] == '\t')
	    && (index == (ciPtr->numChars-1))) {
d4594 1
a4594 1
		ciPtr->chars + index, 1, *xPtr, 1000000, 0, widthPtr);
d4729 1
a4729 1
	for (p = ciPtr->chars, i = 0; i < ciPtr->numChars; p++, i++) {
d4750 1
a4750 1
		ciPtr->chars, decimal, decimalChunkPtr->x, 1000000, 0, &curX);
d4775 1
a4775 1
    MeasureChars(textPtr->tkfont, " ", 1, 0, INT_MAX, 0, &spaceWidth);
d4880 1
a4880 1
    MeasureChars(textPtr->tkfont, " ", 1, 0, INT_MAX, 0, &spaceWidth);
d4950 1
a4950 1
 *	The return value is the number of characters from source
d4963 1
a4963 1
MeasureChars(tkfont, source, maxChars, startX, maxX, tabOrigin, nextXPtr)
d4967 1
a4967 1
    int maxChars;		/* Maximum # of characters to consider from
d4984 1
a4984 1
    end = source + maxChars;
d5004 1
a5004 1
	if (curX >= maxX) {
@


1.2
log
@Replaced SCCS strings, fixed binary files
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id$
d20 4
@


1.2.4.1
log
@Changes for Tk Stubs implementation.
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.2 1998/09/14 18:23:18 stanton Exp $
a19 4

#ifdef __WIN32__
#include "tkWinInt.h"
#endif
@


1.1
log
@Initial revision
@
text
@d14 1
a14 1
 * SCCS: @@(#) tkTextDisp.c 1.124 97/07/11 18:01:03
@


1.1.4.1
log
@initial tk8.1a2 version
@
text
@d14 1
a14 1
 * SCCS: @@(#) tkTextDisp.c 1.127 98/01/08 13:41:28
d101 1
a101 1
    int byteCount;		/* Number of bytes accounted for by this
d202 1
a202 1
    int newByteOffset;		/* Desired x scroll position, measured as the
d225 2
a226 3
    int scanMarkIndex;		/* Byte index of character that was at the
				 * left edge of the window when the scan
				 * started. */
d257 3
a259 3
    int numBytes;		/* Number of bytes to display. */
    char chars[4];		/* UTF characters to display.  Actual size
				 * will be numBytes, not 4.  THIS MUST BE
d334 1
a334 1
			    CONST char *source, int maxBytes, int startX,
d384 1
a384 1
    dInfoPtr->newByteOffset = 0;
d391 1
a391 1
    dInfoPtr->scanMarkIndex = 0;
d742 2
a743 3
    int breakByteOffset;		/* Byte offset of character within
					 * breakChunkPtr just to right of best
					 * break point. */
d747 1
a747 2
					 * style for the first character in
					 * line. */
d761 1
a761 1
    int maxBytes;			/* Maximum number of bytes to
d763 2
a764 3
    TkTextTabArray *tabArrayPtr;	/* Tab stops for line; taken from
					 * style for the first character on
					 * line. */
d768 1
a768 1
					 * lines with numBytes > 0.  Used to
d771 1
a771 1
    int byteOffset, ascent, descent, code;
d780 1
a780 1
    dlPtr->byteCount = 0;
d801 1
a801 1
    breakByteOffset = 0;
d817 3
a819 3
    for (byteOffset = curIndex.byteIndex, segPtr = curIndex.linePtr->segPtr;
	    (byteOffset > 0) && (byteOffset >= segPtr->size);
	    byteOffset -= segPtr->size, segPtr = segPtr->nextPtr) {
d826 1
a826 1
	    byteOffset = 0;
d846 1
a846 1
	    x = ((curIndex.byteIndex == 0)
d850 1
a850 1
		maxX = -1;
d866 1
a866 1
	maxBytes = segPtr->size - byteOffset;
d871 1
a871 1
		for (p = segPtr->body.chars  + byteOffset; *p != 0; p++) {
d873 1
a873 1
			maxBytes = (p + 1 - segPtr->body.chars) - byteOffset;
d883 1
a883 1
		byteOffset, maxX-tabSize, maxBytes, noCharsYet, wrapMode,
d894 1
a894 1
		byteOffset = 0;
d908 1
a908 1
	if (chunkPtr->numBytes > 0) {
d920 1
a920 1
	    breakByteOffset = chunkPtr->breakIndex;
d924 1
a924 1
	if (chunkPtr->numBytes != maxBytes) {
d943 1
a943 1
	    if ((maxX >= 0) && (tabSize >= maxX - x)) {
d947 4
a950 4
	curIndex.byteIndex += chunkPtr->numBytes;
	byteOffset += chunkPtr->numBytes;
	if (byteOffset >= segPtr->size) {
	    byteOffset = 0;
d976 1
a976 1
	breakByteOffset = breakChunkPtr->numBytes;
d979 1
a979 1
	    || (breakByteOffset != lastChunkPtr->numBytes))) {
d990 1
a990 1
	if (breakByteOffset != breakChunkPtr->numBytes) {
d992 1
a992 1
	    segPtr = TkTextIndexToSeg(&breakIndex, &byteOffset);
d994 1
a994 1
		    segPtr, byteOffset, maxX, breakByteOffset, 0, 
d1011 1
a1011 1
     * like its height, length, and total number of bytes.  Also
d1037 1
a1037 1
	dlPtr->byteCount += chunkPtr->numBytes;
d1060 1
a1060 1
    if (dlPtr->index.byteIndex == 0) {
d1213 1
a1213 1
	    if (index.byteIndex == dlPtr->index.byteIndex) {
d1224 1
a1224 1
	    if (index.byteIndex < dlPtr->index.byteIndex) {
d1251 1
a1251 1
	TkTextIndexForwBytes(&index, dlPtr->byteCount, &index);
d1302 1
a1302 1
	int lineNum, spaceLeft, bytesToCount;
d1315 3
a1317 3
	bytesToCount = dInfoPtr->dLinePtr->index.byteIndex;
	if (bytesToCount == 0) {
	    bytesToCount = INT_MAX;
d1322 1
a1322 1
	    index.byteIndex = 0;
d1328 3
a1330 3
		TkTextIndexForwBytes(&index, dlPtr->byteCount, &index);
		bytesToCount -= dlPtr->byteCount;
	    } while ((bytesToCount > 0)
d1357 1
a1357 1
	    bytesToCount = INT_MAX;
d1444 2
a1445 2
    if (dInfoPtr->newByteOffset > maxOffset) {
	dInfoPtr->newByteOffset = maxOffset;
d1447 2
a1448 2
    if (dInfoPtr->newByteOffset < 0) {
	dInfoPtr->newByteOffset = 0;
d1450 1
a1450 1
    pixelOffset = dInfoPtr->newByteOffset * textPtr->charWidth;
d2594 1
a2594 1
    rounded.byteIndex = 0;
d2670 1
a2670 1
	index2Ptr = TkTextMakeByteIndex(textPtr->tree,
d2724 1
a2724 1
	if (curIndexPtr->byteIndex == 0) {
d2730 1
a2730 1
	    tmp.byteIndex -= 1;
d2749 1
a2749 1
		&& (endPtr->index.byteIndex < endIndexPtr->byteIndex)) {
d2861 1
a2861 1
    if (textPtr->topIndex.byteIndex != 0) {
d2928 1
a2928 1
	if (indexPtr->byteIndex == 0) {
d2956 1
a2956 1
		&& (dlPtr->index.byteIndex <= indexPtr->byteIndex)) {
d3054 2
a3055 2
    int bytesToCount;		/* Maximum number of bytes to measure in
				 * current line. */
d3062 1
a3062 1
    bytesToCount = srcPtr->byteIndex + 1;
d3069 1
a3069 1
	 * part up through srcPtr (bytesToCount is non-infinite to
d3076 1
a3076 1
	index.byteIndex = 0;
d3082 3
a3084 3
	    TkTextIndexForwBytes(&index, dlPtr->byteCount, &index);
	    bytesToCount -= dlPtr->byteCount;
	} while ((bytesToCount > 0) && (index.linePtr == dlPtr->index.linePtr));
d3111 1
a3111 1
	bytesToCount = INT_MAX;		/* Consider all chars. in next line. */
d3119 1
a3119 1
    TkTextMakeByteIndex(textPtr->tree, 0, 0, dstPtr);
d3151 1
a3151 1
    int x, y, width, height, lineWidth, byteCount, oneThird, delta;
d3196 1
a3196 1
    byteCount = index.byteIndex - dlPtr->index.byteIndex;
d3198 1
a3198 1
	if (byteCount < chunkPtr->numBytes) {
d3201 1
a3201 1
	byteCount -= chunkPtr->numBytes;
d3209 1
a3209 1
    (*chunkPtr->bboxProc)(chunkPtr, byteCount, dlPtr->y + dlPtr->spaceAbove,
d3217 1
a3217 1
	    dInfoPtr->newByteOffset = (x - lineWidth/2)/textPtr->charWidth;
d3219 1
a3219 1
	    dInfoPtr->newByteOffset -= ((-delta) + textPtr->charWidth - 1)
d3226 1
a3226 1
		dInfoPtr->newByteOffset = (x - lineWidth/2)/textPtr->charWidth;
d3228 1
a3228 1
		dInfoPtr->newByteOffset += (delta + textPtr->charWidth - 1)
d3283 1
a3283 1
    newOffset = dInfoPtr->newByteOffset;
d3304 1
a3304 1
	    newOffset += charsPerPage * count;
d3311 1
a3311 1
    dInfoPtr->newByteOffset = newOffset;
d3347 1
a3347 1
    int i, bytesToCount, lineNum;
d3360 1
a3360 1
	bytesToCount = textPtr->topIndex.byteIndex + 1;
d3366 1
a3366 1
	    index.byteIndex = 0;
d3372 3
a3374 3
		TkTextIndexForwBytes(&index, dlPtr->byteCount, &index);
		bytesToCount -= dlPtr->byteCount;
	    } while ((bytesToCount > 0)
d3394 1
a3394 1
	    bytesToCount = INT_MAX;
d3402 1
a3402 1
	TkTextMakeByteIndex(textPtr->tree, 0, 0, &textPtr->topIndex);
d3414 1
a3414 1
	    TkTextIndexForwBytes(&textPtr->topIndex, dlPtr->byteCount, &new);
d3458 1
a3458 1
    int pickPlace, lineNum, type, bytesInLine;
d3496 1
a3496 1
	    TkTextMakeByteIndex(textPtr->tree, lineNum, 0, &index);
d3531 5
a3535 5
	    TkTextMakeByteIndex(textPtr->tree, lineNum, 0, &index);
	    bytesInLine = TkBTreeBytesInLine(index.linePtr);
	    index.byteIndex = (int)((bytesInLine * (fraction-lineNum)) + 0.5);
	    if (index.byteIndex >= bytesInLine) {
		TkTextMakeByteIndex(textPtr->tree, lineNum + 1, 0, &index);
d3573 1
a3573 1
		    TkTextIndexForwBytes(&textPtr->topIndex, dlPtr->byteCount,
d3625 1
a3625 1
    int c, x, y, totalScroll, newByte, maxByte;
d3655 1
a3655 1
	newByte = dInfoPtr->scanMarkIndex + (10*(dInfoPtr->scanMarkX - x))
d3657 1
a3657 1
	maxByte = 1 + (dInfoPtr->maxLength - (dInfoPtr->maxX - dInfoPtr->x)
d3659 2
a3660 3
	if (newByte < 0) {
	    newByte = 0;
	    dInfoPtr->scanMarkIndex = 0;
d3662 2
a3663 3
	} else if (newByte > maxByte) {
	    newByte = maxByte;
	    dInfoPtr->scanMarkIndex = maxByte;
d3666 1
a3666 1
	dInfoPtr->newByteOffset = newByte;
d3675 1
a3675 1
		    (index.byteIndex == textPtr->topIndex.byteIndex)) {
d3681 1
a3681 1
	dInfoPtr->scanMarkIndex = dInfoPtr->newByteOffset;
d3708 1
a3708 1
 *	If report is zero, then the interp's result is filled in with
d3712 1
a3712 1
 *	edge.  If report is non-zero, then the interp's result isn't modified
d3727 1
a3727 1
					 * stored in the interp's result. */
d3733 1
a3733 1
    char buffer[TCL_DOUBLE_SPACE * 2];
d3750 1
a3750 2
	sprintf(buffer, "%g %g", first, last);
	Tcl_SetResult(interp, buffer, TCL_VOLATILE);
d3778 1
a3778 1
 *	If report is zero, then the interp's result is filled in with
d3782 1
a3782 1
 *	report is non-zero, then the interp's result isn't modified directly,
d3797 1
a3797 1
					 * stored in the interp's result. */
d3803 1
a3803 1
    char buffer[TCL_DOUBLE_SPACE * 2];
d3810 3
a3812 3
    first = (double) TkBTreeLineIndex(dlPtr->index.linePtr)
	    + (double) dlPtr->index.byteIndex
		    / TkBTreeBytesInLine(dlPtr->index.linePtr);
d3824 1
a3824 1
	    count = dlPtr->byteCount;
d3830 2
a3831 2
	    + ((double) (dlPtr->index.byteIndex + count))
		    / (TkBTreeBytesInLine(dlPtr->index.linePtr));
d3834 1
a3834 2
	sprintf(buffer, "%g %g", first, last);
	Tcl_SetResult(interp, buffer, TCL_VOLATILE);
d3843 2
a3844 1
    code = Tcl_VarEval(interp, textPtr->yScrollCmd, buffer, (char *) NULL);
d3916 1
a3916 1
    while (indexPtr->byteIndex >= (dlPtr->index.byteIndex + dlPtr->byteCount)) {
d4008 1
a4008 1
	    indexPtr->byteIndex += chunkPtr->numBytes,
d4011 1
a4011 2
	    indexPtr->byteIndex += chunkPtr->numBytes;
	    TkTextIndexBackChars(indexPtr, 1, indexPtr);
d4017 1
a4017 1
     * If the chunk has more than one byte in it, ask it which
d4021 2
a4022 2
    if (chunkPtr->numBytes > 1) {
	indexPtr->byteIndex += (*chunkPtr->measureProc)(chunkPtr, x);
d4059 1
a4059 1
    int byteIndex;
d4083 1
a4083 1
    byteIndex = indexPtr->byteIndex - dlPtr->index.byteIndex;
d4088 1
a4088 1
	if (byteIndex < chunkPtr->numBytes) {
d4091 1
a4091 1
	byteIndex -= chunkPtr->numBytes;
d4102 1
a4102 1
    (*chunkPtr->bboxProc)(chunkPtr, byteIndex, dlPtr->y + dlPtr->spaceAbove,
d4107 1
a4107 1
    if ((byteIndex == (chunkPtr->numBytes - 1)) && (chunkPtr->nextPtr == NULL)) {
d4206 1
a4206 1
n * Results:
d4223 1
a4223 1
TkTextCharLayoutProc(textPtr, indexPtr, segPtr, byteOffset, maxX, maxBytes,
d4229 2
a4230 2
    int byteOffset;		/* Byte offset within segment of first
				 * character to consider. */
d4233 1
a4233 1
    int maxBytes;		/* Chunk must not include more than this
d4245 1
a4245 1
    int nextX, bytesThatFit, count;
d4263 1
a4263 1
    p = segPtr->body.chars + byteOffset;
d4265 1
a4265 1
    bytesThatFit = MeasureChars(tkfont, p, maxBytes, chunkPtr->x, maxX, 0,
d4267 4
a4270 6
    if (bytesThatFit < maxBytes) {
	if ((bytesThatFit == 0) && noCharsYet) {
	    Tcl_UniChar ch;
	    
	    bytesThatFit = MeasureChars(tkfont, p, Tcl_UtfToUniChar(p, &ch),
		    chunkPtr->x, -1, 0, &nextX);
d4272 2
a4273 2
	if ((nextX < maxX) && ((p[bytesThatFit] == ' ')
		|| (p[bytesThatFit] == '\t'))) {
d4281 1
a4281 1
	    bytesThatFit++;
d4283 1
a4283 1
	if (p[bytesThatFit] == '\n') {
d4289 1
a4289 1
	    bytesThatFit++;
d4291 1
a4291 1
	if (bytesThatFit == 0) {
d4308 1
a4308 1
    chunkPtr->numBytes = bytesThatFit;
d4315 1
a4315 1
	    (sizeof(CharInfo) - 3 + bytesThatFit));
d4317 4
a4320 4
    ciPtr->numBytes = bytesThatFit;
    strncpy(ciPtr->chars, p, (size_t) bytesThatFit);
    if (p[bytesThatFit - 1] == '\n') {
	ciPtr->numBytes--;
d4331 1
a4331 1
	chunkPtr->breakIndex = chunkPtr->numBytes;
d4333 1
a4333 1
	for (count = bytesThatFit, p += bytesThatFit - 1; count > 0;
d4340 1
a4340 1
	if ((bytesThatFit + byteOffset) == segPtr->size) {
d4345 1
a4345 1
			chunkPtr->breakIndex = chunkPtr->numBytes;
d4392 1
a4392 1
    int offsetBytes, offsetX;
d4414 1
a4414 1
    offsetBytes = 0;
d4416 2
a4417 2
	offsetBytes = MeasureChars(sValuePtr->tkfont, ciPtr->chars,
	    ciPtr->numBytes, x, 0, x - chunkPtr->x, &offsetX);
d4424 3
a4426 3
    if (ciPtr->numBytes > offsetBytes) {
	int numBytes = ciPtr->numBytes - offsetBytes;
	char *string = ciPtr->chars + offsetBytes;
d4428 2
a4429 2
	if ((numBytes > 0) && (string[numBytes - 1] == '\t')) {
	    numBytes--;
d4432 1
a4432 1
		numBytes, offsetX, y + baseline - sValuePtr->offset);
d4435 3
a4437 2
		    ciPtr->chars + offsetBytes, offsetX,
		    y + baseline - sValuePtr->offset, 0, numBytes);
d4445 1
a4445 1
		    ciPtr->chars + offsetBytes, offsetX,
d4448 1
a4448 1
		    0, numBytes);
d4510 1
a4510 2
	    chunkPtr->numBytes - 1, chunkPtr->x, x, 0, &endX);
						/* CHAR OFFSET */
d4537 1
a4537 1
CharBboxProc(chunkPtr, byteIndex, y, lineHeight, baseline, xPtr, yPtr,
d4540 2
a4541 2
    int byteIndex;				/* Byte offset of desired character
					 * within the chunk. */
d4560 2
a4561 2
    MeasureChars(chunkPtr->stylePtr->sValuePtr->tkfont, ciPtr->chars,
	    byteIndex, chunkPtr->x, -1, 0, xPtr);
d4563 1
a4563 1
    if (byteIndex == ciPtr->numBytes) {
d4571 2
a4572 2
    } else if ((ciPtr->chars[byteIndex] == '\t')
	    && (byteIndex == ciPtr->numBytes - 1)) {
d4581 1
a4581 1
		ciPtr->chars + byteIndex, 1, *xPtr, -1, 0, widthPtr);
d4716 1
a4716 1
	for (p = ciPtr->chars, i = 0; i < ciPtr->numBytes; p++, i++) {
d4737 1
a4737 1
		ciPtr->chars, decimal, decimalChunkPtr->x, -1, 0, &curX);
d4762 1
a4762 1
    MeasureChars(textPtr->tkfont, " ", 1, 0, -1, 0, &spaceWidth);
d4867 1
a4867 1
    MeasureChars(textPtr->tkfont, " ", 1, 0, -1, 0, &spaceWidth);
d4937 1
a4937 1
 *	The return value is the number of bytes from source
d4950 1
a4950 1
MeasureChars(tkfont, source, maxBytes, startX, maxX, tabOrigin, nextXPtr)
d4954 1
a4954 1
    int maxBytes;		/* Maximum # of bytes to consider from
d4971 1
a4971 1
    end = source + maxBytes;
d4991 1
a4991 1
	if ((maxX >= 0) && (curX >= maxX)) {
@


1.1.4.2
log
@Merged 8.0.3 changes into 8.1
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.2 1998/09/14 18:23:18 stanton Exp $
@


1.1.4.3
log
@Removed perky gloabal Tk_Uids causing concurrency problems.
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.1.4.2 1998/09/30 02:17:23 stanton Exp $
d1584 1
a1584 1
    if (textPtr->state == TK_STATE_NORMAL) {
@


1.1.4.4
log
@Eliminated last stronghold of static data in the Tk core...until the next one.
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.1.4.3 1999/01/07 02:42:52 lfb Exp $
d59 2
a60 1
				 * One of char, none, or text. */
d811 1
a811 1
    wrapMode = Tk_GetUid("char");
d853 1
a853 1
	    if (wrapMode == Tk_GetUid("none")) {
d1026 1
a1026 1
    if (wrapMode == Tk_GetUid("none")) {
d1584 1
a1584 1
    if (textPtr->state == Tk_GetUid("normal")) {
d4245 2
a4246 2
    Tk_Uid wrapMode;		/* How to handle line wrapping: char, 
				 * none, or text. */
d4340 1
a4340 1
    if (wrapMode != Tk_GetUid("word")) {
@


1.1.4.5
log
@integrated stubs from development branch
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.1.4.4 1999/02/16 11:39:32 lfb Exp $
a19 4

#ifdef __WIN32__
#include "tkWinInt.h"
#endif
@


1.1.4.4.2.1
log
@Changes to support stubs in 8.1. Merged from 8.0.6 code base.
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextDisp.c,v 1.1.4.4 1999/02/16 11:39:32 lfb Exp $
a19 4

#ifdef __WIN32__
#include "tkWinInt.h"
#endif
@


