head	1.32;
access;
symbols
	core-8-5-9:1.27.2.1
	core-8-5-8:1.27.2.1
	macosx-cocoa-merge:1.29
	macosx-cocoa-premerge:1.29
	macosx-carbon-freeze:1.29
	core-8-5-7:1.27.2.1
	core-8-5-6:1.27
	core-8-6-b1:1.28
	core-8-6-a3:1.27
	core-8-5-5:1.27
	core-8-6-a2:1.27
	core-8-5-4:1.27
	core-8-5-3:1.27
	core-8-6-a1:1.27
	core-8-4-19:1.6.2.3
	core-8-5-branch:1.27.0.2
	core-8-5-2:1.27
	core-8-4-18:1.6.2.3
	core-8-5-1:1.27
	core-8-4-17:1.6.2.3
	core-8-5-0:1.27
	core-8-5-b3:1.26
	core-8-5-b2:1.26
	core-8-4-16:1.6.2.3
	core-8-5-b1:1.26
	core-stablilizer-merge:1.25
	core-stabilizer-branch:1.25.0.2
	core-stabilizer-merge:1.27
	core-8-4-15:1.6.2.3
	core-8-5-a6:1.25
	core-8-5-a5:1.22
	core-8-4-14:1.6.2.3
	core-8-5-a4:1.21
	core-8-4-13:1.6.2.1
	core-8-4-12:1.6.2.1
	core-8-4-11:1.6
	core-8-5-a3:1.16
	core-8-4-10:1.6
	core-8-4-9-branch:1.6.0.4
	core-8-5-a2:1.15
	core-8-4-9:1.6
	core-8-4-8:1.6
	core-8-4-7:1.6
	core-8-5-a1:1.13
	core-8-4-6:1.6
	core-8-4-5:1.6
	mistachkin-dev-branch:1.7.0.2
	core-8-4-4:1.6
	core-8-4-3:1.6
	core-8-4-branch:1.6.0.2
	core-8-4-2:1.6
	core-8-4-1:1.6
	core-8-3-5:1.5
	core-8-4-0:1.6
	macosx-8-4-merge-2002-08-31-trunk:1.6
	macosx-8-4-premerge-2002-08-31-branch:1.5.8.1
	macosx-8-4-premerge-2002-08-31-trunk:1.6
	macosx-8-4-merge-2002-08-30-branch:1.5.8.1
	macosx-8-4-merge-2002-08-30-trunk:1.6
	macosx-8-4-merge-2002-08-20-branch:1.5.8.1
	macosx-8-4-merge-2002-08-20-trunk:1.6
	core-8-4-b2:1.6
	core-8-4-b1:1.5
	macosx-premerge-6-9-02:1.5
	core-8-4-a4:1.5
	core-8-3-4:1.5
	macosx-8-4-branch:1.5.0.8
	macosx-8-4-branchpoint:1.5
	core-8-4-a3:1.5
	core-8-3-3:1.5
	dgp-privates-into-namespace:1.5.0.6
	tclpro-1-5-0:1.5
	core-8-4-win-speedup:1.5.0.4
	core-8-4-a2:1.5
	tclpro-1-4-1:1.5
	tclpro-1-4-0:1.5
	core-8-3-2:1.5
	ajuba-ajuba2-2-1-base:1.5
	ajuba-ajuba2-2-0:1.5
	core-8-4-a1:1.5
	core-8-3-1-branch:1.5.0.2
	core-8-3-1:1.5
	scriptics-sc-2-0-b5:1.5
	scriptics-sc-2-0-fixed:1.5
	scriptics-sc-2-0-b2:1.5
	scriptics-sc-2-0-b1:1.5
	core-8-3-0:1.5
	core-8-3-b2:1.5
	core-8-3-b1:1.4
	scriptics-sc-1-1:1.3
	scriptics-sc-1-1-b1:1.3
	core-8-2-3:1.3
	scriptics-sc-1-1-branch:1.3.0.10
	scriptics-sc-1-1-base:1.3
	scriptics-sc-1-0:1.3
	scriptics-sc-1-0-branch:1.3.0.8
	scriptics-sc-1-0-base:1.3
	core-8-2-2:1.3
	core-8-2-1:1.3
	scriptics-bc-1-0-b1:1.3
	core-8-2-1-branch:1.3.0.6
	scriptics-tclpro-1-3-0:1.3
	core-8-2-0:1.3
	core-8-2-b3-branch:1.3.0.4
	core-8-2-b3-base:1.3
	scriptics-tclpro-1-3-b4:1.3
	core-8-2-b2:1.3
	scriptics-tclpro-1-3-b3:1.3
	core-8-2-b1:1.3
	scriptics-tclpro-1-3-b2:1.3
	merge-to-mainline-5/21/99:1.3
	scriptics-tclpro-1-3-b2-branch:1.3.0.2
	core-8-1-1:1.3
	scriptics-tclpro-1-3-b1:1.3
	core-8-1-1-base:1.3
	core-8-1-0:1.3
	merge-to-mainline-4-29-99:1.3
	merge-to-mainline-4-23-99:1.3
	core-8-1-branch-old:1.1.0.4
	core-8-0-6-branch:1.2.0.6
	core-8-0-6-base:1.2
	core-8-0-5:1.2
	core-8-1-b3:1.1.4.2
	core-8-1-b2:1.1.4.2
	dev-8-1-stubs-branch:1.1.4.2.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.32
date	2010.11.19.14.48.00;	author nijtmans;	state Exp;
branches;
next	1.31;

1.31
date	2010.01.02.22.52.38;	author dkf;	state Exp;
branches;
next	1.30;

1.30
date	2009.09.07.07.29.04;	author das;	state Exp;
branches;
next	1.29;

1.29
date	2009.04.10.15.53.24;	author das;	state Exp;
branches;
next	1.28;

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

1.27
date	2007.12.13.15.24.17;	author dgp;	state Exp;
branches
	1.27.2.1;
next	1.26;

1.26
date	2007.09.07.00.34.54;	author dgp;	state Exp;
branches;
next	1.25;

1.25
date	2007.02.23.13.37.49;	author das;	state Exp;
branches
	1.25.2.1;
next	1.24;

1.24
date	2007.02.22.13.56.33;	author dkf;	state Exp;
branches;
next	1.23;

1.23
date	2007.01.18.23.20.37;	author nijtmans;	state Exp;
branches;
next	1.22;

1.22
date	2006.09.10.17.06.32;	author das;	state Exp;
branches;
next	1.21;

1.21
date	2006.03.26.17.52.40;	author vincentdarley;	state Exp;
branches;
next	1.20;

1.20
date	2005.11.17.16.21.56;	author dkf;	state Exp;
branches;
next	1.19;

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

1.18
date	2005.07.29.13.43.43;	author dkf;	state Exp;
branches;
next	1.17;

1.17
date	2005.07.29.13.35.07;	author dkf;	state Exp;
branches;
next	1.16;

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

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

1.14
date	2004.06.07.16.23.52;	author vincentdarley;	state Exp;
branches;
next	1.13;

1.13
date	2004.01.13.02.06.00;	author davygrvy;	state Exp;
branches;
next	1.12;

1.12
date	2004.01.07.16.28.23;	author vincentdarley;	state Exp;
branches;
next	1.11;

1.11
date	2003.12.15.11.51.06;	author vincentdarley;	state Exp;
branches;
next	1.10;

1.10
date	2003.11.08.17.22.46;	author vincentdarley;	state Exp;
branches;
next	1.9;

1.9
date	2003.11.07.15.36.26;	author vincentdarley;	state Exp;
branches;
next	1.8;

1.8
date	2003.10.31.09.02.10;	author vincentdarley;	state Exp;
branches;
next	1.7;

1.7
date	2003.05.19.13.04.23;	author vincentdarley;	state Exp;
branches;
next	1.6;

1.6
date	2002.08.05.04.30.40;	author dgp;	state Exp;
branches
	1.6.2.1;
next	1.5;

1.5
date	2000.01.06.02.18.58;	author hobbs;	state Exp;
branches
	1.5.8.1;
next	1.4;

1.4
date	99.12.14.06.52.32;	author hobbs;	state Exp;
branches;
next	1.3;

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

1.2
date	98.09.14.18.23.18;	author stanton;	state Exp;
branches;
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.26;	author stanton;	state Exp;
branches;
next	1.1.4.2;

1.1.4.2
date	98.09.30.02.17.22;	author stanton;	state Exp;
branches;
next	;

1.5.8.1
date	2002.08.20.20.27.07;	author das;	state Exp;
branches;
next	;

1.6.2.1
date	2005.11.27.02.44.25;	author das;	state Exp;
branches;
next	1.6.2.2;

1.6.2.2
date	2006.09.06.22.01.25;	author hobbs;	state Exp;
branches;
next	1.6.2.3;

1.6.2.3
date	2006.09.10.17.07.35;	author das;	state Exp;
branches;
next	;

1.25.2.1
date	2007.09.07.01.25.36;	author dgp;	state Exp;
branches;
next	;

1.27.2.1
date	2009.04.10.15.53.28;	author das;	state Exp;
branches;
next	;


desc
@@


1.32
log
@Revise Tcl_Panic() calls ending with a newline removing the newline, because Tcl_Panic() outputs a final newline already
@
text
@/*
 * tkTextBTree.c --
 *
 *	This file contains code that manages the B-tree representation of text
 *	for Tk's text widget and implements character and toggle segment
 *	types.
 *
 * Copyright (c) 1992-1994 The Regents of the University of California.
 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.31 2010/01/02 22:52:38 dkf Exp $
 */

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

/*
 * Implementation notes:
 *
 * Most of this file is independent of the text widget implementation and
 * representation now. Without much effort this could be developed further
 * into a new Tcl object type of which the Tk text widget is one example of a
 * client.
 *
 * The B-tree is set up with a dummy last line of text which must not be
 * displayed, and must _never_ have a non-zero pixel count. This dummy line is
 * a historical convenience to avoid other code having to deal with NULL
 * TkTextLines. Since Tk 8.5, with pixel line height calculations and peer
 * widgets, this dummy line is becoming somewhat of a liability, and special
 * case code has been required to deal with it. It is probably a good idea to
 * investigate removing the dummy line completely. This could result in an
 * overall simplification (although it would require new special case code to
 * deal with the fact that '.text index end' would then not really point to a
 * valid line, rather it would point to the beginning of a non-existent line
 * one beyond all current lines - we could perhaps define that as a
 * TkTextIndex with a NULL TkTextLine ptr).
 */

/*
 * The data structure below keeps summary information about one tag as part of
 * the tag information in a node.
 */

typedef struct Summary {
    TkTextTag *tagPtr;		/* Handle for tag. */
    int toggleCount;		/* Number of transitions into or out of this
				 * tag that occur in the subtree rooted at
				 * this node. */
    struct Summary *nextPtr;	/* Next in list of all tags for same node, or
				 * NULL if at end of list. */
} Summary;

/*
 * The data structure below defines a node in the B-tree.
 */

typedef struct Node {
    struct Node *parentPtr;	/* Pointer to parent node, or NULL if this is
				 * the root. */
    struct Node *nextPtr;	/* Next in list of siblings with the same
				 * parent node, or NULL for end of list. */
    Summary *summaryPtr;	/* First in malloc-ed list of info about tags
				 * in this subtree (NULL if no tag info in the
				 * subtree). */
    int level;			/* Level of this node in the B-tree. 0 refers
				 * to the bottom of the tree (children are
				 * lines, not nodes). */
    union {			/* First in linked list of children. */
	struct Node *nodePtr;	/* Used if level > 0. */
	TkTextLine *linePtr;	/* Used if level == 0. */
    } children;
    int numChildren;		/* Number of children of this node. */
    int numLines;		/* Total number of lines (leaves) in the
				 * subtree rooted here. */
    int *numPixels;		/* Array containing total number of vertical
				 * display pixels in the subtree rooted here,
				 * one entry for each peer widget. */
} Node;

/*
 * Used to avoid having to allocate and deallocate arrays on the fly for
 * commonly used functions. Must be > 0.
 */

#define PIXEL_CLIENTS 5

/*
 * Upper and lower bounds on how many children a node may have: rebalance when
 * either of these limits is exceeded. MAX_CHILDREN should be twice
 * MIN_CHILDREN and MIN_CHILDREN must be >= 2.
 */

#define MAX_CHILDREN 12
#define MIN_CHILDREN 6

/*
 * The data structure below defines an entire B-tree. Since text widgets are
 * the only current B-tree clients, 'clients' and 'pixelReferences' are
 * identical.
 */

typedef struct BTree {
    Node *rootPtr;		/* Pointer to root of B-tree. */
    int clients;		/* Number of clients of this B-tree. */
    int pixelReferences;	/* Number of clients of this B-tree which care
				 * about pixel heights. */
    int stateEpoch;		/* Updated each time any aspect of the B-tree
				 * changes. */
    TkSharedText *sharedTextPtr;/* Used to find tagTable in consistency
				 * checking code, and to access list of all
				 * B-tree clients. */
    int startEndCount;
    TkTextLine **startEnd;
    TkText **startEndRef;
} BTree;

/*
 * The structure below is used to pass information between
 * TkBTreeGetTags and IncCount:
 */

typedef struct TagInfo {
    int numTags;		/* Number of tags for which there is currently
				 * information in tags and counts. */
    int arraySize;		/* Number of entries allocated for tags and
				 * counts. */
    TkTextTag **tagPtrs;	/* Array of tags seen so far. Malloc-ed. */
    int *counts;		/* Toggle count (so far) for each entry in
				 * tags. Malloc-ed. */
} TagInfo;

/*
 * Variable that indicates whether to enable consistency checks for debugging.
 */

int tkBTreeDebug = 0;

/*
 * Macros that determine how much space to allocate for new segments:
 */

#define CSEG_SIZE(chars) ((unsigned) (Tk_Offset(TkTextSegment, body) \
	+ 1 + (chars)))
#define TSEG_SIZE ((unsigned) (Tk_Offset(TkTextSegment, body) \
	+ sizeof(TkTextToggle)))

/*
 * Forward declarations for functions defined in this file:
 */

static int		AdjustPixelClient(BTree *treePtr, int defaultHeight,
			    Node *nodePtr, TkTextLine *start, TkTextLine *end,
			    int useReference, int newPixelReferences,
			    int *counting);
static void		ChangeNodeToggleCount(Node *nodePtr,
			    TkTextTag *tagPtr, int delta);
static void		CharCheckProc(TkTextSegment *segPtr,
			    TkTextLine *linePtr);
static int		CharDeleteProc(TkTextSegment *segPtr,
			    TkTextLine *linePtr, int treeGone);
static TkTextSegment *	CharCleanupProc(TkTextSegment *segPtr,
			    TkTextLine *linePtr);
static TkTextSegment *	CharSplitProc(TkTextSegment *segPtr, int index);
static void		CheckNodeConsistency(Node *nodePtr, int references);
static void		CleanupLine(TkTextLine *linePtr);
static void		DeleteSummaries(Summary *tagPtr);
static void		DestroyNode(Node *nodePtr);
static TkTextSegment *	FindTagEnd(TkTextBTree tree, TkTextTag *tagPtr,
			    TkTextIndex *indexPtr);
static void		IncCount(TkTextTag *tagPtr, int inc,
			    TagInfo *tagInfoPtr);
static void		Rebalance(BTree *treePtr, Node *nodePtr);
static void		RecomputeNodeCounts(BTree *treePtr, Node *nodePtr);
static void		RemovePixelClient(BTree *treePtr, Node *nodePtr,
			    int overwriteWithLast);
static TkTextSegment *	SplitSeg(TkTextIndex *indexPtr);
static void		ToggleCheckProc(TkTextSegment *segPtr,
			    TkTextLine *linePtr);
static TkTextSegment *	ToggleCleanupProc(TkTextSegment *segPtr,
			    TkTextLine *linePtr);
static int		ToggleDeleteProc(TkTextSegment *segPtr,
			    TkTextLine *linePtr, int treeGone);
static void		ToggleLineChangeProc(TkTextSegment *segPtr,
			    TkTextLine *linePtr);
static TkTextSegment *	FindTagStart(TkTextBTree tree, TkTextTag *tagPtr,
			    TkTextIndex *indexPtr);
static void		AdjustStartEndRefs(BTree *treePtr, TkText *textPtr,
			    int action);

/*
 * Actions for use by AdjustStartEndRefs
 */

#define TEXT_ADD_REFS		1
#define TEXT_REMOVE_REFS	2

/*
 * Type record for character segments:
 */

const Tk_SegType tkTextCharType = {
    "character",		/* name */
    0,				/* leftGravity */
    CharSplitProc,		/* splitProc */
    CharDeleteProc,		/* deleteProc */
    CharCleanupProc,		/* cleanupProc */
    NULL,			/* lineChangeProc */
    TkTextCharLayoutProc,	/* layoutProc */
    CharCheckProc		/* checkProc */
};

/*
 * Type record for segments marking the beginning of a tagged range:
 */

const Tk_SegType tkTextToggleOnType = {
    "toggleOn",			/* name */
    0,				/* leftGravity */
    NULL,			/* splitProc */
    ToggleDeleteProc,		/* deleteProc */
    ToggleCleanupProc,		/* cleanupProc */
    ToggleLineChangeProc,	/* lineChangeProc */
    NULL,			/* layoutProc */
    ToggleCheckProc		/* checkProc */
};

/*
 * Type record for segments marking the end of a tagged range:
 */

const Tk_SegType tkTextToggleOffType = {
    "toggleOff",		/* name */
    1,				/* leftGravity */
    NULL,			/* splitProc */
    ToggleDeleteProc,		/* deleteProc */
    ToggleCleanupProc,		/* cleanupProc */
    ToggleLineChangeProc,	/* lineChangeProc */
    NULL,			/* layoutProc */
    ToggleCheckProc		/* checkProc */
};

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeCreate --
 *
 *	This function is called to create a new text B-tree.
 *
 * Results:
 *	The return value is a pointer to a new B-tree containing one line with
 *	nothing but a newline character.
 *
 * Side effects:
 *	Memory is allocated and initialized.
 *
 *----------------------------------------------------------------------
 */

TkTextBTree
TkBTreeCreate(
    TkSharedText *sharedTextPtr)
{
    register BTree *treePtr;
    register Node *rootPtr;
    register TkTextLine *linePtr, *linePtr2;
    register TkTextSegment *segPtr;

    /*
     * The tree will initially have two empty lines. The second line isn't
     * actually part of the tree's contents, but its presence makes several
     * operations easier. The tree will have one node, which is also the root
     * of the tree.
     */

    rootPtr = (Node *) ckalloc(sizeof(Node));
    linePtr = (TkTextLine *) ckalloc(sizeof(TkTextLine));
    linePtr2 = (TkTextLine *) ckalloc(sizeof(TkTextLine));

    rootPtr->parentPtr = NULL;
    rootPtr->nextPtr = NULL;
    rootPtr->summaryPtr = NULL;
    rootPtr->level = 0;
    rootPtr->children.linePtr = linePtr;
    rootPtr->numChildren = 2;
    rootPtr->numLines = 2;

    /*
     * The tree currently has no registered clients, so all pixel count
     * pointers are simply NULL.
     */

    rootPtr->numPixels = NULL;
    linePtr->pixels = NULL;
    linePtr2->pixels = NULL;

    linePtr->parentPtr = rootPtr;
    linePtr->nextPtr = linePtr2;
    segPtr = (TkTextSegment *) ckalloc(CSEG_SIZE(1));
    linePtr->segPtr = segPtr;
    segPtr->typePtr = &tkTextCharType;
    segPtr->nextPtr = NULL;
    segPtr->size = 1;
    segPtr->body.chars[0] = '\n';
    segPtr->body.chars[1] = 0;

    linePtr2->parentPtr = rootPtr;
    linePtr2->nextPtr = NULL;
    segPtr = (TkTextSegment *) ckalloc(CSEG_SIZE(1));
    linePtr2->segPtr = segPtr;
    segPtr->typePtr = &tkTextCharType;
    segPtr->nextPtr = NULL;
    segPtr->size = 1;
    segPtr->body.chars[0] = '\n';
    segPtr->body.chars[1] = 0;

    treePtr = (BTree *) ckalloc(sizeof(BTree));
    treePtr->sharedTextPtr = sharedTextPtr;
    treePtr->rootPtr = rootPtr;
    treePtr->clients = 0;
    treePtr->stateEpoch = 0;
    treePtr->pixelReferences = 0;
    treePtr->startEndCount = 0;
    treePtr->startEnd = NULL;
    treePtr->startEndRef = NULL;

    return (TkTextBTree) treePtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeAddClient --
 *
 *	This function is called to provide a client with access to a given
 *	B-tree. If the client wishes to make use of the B-tree's pixel height
 *	storage, caching and calculation mechanisms, then a non-negative
 *	'defaultHeight' must be provided. In this case the return value is a
 *	pixel tree reference which must be provided in all of the B-tree API
 *	which refers to or modifies pixel heights:
 *
 *	TkBTreeAdjustPixelHeight,
 *	TkBTreeFindPixelLine,
 *	TkBTreeNumPixels,
 *	TkBTreePixelsTo,
 *	(and two private functions AdjustPixelClient, RemovePixelClient).
 *
 *	If this is not provided, then the above functions must never be called
 *	for this client.
 *
 * Results:
 *	The return value is the pixelReference used by the B-tree to refer to
 *	pixel counts for the new client. It should be stored by the caller. If
 *	defaultHeight was negative, then the return value will be -1.
 *
 * Side effects:
 *	Memory may be allocated and initialized.
 *
 *----------------------------------------------------------------------
 */

void
TkBTreeAddClient(
    TkTextBTree tree,		/* B-tree to add a client to. */
    TkText *textPtr,		/* Client to add. */
    int defaultHeight)		/* Default line height for the new client, or
				 * -1 if no pixel heights are to be kept. */
{
    register BTree *treePtr = (BTree *) tree;

    if (treePtr == NULL) {
	Tcl_Panic("NULL treePtr in TkBTreeAddClient");
    }

    if (textPtr->start != NULL || textPtr->end != NULL) {
	AdjustStartEndRefs(treePtr, textPtr, TEXT_ADD_REFS);
    }

    if (defaultHeight >= 0) {
	TkTextLine *end;
	int counting = (textPtr->start == NULL ? 1 : 0);
	int useReference = treePtr->pixelReferences;

	/*
	 * We must set the 'end' value in AdjustPixelClient so that the last
	 * dummy line in the B-tree doesn't contain a pixel height.
	 */

	end = textPtr->end;
	if (end == NULL) {
	    end = TkBTreeFindLine(tree, NULL, TkBTreeNumLines(tree, NULL));
	}
	AdjustPixelClient(treePtr, defaultHeight, treePtr->rootPtr,
		textPtr->start, end, useReference, useReference+1, &counting);

	textPtr->pixelReference = useReference;
	treePtr->pixelReferences++;
    } else {
	textPtr->pixelReference = -1;
    }
    treePtr->clients++;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeClientRangeChanged --
 *
 *	Called when the -startline or -endline options of a text widget client
 *	of the B-tree have changed.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Lots of processing of the B-tree is done, with potential for memory to
 *	be allocated and initialized for the pixel heights of the widget.
 *
 *----------------------------------------------------------------------
 */

void
TkBTreeClientRangeChanged(
    TkText *textPtr,		/* Client whose start, end have changed. */
    int defaultHeight)		/* Default line height for the new client, or
				 * -1 if no pixel heights are to be kept. */
{
    TkTextLine *end;
    BTree *treePtr = (BTree *) textPtr->sharedTextPtr->tree;

    int counting = (textPtr->start == NULL ? 1 : 0);
    int useReference = textPtr->pixelReference;

    AdjustStartEndRefs(treePtr, textPtr, TEXT_ADD_REFS | TEXT_REMOVE_REFS);

    /*
     * We must set the 'end' value in AdjustPixelClient so that the last dummy
     * line in the B-tree doesn't contain a pixel height.
     */

    end = textPtr->end;
    if (end == NULL) {
	end = TkBTreeFindLine(textPtr->sharedTextPtr->tree,
		NULL, TkBTreeNumLines(textPtr->sharedTextPtr->tree, NULL));
    }
    AdjustPixelClient(treePtr, defaultHeight, treePtr->rootPtr,
	    textPtr->start, end, useReference, treePtr->pixelReferences,
	    &counting);
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeDestroy --
 *
 *	Delete a B-tree, recycling all of the storage it contains.
 *
 * Results:
 *	The tree is deleted, so 'tree' should never again be used.
 *
 * Side effects:
 *	Memory is freed.
 *
 *----------------------------------------------------------------------
 */

void
TkBTreeDestroy(
    TkTextBTree tree)		/* Tree to clean up. */
{
    BTree *treePtr = (BTree *) tree;

    /*
     * There's no need to loop over each client of the tree, calling
     * 'TkBTreeRemoveClient', since the 'DestroyNode' will clean everything up
     * itself.
     */

    DestroyNode(treePtr->rootPtr);
    if (treePtr->startEnd != NULL) {
	ckfree((char *) treePtr->startEnd);
	ckfree((char *) treePtr->startEndRef);
    }
    ckfree((char *) treePtr);
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeEpoch --
 *
 *	Return the epoch for the B-tree. This number is incremented any time
 *	anything changes in the tree.
 *
 * Results:
 *	The epoch number.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
TkBTreeEpoch(
    TkTextBTree tree)		/* Tree to get epoch for. */
{
    BTree *treePtr = (BTree *) tree;
    return treePtr->stateEpoch;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeRemoveClient --
 *
 *	Remove a client widget from its B-tree, cleaning up the pixel arrays
 *	which it uses if necessary. If this is the last such widget, we also
 *	destroy the whole tree.
 *
 * Results:
 *	All tree-specific aspects of the given client are deleted. If no more
 *	references exist, then the given tree is also deleted (in which case
 *	'tree' must not be used again).
 *
 * Side effects:
 *	Memory may be freed.
 *
 *----------------------------------------------------------------------
 */

void
TkBTreeRemoveClient(
    TkTextBTree tree,		/* Tree to remove client from. */
    TkText *textPtr)		/* Client to remove. */
{
    BTree *treePtr = (BTree *) tree;
    int pixelReference = textPtr->pixelReference;

    if (treePtr->clients == 1) {
	/*
	 * The last reference to the tree.
	 */

	DestroyNode(treePtr->rootPtr);
	ckfree((char *) treePtr);
	return;
    } else if (pixelReference == -1) {
	/*
	 * A client which doesn't care about pixels.
	 */

	treePtr->clients--;
    } else {
	/*
	 * Clean up pixel data for the given reference.
	 */

	if (pixelReference == (treePtr->pixelReferences-1)) {
	    /*
	     * The widget we're removing has the last index, so deletion is
	     * easier.
	     */

	    RemovePixelClient(treePtr, treePtr->rootPtr, -1);
	} else {
	    TkText *adjustPtr;

	    RemovePixelClient(treePtr, treePtr->rootPtr, pixelReference);

	    /*
	     * Now we need to adjust the 'pixelReference' of the peer widget
	     * whose storage we've just moved.
	     */

	    adjustPtr = treePtr->sharedTextPtr->peers;
	    while (adjustPtr != NULL) {
		if (adjustPtr->pixelReference == treePtr->pixelReferences-1) {
		    adjustPtr->pixelReference = pixelReference;
		    break;
		}
		adjustPtr = adjustPtr->next;
	    }
	    if (adjustPtr == NULL) {
		Tcl_Panic("Couldn't find text widget with correct reference");
	    }
	}
	treePtr->pixelReferences--;
	treePtr->clients--;
    }

    if (textPtr->start != NULL || textPtr->end != NULL) {
	AdjustStartEndRefs(treePtr, textPtr, TEXT_REMOVE_REFS);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * AdjustStartEndRefs --
 *
 *	Modify B-tree's cache of start, end lines for the given text widget.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The number of cached items may change (treePtr->startEndCount).
 *
 *----------------------------------------------------------------------
 */

static void
AdjustStartEndRefs(
    BTree *treePtr,		/* The entire B-tree. */
    TkText *textPtr,		/* The text widget for which we want to adjust
				 * it's start and end cache. */
    int action)			/* Action to perform. */
{
    if (action & TEXT_REMOVE_REFS) {
	int i = 0;
	int count = 0;

	while (i < treePtr->startEndCount) {
	    if (i != count) {
		treePtr->startEnd[count] = treePtr->startEnd[i];
		treePtr->startEndRef[count] = treePtr->startEndRef[i];
	    }
	    if (treePtr->startEndRef[i] != textPtr) {
		count++;
	    }
	    i++;
	}
	treePtr->startEndCount = count;
	treePtr->startEnd = (TkTextLine **)
		ckrealloc((char *) treePtr->startEnd,
		sizeof(TkTextLine *) * count);
	treePtr->startEndRef = (TkText **)
		ckrealloc((char *) treePtr->startEndRef,
		sizeof(TkText *) * count);
    }
    if ((action & TEXT_ADD_REFS)
	    && (textPtr->start != NULL || textPtr->end != NULL)) {
	int count;

	if (textPtr->start != NULL) {
	    treePtr->startEndCount++;
	}
	if (textPtr->end != NULL) {
	    treePtr->startEndCount++;
	}

	count = treePtr->startEndCount;

	treePtr->startEnd = (TkTextLine **)
		ckrealloc((char *) treePtr->startEnd,
		sizeof(TkTextLine *) * count);
	treePtr->startEndRef = (TkText **)
		ckrealloc((char *) treePtr->startEndRef,
		sizeof(TkText *) * count);

	if (textPtr->start != NULL) {
	    count--;
	    treePtr->startEnd[count] = textPtr->start;
	    treePtr->startEndRef[count] = treePtr->sharedTextPtr->peers;
	}
	if (textPtr->end != NULL) {
	    count--;
	    treePtr->startEnd[count] = textPtr->end;
	    treePtr->startEndRef[count] = treePtr->sharedTextPtr->peers;
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * AdjustPixelClient --
 *
 *	Utility function used to update all data structures for the existence
 *	of a new peer widget based on this B-tree, or for the modification of
 *	the start, end lines of an existing peer widget.
 *
 *	Immediately _after_ calling this, treePtr->pixelReferences and
 *	treePtr->clients should be adjusted if needed (i.e. if this is a new
 *	peer).
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	All the storage for Nodes and TkTextLines in the tree may be adjusted.
 *
 *----------------------------------------------------------------------
 */

static int
AdjustPixelClient(
    BTree *treePtr,		/* Pointer to tree. */
    int defaultHeight,		/* Default pixel line height, which can be
				 * zero. */
    Node *nodePtr,		/* Adjust from this node downwards. */
    TkTextLine *start,		/* First line for this pixel client. */
    TkTextLine *end,		/* Last line for this pixel client. */
    int useReference,		/* pixel reference for the client we are
				 * adding or changing. */
    int newPixelReferences,	/* New number of pixel references to this
				 * B-tree. */
    int *counting)		/* References an integer which is zero if
				 * we're outside the relevant range for this
				 * client, and 1 if we're inside. */
{
    int pixelCount = 0;

    /*
     * Traverse entire tree down from nodePtr, reallocating pixel structures
     * for each Node and TkTextLine, adding room for the new peer's pixel
     * information (1 extra int per Node, 2 extra ints per TkTextLine). Also
     * copy the information from the last peer into the new space (so it
     * contains something sensible).
     */

    if (nodePtr->level != 0) {
	Node *loopPtr = nodePtr->children.nodePtr;

	while (loopPtr != NULL) {
	    pixelCount += AdjustPixelClient(treePtr, defaultHeight, loopPtr,
		    start, end, useReference, newPixelReferences, counting);
	    loopPtr = loopPtr->nextPtr;
	}
    } else {
	register TkTextLine *linePtr = nodePtr->children.linePtr;

	while (linePtr != NULL) {
	    if (!*counting && (linePtr == start)) {
		*counting = 1;
	    }
	    if (*counting && (linePtr == end)) {
		*counting = 0;
	    }
	    if (newPixelReferences != treePtr->pixelReferences) {
		linePtr->pixels = (int *) ckrealloc((char *) linePtr->pixels,
			sizeof(int) * 2 * newPixelReferences);
	    }

	    /*
	     * Notice that for the very last line, we are never counting and
	     * therefore this always has a height of 0 and an epoch of 1.
	     */

	    linePtr->pixels[2*useReference] = (*counting ? defaultHeight : 0);
	    linePtr->pixels[2*useReference+1] = (*counting ? 0 : 1);
	    pixelCount += linePtr->pixels[2*useReference];

	    linePtr = linePtr->nextPtr;
	}
    }
    if (newPixelReferences != treePtr->pixelReferences) {
	nodePtr->numPixels = (int *) ckrealloc((char *) nodePtr->numPixels,
		sizeof(int) * newPixelReferences);
    }
    nodePtr->numPixels[useReference] = pixelCount;
    return pixelCount;
}

/*
 *----------------------------------------------------------------------
 *
 * RemovePixelClient --
 *
 *	Utility function used to update all data structures for the removal of
 *	a peer widget which used to be based on this B-tree.
 *
 *	Immediately _after_ calling this, treePtr->clients should be
 *	decremented.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	All the storage for Nodes and TkTextLines in the tree may be adjusted.
 *
 *----------------------------------------------------------------------
 */

static void
RemovePixelClient(
    BTree *treePtr,		/* Pointer to tree. */
    Node *nodePtr,		/* Adjust from this node downwards. */
    int overwriteWithLast)	/* Over-write this peer widget's information
				 * with the last one. */
{
    /*
     * Traverse entire tree down from nodePtr, reallocating pixel structures
     * for each Node and TkTextLine, removing space allocated for one peer. If
     * 'overwriteWithLast' is not -1, then copy the information which was in
     * the last slot on top of one of the others (i.e. it's not the last one
     * we're deleting).
     */

    if (overwriteWithLast != -1) {
	nodePtr->numPixels[overwriteWithLast] =
		nodePtr->numPixels[treePtr->pixelReferences-1];
    }
    if (treePtr->pixelReferences == 1) {
	nodePtr->numPixels = NULL;
    } else {
	nodePtr->numPixels = (int *) ckrealloc((char *) nodePtr->numPixels,
		sizeof(int) * (treePtr->pixelReferences - 1));
    }
    if (nodePtr->level != 0) {
	nodePtr = nodePtr->children.nodePtr;
	while (nodePtr != NULL) {
	    RemovePixelClient(treePtr, nodePtr, overwriteWithLast);
	    nodePtr = nodePtr->nextPtr;
	}
    } else {
	register TkTextLine *linePtr = nodePtr->children.linePtr;
	while (linePtr != NULL) {
	    if (overwriteWithLast != -1) {
		linePtr->pixels[2*overwriteWithLast] =
			linePtr->pixels[2*(treePtr->pixelReferences-1)];
		linePtr->pixels[1+2*overwriteWithLast] =
			linePtr->pixels[1+2*(treePtr->pixelReferences-1)];
	    }
	    if (treePtr->pixelReferences == 1) {
		linePtr->pixels = NULL;
	    } else {
		linePtr->pixels = (int *) ckrealloc((char *) linePtr->pixels,
			sizeof(int) * 2 * (treePtr->pixelReferences-1));
	    }
	    linePtr = linePtr->nextPtr;
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * DestroyNode --
 *
 *	This is a recursive utility function used during the deletion of a
 *	B-tree.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	All the storage for nodePtr and its descendants is freed.
 *
 *----------------------------------------------------------------------
 */

static void
DestroyNode(
    register Node *nodePtr)	/* Destroy from this node downwards. */
{
    if (nodePtr->level == 0) {
	TkTextLine *linePtr;
	TkTextSegment *segPtr;

	while (nodePtr->children.linePtr != NULL) {
	    linePtr = nodePtr->children.linePtr;
	    nodePtr->children.linePtr = linePtr->nextPtr;
	    while (linePtr->segPtr != NULL) {
		segPtr = linePtr->segPtr;
		linePtr->segPtr = segPtr->nextPtr;
		segPtr->typePtr->deleteProc(segPtr, linePtr, 1);
	    }
	    ckfree((char *) linePtr->pixels);
	    ckfree((char *) linePtr);
	}
    } else {
	register Node *childPtr;

	while (nodePtr->children.nodePtr != NULL) {
	    childPtr = nodePtr->children.nodePtr;
	    nodePtr->children.nodePtr = childPtr->nextPtr;
	    DestroyNode(childPtr);
	}
    }
    DeleteSummaries(nodePtr->summaryPtr);
    ckfree((char *) nodePtr->numPixels);
    ckfree((char *) nodePtr);
}

/*
 *----------------------------------------------------------------------
 *
 * DeleteSummaries --
 *
 *	Free up all of the memory in a list of tag summaries associated with a
 *	node.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Storage is released.
 *
 *----------------------------------------------------------------------
 */

static void
DeleteSummaries(
    register Summary *summaryPtr)
				/* First in list of node's tag summaries. */
{
    register Summary *nextPtr;

    while (summaryPtr != NULL) {
	nextPtr = summaryPtr->nextPtr;
	ckfree((char *) summaryPtr);
	summaryPtr = nextPtr;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeAdjustPixelHeight --
 *
 *	Adjust the pixel height of a given logical line to the specified
 *	value.
 *
 * Results:
 *	Total number of valid pixels currently known in the tree.
 *
 * Side effects:
 *	Updates overall data structures so pixel height count is consistent.
 *
 *----------------------------------------------------------------------
 */

int
TkBTreeAdjustPixelHeight(
    const TkText *textPtr,	/* Client of the B-tree. */
    register TkTextLine *linePtr,
				/* The logical line to update. */
    int newPixelHeight,		/* The line's known height in pixels. */
    int mergedLogicalLines)	/* The number of extra logical lines which
				 * have been merged with this one (due to
				 * elided eols). They will have their pixel
				 * height set to zero, and the total pixel
				 * height associated with the given
				 * linePtr. */
{
    register Node *nodePtr;
    int changeToPixelCount;	/* Counts change to total number of pixels in
				 * file. */
    int pixelReference = textPtr->pixelReference;

    changeToPixelCount = newPixelHeight - linePtr->pixels[2 * pixelReference];

    /*
     * Increment the pixel counts in all the parent nodes of the current line,
     * then rebalance the tree if necessary.
     */

    nodePtr = linePtr->parentPtr;
    nodePtr->numPixels[pixelReference] += changeToPixelCount;

    while (nodePtr->parentPtr != NULL) {
	nodePtr = nodePtr->parentPtr;
	nodePtr->numPixels[pixelReference] += changeToPixelCount;
    }

    linePtr->pixels[2 * pixelReference] = newPixelHeight;

    /*
     * Any merged logical lines must have their height set to zero.
     */

    while (mergedLogicalLines-- > 0) {
	linePtr = TkBTreeNextLine(textPtr, linePtr);
	TkBTreeAdjustPixelHeight(textPtr, linePtr, 0, 0);
    }

    /*
     * Return total number of pixels in the tree.
     */

    return nodePtr->numPixels[pixelReference];
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeInsertChars --
 *
 *	Insert characters at a given position in a B-tree.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Characters are added to the B-tree at the given position. If the
 *	string contains newlines, new lines will be added, which could cause
 *	the structure of the B-tree to change.
 *
 *----------------------------------------------------------------------
 */

void
TkBTreeInsertChars(
    TkTextBTree tree,		/* Tree to insert into. */
    register TkTextIndex *indexPtr,
				/* Indicates where to insert text. When the
				 * function returns, this index is no longer
				 * valid because of changes to the segment
				 * structure. */
    const char *string)		/* Pointer to bytes to insert (may contain
				 * newlines, must be null-terminated). */
{
    register Node *nodePtr;
    register TkTextSegment *prevPtr;
				/* The segment just before the first new
				 * segment (NULL means new segment is at
				 * beginning of line). */
    TkTextSegment *curPtr;	/* Current segment; new characters are
				 * inserted just after this one. NULL means
				 * insert at beginning of line. */
    TkTextLine *linePtr;	/* Current line (new segments are added to
				 * this line). */
    register TkTextSegment *segPtr;
    TkTextLine *newLinePtr;
    int chunkSize;		/* # characters in current chunk. */
    register const char *eol;	/* Pointer to character just after last one in
				 * current chunk. */
    int changeToLineCount;	/* Counts change to total number of lines in
				 * file. */
    int *changeToPixelCount;	/* Counts change to total number of pixels in
				 * file. */
    int ref;
    int pixels[PIXEL_CLIENTS];

    BTree *treePtr = (BTree *) tree;
    treePtr->stateEpoch++;
    prevPtr = SplitSeg(indexPtr);
    linePtr = indexPtr->linePtr;
    curPtr = prevPtr;

    /*
     * Chop the string up into lines and create a new segment for each line,
     * plus a new line for the leftovers from the previous line.
     */

    changeToLineCount = 0;
    if (treePtr->pixelReferences > PIXEL_CLIENTS) {
	changeToPixelCount = (int *)
		ckalloc(sizeof(int) * treePtr->pixelReferences);
    } else {
	changeToPixelCount = pixels;
    }
    for (ref = 0; ref < treePtr->pixelReferences; ref++) {
	changeToPixelCount[ref] = 0;
    }

    while (*string != 0) {
	for (eol = string; *eol != 0; eol++) {
	    if (*eol == '\n') {
		eol++;
		break;
	    }
	}
	chunkSize = eol-string;
	segPtr = (TkTextSegment *) ckalloc(CSEG_SIZE(chunkSize));
	segPtr->typePtr = &tkTextCharType;
	if (curPtr == NULL) {
	    segPtr->nextPtr = linePtr->segPtr;
	    linePtr->segPtr = segPtr;
	} else {
	    segPtr->nextPtr = curPtr->nextPtr;
	    curPtr->nextPtr = segPtr;
	}
	segPtr->size = chunkSize;
	strncpy(segPtr->body.chars, string, (size_t) chunkSize);
	segPtr->body.chars[chunkSize] = 0;

	if (eol[-1] != '\n') {
	    break;
	}

	/*
	 * The chunk ended with a newline, so create a new TkTextLine and move
	 * the remainder of the old line to it.
	 */

	newLinePtr = (TkTextLine *) ckalloc(sizeof(TkTextLine));
	newLinePtr->pixels = (int *)
		ckalloc(sizeof(int) * 2 * treePtr->pixelReferences);

	newLinePtr->parentPtr = linePtr->parentPtr;
	newLinePtr->nextPtr = linePtr->nextPtr;
	linePtr->nextPtr = newLinePtr;
	newLinePtr->segPtr = segPtr->nextPtr;

	/*
	 * Set up a starting default height, which will be re-adjusted later.
	 * We need to do this for each referenced widget.
	 */

	for (ref = 0; ref < treePtr->pixelReferences; ref++) {
	    newLinePtr->pixels[2 * ref] = linePtr->pixels[2 * ref];
	    newLinePtr->pixels[2 * ref + 1] = 0;
	    changeToPixelCount[ref] += newLinePtr->pixels[2 * ref];
	}

	segPtr->nextPtr = NULL;
	linePtr = newLinePtr;
	curPtr = NULL;
	changeToLineCount++;

	string = eol;
    }

    /*
     * I don't believe it's possible for either of the two lines passed to
     * this function to be the last line of text, but the function is robust
     * to that case anyway. (We must never re-calculated the line height of
     * the last line).
     */

    TkTextInvalidateLineMetrics(treePtr->sharedTextPtr, NULL,
	    indexPtr->linePtr, changeToLineCount, TK_TEXT_INVALIDATE_INSERT);

    /*
     * Cleanup the starting line for the insertion, plus the ending line if
     * it's different.
     */

    CleanupLine(indexPtr->linePtr);
    if (linePtr != indexPtr->linePtr) {
	CleanupLine(linePtr);
    }

    /*
     * Increment the line and pixel counts in all the parent nodes of the
     * insertion point, then rebalance the tree if necessary.
     */

    for (nodePtr = linePtr->parentPtr ; nodePtr != NULL;
	    nodePtr = nodePtr->parentPtr) {
	nodePtr->numLines += changeToLineCount;
	for (ref = 0; ref < treePtr->pixelReferences; ref++) {
	    nodePtr->numPixels[ref] += changeToPixelCount[ref];
	}
    }
    if (treePtr->pixelReferences > PIXEL_CLIENTS) {
	ckfree((char *) changeToPixelCount);
    }

    nodePtr = linePtr->parentPtr;
    nodePtr->numChildren += changeToLineCount;
    if (nodePtr->numChildren > MAX_CHILDREN) {
	Rebalance(treePtr, nodePtr);
    }

    if (tkBTreeDebug) {
	TkBTreeCheck(indexPtr->tree);
    }
}

/*
 *--------------------------------------------------------------
 *
 * SplitSeg --
 *
 *	This function is called before adding or deleting segments. It does
 *	three things: (a) it finds the segment containing indexPtr; (b) if
 *	there are several such segments (because some segments have zero
 *	length) then it picks the first segment that does not have left
 *	gravity; (c) if the index refers to the middle of a segment then it
 *	splits the segment so that the index now refers to the beginning of a
 *	segment.
 *
 * Results:
 *	The return value is a pointer to the segment just before the segment
 *	corresponding to indexPtr (as described above). If the segment
 *	corresponding to indexPtr is the first in its line then the return
 *	value is NULL.
 *
 * Side effects:
 *	The segment referred to by indexPtr is split unless indexPtr refers to
 *	its first character.
 *
 *--------------------------------------------------------------
 */

static TkTextSegment *
SplitSeg(
    TkTextIndex *indexPtr)	/* Index identifying position at which to
				 * split a segment. */
{
    TkTextSegment *prevPtr, *segPtr;
    TkTextLine *linePtr;
    int count = indexPtr->byteIndex;

    linePtr = indexPtr->linePtr;
    prevPtr = NULL;
    segPtr = linePtr->segPtr;

    while (segPtr != NULL) {
	if (segPtr->size > count) {
	    if (count == 0) {
		return prevPtr;
	    }
	    segPtr = segPtr->typePtr->splitProc(segPtr, count);
	    if (prevPtr == NULL) {
		indexPtr->linePtr->segPtr = segPtr;
	    } else {
		prevPtr->nextPtr = segPtr;
	    }
	    return segPtr;
	} else if ((segPtr->size == 0) && (count == 0)
		&& !segPtr->typePtr->leftGravity) {
	    return prevPtr;
	}

	count -= segPtr->size;
	prevPtr = segPtr;
	segPtr = segPtr->nextPtr;
	if (segPtr == NULL) {
	    /*
	     * Two logical lines merged into one display line through eliding
	     * of a newline.
	     */

	    linePtr = TkBTreeNextLine(NULL, linePtr);
	    if (linePtr == NULL) {
		/*
		 * Reached end of the text.
		 */
	    } else {
		segPtr = linePtr->segPtr;
	    }
	}
    }
    Tcl_Panic("SplitSeg reached end of line!");
    return NULL;
}

/*
 *--------------------------------------------------------------
 *
 * CleanupLine --
 *
 *	This function is called after modifications have been made to a line.
 *	It scans over all of the segments in the line, giving each a chance to
 *	clean itself up, e.g. by merging with the following segments, updating
 *	internal information, etc.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Depends on what the segment-specific cleanup functions do.
 *
 *--------------------------------------------------------------
 */

static void
CleanupLine(
    TkTextLine *linePtr)	/* Line to be cleaned up. */
{
    TkTextSegment *segPtr, **prevPtrPtr;
    int anyChanges;

    /*
     * Make a pass over all of the segments in the line, giving each a chance
     * to clean itself up. This could potentially change the structure of the
     * line, e.g. by merging two segments together or having two segments
     * cancel themselves; if so, then repeat the whole process again, since
     * the first structure change might make other structure changes possible.
     * Repeat until eventually there are no changes.
     */

    while (1) {
	anyChanges = 0;
	for (prevPtrPtr = &linePtr->segPtr, segPtr = *prevPtrPtr;
		segPtr != NULL;
		prevPtrPtr = &(*prevPtrPtr)->nextPtr, segPtr = *prevPtrPtr) {
	    if (segPtr->typePtr->cleanupProc != NULL) {
		*prevPtrPtr = segPtr->typePtr->cleanupProc(segPtr, linePtr);
		if (segPtr != *prevPtrPtr) {
		    anyChanges = 1;
		}
	    }
	}
	if (!anyChanges) {
	    break;
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeDeleteIndexRange --
 *
 *	Delete a range of characters from a B-tree. The caller must make sure
 *	that the final newline of the B-tree is never deleted.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Information is deleted from the B-tree. This can cause the internal
 *	structure of the B-tree to change. Note: because of changes to the
 *	B-tree structure, the indices pointed to by index1Ptr and index2Ptr
 *	should not be used after this function returns.
 *
 *----------------------------------------------------------------------
 */

void
TkBTreeDeleteIndexRange(
    TkTextBTree tree,		/* Tree to delete from. */
    register TkTextIndex *index1Ptr,
				/* Indicates first character that is to be
				 * deleted. */
    register TkTextIndex *index2Ptr)
				/* Indicates character just after the last one
				 * that is to be deleted. */
{
    TkTextSegment *prevPtr;	/* The segment just before the start of the
				 * deletion range. */
    TkTextSegment *lastPtr;	/* The segment just after the end of the
				 * deletion range. */
    TkTextSegment *segPtr, *nextPtr;
    TkTextLine *curLinePtr;
    Node *curNodePtr, *nodePtr;
    int changeToLineCount = 0;
    int ref;
    BTree *treePtr = (BTree *) tree;

    treePtr->stateEpoch++;

    /*
     * Tricky point: split at index2Ptr first; otherwise the split at
     * index2Ptr may invalidate segPtr and/or prevPtr.
     */

    lastPtr = SplitSeg(index2Ptr);
    if (lastPtr != NULL) {
	lastPtr = lastPtr->nextPtr;
    } else {
	lastPtr = index2Ptr->linePtr->segPtr;
    }
    prevPtr = SplitSeg(index1Ptr);
    if (prevPtr != NULL) {
	segPtr = prevPtr->nextPtr;
	prevPtr->nextPtr = lastPtr;
    } else {
	segPtr = index1Ptr->linePtr->segPtr;
	index1Ptr->linePtr->segPtr = lastPtr;
    }

    /*
     * Delete all of the segments between prevPtr and lastPtr.
     */

    curLinePtr = index1Ptr->linePtr;

    curNodePtr = curLinePtr->parentPtr;
    while (segPtr != lastPtr) {
	if (segPtr == NULL) {
	    TkTextLine *nextLinePtr;

	    /*
	     * We just ran off the end of a line. First find the next line,
	     * then go back to the old line and delete it (unless it's the
	     * starting line for the range).
	     */

	    nextLinePtr = TkBTreeNextLine(NULL, curLinePtr);
	    if (curLinePtr != index1Ptr->linePtr) {
		if (curNodePtr == index1Ptr->linePtr->parentPtr) {
		    index1Ptr->linePtr->nextPtr = curLinePtr->nextPtr;
		} else {
		    curNodePtr->children.linePtr = curLinePtr->nextPtr;
		}
		for (nodePtr = curNodePtr; nodePtr != NULL;
			nodePtr = nodePtr->parentPtr) {
		    nodePtr->numLines--;
		    for (ref = 0; ref < treePtr->pixelReferences; ref++) {
			nodePtr->numPixels[ref] -= curLinePtr->pixels[2*ref];
		    }
		}
		changeToLineCount++;
		CLANG_ASSERT(curNodePtr);
		curNodePtr->numChildren--;

		/*
		 * Check if we need to adjust any partial clients.
		 */

		if (treePtr->startEnd != NULL) {
		    int checkCount = 0;

		    while (checkCount < treePtr->startEndCount) {
			if (treePtr->startEnd[checkCount] == curLinePtr) {
			    TkText *peer = treePtr->startEndRef[checkCount];

			    /*
			     * We're deleting a line which is the start or end
			     * of a current client. This means we need to
			     * adjust that client.
			     */

			    treePtr->startEnd[checkCount] = nextLinePtr;
			    if (peer->start == curLinePtr) {
				peer->start = nextLinePtr;
			    }
			    if (peer->end == curLinePtr) {
				peer->end = nextLinePtr;
			    }
			}
			checkCount++;
		    }
		}
		ckfree((char *) curLinePtr->pixels);
		ckfree((char *) curLinePtr);
	    }
	    curLinePtr = nextLinePtr;
	    segPtr = curLinePtr->segPtr;

	    /*
	     * If the node is empty then delete it and its parents recursively
	     * upwards until a non-empty node is found.
	     */

	    while (curNodePtr->numChildren == 0) {
		Node *parentPtr;

		parentPtr = curNodePtr->parentPtr;
		if (parentPtr->children.nodePtr == curNodePtr) {
		    parentPtr->children.nodePtr = curNodePtr->nextPtr;
		} else {
		    Node *prevNodePtr = parentPtr->children.nodePtr;
		    while (prevNodePtr->nextPtr != curNodePtr) {
			prevNodePtr = prevNodePtr->nextPtr;
		    }
		    prevNodePtr->nextPtr = curNodePtr->nextPtr;
		}
		parentPtr->numChildren--;
		ckfree((char *) curNodePtr);
		curNodePtr = parentPtr;
	    }
	    curNodePtr = curLinePtr->parentPtr;
	    continue;
	}

	nextPtr = segPtr->nextPtr;
	if (segPtr->typePtr->deleteProc(segPtr, curLinePtr, 0) != 0) {
	    /*
	     * This segment refuses to die. Move it to prevPtr and advance
	     * prevPtr if the segment has left gravity.
	     */

	    if (prevPtr == NULL) {
		segPtr->nextPtr = index1Ptr->linePtr->segPtr;
		index1Ptr->linePtr->segPtr = segPtr;
	    } else {
		segPtr->nextPtr = prevPtr->nextPtr;
		prevPtr->nextPtr = segPtr;
	    }
	    if (segPtr->typePtr->leftGravity) {
		prevPtr = segPtr;
	    }
	}
	segPtr = nextPtr;
    }

    /*
     * If the beginning and end of the deletion range are in different lines,
     * join the two lines together and discard the ending line.
     */

    if (index1Ptr->linePtr != index2Ptr->linePtr) {
	TkTextLine *prevLinePtr;

	for (segPtr = lastPtr; segPtr != NULL;
		segPtr = segPtr->nextPtr) {
	    if (segPtr->typePtr->lineChangeProc != NULL) {
		segPtr->typePtr->lineChangeProc(segPtr, index2Ptr->linePtr);
	    }
	}
	curNodePtr = index2Ptr->linePtr->parentPtr;
	for (nodePtr = curNodePtr; nodePtr != NULL;
		nodePtr = nodePtr->parentPtr) {
	    nodePtr->numLines--;
	    for (ref = 0; ref < treePtr->pixelReferences; ref++) {
		nodePtr->numPixels[ref] -= index2Ptr->linePtr->pixels[2*ref];
	    }
	}
	changeToLineCount++;
	curNodePtr->numChildren--;
	prevLinePtr = curNodePtr->children.linePtr;
	if (prevLinePtr == index2Ptr->linePtr) {
	    curNodePtr->children.linePtr = index2Ptr->linePtr->nextPtr;
	} else {
	    while (prevLinePtr->nextPtr != index2Ptr->linePtr) {
		prevLinePtr = prevLinePtr->nextPtr;
	    }
	    prevLinePtr->nextPtr = index2Ptr->linePtr->nextPtr;
	}

	/*
	 * Check if we need to adjust any partial clients. In this case if
	 * we're deleting the line, we actually move back to the previous line
	 * for our (start,end) storage. We do this because we still want the
	 * portion of the second line that still exists to be in the start,end
	 * range.
	 */

	if (treePtr->startEnd != NULL) {
	    int checkCount = 0;

	    while (checkCount < treePtr->startEndCount &&
		    treePtr->startEnd[checkCount] != NULL) {
		if (treePtr->startEnd[checkCount] == index2Ptr->linePtr) {
		    TkText *peer = treePtr->startEndRef[checkCount];

		    /*
		     * We're deleting a line which is the start or end of a
		     * current client. This means we need to adjust that
		     * client.
		     */

		    treePtr->startEnd[checkCount] = index1Ptr->linePtr;
		    if (peer->start == index2Ptr->linePtr) {
			peer->start = index1Ptr->linePtr;
		    }
		    if (peer->end == index2Ptr->linePtr) {
			peer->end = index1Ptr->linePtr;
		    }
		}
		checkCount++;
	    }
	}
	ckfree((char *) index2Ptr->linePtr->pixels);
	ckfree((char *) index2Ptr->linePtr);

	Rebalance((BTree *) index2Ptr->tree, curNodePtr);
    }

    /*
     * Cleanup the segments in the new line.
     */

    CleanupLine(index1Ptr->linePtr);

    /*
     * This line now needs to have its height recalculated. For safety, ensure
     * we don't call this function with the last artificial line of text. I
     * _believe_ that it isn't possible to get this far with the last line,
     * but it is good to be safe.
     */

    if (TkBTreeNextLine(NULL, index1Ptr->linePtr) != NULL) {
	TkTextInvalidateLineMetrics(treePtr->sharedTextPtr, NULL,
		index1Ptr->linePtr, changeToLineCount,
		TK_TEXT_INVALIDATE_DELETE);
    }

    /*
     * Lastly, rebalance the first node of the range.
     */

    Rebalance((BTree *) index1Ptr->tree, index1Ptr->linePtr->parentPtr);
    if (tkBTreeDebug) {
	TkBTreeCheck(index1Ptr->tree);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeFindLine --
 *
 *	Find a particular line in a B-tree based on its line number.
 *
 * Results:
 *	The return value is a pointer to the line structure for the line whose
 *	index is "line", or NULL if no such line exists.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

TkTextLine *
TkBTreeFindLine(
    TkTextBTree tree,		/* B-tree in which to find line. */
    const TkText *textPtr,	/* Relative to this client of the B-tree. */
    int line)			/* Index of desired line. */
{
    BTree *treePtr = (BTree *) tree;
    register Node *nodePtr;
    register TkTextLine *linePtr;

    if (treePtr == NULL) {
	treePtr = (BTree *) textPtr->sharedTextPtr->tree;
    }

    nodePtr = treePtr->rootPtr;
    if ((line < 0) || (line >= nodePtr->numLines)) {
	return NULL;
    }

    /*
     * Check for the any start/end offset for this text widget.
     */

    if (textPtr != NULL) {
	if (textPtr->start != NULL) {
	    line += TkBTreeLinesTo(NULL, textPtr->start);
	    if (line >= nodePtr->numLines) {
		return NULL;
	    }
	}
	if (textPtr->end != NULL) {
	    if (line > TkBTreeLinesTo(NULL, textPtr->end)) {
		return NULL;
	    }
	}
    }

    /*
     * Work down through levels of the tree until a node is found at level 0.
     */

    while (nodePtr->level != 0) {
	for (nodePtr = nodePtr->children.nodePtr;
		nodePtr->numLines <= line;
		nodePtr = nodePtr->nextPtr) {
	    if (nodePtr == NULL) {
		Tcl_Panic("TkBTreeFindLine ran out of nodes");
	    }
	    line -= nodePtr->numLines;
	}
    }

    /*
     * Work through the lines attached to the level-0 node.
     */

    for (linePtr = nodePtr->children.linePtr; line > 0;
	    linePtr = linePtr->nextPtr) {
	if (linePtr == NULL) {
	    Tcl_Panic("TkBTreeFindLine ran out of lines");
	}
	line -= 1;
    }
    return linePtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeFindPixelLine --
 *
 *	Find a particular line in a B-tree based on its pixel count.
 *
 * Results:
 *	The return value is a pointer to the line structure for the line which
 *	contains the pixel "pixels", or NULL if no such line exists. If the
 *	first line is of height 20, then pixels 0-19 will return it, and
 *	pixels = 20 will return the next line.
 *
 *	If pixelOffset is non-NULL, it is set to the amount by which 'pixels'
 *	exceeds the first pixel located on the returned line. This should
 *	always be non-negative.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

TkTextLine *
TkBTreeFindPixelLine(
    TkTextBTree tree,		/* B-tree to use. */
    const TkText *textPtr,	/* Relative to this client of the B-tree. */
    int pixels,			/* Pixel index of desired line. */
    int *pixelOffset)		/* Used to return offset. */
{
    BTree *treePtr = (BTree *) tree;
    register Node *nodePtr;
    register TkTextLine *linePtr;
    int pixelReference = textPtr->pixelReference;

    nodePtr = treePtr->rootPtr;

    if ((pixels < 0) || (pixels > nodePtr->numPixels[pixelReference])) {
	return NULL;
    }

    if (nodePtr->numPixels[pixelReference] == 0) {
	Tcl_Panic("TkBTreeFindPixelLine called with empty window");
    }

    /*
     * Work down through levels of the tree until a node is found at level 0.
     */

    while (nodePtr->level != 0) {
	for (nodePtr = nodePtr->children.nodePtr;
		nodePtr->numPixels[pixelReference] <= pixels;
		nodePtr = nodePtr->nextPtr) {
	    if (nodePtr == NULL) {
		Tcl_Panic("TkBTreeFindPixelLine ran out of nodes");
	    }
	    pixels -= nodePtr->numPixels[pixelReference];
	}
    }

    /*
     * Work through the lines attached to the level-0 node.
     */

    for (linePtr = nodePtr->children.linePtr;
	    linePtr->pixels[2 * pixelReference] < pixels;
	    linePtr = linePtr->nextPtr) {
	if (linePtr == NULL) {
	    Tcl_Panic("TkBTreeFindPixelLine ran out of lines");
	}
	pixels -= linePtr->pixels[2 * pixelReference];
    }
    if (pixelOffset != NULL && linePtr != NULL) {
	*pixelOffset = pixels;
    }
    return linePtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeNextLine --
 *
 *	Given an existing line in a B-tree, this function locates the next
 *	line in the B-tree. This function is used for scanning through the
 *	B-tree.
 *
 * Results:
 *	The return value is a pointer to the line that immediately follows
 *	linePtr, or NULL if there is no such line.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

TkTextLine *
TkBTreeNextLine(
    const TkText *textPtr,	/* Next line in the context of this client. */
    register TkTextLine *linePtr)
				/* Pointer to existing line in B-tree. */
{
    register Node *nodePtr;

    if (linePtr->nextPtr != NULL) {
	if (textPtr != NULL && (linePtr == textPtr->end)) {
	    return NULL;
	} else {
	    return linePtr->nextPtr;
	}
    }

    /*
     * This was the last line associated with the particular parent node.
     * Search up the tree for the next node, then search down from that node
     * to find the first line.
     */

    for (nodePtr = linePtr->parentPtr; ; nodePtr = nodePtr->parentPtr) {
	if (nodePtr->nextPtr != NULL) {
	    nodePtr = nodePtr->nextPtr;
	    break;
	}
	if (nodePtr->parentPtr == NULL) {
	    return NULL;
	}
    }
    while (nodePtr->level > 0) {
	nodePtr = nodePtr->children.nodePtr;
    }
    return nodePtr->children.linePtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreePreviousLine --
 *
 *	Given an existing line in a B-tree, this function locates the previous
 *	line in the B-tree. This function is used for scanning through the
 *	B-tree in the reverse direction.
 *
 * Results:
 *	The return value is a pointer to the line that immediately preceeds
 *	linePtr, or NULL if there is no such line.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

TkTextLine *
TkBTreePreviousLine(
    TkText *textPtr,		/* Relative to this client of the B-tree. */
    register TkTextLine *linePtr)
				/* Pointer to existing line in B-tree. */
{
    register Node *nodePtr;
    register Node *node2Ptr;
    register TkTextLine *prevPtr;

    if (textPtr != NULL && textPtr->start == linePtr) {
	return NULL;
    }

    /*
     * Find the line under this node just before the starting line.
     */

    prevPtr = linePtr->parentPtr->children.linePtr;   /* First line at leaf. */
    while (prevPtr != linePtr) {
	if (prevPtr->nextPtr == linePtr) {
	    return prevPtr;
	}
	prevPtr = prevPtr->nextPtr;
	if (prevPtr == NULL) {
	    Tcl_Panic("TkBTreePreviousLine ran out of lines");
	}
    }

    /*
     * This was the first line associated with the particular parent node.
     * Search up the tree for the previous node, then search down from that
     * node to find its last line.
     */

    for (nodePtr = linePtr->parentPtr; ; nodePtr = nodePtr->parentPtr) {
	if (nodePtr == NULL || nodePtr->parentPtr == NULL) {
	    return NULL;
	}
	if (nodePtr != nodePtr->parentPtr->children.nodePtr) {
	    break;
	}
    }
    for (node2Ptr = nodePtr->parentPtr->children.nodePtr; ;
	    node2Ptr = node2Ptr->children.nodePtr) {
	while (node2Ptr->nextPtr != nodePtr) {
	    node2Ptr = node2Ptr->nextPtr;
	}
	if (node2Ptr->level == 0) {
	    break;
	}
	nodePtr = NULL;
    }
    for (prevPtr = node2Ptr->children.linePtr ; ; prevPtr = prevPtr->nextPtr) {
	if (prevPtr->nextPtr == NULL) {
	    return prevPtr;
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreePixelsTo --
 *
 *	Given a pointer to a line in a B-tree, return the numerical pixel
 *	index of the top of that line (i.e. the result does not include the
 *	height of the given line).
 *
 *	Since the last line of text (the artificial one) has zero height by
 *	defintion, calling this with the last line will return the total
 *	number of pixels in the widget.
 *
 * Results:
 *	The result is the index of linePtr within the tree, where 0
 *	corresponds to the first line in the tree.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
TkBTreePixelsTo(
    const TkText *textPtr,	/* Relative to this client of the B-tree. */
    TkTextLine *linePtr)	/* Pointer to existing line in B-tree. */
{
    register TkTextLine *linePtr2;
    register Node *nodePtr, *parentPtr;
    int index;
    int pixelReference = textPtr->pixelReference;

    /*
     * First count how many pixels precede this line in its level-0 node.
     */

    nodePtr = linePtr->parentPtr;
    index = 0;
    for (linePtr2 = nodePtr->children.linePtr; linePtr2 != linePtr;
	    linePtr2 = linePtr2->nextPtr) {
	if (linePtr2 == NULL) {
	    Tcl_Panic("TkBTreePixelsTo couldn't find line");
	}
	index += linePtr2->pixels[2 * pixelReference];
    }

    /*
     * Now work up through the levels of the tree one at a time, counting how
     * many pixels are in nodes preceding the current node.
     */

    for (parentPtr = nodePtr->parentPtr ; parentPtr != NULL;
	    nodePtr = parentPtr, parentPtr = parentPtr->parentPtr) {
	register Node *nodePtr2;

	for (nodePtr2 = parentPtr->children.nodePtr; nodePtr2 != nodePtr;
		nodePtr2 = nodePtr2->nextPtr) {
	    if (nodePtr2 == NULL) {
		Tcl_Panic("TkBTreePixelsTo couldn't find node");
	    }
	    index += nodePtr2->numPixels[pixelReference];
	}
    }
    return index;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeLinesTo --
 *
 *	Given a pointer to a line in a B-tree, return the numerical index of
 *	that line.
 *
 * Results:
 *	The result is the index of linePtr within the tree, where 0
 *	corresponds to the first line in the tree.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
TkBTreeLinesTo(
    const TkText *textPtr,	/* Relative to this client of the B-tree. */
    TkTextLine *linePtr)	/* Pointer to existing line in B-tree. */
{
    register TkTextLine *linePtr2;
    register Node *nodePtr, *parentPtr, *nodePtr2;
    int index;

    /*
     * First count how many lines precede this one in its level-0 node.
     */

    nodePtr = linePtr->parentPtr;
    index = 0;
    for (linePtr2 = nodePtr->children.linePtr; linePtr2 != linePtr;
	    linePtr2 = linePtr2->nextPtr) {
	if (linePtr2 == NULL) {
	    Tcl_Panic("TkBTreeLinesTo couldn't find line");
	}
	index += 1;
    }

    /*
     * Now work up through the levels of the tree one at a time, counting how
     * many lines are in nodes preceding the current node.
     */

    for (parentPtr = nodePtr->parentPtr ; parentPtr != NULL;
	    nodePtr = parentPtr, parentPtr = parentPtr->parentPtr) {
	for (nodePtr2 = parentPtr->children.nodePtr; nodePtr2 != nodePtr;
		nodePtr2 = nodePtr2->nextPtr) {
	    if (nodePtr2 == NULL) {
		Tcl_Panic("TkBTreeLinesTo couldn't find node");
	    }
	    index += nodePtr2->numLines;
	}
    }
    if (textPtr != NULL && textPtr->start != NULL) {
	index -= TkBTreeLinesTo(NULL, textPtr->start);
    }
    return index;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeLinkSegment --
 *
 *	This function adds a new segment to a B-tree at a given location.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	SegPtr will be linked into its tree.
 *
 *----------------------------------------------------------------------
 */

	/* ARGSUSED */
void
TkBTreeLinkSegment(
    TkTextSegment *segPtr,	/* Pointer to new segment to be added to
				 * B-tree. Should be completely initialized by
				 * caller except for nextPtr field. */
    TkTextIndex *indexPtr)	/* Where to add segment: it gets linked in
				 * just before the segment indicated here. */
{
    register TkTextSegment *prevPtr;

    prevPtr = SplitSeg(indexPtr);
    if (prevPtr == NULL) {
	segPtr->nextPtr = indexPtr->linePtr->segPtr;
	indexPtr->linePtr->segPtr = segPtr;
    } else {
	segPtr->nextPtr = prevPtr->nextPtr;
	prevPtr->nextPtr = segPtr;
    }
    CleanupLine(indexPtr->linePtr);
    if (tkBTreeDebug) {
	TkBTreeCheck(indexPtr->tree);
    }
    ((BTree *)indexPtr->tree)->stateEpoch++;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeUnlinkSegment --
 *
 *	This function unlinks a segment from its line in a B-tree.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	SegPtr will be unlinked from linePtr. The segment itself isn't
 *	modified by this function.
 *
 *----------------------------------------------------------------------
 */

	/* ARGSUSED */
void
TkBTreeUnlinkSegment(
    TkTextSegment *segPtr,	/* Segment to be unlinked. */
    TkTextLine *linePtr)	/* Line that currently contains segment. */
{
    register TkTextSegment *prevPtr;

    if (linePtr->segPtr == segPtr) {
	linePtr->segPtr = segPtr->nextPtr;
    } else {
	prevPtr = linePtr->segPtr;
	while (prevPtr->nextPtr != segPtr) {
	    prevPtr = prevPtr->nextPtr;

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

		linePtr = TkBTreeNextLine(NULL, linePtr);
		prevPtr = linePtr->segPtr;
	    }
	}
	prevPtr->nextPtr = segPtr->nextPtr;
    }
    CleanupLine(linePtr);
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeTag --
 *
 *	Turn a given tag on or off for a given range of characters in a B-tree
 *	of text.
 *
 * Results:
 *	1 if the tags on any characters in the range were changed, and zero
 *	otherwise (i.e. if the tag was already absent (add = 0) or present
 *	(add = 1) on the index range in question).
 *
 * Side effects:
 *	The given tag is added to the given range of characters in the tree or
 *	removed from all those characters, depending on the "add" argument.
 *	The structure of the btree is modified enough that index1Ptr and
 *	index2Ptr are no longer valid after this function returns, and the
 *	indexes may be modified by this function.
 *
 *----------------------------------------------------------------------
 */

int
TkBTreeTag(
    register TkTextIndex *index1Ptr,
				/* Indicates first character in range. */
    register TkTextIndex *index2Ptr,
				/* Indicates character just after the last one
				 * in range. */
    TkTextTag *tagPtr,		/* Tag to add or remove. */
    int add)			/* One means add tag to the given range of
				 * characters; zero means remove the tag from
				 * the range. */
{
    TkTextSegment *segPtr, *prevPtr;
    TkTextSearch search;
    TkTextLine *cleanupLinePtr;
    int oldState, changed, anyChanges = 0;

    /*
     * See whether the tag is present at the start of the range. If the state
     * doesn't already match what we want then add a toggle there.
     */

    oldState = TkBTreeCharTagged(index1Ptr, tagPtr);
    if ((add != 0) ^ oldState) {
	segPtr = (TkTextSegment *) ckalloc(TSEG_SIZE);
	segPtr->typePtr = (add) ? &tkTextToggleOnType : &tkTextToggleOffType;
	prevPtr = SplitSeg(index1Ptr);
	if (prevPtr == NULL) {
	    segPtr->nextPtr = index1Ptr->linePtr->segPtr;
	    index1Ptr->linePtr->segPtr = segPtr;
	} else {
	    segPtr->nextPtr = prevPtr->nextPtr;
	    prevPtr->nextPtr = segPtr;
	}
	segPtr->size = 0;
	segPtr->body.toggle.tagPtr = tagPtr;
	segPtr->body.toggle.inNodeCounts = 0;
	anyChanges = 1;
    }

    /*
     * Scan the range of characters and delete any internal tag transitions.
     * Keep track of what the old state was at the end of the range, and add a
     * toggle there if it's needed.
     */

    TkBTreeStartSearch(index1Ptr, index2Ptr, tagPtr, &search);
    cleanupLinePtr = index1Ptr->linePtr;
    while (TkBTreeNextTag(&search)) {
	anyChanges = 1;
	oldState ^= 1;
	segPtr = search.segPtr;
	prevPtr = search.curIndex.linePtr->segPtr;
	if (prevPtr == segPtr) {
	    search.curIndex.linePtr->segPtr = segPtr->nextPtr;
	} else {
	    while (prevPtr->nextPtr != segPtr) {
		prevPtr = prevPtr->nextPtr;
	    }
	    prevPtr->nextPtr = segPtr->nextPtr;
	}
	if (segPtr->body.toggle.inNodeCounts) {
	    ChangeNodeToggleCount(search.curIndex.linePtr->parentPtr,
		    segPtr->body.toggle.tagPtr, -1);
	    segPtr->body.toggle.inNodeCounts = 0;
	    changed = 1;
	} else {
	    changed = 0;
	}
	ckfree((char *) segPtr);

	/*
	 * The code below is a bit tricky. After deleting a toggle we
	 * eventually have to call CleanupLine, in order to allow character
	 * segments to be merged together. To do this, we remember in
	 * cleanupLinePtr a line that needs to be cleaned up, but we don't
	 * clean it up until we've moved on to a different line. That way the
	 * cleanup process won't goof up segPtr.
	 */

	if (cleanupLinePtr != search.curIndex.linePtr) {
	    CleanupLine(cleanupLinePtr);
	    cleanupLinePtr = search.curIndex.linePtr;
	}

	/*
	 * Quick hack. ChangeNodeToggleCount may move the tag's root location
	 * around and leave the search in the void. This resets the search.
	 */

	if (changed) {
	    TkBTreeStartSearch(index1Ptr, index2Ptr, tagPtr, &search);
	}
    }
    if ((add != 0) ^ oldState) {
	segPtr = (TkTextSegment *) ckalloc(TSEG_SIZE);
	segPtr->typePtr = (add) ? &tkTextToggleOffType : &tkTextToggleOnType;
	prevPtr = SplitSeg(index2Ptr);
	if (prevPtr == NULL) {
	    segPtr->nextPtr = index2Ptr->linePtr->segPtr;
	    index2Ptr->linePtr->segPtr = segPtr;
	} else {
	    segPtr->nextPtr = prevPtr->nextPtr;
	    prevPtr->nextPtr = segPtr;
	}
	segPtr->size = 0;
	segPtr->body.toggle.tagPtr = tagPtr;
	segPtr->body.toggle.inNodeCounts = 0;
	anyChanges = 1;
    }

    /*
     * Cleanup cleanupLinePtr and the last line of the range, if these are
     * different.
     */

    if (anyChanges) {
	CleanupLine(cleanupLinePtr);
	if (cleanupLinePtr != index2Ptr->linePtr) {
	    CleanupLine(index2Ptr->linePtr);
	}
	((BTree *)index1Ptr->tree)->stateEpoch++;
    }

    if (tkBTreeDebug) {
	TkBTreeCheck(index1Ptr->tree);
    }
    return anyChanges;
}

/*
 *----------------------------------------------------------------------
 *
 * ChangeNodeToggleCount --
 *
 *	This function increments or decrements the toggle count for a
 *	particular tag in a particular node and all its ancestors up to the
 *	per-tag root node.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The toggle count for tag is adjusted up or down by "delta" in nodePtr.
 *	This routine maintains the tagRootPtr that identifies the root node
 *	for the tag, moving it up or down the tree as needed.
 *
 *----------------------------------------------------------------------
 */

static void
ChangeNodeToggleCount(
    register Node *nodePtr,	/* Node whose toggle count for a tag must be
				 * changed. */
    TkTextTag *tagPtr,		/* Information about tag. */
    int delta)			/* Amount to add to current toggle count for
				 * tag (may be negative). */
{
    register Summary *summaryPtr, *prevPtr;
    register Node *node2Ptr;
    int rootLevel;		/* Level of original tag root. */

    tagPtr->toggleCount += delta;
    if (tagPtr->tagRootPtr == NULL) {
	tagPtr->tagRootPtr = nodePtr;
	return;
    }

    /*
     * Note the level of the existing root for the tag so we can detect if it
     * needs to be moved because of the toggle count change.
     */

    rootLevel = tagPtr->tagRootPtr->level;

    /*
     * Iterate over the node and its ancestors up to the tag root, adjusting
     * summary counts at each node and moving the tag's root upwards if
     * necessary.
     */

    for ( ; nodePtr != tagPtr->tagRootPtr; nodePtr = nodePtr->parentPtr) {
	/*
	 * See if there's already an entry for this tag for this node. If so,
	 * perhaps all we have to do is adjust its count.
	 */

	for (prevPtr = NULL, summaryPtr = nodePtr->summaryPtr;
		summaryPtr != NULL;
		prevPtr = summaryPtr, summaryPtr = summaryPtr->nextPtr) {
	    if (summaryPtr->tagPtr == tagPtr) {
		break;
	    }
	}
	if (summaryPtr != NULL) {
	    summaryPtr->toggleCount += delta;
	    if (summaryPtr->toggleCount > 0 &&
		    summaryPtr->toggleCount < tagPtr->toggleCount) {
		continue;
	    }
	    if (summaryPtr->toggleCount != 0) {
		/*
		 * Should never find a node with max toggle count at this
		 * point (there shouldn't have been a summary entry in the
		 * first place).
		 */

		Tcl_Panic("ChangeNodeToggleCount: bad toggle count (%d) max (%d)",
		    summaryPtr->toggleCount, tagPtr->toggleCount);
	    }

	    /*
	     * Zero toggle count; must remove this tag from the list.
	     */

	    if (prevPtr == NULL) {
		nodePtr->summaryPtr = summaryPtr->nextPtr;
	    } else {
		prevPtr->nextPtr = summaryPtr->nextPtr;
	    }
	    ckfree((char *) summaryPtr);
	} else {
	    /*
	     * This tag isn't currently in the summary information list.
	     */

	    if (rootLevel == nodePtr->level) {
		/*
		 * The old tag root is at the same level in the tree as this
		 * node, but it isn't at this node. Move the tag root up a
		 * level, in the hopes that it will now cover this node as
		 * well as the old root (if not, we'll move it up again the
		 * next time through the loop). To push it up one level we
		 * copy the original toggle count into the summary information
		 * at the old root and change the root to its parent node.
		 */

		Node *rootNodePtr = tagPtr->tagRootPtr;

		summaryPtr = (Summary *) ckalloc(sizeof(Summary));
		summaryPtr->tagPtr = tagPtr;
		summaryPtr->toggleCount = tagPtr->toggleCount - delta;
		summaryPtr->nextPtr = rootNodePtr->summaryPtr;
		rootNodePtr->summaryPtr = summaryPtr;
		rootNodePtr = rootNodePtr->parentPtr;
		rootLevel = rootNodePtr->level;
		tagPtr->tagRootPtr = rootNodePtr;
	    }
	    summaryPtr = (Summary *) ckalloc(sizeof(Summary));
	    summaryPtr->tagPtr = tagPtr;
	    summaryPtr->toggleCount = delta;
	    summaryPtr->nextPtr = nodePtr->summaryPtr;
	    nodePtr->summaryPtr = summaryPtr;
	}
    }

    /*
     * If we've decremented the toggle count, then it may be necessary to push
     * the tag root down one or more levels.
     */

    if (delta >= 0) {
	return;
    }
    if (tagPtr->toggleCount == 0) {
	tagPtr->tagRootPtr = NULL;
	return;
    }
    nodePtr = tagPtr->tagRootPtr;
    while (nodePtr->level > 0) {
	/*
	 * See if a single child node accounts for all of the tag's toggles.
	 * If so, push the root down one level.
	 */

	for (node2Ptr = nodePtr->children.nodePtr;
		node2Ptr != NULL ;
		node2Ptr = node2Ptr->nextPtr) {
	    for (prevPtr = NULL, summaryPtr = node2Ptr->summaryPtr;
		    summaryPtr != NULL;
		    prevPtr = summaryPtr, summaryPtr = summaryPtr->nextPtr) {
		if (summaryPtr->tagPtr == tagPtr) {
		    break;
		}
	    }
	    if (summaryPtr == NULL) {
		continue;
	    }
	    if (summaryPtr->toggleCount != tagPtr->toggleCount) {
		/*
		 * No node has all toggles, so the root is still valid.
		 */

		return;
	    }

	    /*
	     * This node has all the toggles, so push down the root.
	     */

	    if (prevPtr == NULL) {
		node2Ptr->summaryPtr = summaryPtr->nextPtr;
	    } else {
		prevPtr->nextPtr = summaryPtr->nextPtr;
	    }
	    ckfree((char *) summaryPtr);
	    tagPtr->tagRootPtr = node2Ptr;
	    break;
	}
	nodePtr = tagPtr->tagRootPtr;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * FindTagStart --
 *
 *	Find the start of the first range of a tag.
 *
 * Results:
 *	The return value is a pointer to the first tag toggle segment for the
 *	tag. This can be either a tagon or tagoff segments because of the way
 *	TkBTreeAdd removes a tag. Sets *indexPtr to be the index of the tag
 *	toggle.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static TkTextSegment *
FindTagStart(
    TkTextBTree tree,		/* Tree to search within. */
    TkTextTag *tagPtr,		/* Tag to search for. */
    TkTextIndex *indexPtr)	/* Return - index information. */
{
    register Node *nodePtr;
    register TkTextLine *linePtr;
    register TkTextSegment *segPtr;
    register Summary *summaryPtr;
    int offset;

    nodePtr = tagPtr->tagRootPtr;
    if (nodePtr == NULL) {
	return NULL;
    }

    /*
     * Search from the root of the subtree that contains the tag down to the
     * level 0 node.
     */

    while (nodePtr && nodePtr->level > 0) {
	for (nodePtr = nodePtr->children.nodePtr ; nodePtr != NULL;
		nodePtr = nodePtr->nextPtr) {
	    for (summaryPtr = nodePtr->summaryPtr ; summaryPtr != NULL;
		    summaryPtr = summaryPtr->nextPtr) {
		if (summaryPtr->tagPtr == tagPtr) {
		    goto gotNodeWithTag;
		}
	    }
	}
    gotNodeWithTag:
	continue;
    }

    if (nodePtr == NULL) {
	return NULL;
    }

    /*
     * Work through the lines attached to the level-0 node.
     */

    for (linePtr = nodePtr->children.linePtr; linePtr != NULL;
	    linePtr = linePtr->nextPtr) {
	for (offset = 0, segPtr = linePtr->segPtr ; segPtr != NULL;
		offset += segPtr->size, segPtr = segPtr->nextPtr) {
	    if (((segPtr->typePtr == &tkTextToggleOnType)
		    || (segPtr->typePtr == &tkTextToggleOffType))
		    && (segPtr->body.toggle.tagPtr == tagPtr)) {
		/*
		 * It is possible that this is a tagoff tag, but that gets
		 * cleaned up later.
		 */

		indexPtr->tree = tree;
		indexPtr->linePtr = linePtr;
		indexPtr->byteIndex = offset;
		return segPtr;
	    }
	}
    }
    return NULL;
}

/*
 *----------------------------------------------------------------------
 *
 * FindTagEnd --
 *
 *	Find the end of the last range of a tag.
 *
 * Results:
 *	The return value is a pointer to the last tag toggle segment for the
 *	tag. This can be either a tagon or tagoff segments because of the way
 *	TkBTreeAdd removes a tag. Sets *indexPtr to be the index of the tag
 *	toggle.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static TkTextSegment *
FindTagEnd(
    TkTextBTree tree,		/* Tree to search within. */
    TkTextTag *tagPtr,		/* Tag to search for. */
    TkTextIndex *indexPtr)	/* Return - index information. */
{
    register Node *nodePtr, *lastNodePtr;
    register TkTextLine *linePtr ,*lastLinePtr;
    register TkTextSegment *segPtr, *lastSegPtr, *last2SegPtr;
    register Summary *summaryPtr;
    int lastoffset, lastoffset2, offset;

    nodePtr = tagPtr->tagRootPtr;
    if (nodePtr == NULL) {
	return NULL;
    }

    /*
     * Search from the root of the subtree that contains the tag down to the
     * level 0 node.
     */

    while (nodePtr && nodePtr->level > 0) {
	for (lastNodePtr = NULL, nodePtr = nodePtr->children.nodePtr ;
		nodePtr != NULL; nodePtr = nodePtr->nextPtr) {
	    for (summaryPtr = nodePtr->summaryPtr ; summaryPtr != NULL;
		    summaryPtr = summaryPtr->nextPtr) {
		if (summaryPtr->tagPtr == tagPtr) {
		    lastNodePtr = nodePtr;
		    break;
		}
	    }
	}
	nodePtr = lastNodePtr;
    }

    if (nodePtr == NULL) {
	return NULL;
    }

    /*
     * Work through the lines attached to the level-0 node.
     */

    last2SegPtr = NULL;
    lastoffset2 = 0;
    lastoffset = 0;
    for (lastLinePtr = NULL, linePtr = nodePtr->children.linePtr;
	    linePtr != NULL; linePtr = linePtr->nextPtr) {
	for (offset = 0, lastSegPtr = NULL, segPtr = linePtr->segPtr ;
		segPtr != NULL;
		offset += segPtr->size, segPtr = segPtr->nextPtr) {
	    if (((segPtr->typePtr == &tkTextToggleOnType)
		    || (segPtr->typePtr == &tkTextToggleOffType))
		    && (segPtr->body.toggle.tagPtr == tagPtr)) {
		lastSegPtr = segPtr;
		lastoffset = offset;
	    }
	}
	if (lastSegPtr != NULL) {
	    lastLinePtr = linePtr;
	    last2SegPtr = lastSegPtr;
	    lastoffset2 = lastoffset;
	}
    }
    indexPtr->tree = tree;
    indexPtr->linePtr = lastLinePtr;
    indexPtr->byteIndex = lastoffset2;
    return last2SegPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeStartSearch --
 *
 *	This function sets up a search for tag transitions involving a given
 *	tag (or all tags) in a given range of the text.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The information at *searchPtr is set up so that subsequent calls to
 *	TkBTreeNextTag or TkBTreePrevTag will return information about the
 *	locations of tag transitions. Note that TkBTreeNextTag or
 *	TkBTreePrevTag must be called to get the first transition. Note:
 *	unlike TkBTreeNextTag and TkBTreePrevTag, this routine does not
 *	guarantee that searchPtr->curIndex is equal to *index1Ptr. It may be
 *	greater than that if *index1Ptr is less than the first tag transition.
 *
 *----------------------------------------------------------------------
 */

void
TkBTreeStartSearch(
    TkTextIndex *index1Ptr,	/* Search starts here. Tag toggles at this
				 * position will not be returned. */
    TkTextIndex *index2Ptr,	/* Search stops here. Tag toggles at this
				 * position *will* be returned. */
    TkTextTag *tagPtr,		/* Tag to search for. NULL means search for
				 * any tag. */
    register TkTextSearch *searchPtr)
				/* Where to store information about search's
				 * progress. */
{
    int offset;
    TkTextIndex index0;		/* First index of the tag. */
    TkTextSegment *seg0Ptr;	/* First segment of the tag. */

    /*
     * Find the segment that contains the first toggle for the tag. This may
     * become the starting point in the search.
     */

    seg0Ptr = FindTagStart(index1Ptr->tree, tagPtr, &index0);
    if (seg0Ptr == NULL) {
	/*
	 * Even though there are no toggles, the display code still uses the
	 * search curIndex, so initialize that anyway.
	 */

	searchPtr->linesLeft = 0;
	searchPtr->curIndex = *index1Ptr;
	searchPtr->segPtr = NULL;
	searchPtr->nextPtr = NULL;
	return;
    }
    if (TkTextIndexCmp(index1Ptr, &index0) < 0) {
	/*
	 * Adjust start of search up to the first range of the tag.
	 */

	searchPtr->curIndex = index0;
	searchPtr->segPtr = NULL;
	searchPtr->nextPtr = seg0Ptr;	/* Will be returned by NextTag. */
	index1Ptr = &index0;
    } else {
	searchPtr->curIndex = *index1Ptr;
	searchPtr->segPtr = NULL;
	searchPtr->nextPtr = TkTextIndexToSeg(index1Ptr, &offset);
	searchPtr->curIndex.byteIndex -= offset;
    }
    searchPtr->lastPtr = TkTextIndexToSeg(index2Ptr, NULL);
    searchPtr->tagPtr = tagPtr;
    searchPtr->linesLeft = TkBTreeLinesTo(NULL, index2Ptr->linePtr) + 1
	    - TkBTreeLinesTo(NULL, index1Ptr->linePtr);
    searchPtr->allTags = (tagPtr == NULL);
    if (searchPtr->linesLeft == 1) {
	/*
	 * Starting and stopping segments are in the same line; mark the
	 * search as over immediately if the second segment is before the
	 * first. A search does not return a toggle at the very start of the
	 * range, unless the range is artificially moved up to index0.
	 */

	if (((index1Ptr == &index0) &&
		(index1Ptr->byteIndex > index2Ptr->byteIndex)) ||
		((index1Ptr != &index0) &&
		(index1Ptr->byteIndex >= index2Ptr->byteIndex))) {
	    searchPtr->linesLeft = 0;
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeStartSearchBack --
 *
 *	This function sets up a search backwards for tag transitions involving
 *	a given tag (or all tags) in a given range of the text. In the normal
 *	case the first index (*index1Ptr) is beyond the second index
 *	(*index2Ptr).
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The information at *searchPtr is set up so that subsequent calls to
 *	TkBTreePrevTag will return information about the locations of tag
 *	transitions. Note that TkBTreePrevTag must be called to get the first
 *	transition. Note: unlike TkBTreeNextTag and TkBTreePrevTag, this
 *	routine does not guarantee that searchPtr->curIndex is equal to
 *	*index1Ptr. It may be less than that if *index1Ptr is greater than the
 *	last tag transition.
 *
 *----------------------------------------------------------------------
 */

void
TkBTreeStartSearchBack(
    TkTextIndex *index1Ptr,	/* Search starts here. Tag toggles at this
				 * position will not be returned. */
    TkTextIndex *index2Ptr,	/* Search stops here. Tag toggles at this
				 * position *will* be returned. */
    TkTextTag *tagPtr,		/* Tag to search for. NULL means search for
				 * any tag. */
    register TkTextSearch *searchPtr)
				/* Where to store information about search's
				 * progress. */
{
    int offset;
    TkTextIndex index0;		/* Last index of the tag. */
    TkTextIndex backOne;	/* One character before starting index. */
    TkTextSegment *seg0Ptr;	/* Last segment of the tag. */

    /*
     * Find the segment that contains the last toggle for the tag. This may
     * become the starting point in the search.
     */

    seg0Ptr = FindTagEnd(index1Ptr->tree, tagPtr, &index0);
    if (seg0Ptr == NULL) {
	/*
	 * Even though there are no toggles, the display code still uses the
	 * search curIndex, so initialize that anyway.
	 */

	searchPtr->linesLeft = 0;
	searchPtr->curIndex = *index1Ptr;
	searchPtr->segPtr = NULL;
	searchPtr->nextPtr = NULL;
	return;
    }

    /*
     * Adjust the start of the search so it doesn't find any tag toggles
     * that are right at the index specified by the user.
     */

    if (TkTextIndexCmp(index1Ptr, &index0) > 0) {
	searchPtr->curIndex = index0;
	index1Ptr = &index0;
    } else {
	TkTextIndexBackChars(NULL, index1Ptr, 1, &searchPtr->curIndex,
		COUNT_INDICES);
    }
    searchPtr->segPtr = NULL;
    searchPtr->nextPtr = TkTextIndexToSeg(&searchPtr->curIndex, &offset);
    searchPtr->curIndex.byteIndex -= offset;

    /*
     * Adjust the end of the search so it does find toggles that are right at
     * the second index specified by the user.
     */

    if ((TkBTreeLinesTo(NULL, index2Ptr->linePtr) == 0) &&
	    (index2Ptr->byteIndex == 0)) {
	backOne = *index2Ptr;
	searchPtr->lastPtr = NULL;	/* Signals special case for 1.0. */
    } else {
	TkTextIndexBackChars(NULL, index2Ptr, 1, &backOne, COUNT_INDICES);
	searchPtr->lastPtr = TkTextIndexToSeg(&backOne, NULL);
    }
    searchPtr->tagPtr = tagPtr;
    searchPtr->linesLeft = TkBTreeLinesTo(NULL, index1Ptr->linePtr) + 1
	    - TkBTreeLinesTo(NULL, backOne.linePtr);
    searchPtr->allTags = (tagPtr == NULL);
    if (searchPtr->linesLeft == 1) {
	/*
	 * Starting and stopping segments are in the same line; mark the
	 * search as over immediately if the second segment is after the
	 * first.
	 */

	if (index1Ptr->byteIndex <= backOne.byteIndex) {
	    searchPtr->linesLeft = 0;
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeNextTag --
 *
 *	Once a tag search has begun, successive calls to this function return
 *	successive tag toggles. Note: it is NOT SAFE to call this function if
 *	characters have been inserted into or deleted from the B-tree since
 *	the call to TkBTreeStartSearch.
 *
 * Results:
 *	The return value is 1 if another toggle was found that met the
 *	criteria specified in the call to TkBTreeStartSearch; in this case
 *	searchPtr->curIndex gives the toggle's position and
 *	searchPtr->curTagPtr points to its segment. 0 is returned if no more
 *	matching tag transitions were found; in this case searchPtr->curIndex
 *	is the same as searchPtr->stopIndex.
 *
 * Side effects:
 *	Information in *searchPtr is modified to update the state of the
 *	search and indicate where the next tag toggle is located.
 *
 *----------------------------------------------------------------------
 */

int
TkBTreeNextTag(
    register TkTextSearch *searchPtr)
				/* Information about search in progress; must
				 * have been set up by call to
				 * TkBTreeStartSearch. */
{
    register TkTextSegment *segPtr;
    register Node *nodePtr;
    register Summary *summaryPtr;

    if (searchPtr->linesLeft <= 0) {
	goto searchOver;
    }

    /*
     * The outermost loop iterates over lines that may potentially contain a
     * relevant tag transition, starting from the current segment in the
     * current line.
     */

    segPtr = searchPtr->nextPtr;
    while (1) {
	/*
	 * Check for more tags on the current line.
	 */

	for ( ; segPtr != NULL; segPtr = segPtr->nextPtr) {
	    if (segPtr == searchPtr->lastPtr) {
		goto searchOver;
	    }
	    if (((segPtr->typePtr == &tkTextToggleOnType)
		    || (segPtr->typePtr == &tkTextToggleOffType))
		    && (searchPtr->allTags
		    || (segPtr->body.toggle.tagPtr == searchPtr->tagPtr))) {
		searchPtr->segPtr = segPtr;
		searchPtr->nextPtr = segPtr->nextPtr;
		searchPtr->tagPtr = segPtr->body.toggle.tagPtr;
		return 1;
	    }
	    searchPtr->curIndex.byteIndex += segPtr->size;
	}

	/*
	 * See if there are more lines associated with the current parent
	 * node. If so, go back to the top of the loop to search the next one.
	 */

	nodePtr = searchPtr->curIndex.linePtr->parentPtr;
	searchPtr->curIndex.linePtr = searchPtr->curIndex.linePtr->nextPtr;
	searchPtr->linesLeft--;
	if (searchPtr->linesLeft <= 0) {
	    goto searchOver;
	}
	if (searchPtr->curIndex.linePtr != NULL) {
	    segPtr = searchPtr->curIndex.linePtr->segPtr;
	    searchPtr->curIndex.byteIndex = 0;
	    continue;
	}
	if (nodePtr == searchPtr->tagPtr->tagRootPtr) {
	    goto searchOver;
	}

	/*
	 * Search across and up through the B-tree's node hierarchy looking
	 * for the next node that has a relevant tag transition somewhere in
	 * its subtree. Be sure to update linesLeft as we skip over large
	 * chunks of lines.
	 */

	while (1) {
	    while (nodePtr->nextPtr == NULL) {
		if (nodePtr->parentPtr == NULL ||
		    nodePtr->parentPtr == searchPtr->tagPtr->tagRootPtr) {
		    goto searchOver;
		}
		nodePtr = nodePtr->parentPtr;
	    }
	    nodePtr = nodePtr->nextPtr;
	    for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
		    summaryPtr = summaryPtr->nextPtr) {
		if ((searchPtr->allTags) ||
			(summaryPtr->tagPtr == searchPtr->tagPtr)) {
		    goto gotNodeWithTag;
		}
	    }
	    searchPtr->linesLeft -= nodePtr->numLines;
	}

	/*
	 * At this point we've found a subtree that has a relevant tag
	 * transition. Now search down (and across) through that subtree to
	 * find the first level-0 node that has a relevant tag transition.
	 */

    gotNodeWithTag:
	while (nodePtr->level > 0) {
	    for (nodePtr = nodePtr->children.nodePtr; ;
		    nodePtr = nodePtr->nextPtr) {
		for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
			summaryPtr = summaryPtr->nextPtr) {
		    if ((searchPtr->allTags)
			    || (summaryPtr->tagPtr == searchPtr->tagPtr)) {
			/*
			 * Would really like a multi-level continue here...
			 */

			goto nextChild;
		    }
		}
		searchPtr->linesLeft -= nodePtr->numLines;
		if (nodePtr->nextPtr == NULL) {
		    Tcl_Panic("TkBTreeNextTag found incorrect tag summary info");
		}
	    }
	nextChild:
	    continue;
	}

	/*
	 * Now we're down to a level-0 node that contains a line that contains
	 * a relevant tag transition. Set up line information and go back to
	 * the beginning of the loop to search through lines.
	 */

	searchPtr->curIndex.linePtr = nodePtr->children.linePtr;
	searchPtr->curIndex.byteIndex = 0;
	segPtr = searchPtr->curIndex.linePtr->segPtr;
	if (searchPtr->linesLeft <= 0) {
	    goto searchOver;
	}
	continue;
    }

  searchOver:
    searchPtr->linesLeft = 0;
    searchPtr->segPtr = NULL;
    return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreePrevTag --
 *
 *	Once a tag search has begun, successive calls to this function return
 *	successive tag toggles in the reverse direction. Note: it is NOT SAFE
 *	to call this function if characters have been inserted into or deleted
 *	from the B-tree since the call to TkBTreeStartSearch.
 *
 * Results:
 *	The return value is 1 if another toggle was found that met the
 *	criteria specified in the call to TkBTreeStartSearch; in this case
 *	searchPtr->curIndex gives the toggle's position and
 *	searchPtr->curTagPtr points to its segment. 0 is returned if no more
 *	matching tag transitions were found; in this case searchPtr->curIndex
 *	is the same as searchPtr->stopIndex.
 *
 * Side effects:
 *	Information in *searchPtr is modified to update the state of the
 *	search and indicate where the next tag toggle is located.
 *
 *----------------------------------------------------------------------
 */

int
TkBTreePrevTag(
    register TkTextSearch *searchPtr)
				/* Information about search in progress; must
				 * have been set up by call to
				 * TkBTreeStartSearch. */
{
    register TkTextSegment *segPtr, *prevPtr;
    register TkTextLine *linePtr, *prevLinePtr;
    register Node *nodePtr, *node2Ptr, *prevNodePtr;
    register Summary *summaryPtr;
    int byteIndex, linesSkipped;
    int pastLast;		/* Saw last marker during scan. */

    if (searchPtr->linesLeft <= 0) {
	goto searchOver;
    }

    /*
     * The outermost loop iterates over lines that may potentially contain a
     * relevant tag transition, starting from the current segment in the
     * current line. "nextPtr" is maintained as the last segment in a line
     * that we can look at.
     */

    while (1) {
	/*
	 * Check for the last toggle before the current segment on this line.
	 */

	byteIndex = 0;
	if (searchPtr->lastPtr == NULL) {
	    /*
	     * Search back to the very beginning, so pastLast is irrelevent.
	     */

	    pastLast = 1;
	} else {
	    pastLast = 0;
	}

	for (prevPtr = NULL, segPtr = searchPtr->curIndex.linePtr->segPtr ;
		segPtr != NULL && segPtr != searchPtr->nextPtr;
		segPtr = segPtr->nextPtr) {
	    if (((segPtr->typePtr == &tkTextToggleOnType)
		    || (segPtr->typePtr == &tkTextToggleOffType))
		    && (searchPtr->allTags
		    || (segPtr->body.toggle.tagPtr == searchPtr->tagPtr))) {
		prevPtr = segPtr;
		searchPtr->curIndex.byteIndex = byteIndex;
	    }
	    if (segPtr == searchPtr->lastPtr) {
		prevPtr = NULL;		/* Segments earlier than last don't
					 * count. */
		pastLast = 1;
	    }
	    byteIndex += segPtr->size;
	}
	if (prevPtr != NULL) {
	    if (searchPtr->linesLeft == 1 && !pastLast) {
		/*
		 * We found a segment that is before the stopping index. Note
		 * that it is OK if prevPtr == lastPtr.
		 */

		goto searchOver;
	    }
	    searchPtr->segPtr = prevPtr;
	    searchPtr->nextPtr = prevPtr;
	    searchPtr->tagPtr = prevPtr->body.toggle.tagPtr;
	    return 1;
	}

	searchPtr->linesLeft--;
	if (searchPtr->linesLeft <= 0) {
	    goto searchOver;
	}

	/*
	 * See if there are more lines associated with the current parent
	 * node. If so, go back to the top of the loop to search the previous
	 * one.
	 */

	nodePtr = searchPtr->curIndex.linePtr->parentPtr;
	for (prevLinePtr = NULL, linePtr = nodePtr->children.linePtr;
		linePtr != NULL && linePtr != searchPtr->curIndex.linePtr;
		prevLinePtr = linePtr, linePtr = linePtr->nextPtr) {
	    /* empty loop body */ ;
	}
	if (prevLinePtr != NULL) {
	    searchPtr->curIndex.linePtr = prevLinePtr;
	    searchPtr->nextPtr = NULL;
	    continue;
	}
	if (nodePtr == searchPtr->tagPtr->tagRootPtr) {
	    goto searchOver;
	}

	/*
	 * Search across and up through the B-tree's node hierarchy looking
	 * for the previous node that has a relevant tag transition somewhere
	 * in its subtree. The search and line counting is trickier with/out
	 * back pointers. We'll scan all the nodes under a parent up to the
	 * current node, searching all of them for tag state. The last one we
	 * find, if any, is recorded in prevNodePtr, and any nodes past
	 * prevNodePtr that don't have tag state increment linesSkipped.
	 */

	while (1) {
	    for (prevNodePtr = NULL, linesSkipped = 0,
		    node2Ptr = nodePtr->parentPtr->children.nodePtr ;
		    node2Ptr != nodePtr;  node2Ptr = node2Ptr->nextPtr) {
		for (summaryPtr = node2Ptr->summaryPtr; summaryPtr != NULL;
			summaryPtr = summaryPtr->nextPtr) {
		    if ((searchPtr->allTags) ||
			    (summaryPtr->tagPtr == searchPtr->tagPtr)) {
			prevNodePtr = node2Ptr;
			linesSkipped = 0;
			goto keepLooking;
		    }
		}
		linesSkipped += node2Ptr->numLines;

	    keepLooking:
		continue;
	    }
	    if (prevNodePtr != NULL) {
		nodePtr = prevNodePtr;
		searchPtr->linesLeft -= linesSkipped;
		goto gotNodeWithTag;
	    }
	    nodePtr = nodePtr->parentPtr;
	    if (nodePtr->parentPtr == NULL ||
		nodePtr == searchPtr->tagPtr->tagRootPtr) {
		goto searchOver;
	    }
	}

	/*
	 * At this point we've found a subtree that has a relevant tag
	 * transition. Now search down (and across) through that subtree to
	 * find the last level-0 node that has a relevant tag transition.
	 */

    gotNodeWithTag:
	while (nodePtr->level > 0) {
	    for (linesSkipped = 0, prevNodePtr = NULL,
		    nodePtr = nodePtr->children.nodePtr; nodePtr != NULL ;
		    nodePtr = nodePtr->nextPtr) {
		for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
			summaryPtr = summaryPtr->nextPtr) {
		    if ((searchPtr->allTags)
			    || (summaryPtr->tagPtr == searchPtr->tagPtr)) {
			prevNodePtr = nodePtr;
			linesSkipped = 0;
			goto keepLooking2;
		    }
		}
		linesSkipped += nodePtr->numLines;

	    keepLooking2:
		continue;
	    }
	    if (prevNodePtr == NULL) {
		Tcl_Panic("TkBTreePrevTag found incorrect tag summary info");
	    }
	    searchPtr->linesLeft -= linesSkipped;
	    nodePtr = prevNodePtr;
	}

	/*
	 * Now we're down to a level-0 node that contains a line that contains
	 * a relevant tag transition. Set up line information and go back to
	 * the beginning of the loop to search through lines. We start with
	 * the last line below the node.
	 */

	for (prevLinePtr = NULL, linePtr = nodePtr->children.linePtr;
		linePtr != NULL ;
		prevLinePtr = linePtr, linePtr = linePtr->nextPtr) {
	    /* empty loop body */ ;
	}
	searchPtr->curIndex.linePtr = prevLinePtr;
	searchPtr->curIndex.byteIndex = 0;
	if (searchPtr->linesLeft <= 0) {
	    goto searchOver;
	}
	continue;
    }

  searchOver:
    searchPtr->linesLeft = 0;
    searchPtr->segPtr = NULL;
    return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeCharTagged --
 *
 *	Determine whether a particular character has a particular tag.
 *
 * Results:
 *	The return value is 1 if the given tag is in effect at the character
 *	given by linePtr and ch, and 0 otherwise.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
TkBTreeCharTagged(
    const TkTextIndex *indexPtr,/* Indicates a character position at which to
				 * check for a tag. */
    TkTextTag *tagPtr)		/* Tag of interest. */
{
    register Node *nodePtr;
    register TkTextLine *siblingLinePtr;
    register TkTextSegment *segPtr;
    TkTextSegment *toggleSegPtr;
    int toggles, index;

    /*
     * Check for toggles for the tag in indexPtr's line but before indexPtr.
     * If there is one, its type indicates whether or not the character is
     * tagged.
     */

    toggleSegPtr = NULL;
    for (index = 0, segPtr = indexPtr->linePtr->segPtr;
	    (index + segPtr->size) <= indexPtr->byteIndex;
	    index += segPtr->size, segPtr = segPtr->nextPtr) {
	if (((segPtr->typePtr == &tkTextToggleOnType)
		|| (segPtr->typePtr == &tkTextToggleOffType))
		&& (segPtr->body.toggle.tagPtr == tagPtr)) {
	    toggleSegPtr = segPtr;
	}
    }
    if (toggleSegPtr != NULL) {
	return (toggleSegPtr->typePtr == &tkTextToggleOnType);
    }

    /*
     * No toggle in this line. Look for toggles for the tag in lines that are
     * predecessors of indexPtr->linePtr but under the same level-0 node.
     */

    for (siblingLinePtr = indexPtr->linePtr->parentPtr->children.linePtr;
	    siblingLinePtr != indexPtr->linePtr;
	    siblingLinePtr = siblingLinePtr->nextPtr) {
	for (segPtr = siblingLinePtr->segPtr; segPtr != NULL;
		segPtr = segPtr->nextPtr) {
	    if (((segPtr->typePtr == &tkTextToggleOnType)
		    || (segPtr->typePtr == &tkTextToggleOffType))
		    && (segPtr->body.toggle.tagPtr == tagPtr)) {
		toggleSegPtr = segPtr;
	    }
	}
    }
    if (toggleSegPtr != NULL) {
	return (toggleSegPtr->typePtr == &tkTextToggleOnType);
    }

    /*
     * No toggle in this node. Scan upwards through the ancestors of this
     * node, counting the number of toggles of the given tag in siblings that
     * precede that node.
     */

    toggles = 0;
    for (nodePtr = indexPtr->linePtr->parentPtr; nodePtr->parentPtr != NULL;
	    nodePtr = nodePtr->parentPtr) {
	register Node *siblingPtr;
	register Summary *summaryPtr;

	for (siblingPtr = nodePtr->parentPtr->children.nodePtr;
		siblingPtr != nodePtr; siblingPtr = siblingPtr->nextPtr) {
	    for (summaryPtr = siblingPtr->summaryPtr; summaryPtr != NULL;
		    summaryPtr = summaryPtr->nextPtr) {
		if (summaryPtr->tagPtr == tagPtr) {
		    toggles += summaryPtr->toggleCount;
		}
	    }
	}
	if (nodePtr == tagPtr->tagRootPtr) {
	    break;
	}
    }

    /*
     * An odd number of toggles means that the tag is present at the given
     * point.
     */

    return toggles & 1;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeGetTags --
 *
 *	Return information about all of the tags that are associated with a
 *	particular character in a B-tree of text.
 *
 * Results:
 *	The return value is a malloc-ed array containing pointers to
 *	information for each of the tags that is associated with the character
 *	at the position given by linePtr and ch. The word at *numTagsPtr is
 *	filled in with the number of pointers in the array. It is up to the
 *	caller to free the array by passing it to free. If there are no tags
 *	at the given character then a NULL pointer is returned and *numTagsPtr
 *	will be set to 0.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

	/* ARGSUSED */
TkTextTag **
TkBTreeGetTags(
    const TkTextIndex *indexPtr,/* Indicates a particular position in the
				 * B-tree. */
    const TkText *textPtr,	/* If non-NULL, then only return tags for this
				 * text widget (when there are peer
				 * widgets). */
    int *numTagsPtr)		/* Store number of tags found at this
				 * location. */
{
    register Node *nodePtr;
    register TkTextLine *siblingLinePtr;
    register TkTextSegment *segPtr;
    TkTextLine *linePtr;
    int src, dst, index;
    TagInfo tagInfo;
#define NUM_TAG_INFOS 10

    tagInfo.numTags = 0;
    tagInfo.arraySize = NUM_TAG_INFOS;
    tagInfo.tagPtrs = (TkTextTag **)
	    ckalloc((unsigned) NUM_TAG_INFOS * sizeof(TkTextTag *));
    tagInfo.counts = (int *)
	    ckalloc((unsigned) NUM_TAG_INFOS * sizeof(int));

    /*
     * Record tag toggles within the line of indexPtr but preceding indexPtr.
     */

    linePtr = indexPtr->linePtr;
    index = 0;
    segPtr = linePtr->segPtr;
    while ((index + segPtr->size) <= indexPtr->byteIndex) {
	if ((segPtr->typePtr == &tkTextToggleOnType)
		|| (segPtr->typePtr == &tkTextToggleOffType)) {
	    IncCount(segPtr->body.toggle.tagPtr, 1, &tagInfo);
	}
	index += segPtr->size;
	segPtr = segPtr->nextPtr;

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

	    linePtr = TkBTreeNextLine(NULL, linePtr);
	    segPtr = linePtr->segPtr;
	}
    }

    /*
     * Record toggles for tags in lines that are predecessors of
     * indexPtr->linePtr but under the same level-0 node.
     */

    for (siblingLinePtr = indexPtr->linePtr->parentPtr->children.linePtr;
	    siblingLinePtr != indexPtr->linePtr;
	    siblingLinePtr = siblingLinePtr->nextPtr) {
	for (segPtr = siblingLinePtr->segPtr; segPtr != NULL;
		segPtr = segPtr->nextPtr) {
	    if ((segPtr->typePtr == &tkTextToggleOnType)
		    || (segPtr->typePtr == &tkTextToggleOffType)) {
		IncCount(segPtr->body.toggle.tagPtr, 1, &tagInfo);
	    }
	}
    }

    /*
     * For each node in the ancestry of this line, record tag toggles for all
     * siblings that precede that node.
     */

    for (nodePtr = indexPtr->linePtr->parentPtr; nodePtr->parentPtr != NULL;
	    nodePtr = nodePtr->parentPtr) {
	register Node *siblingPtr;
	register Summary *summaryPtr;

	for (siblingPtr = nodePtr->parentPtr->children.nodePtr;
		siblingPtr != nodePtr; siblingPtr = siblingPtr->nextPtr) {
	    for (summaryPtr = siblingPtr->summaryPtr; summaryPtr != NULL;
		    summaryPtr = summaryPtr->nextPtr) {
		if (summaryPtr->toggleCount & 1) {
		    IncCount(summaryPtr->tagPtr, summaryPtr->toggleCount,
			    &tagInfo);
		}
	    }
	}
    }

    /*
     * Go through the tag information and squash out all of the tags that have
     * even toggle counts (these tags exist before the point of interest, but
     * not at the desired character itself). Also squash out all tags that
     * don't belong to the requested widget.
     */

    for (src = 0, dst = 0; src < tagInfo.numTags; src++) {
	if (tagInfo.counts[src] & 1) {
	    const TkText *tagTextPtr = tagInfo.tagPtrs[src]->textPtr;

	    if (tagTextPtr==NULL || textPtr==NULL || tagTextPtr==textPtr) {
		tagInfo.tagPtrs[dst] = tagInfo.tagPtrs[src];
		dst++;
	    }
	}
    }
    *numTagsPtr = dst;
    ckfree((char *) tagInfo.counts);
    if (dst == 0) {
	ckfree((char *) tagInfo.tagPtrs);
	return NULL;
    }
    return tagInfo.tagPtrs;
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextIsElided --
 *
 *	Special case to just return information about elided attribute.
 *	Specialized from TkBTreeGetTags(indexPtr, textPtr, numTagsPtr) and
 *	GetStyle(textPtr, indexPtr). Just need to keep track of invisibility
 *	settings for each priority, pick highest one active at end.
 *
 *	Note that this returns all elide information up to and including the
 *	given index (quite obviously). However, this does mean that if
 *	indexPtr is a line-start and one then iterates from the beginning of
 *	that line forwards, one will actually revisit the segPtrs of size zero
 *	(for tag toggling, for example) which have already been seen here.
 *
 *	For this reason we fill in the fields 'segPtr' and 'segOffset' of
 *	elideInfo, enabling our caller easily to calculate incremental changes
 *	from where we left off.
 *
 * Results:
 *	Returns whether this text should be elided or not.
 *
 *	Optionally returns more detailed information in elideInfo.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

	/* ARGSUSED */
int
TkTextIsElided(
    const TkText *textPtr,	/* Overall information about text widget. */
    const TkTextIndex *indexPtr,/* The character in the text for which display
				 * information is wanted. */
    TkTextElideInfo *elideInfo)	/* NULL or a pointer to a structure in which
				 * indexPtr's elide state will be stored and
				 * returned. */
{
    register Node *nodePtr;
    register TkTextLine *siblingLinePtr;
    register TkTextSegment *segPtr;
    register TkTextTag *tagPtr = NULL;
    register int i, index;
    register TkTextElideInfo *infoPtr;
    TkTextLine *linePtr;
    int elide;

    if (elideInfo == NULL) {
	infoPtr = (TkTextElideInfo *)
		ckalloc((unsigned) sizeof(TkTextElideInfo));
    } else {
	infoPtr = elideInfo;
    }

    infoPtr->elide = 0;		/* If nobody says otherwise, it's visible. */
    infoPtr->tagCnts = infoPtr->deftagCnts;
    infoPtr->tagPtrs = infoPtr->deftagPtrs;
    infoPtr->numTags = textPtr->sharedTextPtr->numTags;

    /*
     * Almost always avoid malloc, so stay out of system calls.
     */

    if (LOTSA_TAGS < infoPtr->numTags) {
	infoPtr->tagCnts = (int *)
		ckalloc((unsigned) sizeof(int) * infoPtr->numTags);
	infoPtr->tagPtrs = (TkTextTag **)
		ckalloc((unsigned) sizeof(TkTextTag *) * infoPtr->numTags);
    }

    for (i=0; i<infoPtr->numTags; i++) {
	infoPtr->tagCnts[i] = 0;
    }

    /*
     * Record tag toggles within the line of indexPtr but preceding indexPtr.
     */

    index = 0;
    linePtr = indexPtr->linePtr;
    segPtr = linePtr->segPtr;
    while ((index + segPtr->size) <= indexPtr->byteIndex) {
	if ((segPtr->typePtr == &tkTextToggleOnType)
		|| (segPtr->typePtr == &tkTextToggleOffType)) {
	    tagPtr = segPtr->body.toggle.tagPtr;
	    if (tagPtr->elideString != NULL) {
		infoPtr->tagPtrs[tagPtr->priority] = tagPtr;
		infoPtr->tagCnts[tagPtr->priority]++;
	    }
	}

	index += segPtr->size;
	segPtr = segPtr->nextPtr;
	if (segPtr == NULL) {
	    /*
	     * Two logical lines merged into one display line through eliding
	     * of a newline.
	     */

	    linePtr = TkBTreeNextLine(NULL, linePtr);
	    segPtr = linePtr->segPtr;
	}
    }

    /*
     * Store the first segPtr we haven't examined completely so that our
     * caller knows where to start.
     */

    infoPtr->segPtr = segPtr;
    infoPtr->segOffset = index;

    /*
     * Record toggles for tags in lines that are predecessors of
     * indexPtr->linePtr but under the same level-0 node.
     */

    for (siblingLinePtr = indexPtr->linePtr->parentPtr->children.linePtr;
	    siblingLinePtr != indexPtr->linePtr;
	    siblingLinePtr = siblingLinePtr->nextPtr) {
	for (segPtr = siblingLinePtr->segPtr; segPtr != NULL;
		segPtr = segPtr->nextPtr) {
	    if ((segPtr->typePtr == &tkTextToggleOnType)
		    || (segPtr->typePtr == &tkTextToggleOffType)) {
		tagPtr = segPtr->body.toggle.tagPtr;
		if (tagPtr->elideString != NULL) {
		    infoPtr->tagPtrs[tagPtr->priority] = tagPtr;
		    infoPtr->tagCnts[tagPtr->priority]++;
		}
	    }
	}
    }

    /*
     * For each node in the ancestry of this line, record tag toggles for all
     * siblings that precede that node.
     */

    for (nodePtr = indexPtr->linePtr->parentPtr; nodePtr->parentPtr != NULL;
	    nodePtr = nodePtr->parentPtr) {
	register Node *siblingPtr;
	register Summary *summaryPtr;

	for (siblingPtr = nodePtr->parentPtr->children.nodePtr;
		siblingPtr != nodePtr; siblingPtr = siblingPtr->nextPtr) {
	    for (summaryPtr = siblingPtr->summaryPtr; summaryPtr != NULL;
		    summaryPtr = summaryPtr->nextPtr) {
		if (summaryPtr->toggleCount & 1) {
		    tagPtr = summaryPtr->tagPtr;
		    if (tagPtr->elideString != NULL) {
			infoPtr->tagPtrs[tagPtr->priority] = tagPtr;
			infoPtr->tagCnts[tagPtr->priority] +=
				summaryPtr->toggleCount;
		    }
		}
	    }
	}
    }

    /*
     * Now traverse from highest priority to lowest, take elided value from
     * first odd count (= on).
     */

    infoPtr->elidePriority = -1;
    for (i = infoPtr->numTags-1; i >=0; i--) {
	if (infoPtr->tagCnts[i] & 1) {
	    /*
	     * Who would make the selection elided?
	     */

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

	    /*
	     * Note: i == infoPtr->tagPtrs[i]->priority
	     */

	    infoPtr->elidePriority = i;
	    break;
	}
    }

    elide = infoPtr->elide;

    if (elideInfo == NULL) {
	if (LOTSA_TAGS < infoPtr->numTags) {
	    ckfree((char *) infoPtr->tagCnts);
	    ckfree((char *) infoPtr->tagPtrs);
	}

	ckfree((char *) infoPtr);
    }

    return elide;
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextFreeElideInfo --
 *
 *	This is a utility function used to free up any memory allocated by the
 *	TkTextIsElided function above.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Memory may be freed.
 *
 *----------------------------------------------------------------------
 */

void
TkTextFreeElideInfo(
    TkTextElideInfo *elideInfo)	/* Free any allocated memory in this
				 * structure. */
{
    if (LOTSA_TAGS < elideInfo->numTags) {
	ckfree((char *) elideInfo->tagCnts);
	ckfree((char *) elideInfo->tagPtrs);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * IncCount --
 *
 *	This is a utility function used by TkBTreeGetTags. It increments the
 *	count for a particular tag, adding a new entry for that tag if there
 *	wasn't one previously.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The information at *tagInfoPtr may be modified, and the arrays may be
 *	reallocated to make them larger.
 *
 *----------------------------------------------------------------------
 */

static void
IncCount(
    TkTextTag *tagPtr,		/* Handle for tag. */
    int inc,			/* Amount by which to increment tag count. */
    TagInfo *tagInfoPtr)	/* Holds cumulative information about tags;
				 * increment count here. */
{
    register TkTextTag **tagPtrPtr;
    int count;

    for (tagPtrPtr = tagInfoPtr->tagPtrs, count = tagInfoPtr->numTags;
	    count > 0; tagPtrPtr++, count--) {
	if (*tagPtrPtr == tagPtr) {
	    tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
	    return;
	}
    }

    /*
     * There isn't currently an entry for this tag, so we have to make a new
     * one. If the arrays are full, then enlarge the arrays first.
     */

    if (tagInfoPtr->numTags == tagInfoPtr->arraySize) {
	TkTextTag **newTags;
	int *newCounts, newSize;

	newSize = 2 * tagInfoPtr->arraySize;
	newTags = (TkTextTag **)
		ckalloc((unsigned) newSize * sizeof(TkTextTag *));
	memcpy(newTags, tagInfoPtr->tagPtrs,
		tagInfoPtr->arraySize * sizeof(TkTextTag *));
	ckfree((char *) tagInfoPtr->tagPtrs);
	tagInfoPtr->tagPtrs = newTags;
	newCounts = (int *) ckalloc((unsigned) newSize * sizeof(int));
	memcpy(newCounts, tagInfoPtr->counts,
		tagInfoPtr->arraySize * sizeof(int));
	ckfree((char *) tagInfoPtr->counts);
	tagInfoPtr->counts = newCounts;
	tagInfoPtr->arraySize = newSize;
    }

    tagInfoPtr->tagPtrs[tagInfoPtr->numTags] = tagPtr;
    tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
    tagInfoPtr->numTags++;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeCheck --
 *
 *	This function runs a set of consistency checks over a B-tree and
 *	panics if any inconsistencies are found.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	If a structural defect is found, the function panics with an error
 *	message.
 *
 *----------------------------------------------------------------------
 */

void
TkBTreeCheck(
    TkTextBTree tree)		/* Tree to check. */
{
    BTree *treePtr = (BTree *) tree;
    register Summary *summaryPtr;
    register Node *nodePtr;
    register TkTextLine *linePtr;
    register TkTextSegment *segPtr;
    register TkTextTag *tagPtr;
    Tcl_HashEntry *entryPtr;
    Tcl_HashSearch search;
    int count;

    /*
     * Make sure that the tag toggle counts and the tag root pointers are OK.
     */

    for (entryPtr=Tcl_FirstHashEntry(&treePtr->sharedTextPtr->tagTable,&search);
	    entryPtr != NULL ; entryPtr = Tcl_NextHashEntry(&search)) {
	tagPtr = Tcl_GetHashValue(entryPtr);
	nodePtr = tagPtr->tagRootPtr;
	if (nodePtr == NULL) {
	    if (tagPtr->toggleCount != 0) {
		Tcl_Panic("TkBTreeCheck found \"%s\" with toggles (%d) but no root",
			tagPtr->name, tagPtr->toggleCount);
	    }
	    continue;		/* No ranges for the tag. */
	} else if (tagPtr->toggleCount == 0) {
	    Tcl_Panic("TkBTreeCheck found root for \"%s\" with no toggles",
		    tagPtr->name);
	} else if (tagPtr->toggleCount & 1) {
	    Tcl_Panic("TkBTreeCheck found odd toggle count for \"%s\" (%d)",
		    tagPtr->name, tagPtr->toggleCount);
	}
	for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
		summaryPtr = summaryPtr->nextPtr) {
	    if (summaryPtr->tagPtr == tagPtr) {
		Tcl_Panic("TkBTreeCheck found root node with summary info");
	    }
	}
	count = 0;
	if (nodePtr->level > 0) {
	    for (nodePtr = nodePtr->children.nodePtr ; nodePtr != NULL ;
		    nodePtr = nodePtr->nextPtr) {
		for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
			summaryPtr = summaryPtr->nextPtr) {
		    if (summaryPtr->tagPtr == tagPtr) {
			count += summaryPtr->toggleCount;
		    }
		}
	    }
	} else {
	    for (linePtr = nodePtr->children.linePtr ; linePtr != NULL ;
		    linePtr = linePtr->nextPtr) {
		for (segPtr = linePtr->segPtr; segPtr != NULL;
			segPtr = segPtr->nextPtr) {
		    if ((segPtr->typePtr == &tkTextToggleOnType ||
			 segPtr->typePtr == &tkTextToggleOffType) &&
			 segPtr->body.toggle.tagPtr == tagPtr) {
			count++;
		    }
		}
	    }
	}
	if (count != tagPtr->toggleCount) {
	    Tcl_Panic("TkBTreeCheck toggleCount (%d) wrong for \"%s\" should be (%d)",
		    tagPtr->toggleCount, tagPtr->name, count);
	}
    }

    /*
     * Call a recursive function to do the main body of checks.
     */

    nodePtr = treePtr->rootPtr;
    CheckNodeConsistency(treePtr->rootPtr, treePtr->pixelReferences);

    /*
     * Make sure that there are at least two lines in the text and that the
     * last line has no characters except a newline.
     */

    if (nodePtr->numLines < 2) {
	Tcl_Panic("TkBTreeCheck: less than 2 lines in tree");
    }
    while (nodePtr->level > 0) {
	nodePtr = nodePtr->children.nodePtr;
	while (nodePtr->nextPtr != NULL) {
	    nodePtr = nodePtr->nextPtr;
	}
    }
    linePtr = nodePtr->children.linePtr;
    while (linePtr->nextPtr != NULL) {
	linePtr = linePtr->nextPtr;
    }
    segPtr = linePtr->segPtr;
    while ((segPtr->typePtr == &tkTextToggleOffType)
	    || (segPtr->typePtr == &tkTextRightMarkType)
	    || (segPtr->typePtr == &tkTextLeftMarkType)) {
	/*
	 * It's OK to toggle a tag off in the last line, but not to start a
	 * new range. It's also OK to have marks in the last line.
	 */

	segPtr = segPtr->nextPtr;
    }
    if (segPtr->typePtr != &tkTextCharType) {
	Tcl_Panic("TkBTreeCheck: last line has bogus segment type");
    }
    if (segPtr->nextPtr != NULL) {
	Tcl_Panic("TkBTreeCheck: last line has too many segments");
    }
    if (segPtr->size != 1) {
	Tcl_Panic("TkBTreeCheck: last line has wrong # characters: %d",
		segPtr->size);
    }
    if ((segPtr->body.chars[0] != '\n') || (segPtr->body.chars[1] != 0)) {
	Tcl_Panic("TkBTreeCheck: last line had bad value: %s",
		segPtr->body.chars);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * CheckNodeConsistency --
 *
 *	This function is called as part of consistency checking for B-trees:
 *	it checks several aspects of a node and also runs checks recursively
 *	on the node's children.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	If anything suspicious is found in the tree structure, the function
 *	panics.
 *
 *----------------------------------------------------------------------
 */

static void
CheckNodeConsistency(
    register Node *nodePtr,	/* Node whose subtree should be checked. */
    int references)		/* Number of referring widgets which have
				 * pixel counts. */
{
    register Node *childNodePtr;
    register Summary *summaryPtr, *summaryPtr2;
    register TkTextLine *linePtr;
    register TkTextSegment *segPtr;
    int numChildren, numLines, toggleCount, minChildren, i;
    int *numPixels;
    int pixels[PIXEL_CLIENTS];

    if (nodePtr->parentPtr != NULL) {
	minChildren = MIN_CHILDREN;
    } else if (nodePtr->level > 0) {
	minChildren = 2;
    } else {
	minChildren = 1;
    }
    if ((nodePtr->numChildren < minChildren)
	    || (nodePtr->numChildren > MAX_CHILDREN)) {
	Tcl_Panic("CheckNodeConsistency: bad child count (%d)",
		nodePtr->numChildren);
    }

    numChildren = 0;
    numLines = 0;
    if (references > PIXEL_CLIENTS) {
	numPixels = (int *) ckalloc(sizeof(int) * references);
    } else {
	numPixels = pixels;
    }
    for (i = 0; i<references; i++) {
	numPixels[i] = 0;
    }

    if (nodePtr->level == 0) {
	for (linePtr = nodePtr->children.linePtr; linePtr != NULL;
		linePtr = linePtr->nextPtr) {
	    if (linePtr->parentPtr != nodePtr) {
		Tcl_Panic("CheckNodeConsistency: line doesn't point to parent");
	    }
	    if (linePtr->segPtr == NULL) {
		Tcl_Panic("CheckNodeConsistency: line has no segments");
	    }
	    for (segPtr = linePtr->segPtr; segPtr != NULL;
		    segPtr = segPtr->nextPtr) {
		if (segPtr->typePtr->checkProc != NULL) {
		    segPtr->typePtr->checkProc(segPtr, linePtr);
		}
		if ((segPtr->size == 0) && (!segPtr->typePtr->leftGravity)
			&& (segPtr->nextPtr != NULL)
			&& (segPtr->nextPtr->size == 0)
			&& (segPtr->nextPtr->typePtr->leftGravity)) {
		    Tcl_Panic("CheckNodeConsistency: wrong segment order for gravity");
		}
		if ((segPtr->nextPtr == NULL)
			&& (segPtr->typePtr != &tkTextCharType)) {
		    Tcl_Panic("CheckNodeConsistency: line ended with wrong type");
		}
	    }
	    numChildren++;
	    numLines++;
	    for (i = 0; i<references; i++) {
		numPixels[i] += linePtr->pixels[2 * i];
	    }
	}
    } else {
	for (childNodePtr = nodePtr->children.nodePtr; childNodePtr != NULL;
		childNodePtr = childNodePtr->nextPtr) {
	    if (childNodePtr->parentPtr != nodePtr) {
		Tcl_Panic("CheckNodeConsistency: node doesn't point to parent");
	    }
	    if (childNodePtr->level != (nodePtr->level-1)) {
		Tcl_Panic("CheckNodeConsistency: level mismatch (%d %d)",
			nodePtr->level, childNodePtr->level);
	    }
	    CheckNodeConsistency(childNodePtr, references);
	    for (summaryPtr = childNodePtr->summaryPtr; summaryPtr != NULL;
			summaryPtr = summaryPtr->nextPtr) {
		for (summaryPtr2 = nodePtr->summaryPtr; ;
			summaryPtr2 = summaryPtr2->nextPtr) {
		    if (summaryPtr2 == NULL) {
			if (summaryPtr->tagPtr->tagRootPtr == nodePtr) {
			    break;
			}
			Tcl_Panic("CheckNodeConsistency: node tag \"%s\" not %s",
				summaryPtr->tagPtr->name,
				"present in parent summaries");
		    }
		    if (summaryPtr->tagPtr == summaryPtr2->tagPtr) {
			break;
		    }
		}
	    }
	    numChildren++;
	    numLines += childNodePtr->numLines;
	    for (i = 0; i<references; i++) {
		numPixels[i] += childNodePtr->numPixels[i];
	    }
	}
    }
    if (numChildren != nodePtr->numChildren) {
	Tcl_Panic("CheckNodeConsistency: mismatch in numChildren (%d %d)",
		numChildren, nodePtr->numChildren);
    }
    if (numLines != nodePtr->numLines) {
	Tcl_Panic("CheckNodeConsistency: mismatch in numLines (%d %d)",
		numLines, nodePtr->numLines);
    }
    for (i = 0; i<references; i++) {
	if (numPixels[i] != nodePtr->numPixels[i]) {
	    Tcl_Panic("CheckNodeConsistency: mismatch in numPixels (%d %d) for widget (%d)",
		    numPixels[i], nodePtr->numPixels[i], i);
	}
    }
    if (references > PIXEL_CLIENTS) {
	ckfree((char *) numPixels);
    }

    for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
	    summaryPtr = summaryPtr->nextPtr) {
	if (summaryPtr->tagPtr->toggleCount == summaryPtr->toggleCount) {
	    Tcl_Panic("CheckNodeConsistency: found unpruned root for \"%s\"",
		    summaryPtr->tagPtr->name);
	}
	toggleCount = 0;
	if (nodePtr->level == 0) {
	    for (linePtr = nodePtr->children.linePtr; linePtr != NULL;
		    linePtr = linePtr->nextPtr) {
		for (segPtr = linePtr->segPtr; segPtr != NULL;
			segPtr = segPtr->nextPtr) {
		    if ((segPtr->typePtr != &tkTextToggleOnType)
			    && (segPtr->typePtr != &tkTextToggleOffType)) {
			continue;
		    }
		    if (segPtr->body.toggle.tagPtr == summaryPtr->tagPtr) {
			toggleCount++;
		    }
		}
	    }
	} else {
	    for (childNodePtr = nodePtr->children.nodePtr;
		    childNodePtr != NULL;
		    childNodePtr = childNodePtr->nextPtr) {
		for (summaryPtr2 = childNodePtr->summaryPtr;
			summaryPtr2 != NULL;
			summaryPtr2 = summaryPtr2->nextPtr) {
		    if (summaryPtr2->tagPtr == summaryPtr->tagPtr) {
			toggleCount += summaryPtr2->toggleCount;
		    }
		}
	    }
	}
	if (toggleCount != summaryPtr->toggleCount) {
	    Tcl_Panic("CheckNodeConsistency: mismatch in toggleCount (%d %d)",
		    toggleCount, summaryPtr->toggleCount);
	}
	for (summaryPtr2 = summaryPtr->nextPtr; summaryPtr2 != NULL;
		summaryPtr2 = summaryPtr2->nextPtr) {
	    if (summaryPtr2->tagPtr == summaryPtr->tagPtr) {
		Tcl_Panic("CheckNodeConsistency: duplicated node tag: %s",
			summaryPtr->tagPtr->name);
	    }
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Rebalance --
 *
 *	This function is called when a node of a B-tree appears to be out of
 *	balance (too many children, or too few). It rebalances that node and
 *	all of its ancestors in the tree.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The internal structure of treePtr may change.
 *
 *----------------------------------------------------------------------
 */

static void
Rebalance(
    BTree *treePtr,		/* Tree that is being rebalanced. */
    register Node *nodePtr)	/* Node that may be out of balance. */
{
    /*
     * Loop over the entire ancestral chain of the node, working up through
     * the tree one node at a time until the root node has been processed.
     */

    for ( ; nodePtr != NULL; nodePtr = nodePtr->parentPtr) {
	register Node *newPtr, *childPtr;
	register TkTextLine *linePtr;
	int i;

	/*
	 * Check to see if the node has too many children. If it does, then
	 * split off all but the first MIN_CHILDREN into a separate node
	 * following the original one. Then repeat until the node has a decent
	 * size.
	 */

	if (nodePtr->numChildren > MAX_CHILDREN) {
	    while (1) {
		/*
		 * If the node being split is the root node, then make a new
		 * root node above it first.
		 */

		if (nodePtr->parentPtr == NULL) {
		    newPtr = (Node *) ckalloc(sizeof(Node));
		    newPtr->parentPtr = NULL;
		    newPtr->nextPtr = NULL;
		    newPtr->summaryPtr = NULL;
		    newPtr->level = nodePtr->level + 1;
		    newPtr->children.nodePtr = nodePtr;
		    newPtr->numChildren = 1;
		    newPtr->numLines = nodePtr->numLines;
		    newPtr->numPixels = (int *)
			    ckalloc(sizeof(int) * treePtr->pixelReferences);
		    for (i=0; i<treePtr->pixelReferences; i++) {
			newPtr->numPixels[i] = nodePtr->numPixels[i];
		    }
		    RecomputeNodeCounts(treePtr, newPtr);
		    treePtr->rootPtr = newPtr;
		}
		newPtr = (Node *) ckalloc(sizeof(Node));
		newPtr->numPixels = (int *)
			ckalloc(sizeof(int) * treePtr->pixelReferences);
		for (i=0; i<treePtr->pixelReferences; i++) {
		    newPtr->numPixels[i] = 0;
		}
		newPtr->parentPtr = nodePtr->parentPtr;
		newPtr->nextPtr = nodePtr->nextPtr;
		nodePtr->nextPtr = newPtr;
		newPtr->summaryPtr = NULL;
		newPtr->level = nodePtr->level;
		newPtr->numChildren = nodePtr->numChildren - MIN_CHILDREN;
		if (nodePtr->level == 0) {
		    for (i = MIN_CHILDREN-1,
			    linePtr = nodePtr->children.linePtr;
			    i > 0; i--, linePtr = linePtr->nextPtr) {
			/* Empty loop body. */
		    }
		    newPtr->children.linePtr = linePtr->nextPtr;
		    linePtr->nextPtr = NULL;
		} else {
		    for (i = MIN_CHILDREN-1,
			    childPtr = nodePtr->children.nodePtr;
			    i > 0; i--, childPtr = childPtr->nextPtr) {
			/* Empty loop body. */
		    }
		    newPtr->children.nodePtr = childPtr->nextPtr;
		    childPtr->nextPtr = NULL;
		}
		RecomputeNodeCounts(treePtr, nodePtr);
		nodePtr->parentPtr->numChildren++;
		nodePtr = newPtr;
		if (nodePtr->numChildren <= MAX_CHILDREN) {
		    RecomputeNodeCounts(treePtr, nodePtr);
		    break;
		}
	    }
	}

	while (nodePtr->numChildren < MIN_CHILDREN) {
	    register Node *otherPtr;
	    Node *halfwayNodePtr = NULL;       /* Initialization needed only */
	    TkTextLine *halfwayLinePtr = NULL; /* to prevent cc warnings. */
	    int totalChildren, firstChildren, i;

	    /*
	     * Too few children for this node. If this is the root then, it's
	     * OK for it to have less than MIN_CHILDREN children as long as
	     * it's got at least two. If it has only one (and isn't at level
	     * 0), then chop the root node out of the tree and use its child
	     * as the new root.
	     */

	    if (nodePtr->parentPtr == NULL) {
		if ((nodePtr->numChildren == 1) && (nodePtr->level > 0)) {
		    treePtr->rootPtr = nodePtr->children.nodePtr;
		    treePtr->rootPtr->parentPtr = NULL;
		    DeleteSummaries(nodePtr->summaryPtr);
		    ckfree((char *) nodePtr);
		}
		return;
	    }

	    /*
	     * Not the root. Make sure that there are siblings to balance
	     * with.
	     */

	    if (nodePtr->parentPtr->numChildren < 2) {
		Rebalance(treePtr, nodePtr->parentPtr);
		continue;
	    }

	    /*
	     * Find a sibling neighbor to borrow from, and arrange for nodePtr
	     * to be the earlier of the pair.
	     */

	    if (nodePtr->nextPtr == NULL) {
		for (otherPtr = nodePtr->parentPtr->children.nodePtr;
			otherPtr->nextPtr != nodePtr;
			otherPtr = otherPtr->nextPtr) {
		    /* Empty loop body. */
		}
		nodePtr = otherPtr;
	    }
	    otherPtr = nodePtr->nextPtr;

	    /*
	     * We're going to either merge the two siblings together into one
	     * node or redivide the children among them to balance their
	     * loads. As preparation, join their two child lists into a single
	     * list and remember the half-way point in the list.
	     */

	    totalChildren = nodePtr->numChildren + otherPtr->numChildren;
	    firstChildren = totalChildren/2;
	    if (nodePtr->children.nodePtr == NULL) {
		nodePtr->children = otherPtr->children;
		otherPtr->children.nodePtr = NULL;
		otherPtr->children.linePtr = NULL;
	    }
	    if (nodePtr->level == 0) {
		register TkTextLine *linePtr;

		for (linePtr = nodePtr->children.linePtr, i = 1;
			linePtr->nextPtr != NULL;
			linePtr = linePtr->nextPtr, i++) {
		    if (i == firstChildren) {
			halfwayLinePtr = linePtr;
		    }
		}
		linePtr->nextPtr = otherPtr->children.linePtr;
		while (i <= firstChildren) {
		    halfwayLinePtr = linePtr;
		    linePtr = linePtr->nextPtr;
		    i++;
		}
	    } else {
		register Node *childPtr;

		for (childPtr = nodePtr->children.nodePtr, i = 1;
			childPtr->nextPtr != NULL;
			childPtr = childPtr->nextPtr, i++) {
		    if (i <= firstChildren) {
			if (i == firstChildren) {
			    halfwayNodePtr = childPtr;
			}
		    }
		}
		childPtr->nextPtr = otherPtr->children.nodePtr;
		while (i <= firstChildren) {
		    halfwayNodePtr = childPtr;
		    childPtr = childPtr->nextPtr;
		    i++;
		}
	    }

	    /*
	     * If the two siblings can simply be merged together, do it.
	     */

	    if (totalChildren <= MAX_CHILDREN) {
		RecomputeNodeCounts(treePtr, nodePtr);
		nodePtr->nextPtr = otherPtr->nextPtr;
		nodePtr->parentPtr->numChildren--;
		DeleteSummaries(otherPtr->summaryPtr);
		ckfree((char *) otherPtr);
		continue;
	    }

	    /*
	     * The siblings can't be merged, so just divide their children
	     * evenly between them.
	     */

	    if (nodePtr->level == 0) {
		CLANG_ASSERT(halfwayLinePtr);
		otherPtr->children.linePtr = halfwayLinePtr->nextPtr;
		halfwayLinePtr->nextPtr = NULL;
	    } else {
		CLANG_ASSERT(halfwayNodePtr);
		otherPtr->children.nodePtr = halfwayNodePtr->nextPtr;
		halfwayNodePtr->nextPtr = NULL;
	    }
	    RecomputeNodeCounts(treePtr, nodePtr);
	    RecomputeNodeCounts(treePtr, otherPtr);
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * RecomputeNodeCounts --
 *
 *	This function is called to recompute all the counts in a node (tags,
 *	child information, etc.) by scanning the information in its
 *	descendants. This function is called during rebalancing when a node's
 *	child structure has changed.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The tag counts for nodePtr are modified to reflect its current child
 *	structure, as are its numChildren and numLines fields. Also, all of
 *	the childrens' parentPtr fields are made to point to nodePtr.
 *
 *----------------------------------------------------------------------
 */

static void
RecomputeNodeCounts(
    register BTree *treePtr,	/* The whole B-tree. */
    register Node *nodePtr)	/* Node whose tag summary information must be
				 * recomputed. */
{
    register Summary *summaryPtr, *summaryPtr2;
    register Node *childPtr;
    register TkTextLine *linePtr;
    register TkTextSegment *segPtr;
    TkTextTag *tagPtr;
    int ref;

    /*
     * Zero out all the existing counts for the node, but don't delete the
     * existing Summary records (most of them will probably be reused).
     */

    for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
	    summaryPtr = summaryPtr->nextPtr) {
	summaryPtr->toggleCount = 0;
    }
    nodePtr->numChildren = 0;
    nodePtr->numLines = 0;
    for (ref = 0; ref<treePtr->pixelReferences; ref++) {
	nodePtr->numPixels[ref] = 0;
    }

    /*
     * Scan through the children, adding the childrens' tag counts into the
     * node's tag counts and adding new Summary structures if necessary.
     */

    if (nodePtr->level == 0) {
	for (linePtr = nodePtr->children.linePtr; linePtr != NULL;
		linePtr = linePtr->nextPtr) {
	    nodePtr->numChildren++;
	    nodePtr->numLines++;
	    for (ref = 0; ref<treePtr->pixelReferences; ref++) {
		nodePtr->numPixels[ref] += linePtr->pixels[2 * ref];
	    }
	    linePtr->parentPtr = nodePtr;
	    for (segPtr = linePtr->segPtr; segPtr != NULL;
		    segPtr = segPtr->nextPtr) {
		if (((segPtr->typePtr != &tkTextToggleOnType)
			&& (segPtr->typePtr != &tkTextToggleOffType))
			|| !(segPtr->body.toggle.inNodeCounts)) {
		    continue;
		}
		tagPtr = segPtr->body.toggle.tagPtr;
		for (summaryPtr = nodePtr->summaryPtr; ;
			summaryPtr = summaryPtr->nextPtr) {
		    if (summaryPtr == NULL) {
			summaryPtr = (Summary *) ckalloc(sizeof(Summary));
			summaryPtr->tagPtr = tagPtr;
			summaryPtr->toggleCount = 1;
			summaryPtr->nextPtr = nodePtr->summaryPtr;
			nodePtr->summaryPtr = summaryPtr;
			break;
		    }
		    if (summaryPtr->tagPtr == tagPtr) {
			summaryPtr->toggleCount++;
			break;
		    }
		}
	    }
	}
    } else {
	for (childPtr = nodePtr->children.nodePtr; childPtr != NULL;
		childPtr = childPtr->nextPtr) {
	    nodePtr->numChildren++;
	    nodePtr->numLines += childPtr->numLines;
	    for (ref = 0; ref<treePtr->pixelReferences; ref++) {
		nodePtr->numPixels[ref] += childPtr->numPixels[ref];
	    }
	    childPtr->parentPtr = nodePtr;
	    for (summaryPtr2 = childPtr->summaryPtr; summaryPtr2 != NULL;
		    summaryPtr2 = summaryPtr2->nextPtr) {
		for (summaryPtr = nodePtr->summaryPtr; ;
			summaryPtr = summaryPtr->nextPtr) {
		    if (summaryPtr == NULL) {
			summaryPtr = (Summary *) ckalloc(sizeof(Summary));
			summaryPtr->tagPtr = summaryPtr2->tagPtr;
			summaryPtr->toggleCount = summaryPtr2->toggleCount;
			summaryPtr->nextPtr = nodePtr->summaryPtr;
			nodePtr->summaryPtr = summaryPtr;
			break;
		    }
		    if (summaryPtr->tagPtr == summaryPtr2->tagPtr) {
			summaryPtr->toggleCount += summaryPtr2->toggleCount;
			break;
		    }
		}
	    }
	}
    }

    /*
     * Scan through the node's tag records again and delete any Summary
     * records that still have a zero count, or that have all the toggles.
     * The node with the children that account for all the tags toggles have
     * no summary information, and they become the tagRootPtr for the tag.
     */

    summaryPtr2 = NULL;
    for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL; ) {
	if (summaryPtr->toggleCount > 0 &&
		summaryPtr->toggleCount < summaryPtr->tagPtr->toggleCount) {
	    if (nodePtr->level == summaryPtr->tagPtr->tagRootPtr->level) {
		/*
		 * The tag's root node split and some toggles left. The tag
		 * root must move up a level.
		 */

		summaryPtr->tagPtr->tagRootPtr = nodePtr->parentPtr;
	    }
	    summaryPtr2 = summaryPtr;
	    summaryPtr = summaryPtr->nextPtr;
	    continue;
	}
	if (summaryPtr->toggleCount == summaryPtr->tagPtr->toggleCount) {
	    /*
	     * A node merge has collected all the toggles under one node. Push
	     * the root down to this level.
	     */

	    summaryPtr->tagPtr->tagRootPtr = nodePtr;
	}
	if (summaryPtr2 != NULL) {
	    summaryPtr2->nextPtr = summaryPtr->nextPtr;
	    ckfree((char *) summaryPtr);
	    summaryPtr = summaryPtr2->nextPtr;
	} else {
	    nodePtr->summaryPtr = summaryPtr->nextPtr;
	    ckfree((char *) summaryPtr);
	    summaryPtr = nodePtr->summaryPtr;
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeNumLines --
 *
 *	This function returns a count of the number of logical lines of text
 *	present in a given B-tree.
 *
 * Results:
 *	The return value is a count of the number of usable lines in tree
 *	(i.e. it doesn't include the dummy line that is just used to mark the
 *	end of the tree).
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
TkBTreeNumLines(
    TkTextBTree tree,		/* Information about tree. */
    const TkText *textPtr)	/* Relative to this client of the B-tree. */
{
    BTree *treePtr = (BTree *) tree;
    int count;

    if (textPtr != NULL && textPtr->end != NULL) {
	count = TkBTreeLinesTo(NULL, textPtr->end);
    } else {
	count = treePtr->rootPtr->numLines - 1;
    }
    if (textPtr != NULL && textPtr->start != NULL) {
	count -= TkBTreeLinesTo(NULL, textPtr->start);
    }

    return count;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeNumPixels --
 *
 *	This function returns a count of the number of pixels of text present
 *	in a given widget's B-tree representation.
 *
 * Results:
 *	The return value is a count of the number of usable pixels in tree
 *	(since the dummy line used to mark the end of the B-tree is maintained
 *	with zero height, as are any lines that are before or after the
 *	'-start -end' range of the text widget in question, the number stored
 *	at the root is the number we want).
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
TkBTreeNumPixels(
    TkTextBTree tree,		/* The B-tree. */
    const TkText *textPtr)	/* Relative to this client of the B-tree. */
{
    BTree *treePtr = (BTree *) tree;
    return treePtr->rootPtr->numPixels[textPtr->pixelReference];
}

/*
 *--------------------------------------------------------------
 *
 * CharSplitProc --
 *
 *	This function implements splitting for character segments.
 *
 * Results:
 *	The return value is a pointer to a chain of two segments that have the
 *	same characters as segPtr except split among the two segments.
 *
 * Side effects:
 *	Storage for segPtr is freed.
 *
 *--------------------------------------------------------------
 */

static TkTextSegment *
CharSplitProc(
    TkTextSegment *segPtr,	/* Pointer to segment to split. */
    int index)			/* Position within segment at which to
				 * split. */
{
    TkTextSegment *newPtr1, *newPtr2;

    newPtr1 = (TkTextSegment *) ckalloc(CSEG_SIZE(index));
    newPtr2 = (TkTextSegment *) ckalloc(
	    CSEG_SIZE(segPtr->size - index));
    newPtr1->typePtr = &tkTextCharType;
    newPtr1->nextPtr = newPtr2;
    newPtr1->size = index;
    strncpy(newPtr1->body.chars, segPtr->body.chars, (size_t) index);
    newPtr1->body.chars[index] = 0;
    newPtr2->typePtr = &tkTextCharType;
    newPtr2->nextPtr = segPtr->nextPtr;
    newPtr2->size = segPtr->size - index;
    strcpy(newPtr2->body.chars, segPtr->body.chars + index);
    ckfree((char *) segPtr);
    return newPtr1;
}

/*
 *--------------------------------------------------------------
 *
 * CharCleanupProc --
 *
 *	This function merges adjacent character segments into a single
 *	character segment, if possible.
 *
 * Results:
 *	The return value is a pointer to the first segment in the (new) list
 *	of segments that used to start with segPtr.
 *
 * Side effects:
 *	Storage for the segments may be allocated and freed.
 *
 *--------------------------------------------------------------
 */

	/* ARGSUSED */
static TkTextSegment *
CharCleanupProc(
    TkTextSegment *segPtr,	/* Pointer to first of two adjacent segments
				 * to join. */
    TkTextLine *linePtr)	/* Line containing segments (not used). */
{
    TkTextSegment *segPtr2, *newPtr;

    segPtr2 = segPtr->nextPtr;
    if ((segPtr2 == NULL) || (segPtr2->typePtr != &tkTextCharType)) {
	return segPtr;
    }
    newPtr = (TkTextSegment *) ckalloc(CSEG_SIZE(
	    segPtr->size + segPtr2->size));
    newPtr->typePtr = &tkTextCharType;
    newPtr->nextPtr = segPtr2->nextPtr;
    newPtr->size = segPtr->size + segPtr2->size;
    strcpy(newPtr->body.chars, segPtr->body.chars);
    strcpy(newPtr->body.chars + segPtr->size, segPtr2->body.chars);
    ckfree((char *) segPtr);
    ckfree((char *) segPtr2);
    return newPtr;
}

/*
 *--------------------------------------------------------------
 *
 * CharDeleteProc --
 *
 *	This function is invoked to delete a character segment.
 *
 * Results:
 *	Always returns 0 to indicate that the segment was deleted.
 *
 * Side effects:
 *	Storage for the segment is freed.
 *
 *--------------------------------------------------------------
 */

	/* ARGSUSED */
static int
CharDeleteProc(
    TkTextSegment *segPtr,	/* Segment to delete. */
    TkTextLine *linePtr,	/* Line containing segment. */
    int treeGone)		/* Non-zero means the entire tree is being
				 * deleted, so everything must get cleaned
				 * up. */
{
    ckfree((char *) segPtr);
    return 0;
}

/*
 *--------------------------------------------------------------
 *
 * CharCheckProc --
 *
 *	This function is invoked to perform consistency checks on character
 *	segments.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	If the segment isn't inconsistent then the function panics.
 *
 *--------------------------------------------------------------
 */

	/* ARGSUSED */
static void
CharCheckProc(
    TkTextSegment *segPtr,	/* Segment to check. */
    TkTextLine *linePtr)	/* Line containing segment. */
{
    /*
     * Make sure that the segment contains the number of characters indicated
     * by its header, and that the last segment in a line ends in a newline.
     * Also make sure that there aren't ever two character segments adjacent
     * to each other: they should be merged together.
     */

    if (segPtr->size <= 0) {
	Tcl_Panic("CharCheckProc: segment has size <= 0");
    }
    if (strlen(segPtr->body.chars) != (size_t) segPtr->size) {
	Tcl_Panic("CharCheckProc: segment has wrong size");
    }
    if (segPtr->nextPtr == NULL) {
	if (segPtr->body.chars[segPtr->size-1] != '\n') {
	    Tcl_Panic("CharCheckProc: line doesn't end with newline");
	}
    } else if (segPtr->nextPtr->typePtr == &tkTextCharType) {
	Tcl_Panic("CharCheckProc: adjacent character segments weren't merged");
    }
}

/*
 *--------------------------------------------------------------
 *
 * ToggleDeleteProc --
 *
 *	This function is invoked to delete toggle segments.
 *
 * Results:
 *	Returns 1 to indicate that the segment may not be deleted, unless the
 *	entire B-tree is going away.
 *
 * Side effects:
 *	If the tree is going away then the toggle's memory is freed; otherwise
 *	the toggle counts in nodes above the segment get updated.
 *
 *--------------------------------------------------------------
 */

static int
ToggleDeleteProc(
    TkTextSegment *segPtr,	/* Segment to check. */
    TkTextLine *linePtr,	/* Line containing segment. */
    int treeGone)		/* Non-zero means the entire tree is being
				 * deleted, so everything must get cleaned
				 * up. */
{
    if (treeGone) {
	ckfree((char *) segPtr);
	return 0;
    }

    /*
     * This toggle is in the middle of a range of characters that's being
     * deleted. Refuse to die. We'll be moved to the end of the deleted range
     * and our cleanup function will be called later. Decrement node toggle
     * counts here, and set a flag so we'll re-increment them in the cleanup
     * function.
     */

    if (segPtr->body.toggle.inNodeCounts) {
	ChangeNodeToggleCount(linePtr->parentPtr,
		segPtr->body.toggle.tagPtr, -1);
	segPtr->body.toggle.inNodeCounts = 0;
    }
    return 1;
}

/*
 *--------------------------------------------------------------
 *
 * ToggleCleanupProc --
 *
 *	This function is called when a toggle is part of a line that's been
 *	modified in some way. It's invoked after the modifications are
 *	complete.
 *
 * Results:
 *	The return value is the head segment in a new list that is to replace
 *	the tail of the line that used to start at segPtr. This allows the
 *	function to delete or modify segPtr.
 *
 * Side effects:
 *	Toggle counts in the nodes above the new line will be updated if
 *	they're not already. Toggles may be collapsed if there are duplicate
 *	toggles at the same position.
 *
 *--------------------------------------------------------------
 */

static TkTextSegment *
ToggleCleanupProc(
    TkTextSegment *segPtr,	/* Segment to check. */
    TkTextLine *linePtr)	/* Line that now contains segment. */
{
    TkTextSegment *segPtr2, *prevPtr;
    int counts;

    /*
     * If this is a toggle-off segment, look ahead through the next segments
     * to see if there's a toggle-on segment for the same tag before any
     * segments with non-zero size. If so then the two toggles cancel each
     * other; remove them both.
     */

    if (segPtr->typePtr == &tkTextToggleOffType) {
	for (prevPtr = segPtr, segPtr2 = prevPtr->nextPtr;
		(segPtr2 != NULL) && (segPtr2->size == 0);
		prevPtr = segPtr2, segPtr2 = prevPtr->nextPtr) {
	    if (segPtr2->typePtr != &tkTextToggleOnType) {
		continue;
	    }
	    if (segPtr2->body.toggle.tagPtr != segPtr->body.toggle.tagPtr) {
		continue;
	    }
	    counts = segPtr->body.toggle.inNodeCounts
		    + segPtr2->body.toggle.inNodeCounts;
	    if (counts != 0) {
		ChangeNodeToggleCount(linePtr->parentPtr,
			segPtr->body.toggle.tagPtr, -counts);
	    }
	    prevPtr->nextPtr = segPtr2->nextPtr;
	    ckfree((char *) segPtr2);
	    segPtr2 = segPtr->nextPtr;
	    ckfree((char *) segPtr);
	    return segPtr2;
	}
    }

    if (!segPtr->body.toggle.inNodeCounts) {
	ChangeNodeToggleCount(linePtr->parentPtr,
		segPtr->body.toggle.tagPtr, 1);
	segPtr->body.toggle.inNodeCounts = 1;
    }
    return segPtr;
}

/*
 *--------------------------------------------------------------
 *
 * ToggleLineChangeProc --
 *
 *	This function is invoked when a toggle segment is about to move from
 *	one line to another.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Toggle counts are decremented in the nodes above the line.
 *
 *--------------------------------------------------------------
 */

static void
ToggleLineChangeProc(
    TkTextSegment *segPtr,	/* Segment to check. */
    TkTextLine *linePtr)	/* Line that used to contain segment. */
{
    if (segPtr->body.toggle.inNodeCounts) {
	ChangeNodeToggleCount(linePtr->parentPtr,
		segPtr->body.toggle.tagPtr, -1);
	segPtr->body.toggle.inNodeCounts = 0;
    }
}

/*
 *--------------------------------------------------------------
 *
 * ToggleCheckProc --
 *
 *	This function is invoked to perform consistency checks on toggle
 *	segments.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	If a consistency problem is found the function panics.
 *
 *--------------------------------------------------------------
 */

static void
ToggleCheckProc(
    TkTextSegment *segPtr,	/* Segment to check. */
    TkTextLine *linePtr)	/* Line containing segment. */
{
    register Summary *summaryPtr;
    int needSummary;

    if (segPtr->size != 0) {
	Tcl_Panic("ToggleCheckProc: segment had non-zero size");
    }
    if (!segPtr->body.toggle.inNodeCounts) {
	Tcl_Panic("ToggleCheckProc: toggle counts not updated in nodes");
    }
    needSummary = (segPtr->body.toggle.tagPtr->tagRootPtr!=linePtr->parentPtr);
    for (summaryPtr = linePtr->parentPtr->summaryPtr; ;
	    summaryPtr = summaryPtr->nextPtr) {
	if (summaryPtr == NULL) {
	    if (needSummary) {
		Tcl_Panic("ToggleCheckProc: tag not present in node");
	    } else {
		break;
	    }
	}
	if (summaryPtr->tagPtr == segPtr->body.toggle.tagPtr) {
	    if (!needSummary) {
		Tcl_Panic("ToggleCheckProc: tag present in root node summary");
	    }
	    break;
	}
    }
}

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */
@


1.31
log
@SMALL FIX: Remove useless explicit casts now that we require C89 or later.
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.30 2009/09/07 07:29:04 das Exp $
d2942 1
a2942 1
		    Tcl_Panic("TkBTreeNextTag found incorrect tag summary info.");
d3160 1
a3160 1
		Tcl_Panic("TkBTreePrevTag found incorrect tag summary info.");
@


1.30
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
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.29 2009/04/10 15:53:24 das Exp $
d3777 1
a3777 1
	tagPtr = (TkTextTag *) Tcl_GetHashValue(entryPtr);
@


1.29
log
@	* generic/tkTextBTree.c (TkBTreeDeleteIndexRange): add bounds check
	to startEnd array access (fixes testsuite crash).
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.28 2008/11/08 18:44:40 dkf Exp $
d1236 2
a1238 1
	    segPtr = linePtr->segPtr;
d1393 1
d2470 1
a2470 1
    while (nodePtr->level > 0) {
d2484 4
d2555 1
a2555 1
    while (nodePtr->level > 0) {
d2569 4
d4290 1
d4294 1
@


1.28
log
@Lots of small changes to make code more pretty and C89-like.
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.27 2007/12/13 15:24:17 dgp Exp $
d1520 2
a1521 1
	    while (treePtr->startEnd[checkCount] != NULL) {
@


1.27
log
@merge stable branch onto HEAD
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.25.2.1 2007/09/07 01:25:36 dgp Exp $
d870 1
a870 1
		(*segPtr->typePtr->deleteProc)(segPtr, linePtr, 1);
d1210 1
a1210 1
	    segPtr = (*segPtr->typePtr->splitProc)(segPtr, count);
d1285 1
a1285 1
		*prevPtrPtr = (*segPtr->typePtr->cleanupProc)(segPtr, linePtr);
d1455 1
a1455 1
	if ((*segPtr->typePtr->deleteProc)(segPtr, curLinePtr, 0) != 0) {
d1486 1
a1486 1
		(*segPtr->typePtr->lineChangeProc)(segPtr, index2Ptr->linePtr);
d3938 1
a3938 1
		    (*segPtr->typePtr->checkProc)(segPtr, linePtr);
@


1.27.2.1
log
@	* generic/tkTextBTree.c (TkBTreeDeleteIndexRange): add bounds check
	to startEnd array access (fixes testsuite crash).
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.27 2007/12/13 15:24:17 dgp Exp $
d1520 1
a1520 2
	    while (checkCount < treePtr->startEndCount &&
		    treePtr->startEnd[checkCount] != NULL) {
@


1.26
log
@header cleanup
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.25 2007/02/23 13:37:49 das Exp $
@


1.25
log
@missing comma in previous commit
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.24 2007/02/22 13:56:33 dkf Exp $
a17 1
#include "tkPort.h"
@


1.25.2.1
log
@merge updates from HEAD
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.25 2007/02/23 13:37:49 das Exp $
d18 1
@


1.24
log
@Less of that K&R or wrongly indented function header style!
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.23 2007/01/18 23:20:37 nijtmans Exp $
d1815 1
a1815 1
    TkText *textPtr		/* Relative to this client of the B-tree. */
@


1.23
log
@various "const" additions, in line with TIP #27
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.22 2006/09/10 17:06:32 das Exp $
d69 1
a69 1
    int level;			/* Level of this node in the B-tree.  0 refers
d79 1
a79 1
    int* numPixels;		/* Array containing total number of vertical
d111 2
a112 2
    int stateEpoch;             /* Updated each time any aspect of the B-tree
                                 * changes. */
d137 1
a137 2
 * Variable that indicates whether to enable consistency checks for
 * debugging.
d155 38
a192 41
static int		AdjustPixelClient _ANSI_ARGS_((BTree *treePtr,
			    int defaultHeight, Node *nodePtr, TkTextLine *start,
			    TkTextLine *end, int useReference,
			    int newPixelReferences, int *counting));
static void		ChangeNodeToggleCount _ANSI_ARGS_((Node *nodePtr,
			    TkTextTag *tagPtr, int delta));
static void		CharCheckProc _ANSI_ARGS_((TkTextSegment *segPtr,
			    TkTextLine *linePtr));
static int		CharDeleteProc _ANSI_ARGS_((TkTextSegment *segPtr,
			    TkTextLine *linePtr, int treeGone));
static TkTextSegment *	CharCleanupProc _ANSI_ARGS_((TkTextSegment *segPtr,
			    TkTextLine *linePtr));
static TkTextSegment *	CharSplitProc _ANSI_ARGS_((TkTextSegment *segPtr,
			    int index));
static void		CheckNodeConsistency _ANSI_ARGS_((Node *nodePtr,
			    int references));
static void		CleanupLine _ANSI_ARGS_((TkTextLine *linePtr));
static void		DeleteSummaries _ANSI_ARGS_((Summary *tagPtr));
static void		DestroyNode _ANSI_ARGS_((Node *nodePtr));
static TkTextSegment *	FindTagEnd _ANSI_ARGS_((TkTextBTree tree,
			    TkTextTag *tagPtr, TkTextIndex *indexPtr));
static void		IncCount _ANSI_ARGS_((TkTextTag *tagPtr, int inc,
			    TagInfo *tagInfoPtr));
static void		Rebalance _ANSI_ARGS_((BTree *treePtr, Node *nodePtr));
static void		RecomputeNodeCounts _ANSI_ARGS_((BTree *treePtr,
			    Node *nodePtr));
static void		RemovePixelClient _ANSI_ARGS_((BTree *treePtr,
			    Node *nodePtr, int overwriteWithLast));
static TkTextSegment *	SplitSeg _ANSI_ARGS_((TkTextIndex *indexPtr));
static void		ToggleCheckProc _ANSI_ARGS_((TkTextSegment *segPtr,
			    TkTextLine *linePtr));
static TkTextSegment *	ToggleCleanupProc _ANSI_ARGS_((TkTextSegment *segPtr,
			    TkTextLine *linePtr));
static int		ToggleDeleteProc _ANSI_ARGS_((TkTextSegment *segPtr,
			    TkTextLine *linePtr, int treeGone));
static void		ToggleLineChangeProc _ANSI_ARGS_((TkTextSegment *segPtr,
			    TkTextLine *linePtr));
static TkTextSegment *	FindTagStart _ANSI_ARGS_((TkTextBTree tree,
			    TkTextTag *tagPtr, TkTextIndex *indexPtr));
static void		AdjustStartEndRefs _ANSI_ARGS_((BTree *treePtr,
			    TkText *textPtr, int action));
d264 2
a265 2
TkBTreeCreate(sharedTextPtr)
    TkSharedText *sharedTextPtr;
d293 1
a293 1
     * pointers are simply NULL
d356 2
a357 2
 *	pixel counts for the new client. It should be stored by the caller.
 *	If defaultHeight was negative, then the return value will be -1.
d366 4
a369 4
TkBTreeAddClient(tree, textPtr, defaultHeight)
    TkTextBTree tree;		/* B-tree to add a client to */
    TkText *textPtr;		/* Client to add */
    int defaultHeight;		/* Default line height for the new client, or
d372 1
a372 1
    register BTree *treePtr = (BTree*) tree;
d426 3
a428 3
TkBTreeClientRangeChanged(textPtr, defaultHeight)
    TkText *textPtr;		/* Client whose start, end have changed. */
    int defaultHeight;		/* Default line height for the new client, or
d432 1
a432 1
    BTree *treePtr = (BTree*) textPtr->sharedTextPtr->tree;
d471 2
a472 2
TkBTreeDestroy(tree)
    TkTextBTree tree;		/* Tree to clean up */
d495 2
a496 2
 *	Return the epoch for the B-tree.  This number is incremented
 *	any time anything changes in the tree.
d508 2
a509 2
TkBTreeEpoch(tree)
    TkTextBTree tree;		/* Tree to get epoch for */
d536 3
a538 3
TkBTreeRemoveClient(tree, textPtr)
    TkTextBTree tree;		/* Tree to remove client from. */
    TkText *textPtr;		/* Client to remove. */
d581 1
a581 1
		if (adjustPtr->pixelReference == (treePtr->pixelReferences-1)) {
d617 3
a619 3
AdjustStartEndRefs(treePtr, textPtr, action)
    BTree *treePtr;		/* The entire B-tree */
    TkText *textPtr;		/* The text widget for which we want to adjust
d621 1
a621 1
    int action;			/* Action to perform */
d687 3
a689 3
 *	Immediately _after_ calling this, treePtr->clients and
 *	treePtr->pixelReferences should be adjusted if needed (i.e. if this is
 *	a new peer).
d701 3
a703 4
AdjustPixelClient(treePtr, defaultHeight, nodePtr, start, end,
	    useReference, newPixelReferences, counting)
    BTree *treePtr;		/* Pointer to tree */
    int defaultHeight;		/* Default pixel line height, which can be
d705 8
a712 8
    Node *nodePtr;		/* Adjust from this node downwards */
    TkTextLine *start;		/* First line for this pixel client */
    TkTextLine *end;		/* Last line for this pixel client */
    int useReference;		/* pixel reference for the client we are
				 * adding or changing */
    int newPixelReferences;	/* New number of pixel references to this
				 * B-tree */
    int *counting;		/* References an integer which is zero if
d728 1
d755 1
a755 1
	    linePtr->pixels[1+2*useReference] = (*counting ? 0 : 1);
d790 4
a793 4
RemovePixelClient(treePtr, nodePtr, overwriteWithLast)
    BTree *treePtr;		/* Pointer to tree */
    Node *nodePtr;		/* Adjust from this node downwards */
    int overwriteWithLast;	/* Over-write this peer widget's information
d798 4
a801 4
     * for each Node and TkTextLine, removing space allocated for one peer.
     * If 'overwriteWithLast' is not -1, then copy the information which was
     * in the last slot on top of one of the others (i.e. it's not the last
     * one we're deleting).
d858 2
a859 2
DestroyNode(nodePtr)
    register Node *nodePtr;	/* Destroy from this node downwards */
d908 2
a909 2
DeleteSummaries(summaryPtr)
    register Summary *summaryPtr;
d913 1
d939 3
a941 3
TkBTreeAdjustPixelHeight(textPtr, linePtr, newPixelHeight, mergedLogicalLines)
    const TkText *textPtr;	/* Client of the B-tree */
    register TkTextLine *linePtr;
d943 2
a944 2
    int newPixelHeight;		/* The line's known height in pixels. */
    int mergedLogicalLines;	/* The number of extra logical lines which
d948 2
a949 1
				 * height associated with the given linePtr */
d956 1
a956 1
    changeToPixelCount = newPixelHeight - linePtr->pixels[2*pixelReference];
d971 1
a971 1
    linePtr->pixels[2*pixelReference] = newPixelHeight;
d1008 3
a1010 3
TkBTreeInsertChars(tree, indexPtr, string)
    TkTextBTree tree;		/* Tree to insert into */
    register TkTextIndex *indexPtr;
d1015 1
a1015 1
    const char *string;		/* Pointer to bytes to insert (may contain
d1040 1
a1040 1
    BTree *treePtr = (BTree*)tree;
d1094 1
a1094 1
		ckalloc(sizeof(int)*2*treePtr->pixelReferences);
d1102 2
a1103 2
	 * Set up a starting default height, which will be re-adjusted
	 * later. We need to do this for each referenced widget
d1107 3
a1109 3
	    newLinePtr->pixels[2*ref] = linePtr->pixels[2*ref];
	    newLinePtr->pixels[1+2*ref] = 0;
	    changeToPixelCount[ref] += newLinePtr->pixels[2*ref];
d1153 1
a1153 1
	ckfree((char*)changeToPixelCount);
d1194 2
a1195 2
SplitSeg(indexPtr)
    TkTextIndex *indexPtr;	/* Index identifying position at which to
d1200 1
a1201 1
    int count = indexPtr->byteIndex;
d1229 1
a1229 1
	     * of a newline
d1234 3
a1236 1
		/* Reached end of the text */
d1265 2
a1266 2
CleanupLine(linePtr)
    TkTextLine *linePtr;		/* Line to be cleaned up. */
d1319 3
a1321 3
TkBTreeDeleteIndexRange(tree, index1Ptr, index2Ptr)
    TkTextBTree tree;		/* Tree to delete from */
    register TkTextIndex *index1Ptr;
d1324 1
a1324 1
    register TkTextIndex *index2Ptr;
d1337 2
a1338 1
    BTree *treePtr = (BTree*)tree;
d1349 1
a1349 1
    }  else {
d1430 2
a1431 2
	     * If the node is empty then delete it and its parents,
	     * recursively upwards until a non-empty node is found.
d1595 4
a1598 4
TkBTreeFindLine(tree, textPtr, line)
    TkTextBTree tree;		/* B-tree in which to find line. */
    const TkText *textPtr;	/* Relative to this client of the B-tree */
    int line;			/* Index of desired line. */
d1614 1
a1614 1
     * Check for the any start/end offset for this text widget
d1684 5
a1688 5
TkBTreeFindPixelLine(tree, textPtr, pixels, pixelOffset)
    TkTextBTree tree;		/* B-tree to use. */
    const TkText *textPtr;	/* Relative to this client of the B-tree */
    int pixels;			/* Pixel index of desired line. */
    int *pixelOffset;		/* Used to return offset */
d1725 1
a1725 1
	    linePtr->pixels[2*pixelReference] < pixels;
d1730 1
a1730 1
	pixels -= linePtr->pixels[2*pixelReference];
d1758 3
a1760 3
TkBTreeNextLine(textPtr, linePtr)
    const TkText *textPtr;	/* Next line in the context of this client */
    register TkTextLine *linePtr;
d1814 3
a1816 3
TkBTreePreviousLine(textPtr, linePtr)
    TkText *textPtr;		/* Relative to this client of the B-tree */
    register TkTextLine *linePtr;
d1831 1
a1831 1
    prevPtr = linePtr->parentPtr->children.linePtr;    /* First line at leaf */
d1897 3
a1899 3
TkBTreePixelsTo(textPtr, linePtr)
    const TkText *textPtr;	/* Relative to this client of the B-tree */
    TkTextLine *linePtr;	/* Pointer to existing line in B-tree. */
d1917 1
a1917 1
	index += linePtr2->pixels[2*pixelReference];
d1928 1
d1959 3
a1961 3
TkBTreeLinesTo(textPtr, linePtr)
    const TkText *textPtr;	/* Relative to this client of the B-tree */
    TkTextLine *linePtr;	/* Pointer to existing line in B-tree. */
d2020 2
a2021 2
TkBTreeLinkSegment(segPtr, indexPtr)
    TkTextSegment *segPtr;	/* Pointer to new segment to be added to
d2024 1
a2024 1
    TkTextIndex *indexPtr;	/* Where to add segment: it gets linked in
d2041 1
a2041 1
    ((BTree*)indexPtr->tree)->stateEpoch++;
d2063 3
a2065 3
TkBTreeUnlinkSegment(segPtr, linePtr)
    TkTextSegment *segPtr;	/* Segment to be unlinked. */
    TkTextLine *linePtr;	/* Line that currently contains segment. */
d2079 1
a2079 1
		 * eliding of a newline
d2115 2
a2116 2
TkBTreeTag(index1Ptr, index2Ptr, tagPtr, add)
    register TkTextIndex *index1Ptr;
d2118 1
a2118 1
    register TkTextIndex *index2Ptr;
d2121 2
a2122 2
    TkTextTag *tagPtr;		/* Tag to add or remove. */
    int add;			/* One means add tag to the given range of
d2129 1
a2129 3
    int oldState;
    int changed;
    int anyChanges = 0;
d2235 1
a2235 1
	((BTree*)index1Ptr->tree)->stateEpoch++;
d2265 2
a2266 2
ChangeNodeToggleCount(nodePtr, tagPtr, delta)
    register Node *nodePtr;	/* Node whose toggle count for a tag must be
d2268 2
a2269 2
    TkTextTag *tagPtr;		/* Information about tag. */
    int delta;			/* Amount to add to current toggle count for
d2274 1
a2274 1
    int rootLevel;		/* Level of original tag root */
d2326 1
a2326 1
	     * Zero toggle count;  must remove this tag from the list.
a2340 1

d2352 1
d2447 4
a2450 4
FindTagStart(tree, tagPtr, indexPtr)
    TkTextBTree tree;		/* Tree to search within */
    TkTextTag *tagPtr;		/* Tag to search for. */
    TkTextIndex *indexPtr;	/* Return - index information */
d2528 4
a2531 4
FindTagEnd(tree, tagPtr, indexPtr)
    TkTextBTree tree;		/* Tree to search within */
    TkTextTag *tagPtr;		/* Tag to search for. */
    TkTextIndex *indexPtr;	/* Return - index information */
d2618 2
a2619 2
TkBTreeStartSearch(index1Ptr, index2Ptr, tagPtr, searchPtr)
    TkTextIndex *index1Ptr;	/* Search starts here. Tag toggles at this
d2621 1
a2621 1
    TkTextIndex *index2Ptr;	/* Search stops here. Tag toggles at this
d2623 1
a2623 1
    TkTextTag *tagPtr;		/* Tag to search for. NULL means search for
d2625 1
a2625 1
    register TkTextSearch *searchPtr;
d2630 2
a2631 2
    TkTextIndex index0;		/* First index of the tag */
    TkTextSegment *seg0Ptr;	/* First segment of the tag */
d2653 1
a2653 1
	 * Adjust start of search up to the first range of the tag
d2658 1
a2658 1
	searchPtr->nextPtr = seg0Ptr;	/* Will be returned by NextTag */
d2714 2
a2715 2
TkBTreeStartSearchBack(index1Ptr, index2Ptr, tagPtr, searchPtr)
    TkTextIndex *index1Ptr;	/* Search starts here. Tag toggles at this
d2717 1
a2717 1
    TkTextIndex *index2Ptr;	/* Search stops here. Tag toggles at this
d2719 1
a2719 1
    TkTextTag *tagPtr;		/* Tag to search for. NULL means search for
d2721 3
a2723 3
    register TkTextSearch *searchPtr;
				/* Where to store information about
				 * search's progress. */
d2726 3
a2728 3
    TkTextIndex index0;		/* Last index of the tag */
    TkTextIndex backOne;	/* One character before starting index */
    TkTextSegment *seg0Ptr;	/* Last segment of the tag */
d2773 1
a2773 1
	searchPtr->lastPtr = NULL;	/* Signals special case for 1.0 */
d2821 2
a2822 2
TkBTreeNextTag(searchPtr)
    register TkTextSearch *searchPtr;
d2986 2
a2987 2
TkBTreePrevTag(searchPtr)
    register TkTextSearch *searchPtr;
d2996 2
a2997 3
    int byteIndex;
    int pastLast;		/* Saw last marker during scan */
    int linesSkipped;
d3037 2
a3038 1
		prevPtr = NULL;	  /* Segments earlier than last don't count */
d3200 2
a3201 2
TkBTreeCharTagged(indexPtr, tagPtr)
    const TkTextIndex *indexPtr;/* Indicates a character position at which to
d3203 1
a3203 1
    TkTextTag *tagPtr;		/* Tag of interest. */
d3311 2
a3312 2
TkBTreeGetTags(indexPtr, textPtr, numTagsPtr)
    const TkTextIndex *indexPtr;/* Indicates a particular position in the
d3314 1
a3314 1
    const TkText *textPtr;	/* If non-NULL, then only return tags for this
d3317 1
a3317 1
    int *numTagsPtr;		/* Store number of tags found at this
d3330 4
a3333 4
    tagInfo.tagPtrs = (TkTextTag **) ckalloc((unsigned)
	    NUM_TAG_INFOS*sizeof(TkTextTag *));
    tagInfo.counts = (int *) ckalloc((unsigned)
	    NUM_TAG_INFOS*sizeof(int));
d3410 2
a3411 1
	    if (tagTextPtr == NULL || textPtr == NULL || tagTextPtr == textPtr) {
d3459 3
a3461 3
TkTextIsElided(textPtr, indexPtr, elideInfo)
    const TkText *textPtr;	/* Overall information about text widget. */
    const TkTextIndex *indexPtr;/* The character in the text for which display
d3463 1
a3463 1
    TkTextElideInfo *elideInfo; /* NULL or a pointer to a structure in which
d3477 2
a3478 1
	infoPtr = (TkTextElideInfo*)ckalloc((unsigned)sizeof(TkTextElideInfo));
d3483 1
a3483 1
    infoPtr->elide = 0;		/* if nobody says otherwise, it's visible */
d3496 1
a3496 1
		ckalloc((unsigned)sizeof(TkTextTag *) * infoPtr->numTags);
d3590 1
a3590 1
     * first odd count (= on)
d3629 1
a3629 1
	ckfree((char*) infoPtr);
d3653 2
a3654 2
TkTextFreeElideInfo(elideInfo)
    TkTextElideInfo *elideInfo; /* Free any allocated memory in this
d3658 2
a3659 2
	ckfree((char*)elideInfo->tagCnts);
	ckfree((char*)elideInfo->tagPtrs);
d3683 4
a3686 4
IncCount(tagPtr, inc, tagInfoPtr)
    TkTextTag *tagPtr;		/* Handle for tag. */
    int inc;			/* Amount by which to increment tag count. */
    TagInfo *tagInfoPtr;	/* Holds cumulative information about tags;
d3709 4
a3712 4
	newSize = 2*tagInfoPtr->arraySize;
	newTags = (TkTextTag **) ckalloc((unsigned)
		(newSize * sizeof(TkTextTag *)));
	memcpy((VOID *) newTags, (VOID *) tagInfoPtr->tagPtrs,
d3716 2
a3717 2
	newCounts = (int *) ckalloc((unsigned) (newSize*sizeof(int)));
	memcpy((VOID *) newCounts, (VOID *) tagInfoPtr->counts,
d3748 2
a3749 2
TkBTreeCheck(tree)
    TkTextBTree tree;		/* Tree to check. */
d3774 1
a3774 1
	    continue;		/* no ranges for the tag */
d3890 3
a3892 3
CheckNodeConsistency(nodePtr, references)
    register Node *nodePtr;	/* Node whose subtree should be checked. */
    int references;		/* Number of referring widgets which have
d3907 1
a3907 1
    } else  {
d3919 1
a3919 1
	numPixels = (int*)ckalloc(sizeof(int)*references);
d3955 1
a3955 1
		numPixels[i] += linePtr->pixels[2*i];
d4004 1
a4004 1
		  numPixels[i], nodePtr->numPixels[i], i);
d4008 1
a4008 1
	ckfree((char*)numPixels);
d4015 1
a4015 1
		summaryPtr->tagPtr->name);
d4028 1
a4028 1
			toggleCount ++;
d4078 3
a4080 3
Rebalance(treePtr, nodePtr)
    BTree *treePtr;		/* Tree that is being rebalanced. */
    register Node *nodePtr;	/* Node that may be out of balance. */
d4275 2
a4276 2
	     * The siblings can't be merged, so just divide their
	     * children evenly between them.
d4314 3
a4316 3
RecomputeNodeCounts(treePtr, nodePtr)
    register BTree *treePtr;	/* The whole B-tree */
    register Node *nodePtr;	/* Node whose tag summary information must be
d4352 1
a4352 1
		nodePtr->numPixels[ref] += linePtr->pixels[2*ref];
d4435 2
a4436 2
	     * A node merge has collected all the toggles under one node.
	     * Push the root down to this level.
d4473 3
a4475 3
TkBTreeNumLines(tree, textPtr)
    TkTextBTree tree;		/* Information about tree. */
    const TkText *textPtr;	/* Relative to this client of the B-tree */
d4514 3
a4516 3
TkBTreeNumPixels(tree, textPtr)
    TkTextBTree tree;		/* The B-tree */
    const TkText *textPtr;	/* Relative to this client of the B-tree */
d4540 3
a4542 3
CharSplitProc(segPtr, index)
    TkTextSegment *segPtr;	/* Pointer to segment to split. */
    int index;			/* Position within segment at which to
d4559 1
a4559 1
    ckfree((char*) segPtr);
d4583 2
a4584 2
CharCleanupProc(segPtr, linePtr)
    TkTextSegment *segPtr;	/* Pointer to first of two adjacent segments
d4586 1
a4586 1
    TkTextLine *linePtr;	/* Line containing segments (not used). */
d4601 2
a4602 2
    ckfree((char*) segPtr);
    ckfree((char*) segPtr2);
d4624 4
a4627 4
CharDeleteProc(segPtr, linePtr, treeGone)
    TkTextSegment *segPtr;	/* Segment to delete. */
    TkTextLine *linePtr;	/* Line containing segment. */
    int treeGone;		/* Non-zero means the entire tree is being
d4631 1
a4631 1
    ckfree((char*) segPtr);
d4654 3
a4656 3
CharCheckProc(segPtr, linePtr)
    TkTextSegment *segPtr;	/* Segment to check. */
    TkTextLine *linePtr;	/* Line containing segment. */
d4699 4
a4702 4
ToggleDeleteProc(segPtr, linePtr, treeGone)
    TkTextSegment *segPtr;	/* Segment to check. */
    TkTextLine *linePtr;	/* Line containing segment. */
    int treeGone;		/* Non-zero means the entire tree is being
d4750 3
a4752 3
ToggleCleanupProc(segPtr, linePtr)
    TkTextSegment *segPtr;	/* Segment to check. */
    TkTextLine *linePtr;	/* Line that now contains segment. */
d4814 3
a4816 3
ToggleLineChangeProc(segPtr, linePtr)
    TkTextSegment *segPtr;	/* Segment to check. */
    TkTextLine *linePtr;	/* Line that used to contain segment. */
d4843 3
a4845 3
ToggleCheckProc(segPtr, linePtr)
    TkTextSegment *segPtr;	/* Segment to check. */
    TkTextLine *linePtr;	/* Line containing segment. */
d4856 1
a4856 1
    needSummary = (segPtr->body.toggle.tagPtr->tagRootPtr != linePtr->parentPtr);
d4874 8
@


1.22
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
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.21 2006/03/26 17:52:40 vincentdarley Exp $
d209 1
a209 1
Tk_SegType tkTextCharType = {
d224 1
a224 1
Tk_SegType tkTextToggleOnType = {
d239 1
a239 1
Tk_SegType tkTextToggleOffType = {
d943 1
a943 1
    CONST TkText *textPtr;	/* Client of the B-tree */
d1017 1
a1017 1
    CONST char *string;		/* Pointer to bytes to insert (may contain
d1033 1
a1033 1
    register CONST char *eol;	/* Pointer to character just after last one in
d1596 1
a1596 1
    CONST TkText *textPtr;	/* Relative to this client of the B-tree */
d1685 1
a1685 1
    CONST TkText *textPtr;	/* Relative to this client of the B-tree */
d1758 1
a1758 1
    CONST TkText *textPtr;	/* Next line in the context of this client */
d1897 1
a1897 1
    CONST TkText *textPtr;	/* Relative to this client of the B-tree */
d1958 1
a1958 1
    CONST TkText *textPtr;	/* Relative to this client of the B-tree */
d3201 1
a3201 1
    CONST TkTextIndex *indexPtr;/* Indicates a character position at which to
d3312 1
a3312 1
    CONST TkTextIndex *indexPtr;/* Indicates a particular position in the
d3314 1
a3314 1
    CONST TkText *textPtr;	/* If non-NULL, then only return tags for this
d3409 1
a3409 1
	    CONST TkText *tagTextPtr = tagInfo.tagPtrs[src]->textPtr;
d3459 2
a3460 2
    CONST TkText *textPtr;	/* Overall information about text widget. */
    CONST TkTextIndex *indexPtr;/* The character in the text for which display
d4473 1
a4473 1
    CONST TkText *textPtr;	/* Relative to this client of the B-tree */
d4514 1
a4514 1
    CONST TkText *textPtr;	/* Relative to this client of the B-tree */
@


1.21
log
@fix for text dump -command callbacks which modify the text widget
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.20 2005/11/17 16:21:56 dkf Exp $
d3600 6
a3605 1
		    && (textPtr->inactiveSelBorder == NULL)) {
@


1.20
log
@ANSIfy and reduce casting of NULL to promote readability
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.19 2005/10/10 10:36:35 vincentdarley Exp $
d111 2
d328 1
d497 25
d1043 1
d1338 1
d2039 1
d2235 1
@


1.19
log
@tip256 implementation
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.18 2005/07/29 13:43:43 dkf Exp $
d1754 1
a1754 1
	    return (TkTextLine *) NULL;
d1806 1
a1806 1
	if (prevPtr == (TkTextLine *) NULL) {
d1818 2
a1819 2
	if (nodePtr == (Node *) NULL || nodePtr->parentPtr == (Node *) NULL) {
	    return (TkTextLine *) NULL;
d1833 1
a1833 1
	nodePtr = (Node *)NULL;
d1836 1
a1836 1
	if (prevPtr->nextPtr == (TkTextLine *) NULL) {
d2245 1
a2245 1
    if (tagPtr->tagRootPtr == (Node *) NULL) {
d2347 1
a2347 1
	tagPtr->tagRootPtr = (Node *) NULL;
d2358 1
a2358 1
		node2Ptr != (Node *)NULL ;
d2427 1
a2427 1
    if (nodePtr == (Node *) NULL) {
d2437 1
a2437 1
	for (nodePtr = nodePtr->children.nodePtr ; nodePtr != (Node *) NULL;
d2454 1
a2454 1
    for (linePtr = nodePtr->children.linePtr; linePtr != (TkTextLine *) NULL;
d2508 1
a2508 1
    if (nodePtr == (Node *) NULL) {
d2519 1
a2519 1
		nodePtr != (Node *) NULL; nodePtr = nodePtr->nextPtr) {
d2539 1
a2539 1
	    linePtr != (TkTextLine *) NULL; linePtr = linePtr->nextPtr) {
d2607 1
a2607 1
    if (seg0Ptr == (TkTextSegment *) NULL) {
d2634 1
a2634 1
    searchPtr->lastPtr = TkTextIndexToSeg(index2Ptr, (int *) NULL);
d2704 1
a2704 1
    if (seg0Ptr == (TkTextSegment *) NULL) {
d2744 1
a2744 1
	searchPtr->lastPtr = TkTextIndexToSeg(&backOne, (int *) NULL);
d3730 1
a3730 1
	if (nodePtr == (Node *) NULL) {
@


1.18
log
@Oops, missed a few spots
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.17 2005/07/29 13:35:07 dkf Exp $
d1272 1
a1272 1
 * TkBTreeDeleteChars --
d1290 1
a1290 1
TkBTreeDeleteChars(tree, index1Ptr, index2Ptr)
a4834 8

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */
@


1.17
log
@Getting more systematic about style
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.16 2005/02/14 23:00:44 vincentdarley Exp $
d49 6
a54 6
    TkTextTag *tagPtr;			/* Handle for tag. */
    int toggleCount;			/* Number of transitions into or out
					 * of this tag that occur in the
					 * subtree rooted at this node. */
    struct Summary *nextPtr;		/* Next in list of all tags for same
					 * node, or NULL if at end of list. */
d62 13
a74 14
    struct Node *parentPtr;		/* Pointer to parent node, or NULL if
					 * this is the root. */
    struct Node *nextPtr;		/* Next in list of siblings with the
					 * same parent node, or NULL for end
					 * of list. */
    Summary *summaryPtr;		/* First in malloc-ed list of info
					 * about tags in this subtree (NULL if
					 * no tag info in the subtree). */
    int level;				/* Level of this node in the B-tree.
					 * 0 refers to the bottom of the tree
					 * (children are lines, not nodes). */
    union {				/* First in linked list of children. */
	struct Node *nodePtr;		/* Used if level > 0. */
	TkTextLine *linePtr;		/* Used if level == 0. */
d76 6
a81 7
    int numChildren;			/* Number of children of this node. */
    int numLines;			/* Total number of lines (leaves) in
					 * the subtree rooted here. */
    int* numPixels;			/* Array containing total number of
					 * vertical display pixels in the
					 * subtree rooted here, one entry for
					 * each peer widget. */
d107 7
a113 8
    Node *rootPtr;			/* Pointer to root of B-tree. */
    int clients;			/* Number of clients of this B-tree */
    int pixelReferences;		/* Number of clients of this B-tree
					 * which care about pixel heights. */
    TkSharedText *sharedTextPtr;	/* Used to find tagTable in
					 * consistency checking code, and to
					 * access list of all B-tree
					 * clients. */
d125 7
a131 9
    int numTags;			/* Number of tags for which there is
					 * currently information in tags and
					 * counts. */
    int arraySize;			/* Number of entries allocated for
					 * tags and counts. */
    TkTextTag **tagPtrs;		/* Array of tags seen so far.
					 * Malloc-ed. */
    int *counts;			/* Toggle count (so far) for each
					 * entry in tags. Malloc-ed. */
d448 1
a448 1
		  NULL, TkBTreeNumLines(textPtr->sharedTextPtr->tree, NULL));
d4616 2
a4617 2
    TkTextSegment *segPtr;		/* Segment to check. */
    TkTextLine *linePtr;		/* Line containing segment. */
@


1.16
log
@fix to newline eliding in text widget
@
text
@d1 1
a1 1
/* 
d4 4
a7 4
 *	This file contains code that manages the B-tree representation
 *	of text for Tk's text widget and implements character and
 *	toggle segment types.
 *	
d11 2
a12 2
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.15 2004/09/10 12:13:41 vincentdarley Exp $
d23 6
a28 6
 * 
 * Most of this file is independent of the text widget implementation
 * and representation now.  Without much effort this could be developed
 * further into a new Tcl object type of which the Tk text widget is one
 * example of a client.
 * 
d30 9
a38 10
 * displayed, and must _never_ have a non-zero pixel count.  This dummy
 * line is a historical convenience to avoid other code having to deal
 * with NULL TkTextLines.  Since Tk 8.5, with pixel line height
 * calculations and peer widgets, this dummy line is becoming somewhat
 * of a liability, and special case code has been required to deal with
 * it.  It is probably a good idea to investigate removing the dummy
 * line completely.  This could result in an overall simplification
 * (although it would require new special case code to deal with the
 * fact that '.text index end' would then not really point to a valid
 * line, rather it would point to the beginning of a non-existent line
d44 2
a45 2
 * The data structure below keeps summary information about one tag as part
 * of the tag information in a node.
d50 3
a52 3
    int toggleCount;			/* Number of transitions into or
					 * out of this tag that occur in
					 * the subtree rooted at this node. */
d80 4
a83 4
    int* numPixels;                     /* Array containing total number
                                         * of vertical display pixels in
                                         * the subtree rooted here, one
                                         * entry for each peer widget.  */
d86 3
a88 3
/* 
 * Used to avoid having to allocate and deallocate arrays on the
 * fly for commonly used procedures.  Must be > 0.
d94 3
a96 3
 * Upper and lower bounds on how many children a node may have:
 * rebalance when either of these limits is exceeded.  MAX_CHILDREN
 * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
d103 3
a105 3
 * The data structure below defines an entire B-tree.  Since text
 * widgets are the only current B-tree clients, 'clients' and
 * 'pixelReferences' are identical.
d110 7
a116 8
    int clients;                        /* Number of clients of this
                                         * B-tree */
    int pixelReferences;                /* Number of clients of this
                                         * B-tree which care about
                                         * pixel heights */
    TkSharedText *sharedTextPtr;	/* Used to find tagTable in consistency
					 * checking code, and to access 
					 * list of all B-tree clients */
d128 3
a130 3
    int numTags;			/* Number of tags for which there
					 * is currently information in
					 * tags and counts. */
d136 1
a136 1
					 * entry in tags.  Malloc-ed. */
d156 1
a156 1
 * Forward declarations for procedures defined in this file:
d159 1
a159 1
static int              AdjustPixelClient _ANSI_ARGS_((BTree *treePtr, 
d161 1
a161 1
			    TkTextLine *end, int useReference, 
d174 1
a174 1
							  int references));
d178 1
a178 1
static TkTextSegment *	FindTagEnd _ANSI_ARGS_((TkTextBTree tree, 
d183 3
a185 3
static void		RecomputeNodeCounts _ANSI_ARGS_((BTree *treePtr, 
							 Node *nodePtr));
static void             RemovePixelClient _ANSI_ARGS_((BTree *treePtr, 
d198 1
a198 1
static void             AdjustStartEndRefs _ANSI_ARGS_((BTree *treePtr, 
d201 1
a201 1
/* 
d204 3
a206 2
#define TEXT_ADD_REFS     1
#define TEXT_REMOVE_REFS  2
d213 8
a220 8
    "character",				/* name */
    0,						/* leftGravity */
    CharSplitProc,				/* splitProc */
    CharDeleteProc,				/* deleteProc */
    CharCleanupProc,				/* cleanupProc */
    (Tk_SegLineChangeProc *) NULL,		/* lineChangeProc */
    TkTextCharLayoutProc,			/* layoutProc */
    CharCheckProc				/* checkProc */
d224 1
a224 2
 * Type record for segments marking the beginning of a tagged
 * range:
d228 8
a235 8
    "toggleOn",					/* name */
    0,						/* leftGravity */
    (Tk_SegSplitProc *) NULL,			/* splitProc */
    ToggleDeleteProc,				/* deleteProc */
    ToggleCleanupProc,				/* cleanupProc */
    ToggleLineChangeProc,			/* lineChangeProc */
    (Tk_SegLayoutProc *) NULL,			/* layoutProc */
    ToggleCheckProc				/* checkProc */
d239 1
a239 2
 * Type record for segments marking the end of a tagged
 * range:
d243 8
a250 8
    "toggleOff",				/* name */
    1,						/* leftGravity */
    (Tk_SegSplitProc *) NULL,			/* splitProc */
    ToggleDeleteProc,				/* deleteProc */
    ToggleCleanupProc,				/* cleanupProc */
    ToggleLineChangeProc,			/* lineChangeProc */
    (Tk_SegLayoutProc *) NULL,			/* layoutProc */
    ToggleCheckProc				/* checkProc */
d258 1
a258 1
 *	This procedure is called to create a new text B-tree.
d261 2
a262 2
 *	The return value is a pointer to a new B-tree containing
 *	one line with nothing but a newline character.
d278 1
a278 1
    
d280 4
a283 4
     * The tree will initially have two empty lines.  The second line
     * isn't actually part of the tree's contents, but its presence
     * makes several operations easier.  The tree will have one node,
     * which is also the root of the tree.
d289 1
a289 1
    
d297 4
a300 3
    /* 
     * The tree currently has no registered clients, so all pixel
     * count pointers are simply NULL
d302 1
d306 1
a306 1
    
d316 1
a316 1
    
d335 1
a335 1
    
d344 8
a351 9
 *	This procedure is called to provide a client with access to
 *	a given B-tree.  If the client wishes to make use of the
 *	B-tree's pixel height storage, caching and calculation 
 *	mechanisms, then a non-negative 'defaultHeight' must be
 *	provided.  In this case the return value is a pixel tree
 *	reference which must be provided in all of the B-tree API which
 *	refers to or modifies pixel heights:
 *	
 *	TkBTreeAdjustPixelHeight, 
d353 1
a353 1
 *	TkBTreeNumPixels, 
d356 3
a358 3
 *	
 *	If this is not provided, then the above functions must
 *	never be called for this client.
d361 3
a363 4
 *	The return value is the pixelReference used by the B-tree to
 *	refer to pixel counts for the new client.  It should be stored
 *	by the caller.  If defaultHeight was negative, then the return
 *	value will be -1.
d373 4
a376 5
    TkTextBTree tree;              /* B-tree to add a client to */
    TkText *textPtr;               /* Client to add */
    int defaultHeight;             /* Default line height for the new
                                    * client, or -1 if no pixel
                                    * heights are to be kept. */
d379 1
a379 1
    
d392 4
a395 4
	/* 
	 * We must set the 'end' value in AdjustPixelClient so that
	 * the last dummy line in the B-tree doesn't contain
	 * a pixel height.
d397 1
d402 3
a404 4
        AdjustPixelClient(treePtr, defaultHeight, treePtr->rootPtr,
			  textPtr->start, end, useReference, 
			  1 + useReference, &counting);
	
d418 2
a419 2
 *	Called when the -startline or -endline options of a text 
 *	widget client of the B-tree have changed.
d425 2
a426 3
 *	Lots of processing of the B-tree is done, with potential
 *	for memory to be allocated and initialized for the pixel
 *	heights of the widget.
d433 3
a435 5
    TkText *textPtr;               /* Client whose start, end have
                                    * changed. */
    int defaultHeight;             /* Default line height for the new
				    * client, or -1 if no pixel
				    * heights are to be kept. */
d439 1
a439 1
    
d444 4
a447 4
    /* 
     * We must set the 'end' value in AdjustPixelClient so that
     * the last dummy line in the B-tree doesn't contain
     * a pixel height.
d449 1
d452 1
a452 1
	end = TkBTreeFindLine(textPtr->sharedTextPtr->tree, 
d456 2
a457 2
		      textPtr->start, end, useReference, 
		      treePtr->pixelReferences, &counting);
d468 1
a468 2
 *	The tree is deleted, so 'tree' should never
 *	again be used.
d478 1
a478 1
    TkTextBTree tree;			/* Tree to clean up */
d482 1
a482 1
    /* 
d484 2
a485 2
     * 'TkBTreeRemoveClient', since the 'DestroyNode' will clean
     * everything up itself.
d487 1
a487 1
    
d490 1
a490 1
        ckfree((char *) treePtr->startEnd);
d501 3
a503 3
 *	Remove a client widget from its B-tree, cleaning up the pixel
 *	arrays which it uses if necessary.  If this is the last such
 *	widget, we also destroy the whole tree.
d506 3
a508 3
 *	All tree-specific aspects of the given client are deleted.
 *	If no more references exist, then the given tree is also
 *	deleted (in which case 'tree' must not be used again).
d518 2
a519 2
    TkTextBTree tree;                   /* Tree to remove client from */
    TkText *textPtr;                    /* Client to remove. */
d523 1
a523 1
    
d525 4
a528 1
	/* The last reference to the tree */
d533 4
a536 1
	/* A client which doesn't care about pixels */
d539 4
a542 2
	/* Clean up pixel data for the given reference */
	
d544 3
a546 3
	    /* 
	     * The widget we're removing has the last index,
	     * so deletion is easier.
d548 1
d552 1
a552 1
	    
d555 3
a557 3
	    /* 
	     * Now we need to adjust the 'pixelReference' of the
	     * peer widget whose storage we've just moved.
d559 1
d562 1
a562 1
	        if (adjustPtr->pixelReference == (treePtr->pixelReferences-1)) {
d564 3
a566 3
	            break;
	        }
	        adjustPtr = adjustPtr->next;
d575 1
a575 1
    
d586 1
a586 2
 *	Modify B-tree's cache of start, end lines for the given text
 *	widget.
d592 1
a592 2
 *	The number of cached items may change
 *	(treePtr->startEndCount).
d598 5
a602 5
AdjustStartEndRefs(treePtr, textPtr, action) 
    BTree *treePtr;           /* The entire B-tree */
    TkText *textPtr;          /* The text widget for which we want to 
                               * adjust it's start and end cache. */
    int action;               /* Action to perform */
d614 1
a614 1
	        count++;
d619 6
a624 4
	treePtr->startEnd = (TkTextLine**)ckrealloc((char*)treePtr->startEnd, 
						   sizeof(TkTextLine*)*count);
	treePtr->startEndRef = (TkText**)ckrealloc((char*)treePtr->startEndRef, 
						  sizeof(TkText*)*count);
d626 2
a627 2
    if ((action & TEXT_ADD_REFS) 
      && (textPtr->start != NULL || textPtr->end != NULL)) {
d630 6
a635 2
	if (textPtr->start != NULL) treePtr->startEndCount++;
	if (textPtr->end != NULL) treePtr->startEndCount++;
d638 8
a645 6
	
	treePtr->startEnd = (TkTextLine**)ckrealloc((char*)treePtr->startEnd, 
						    sizeof(TkTextLine*)*count);
	treePtr->startEndRef = (TkText**)ckrealloc((char*)treePtr->startEndRef,
						   sizeof(TkText*)*count);
	
d664 4
a667 5
 *	Utility procedure used to update all data structures for the
 *	existence of a new peer widget based on this B-tree, or for
 *	the modification of the start, end lines of an existing peer
 *	widget.
 *	
d669 2
a670 2
 *	treePtr->pixelReferences should be adjusted if needed (i.e.
 *	if this is a new peer).
d676 1
a676 2
 *	All the storage for Nodes and TkTextLines in the tree may
 *	be adjusted.
d682 15
a696 21
AdjustPixelClient(treePtr, defaultHeight, nodePtr, start, end, 
		  useReference, newPixelReferences, counting)
    BTree *treePtr;			/* Pointer to tree */
    int defaultHeight;		        /* Default pixel line
                      		         * height, which can be zero. */
    Node *nodePtr;                      /* Adjust from this node
                                         * downwards */
    TkTextLine *start;                  /* First line for this pixel
                                         * client */
    TkTextLine *end;                    /* Last line for this pixel
                                         * client */
    int useReference;                   /* pixel reference for the
                                         * client we are adding or
                                         * changing */
    int newPixelReferences;             /* New number of pixel
                                         * references to this B-tree */
    int *counting;                      /* References an integer which
                                         * is zero if we're outside the
                                         * relevant range for this
                                         * client, and 1 if we're 
                                         * inside. */
d701 5
a705 5
     * Traverse entire tree down from nodePtr, reallocating pixel
     * structures for each Node and TkTextLine, adding room for the new
     * peer's pixel information (1 extra int per Node, 2 extra ints per
     * TkTextLine).  Also copy the information from the last peer into
     * the new space (so it contains something sensible).
d707 1
a707 1
    
d712 1
a712 2
					    start, end, useReference, 
					    newPixelReferences, counting);
d717 1
a717 1
	
d726 2
a727 2
		linePtr->pixels = (int*)ckrealloc((char*)linePtr->pixels, 
				    sizeof(int)*2*newPixelReferences);
d729 4
a732 4
	    /* 
	     * Notice that for the very last line, we are never counting
	     * and therefore this always has a height of 0 and an epoch
	     * of 1.
d734 1
d738 1
a738 1
	    
d743 2
a744 2
	nodePtr->numPixels = (int*)ckrealloc((char*)nodePtr->numPixels, 
			      sizeof(int)*newPixelReferences);
d755 5
a759 5
 *	Utility procedure used to update all data structures for the
 *	removal of a peer widget which used to be based on this B-tree.
 *	
 *	Immediately _after_ calling this, treePtr->clients should
 *	be decremented.
d765 1
a765 2
 *	All the storage for Nodes and TkTextLines in the tree may
 *	be adjusted.
d772 4
a775 6
    BTree *treePtr;			/* Pointer to tree */
    Node *nodePtr;                      /* Adjust from this node
                                         * downwards */
    int overwriteWithLast;              /* Over-write this peer widget's
                                         * information with the last one
                                         */
d778 5
a782 5
     * Traverse entire tree down from nodePtr, reallocating pixel
     * structures for each Node and TkTextLine, removing space allocated
     * for one peer.  If 'overwriteWithLast' is not -1, then copy the
     * information which was in the last slot on top of one of the
     * others (i.e. it's not the last one we're deleting).
d786 2
a787 2
	nodePtr->numPixels[overwriteWithLast] 
	  = nodePtr->numPixels[treePtr->pixelReferences-1];
d792 2
a793 2
	nodePtr->numPixels = (int*)ckrealloc((char*)nodePtr->numPixels, 
			sizeof(int)*(treePtr->pixelReferences-1));
d805 4
a808 4
		linePtr->pixels[2*overwriteWithLast] 
		  = linePtr->pixels[2*(treePtr->pixelReferences-1)];
		linePtr->pixels[1+2*overwriteWithLast] 
		  = linePtr->pixels[1+2*(treePtr->pixelReferences-1)];
d813 2
a814 2
		linePtr->pixels = (int*)ckrealloc((char*)linePtr->pixels, 
			sizeof(int)*2*(treePtr->pixelReferences-1));
d826 2
a827 2
 *	This is a recursive utility procedure used during the deletion
 *	of a B-tree.
d840 1
a840 1
    register Node *nodePtr;    /* Destroy from this node downwards */
d876 2
a877 2
 *	Free up all of the memory in a list of tag summaries associated
 *	with a node.
d890 2
a891 2
    register Summary *summaryPtr;	/* First in list of node's tag
					 * summaries. */
d906 2
a907 2
 *	Adjust the pixel height of a given logical line to the
 *	specified value.
d913 1
a913 2
 *	Updates overall data structures so pixel height count is
 *	consistent.
d920 9
a928 12
    CONST TkText *textPtr;              /* Client of the B-tree */
    register TkTextLine *linePtr;	/* The logical line to update */
    int newPixelHeight;			/* The line's known height 
                       			 * in pixels */
    int mergedLogicalLines;             /* The number of extra logical
                                         * lines which have been merged
                                         * with this one (due to elided
                                         * eols).  They will have their
                                         * pixel height set to zero, and
                                         * the total pixel height
                                         * associated with the given
                                         * linePtr.  */
d931 2
a932 2
    int changeToPixelCount;		/* Counts change to total number of
					 * pixels in file. */
d934 1
a934 1
    
d938 2
a939 2
     * Increment the pixel counts in all the parent nodes of the
     * current line, then rebalance the tree if necessary.
d944 1
a944 1
    
d951 3
a953 3
    
    /* 
     * Any merged logical lines must have their height set to zero. 
d955 1
d957 1
a957 1
        linePtr = TkBTreeNextLine(textPtr, linePtr);
d960 1
a960 1
    
d964 1
d979 3
a981 3
 *	Characters are added to the B-tree at the given position.
 *	If the string contains newlines, new lines will be added,
 *	which could cause the structure of the B-tree to change.
d988 8
a995 9
    TkTextBTree tree;                   /* Tree to insert into */
    register TkTextIndex *indexPtr;	/* Indicates where to insert text.
					 * When the procedure returns, this
					 * index is no longer valid because
					 * of changes to the segment
					 * structure. */
    CONST char *string;			/* Pointer to bytes to insert (may
					 * contain newlines, must be null-
					 * terminated). */
d998 9
a1006 9
    register TkTextSegment *prevPtr;	/* The segment just before the first
					 * new segment (NULL means new segment
					 * is at beginning of line). */
    TkTextSegment *curPtr;		/* Current segment;  new characters
					 * are inserted just after this one. 
					 * NULL means insert at beginning of
					 * line. */
    TkTextLine *linePtr;		/* Current line (new segments are
					 * added to this line). */
d1009 7
a1015 7
    int chunkSize;			/* # characters in current chunk. */
    register CONST char *eol;		/* Pointer to character just after last
					 * one in current chunk. */
    int changeToLineCount;		/* Counts change to total number of
					 * lines in file. */
    int *changeToPixelCount;		/* Counts change to total number of
					 * pixels in file. */
d1018 1
a1018 1
    
d1025 2
a1026 3
     * Chop the string up into lines and create a new segment for
     * each line, plus a new line for the leftovers from the
     * previous line.
d1031 2
a1032 2
        changeToPixelCount = (int*) ckalloc(sizeof(int) * 
					    treePtr->pixelReferences);
d1039 1
a1039 1
    
d1066 2
a1067 2
	 * The chunk ended with a newline, so create a new TkTextLine
	 * and move the remainder of the old line to it.
d1071 3
a1073 3
	newLinePtr->pixels = 
                 (int*) ckalloc(sizeof(int)*2*treePtr->pixelReferences);
	
d1078 1
d1080 1
a1080 1
	 * Set up a starting default height, which will be re-adjusted 
d1083 1
d1094 1
a1094 1
	
d1099 8
a1106 8
     * I don't believe it's possible for either of the two lines
     * passed to this function to be the last line of text, but
     * the function is robust to that case anyway.  (We must never
     * re-calculated the line height of the last line).
     */
    TkTextInvalidateLineMetrics(treePtr->sharedTextPtr, NULL, 
				indexPtr->linePtr, changeToLineCount, 
				TK_TEXT_INVALIDATE_INSERT);
d1109 2
a1110 2
     * Cleanup the starting line for the insertion, plus the ending
     * line if it's different.
d1117 1
a1117 1
    
d1133 1
a1133 1
        
d1150 7
a1156 8
 *	This procedure is called before adding or deleting
 *	segments.  It does three things: (a) it finds the segment
 *	containing indexPtr;  (b) if there are several such
 *	segments (because some segments have zero length) then
 *	it picks the first segment that does not have left
 *	gravity;  (c) if the index refers to the middle of
 *	a segment then it splits the segment so that the
 *	index now refers to the beginning of a segment.
d1159 3
a1161 4
 *	The return value is a pointer to the segment just
 *	before the segment corresponding to indexPtr (as
 *	described above).  If the segment corresponding to
 *	indexPtr is the first in its line then the return
d1165 2
a1166 2
 *	The segment referred to by indexPtr is split unless
 *	indexPtr refers to its first character.
d1173 2
a1174 2
    TkTextIndex *indexPtr;		/* Index identifying position
					 * at which to split a segment. */
d1178 1
a1178 1
    
d1183 1
a1183 1
    
d1200 1
a1200 1
    
d1205 3
a1207 3
	    /* 
	     * Two logical lines merged into one display line 
	     * through eliding of a newline
d1209 1
d1226 4
a1229 5
 *	This procedure is called after modifications have been
 *	made to a line.  It scans over all of the segments in
 *	the line, giving each a chance to clean itself up, e.g.
 *	by merging with the following segments, updating internal
 *	information, etc.
d1235 1
a1235 1
 *	Depends on what the segment-specific cleanup procedures do.
d1248 6
a1253 7
     * Make a pass over all of the segments in the line, giving each
     * a chance to clean itself up.  This could potentially change
     * the structure of the line, e.g. by merging two segments
     * together or having two segments cancel themselves;  if so,
     * then repeat the whole process again, since the first structure
     * change might make other structure changes possible.  Repeat
     * until eventually there are no changes.
d1279 2
a1280 3
 *	Delete a range of characters from a B-tree.  The caller
 *	must make sure that the final newline of the B-tree is
 *	never deleted.
d1286 4
a1289 5
 *	Information is deleted from the B-tree.  This can cause the
 *	internal structure of the B-tree to change.  Note: because
 *	of changes to the B-tree structure, the indices pointed
 *	to by index1Ptr and index2Ptr should not be used after this
 *	procedure returns.
d1296 12
a1307 10
    TkTextBTree tree;                   /* Tree to delete from */
    register TkTextIndex *index1Ptr;	/* Indicates first character that is
					 * to be deleted. */
    register TkTextIndex *index2Ptr;	/* Indicates character just after the
					 * last one that is to be deleted. */
{
    TkTextSegment *prevPtr;		/* The segment just before the start
					 * of the deletion range. */
    TkTextSegment *lastPtr;		/* The segment just after the end
					 * of the deletion range. */
d1314 1
a1314 1
    
d1316 2
a1317 2
     * Tricky point:  split at index2Ptr first;  otherwise the split
     * at index2Ptr may invalidate segPtr and/or prevPtr.
d1347 3
a1349 3
	     * We just ran off the end of a line.  First find the
	     * next line, then go back to the old line and delete it
	     * (unless it's the starting line for the range).
d1368 5
a1372 1
		/* Check if we need to adjust any partial clients */
d1375 1
d1379 5
a1383 4
			    /* 
			     * We're deleting a line which is the start
			     * or end of a current client.  This means
			     * we need to adjust that client.
d1385 1
d1432 2
a1433 2
	     * This segment refuses to die.  Move it to prevPtr and
	     * advance prevPtr if the segment has left gravity.
d1451 2
a1452 2
     * If the beginning and end of the deletion range are in different
     * lines, join the two lines together and discard the ending line.
d1483 7
a1489 6
	/* 
	 * Check if we need to adjust any partial clients.  In this case
	 * if we're deleting the line, we actually move back to the
	 * previous line for our (start,end) storage.  We do this
	 * because we still want the portion of the second line that
	 * still exists to be in the start,end range.
d1491 1
d1498 5
a1502 4
		    /* 
		     * We're deleting a line which is the start
		     * or end of a current client.  This means
		     * we need to adjust that client.
d1504 1
d1529 4
a1532 4
     * This line now needs to have its height recalculated.  For safety,
     * ensure we don't call this function with the last artificial line
     * of text.  I _believe_ that it isn't possible to get this far with
     * the last line, but it is good to be safe.
d1534 1
d1536 3
a1538 3
	TkTextInvalidateLineMetrics(treePtr->sharedTextPtr, NULL, 
				    index1Ptr->linePtr, changeToLineCount, 
				    TK_TEXT_INVALIDATE_DELETE);
d1540 1
a1540 1
    
d1559 2
a1560 2
 *	The return value is a pointer to the line structure for the
 *	line whose index is "line", or NULL if no such line exists.
d1570 3
a1572 4
    TkTextBTree tree;			/* B-tree in which to find line. */
    CONST TkText *textPtr;		/* Relative to this client of the 
                          		 * B-tree */
    int line;				/* Index of desired line. */
d1581 1
a1581 1
    
d1586 3
a1588 3
    
    /* 
     * Check for the any start/end offset for this text widget 
d1590 1
d1592 1
a1592 1
        if (textPtr->start != NULL) {
d1604 1
a1604 1
    
d1606 1
a1606 2
     * Work down through levels of the tree until a node is found at
     * level 0.
d1642 8
a1649 9
 *	The return value is a pointer to the line structure for the
 *	line which contains the pixel "pixels", or NULL if no such 
 *	line exists.  If the first line is of height 20, then pixels
 *	0-19 will return it, and pixels = 20 will return the next
 *	line.
 *	
 *	If pixelOffset is non-NULL, it is set to the amount by which 
 *	'pixels' exceeds the first pixel located on the returned
 *	line.  This should always be non-negative.
d1659 4
a1662 5
    TkTextBTree tree;			/* B-tree to use. */
    CONST TkText *textPtr;              /* Relative to this client of the 
                          		 * B-tree */
    int pixels;				/* Pixel index of desired line. */
    int *pixelOffset;                   /* Used to return offset */
d1668 1
a1668 1
    
d1678 1
a1678 1
    
d1680 1
a1680 2
     * Work down through levels of the tree until a node is found at
     * level 0.
d1698 1
a1698 1
    for (linePtr = nodePtr->children.linePtr; 
d1707 1
a1707 1
        *pixelOffset = pixels;
d1717 3
a1719 3
 *	Given an existing line in a B-tree, this procedure locates the
 *	next line in the B-tree.  This procedure is used for scanning
 *	through the B-tree.
d1722 2
a1723 2
 *	The return value is a pointer to the line that immediately
 *	follows linePtr, or NULL if there is no such line.
d1733 3
a1735 4
    CONST TkText *textPtr;              /* Next line in the context of
                                         * this client */
    register TkTextLine *linePtr;	/* Pointer to existing line in
					 * B-tree. */
d1746 1
a1746 1
    
d1749 2
a1750 2
     * Search up the tree for the next node, then search down from that
     * node to find the first line.
d1773 3
a1775 3
 *	Given an existing line in a B-tree, this procedure locates the
 *	previous line in the B-tree.  This procedure is used for scanning
 *	through the B-tree in the reverse direction.
d1778 2
a1779 2
 *	The return value is a pointer to the line that immediately
 *	preceeds linePtr, or NULL if there is no such line.
d1789 3
a1791 4
    TkText *textPtr;                    /* Relative to this client of the 
                          		 * B-tree */
    register TkTextLine *linePtr;	/* Pointer to existing line in
					 * B-tree. */
d1798 1
a1798 1
        return NULL;
d1800 1
a1800 1
    
d1804 2
a1805 1
    prevPtr = linePtr->parentPtr->children.linePtr;	/* First line at leaf */
d1821 1
d1830 1
a1830 1
    for (node2Ptr = nodePtr->parentPtr->children.nodePtr; ; 
d1852 7
a1858 7
 *	Given a pointer to a line in a B-tree, return the numerical
 *	pixel index of the top of that line (i.e. the result does
 *	not include the height of the given line).
 *	
 *	Since the last line of text (the artificial one) has zero
 *	height by defintion, calling this with the last line will
 *	return the total number of pixels in the widget.
d1872 2
a1873 4
    CONST TkText *textPtr;              /* Relative to this client of the 
                          		 * B-tree */
    TkTextLine *linePtr;		/* Pointer to existing line in
					 * B-tree. */
d1881 1
a1881 2
     * First count how many pixels precede this line in its level-0
     * node.
d1895 2
a1896 3
     * Now work up through the levels of the tree one at a time,
     * counting how many pixels are in nodes preceding the current
     * node.
d1918 2
a1919 2
 *	Given a pointer to a line in a B-tree, return the numerical
 *	index of that line.
d1933 2
a1934 4
    CONST TkText *textPtr;              /* Relative to this client of the 
                          		 * B-tree */
    TkTextLine *linePtr;		/* Pointer to existing line in
					 * B-tree. */
d1941 1
a1941 2
     * First count how many lines precede this one in its level-0
     * node.
d1955 2
a1956 3
     * Now work up through the levels of the tree one at a time,
     * counting how many lines are in nodes preceding the current
     * node.
d1970 1
a1970 1
        index -= TkBTreeLinesTo(NULL, textPtr->start);
d1980 1
a1980 2
 *	This procedure adds a new segment to a B-tree at a given
 *	location.
d1995 4
a1998 5
				 * B-tree.  Should be completely initialized
				 * by caller except for nextPtr field. */
    TkTextIndex *indexPtr;	/* Where to add segment:  it gets linked
				 * in just before the segment indicated
				 * here. */
d2021 1
a2021 1
 *	This procedure unlinks a segment from its line in a B-tree.
d2027 2
a2028 2
 *	SegPtr will be unlinked from linePtr.  The segment itself
 *	isn't modified by this procedure.
d2036 2
a2037 3
    TkTextSegment *segPtr;		/* Segment to be unlinked. */
    TkTextLine *linePtr;		/* Line that currently contains
					 * segment. */
d2047 1
a2047 1
	    
d2049 3
a2051 3
		/* 
		 * Two logical lines merged into one display line 
		 * through eliding of a newline
d2053 1
d2068 2
a2069 2
 *	Turn a given tag on or off for a given range of characters in
 *	a B-tree of text.
d2072 3
a2074 3
 *	1 if the tags on any characters in the range were changed,
 *	and zero otherwise (i.e. if the tag was already absent (add = 0)
 *	or present (add = 1) on the index range in question).
d2077 5
a2081 6
 *	The given tag is added to the given range of characters
 *	in the tree or removed from all those characters, depending
 *	on the "add" argument.  The structure of the btree is modified
 *	enough that index1Ptr and index2Ptr are no longer valid after
 *	this procedure returns, and the indexes may be modified by
 *	this procedure.
d2088 9
a2096 8
    register TkTextIndex *index1Ptr;	/* Indicates first character in
					 * range. */
    register TkTextIndex *index2Ptr;	/* Indicates character just after the
					 * last one in range. */
    TkTextTag *tagPtr;			/* Tag to add or remove. */
    int add;				/* One means add tag to the given
					 * range of characters;  zero means
					 * remove the tag from the range. */
d2104 1
a2104 1
    
d2106 2
a2107 3
     * See whether the tag is present at the start of the range.  If
     * the state doesn't already match what we want then add a toggle
     * there.
d2129 3
a2131 3
     * Scan the range of characters and delete any internal tag
     * transitions.  Keep track of what the old state was at the end
     * of the range, and add a toggle there if it's needed.
d2160 6
a2165 7
	 * The code below is a bit tricky.  After deleting a toggle
	 * we eventually have to call CleanupLine, in order to allow
	 * character segments to be merged together.  To do this, we
	 * remember in cleanupLinePtr a line that needs to be
	 * cleaned up, but we don't clean it up until we've moved
	 * on to a different line.  That way the cleanup process
	 * won't goof up segPtr.
d2172 1
d2174 2
a2175 3
	 * Quick hack.  ChangeNodeToggleCount may move the tag's root
	 * location around and leave the search in the void.  This resets
	 * the search.
d2177 1
d2200 2
a2201 2
     * Cleanup cleanupLinePtr and the last line of the range, if
     * these are different.
d2205 1
a2205 1
        CleanupLine(cleanupLinePtr);
d2222 3
a2224 3
 *	This procedure increments or decrements the toggle count for
 *	a particular tag in a particular node and all its ancestors
 *	up to the per-tag root node.
d2230 3
a2232 3
 *	The toggle count for tag is adjusted up or down by "delta" in
 *	nodePtr.  This routine maintains the tagRootPtr that identifies
 *	the root node for the tag, moving it up or down the tree as needed.
d2239 5
a2243 5
    register Node *nodePtr;		/* Node whose toggle count for a tag
					 * must be changed. */
    TkTextTag *tagPtr;			/* Information about tag. */
    int delta;				/* Amount to add to current toggle
					 * count for tag (may be negative). */
d2247 1
a2247 1
    int rootLevel;			/* Level of original tag root */
d2256 2
a2257 2
     * Note the level of the existing root for the tag so we can detect
     * if it needs to be moved because of the toggle count change.
d2270 1
a2270 1
	 * See if there's already an entry for this tag for this node.  If so,
d2273 1
a2273 1
    
d2297 1
a2297 1
    
d2312 1
a2312 1
    
d2314 1
a2314 1
    
d2317 6
a2322 7
		 * node, but it isn't at this node.  Move the tag root up
		 * a level, in the hopes that it will now cover this node
		 * as well as the old root (if not, we'll move it up again
		 * the next time through the loop).  To push it up one level
		 * we copy the original toggle count into the summary
		 * information at the old root and change the root to its
		 * parent node.
d2324 1
a2324 1
    
d2344 2
a2345 2
     * If we've decremented the toggle count, then it may be necessary
     * to push the tag root down one or more levels.
d2358 2
a2359 2
	 * See if a single child node accounts for all of the tag's
	 * toggles.  If so, push the root down one level.
d2408 4
a2411 4
 *	The return value is a pointer to the first tag toggle segment
 *	for the tag.  This can be either a tagon or tagoff segments because
 *	of the way TkBTreeAdd removes a tag.
 *	Sets *indexPtr to be the index of the tag toggle.
d2421 3
a2423 3
    TkTextBTree tree;			/* Tree to search within */
    TkTextTag *tagPtr;			/* Tag to search for. */
    TkTextIndex *indexPtr;		/* Return - index information */
d2437 2
a2438 2
     * Search from the root of the subtree that contains the tag down
     * to the level 0 node.
d2451 1
a2451 1
	gotNodeWithTag:
d2467 2
a2468 2
		 * It is possible that this is a tagoff tag, but that
		 * gets cleaned up later.
d2470 1
d2489 4
a2492 4
 *	The return value is a pointer to the last tag toggle segment
 *	for the tag.  This can be either a tagon or tagoff segments because
 *	of the way TkBTreeAdd removes a tag.
 *	Sets *indexPtr to be the index of the tag toggle.
d2502 3
a2504 3
    TkTextBTree tree;			/* Tree to search within */
    TkTextTag *tagPtr;			/* Tag to search for. */
    TkTextIndex *indexPtr;		/* Return - index information */
d2518 2
a2519 2
     * Search from the root of the subtree that contains the tag down
     * to the level 0 node.
d2539 1
d2546 1
a2546 1
		segPtr != NULL; 
d2572 2
a2573 2
 *	This procedure sets up a search for tag transitions involving
 *	a given tag (or all tags) in a given range of the text.
d2579 6
a2584 6
 *	The information at *searchPtr is set up so that subsequent calls
 *	to TkBTreeNextTag or TkBTreePrevTag will return information about the
 *	locations of tag transitions.  Note that TkBTreeNextTag or 
 *	TkBTreePrevTag must be called to get the first transition.
 *	Note: unlike TkBTreeNextTag and TkBTreePrevTag, this routine does not
 *	guarantee that searchPtr->curIndex is equal to *index1Ptr.  It may be
d2592 9
a2600 10
    TkTextIndex *index1Ptr;		/* Search starts here.  Tag toggles
					 * at this position will not be
					 * returned. */
    TkTextIndex *index2Ptr;		/* Search stops here.  Tag toggles
					 * at this position *will* be
					 * returned. */
    TkTextTag *tagPtr;			/* Tag to search for.  NULL means
					 * search for any tag. */
    register TkTextSearch *searchPtr;	/* Where to store information about
					 * search's progress. */
d2607 2
a2608 2
     * Find the segment that contains the first toggle for the tag.  This
     * may become the starting point in the search.
d2614 2
a2615 2
	 * Even though there are no toggles, the display code still
	 * uses the search curIndex, so initialize that anyway.
d2648 2
a2649 2
	 * first.  A search does not return a toggle at the very start of
	 * the range, unless the range is artificially moved up to index0.
d2651 2
a2652 1
	if (((index1Ptr == &index0) && 
d2654 1
a2654 1
	    ((index1Ptr != &index0) && 
d2656 1
a2656 1
		searchPtr->linesLeft = 0;
d2666 4
a2669 5
 *	This procedure sets up a search backwards for tag transitions involving
 *	a given tag (or all tags) in a given range of the text.  In the
 *	normal case the first index (*index1Ptr) is beyond the second
 *	index (*index2Ptr).
 *	
d2675 7
a2681 7
 *	The information at *searchPtr is set up so that subsequent calls
 *	to TkBTreePrevTag will return information about the
 *	locations of tag transitions.  Note that TkBTreePrevTag must be called
 *	to get the first transition.
 *	Note: unlike TkBTreeNextTag and TkBTreePrevTag, this routine does not
 *	guarantee that searchPtr->curIndex is equal to *index1Ptr.  It may be
 *	less than that if *index1Ptr is greater than the last tag transition.
d2688 9
a2696 10
    TkTextIndex *index1Ptr;		/* Search starts here.  Tag toggles
					 * at this position will not be
					 * returned. */
    TkTextIndex *index2Ptr;		/* Search stops here.  Tag toggles
					 * at this position *will* be
					 * returned. */
    TkTextTag *tagPtr;			/* Tag to search for.  NULL means
					 * search for any tag. */
    register TkTextSearch *searchPtr;	/* Where to store information about
					 * search's progress. */
d2704 2
a2705 2
     * Find the segment that contains the last toggle for the tag.  This
     * may become the starting point in the search.
d2711 2
a2712 2
	 * Even though there are no toggles, the display code still
	 * uses the search curIndex, so initialize that anyway.
d2731 2
a2732 2
	TkTextIndexBackChars(NULL, index1Ptr, 1, &searchPtr->curIndex, 
			     COUNT_INDICES);
d2739 2
a2740 2
     * Adjust the end of the search so it does find toggles that are right
     * at the second index specified by the user.
d2773 4
a2776 4
 *	Once a tag search has begun, successive calls to this procedure
 *	return successive tag toggles.  Note:  it is NOT SAFE to call this
 *	procedure if characters have been inserted into or deleted from
 *	the B-tree since the call to TkBTreeStartSearch.
d2780 5
a2784 5
 *	criteria specified in the call to TkBTreeStartSearch;  in this
 *	case searchPtr->curIndex gives the toggle's position and
 *	searchPtr->curTagPtr points to its segment.  0 is returned if
 *	no more matching tag transitions were found; in this case
 *	searchPtr->curIndex is the same as searchPtr->stopIndex.
d2795 4
a2798 3
    register TkTextSearch *searchPtr;	/* Information about search in
					 * progress;  must have been set up by
					 * call to TkBTreeStartSearch. */
d2809 3
a2811 3
     * The outermost loop iterates over lines that may potentially contain
     * a relevant tag transition, starting from the current segment in
     * the current line.
d2835 1
a2835 1
    
d2838 1
a2838 2
	 * node.  If so, go back to the top of the loop to search the next
	 * one.
d2855 1
a2855 1
    
d2859 1
a2859 1
	 * its subtree.  Be sure to update linesLeft as we skip over large
d2862 1
a2862 1
    
d2881 1
a2881 1
    
d2884 2
a2885 2
	 * transition.  Now search down (and across) through that subtree
	 * to find the first level-0 node that has a relevant tag transition.
d2887 2
a2888 2
    
	gotNodeWithTag:
d2896 4
d2908 1
a2908 1
	    nextChild:
d2911 1
a2911 1
    
d2914 1
a2914 1
	 * a relevant tag transition.  Set up line information and go back to
d2927 1
a2927 1
    searchOver:
d2938 4
a2941 5
 *	Once a tag search has begun, successive calls to this procedure
 *	return successive tag toggles in the reverse direction.
 *	Note:  it is NOT SAFE to call this
 *	procedure if characters have been inserted into or deleted from
 *	the B-tree since the call to TkBTreeStartSearch.
d2945 5
a2949 5
 *	criteria specified in the call to TkBTreeStartSearch;  in this
 *	case searchPtr->curIndex gives the toggle's position and
 *	searchPtr->curTagPtr points to its segment.  0 is returned if
 *	no more matching tag transitions were found; in this case
 *	searchPtr->curIndex is the same as searchPtr->stopIndex.
d2960 4
a2963 3
    register TkTextSearch *searchPtr;	/* Information about search in
					 * progress;  must have been set up by
					 * call to TkBTreeStartSearch. */
d2970 1
a2970 1
    int pastLast;			/* Saw last marker during scan */
d2978 4
a2981 4
     * The outermost loop iterates over lines that may potentially contain
     * a relevant tag transition, starting from the current segment in
     * the current line.  "nextPtr" is maintained as the last segment in
     * a line that we can look at. 
d2988 1
d2991 1
a2991 1
	    /* 
d2994 2
a2995 1
	    pastLast = 1; 
d2999 1
d3011 1
a3011 1
	        prevPtr = NULL;   /* Segments earlier than last don't count */
d3019 2
a3020 2
		 * We found a segment that is before the stopping index.
		 * Note that it is OK if prevPtr == lastPtr.
d3022 1
d3030 1
a3030 1
    
d3038 1
a3038 1
	 * node.  If so, go back to the top of the loop to search the previous
d3056 1
a3056 1
    
d3059 6
a3064 6
	 * for the previous node that has a relevant tag transition somewhere in
	 * its subtree.  The search and line counting is trickier with/out
	 * back pointers. We'll scan all the nodes under a parent up to
	 * the current node, searching all of them for tag state.  The last
	 * one we find, if any, is recorded in prevNodePtr, and any nodes
	 * past prevNodePtr that don't have tag state increment linesSkipped.
d3066 1
a3066 1
    
d3082 1
a3082 1
		keepLooking:
d3096 1
a3096 1
    
d3099 2
a3100 2
	 * transition.  Now search down (and across) through that subtree
	 * to find the last level-0 node that has a relevant tag transition.
d3102 2
a3103 2
    
	gotNodeWithTag:
d3119 1
a3119 1
		keepLooking2:
d3128 1
a3128 1
    
d3131 2
a3132 2
	 * a relevant tag transition.  Set up line information and go back to
	 * the beginning of the loop to search through lines.  We start with
d3149 1
a3149 1
    searchOver:
d3163 2
a3164 2
 *	The return value is 1 if the given tag is in effect at the
 *	character given by linePtr and ch, and 0 otherwise.
d3174 3
a3176 3
    CONST TkTextIndex *indexPtr;	/* Indicates a character position at
					 * which to check for a tag. */
    TkTextTag *tagPtr;			/* Tag of interest. */
d3184 4
a3187 4
    /* 
     * Check for toggles for the tag in indexPtr's line but before
     * indexPtr.  If there is one, its type indicates whether or
     * not the character is tagged.
d3205 2
a3206 3
     * No toggle in this line.  Look for toggles for the tag in lines
     * that are predecessors of indexPtr->linePtr but under the same
     * level-0 node.
d3226 3
a3228 3
     * No toggle in this node.  Scan upwards through the ancestors of
     * this node, counting the number of toggles of the given tag in
     * siblings that precede that node.
d3237 1
a3237 1
	for (siblingPtr = nodePtr->parentPtr->children.nodePtr; 
d3252 2
a3253 2
     * An odd number of toggles means that the tag is present at the
     * given point.
d3264 2
a3265 2
 *	Return information about all of the tags that are associated
 *	with a particular character in a B-tree of text.
d3269 6
a3274 6
 *	information for each of the tags that is associated with
 *	the character at the position given by linePtr and ch.  The
 *	word at *numTagsPtr is filled in with the number of pointers
 *	in the array.  It is up to the caller to free the array by
 *	passing it to free.  If there are no tags at the given character
 *	then a NULL pointer is returned and *numTagsPtr will be set to 0.
d3285 5
a3289 5
    CONST TkTextIndex *indexPtr;/* Indicates a particular position in
				 * the B-tree. */
    CONST TkText *textPtr;      /* If non-NULL, then only return tags
                                 * for this text widget (when there are
                                 * peer widgets). */
d3309 1
a3309 2
     * Record tag toggles within the line of indexPtr but preceding
     * indexPtr.
d3322 1
a3322 1
	
d3324 3
a3326 3
	    /* 
	     * Two logical lines merged into one display line 
	     * through eliding of a newline
d3328 1
d3352 2
a3353 2
     * For each node in the ancestry of this line, record tag toggles
     * for all siblings that precede that node.
d3361 1
a3361 1
	for (siblingPtr = nodePtr->parentPtr->children.nodePtr; 
d3374 4
a3377 4
     * Go through the tag information and squash out all of the tags
     * that have even toggle counts (these tags exist before the point
     * of interest, but not at the desired character itself).  Also
     * squash out all tags that don't belong to the requested widget.
d3405 12
a3416 14
 *	GetStyle(textPtr, indexPtr).  Just need to keep track of
 *	invisibility settings for each priority, pick highest one active
 *	at end.
 *	
 *	Note that this returns all elide information up to and including
 *	the given index (quite obviously).  However, this does mean that
 *	if indexPtr is a line-start and one then iterates from the beginning
 *	of that line forwards, one will actually revisit the segPtrs of
 *	size zero (for tag toggling, for example) which have already been
 *	seen here.
 *	
 *	For this reason we fill in the fields 'segPtr' and 'segOffset'
 *	of elideInfo, enabling our caller easily to calculate
 *	incremental changes from where we left off.
d3420 1
a3420 1
 *	
d3433 5
a3437 5
    CONST TkTextIndex *indexPtr;/* The character in the text for which
				 * display information is wanted. */
    TkTextElideInfo *elideInfo; /* NULL or a pointer to a structure in
                                 * which indexPtr's elide state will
                                 * be stored and returned. */
d3447 1
a3447 1
    
d3453 1
a3453 1
    
d3459 4
a3462 1
    /* Almost always avoid malloc, so stay out of system calls */
d3464 4
a3467 4
	infoPtr->tagCnts = (int *)ckalloc((unsigned)sizeof(int) 
					  * infoPtr->numTags);
	infoPtr->tagPtrs = (TkTextTag **)ckalloc((unsigned)sizeof(TkTextTag*) 
						 * infoPtr->numTags);
d3469 1
a3469 1
 
d3475 1
a3475 2
     * Record tag toggles within the line of indexPtr but preceding
     * indexPtr.
d3490 1
a3490 1
       
d3494 3
a3496 3
	    /* 
	     * Two logical lines merged into one display line 
	     * through eliding of a newline
d3498 1
d3503 4
a3506 3
    /* 
     * Store the first segPtr we haven't examined completely
     * so that our caller knows where to start.
d3508 1
d3511 1
a3511 1
    
d3518 2
a3519 2
	 siblingLinePtr != indexPtr->linePtr;
	 siblingLinePtr = siblingLinePtr->nextPtr) {
d3521 1
a3521 1
	     segPtr = segPtr->nextPtr) {
d3534 2
a3535 2
     * For each node in the ancestry of this line, record tag toggles
     * for all siblings that precede that node.
d3539 1
a3539 1
	 nodePtr = nodePtr->parentPtr) {
d3543 2
a3544 2
	for (siblingPtr = nodePtr->parentPtr->children.nodePtr; 
	     siblingPtr != nodePtr; siblingPtr = siblingPtr->nextPtr) {
d3546 1
a3546 1
		 summaryPtr = summaryPtr->nextPtr) {
d3551 2
a3552 1
			infoPtr->tagCnts[tagPtr->priority] += summaryPtr->toggleCount;
d3560 2
a3561 2
     * Now traverse from highest priority to lowest, 
     * take elided value from first odd count (= on)
d3567 4
a3570 1
	    /* Who would make the selection elided? */
d3577 5
a3581 1
	    /* Note: i == infoPtr->tagPtrs[i]->priority */
d3590 1
a3590 1
        if (LOTSA_TAGS < infoPtr->numTags) {
d3594 1
a3594 1
    
d3606 2
a3607 2
 *	This is a utility procedure used to free up any memory
 *	allocated by the TkTextIsElided function above.
d3621 1
a3621 1
                                 * structure. */
d3634 3
a3636 3
 *	This is a utility procedure used by TkBTreeGetTags.  It
 *	increments the count for a particular tag, adding a new
 *	entry for that tag if there wasn't one previously.
d3642 2
a3643 2
 *	The information at *tagInfoPtr may be modified, and the arrays
 *	may be reallocated to make them larger.
d3667 2
a3668 3
     * There isn't currently an entry for this tag, so we have to
     * make a new one.  If the arrays are full, then enlarge the
     * arrays first.
d3677 1
a3677 1
		(newSize*sizeof(TkTextTag *)));
d3700 2
a3701 2
 *	This procedure runs a set of consistency checks over a B-tree
 *	and panics if any inconsistencies are found.
d3707 2
a3708 2
 *	If a structural defect is found, the procedure panics with an
 *	error message.
d3730 2
a3731 1
    for (entryPtr = Tcl_FirstHashEntry(&treePtr->sharedTextPtr->tagTable, &search);
d3738 1
a3738 1
		    tagPtr->name, tagPtr->toggleCount);
d3780 1
a3780 1
		tagPtr->toggleCount, tagPtr->name, count);
d3785 1
a3785 1
     * Call a recursive procedure to do the main body of checks.
d3792 2
a3793 2
     * Make sure that there are at least two lines in the text and
     * that the last line has no characters except a newline.
d3814 2
a3815 3
	 * It's OK to toggle a tag off in the last line, but
	 * not to start a new range.  It's also OK to have marks
	 * in the last line.
d3841 3
a3843 3
 *	This procedure is called as part of consistency checking for
 *	B-trees:  it checks several aspects of a node and also runs
 *	checks recursively on the node's children.
d3849 2
a3850 2
 *	If anything suspicious is found in the tree structure, the
 *	procedure panics.
d3857 3
a3859 4
    register Node *nodePtr;		/* Node whose subtree should be
					 * checked. */
    int references;                     /* Number of referring widgets
                                         * which have pixel counts. */
d3921 1
a3921 1
	        numPixels[i] += linePtr->pixels[2*i];
d3976 1
a3976 1
        
d4030 3
a4032 3
 *	This procedure is called when a node of a B-tree appears to be
 *	out of balance (too many children, or too few).  It rebalances
 *	that node and all of its ancestors in the tree.
d4045 2
a4046 2
    BTree *treePtr;			/* Tree that is being rebalanced. */
    register Node *nodePtr;		/* Node that may be out of balance. */
d4049 2
a4050 3
     * Loop over the entire ancestral chain of the node, working up
     * through the tree one node at a time until the root node has
     * been processed.
d4059 4
a4062 4
	 * Check to see if the node has too many children.  If it does,
	 * then split off all but the first MIN_CHILDREN into a separate
	 * node following the original one.  Then repeat until the
	 * node has a decent size.
d4068 2
a4069 2
		 * If the node being split is the root node, then make a
		 * new root node above it first.
d4071 1
a4071 1
    
d4081 2
a4082 2
		    newPtr->numPixels = (int*) ckalloc(sizeof(int)*
						       treePtr->pixelReferences);
d4084 1
a4084 1
		        newPtr->numPixels[i] = nodePtr->numPixels[i];
d4090 2
a4091 2
		newPtr->numPixels = (int*) ckalloc(sizeof(int)*
						   treePtr->pixelReferences);
d4130 2
a4131 2
	    Node *halfwayNodePtr = NULL;	/* Initialization needed only */
	    TkTextLine *halfwayLinePtr = NULL;	/* to prevent cc warnings. */
d4135 5
a4139 5
	     * Too few children for this node.  If this is the root then,
	     * it's OK for it to have less than MIN_CHILDREN children
	     * as long as it's got at least two.  If it has only one
	     * (and isn't at level 0), then chop the root node out of
	     * the tree and use its child as the new root.
d4153 2
a4154 2
	     * Not the root.  Make sure that there are siblings to
	     * balance with.
d4163 2
a4164 2
	     * Find a sibling neighbor to borrow from, and arrange for
	     * nodePtr to be the earlier of the pair.
d4178 4
a4181 5
	     * We're going to either merge the two siblings together
	     * into one node or redivide the children among them to
	     * balance their loads.  As preparation, join their two
	     * child lists into a single list and remember the half-way
	     * point in the list.
d4263 4
a4266 4
 *	This procedure is called to recompute all the counts in a node
 *	(tags, child information, etc.) by scanning the information in
 *	its descendants.  This procedure is called during rebalancing
 *	when a node's child structure has changed.
d4272 3
a4274 4
 *	The tag counts for nodePtr are modified to reflect its current
 *	child structure, as are its numChildren and numLines fields.
 *	Also, all of the childrens' parentPtr fields are made to point
 *	to nodePtr.
d4281 3
a4283 3
    register BTree *treePtr;            /* The whole B-tree */
    register Node *nodePtr;		/* Node whose tag summary information
					 * must be recomputed. */
d4291 1
a4291 1
    
d4293 2
a4294 2
     * Zero out all the existing counts for the node, but don't delete
     * the existing Summary records (most of them will probably be reused).
d4308 2
a4309 3
     * Scan through the children, adding the childrens' tag counts into
     * the node's tag counts and adding new Summary structures if
     * necessary.
d4318 1
a4318 1
	        nodePtr->numPixels[ref] += linePtr->pixels[2*ref];
d4379 2
a4380 2
     * The node with the children that account for all the tags toggles
     * have no summary information, and they become the tagRootPtr for the tag.
d4385 1
a4385 1
	if (summaryPtr->toggleCount > 0 && 
d4389 2
a4390 2
		 * The tag's root node split and some toggles left.
		 * The tag root must move up a level.
d4392 1
d4404 1
d4424 2
a4425 2
 *	This procedure returns a count of the number of logical lines of
 *	text present in a given B-tree.
d4428 3
a4430 3
 *	The return value is a count of the number of usable lines
 *	in tree (i.e. it doesn't include the dummy line that is just
 * 	used to mark the end of the tree).
d4440 2
a4441 3
    TkTextBTree tree;			/* Information about tree. */
    CONST TkText *textPtr;              /* Relative to this client of the 
					 * B-tree */
d4445 1
a4445 1
    
d4463 2
a4464 2
 *	This procedure returns a count of the number of pixels of
 *	text present in a given widget's B-tree representation.
d4467 5
a4471 5
 *	The return value is a count of the number of usable pixels in
 *	tree (since the dummy line used to mark the end of the B-tree is
 *	maintained with zero height, as are any lines that are before or
 *	after the '-start -end' range of the text widget in question,
 *	the number stored at the root is the number we want).
d4481 2
a4482 3
    TkTextBTree tree;                   /* The B-tree */
    CONST TkText *textPtr;              /* Relative to this client of the 
                          		 * B-tree */
d4493 1
a4493 1
 *	This procedure implements splitting for character segments.
d4496 2
a4497 3
 *	The return value is a pointer to a chain of two segments
 *	that have the same characters as segPtr except split
 *	among the two segments.
d4507 3
a4509 3
    TkTextSegment *segPtr;		/* Pointer to segment to split. */
    int index;				/* Position within segment at which
					 * to split. */
d4534 2
a4535 2
 *	This procedure merges adjacent character segments into
 *	a single character segment, if possible.
d4538 2
a4539 2
 *	The return value is a pointer to the first segment in
 *	the (new) list of segments that used to start with segPtr.
d4550 3
a4552 4
    TkTextSegment *segPtr;		/* Pointer to first of two adjacent
					 * segments to join. */
    TkTextLine *linePtr;		/* Line containing segments (not
					 * used). */
d4577 1
a4577 1
 *	This procedure is invoked to delete a character segment.
d4591 5
a4595 5
    TkTextSegment *segPtr;		/* Segment to delete. */
    TkTextLine *linePtr;		/* Line containing segment. */
    int treeGone;			/* Non-zero means the entire tree is
					 * being deleted, so everything must
					 * get cleaned up. */
d4606 2
a4607 2
 *	This procedure is invoked to perform consistency checks
 *	on character segments.
d4613 1
a4613 2
 *	If the segment isn't inconsistent then the procedure
 *	panics.
d4625 4
a4628 5
     * Make sure that the segment contains the number of
     * characters indicated by its header, and that the last
     * segment in a line ends in a newline.  Also make sure
     * that there aren't ever two character segments adjacent
     * to each other:  they should be merged together.
d4641 2
a4642 4
    } else {
	if (segPtr->nextPtr->typePtr == &tkTextCharType) {
	    Tcl_Panic("CharCheckProc: adjacent character segments weren't merged");
	}
d4651 1
a4651 1
 *	This procedure is invoked to delete toggle segments.
d4654 2
a4655 2
 *	Returns 1 to indicate that the segment may not be deleted,
 *	unless the entire B-tree is going away.
d4658 2
a4659 3
 *	If the tree is going away then the toggle's memory is
 *	freed;  otherwise the toggle counts in nodes above the
 *	segment get updated.
d4666 5
a4670 5
    TkTextSegment *segPtr;		/* Segment to check. */
    TkTextLine *linePtr;		/* Line containing segment. */
    int treeGone;			/* Non-zero means the entire tree is
					 * being deleted, so everything must
					 * get cleaned up. */
d4678 5
a4682 5
     * This toggle is in the middle of a range of characters that's
     * being deleted.  Refuse to die.  We'll be moved to the end of
     * the deleted range and our cleanup procedure will be called
     * later.  Decrement node toggle counts here, and set a flag
     * so we'll re-increment them in the cleanup procedure.
d4698 3
a4700 3
 *	This procedure is called when a toggle is part of a line that's
 *	been modified in some way.  It's invoked after the
 *	modifications are complete.
d4703 3
a4705 4
 *	The return value is the head segment in a new list
 *	that is to replace the tail of the line that used to
 *	start at segPtr.  This allows the procedure to delete
 *	or modify segPtr.
d4708 3
a4710 3
 *	Toggle counts in the nodes above the new line will be
 *	updated if they're not already.  Toggles may be collapsed
 *	if there are duplicate toggles at the same position.
d4724 4
a4727 4
     * If this is a toggle-off segment, look ahead through the next
     * segments to see if there's a toggle-on segment for the same tag
     * before any segments with non-zero size.  If so then the two
     * toggles cancel each other;  remove them both.
d4767 2
a4768 2
 *	This procedure is invoked when a toggle segment is about
 *	to move from one line to another.
d4796 2
a4797 2
 *	This procedure is invoked to perform consistency checks
 *	on toggle segments.
d4803 1
a4803 1
 *	If a consistency problem is found the procedure panics.
d4810 2
a4811 2
    TkTextSegment *segPtr;		/* Segment to check. */
    TkTextLine *linePtr;		/* Line containing segment. */
d4840 8
@


1.15
log
@text widget 'peer' subcommand -- TIP#169 implementation
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.14 2004/06/07 16:23:52 vincentdarley Exp $
d922 1
a922 1
TkBTreeAdjustPixelHeight(textPtr, linePtr, newPixelHeight)
d927 8
d958 11
d1183 8
a1190 5
    int count;

    for (count = indexPtr->byteIndex, prevPtr = NULL,
	    segPtr = indexPtr->linePtr->segPtr; segPtr != NULL;
	    count -= segPtr->size, prevPtr = segPtr, segPtr = segPtr->nextPtr) {
d1206 15
d2054 12
a2065 3
	for (prevPtr = linePtr->segPtr; prevPtr->nextPtr != segPtr;
		prevPtr = prevPtr->nextPtr) {
	    /* Empty loop body. */
d3300 1
d3317 4
a3320 3
    for (index = 0, segPtr = indexPtr->linePtr->segPtr;
	    (index + segPtr->size) <= indexPtr->byteIndex;
	    index += segPtr->size, segPtr = segPtr->nextPtr) {
d3325 11
d3451 1
d3482 4
a3485 3
    for (index = 0, segPtr = indexPtr->linePtr->segPtr;
	 (index + segPtr->size) <= indexPtr->byteIndex;
	 index += segPtr->size, segPtr = segPtr->nextPtr) {
d3494 11
@


1.14
log
@text widget partial line height calculations fix
@
text
@d7 1
a7 1
 *
d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.13 2004/01/13 02:06:00 davygrvy Exp $
d22 23
d81 4
a84 3
    int numPixels;                      /* Total number of vertical
                                         * display pixels in the
                                         * subtree rooted here. */
d87 7
d104 3
a106 1
 * The data structure below defines an entire B-tree.
d111 11
a121 2
    TkText *textPtr;			/* Used to find tagTable in consistency
					 * checking code */
d161 4
d175 2
a176 1
static void		CheckNodeConsistency _ANSI_ARGS_((Node *nodePtr));
d185 4
a188 1
static void		RecomputeNodeCounts _ANSI_ARGS_((Node *nodePtr));
d200 8
d274 2
a275 2
TkBTreeCreate(textPtr)
    TkText *textPtr;
d292 1
d300 7
a306 6
    rootPtr->numPixels = textPtr->charHeight;
    linePtr->pixelHeight = textPtr->charHeight;
    linePtr->pixelCalculationEpoch = 0;
    /* The last line permanently has a pixel height of zero */
    linePtr2->pixelHeight = 0;
    linePtr2->pixelCalculationEpoch = 1;
d329 1
d331 60
a390 1
    treePtr->textPtr = textPtr;
d392 72
a463 1
    return (TkTextBTree) treePtr;
d474 1
a474 1
 *	The tree given by treePtr is deleted.  TreePtr should never
d485 1
a485 1
    TkTextBTree tree;			/* Pointer to tree to delete. */ 
d489 6
d496 4
d506 320
d842 1
a842 1
    register Node *nodePtr;
d856 1
d869 1
d922 2
a923 1
TkBTreeAdjustPixelHeight(linePtr, newPixelHeight)
d931 3
a933 2

    changeToPixelCount = newPixelHeight - linePtr->pixelHeight;
d941 1
a941 1
    nodePtr->numPixels += changeToPixelCount;
d945 1
a945 1
	nodePtr->numPixels += changeToPixelCount;
d948 1
a948 1
    linePtr->pixelHeight = newPixelHeight;
d950 1
a950 1
    return nodePtr->numPixels;
d972 2
a973 1
TkBTreeInsertChars(indexPtr, string)
d1000 1
a1000 1
    int changeToPixelCount;		/* Counts change to total number of
d1002 4
a1005 1

d1017 10
a1026 1
    changeToPixelCount = 0;
d1058 3
d1067 1
a1067 1
	 * later
d1069 5
a1073 2
	newLinePtr->pixelHeight = linePtr->pixelHeight;
	newLinePtr->pixelCalculationEpoch = 0;
a1078 1
	changeToPixelCount += newLinePtr->pixelHeight;
d1089 1
a1089 1
    TkTextInvalidateLineMetrics(((BTree*)indexPtr->tree)->textPtr, 
d1111 6
a1116 1
	nodePtr->numPixels += changeToPixelCount;
d1118 1
d1122 1
a1122 1
	Rebalance((BTree *) indexPtr->tree, nodePtr);
d1267 2
a1268 1
TkBTreeDeleteChars(index1Ptr, index2Ptr)
d1282 2
d1322 1
a1322 1
	    nextLinePtr = TkBTreeNextLine(curLinePtr);
d1332 3
a1334 1
		    nodePtr->numPixels -= curLinePtr->pixelHeight;
d1338 23
d1431 3
a1433 1
	    nodePtr->numPixels -= index2Ptr->linePtr->pixelHeight;
d1446 30
d1493 2
a1494 2
    if (TkBTreeNextLine(index1Ptr->linePtr) != NULL) {
        TkTextInvalidateLineMetrics(((BTree*)index1Ptr->tree)->textPtr, 
d1527 1
a1527 1
TkBTreeFindLine(tree, line)
d1529 2
a1535 1
    int linesLeft;
d1537 4
a1541 1
    linesLeft = line;
d1545 18
a1562 1

d1570 1
a1570 1
		nodePtr->numLines <= linesLeft;
d1575 1
a1575 1
	    linesLeft -= nodePtr->numLines;
d1583 1
a1583 1
    for (linePtr = nodePtr->children.linePtr; linesLeft > 0;
d1588 1
a1588 1
	linesLeft -= 1;
d1618 5
a1622 3
TkBTreeFindPixelLine(tree, pixels, pixelOffset)
    TkTextBTree tree;			/* B-tree in which to find line. */
    int pixels;				/* Index of desired line. */
d1628 2
a1629 2
    int pixelsLeft;

a1630 1
    pixelsLeft = pixels;
d1632 1
a1632 1
    if ((pixels < 0) || (pixels > nodePtr->numPixels)) {
d1636 1
a1636 1
    if (nodePtr->numPixels == 0) {
d1647 1
a1647 1
		nodePtr->numPixels <= pixelsLeft;
d1652 1
a1652 1
	    pixelsLeft -= nodePtr->numPixels;
d1661 1
a1661 1
	    linePtr->pixelHeight < pixelsLeft;
d1666 1
a1666 1
	pixelsLeft -= linePtr->pixelHeight;
d1669 1
a1669 1
        *pixelOffset = pixelsLeft;
d1694 3
a1696 1
TkBTreeNextLine(linePtr)
d1703 5
a1707 1
	return linePtr->nextPtr;
d1709 1
a1709 1

d1751 3
a1753 1
TkBTreePreviousLine(linePtr)
d1761 4
d1812 1
a1812 1
 * TkBTreePixels --
d1833 3
a1835 1
TkBTreePixels(linePtr)
d1840 1
a1840 1
    register Node *nodePtr, *parentPtr, *nodePtr2;
d1842 1
d1845 1
a1845 1
     * First count how many lines precede this one in its level-0
d1854 1
a1854 1
	    Tcl_Panic("TkBTreePixels couldn't find line");
d1856 1
a1856 1
	index += linePtr2->pixelHeight;
d1861 1
a1861 1
     * counting how many lines are in nodes preceding the current
d1867 1
d1871 1
a1871 1
		Tcl_Panic("TkBTreePixels couldn't find node");
d1873 1
a1873 1
	    index += nodePtr2->numPixels;
d1882 1
a1882 1
 * TkBTreeLineIndex --
d1898 3
a1900 1
TkBTreeLineIndex(linePtr)
d1918 1
a1918 1
	    Tcl_Panic("TkBTreeLineIndex couldn't find line");
d1934 1
a1934 1
		Tcl_Panic("TkBTreeLineIndex couldn't find node");
d1939 3
d2007 1
a2007 2
TkBTreeUnlinkSegment(tree, segPtr, linePtr)
    TkTextBTree tree;			/* Tree containing segment. */
d2605 2
a2606 2
    searchPtr->linesLeft = TkBTreeLineIndex(index2Ptr->linePtr) + 1
	    - TkBTreeLineIndex(index1Ptr->linePtr);
d2696 1
a2696 1
	TkTextIndexBackChars(NULL,index1Ptr, 1, &searchPtr->curIndex, 
d2708 1
a2708 1
    if ((TkBTreeLineIndex(index2Ptr->linePtr) == 0) &&
d2717 2
a2718 2
    searchPtr->linesLeft = TkBTreeLineIndex(index1Ptr->linePtr) + 1
	    - TkBTreeLineIndex(backOne.linePtr);
d3242 1
a3242 1
TkBTreeGetTags(indexPtr, numTagsPtr)
d3245 3
d3321 2
a3322 1
     * of interest, but not at the desired character itself).
d3327 5
a3331 2
	    tagInfo.tagPtrs[dst] = tagInfo.tagPtrs[src];
	    dst++;
d3349 1
a3349 1
 *	Specialized from TkBTreeGetTags(indexPtr, numTagsPtr) and
d3356 1
a3356 1
 *	indexPtr is a line-start and one then iterates from the beginning
d3360 4
d3403 1
a3403 1
    infoPtr->numTags = textPtr->numTags;
d3439 2
a3440 1

d3495 1
a3495 2
#ifndef ALWAYS_SHOW_SELECTION
	    /* who would make the selection elided? */
d3497 2
a3498 1
		    && !(textPtr->flags & GOT_FOCUS)) {
a3500 1
#endif
d3652 1
a3652 1
    for (entryPtr = Tcl_FirstHashEntry(&treePtr->textPtr->tagTable, &search);
d3710 1
a3710 1
    CheckNodeConsistency(treePtr->rootPtr);
d3778 1
a3778 1
CheckNodeConsistency(nodePtr)
d3781 2
d3788 3
a3790 1
    int numChildren, numLines, numPixels, toggleCount, minChildren;
d3807 9
a3815 1
    numPixels = 0;
d3843 3
a3845 1
	    numPixels += linePtr->pixelHeight;
d3857 1
a3857 1
	    CheckNodeConsistency(childNodePtr);
d3877 3
a3879 1
	    numPixels += childNodePtr->numPixels;
d3890 5
a3894 3
    if (numPixels != nodePtr->numPixels) {
	Tcl_Panic("CheckNodeConsistency: mismatch in numPixels (%d %d)",
	      numPixels, nodePtr->numPixels);
d3896 4
a3899 1

d4005 6
a4010 2
		    newPtr->numPixels = nodePtr->numPixels;
		    RecomputeNodeCounts(newPtr);
d4014 5
d4042 1
a4042 1
		RecomputeNodeCounts(nodePtr);
d4046 1
a4046 1
		    RecomputeNodeCounts(nodePtr);
d4157 1
a4157 1
		RecomputeNodeCounts(nodePtr);
d4177 2
a4178 2
	    RecomputeNodeCounts(nodePtr);
	    RecomputeNodeCounts(otherPtr);
d4206 2
a4207 1
RecomputeNodeCounts(nodePtr)
d4216 2
a4217 1

d4229 3
a4231 1
    nodePtr->numPixels = 0;
d4244 3
a4246 1
	    nodePtr->numPixels += linePtr->pixelHeight;
d4278 3
a4280 1
	    nodePtr->numPixels += childPtr->numPixels;
d4349 1
a4349 1
 *	This procedure returns a count of the number of lines of
d4364 1
a4364 1
TkBTreeNumLines(tree)
d4366 2
d4370 12
a4381 1
    return treePtr->rootPtr->numLines - 1;
d4390 1
a4390 1
 *	text present in a given B-tree.
d4394 4
a4397 3
 *	tree (since the dummy line used to mark the end of the tree is
 *	maintained with zero height, it doesn't feature in this
 *	calculation).
d4406 4
a4409 2
TkBTreeNumPixels(tree)
    TkTextBTree tree;			/* Information about tree. */
d4412 1
a4412 1
    return treePtr->rootPtr->numPixels;
a4774 51

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeCharsInLine --
 *
 *	This procedure returns a count of the number of characters
 *	in a given line.
 *
 * Results:
 *	The return value is the character count for linePtr.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
TkBTreeCharsInLine(linePtr)
    TkTextLine *linePtr;		/* Line whose characters should be
					 * counted. */
{
    TkTextSegment *segPtr;
    int count;

    count = 0;
    for (segPtr = linePtr->segPtr; segPtr != NULL; segPtr = segPtr->nextPtr) {
	if (segPtr->typePtr == &tkTextCharType) {
	    count += Tcl_NumUtfChars(segPtr->body.chars, segPtr->size);
	} else {
	    count += segPtr->size;
	}
    }
    return count;
}

int
TkBTreeBytesInLine(linePtr)
    TkTextLine *linePtr;		/* Line whose characters should be
					 * counted. */
{
    TkTextSegment *segPtr;
    int count;

    count = 0;
    for (segPtr = linePtr->segPtr; segPtr != NULL; segPtr = segPtr->nextPtr) {
	count += segPtr->size;
    }
    return count;
}
@


1.13
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
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.12 2004/01/07 16:28:23 vincentdarley Exp $
d541 10
a560 10
     * I don't believe it's possible for either of the two lines
     * passed to this function to be the last line of text, but
     * the function is robust to that case anyway.  (We must never
     * re-calculated the line height of the last line).
     */
    TkTextInvalidateLineMetrics(((BTree*)indexPtr->tree)->textPtr, 
				indexPtr->linePtr, changeToLineCount, 
				TK_TEXT_INVALIDATE_INSERT);

    /*
@


1.12
log
@fix to text widget yview bug
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.11 2003/12/15 11:51:06 vincentdarley Exp $
d636 1
a636 1
    panic("SplitSeg reached end of line!");
d943 1
a943 1
		panic("TkBTreeFindLine ran out of nodes");
d956 1
a956 1
	    panic("TkBTreeFindLine ran out of lines");
d1006 1
a1006 1
	panic("TkBTreeFindPixelLine called with empty window");
d1019 1
a1019 1
		panic("TkBTreeFindPixelLine ran out of nodes");
d1033 1
a1033 1
	    panic("TkBTreeFindPixelLine ran out of lines");
d1132 1
a1132 1
	    panic("TkBTreePreviousLine ran out of lines");
d1208 1
a1208 1
	    panic("TkBTreePixels couldn't find line");
d1224 1
a1224 1
		panic("TkBTreePixels couldn't find node");
d1269 1
a1269 1
	    panic("TkBTreeLineIndex couldn't find line");
d1285 1
a1285 1
		panic("TkBTreeLineIndex couldn't find node");
d1607 1
a1607 1
		panic("ChangeNodeToggleCount: bad toggle count (%d) max (%d)",
d2215 1
a2215 1
		    panic("TkBTreeNextTag found incorrect tag summary info.");
d2429 1
a2429 1
		panic("TkBTreePrevTag found incorrect tag summary info.");
d2996 1
a2996 1
		panic("TkBTreeCheck found \"%s\" with toggles (%d) but no root",
d3001 1
a3001 1
	    panic("TkBTreeCheck found root for \"%s\" with no toggles",
d3004 1
a3004 1
	    panic("TkBTreeCheck found odd toggle count for \"%s\" (%d)",
d3010 1
a3010 1
		panic("TkBTreeCheck found root node with summary info");
d3038 1
a3038 1
	    panic("TkBTreeCheck toggleCount (%d) wrong for \"%s\" should be (%d)",
d3056 1
a3056 1
	panic("TkBTreeCheck: less than 2 lines in tree");
d3081 1
a3081 1
	panic("TkBTreeCheck: last line has bogus segment type");
d3084 1
a3084 1
	panic("TkBTreeCheck: last line has too many segments");
d3087 1
a3087 1
	panic("TkBTreeCheck: last line has wrong # characters: %d",
d3091 1
a3091 1
	panic("TkBTreeCheck: last line had bad value: %s",
d3135 1
a3135 1
	panic("CheckNodeConsistency: bad child count (%d)",
d3146 1
a3146 1
		panic("CheckNodeConsistency: line doesn't point to parent");
d3149 1
a3149 1
		panic("CheckNodeConsistency: line has no segments");
d3160 1
a3160 1
		    panic("CheckNodeConsistency: wrong segment order for gravity");
d3164 1
a3164 1
		    panic("CheckNodeConsistency: line ended with wrong type");
d3175 1
a3175 1
		panic("CheckNodeConsistency: node doesn't point to parent");
d3178 1
a3178 1
		panic("CheckNodeConsistency: level mismatch (%d %d)",
d3190 1
a3190 1
			panic("CheckNodeConsistency: node tag \"%s\" not %s",
d3205 1
a3205 1
	panic("CheckNodeConsistency: mismatch in numChildren (%d %d)",
d3209 1
a3209 1
	panic("CheckNodeConsistency: mismatch in numLines (%d %d)",
d3213 1
a3213 1
	panic("CheckNodeConsistency: mismatch in numPixels (%d %d)",
d3220 1
a3220 1
	    panic("CheckNodeConsistency: found unpruned root for \"%s\"",
d3252 1
a3252 1
	    panic("CheckNodeConsistency: mismatch in toggleCount (%d %d)",
d3258 1
a3258 1
		panic("CheckNodeConsistency: duplicated node tag: %s",
d3847 1
a3847 1
	panic("CharCheckProc: segment has size <= 0");
d3850 1
a3850 1
	panic("CharCheckProc: segment has wrong size");
d3854 1
a3854 1
	    panic("CharCheckProc: line doesn't end with newline");
d3858 1
a3858 1
	    panic("CharCheckProc: adjacent character segments weren't merged");
d4036 1
a4036 1
	panic("ToggleCheckProc: segment had non-zero size");
d4039 1
a4039 1
	panic("ToggleCheckProc: toggle counts not updated in nodes");
d4046 1
a4046 1
		panic("ToggleCheckProc: tag not present in node");
d4053 1
a4053 1
		panic("ToggleCheckProc: tag present in root node summary");
@


1.11
log
@text widget more extensive documentation, and two small code improvements
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.10 2003/11/08 17:22:46 vincentdarley Exp $
d1005 4
d1019 1
a1019 1
		panic("TkBTreeFindLine ran out of nodes");
d1033 1
a1033 1
	    panic("TkBTreeFindLine ran out of lines");
@


1.10
log
@elide tag handling, once more
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.9 2003/11/07 15:36:26 vincentdarley Exp $
d58 3
a60 1
    int numPixels;
a2841 4
    if (LOTSA_TAGS < infoPtr->numTags) {
	ckfree((char *) infoPtr->tagCnts);
	ckfree((char *) infoPtr->tagPtrs);
    }
d2843 6
a2849 1
    if (elideInfo == NULL) {
d2854 28
@


1.9
log
@better elide tag handling
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.8 2003/10/31 09:02:10 vincentdarley Exp $
d2685 11
a2695 4
 *	Specialized from TkBTreeGetTags(indexPtr, numTagsPtr)
 *	and GetStyle(textPtr, indexPtr).
 *	Just need to keep track of invisibility settings for each priority,
 *	pick highest one active at end
d2699 2
d2766 5
d2834 1
@


1.8
log
@TIP 155 implementation
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.7 2003/05/19 13:04:23 vincentdarley Exp $
d2039 1
a2039 1
	TkTextIndexBackChars(index1Ptr, 1, &searchPtr->curIndex, 
d2056 1
a2056 1
	TkTextIndexBackChars(index2Ptr, 1, &backOne, COUNT_INDICES);
d2701 1
a2701 1
TkTextIsElided(textPtr, indexPtr)
d2705 3
a2708 8
#define LOTSA_TAGS 1000
    int elide = 0;		/* if nobody says otherwise, it's visible */

    int deftagCnts[LOTSA_TAGS];
    int *tagCnts = deftagCnts;
    TkTextTag *deftagPtrs[LOTSA_TAGS];
    TkTextTag **tagPtrs = deftagPtrs;
    int numTags = textPtr->numTags;
d2714 13
d2729 5
a2733 3
    if (LOTSA_TAGS < numTags) {
	tagCnts = (int *)ckalloc((unsigned)sizeof(int) * numTags);
	tagPtrs = (TkTextTag **)ckalloc((unsigned)sizeof(TkTextTag *) * numTags);
d2736 2
a2737 2
    for (i=0; i<numTags; i++) {
	tagCnts[i] = 0;
d2752 2
a2753 2
		tagPtrs[tagPtr->priority] = tagPtr;
		tagCnts[tagPtr->priority]++;
d2772 2
a2773 2
		    tagPtrs[tagPtr->priority] = tagPtr;
		    tagCnts[tagPtr->priority]++;
d2796 2
a2797 2
			tagPtrs[tagPtr->priority] = tagPtr;
			tagCnts[tagPtr->priority] += summaryPtr->toggleCount;
d2809 3
a2811 2
    for (i = numTags-1; i >=0; i--) {
	if (tagCnts[i] & 1) {
d2819 2
a2820 1
	    elide = tagPtrs[i]->elide;
d2825 8
a2832 3
    if (LOTSA_TAGS < numTags) {
	ckfree((char *) tagCnts);
	ckfree((char *) tagPtrs);
@


1.7
log
@tip 113 implementation
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.6 2002/08/05 04:30:40 dgp Exp $
d58 1
d221 1
a221 1

d239 7
a245 1

d255 1
a255 1

d381 48
d472 2
d486 1
d522 7
d533 2
a534 1

d547 10
d559 2
a560 2
     * Increment the line counts in all the parent nodes of the insertion
     * point, then rebalance the tree if necessary.
d566 1
d729 2
a730 1

d756 1
d778 1
d780 1
d852 1
d854 1
d866 1
d877 12
d964 76
d1163 66
d1378 3
a1380 1
 *	None.
d1393 1
a1393 1
void
d1409 2
a1410 1

d1432 1
d1444 1
d1503 1
d1511 5
a1515 3
    CleanupLine(cleanupLinePtr);
    if (cleanupLinePtr != index2Ptr->linePtr) {
	CleanupLine(index2Ptr->linePtr);
d1521 1
d2039 2
a2040 1
	TkTextIndexBackChars(index1Ptr, 1, &searchPtr->curIndex);
d2056 1
a2056 1
	TkTextIndexBackChars(index2Ptr, 1, &backOne);
d2702 1
a2702 1
    TkText *textPtr;		/* Overall information about text widget. */
d2720 1
a2720 1
	/* almost always avoid malloc, so stay out of system calls */
d3057 1
a3057 1
    int numChildren, numLines, toggleCount, minChildren;
d3074 1
d3102 1
d3134 1
d3145 4
d3255 1
d3468 1
d3481 1
d3513 1
d3602 28
@


1.6
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: tkTextBTree.c,v 1.5 2000/01/06 02:18:58 hobbs Exp $
d2226 1
a2226 1
    TkTextIndex *indexPtr;		/* Indicates a character position at
d2338 1
a2338 1
    TkTextIndex *indexPtr;	/* Indicates a particular position in
d2455 1
a2455 1
    TkTextIndex *indexPtr;	/* The character in the text for which
d2469 1
a2469 1
    register TkTextTag *tagPtr;
@


1.6.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: tkTextBTree.c,v 1.6 2002/08/05 04:30:40 dgp Exp $
d2469 1
a2469 1
    register TkTextTag *tagPtr = NULL; /* silence gcc 4 warning */
@


1.6.2.2
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: tkTextBTree.c,v 1.6.2.1 2005/11/27 02:44:25 das Exp $
d2553 1
d2555 1
a2555 2
	    if (!TkpAlwaysShowSelection(textPtr->tkwin)
		    && (tagPtr == textPtr->selTagPtr)
d2559 1
@


1.6.2.3
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: tkTextBTree.c,v 1.6.2.2 2006/09/06 22:01:25 hobbs Exp $
d2554 1
a2554 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.5
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: tkTextBTree.c,v 1.4 1999/12/14 06:52:32 hobbs Exp $
d396 1
a396 1
    char *string;			/* Pointer to bytes to insert (may
d413 1
a413 1
    register char *eol;			/* Pointer to character just after last
@


1.5.8.1
log
@merged with trunk at tag macosx-8-4-merge-2002-08-20-trunk
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id$
d396 1
a396 1
    CONST char *string;			/* Pointer to bytes to insert (may
d413 1
a413 1
    register CONST char *eol;		/* Pointer to character just after last
@


1.4
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: tkTextBTree.c,v 1.3 1999/04/16 01:51:23 stanton Exp $
d2430 20
d2451 1
a2451 6

/*
   special case to just return information about elided attribute
   specialized from TkBTreeGetTags(indexPtr, numTagsPtr) and GetStyle(textPtr, indexPtr)
   just need to keep track of invisibility settings for each priority, pick highest one active at end
*/
d2459 1
a2459 1
	int elide = 0;		/* if nobody says otherwise, it's visible */
d2461 5
a2465 5
	int deftagCnts[LOTSA_TAGS];
	int *tagCnts = deftagCnts;
	TkTextTag *deftagPtrs[LOTSA_TAGS];
	TkTextTag **tagPtrs = deftagPtrs;
	int numTags = textPtr->numTags;
d2473 4
a2476 4
	if (LOTSA_TAGS < numTags) {
	    tagCnts = (int *)ckalloc((unsigned)sizeof(int) * numTags);
	    tagPtrs = (TkTextTag **)ckalloc((unsigned)sizeof(TkTextTag *) * numTags);
	}
d2478 3
a2480 2
	for (i=0; i<numTags; i++) tagCnts[i]=0;

d2488 2
a2489 2
	    (index + segPtr->size) <= indexPtr->byteIndex;
	    index += segPtr->size, segPtr = segPtr->nextPtr) {
d2491 1
a2491 1
	    || (segPtr->typePtr == &tkTextToggleOffType)) {
d2493 3
a2495 3
	    if (tagPtr->state != TK_STATE_NULL) {
		   tagPtrs[tagPtr->priority] = tagPtr;
		   tagCnts[tagPtr->priority]++;
d2506 2
a2507 2
	    siblingLinePtr != indexPtr->linePtr;
	    siblingLinePtr = siblingLinePtr->nextPtr) {
d2509 1
a2509 1
		segPtr = segPtr->nextPtr) {
d2511 6
a2516 6
		   || (segPtr->typePtr == &tkTextToggleOffType)) {
		   tagPtr = segPtr->body.toggle.tagPtr;
		   if (tagPtr->state != TK_STATE_NULL) {
			  tagPtrs[tagPtr->priority] = tagPtr;
			  tagCnts[tagPtr->priority]++;
		   }
d2527 1
a2527 1
	    nodePtr = nodePtr->parentPtr) {
d2532 1
a2532 1
		siblingPtr != nodePtr; siblingPtr = siblingPtr->nextPtr) {
d2534 1
a2534 1
		    summaryPtr = summaryPtr->nextPtr) {
d2537 3
a2539 3
		    if (tagPtr->state != TK_STATE_NULL) {
			   tagPtrs[tagPtr->priority] = tagPtr;
			   tagCnts[tagPtr->priority] += summaryPtr->toggleCount;
a2545 1

d2552 1
a2552 1
	   if (tagCnts[i] & 1) {
d2554 5
a2558 4
		  /* who would make the selection elided? */
		  if ((tagPtr == textPtr->selTagPtr) && !(textPtr->flags & GOT_FOCUS)) {
			 continue;
		  }
d2560 3
a2562 3
		  elide = (tagPtrs[i]->state == TK_STATE_HIDDEN);
		  break;
	   }
d2566 2
a2567 2
	   ckfree((char *) tagCnts);
	   ckfree((char *) tagPtrs);
@


1.3
log
@* Merged 8.1 branch into the main trunk
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id$
d2429 126
@


1.2
log
@Replaced SCCS strings, fixed binary files
@
text
@d538 1
a538 1
    for (count = indexPtr->charIndex, prevPtr = NULL,
d1533 1
a1533 1
		indexPtr->charIndex = offset;
d1622 1
a1622 1
    indexPtr->charIndex = lastoffset2;
d1697 1
a1697 1
	searchPtr->curIndex.charIndex -= offset;
d1712 1
a1712 1
		(index1Ptr->charIndex > index2Ptr->charIndex)) ||
d1714 1
a1714 1
		(index1Ptr->charIndex >= index2Ptr->charIndex))) {
d1796 1
a1796 1
    searchPtr->curIndex.charIndex -= offset;
d1804 1
a1804 1
	    (index2Ptr->charIndex == 0)) {
d1822 1
a1822 1
	if (index1Ptr->charIndex <= backOne.charIndex) {
d1892 1
a1892 1
	    searchPtr->curIndex.charIndex += segPtr->size;
d1909 1
a1909 1
	    searchPtr->curIndex.charIndex = 0;
d1975 1
a1975 1
	searchPtr->curIndex.charIndex = 0;
d2025 1
a2025 1
    int charIndex;
d2044 1
a2044 1
	charIndex = 0;
d2061 1
a2061 1
		searchPtr->curIndex.charIndex = charIndex;
d2067 1
a2067 1
	    charIndex += segPtr->size;
d2194 1
a2194 1
	searchPtr->curIndex.charIndex = 0;
d2244 1
a2244 1
	    (index + segPtr->size) <= indexPtr->charIndex;
d2363 1
a2363 1
	    (index + segPtr->size) <= indexPtr->charIndex;
d3583 19
@


1.1
log
@Initial revision
@
text
@d14 1
a14 1
 * SCCS: @@(#) tkTextBTree.c 1.37 97/04/25 16:52:00
@


1.1.4.1
log
@initial tk8.1a2 version
@
text
@d14 1
a14 1
 * SCCS: @@(#) tkTextBTree.c 1.38 98/01/08 13:40:24
d538 1
a538 1
    for (count = indexPtr->byteIndex, prevPtr = NULL,
d1533 1
a1533 1
		indexPtr->byteIndex = offset;
d1622 1
a1622 1
    indexPtr->byteIndex = lastoffset2;
d1697 1
a1697 1
	searchPtr->curIndex.byteIndex -= offset;
d1712 1
a1712 1
		(index1Ptr->byteIndex > index2Ptr->byteIndex)) ||
d1714 1
a1714 1
		(index1Ptr->byteIndex >= index2Ptr->byteIndex))) {
d1796 1
a1796 1
    searchPtr->curIndex.byteIndex -= offset;
d1804 1
a1804 1
	    (index2Ptr->byteIndex == 0)) {
d1822 1
a1822 1
	if (index1Ptr->byteIndex <= backOne.byteIndex) {
d1892 1
a1892 1
	    searchPtr->curIndex.byteIndex += segPtr->size;
d1909 1
a1909 1
	    searchPtr->curIndex.byteIndex = 0;
d1975 1
a1975 1
	searchPtr->curIndex.byteIndex = 0;
d2025 1
a2025 1
    int byteIndex;
d2044 1
a2044 1
	byteIndex = 0;
d2061 1
a2061 1
		searchPtr->curIndex.byteIndex = byteIndex;
d2067 1
a2067 1
	    byteIndex += segPtr->size;
d2194 1
a2194 1
	searchPtr->curIndex.byteIndex = 0;
d2244 1
a2244 1
	    (index + segPtr->size) <= indexPtr->byteIndex;
d2363 1
a2363 1
	    (index + segPtr->size) <= indexPtr->byteIndex;
a3582 19
    TkTextLine *linePtr;		/* Line whose characters should be
					 * counted. */
{
    TkTextSegment *segPtr;
    int count;

    count = 0;
    for (segPtr = linePtr->segPtr; segPtr != NULL; segPtr = segPtr->nextPtr) {
	if (segPtr->typePtr == &tkTextCharType) {
	    count += Tcl_NumUtfChars(segPtr->body.chars, segPtr->size);
	} else {
	    count += segPtr->size;
	}
    }
    return count;
}

int
TkBTreeBytesInLine(linePtr)
@


1.1.4.2
log
@Merged 8.0.3 changes into 8.1
@
text
@d14 1
a14 1
 * RCS: @@(#) $Id: tkTextBTree.c,v 1.2 1998/09/14 18:23:18 stanton Exp $
@


