head	1.37;
access;
symbols
	core-8-5-9:1.28.2.1
	core-8-5-8:1.28.2.1
	macosx-cocoa-merge:1.35
	macosx-cocoa-premerge:1.35
	macosx-carbon-freeze:1.35
	core-8-5-7:1.28.2.1
	core-8-5-6:1.28
	core-8-6-b1:1.34
	core-8-6-a3:1.30
	core-8-5-5:1.28
	core-8-6-a2:1.29
	core-8-5-4:1.28
	core-8-5-3:1.28
	core-8-6-a1:1.29
	core-8-4-19:1.6
	core-8-5-branch:1.28.0.2
	core-8-5-2:1.28
	core-8-4-18:1.6
	core-8-5-1:1.28
	core-8-4-17:1.6
	core-8-5-0:1.28
	core-8-5-b3:1.27
	core-8-5-b2:1.27
	core-8-4-16:1.6
	core-8-5-b1:1.26
	core-stablilizer-merge:1.25
	core-stabilizer-branch:1.25.0.2
	core-stabilizer-merge:1.28
	core-8-4-15:1.6
	core-8-5-a6:1.25
	core-8-5-a5:1.24
	core-8-4-14:1.6
	core-8-5-a4:1.24
	core-8-4-13:1.6
	core-8-4-12:1.6
	core-8-4-11:1.6
	core-8-5-a3:1.21
	core-8-4-10:1.6
	core-8-4-9-branch:1.6.0.4
	core-8-5-a2:1.20
	core-8-4-9:1.6
	core-8-4-8:1.6
	core-8-4-7:1.6
	core-8-5-a1:1.17
	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.3
	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.3.18.2
	macosx-8-4-premerge-2002-08-31-trunk:1.6
	macosx-8-4-merge-2002-08-30-branch:1.3.18.2
	macosx-8-4-merge-2002-08-30-trunk:1.6
	macosx-8-4-merge-2002-08-20-branch:1.3.18.2
	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.3.18.1
	core-8-4-a4:1.4
	core-8-3-4:1.3
	macosx-8-4-branch:1.3.0.18
	macosx-8-4-branchpoint:1.3
	core-8-4-a3:1.3
	core-8-3-3:1.3
	dgp-privates-into-namespace:1.3.0.16
	tclpro-1-5-0:1.3
	core-8-4-win-speedup:1.3.0.14
	core-8-4-a2:1.3
	tclpro-1-4-1:1.3
	tclpro-1-4-0:1.3
	core-8-3-2:1.3
	ajuba-ajuba2-2-1-base:1.3
	ajuba-ajuba2-2-0:1.3
	core-8-4-a1:1.3
	core-8-3-1-branch:1.3.0.12
	core-8-3-1:1.3
	scriptics-sc-2-0-b5:1.3
	scriptics-sc-2-0-fixed:1.3
	scriptics-sc-2-0-b2:1.3
	scriptics-sc-2-0-b1:1.3
	core-8-3-0:1.3
	core-8-3-b2:1.3
	core-8-3-b1:1.3
	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.4
	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.37
date	2009.11.21.17.24.42;	author dkf;	state Exp;
branches;
next	1.36;

1.36
date	2009.09.07.07.29.04;	author das;	state Exp;
branches;
next	1.35;

1.35
date	2009.02.06.08.12.07;	author das;	state Exp;
branches;
next	1.34;

1.34
date	2008.12.05.15.51.31;	author nijtmans;	state Exp;
branches;
next	1.33;

1.33
date	2008.12.05.09.33.12;	author patthoyts;	state Exp;
branches;
next	1.32;

1.32
date	2008.11.08.18.44.40;	author dkf;	state Exp;
branches;
next	1.31;

1.31
date	2008.10.15.06.41.06;	author nijtmans;	state Exp;
branches;
next	1.30;

1.30
date	2008.10.05.18.22.22;	author dkf;	state Exp;
branches;
next	1.29;

1.29
date	2008.04.27.22.38.58;	author dkf;	state Exp;
branches;
next	1.28;

1.28
date	2007.12.13.15.24.17;	author dgp;	state Exp;
branches
	1.28.2.1;
next	1.27;

1.27
date	2007.10.15.07.24.49;	author das;	state Exp;
branches;
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.22.13.56.33;	author dkf;	state Exp;
branches
	1.25.2.1;
next	1.24;

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

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

1.22
date	2005.08.10.22.02.22;	author dkf;	state Exp;
branches;
next	1.21;

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

1.20
date	2004.10.05.01.26.10;	author hobbs;	state Exp;
branches;
next	1.19;

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

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

1.17
date	2004.02.28.16.04.43;	author vincentdarley;	state Exp;
branches;
next	1.16;

1.16
date	2004.01.27.10.09.35;	author das;	state Exp;
branches;
next	1.15;

1.15
date	2004.01.13.02.06.01;	author davygrvy;	state Exp;
branches;
next	1.14;

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

1.13
date	2003.12.15.11.51.06;	author vincentdarley;	state Exp;
branches;
next	1.12;

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

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

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

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

1.8
date	2003.10.10.15.56.22;	author dkf;	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;
next	1.5;

1.5
date	2002.06.22.08.21.51;	author hobbs;	state Exp;
branches;
next	1.4;

1.4
date	2002.01.17.03.35.00;	author dgp;	state Exp;
branches;
next	1.3;

1.3
date	99.04.16.01.51.24;	author stanton;	state Exp;
branches
	1.3.14.1
	1.3.18.1;
next	1.2;

1.2
date	98.09.14.18.23.19;	author stanton;	state Exp;
branches;
next	1.1;

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

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

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

1.1.4.3
date	99.03.27.02.31.39;	author redman;	state Exp;
branches;
next	1.1.4.4;

1.1.4.4
date	99.03.29.23.50.33;	author redman;	state Exp;
branches;
next	;

1.3.14.1
date	2002.04.02.21.01.00;	author hobbs;	state Exp;
branches;
next	;

1.3.18.1
date	2002.02.05.02.25.16;	author wolfsuit;	state Exp;
branches;
next	1.3.18.2;

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

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

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

1.28.2.1
date	2009.02.06.08.13.23;	author das;	state Exp;
branches;
next	;


desc
@@


1.37
log
@[Tcl Bug 2857044]: Corrections following audit of Tcl_ObjType freeing
practises; the typePtr field is now cleared when an object ceases to be of
the type.
Also remove some anachronistic techniques (pointless casts, mixed assignments
and tests, etc.)
@
text
@/*
 * tkTextIndex.c --
 *
 *	This module provides functions that manipulate indices for text
 *	widgets.
 *
 * Copyright (c) 1992-1994 The Regents of the University of California.
 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.36 2009/09/07 07:29:04 das Exp $
 */

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

/*
 * Index to use to select last character in line (very large integer):
 */

#define LAST_CHAR 1000000

/*
 * Modifiers for index parsing: 'display', 'any' or nothing.
 */

#define TKINDEX_NONE	0
#define TKINDEX_DISPLAY	1
#define TKINDEX_ANY	2

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

static const char *	ForwBack(TkText *textPtr, const char *string,
			    TkTextIndex *indexPtr);
static const char *	StartEnd(TkText *textPtr, const char *string,
			    TkTextIndex *indexPtr);
static int		GetIndex(Tcl_Interp *interp, TkSharedText *sharedPtr,
			    TkText *textPtr, const char *string,
			    TkTextIndex *indexPtr, int *canCachePtr);

/*
 * The "textindex" Tcl_Obj definition:
 */

static void		DupTextIndexInternalRep(Tcl_Obj *srcPtr,
			    Tcl_Obj *copyPtr);
static void		FreeTextIndexInternalRep(Tcl_Obj *listPtr);
static int		SetTextIndexFromAny(Tcl_Interp *interp,
			    Tcl_Obj *objPtr);
static void		UpdateStringOfTextIndex(Tcl_Obj *objPtr);

/*
 * Accessor macros for the "textindex" type.
 */

#define GET_TEXTINDEX(objPtr) \
	((TkTextIndex *) (objPtr)->internalRep.twoPtrValue.ptr1)
#define GET_INDEXEPOCH(objPtr) \
	(PTR2INT((objPtr)->internalRep.twoPtrValue.ptr2))
#define SET_TEXTINDEX(objPtr, indexPtr) \
	((objPtr)->internalRep.twoPtrValue.ptr1 = (void *) (indexPtr))
#define SET_INDEXEPOCH(objPtr, epoch) \
	((objPtr)->internalRep.twoPtrValue.ptr2 = INT2PTR(epoch))

/*
 * Define the 'textindex' object type, which Tk uses to represent indices in
 * text widgets internally.
 */

const Tcl_ObjType tkTextIndexType = {
    "textindex",		/* name */
    FreeTextIndexInternalRep,	/* freeIntRepProc */
    DupTextIndexInternalRep,	/* dupIntRepProc */
    NULL,			/* updateStringProc */
    SetTextIndexFromAny		/* setFromAnyProc */
};

static void
FreeTextIndexInternalRep(
    Tcl_Obj *indexObjPtr)	/* TextIndex object with internal rep to
				 * free. */
{
    TkTextIndex *indexPtr = GET_TEXTINDEX(indexObjPtr);
    if (indexPtr->textPtr != NULL) {
	if (--indexPtr->textPtr->refCount == 0) {
	    /*
	     * The text widget has been deleted and we need to free it now.
	     */

	    ckfree((char *) indexPtr->textPtr);
	}
    }
    ckfree((char *) indexPtr);
    indexObjPtr->typePtr = NULL;
}

static void
DupTextIndexInternalRep(
    Tcl_Obj *srcPtr,		/* TextIndex obj with internal rep to copy. */
    Tcl_Obj *copyPtr)		/* TextIndex obj with internal rep to set. */
{
    int epoch;
    TkTextIndex *dupIndexPtr, *indexPtr;

    dupIndexPtr = (TkTextIndex *) ckalloc(sizeof(TkTextIndex));
    indexPtr = GET_TEXTINDEX(srcPtr);
    epoch = GET_INDEXEPOCH(srcPtr);

    dupIndexPtr->tree = indexPtr->tree;
    dupIndexPtr->linePtr = indexPtr->linePtr;
    dupIndexPtr->byteIndex = indexPtr->byteIndex;
    dupIndexPtr->textPtr = indexPtr->textPtr;
    if (dupIndexPtr->textPtr != NULL) {
	dupIndexPtr->textPtr->refCount++;
    }
    SET_TEXTINDEX(copyPtr, dupIndexPtr);
    SET_INDEXEPOCH(copyPtr, epoch);
    copyPtr->typePtr = &tkTextIndexType;
}

/*
 * This will not be called except by TkTextNewIndexObj below. This is because
 * if a TkTextIndex is no longer valid, it is not possible to regenerate the
 * string representation.
 */

static void
UpdateStringOfTextIndex(
    Tcl_Obj *objPtr)
{
    char buffer[TK_POS_CHARS];
    register int len;

    const TkTextIndex *indexPtr = GET_TEXTINDEX(objPtr);

    len = TkTextPrintIndex(indexPtr->textPtr, indexPtr, buffer);

    objPtr->bytes = ckalloc((unsigned) len + 1);
    strcpy(objPtr->bytes, buffer);
    objPtr->length = len;
}

static int
SetTextIndexFromAny(
    Tcl_Interp *interp,		/* Used for error reporting if not NULL. */
    Tcl_Obj *objPtr)		/* The object to convert. */
{
    Tcl_AppendResult(interp, "can't convert value to textindex except "
	    "via TkTextGetIndexFromObj API", -1);
    return TCL_ERROR;
}

/*
 *---------------------------------------------------------------------------
 *
 * MakeObjIndex --
 *
 *	This function generates a Tcl_Obj description of an index, suitable
 *	for reading in again later. If the 'textPtr' is NULL then we still
 *	generate an index object, but it's internal description is deemed
 *	non-cacheable, and therefore effectively useless (apart from as a
 *	temporary memory storage). This is used for indices whose meaning is
 *	very temporary (like @@0,0 or the name of a mark or tag). The mapping
 *	from such strings/objects to actual TkTextIndex pointers is not stable
 *	to minor text widget changes which we do not track (we track
 *	insertions and deletions).
 *
 * Results:
 *	A pointer to an allocated TkTextIndex which will be freed
 *	automatically when the Tcl_Obj is used for other purposes.
 *
 * Side effects:
 *	A small amount of memory is allocated.
 *
 *---------------------------------------------------------------------------
 */

static TkTextIndex *
MakeObjIndex(
    TkText *textPtr,		/* Information about text widget. */
    Tcl_Obj *objPtr,		/* Object containing description of
				 * position. */
    const TkTextIndex *origPtr)	/* Pointer to index. */
{
    TkTextIndex *indexPtr = (TkTextIndex *) ckalloc(sizeof(TkTextIndex));

    indexPtr->tree = origPtr->tree;
    indexPtr->linePtr = origPtr->linePtr;
    indexPtr->byteIndex = origPtr->byteIndex;
    SET_TEXTINDEX(objPtr, indexPtr);
    objPtr->typePtr = &tkTextIndexType;
    indexPtr->textPtr = textPtr;

    if (textPtr != NULL) {
	textPtr->refCount++;
	SET_INDEXEPOCH(objPtr, textPtr->sharedTextPtr->stateEpoch);
    } else {
	SET_INDEXEPOCH(objPtr, 0);
    }
    return indexPtr;
}

const TkTextIndex *
TkTextGetIndexFromObj(
    Tcl_Interp *interp,		/* Use this for error reporting. */
    TkText *textPtr,		/* Information about text widget. */
    Tcl_Obj *objPtr)		/* Object containing description of
				 * position. */
{
    TkTextIndex index;
    TkTextIndex *indexPtr = NULL;
    int cache;

    if (objPtr->typePtr == &tkTextIndexType) {
	int epoch;

	indexPtr = GET_TEXTINDEX(objPtr);
	epoch = GET_INDEXEPOCH(objPtr);

	if (epoch == textPtr->sharedTextPtr->stateEpoch) {
	    if (indexPtr->textPtr == textPtr) {
		return indexPtr;
	    }
	}
    }

    /*
     * The object is either not an index type or referred to a different text
     * widget, or referred to the correct widget, but it is out of date (text
     * has been added/deleted since).
     */

    if (GetIndex(interp, NULL, textPtr, Tcl_GetString(objPtr), &index,
	    &cache) != TCL_OK) {
	return NULL;
    }

    if (objPtr->typePtr != NULL) {
	if (objPtr->bytes == NULL) {
	    objPtr->typePtr->updateStringProc(objPtr);
	}
	if (objPtr->typePtr->freeIntRepProc != NULL) {
	    objPtr->typePtr->freeIntRepProc(objPtr);
	}
    }

    return MakeObjIndex((cache ? textPtr : NULL), objPtr, &index);
}

/*
 *---------------------------------------------------------------------------
 *
 * TkTextNewIndexObj --
 *
 *	This function generates a Tcl_Obj description of an index, suitable
 *	for reading in again later. The index generated is effectively stable
 *	to all except insertion/deletion operations on the widget.
 *
 * Results:
 *	A new Tcl_Obj with refCount zero.
 *
 * Side effects:
 *	A small amount of memory is allocated.
 *
 *---------------------------------------------------------------------------
 */

Tcl_Obj *
TkTextNewIndexObj(
    TkText *textPtr,		/* Text widget for this index */
    const TkTextIndex *indexPtr)/* Pointer to index. */
{
    Tcl_Obj *retVal;

    retVal = Tcl_NewObj();
    retVal->bytes = NULL;

    /*
     * Assumption that the above call returns an object with:
     * retVal->typePtr == NULL
     */

    MakeObjIndex(textPtr, retVal, indexPtr);

    /*
     * Unfortunately, it isn't possible for us to regenerate the string
     * representation so we have to create it here, while we can be sure the
     * contents of the index are still valid.
     */

    UpdateStringOfTextIndex(retVal);
    return retVal;
}

/*
 *---------------------------------------------------------------------------
 *
 * TkTextMakePixelIndex --
 *
 *	Given a pixel index and a byte index, look things up in the B-tree and
 *	fill in a TkTextIndex structure.
 *
 *	The valid input range for pixelIndex is from 0 to the number of pixels
 *	in the widget-1. Anything outside that range will be rounded to the
 *	closest acceptable value.
 *
 * Results:
 *
 *	The structure at *indexPtr is filled in with information about the
 *	character at pixelIndex (or the closest existing character, if the
 *	specified one doesn't exist), and the number of excess pixels is
 *	returned as a result. This means if the given pixel index is exactly
 *	correct for the top-edge of the indexPtr, then zero will be returned,
 *	and otherwise we will return the calculation 'desired pixelIndex' -
 *	'actual pixel index of indexPtr'.
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

int
TkTextMakePixelIndex(
    TkText *textPtr,		/* The Text Widget */
    int pixelIndex,		/* Pixel-index of desired line (0 means first
				 * pixel of first line of text). */
    TkTextIndex *indexPtr)	/* Structure to fill in. */
{
    int pixelOffset = 0;

    indexPtr->tree = textPtr->sharedTextPtr->tree;
    indexPtr->textPtr = textPtr;

    if (pixelIndex < 0) {
	pixelIndex = 0;
    }
    indexPtr->linePtr = TkBTreeFindPixelLine(textPtr->sharedTextPtr->tree,
	    textPtr, pixelIndex, &pixelOffset);

    /*
     * 'pixelIndex' was too large, so we try again, just to find the last
     * pixel in the window.
     */

    if (indexPtr->linePtr == NULL) {
	int lastMinusOne = TkBTreeNumPixels(textPtr->sharedTextPtr->tree,
		textPtr)-1;

	indexPtr->linePtr = TkBTreeFindPixelLine(textPtr->sharedTextPtr->tree,
		textPtr, lastMinusOne, &pixelOffset);
	indexPtr->byteIndex = 0;
	return pixelOffset;
    }
    indexPtr->byteIndex = 0;

    if (pixelOffset <= 0) {
	return 0;
    }
    return TkTextMeasureDown(textPtr, indexPtr, pixelOffset);
}

/*
 *---------------------------------------------------------------------------
 *
 * TkTextMakeByteIndex --
 *
 *	Given a line index and a byte index, look things up in the B-tree and
 *	fill in a TkTextIndex structure.
 *
 * Results:
 *	The structure at *indexPtr is filled in with information about the
 *	character at lineIndex and byteIndex (or the closest existing
 *	character, if the specified one doesn't exist), and indexPtr is
 *	returned as result.
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

TkTextIndex *
TkTextMakeByteIndex(
    TkTextBTree tree,	/* Tree that lineIndex and byteIndex refer
				 * to. */
    const TkText *textPtr,
    int lineIndex,		/* Index of desired line (0 means first line
				 * of text). */
    int byteIndex,		/* Byte index of desired character. */
    TkTextIndex *indexPtr)	/* Structure to fill in. */
{
    TkTextSegment *segPtr;
    int index;
    const char *p, *start;
    Tcl_UniChar ch;

    indexPtr->tree = tree;
    if (lineIndex < 0) {
	lineIndex = 0;
	byteIndex = 0;
    }
    if (byteIndex < 0) {
	byteIndex = 0;
    }
    indexPtr->linePtr = TkBTreeFindLine(tree, textPtr, lineIndex);
    if (indexPtr->linePtr == NULL) {
	indexPtr->linePtr = TkBTreeFindLine(tree, textPtr,
		TkBTreeNumLines(tree, textPtr));
	byteIndex = 0;
    }
    if (byteIndex == 0) {
	indexPtr->byteIndex = byteIndex;
	return indexPtr;
    }

    /*
     * Verify that the index is within the range of the line and points to a
     * valid character boundary.
     */

    index = 0;
    for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
	if (segPtr == NULL) {
	    /*
	     * Use the index of the last character in the line. Since the last
	     * character on the line is guaranteed to be a '\n', we can back
	     * up a constant sizeof(char) bytes.
	     */

	    indexPtr->byteIndex = index - sizeof(char);
	    break;
	}
	if (index + segPtr->size > byteIndex) {
	    indexPtr->byteIndex = byteIndex;
	    if ((byteIndex > index) && (segPtr->typePtr == &tkTextCharType)) {
		/*
		 * Prevent UTF-8 character from being split up by ensuring
		 * that byteIndex falls on a character boundary. If the index
		 * falls in the middle of a UTF-8 character, it will be
		 * adjusted to the end of that UTF-8 character.
		 */

		start = segPtr->body.chars + (byteIndex - index);
		p = Tcl_UtfPrev(start, segPtr->body.chars);
		p += Tcl_UtfToUniChar(p, &ch);
		indexPtr->byteIndex += p - start;
	    }
	    break;
	}
	index += segPtr->size;
    }
    return indexPtr;
}

/*
 *---------------------------------------------------------------------------
 *
 * TkTextMakeCharIndex --
 *
 *	Given a line index and a character index, look things up in the B-tree
 *	and fill in a TkTextIndex structure.
 *
 * Results:
 *	The structure at *indexPtr is filled in with information about the
 *	character at lineIndex and charIndex (or the closest existing
 *	character, if the specified one doesn't exist), and indexPtr is
 *	returned as result.
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

TkTextIndex *
TkTextMakeCharIndex(
    TkTextBTree tree,		/* Tree that lineIndex and charIndex refer
				 * to. */
    TkText *textPtr,
    int lineIndex,		/* Index of desired line (0 means first line
				 * of text). */
    int charIndex,		/* Index of desired character. */
    TkTextIndex *indexPtr)	/* Structure to fill in. */
{
    register TkTextSegment *segPtr;
    char *p, *start, *end;
    int index, offset;
    Tcl_UniChar ch;

    indexPtr->tree = tree;
    if (lineIndex < 0) {
	lineIndex = 0;
	charIndex = 0;
    }
    if (charIndex < 0) {
	charIndex = 0;
    }
    indexPtr->linePtr = TkBTreeFindLine(tree, textPtr, lineIndex);
    if (indexPtr->linePtr == NULL) {
	indexPtr->linePtr = TkBTreeFindLine(tree, textPtr,
		TkBTreeNumLines(tree, textPtr));
	charIndex = 0;
    }

    /*
     * Verify that the index is within the range of the line. If not, just use
     * the index of the last character in the line.
     */

    index = 0;
    for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
	if (segPtr == NULL) {
	    /*
	     * Use the index of the last character in the line. Since the last
	     * character on the line is guaranteed to be a '\n', we can back
	     * up a constant sizeof(char) bytes.
	     */

	    indexPtr->byteIndex = index - sizeof(char);
	    break;
	}
	if (segPtr->typePtr == &tkTextCharType) {
	    /*
	     * Turn character offset into a byte offset.
	     */

	    start = segPtr->body.chars;
	    end = start + segPtr->size;
	    for (p = start; p < end; p += offset) {
		if (charIndex == 0) {
		    indexPtr->byteIndex = index;
		    return indexPtr;
		}
		charIndex--;
		offset = Tcl_UtfToUniChar(p, &ch);
		index += offset;
	    }
	} else {
	    if (charIndex < segPtr->size) {
		indexPtr->byteIndex = index;
		break;
	    }
	    charIndex -= segPtr->size;
	    index += segPtr->size;
	}
    }
    return indexPtr;
}

/*
 *---------------------------------------------------------------------------
 *
 * TkTextIndexToSeg --
 *
 *	Given an index, this function returns the segment and offset within
 *	segment for the index.
 *
 * Results:
 *	The return value is a pointer to the segment referred to by indexPtr;
 *	this will always be a segment with non-zero size. The variable at
 *	*offsetPtr is set to hold the integer offset within the segment of the
 *	character given by indexPtr.
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

TkTextSegment *
TkTextIndexToSeg(
    const TkTextIndex *indexPtr,/* Text index. */
    int *offsetPtr)		/* Where to store offset within segment, or
				 * NULL if offset isn't wanted. */
{
    TkTextSegment *segPtr;
    int offset;

    for (offset = indexPtr->byteIndex, segPtr = indexPtr->linePtr->segPtr;
	    offset >= segPtr->size;
	    offset -= segPtr->size, segPtr = segPtr->nextPtr) {
	/* Empty loop body. */
    }
    if (offsetPtr != NULL) {
	*offsetPtr = offset;
    }
    return segPtr;
}

/*
 *---------------------------------------------------------------------------
 *
 * TkTextSegToOffset --
 *
 *	Given a segment pointer and the line containing it, this function
 *	returns the offset of the segment within its line.
 *
 * Results:
 *	The return value is the offset (within its line) of the first
 *	character in segPtr.
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

int
TkTextSegToOffset(
    const TkTextSegment *segPtr,/* Segment whose offset is desired. */
    const TkTextLine *linePtr)	/* Line containing segPtr. */
{
    const TkTextSegment *segPtr2;
    int offset = 0;

    for (segPtr2 = linePtr->segPtr; segPtr2 != segPtr;
	    segPtr2 = segPtr2->nextPtr) {
	offset += segPtr2->size;
    }
    return offset;
}

/*
 *---------------------------------------------------------------------------
 *
 * TkTextGetObjIndex --
 *
 *	Simpler wrapper around the string based function, but could be
 *	enhanced with a new object type in the future.
 *
 * Results:
 *	see TkTextGetIndex
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

int
TkTextGetObjIndex(
    Tcl_Interp *interp,		/* Use this for error reporting. */
    TkText *textPtr,		/* Information about text widget. */
    Tcl_Obj *idxObj,		/* Object containing textual description of
				 * position. */
    TkTextIndex *indexPtr)	/* Index structure to fill in. */
{
    return GetIndex(interp, NULL, textPtr, Tcl_GetString(idxObj), indexPtr,
	    NULL);
}

/*
 *---------------------------------------------------------------------------
 *
 * TkTextSharedGetObjIndex --
 *
 *	Simpler wrapper around the string based function, but could be
 *	enhanced with a new object type in the future.
 *
 * Results:
 *	see TkTextGetIndex
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

int
TkTextSharedGetObjIndex(
    Tcl_Interp *interp,		/* Use this for error reporting. */
    TkSharedText *sharedTextPtr,/* Information about text widget. */
    Tcl_Obj *idxObj,		/* Object containing textual description of
				 * position. */
    TkTextIndex *indexPtr)	/* Index structure to fill in. */
{
    return GetIndex(interp, sharedTextPtr, NULL, Tcl_GetString(idxObj),
	    indexPtr, NULL);
}

/*
 *---------------------------------------------------------------------------
 *
 * TkTextGetIndex --
 *
 *	Given a string, return the index that is described.
 *
 * Results:
 *	The return value is a standard Tcl return result. If TCL_OK is
 *	returned, then everything went well and the index at *indexPtr is
 *	filled in; otherwise TCL_ERROR is returned and an error message is
 *	left in the interp's result.
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

int
TkTextGetIndex(
    Tcl_Interp *interp,		/* Use this for error reporting. */
    TkText *textPtr,		/* Information about text widget. */
    const char *string,		/* Textual description of position. */
    TkTextIndex *indexPtr)	/* Index structure to fill in. */
{
    return GetIndex(interp, NULL, textPtr, string, indexPtr, NULL);
}

/*
 *---------------------------------------------------------------------------
 *
 * GetIndex --
 *
 *	Given a string, return the index that is described.
 *
 * Results:
 *	The return value is a standard Tcl return result. If TCL_OK is
 *	returned, then everything went well and the index at *indexPtr is
 *	filled in; otherwise TCL_ERROR is returned and an error message is
 *	left in the interp's result.
 *
 *	If *canCachePtr is non-NULL, and everything went well, the integer it
 *	points to is set to 1 if the indexPtr is something which can be
 *	cached, and zero otherwise.
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

static int
GetIndex(
    Tcl_Interp *interp,		/* Use this for error reporting. */
    TkSharedText *sharedPtr,
    TkText *textPtr,		/* Information about text widget. */
    const char *string,		/* Textual description of position. */
    TkTextIndex *indexPtr,	/* Index structure to fill in. */
    int *canCachePtr)		/* Pointer to integer to store whether we can
				 * cache the index (or NULL). */
{
    char *p, *end, *endOfBase;
    TkTextIndex first, last;
    int wantLast, result;
    char c;
    const char *cp;
    Tcl_DString copy;
    int canCache = 0;

    if (sharedPtr == NULL) {
	sharedPtr = textPtr->sharedTextPtr;
    }

    /*
     *---------------------------------------------------------------------
     * Stage 1: check to see if the index consists of nothing but a mark name.
     * We do this check now even though it's also done later, in order to
     * allow mark names that include funny characters such as spaces or "+1c".
     *---------------------------------------------------------------------
     */

    if (TkTextMarkNameToIndex(textPtr, string, indexPtr) == TCL_OK) {
	goto done;
    }

    /*
     *------------------------------------------------
     * Stage 2: start again by parsing the base index.
     *------------------------------------------------
     */

    indexPtr->tree = sharedPtr->tree;

    /*
     * First look for the form "tag.first" or "tag.last" where "tag" is the
     * name of a valid tag. Try to use up as much as possible of the string in
     * this check (strrchr instead of strchr below). Doing the check now, and
     * in this way, allows tag names to include funny characters like "@@" or
     * "+1c".
     */

    Tcl_DStringInit(&copy);
    p = strrchr(Tcl_DStringAppend(&copy, string, -1), '.');
    if (p != NULL) {
	TkTextSearch search;
	TkTextTag *tagPtr;
	Tcl_HashEntry *hPtr = NULL;
	const char *tagName;

	if ((p[1] == 'f') && (strncmp(p+1, "first", 5) == 0)) {
	    wantLast = 0;
	    endOfBase = p+6;
	} else if ((p[1] == 'l') && (strncmp(p+1, "last", 4) == 0)) {
	    wantLast = 1;
	    endOfBase = p+5;
	} else {
	    goto tryxy;
	}

	tagPtr = NULL;
	tagName = Tcl_DStringValue(&copy);
	if (((p - tagName) == 3) && !strncmp(tagName, "sel", 3)) {
	    /*
	     * Special case for sel tag which is not stored in the hash table.
	     */

	    tagPtr = textPtr->selTagPtr;
	} else {
	    *p = 0;
	    hPtr = Tcl_FindHashEntry(&sharedPtr->tagTable, tagName);
	    *p = '.';
	    if (hPtr != NULL) {
		tagPtr = Tcl_GetHashValue(hPtr);
	    }
	}

	if (tagPtr == NULL) {
	    goto tryxy;
	}

	TkTextMakeByteIndex(sharedPtr->tree, textPtr, 0, 0, &first);
	TkTextMakeByteIndex(sharedPtr->tree, textPtr,
		TkBTreeNumLines(sharedPtr->tree, textPtr), 0, &last);
	TkBTreeStartSearch(&first, &last, tagPtr, &search);
	if (!TkBTreeCharTagged(&first, tagPtr) && !TkBTreeNextTag(&search)) {
	    if (tagPtr == textPtr->selTagPtr) {
		tagName = "sel";
	    } else {
		if (hPtr != NULL) {
		    tagName = Tcl_GetHashKey(&sharedPtr->tagTable, hPtr);
		}
	    }
	    Tcl_ResetResult(interp);
	    Tcl_AppendResult(interp,
		    "text doesn't contain any characters tagged with \"",
		    tagName, "\"", NULL);
	    Tcl_DStringFree(&copy);
	    return TCL_ERROR;
	}
	*indexPtr = search.curIndex;
	if (wantLast) {
	    while (TkBTreeNextTag(&search)) {
		*indexPtr = search.curIndex;
	    }
	}
	goto gotBase;
    }

  tryxy:
    if (string[0] == '@@') {
	/*
	 * Find character at a given x,y location in the window.
	 */

	int x, y;

	cp = string+1;
	x = strtol(cp, &end, 0);
	if ((end == cp) || (*end != ',')) {
	    goto error;
	}
	cp = end+1;
	y = strtol(cp, &end, 0);
	if (end == cp) {
	    goto error;
	}
	TkTextPixelIndex(textPtr, x, y, indexPtr, NULL);
	endOfBase = end;
	goto gotBase;
    }

    if (isdigit(UCHAR(string[0])) || (string[0] == '-')) {
	int lineIndex, charIndex;

	/*
	 * Base is identified with line and character indices.
	 */

	lineIndex = strtol(string, &end, 0) - 1;
	if ((end == string) || (*end != '.')) {
	    goto error;
	}
	p = end+1;
	if ((*p == 'e') && (strncmp(p, "end", 3) == 0)) {
	    charIndex = LAST_CHAR;
	    endOfBase = p+3;
	} else {
	    charIndex = strtol(p, &end, 0);
	    if (end == p) {
		goto error;
	    }
	    endOfBase = end;
	}
	TkTextMakeCharIndex(sharedPtr->tree, textPtr, lineIndex, charIndex,
		indexPtr);
	canCache = 1;
	goto gotBase;
    }

    for (p = Tcl_DStringValue(&copy); *p != 0; p++) {
	if (isspace(UCHAR(*p)) || (*p == '+') || (*p == '-')) {
	    break;
	}
    }
    endOfBase = p;
    if (string[0] == '.') {
	/*
	 * See if the base position is the name of an embedded window.
	 */

	c = *endOfBase;
	*endOfBase = 0;
	result = TkTextWindowIndex(textPtr, Tcl_DStringValue(&copy), indexPtr);
	*endOfBase = c;
	if (result != 0) {
	    goto gotBase;
	}
    }
    if ((string[0] == 'e')
	    && (strncmp(string, "end",
	    (size_t) (endOfBase-Tcl_DStringValue(&copy))) == 0)) {
	/*
	 * Base position is end of text.
	 */

	TkTextMakeByteIndex(sharedPtr->tree, textPtr,
		TkBTreeNumLines(sharedPtr->tree, textPtr), 0, indexPtr);
	canCache = 1;
	goto gotBase;
    } else {
	/*
	 * See if the base position is the name of a mark.
	 */

	c = *endOfBase;
	*endOfBase = 0;
	result = TkTextMarkNameToIndex(textPtr, Tcl_DStringValue(&copy),
		indexPtr);
	*endOfBase = c;
	if (result == TCL_OK) {
	    goto gotBase;
	}

	/*
	 * See if the base position is the name of an embedded image.
	 */

	c = *endOfBase;
	*endOfBase = 0;
	result = TkTextImageIndex(textPtr, Tcl_DStringValue(&copy), indexPtr);
	*endOfBase = c;
	if (result != 0) {
	    goto gotBase;
	}
    }
    goto error;

    /*
     *-------------------------------------------------------------------
     * Stage 3: process zero or more modifiers. Each modifier is either a
     * keyword like "wordend" or "linestart", or it has the form "op count
     * units" where op is + or -, count is a number, and units is "chars" or
     * "lines".
     *-------------------------------------------------------------------
     */

  gotBase:
    cp = endOfBase;
    while (1) {
	while (isspace(UCHAR(*cp))) {
	    cp++;
	}
	if (*cp == 0) {
	    break;
	}

	if ((*cp == '+') || (*cp == '-')) {
	    cp = ForwBack(textPtr, cp, indexPtr);
	} else {
	    cp = StartEnd(textPtr, cp, indexPtr);
	}
	if (cp == NULL) {
	    goto error;
	}
    }
    Tcl_DStringFree(&copy);

  done:
    if (canCachePtr != NULL) {
	*canCachePtr = canCache;
    }
    if (indexPtr->linePtr == NULL) {
	Tcl_Panic("Bad index created");
    }
    return TCL_OK;

  error:
    Tcl_DStringFree(&copy);
    Tcl_ResetResult(interp);
    Tcl_AppendResult(interp, "bad text index \"", string, "\"", NULL);
    return TCL_ERROR;
}

/*
 *---------------------------------------------------------------------------
 *
 * TkTextPrintIndex --
 *
 *	This function generates a string description of an index, suitable for
 *	reading in again later.
 *
 * Results:
 *	The characters pointed to by string are modified. Returns the number
 *	of characters in the string.
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

int
TkTextPrintIndex(
    const TkText *textPtr,
    const TkTextIndex *indexPtr,/* Pointer to index. */
    char *string)		/* Place to store the position. Must have at
				 * least TK_POS_CHARS characters. */
{
    TkTextSegment *segPtr;
    TkTextLine *linePtr;
    int numBytes, charIndex;

    numBytes = indexPtr->byteIndex;
    charIndex = 0;
    linePtr = indexPtr->linePtr;

    for (segPtr = linePtr->segPtr; ; 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;
	}
	if (numBytes <= segPtr->size) {
	    break;
	}
	if (segPtr->typePtr == &tkTextCharType) {
	    charIndex += Tcl_NumUtfChars(segPtr->body.chars, segPtr->size);
	} else {
	    charIndex += segPtr->size;
	}
	numBytes -= segPtr->size;
    }

    if (segPtr->typePtr == &tkTextCharType) {
	charIndex += Tcl_NumUtfChars(segPtr->body.chars, numBytes);
    } else {
	charIndex += numBytes;
    }

    return sprintf(string, "%d.%d",
	    TkBTreeLinesTo(textPtr, indexPtr->linePtr) + 1, charIndex);
}

/*
 *---------------------------------------------------------------------------
 *
 * TkTextIndexCmp --
 *
 *	Compare two indices to see which one is earlier in the text.
 *
 * Results:
 *	The return value is 0 if index1Ptr and index2Ptr refer to the same
 *	position in the file, -1 if index1Ptr refers to an earlier position
 *	than index2Ptr, and 1 otherwise.
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

int
TkTextIndexCmp(
    const TkTextIndex*index1Ptr,/* First index. */
    const TkTextIndex*index2Ptr)/* Second index. */
{
    int line1, line2;

    if (index1Ptr->linePtr == index2Ptr->linePtr) {
	if (index1Ptr->byteIndex < index2Ptr->byteIndex) {
	    return -1;
	} else if (index1Ptr->byteIndex > index2Ptr->byteIndex) {
	    return 1;
	} else {
	    return 0;
	}
    }

    /*
     * Assumption here that it is ok for comparisons to reflect the full
     * B-tree and not just the portion that is available to any client. This
     * should be true because the only indexPtr's we should be given are ones
     * which are valid for the current client.
     */

    line1 = TkBTreeLinesTo(NULL, index1Ptr->linePtr);
    line2 = TkBTreeLinesTo(NULL, index2Ptr->linePtr);
    if (line1 < line2) {
	return -1;
    }
    if (line1 > line2) {
	return 1;
    }
    return 0;
}

/*
 *---------------------------------------------------------------------------
 *
 * ForwBack --
 *
 *	This function handles +/- modifiers for indices to adjust the index
 *	forwards or backwards.
 *
 * Results:
 *	If the modifier in string is successfully parsed then the return value
 *	is the address of the first character after the modifier, and
 *	*indexPtr is updated to reflect the modifier. If there is a syntax
 *	error in the modifier then NULL is returned.
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

static const char *
ForwBack(
    TkText *textPtr,		/* Information about text widget. */
    const char *string,		/* String to parse for additional info about
				 * modifier (count and units). Points to "+"
				 * or "-" that starts modifier. */
    TkTextIndex *indexPtr)	/* Index to update as specified in string. */
{
    register const char *p, *units;
    char *end;
    int count, lineIndex, modifier;
    size_t length;

    /*
     * Get the count (how many units forward or backward).
     */

    p = string+1;
    while (isspace(UCHAR(*p))) {
	p++;
    }
    count = strtol(p, &end, 0);
    if (end == p) {
	return NULL;
    }
    p = end;
    while (isspace(UCHAR(*p))) {
	p++;
    }

    /*
     * Find the end of this modifier (next space or + or - character), then
     * check if there is a textual 'display' or 'any' modifier. These
     * modifiers can be their own word (in which case they can be abbreviated)
     * or they can follow on to the actual unit in a single word (in which
     * case no abbreviation is allowed). So, 'display lines', 'd lines',
     * 'displaylin' are all ok, but 'dline' is not.
     */

    units = p;
    while ((*p != '\0') && !isspace(UCHAR(*p)) && (*p != '+') && (*p != '-')) {
	p++;
    }
    length = p - units;
    if ((*units == 'd') &&
	    (strncmp(units, "display", (length > 7 ? 7 : length)) == 0)) {
	modifier = TKINDEX_DISPLAY;
	if (length > 7) {
	    p -= (length - 7);
	}
    } else if ((*units == 'a') &&
	    (strncmp(units, "any", (length > 3 ? 3 : length)) == 0)) {
	modifier = TKINDEX_ANY;
	if (length > 3) {
	    p -= (length - 3);
	}
    } else {
	modifier = TKINDEX_NONE;
    }

    /*
     * If we had a modifier, which we interpreted ok, so now forward to the
     * actual units.
     */

    if (modifier != TKINDEX_NONE) {
	while (isspace(UCHAR(*p))) {
	    p++;
	}
	units = p;
	while (*p!='\0' && !isspace(UCHAR(*p)) && *p!='+' && *p!='-') {
	    p++;
	}
	length = p - units;
    }

    /*
     * Finally parse the units.
     */

    if ((*units == 'c') && (strncmp(units, "chars", length) == 0)) {
	TkTextCountType type;

	if (modifier == TKINDEX_NONE) {
	    type = COUNT_INDICES;
	} else if (modifier == TKINDEX_ANY) {
	    type = COUNT_CHARS;
	} else {
	    type = COUNT_DISPLAY_CHARS;
	}

	if (*string == '+') {
	    TkTextIndexForwChars(textPtr, indexPtr, count, indexPtr, type);
	} else {
	    TkTextIndexBackChars(textPtr, indexPtr, count, indexPtr, type);
	}
    } else if ((*units == 'i') && (strncmp(units, "indices", length) == 0)) {
	TkTextCountType type;

	if (modifier == TKINDEX_DISPLAY) {
	    type = COUNT_DISPLAY_INDICES;
	} else {
	    type = COUNT_INDICES;
	}

	if (*string == '+') {
	    TkTextIndexForwChars(textPtr, indexPtr, count, indexPtr, type);
	} else {
	    TkTextIndexBackChars(textPtr, indexPtr, count, indexPtr, type);
	}
    } else if ((*units == 'l') && (strncmp(units, "lines", length) == 0)) {
	if (modifier == TKINDEX_DISPLAY) {
	    /*
	     * Find the appropriate pixel offset of the current position
	     * within its display line. This also has the side-effect of
	     * moving indexPtr, but that doesn't matter since we will do it
	     * again below.
	     *
	     * Then find the right display line, and finally calculated the
	     * index we want in that display line, based on the original pixel
	     * offset.
	     */

	    int xOffset, forward;

	    if (TkTextIsElided(textPtr, indexPtr, NULL)) {
		/*
		 * Go forward to the first non-elided index.
		 */

		TkTextIndexForwChars(textPtr, indexPtr, 0, indexPtr,
			COUNT_DISPLAY_INDICES);
	    }

	    /*
	     * Unlike the Forw/BackChars code, the display line code is
	     * sensitive to whether we are genuinely going forwards or
	     * backwards. So, we need to determine that. This is important in
	     * the case where we have "+ -3 displaylines", for example.
	     */

	    if ((count < 0) ^ (*string == '-')) {
		forward = 0;
	    } else {
		forward = 1;
	    }

	    count = abs(count);
	    if (count == 0) {
		return p;
	    }

	    if (forward) {
		TkTextFindDisplayLineEnd(textPtr, indexPtr, 1, &xOffset);
		while (count-- > 0) {
		    /*
		     * Go to the end of the line, then forward one char/byte
		     * to get to the beginning of the next line.
		     */

		    TkTextFindDisplayLineEnd(textPtr, indexPtr, 1, NULL);
		    TkTextIndexForwChars(textPtr, indexPtr, 1, indexPtr,
			    COUNT_DISPLAY_INDICES);
		}
	    } else {
		TkTextFindDisplayLineEnd(textPtr, indexPtr, 0, &xOffset);
		while (count-- > 0) {
		    /*
		     * Go to the beginning of the line, then backward one
		     * char/byte to get to the end of the previous line.
		     */

		    TkTextFindDisplayLineEnd(textPtr, indexPtr, 0, NULL);
		    TkTextIndexBackChars(textPtr, indexPtr, 1, indexPtr,
			    COUNT_DISPLAY_INDICES);
		}
		TkTextFindDisplayLineEnd(textPtr, indexPtr, 0, NULL);
	    }

	    /*
	     * This call assumes indexPtr is the beginning of a display line
	     * and moves it to the 'xOffset' position of that line, which is
	     * just what we want.
	     */

	    TkTextIndexOfX(textPtr, xOffset, indexPtr);
	} else {
	    lineIndex = TkBTreeLinesTo(textPtr, indexPtr->linePtr);
	    if (*string == '+') {
		lineIndex += count;
	    } else {
		lineIndex -= count;

		/*
		 * The check below retains the character position, even if the
		 * line runs off the start of the file. Without it, the
		 * character position will get reset to 0 by TkTextMakeIndex.
		 */

		if (lineIndex < 0) {
		    lineIndex = 0;
		}
	    }

	    /*
	     * This doesn't work quite right if using a proportional font or
	     * UTF-8 characters with varying numbers of bytes, or if there are
	     * embedded windows, images, etc. The cursor will bop around,
	     * keeping a constant number of bytes (not characters) from the
	     * left edge (but making sure not to split any UTF-8 characters),
	     * regardless of the x-position the index corresponds to. The
	     * proper way to do this is to get the x-position of the index and
	     * then pick the character at the same x-position in the new line.
	     */

	    TkTextMakeByteIndex(indexPtr->tree, textPtr, lineIndex,
		    indexPtr->byteIndex, indexPtr);
	}
    } else {
	return NULL;
    }
    return p;
}

/*
 *---------------------------------------------------------------------------
 *
 * TkTextIndexForwBytes --
 *
 *	Given an index for a text widget, this function creates a new index
 *	that points "count" bytes ahead of the source index.
 *
 * Results:
 *	*dstPtr is modified to refer to the character "count" bytes after
 *	srcPtr, or to the last character in the TkText if there aren't "count"
 *	bytes left.
 *
 *	In this latter case, the function returns '1' to indicate that not all
 *	of 'byteCount' could be used.
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

int
TkTextIndexForwBytes(
    const TkText *textPtr,
    const TkTextIndex *srcPtr,	/* Source index. */
    int byteCount,		/* How many bytes forward to move. May be
				 * negative. */
    TkTextIndex *dstPtr)	/* Destination index: gets modified. */
{
    TkTextLine *linePtr;
    TkTextSegment *segPtr;
    int lineLength;

    if (byteCount < 0) {
	TkTextIndexBackBytes(textPtr, srcPtr, -byteCount, dstPtr);
	return 0;
    }

    *dstPtr = *srcPtr;
    dstPtr->byteIndex += byteCount;
    while (1) {
	/*
	 * Compute the length of the current line.
	 */

	lineLength = 0;
	for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
		segPtr = segPtr->nextPtr) {
	    lineLength += segPtr->size;
	}

	/*
	 * If the new index is in the same line then we're done. Otherwise go
	 * on to the next line.
	 */

	if (dstPtr->byteIndex < lineLength) {
	    return 0;
	}
	dstPtr->byteIndex -= lineLength;
	linePtr = TkBTreeNextLine(textPtr, dstPtr->linePtr);
	if (linePtr == NULL) {
	    dstPtr->byteIndex = lineLength - 1;
	    return 1;
	}
	dstPtr->linePtr = linePtr;
    }
}

/*
 *---------------------------------------------------------------------------
 *
 * TkTextIndexForwChars --
 *
 *	Given an index for a text widget, this function creates a new index
 *	that points "count" items of type given by "type" ahead of the source
 *	index. "count" can be zero, which is useful in the case where one
 *	wishes to move forward by display (non-elided) chars or indices or one
 *	wishes to move forward by chars, skipping any intervening indices. In
 *	this case dstPtr will point to the first acceptable index which is
 *	encountered.
 *
 * Results:
 *	*dstPtr is modified to refer to the character "count" items after
 *	srcPtr, or to the last character in the TkText if there aren't
 *	sufficient items left in the widget.
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

void
TkTextIndexForwChars(
    const TkText *textPtr,	/* Overall information about text widget. */
    const TkTextIndex *srcPtr,	/* Source index. */
    int charCount,		/* How many characters forward to move. May
				 * be negative. */
    TkTextIndex *dstPtr,	/* Destination index: gets modified. */
    TkTextCountType type)	/* The type of item to count */
{
    TkTextLine *linePtr;
    TkTextSegment *segPtr;
    TkTextElideInfo *infoPtr = NULL;
    int byteOffset;
    char *start, *end, *p;
    Tcl_UniChar ch;
    int elide = 0;
    int checkElided = (type & COUNT_DISPLAY);

    if (charCount < 0) {
	TkTextIndexBackChars(textPtr, srcPtr, -charCount, dstPtr, type);
	return;
    }
    if (checkElided) {
	infoPtr = (TkTextElideInfo *) ckalloc(sizeof(TkTextElideInfo));
	elide = TkTextIsElided(textPtr, srcPtr, infoPtr);
    }

    *dstPtr = *srcPtr;

    /*
     * Find seg that contains src byteIndex. Move forward specified number of
     * chars.
     */

    if (checkElided) {
	/*
	 * In this case we have already calculated the information we need, so
	 * no need to use TkTextIndexToSeg()
	 */

	segPtr = infoPtr->segPtr;
	byteOffset = dstPtr->byteIndex - infoPtr->segOffset;
    } else {
	segPtr = TkTextIndexToSeg(dstPtr, &byteOffset);
    }

    while (1) {
	/*
	 * Go through each segment in line looking for specified character
	 * index.
	 */

	for ( ; segPtr != NULL; segPtr = segPtr->nextPtr) {
	    /*
	     * If we do need to pay attention to the visibility of
	     * characters/indices, check that first. If the current segment
	     * isn't visible, then we simply continue the loop.
	     */

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

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

		if (tagPtr->elideString != NULL) {
		    infoPtr->tagCnts[tagPtr->priority]++;
		    if (infoPtr->tagCnts[tagPtr->priority] & 1) {
			infoPtr->tagPtrs[tagPtr->priority] = tagPtr;
		    }

		    if (tagPtr->priority >= infoPtr->elidePriority) {
			if (segPtr->typePtr == &tkTextToggleOffType) {
			    /*
			     * If it is being toggled off, and it has an elide
			     * string, it must actually be the current highest
			     * priority tag, so this check is redundant:
			     */

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

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

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

	    if (!elide) {
		if (segPtr->typePtr == &tkTextCharType) {
		    start = segPtr->body.chars + byteOffset;
		    end = segPtr->body.chars + segPtr->size;
		    for (p = start; p < end; p += Tcl_UtfToUniChar(p, &ch)) {
			if (charCount == 0) {
			    dstPtr->byteIndex += (p - start);
			    goto forwardCharDone;
			}
			charCount--;
		    }
		} else if (type & COUNT_INDICES) {
		    if (charCount < segPtr->size - byteOffset) {
			dstPtr->byteIndex += charCount;
			goto forwardCharDone;
		    }
		    charCount -= segPtr->size - byteOffset;
		}
	    }

	    dstPtr->byteIndex += segPtr->size - byteOffset;
	    byteOffset = 0;
	}

	/*
	 * Go to the next line. If we are at the end of the text item, back up
	 * one byte (for the terminal '\n' character) and return that index.
	 */

	linePtr = TkBTreeNextLine(textPtr, dstPtr->linePtr);
	if (linePtr == NULL) {
	    dstPtr->byteIndex -= sizeof(char);
	    goto forwardCharDone;
	}
	dstPtr->linePtr = linePtr;
	dstPtr->byteIndex = 0;
	segPtr = dstPtr->linePtr->segPtr;
    }

  forwardCharDone:
    if (infoPtr != NULL) {
	TkTextFreeElideInfo(infoPtr);
	ckfree((char *) infoPtr);
    }
}

/*
 *---------------------------------------------------------------------------
 *
 * TkTextIndexCount --
 *
 *	Given an ordered pair of indices in a text widget, this function
 *	counts how many characters (not bytes) are between the two indices.
 *
 *	It is illegal to call this function with unordered indices.
 *
 *	Note that 'textPtr' is only used if we need to check for elided
 *	attributes, i.e. if type is COUNT_DISPLAY_INDICES or
 *	COUNT_DISPLAY_CHARS.
 *
 * Results:
 *	The number of characters in the given range, which meet the
 *	appropriate 'type' attributes.
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

int
TkTextIndexCount(
    const TkText *textPtr,	/* Overall information about text widget. */
    const TkTextIndex *indexPtr1,
				/* Index describing location of character from
				 * which to count. */
    const TkTextIndex *indexPtr2,
				/* Index describing location of last character
				 * at which to stop the count. */
    TkTextCountType type)	/* The kind of indices to count. */
{
    TkTextLine *linePtr1;
    TkTextSegment *segPtr, *seg2Ptr = NULL;
    TkTextElideInfo *infoPtr = NULL;
    int byteOffset, maxBytes, count = 0, elide = 0;
    int checkElided = (type & COUNT_DISPLAY);

    /*
     * Find seg that contains src index, and remember how many bytes not to
     * count in the given segment.
     */

    segPtr = TkTextIndexToSeg(indexPtr1, &byteOffset);
    linePtr1 = indexPtr1->linePtr;

    seg2Ptr = TkTextIndexToSeg(indexPtr2, &maxBytes);

    if (checkElided) {
	infoPtr = (TkTextElideInfo *) ckalloc(sizeof(TkTextElideInfo));
	elide = TkTextIsElided(textPtr, indexPtr1, infoPtr);
    }

    while (1) {
	/*
	 * Go through each segment in line adding up the number of characters.
	 */

	for ( ; segPtr != NULL; segPtr = segPtr->nextPtr) {
	    /*
	     * If we do need to pay attention to the visibility of
	     * characters/indices, check that first. If the current segment
	     * isn't visible, then we simply continue the loop.
	     */

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

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

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

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

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

				elide = 0;
				while (--infoPtr->elidePriority > 0) {
				    if (infoPtr->tagCnts[
					    infoPtr->elidePriority] & 1) {
					elide = infoPtr->tagPtrs[
						infoPtr->elidePriority]->elide;
					break;
				    }
				}
			    } else {
				elide = tagPtr->elide;
				infoPtr->elidePriority = tagPtr->priority;
			    }
			}
		    }
		}
		if (elide) {
		    if (segPtr == seg2Ptr) {
			goto countDone;
		    }
		    byteOffset = 0;
		    continue;
		}
	    }

	    if (segPtr->typePtr == &tkTextCharType) {
		int byteLen = segPtr->size - byteOffset;
		register unsigned char *str = (unsigned char *)
			segPtr->body.chars + byteOffset;
		register int i;

		if (segPtr == seg2Ptr) {
		    if (byteLen > (maxBytes - byteOffset)) {
			byteLen = maxBytes - byteOffset;
		    }
		}
		i = byteLen;

		/*
		 * This is a speed sensitive function, so run specially over
		 * the string to count continuous ascii characters before
		 * resorting to the Tcl_NumUtfChars call. This is a long form
		 * of:
		 *
		 *   stringPtr->numChars =
		 *	     Tcl_NumUtfChars(objPtr->bytes, objPtr->length);
		 */

		while (i && (*str < 0xC0)) {
		    i--;
		    str++;
		}
		count += byteLen - i;
		if (i) {
		    count += Tcl_NumUtfChars(segPtr->body.chars + byteOffset
			    + (byteLen - i), i);
		}
	    } else {
		if (type & COUNT_INDICES) {
		    int byteLen = segPtr->size - byteOffset;

		    if (segPtr == seg2Ptr) {
			if (byteLen > (maxBytes - byteOffset)) {
			    byteLen = maxBytes - byteOffset;
			}
		    }
		    count += byteLen;
		}
	    }
	    if (segPtr == seg2Ptr) {
		goto countDone;
	    }
	    byteOffset = 0;
	}

	/*
	 * Go to the next line. If we are at the end of the text item, back up
	 * one byte (for the terminal '\n' character) and return that index.
	 */

	linePtr1 = TkBTreeNextLine(textPtr, linePtr1);
	if (linePtr1 == NULL) {
	    Tcl_Panic("Reached end of text widget when counting characters");
	}
	segPtr = linePtr1->segPtr;
    }

  countDone:
    if (infoPtr != NULL) {
	TkTextFreeElideInfo(infoPtr);
	ckfree((char *) infoPtr);
    }
    return count;
}

/*
 *---------------------------------------------------------------------------
 *
 * TkTextIndexBackBytes --
 *
 *	Given an index for a text widget, this function creates a new index
 *	that points "count" bytes earlier than the source index.
 *
 * Results:
 *	*dstPtr is modified to refer to the character "count" bytes before
 *	srcPtr, or to the first character in the TkText if there aren't
 *	"count" bytes earlier than srcPtr.
 *
 *	Returns 1 if we couldn't use all of 'byteCount' because we have run
 *	into the beginning or end of the text, and zero otherwise.
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

int
TkTextIndexBackBytes(
    const TkText *textPtr,
    const TkTextIndex *srcPtr,	/* Source index. */
    int byteCount,		/* How many bytes backward to move. May be
				 * negative. */
    TkTextIndex *dstPtr)	/* Destination index: gets modified. */
{
    TkTextSegment *segPtr;
    int lineIndex;

    if (byteCount < 0) {
	return TkTextIndexForwBytes(textPtr, srcPtr, -byteCount, dstPtr);
    }

    *dstPtr = *srcPtr;
    dstPtr->byteIndex -= byteCount;
    lineIndex = -1;
    while (dstPtr->byteIndex < 0) {
	/*
	 * Move back one line in the text. If we run off the beginning of the
	 * file then just return the first character in the text.
	 */

	if (lineIndex < 0) {
	    lineIndex = TkBTreeLinesTo(textPtr, dstPtr->linePtr);
	}
	if (lineIndex == 0) {
	    dstPtr->byteIndex = 0;
	    return 1;
	}
	lineIndex--;
	dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, textPtr, lineIndex);

	/*
	 * Compute the length of the line and add that to dstPtr->charIndex.
	 */

	for (segPtr = dstPtr->linePtr->segPtr; segPtr != NULL;
		segPtr = segPtr->nextPtr) {
	    dstPtr->byteIndex += segPtr->size;
	}
    }
    return 0;
}

/*
 *---------------------------------------------------------------------------
 *
 * TkTextIndexBackChars --
 *
 *	Given an index for a text widget, this function creates a new index
 *	that points "count" items of type given by "type" earlier than the
 *	source index. "count" can be zero, which is useful in the case where
 *	one wishes to move backward by display (non-elided) chars or indices
 *	or one wishes to move backward by chars, skipping any intervening
 *	indices. In this case the returned index *dstPtr will point just
 *	_after_ the first acceptable index which is encountered.
 *
 * Results:
 *	*dstPtr is modified to refer to the character "count" items before
 *	srcPtr, or to the first index in the window if there aren't sufficient
 *	items earlier than srcPtr.
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

void
TkTextIndexBackChars(
    const TkText *textPtr,	/* Overall information about text widget. */
    const TkTextIndex *srcPtr,	/* Source index. */
    int charCount,		/* How many characters backward to move. May
				 * be negative. */
    TkTextIndex *dstPtr,	/* Destination index: gets modified. */
    TkTextCountType type)	/* The type of item to count */
{
    TkTextSegment *segPtr, *oldPtr;
    TkTextElideInfo *infoPtr = NULL;
    int lineIndex, segSize;
    const char *p, *start, *end;
    int elide = 0;
    int checkElided = (type & COUNT_DISPLAY);

    if (charCount < 0) {
	TkTextIndexForwChars(textPtr, srcPtr, -charCount, dstPtr, type);
	return;
    }
    if (checkElided) {
	infoPtr = (TkTextElideInfo *) ckalloc(sizeof(TkTextElideInfo));
	elide = TkTextIsElided(textPtr, srcPtr, infoPtr);
    }

    *dstPtr = *srcPtr;

    /*
     * Find offset within seg that contains byteIndex. Move backward specified
     * number of chars.
     */

    lineIndex = -1;

    segSize = dstPtr->byteIndex;

    if (checkElided) {
	segPtr = infoPtr->segPtr;
	segSize -= infoPtr->segOffset;
    } else {
	TkTextLine *linePtr = dstPtr->linePtr;
	for (segPtr = linePtr->segPtr; ; 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;
	    }
	    if (segSize <= segPtr->size) {
		break;
	    }
	    segSize -= segPtr->size;
	}
    }

    /*
     * Now segPtr points to the segment containing the starting index.
     */

    while (1) {
	/*
	 * If we do need to pay attention to the visibility of
	 * characters/indices, check that first. If the current segment isn't
	 * visible, then we simply continue the loop.
	 */

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

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

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

			if (tagPtr->priority != infoPtr->elidePriority) {
			    Tcl_Panic("Bad tag priority being toggled on");
			}

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

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

	if (!elide) {
	    if (segPtr->typePtr == &tkTextCharType) {
		start = segPtr->body.chars;
		end = segPtr->body.chars + segSize;
		for (p = end; ; p = Tcl_UtfPrev(p, start)) {
		    if (charCount == 0) {
			dstPtr->byteIndex -= (end - p);
			goto backwardCharDone;
		    }
		    if (p == start) {
			break;
		    }
		    charCount--;
		}
	    } else {
		if (type & COUNT_INDICES) {
		    if (charCount <= segSize) {
			dstPtr->byteIndex -= charCount;
			goto backwardCharDone;
		    }
		    charCount -= segSize;
		}
	    }
	}
	dstPtr->byteIndex -= segSize;

	/*
	 * Move back into previous segment.
	 */

	oldPtr = segPtr;
	segPtr = dstPtr->linePtr->segPtr;
	if (segPtr != oldPtr) {
	    for ( ; segPtr->nextPtr != oldPtr; segPtr = segPtr->nextPtr) {
		/* Empty body. */
	    }
	    segSize = segPtr->size;
	    continue;
	}

	/*
	 * Move back to previous line.
	 */

	if (lineIndex < 0) {
	    lineIndex = TkBTreeLinesTo(textPtr, dstPtr->linePtr);
	}
	if (lineIndex == 0) {
	    dstPtr->byteIndex = 0;
	    goto backwardCharDone;
	}
	lineIndex--;
	dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, textPtr, lineIndex);

	/*
	 * Compute the length of the line and add that to dstPtr->byteIndex.
	 */

	oldPtr = dstPtr->linePtr->segPtr;
	for (segPtr = oldPtr; segPtr != NULL; segPtr = segPtr->nextPtr) {
	    dstPtr->byteIndex += segPtr->size;
	    oldPtr = segPtr;
	}
	segPtr = oldPtr;
	segSize = segPtr->size;
    }

  backwardCharDone:
    if (infoPtr != NULL) {
	TkTextFreeElideInfo(infoPtr);
	ckfree((char *) infoPtr);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * StartEnd --
 *
 *	This function handles modifiers like "wordstart" and "lineend" to
 *	adjust indices forwards or backwards.
 *
 * Results:
 *	If the modifier is successfully parsed then the return value is the
 *	address of the first character after the modifier, and *indexPtr is
 *	updated to reflect the modifier. If there is a syntax error in the
 *	modifier then NULL is returned.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static const char *
StartEnd(
    TkText *textPtr,		/* Information about text widget. */
    const char *string,		/* String to parse for additional info about
				 * modifier (count and units). Points to first
				 * character of modifer word. */
    TkTextIndex *indexPtr)	/* Index to modify based on string. */
{
    const char *p;
    size_t length;
    register TkTextSegment *segPtr;
    int modifier;

    /*
     * Find the end of the modifier word.
     */

    for (p = string; isalnum(UCHAR(*p)); p++) {
	/* Empty loop body. */
    }

    length = p-string;
    if ((*string == 'd') &&
	    (strncmp(string, "display", (length > 7 ? 7 : length)) == 0)) {
	modifier = TKINDEX_DISPLAY;
	if (length > 7) {
	    p -= (length - 7);
	}
    } else if ((*string == 'a') &&
	    (strncmp(string, "any", (length > 3 ? 3 : length)) == 0)) {
	modifier = TKINDEX_ANY;
	if (length > 3) {
	    p -= (length - 3);
	}
    } else {
	modifier = TKINDEX_NONE;
    }

    /*
     * If we had a modifier, which we interpreted ok, so now forward to the
     * actual units.
     */

    if (modifier != TKINDEX_NONE) {
	while (isspace(UCHAR(*p))) {
	    p++;
	}
	string = p;
	while ((*p!='\0') && !isspace(UCHAR(*p)) && (*p!='+') && (*p!='-')) {
	    p++;
	}
	length = p - string;
    }

    if ((*string == 'l') && (strncmp(string, "lineend", length) == 0)
	    && (length >= 5)) {
	if (modifier == TKINDEX_DISPLAY) {
	    TkTextFindDisplayLineEnd(textPtr, indexPtr, 1, NULL);
	} else {
	    indexPtr->byteIndex = 0;
	    for (segPtr = indexPtr->linePtr->segPtr; segPtr != NULL;
		    segPtr = segPtr->nextPtr) {
		indexPtr->byteIndex += segPtr->size;
	    }

	    /*
	     * We know '\n' is encoded with a single byte index.
	     */

	    indexPtr->byteIndex -= sizeof(char);
	}
    } else if ((*string == 'l') && (strncmp(string, "linestart", length) == 0)
	    && (length >= 5)) {
	if (modifier == TKINDEX_DISPLAY) {
	    TkTextFindDisplayLineEnd(textPtr, indexPtr, 0, NULL);
	} else {
	    indexPtr->byteIndex = 0;
	}
    } else if ((*string == 'w') && (strncmp(string, "wordend", length) == 0)
	    && (length >= 5)) {
	int firstChar = 1;
	int offset;

	/*
	 * If the current character isn't part of a word then just move
	 * forward one character. Otherwise move forward until finding a
	 * character that isn't part of a word and stop there.
	 */

	if (modifier == TKINDEX_DISPLAY) {
	    TkTextIndexForwChars(textPtr, indexPtr, 0, indexPtr,
		    COUNT_DISPLAY_INDICES);
	}
	segPtr = TkTextIndexToSeg(indexPtr, &offset);
	while (1) {
	    int chSize = 1;

	    if (segPtr->typePtr == &tkTextCharType) {
		Tcl_UniChar ch;

		chSize = Tcl_UtfToUniChar(segPtr->body.chars + offset, &ch);
		if (!Tcl_UniCharIsWordChar(ch)) {
		    break;
		}
		firstChar = 0;
	    }
	    offset += chSize;
	    indexPtr->byteIndex += chSize;
	    if (offset >= segPtr->size) {
		segPtr = TkTextIndexToSeg(indexPtr, &offset);
	    }
	}
	if (firstChar) {
	    if (modifier == TKINDEX_DISPLAY) {
		TkTextIndexForwChars(textPtr, indexPtr, 1, indexPtr,
			COUNT_DISPLAY_INDICES);
	    } else {
		TkTextIndexForwChars(NULL, indexPtr, 1, indexPtr,
			COUNT_INDICES);
	    }
	}
    } else if ((*string == 'w') && (strncmp(string, "wordstart", length) == 0)
	    && (length >= 5)) {
	int firstChar = 1;
	int offset;

	if (modifier == TKINDEX_DISPLAY) {
	    TkTextIndexForwChars(NULL, indexPtr, 0, indexPtr,
		    COUNT_DISPLAY_INDICES);
	}

	/*
	 * Starting with the current character, look for one that's not part
	 * of a word and keep moving backward until you find one. Then if the
	 * character found wasn't the first one, move forward again one
	 * position.
	 */

	segPtr = TkTextIndexToSeg(indexPtr, &offset);
	while (1) {
	    int chSize = 1;

	    if (segPtr->typePtr == &tkTextCharType) {
		Tcl_UniChar ch;

		Tcl_UtfToUniChar(segPtr->body.chars + offset, &ch);
		if (!Tcl_UniCharIsWordChar(ch)) {
		    break;
		}
		if (offset > 0) {
		    chSize = (segPtr->body.chars + offset
			    - Tcl_UtfPrev(segPtr->body.chars + offset,
			    segPtr->body.chars));
		}
		firstChar = 0;
	    }
	    offset -= chSize;
	    indexPtr->byteIndex -= chSize;
	    if (offset < 0) {
		if (indexPtr->byteIndex < 0) {
		    indexPtr->byteIndex = 0;
		    goto done;
		}
		segPtr = TkTextIndexToSeg(indexPtr, &offset);
	    }
	}

	if (!firstChar) {
	    if (modifier == TKINDEX_DISPLAY) {
		TkTextIndexForwChars(textPtr, indexPtr, 1, indexPtr,
			COUNT_DISPLAY_INDICES);
	    } else {
		TkTextIndexForwChars(NULL, indexPtr, 1, indexPtr,
			COUNT_INDICES);
	    }
	}
    } else {
	return NULL;
    }

  done:
    return p;
}

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */
@


1.36
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
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.35 2009/02/06 08:12:07 das Exp $
d69 1
a69 1

d95 1
a95 1
	    ckfree((char *) (indexPtr->textPtr));
d99 1
d125 1
a125 1

d157 1
a157 1

d254 1
a254 1

d299 1
a299 1

d367 1
a367 1

d460 1
a460 1

d555 1
a555 1

d595 1
a595 1

d628 1
a628 1

d657 1
a657 1

d686 1
a686 1

d715 1
a715 1

d1010 1
a1010 1

d1074 1
a1074 1

d1127 1
a1127 1

d1371 1
a1371 1

d1441 1
a1441 1

d1620 1
a1620 1

d1817 1
a1817 1

d1885 1
a1885 1

d2100 1
a2100 1

d2304 1
a2304 1

@


1.35
log
@	* generic/tkImgPhInstance.c:	fix numerous leaks discovered with the
	* generic/tkMenu.c:		Mac OS X Instruments.app Leaks tool.
	* generic/tkText.c:
	* generic/tkTextImage.c:
	* generic/tkTextIndex.c:
	* generic/tkUndo.c:
	* generic/tkUtil.c:
	* generic/ttk/ttkFrame.c:
	* macosx/tkMacOSXWm.c:
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.34 2008/12/05 15:51:31 nijtmans Exp $
d835 3
a837 1
		tagName = Tcl_GetHashKey(&sharedPtr->tagTable, hPtr);
@


1.34
log
@undid previous 'fix': 'const' is only useful for pointers, not for
opaque types. Therefore it should not have slipped in here.
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.33 2008/12/05 09:33:12 patthoyts Exp $
d116 4
a119 1

d122 1
@


1.33
log
@Make TkTextMakeByteIndex implementation agree with the prototype (const-ness)
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.32 2008/11/08 18:44:40 dkf Exp $
d82 1
a82 1

d152 1
a152 1

d202 1
a202 1

d249 1
a249 1

d294 1
a294 1

d362 1
a362 1

d385 1
a385 1
    const TkTextBTree tree,	/* Tree that lineIndex and byteIndex refer
d455 1
a455 1

d550 1
a550 1

d590 1
a590 1

d623 1
a623 1

d652 1
a652 1

d681 1
a681 1

d710 1
a710 1

d1003 1
a1003 1

d1067 1
a1067 1

d1120 1
a1120 1

d1364 1
a1364 1

d1434 1
a1434 1

d1613 1
a1613 1

d1878 1
a1878 1

d2093 1
a2093 1

d2297 1
a2297 1

@


1.32
log
@Lots of small changes to make code more pretty and C89-like.
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.31 2008/10/15 06:41:06 nijtmans Exp $
d385 1
a385 1
    TkTextBTree tree,		/* Tree that lineIndex and byteIndex refer
@


1.31
log
@Add "const" to many internal
const tables, so those will be
put by the C-compiler in the
TEXT segment in stead of the
DATA segment. This makes those
table sharable in shared libraries.
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.30 2008/10/05 18:22:22 dkf Exp $
d242 2
a243 2
	if ((objPtr->typePtr->freeIntRepProc) != NULL) {
	    (*objPtr->typePtr->freeIntRepProc)(objPtr);
@


1.30
log
@Greatly clean up Tk's handling of the writability of the Tcl result object.
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.29 2008/04/27 22:38:58 dkf Exp $
d75 1
a75 1
Tcl_ObjType tkTextIndexType = {
@


1.29
log
@Get rid of pre-C89-isms (esp. CONST vs const).
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.28 2007/12/13 15:24:17 dgp Exp $
d148 2
a149 3
    Tcl_AppendToObj(Tcl_GetObjResult(interp),
	    "can't convert value to textindex except via TkTextGetIndexFromObj API",
	    -1);
@


1.28
log
@merge stable branch onto HEAD
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.25.2.2 2007/10/15 18:38:33 dgp Exp $
d38 1
a38 1
static CONST char *	ForwBack(TkText *textPtr, CONST char *string,
d40 1
a40 1
static CONST char *	StartEnd(TkText *textPtr, CONST char *string,
d43 1
a43 1
			    TkText *textPtr, CONST char *string,
d66 1
a66 1
	((objPtr)->internalRep.twoPtrValue.ptr1 = (VOID *) (indexPtr))
d134 1
a134 1
    CONST TkTextIndex *indexPtr = GET_TEXTINDEX(objPtr);
d184 1
a184 1
    CONST TkTextIndex *origPtr)	/* Pointer to index. */
d204 1
a204 1
CONST TkTextIndex *
d272 1
a272 1
    CONST TkTextIndex *indexPtr)/* Pointer to index. */
d388 1
a388 1
    CONST TkText *textPtr,
d396 1
a396 1
    CONST char *p, *start;
d440 1
a440 1
		 * that byteIndex falls on a character boundary. If index
d574 1
a574 1
    CONST TkTextIndex *indexPtr,/* Text index. */
d612 2
a613 2
    CONST TkTextSegment *segPtr,/* Segment whose offset is desired. */
    CONST TkTextLine *linePtr)	/* Line containing segPtr. */
d615 1
a615 1
    CONST TkTextSegment *segPtr2;
d706 1
a706 1
    CONST char *string,		/* Textual description of position. */
d740 1
a740 1
    CONST char *string,		/* Textual description of position. */
d749 1
a749 1
    CONST char *cp;
d791 1
a791 1
	CONST char *tagName;
d816 1
a816 1
		tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);
d1025 2
a1026 2
    CONST TkText *textPtr,
    CONST TkTextIndex *indexPtr,/* Pointer to index. */
d1089 2
a1090 2
    CONST TkTextIndex*index1Ptr,/* First index. */
    CONST TkTextIndex*index2Ptr)/* Second index. */
d1142 1
a1142 1
static CONST char *
d1145 1
a1145 1
    CONST char *string,		/* String to parse for additional info about
d1150 1
a1150 1
    register CONST char *p, *units;
d1390 2
a1391 2
    CONST TkText *textPtr,
    CONST TkTextIndex *srcPtr,	/* Source index. */
d1462 2
a1463 2
    CONST TkText *textPtr,	/* Overall information about text widget. */
    CONST TkTextIndex *srcPtr,	/* Source index. */
d1483 1
a1483 2
	infoPtr = (TkTextElideInfo *)
		ckalloc((unsigned) sizeof(TkTextElideInfo));
d1641 2
a1642 2
    CONST TkText *textPtr,	/* Overall information about text widget. */
    CONST TkTextIndex *indexPtr1,
d1645 1
a1645 1
    CONST TkTextIndex *indexPtr2,
d1667 1
a1667 2
	infoPtr = (TkTextElideInfo *)
		ckalloc((unsigned) sizeof(TkTextElideInfo));
d1836 2
a1837 2
    CONST TkText *textPtr,
    CONST TkTextIndex *srcPtr,	/* Source index. */
d1906 2
a1907 2
    CONST TkText *textPtr,	/* Overall information about text widget. */
    CONST TkTextIndex *srcPtr,	/* Source index. */
d1916 1
a1916 1
    CONST char *p, *start, *end;
d2115 1
a2115 1
static CONST char *
d2118 1
a2118 1
    CONST char *string,		/* String to parse for additional info about
d2123 1
a2123 1
    CONST char *p;
@


1.28.2.1
log
@	* generic/tkImgPhoto.c:		fix numerous leaks discovered with the
	* generic/tkMenu.c:		Mac OS X Instruments.app Leaks tool.
	* generic/tkText.c:
	* generic/tkTextImage.c:
	* generic/tkTextIndex.c:
	* generic/tkUndo.c:
	* generic/ttk/ttkFrame.c:
	* macosx/tkMacOSXWm.c:
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.28 2007/12/13 15:24:17 dgp Exp $
d116 1
a116 4
    dupIndexPtr->textPtr = indexPtr->textPtr;
    if (dupIndexPtr->textPtr != NULL) {
	dupIndexPtr->textPtr->refCount++;
    }
a118 1
    copyPtr->typePtr = &tkTextIndexType;
@


1.27
log
@	* generic/tkArgv.c:		Fix gcc warnings about 'cast to/from
	* generic/tkCanvUtil.c:		pointer from/to integer of different
	* generic/tkCanvas.c:		size' on 64-bit platforms by casting
	* generic/tkCursor.c:		to intermediate types
	* generic/tkInt.h:		intptr_t/uintptr_t via new PTR2INT(),
	* generic/tkListbox.c:		INT2PTR(), PTR2UINT() and UINT2PTR()
	* generic/tkObj.c:		macros.
	* generic/tkStyle.c:
	* generic/tkTextIndex.c:
	* generic/tkUtil.c:
	* generic/ttk/ttkTheme.h:
	* generic/ttk/ttkTreeview.c:
	* unix/tkUnixMenu.c:
	* unix/configure.in:

	* unix/configure:		autoconf-2.59
	* unix/tkConfig.h.in:		autoheader-2.59
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.26 2007/09/07 00:34:54 dgp Exp $
@


1.26
log
@header cleanup
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.25 2007/02/22 13:56:33 dkf Exp $
d64 1
a64 1
	((int) (objPtr)->internalRep.twoPtrValue.ptr2)
d68 1
a68 1
	((objPtr)->internalRep.twoPtrValue.ptr2 = (VOID *) (epoch))
@


1.25
log
@Less of that K&R or wrongly indented function header style!
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.24 2005/11/17 16:21:56 dkf Exp $
a16 1
#include "tkPort.h"
@


1.25.2.1
log
@merge updates from HEAD
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.25 2007/02/22 13:56:33 dkf Exp $
d17 1
@


1.25.2.2
log
@merge updates from HEAD
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.25.2.1 2007/09/07 01:25:36 dgp Exp $
d64 1
a64 1
	(PTR2INT((objPtr)->internalRep.twoPtrValue.ptr2))
d68 1
a68 1
	((objPtr)->internalRep.twoPtrValue.ptr2 = INT2PTR(epoch))
@


1.24
log
@ANSIfy and reduce casting of NULL to promote readability
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.23 2005/10/10 10:36:35 vincentdarley Exp $
d85 2
a86 2
FreeTextIndexInternalRep(indexObjPtr)
    Tcl_Obj *indexObjPtr;	/* TextIndex object with internal rep to
d99 1
a99 1
    ckfree((char*)indexPtr);
d103 3
a105 3
DupTextIndexInternalRep(srcPtr, copyPtr)
    Tcl_Obj *srcPtr;		/* TextIndex obj with internal rep to copy. */
    Tcl_Obj *copyPtr;		/* TextIndex obj with internal rep to set. */
d109 2
a110 1
    dupIndexPtr = (TkTextIndex*) ckalloc(sizeof(TkTextIndex));
d129 2
a130 2
UpdateStringOfTextIndex(objPtr)
    Tcl_Obj *objPtr;
d145 3
a147 3
SetTextIndexFromAny(interp, objPtr)
    Tcl_Interp *interp;		/* Used for error reporting if not NULL. */
    Tcl_Obj *objPtr;		/* The object to convert. */
d168 1
a168 1
 *	insertions/deletions).
d181 3
a183 3
MakeObjIndex(textPtr, objPtr, origPtr)
    TkText *textPtr;		/* Information about text widget. */
    Tcl_Obj *objPtr;		/* Object containing description of
d185 1
a185 1
    CONST TkTextIndex *origPtr;	/* Pointer to index. */
d205 5
a209 5
CONST TkTextIndex*
TkTextGetIndexFromObj(interp, textPtr, objPtr)
    Tcl_Interp *interp;		/* Use this for error reporting. */
    TkText *textPtr;		/* Information about text widget. */
    Tcl_Obj *objPtr;		/* Object containing description of
d249 1
a249 5
    if (cache) {
	return MakeObjIndex(textPtr, objPtr, &index);
    } else {
	return MakeObjIndex(NULL, objPtr, &index);
    }
d270 4
a273 4
Tcl_Obj*
TkTextNewIndexObj(textPtr, indexPtr)
    TkText *textPtr;		/* Text widget for this index */
    CONST TkTextIndex *indexPtr;/* Pointer to index. */
d326 3
a328 3
TkTextMakePixelIndex(textPtr, pixelIndex, indexPtr)
    TkText* textPtr;		/* The Text Widget */
    int pixelIndex;		/* Pixel-index of desired line (0 means first
d330 1
a330 1
    TkTextIndex *indexPtr;	/* Structure to fill in. */
d345 1
a345 1
     * pixel in the window
d386 2
a387 2
TkTextMakeByteIndex(tree, textPtr, lineIndex, byteIndex, indexPtr)
    TkTextBTree tree;		/* Tree that lineIndex and byteIndex refer
d389 2
a390 2
    CONST TkText *textPtr;
    int lineIndex;		/* Index of desired line (0 means first line
d392 2
a393 2
    int byteIndex;		/* Byte index of desired character. */
    TkTextIndex *indexPtr;	/* Structure to fill in. */
d479 2
a480 2
TkTextMakeCharIndex(tree, textPtr, lineIndex, charIndex, indexPtr)
    TkTextBTree tree;		/* Tree that lineIndex and charIndex refer
d482 2
a483 2
    TkText *textPtr;
    int lineIndex;		/* Index of desired line (0 means first line
d485 2
a486 2
    int charIndex;		/* Index of desired character. */
    TkTextIndex *indexPtr;	/* Structure to fill in. */
d574 3
a576 3
TkTextIndexToSeg(indexPtr, offsetPtr)
    CONST TkTextIndex *indexPtr;/* Text index. */
    int *offsetPtr;		/* Where to store offset within segment, or
d612 3
a614 3
TkTextSegToOffset(segPtr, linePtr)
    CONST TkTextSegment *segPtr;/* Segment whose offset is desired. */
    CONST TkTextLine *linePtr;	/* Line containing segPtr. */
d617 1
a617 1
    int offset;
a618 1
    offset = 0;
d644 4
a647 4
TkTextGetObjIndex(interp, textPtr, idxObj, indexPtr)
    Tcl_Interp *interp;		/* Use this for error reporting. */
    TkText *textPtr;		/* Information about text widget. */
    Tcl_Obj *idxObj;		/* Object containing textual description of
d649 1
a649 1
    TkTextIndex *indexPtr;	/* Index structure to fill in. */
d673 4
a676 4
TkTextSharedGetObjIndex(interp, sharedTextPtr, idxObj, indexPtr)
    Tcl_Interp *interp;		/* Use this for error reporting. */
    TkSharedText *sharedTextPtr;/* Information about text widget. */
    Tcl_Obj *idxObj;		/* Object containing textual description of
d678 1
a678 1
    TkTextIndex *indexPtr;	/* Index structure to fill in. */
d704 5
a708 5
TkTextGetIndex(interp, textPtr, string, indexPtr)
    Tcl_Interp *interp;		/* Use this for error reporting. */
    TkText *textPtr;		/* Information about text widget. */
    CONST char *string;		/* Textual description of position. */
    TkTextIndex *indexPtr;	/* Index structure to fill in. */
d737 7
a743 7
GetIndex(interp, sharedPtr, textPtr, string, indexPtr, canCachePtr)
    Tcl_Interp *interp;		/* Use this for error reporting. */
    TkSharedText *sharedPtr;
    TkText *textPtr;		/* Information about text widget. */
    CONST char *string;		/* Textual description of position. */
    TkTextIndex *indexPtr;	/* Index structure to fill in. */
    int *canCachePtr;		/* Pointer to integer to store whether we can
d947 1
a947 1
	 * See if the base position is the name of an embedded image
d1025 4
a1028 4
TkTextPrintIndex(textPtr, indexPtr, string)
    CONST TkText *textPtr;
    CONST TkTextIndex *indexPtr;/* Pointer to index. */
    char *string;		/* Place to store the position. Must have at
d1089 3
a1091 3
TkTextIndexCmp(index1Ptr, index2Ptr)
    CONST TkTextIndex *index1Ptr;		/* First index. */
    CONST TkTextIndex *index2Ptr;		/* Second index. */
d1144 3
a1146 3
ForwBack(textPtr, string, indexPtr)
    TkText *textPtr;		/* Information about text widget. */
    CONST char *string;		/* String to parse for additional info about
d1149 1
a1149 1
    TkTextIndex *indexPtr;	/* Index to update as specified in string. */
d1187 2
a1188 2
    if ((*units == 'd') && (strncmp(units, "display",
				    (length > 7 ? 7 : length)) == 0)) {
d1312 1
a1312 1
		     * char/byte to get to the end of the previous line
d1390 4
a1393 4
TkTextIndexForwBytes(textPtr, srcPtr, byteCount, dstPtr)
    CONST TkText *textPtr;
    CONST TkTextIndex *srcPtr;	/* Source index. */
    int byteCount;		/* How many bytes forward to move. May be
d1395 1
a1395 1
    TkTextIndex *dstPtr;	/* Destination index: gets modified. */
d1462 4
a1465 4
TkTextIndexForwChars(textPtr, srcPtr, charCount, dstPtr, type)
    CONST TkText *textPtr;	/* Overall information about text widget. */
    CONST TkTextIndex *srcPtr;	/* Source index. */
    int charCount;		/* How many characters forward to move. May
d1467 2
a1468 2
    TkTextIndex *dstPtr;	/* Destination index: gets modified. */
    TkTextCountType type;	/* The type of item to count */
d1484 2
a1485 1
	infoPtr = (TkTextElideInfo*)ckalloc((unsigned)sizeof(TkTextElideInfo));
d1518 1
a1518 1
	     * isn't visible, then we simply continue the loop
d1613 1
a1613 1
	ckfree((char*) infoPtr);
d1642 3
a1644 3
TkTextIndexCount(textPtr, indexPtr1, indexPtr2, type)
    CONST TkText *textPtr;	/* Overall information about text widget. */
    CONST TkTextIndex *indexPtr1;
d1647 1
a1647 1
    CONST TkTextIndex *indexPtr2;
d1650 1
a1650 1
    TkTextCountType type;	/* The kind of indices to count. */
d1655 1
a1655 3
    int byteOffset, maxBytes;
    int count = 0;
    int elide = 0;
d1669 2
a1670 1
	infoPtr = (TkTextElideInfo*)ckalloc((unsigned)sizeof(TkTextElideInfo));
d1810 1
a1810 1
	ckfree((char*) infoPtr);
d1838 4
a1841 4
TkTextIndexBackBytes(textPtr, srcPtr, byteCount, dstPtr)
    CONST TkText *textPtr;
    CONST TkTextIndex *srcPtr;	/* Source index. */
    int byteCount;		/* How many bytes backward to move. May be
d1843 1
a1843 1
    TkTextIndex *dstPtr;	/* Destination index: gets modified. */
d1908 4
a1911 4
TkTextIndexBackChars(textPtr, srcPtr, charCount, dstPtr, type)
    CONST TkText *textPtr;	/* Overall information about text widget. */
    CONST TkTextIndex *srcPtr;	/* Source index. */
    int charCount;		/* How many characters backward to move. May
d1913 2
a1914 2
    TkTextIndex *dstPtr;	/* Destination index: gets modified. */
    TkTextCountType type;	/* The type of item to count */
d1966 1
a1966 1
     * Now segPtr points to the segment containing the starting index
d2094 1
a2094 1
	ckfree((char*) infoPtr);
d2119 3
a2121 3
StartEnd(textPtr, string, indexPtr)
    TkText *textPtr;		/* Information about text widget. */
    CONST char *string;		/* String to parse for additional info about
d2124 1
a2124 1
    TkTextIndex *indexPtr;	/* Index to modify based on string. */
a2272 1
	    } else {
d2301 8
@


1.23
log
@tip256 implementation
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.22 2005/08/10 22:02:22 dkf Exp $
d842 1
a842 1
		    tagName, "\"", (char *) NULL);
d1006 1
a1006 1
    Tcl_AppendResult(interp, "bad text index \"", string, "\"", (char *)NULL);
@


1.22
log
@Getting more systematic about style
Also start removing _ANSI_ARGS_; the core's required ANSI C for a while now
Also fix [Bug 1252702]; size_t doesn't mix with Tcl_GetStringFromObj
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.21 2005/02/14 23:00:46 vincentdarley Exp $
a2305 8

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */
@


1.21
log
@fix to newline eliding in text widget
@
text
@d1 1
a1 1
/* 
d4 2
a5 2
 *	This module provides procedures that manipulate indices for
 *	text widgets.
d10 2
a11 2
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.20 2004/10/05 01:26:10 hobbs Exp $
d30 16
a45 16
#define TKINDEX_NONE 0
#define TKINDEX_DISPLAY 1
#define TKINDEX_ANY 2

/*
 * Forward declarations for procedures defined later in this file:
 */

static CONST char *	ForwBack _ANSI_ARGS_((TkText *textPtr, 
			    CONST char *string, TkTextIndex *indexPtr));
static CONST char *	StartEnd _ANSI_ARGS_((TkText *textPtr, 
			    CONST char *string, TkTextIndex *indexPtr));
static int		GetIndex _ANSI_ARGS_((Tcl_Interp *interp,
			    TkSharedText *sharedPtr, TkText *textPtr, 
			    CONST char *string,
			    TkTextIndex *indexPtr, int *canCachePtr));
d51 10
a60 6
static void		DupTextIndexInternalRep _ANSI_ARGS_((Tcl_Obj *srcPtr,
			    Tcl_Obj *copyPtr));
static void		FreeTextIndexInternalRep _ANSI_ARGS_((Tcl_Obj *listPtr));
static int		SetTextIndexFromAny _ANSI_ARGS_((Tcl_Interp *interp,
			    Tcl_Obj *objPtr));
static void		UpdateStringOfTextIndex _ANSI_ARGS_((Tcl_Obj *objPtr));
d63 1
a63 1
		((TkTextIndex *) (objPtr)->internalRep.twoPtrValue.ptr1)
d65 1
a65 1
		((int) (objPtr)->internalRep.twoPtrValue.ptr2)
d67 1
a67 1
		(objPtr)->internalRep.twoPtrValue.ptr1 = (VOID*) (indexPtr)
d69 2
a70 1
		(objPtr)->internalRep.twoPtrValue.ptr2 = (VOID*) (epoch)
d72 2
a73 2
 * Define the 'textindex' object type, which Tk uses to represent
 * indices in text widgets internally.
d75 1
d77 5
a81 5
    "textindex",			/* name */
    FreeTextIndexInternalRep,		/* freeIntRepProc */
    DupTextIndexInternalRep,	        /* dupIntRepProc */
    NULL,                               /* updateStringProc */
    SetTextIndexFromAny		        /* setFromAnyProc */
d83 1
a83 1

d86 2
a87 1
    Tcl_Obj *indexObjPtr;   /* TextIndex object with internal rep to free. */
d91 5
a95 2
        if (--indexPtr->textPtr->refCount == 0) {
	    /* The text widget has been deleted and we need to free it now */
d112 1
a112 1
    
d116 1
a116 1
    
d120 5
a124 4
/* 
 * This will not be called except by TkTextNewIndexObj below.
 * This is because if a TkTextIndex is no longer valid, it is
 * not possible to regenerate the string representation.
d126 1
d142 1
d149 2
a150 2
	"can't convert value to textindex except via TkTextGetIndexFromObj API",
	-1);
d158 10
a167 10
 *	
 *	This procedure generates a Tcl_Obj description of an index,
 *	suitable for reading in again later.  If the 'textPtr' is NULL
 *	then we still generate an index object, but it's internal
 *	description is deemed non-cacheable, and therefore effectively
 *	useless (apart from as a temporary memory storage).  This is used
 *	for indices whose meaning is very temporary (like @@0,0 or the
 *	name of a mark or tag).  The mapping from such strings/objects to
 *	actual TkTextIndex pointers is not stable to minor text widget
 *	changes which we do not track (we track insertions/deletions).
d170 1
a170 1
 *	A pointer to an allocated TkTextIndex which will be freed 
d178 3
a180 2
static TkTextIndex*
MakeObjIndex(textPtr, objPtr, origPtr) 
d182 3
a184 2
    Tcl_Obj *objPtr;		/* Object containing description of position. */
    CONST TkTextIndex *origPtr; /* Pointer to index. */
d186 1
a186 1
    TkTextIndex *indexPtr = (TkTextIndex*) ckalloc(sizeof(TkTextIndex));
d208 2
a209 1
    Tcl_Obj *objPtr;		/* Object containing description of position. */
d214 1
a214 1
    
d217 1
a217 1
	
d220 1
a220 1
	
d228 8
a235 8
    /* 
     * The object is either not an index type or referred to a different
     * text widget, or referred to the correct widget, but it is out of
     * date (text has been added/deleted since).
     */
    
    if (GetIndex(interp, NULL, textPtr, Tcl_GetString(objPtr), 
		 &index, &cache) != TCL_OK) {
d238 1
a238 1
    
d247 1
a247 1
    
d259 4
a262 5
 *	
 *	This procedure generates a Tcl_Obj description of an index,
 *	suitable for reading in again later.  The index generated is
 *	effectively stable to all except insertion/deletion operations on
 *	the widget.
d275 2
a276 2
    TkText *textPtr;             /* text widget for this index */
    CONST TkTextIndex *indexPtr; /* Pointer to index. */
d283 2
a284 2
    /* 
     * Assumption that the above call returns an object with
d287 1
d289 5
a293 5
    
    /* 
     * Unfortunately, it isn't possible for us to regenerate the
     * string representation so we have to create it here, while we
     * can be sure the contents of the index are still valid.
d295 1
d305 6
a310 6
 *	Given a pixel index and a byte index, look things up in the B-tree
 *	and fill in a TkTextIndex structure.
 *	
 *	The valid input range for pixelIndex is from 0 to the number
 *	of pixels in the widget-1.  Anything outside that range will
 *	be rounded to the closest acceptable value. 
d313 8
a320 9
 * 
 *	The structure at *indexPtr is filled in with information about
 *	the character at pixelIndex (or the closest existing character,
 *	if the specified one doesn't exist), and the number of excess
 *	pixels is returned as a result.  This means if the given pixel
 *	index is exactly correct for the top-edge of the indexPtr, then
 *	zero will be returned, and otherwise we will return the
 *	calculation 'desired pixelIndex' - 'actual pixel index of
 *	indexPtr'.
d331 1
a331 1
    int pixelIndex;		/* pixel-index of desired line (0 means first
d339 1
a339 1
    
d343 6
a348 7
    indexPtr->linePtr = TkBTreeFindPixelLine(textPtr->sharedTextPtr->tree, 
					     textPtr,
					     pixelIndex, 
					     &pixelOffset);
    /* 
     * 'pixedlIndex' was too large, so we try again, just to find
     * the last pixel in the window
d350 1
d353 4
a356 4
					    textPtr)-1;
	indexPtr->linePtr = TkBTreeFindPixelLine(textPtr->sharedTextPtr->tree, 
						 textPtr, 
						 lastMinusOne, &pixelOffset);
d373 2
a374 2
 *	Given a line index and a byte index, look things up in the B-tree
 *	and fill in a TkTextIndex structure.
d393 2
a394 2
    int lineIndex;		/* Index of desired line (0 means first
				 * line of text). */
d413 2
a414 2
	indexPtr->linePtr = TkBTreeFindLine(tree, textPtr, 
					    TkBTreeNumLines(tree, textPtr));
d423 2
a424 2
     * Verify that the index is within the range of the line and points
     * to a valid character boundary.  
d431 3
a433 3
	     * Use the index of the last character in the line.  Since
	     * the last character on the line is guaranteed to be a '\n',
	     * we can back up a constant sizeof(char) bytes.
d435 1
a435 1
	     
d444 1
a444 1
		 * that byteIndex falls on a character boundary.  If index
d466 2
a467 2
 *	Given a line index and a character index, look things up in the
 *	B-tree and fill in a TkTextIndex structure.
d486 2
a487 2
    int lineIndex;		/* Index of desired line (0 means first
				 * line of text). */
d506 2
a507 2
	indexPtr->linePtr = TkBTreeFindLine(tree, textPtr, 
					    TkBTreeNumLines(tree, textPtr));
d512 2
a513 2
     * Verify that the index is within the range of the line.
     * If not, just use the index of the last character in the line.
d520 3
a522 3
	     * Use the index of the last character in the line.  Since
	     * the last character on the line is guaranteed to be a '\n',
	     * we can back up a constant sizeof(char) bytes.
d524 1
a524 1
	     
d561 2
a562 2
 *	Given an index, this procedure returns the segment and offset
 *	within segment for the index.
d565 4
a568 4
 *	The return value is a pointer to the segment referred to by
 *	indexPtr; this will always be a segment with non-zero size.  The
 *	variable at *offsetPtr is set to hold the integer offset within
 *	the segment of the character given by indexPtr.
d601 1
a601 1
 *	Given a segment pointer and the line containing it, this procedure
d634 3
a636 3
 * 
 *    Simpler wrapper around the string based function, but could be
 *    enhanced with a new object type in the future.
d639 1
a639 1
 *    see TkTextGetIndex
d642 2
a643 2
 *    None.
 *    
d651 2
a652 2
    Tcl_Obj *idxObj;	        /* Object containing textual description 
				 * of position. */
d655 2
a656 1
    return GetIndex(interp, NULL, textPtr, Tcl_GetString(idxObj), indexPtr, NULL);
d663 3
a665 3
 * 
 *    Simpler wrapper around the string based function, but could be
 *    enhanced with a new object type in the future.
d668 1
a668 1
 *    see TkTextGetIndex
d671 2
a672 2
 *    None.
 *    
d680 2
a681 2
    Tcl_Obj *idxObj;	        /* Object containing textual description 
				 * of position. */
d684 2
a685 2
    return GetIndex(interp, sharedTextPtr, NULL, 
		    Tcl_GetString(idxObj), indexPtr, NULL);
d696 1
a696 1
 *	The return value is a standard Tcl return result.  If TCL_OK is
d698 2
a699 2
 *	filled in; otherwise TCL_ERROR is returned and an error message
 *	is left in the interp's result.
d725 1
a725 1
 *	The return value is a standard Tcl return result.  If TCL_OK is
d727 6
a732 6
 *	filled in; otherwise TCL_ERROR is returned and an error message
 *	is left in the interp's result.
 *	
 *	If *canCachePtr is non-NULL, and everything went well, the
 *	integer it points to is set to 1 if the indexPtr is something
 *	which can be cached, and zero otherwise.
d747 2
a748 2
    int *canCachePtr;           /* Pointer to integer to store whether
                                 * we can cache the index (or NULL) */
d757 1
a757 1
    
d761 1
a761 1
    
d764 3
a766 4
     * Stage 1: check to see if the index consists of nothing but a mark
     * name.  We do this check now even though it's also done later, in
     * order to allow mark names that include funny characters such as
     * spaces or "+1c".
d783 5
a787 5
     * First look for the form "tag.first" or "tag.last" where "tag"
     * is the name of a valid tag.  Try to use up as much as possible
     * of the string in this check (strrchr instead of strchr below).
     * Doing the check now, and in this way, allows tag names to include
     * funny characters like "@@" or "+1c".
d797 1
a797 1
	
d807 1
d811 2
a812 3
	    /* 
	     * Special case for sel tag which is not stored in
	     * the hash table.
d814 1
d824 1
d828 1
d830 2
a831 3
	TkTextMakeByteIndex(sharedPtr->tree, textPtr, 
		    TkBTreeNumLines(sharedPtr->tree, textPtr), 
		    0, &last);
d835 1
a835 1
	        tagName = "sel";
d855 1
a855 1
    tryxy:
d875 1
a875 1
	goto gotBase; 
d900 2
a901 2
	TkTextMakeCharIndex(sharedPtr->tree, textPtr, lineIndex, 
			    charIndex, indexPtr);
d933 1
a933 2
		    TkBTreeNumLines(sharedPtr->tree, textPtr),
		    0, indexPtr);
d966 4
a969 4
     * Stage 3: process zero or more modifiers.  Each modifier is either
     * a keyword like "wordend" or "linestart", or it has the form
     * "op count units" where op is + or -, count is a number, and units
     * is "chars" or "lines".
d973 1
a973 1
    gotBase:
d982 1
a982 1
    
d993 2
a994 1
    done:
d1003 1
a1003 1
    error:
d1006 1
a1006 2
    Tcl_AppendResult(interp, "bad text index \"", string, "\"",
	    (char *) NULL);
d1014 3
a1016 3
 *	
 *	This procedure generates a string description of an index, suitable
 *	for reading in again later.
d1019 2
a1020 2
 *	The characters pointed to by string are modified.  Returns the
 *	number of characters in the string.
d1032 2
a1033 2
    char *string;		/* Place to store the position.  Must have
				 * at least TK_POS_CHARS characters. */
d1042 1
d1045 3
a1047 3
	    /* 
	     * Two logical lines merged into one display line 
	     * through eliding of a newline
d1049 1
d1063 1
d1069 3
a1071 3
    return sprintf(string, "%d.%d", 
		   TkBTreeLinesTo(textPtr, indexPtr->linePtr) + 1,
		   charIndex);
d1108 6
a1113 6
    /* 
     * Assumption here that it is ok for comparisons to reflect
     * the full B-tree and not just the portion that is available
     * to any client.  This should be true because the only
     * indexPtr's we should be given are ones which are valid
     * for the current client.
d1115 1
d1132 2
a1133 2
 *	This procedure handles +/- modifiers for indices to adjust the
 *	index forwards or backwards.
d1136 4
a1139 4
 *	If the modifier in string is successfully parsed then the return
 *	value is the address of the first character after the modifier,
 *	and *indexPtr is updated to reflect the modifier.  If there is a
 *	syntax error in the modifier then NULL is returned.
d1150 3
a1152 4
    CONST char *string;		/* String to parse for additional info
				 * about modifier (count and units). 
				 * Points to "+" or "-" that starts
				 * modifier. */
d1178 6
a1183 7
     * Find the end of this modifier (next space or + or - character),
     * then check if there is a textual 'display' or 'any' modifier.
     * These modifiers can be their own word (in which case they can 
     * be abbreviated) or they can follow on to the actual unit in
     * a single word (in which case no abbreviation is allowed).  So,
     * 'display lines', 'd lines', 'displaylin' are all ok, but 'dline'
     * is not.
d1186 1
a1186 1
    units = p; 
d1191 1
a1191 1
    if ((*units == 'd') && (strncmp(units, "display", 
d1197 2
a1198 2
    } else if ((*units == 'a') && (strncmp(units, "any", 
					   (length > 3 ? 3 : length)) == 0)) {
d1207 3
a1209 3
    /* 
     * If we had a modifier, which we interpreted ok, so now forward
     * to the actual units.
d1211 1
d1216 2
a1217 2
	units = p; 
	while ((*p != '\0') && !isspace(UCHAR(*p)) && (*p != '+') && (*p != '-')) {
d1226 1
d1229 1
d1237 1
d1245 1
d1251 1
d1259 1
a1259 1
	    /* 
d1261 7
a1267 7
	     * within its display line.  This also has the side-effect of
	     * moving indexPtr, but that doesn't matter since we will do
	     * it again below.
	     * 
	     * Then find the right display line, and finally calculated
	     * the index we want in that display line, based on the
	     * original pixel offset.
d1269 1
d1271 1
d1273 13
a1285 10
	        /* Go forward to the first non-elided index */
		TkTextIndexForwChars(textPtr, indexPtr, 0, indexPtr, 
				     COUNT_DISPLAY_INDICES);
	    }
	    /* 
	     * Unlike the Forw/BackChars code, the display line code
	     * is sensitive to whether we are genuinely going
	     * forwards or backwards.  So, we need to determine that.
	     * This is important in the case where we have "+ -3
	     * displaylines", for example.
d1287 1
d1293 1
d1298 1
d1302 3
a1304 4
		    /* 
		     * Go to the end of the line, then forward one
		     * char/byte to get to the beginning of the next
		     * line.
d1306 1
d1308 2
a1309 2
		    TkTextIndexForwChars(textPtr, indexPtr, 1, indexPtr, 
					 COUNT_DISPLAY_INDICES);
d1314 1
a1314 1
		    /* 
d1318 1
d1320 2
a1321 2
		    TkTextIndexBackChars(textPtr, indexPtr, 1, indexPtr, 
					 COUNT_DISPLAY_INDICES);
d1325 2
a1326 1
	    /* 
d1331 1
d1341 3
a1343 4
		 * The check below retains the character position, even
		 * if the line runs off the start of the file.  Without
		 * it, the character position will get reset to 0 by
		 * TkTextMakeIndex.
d1350 1
d1353 7
a1359 8
	     * UTF-8 characters with varying numbers of bytes, or if there
	     * are embedded windows, images, etc.  The cursor will bop
	     * around, keeping a constant number of bytes (not characters)
	     * from the left edge (but making sure not to split any UTF-8
	     * characters), regardless of the x-position the index
	     * corresponds to.  The proper way to do this is to get the
	     * x-position of the index and then pick the character at the
	     * same x-position in the new line.
d1362 2
a1363 3
	    TkTextMakeByteIndex(indexPtr->tree, textPtr, 
				lineIndex, indexPtr->byteIndex,
				indexPtr);
d1376 2
a1377 2
 *	Given an index for a text widget, this procedure creates a new
 *	index that points "count" bytes ahead of the source index.
d1381 5
a1385 5
 *	srcPtr, or to the last character in the TkText if there aren't
 *	"count" bytes left.
 *	
 *	In this latter case, the function returns '1' to indicate
 *	that not all of 'byteCount' could be used.
d1397 1
a1397 1
    int byteCount;		/* How many bytes forward to move.  May be
d1424 2
a1425 2
	 * If the new index is in the same line then we're done.
	 * Otherwise go on to the next line.
d1446 6
a1451 7
 *	Given an index for a text widget, this procedure creates a new
 *	index that points "count" items of type given by "type" ahead of
 *	the source index.  "count" can be zero, which is useful in
 *	the case where one wishes to move forward by display
 *	(non-elided) chars or indices or one wishes to move forward
 *	by chars, skipping any intervening indices.  In this case
 *	dstPtr will point to the first acceptable index which is
d1455 3
a1457 3
 *	*dstPtr is modified to refer to the character "count" items
 *	after srcPtr, or to the last character in the TkText if there
 *	aren't sufficient items left in the widget.
d1467 1
a1467 1
    CONST TkText *textPtr;      /* Overall information about text widget. */
d1469 2
a1470 2
    int charCount;		/* How many characters forward to move.
				 * May be negative. */
d1472 1
a1472 1
    TkTextCountType type;       /* The type of item to count */
d1482 1
a1482 1
    
d1495 2
a1496 2
     * Find seg that contains src byteIndex.
     * Move forward specified number of chars.
d1500 3
a1502 3
	/* 
	 * In this case we have already calculated the information
	 * we need, so no need to use TkTextIndexToSeg()
d1504 1
d1510 1
a1510 1
    
d1520 2
a1521 3
	     * characters/indices, check that first.  If the current
	     * segment isn't visible, then we simply continue the
	     * loop
d1523 37
a1559 34
	    if (checkElided) {
		if ((segPtr->typePtr == &tkTextToggleOffType)
		    || (segPtr->typePtr == &tkTextToggleOnType)) {		
		    TkTextTag *tagPtr = segPtr->body.toggle.tagPtr;
		    /*
		     * The elide state only changes if this tag is
		     * either the current highest priority tag
		     * (and is therefore being toggled off), or it's
		     * a new tag with higher priority.
		     */
		    if (tagPtr->elideString != NULL) {
			infoPtr->tagCnts[tagPtr->priority]++;
			if (infoPtr->tagCnts[tagPtr->priority] & 1) {
			    infoPtr->tagPtrs[tagPtr->priority] = tagPtr;
			}
			if (tagPtr->priority >= infoPtr->elidePriority) {
			    if (segPtr->typePtr == &tkTextToggleOffType) {
				/* 
				 * If it is being toggled off, and it has
				 * an elide string, it must actually be the
				 * current highest priority tag, so this
				 * check is redundant:
				 */
				if (tagPtr->priority != infoPtr->elidePriority) {
				    Tcl_Panic("Bad tag priority being toggled off");
				}
			    
				/* 
				 * Find previous elide tag, if any (if not
				 * then elide will be zero, of course).
				 */
				elide = 0;
				while (--infoPtr->elidePriority > 0) {
				    if (infoPtr->tagCnts[infoPtr->elidePriority] 
d1561 1
a1561 1
					elide = infoPtr->tagPtrs
d1563 1
a1563 2
					break;
				    }
a1564 3
			    } else {
				elide = tagPtr->elide;
				infoPtr->elidePriority = tagPtr->priority;
d1566 3
d1575 1
a1575 1
	        if (segPtr->typePtr == &tkTextCharType) {
d1585 4
a1588 7
		} else {
		    if (type & COUNT_INDICES) {
			if (charCount < segPtr->size - byteOffset) {
			    dstPtr->byteIndex += charCount;
			    goto forwardCharDone;
			}
			charCount -= segPtr->size - byteOffset;
d1590 1
d1593 1
a1593 1
	    
d1599 2
a1600 3
	 * Go to the next line.  If we are at the end of the text item,
	 * back up one byte (for the terminal '\n' character) and return
	 * that index.
d1602 1
a1602 1
	 
d1612 1
d1625 7
a1631 8
 *	Given an ordered pair of indices in a text widget, this
 *	procedure counts how many characters (not bytes) are between 
 *	the two indices.
 *	
 *	It is illegal to call this procedure with unordered indices.
 *	
 *	Note that 'textPtr' is only used if we need to check for
 *	elided attributes, i.e. if type is COUNT_DISPLAY_INDICES or
d1635 2
a1636 2
 *	The number of characters in the given range, which meet
 *	the appropriate 'type' attributes.
d1646 8
a1653 7
    CONST TkText *textPtr;	 /* Overall information about text widget. */
    CONST TkTextIndex *indexPtr1;/* Index describing location of
                                  * character from which to count. */
    CONST TkTextIndex *indexPtr2;/* Index describing location of last
				  * character at which to stop the
				  * count. */
    TkTextCountType type;        /* The kind of indices to count */
d1664 2
a1665 2
     * Find seg that contains src index, and remember
     * how many bytes not to count in the given segment.
d1670 1
a1670 1
    
d1677 1
a1677 1
	
d1680 1
a1680 2
	 * Go through each segment in line adding up the number
	 * of characters.
d1682 1
a1682 1
	
d1686 2
a1687 3
	     * characters/indices, check that first.  If the current
	     * segment isn't visible, then we simply continue the
	     * loop
d1689 1
d1692 1
a1692 1
		  || (segPtr->typePtr == &tkTextToggleOnType)) {
d1694 1
d1696 3
a1698 4
		     * The elide state only changes if this tag is
		     * either the current highest priority tag
		     * (and is therefore being toggled off), or it's
		     * a new tag with higher priority.
d1700 1
d1708 5
a1712 5
				/* 
				 * If it is being toggled off, and it has
				 * an elide string, it must actually be the
				 * current highest priority tag, so this
				 * check is redundant:
d1714 2
a1715 1
				if (tagPtr->priority != infoPtr->elidePriority) {
d1718 2
a1719 2
				
				/* 
d1723 1
d1726 4
a1729 4
				    if (infoPtr->tagCnts[infoPtr->elidePriority] 
					& 1) {
					elide = infoPtr->tagPtrs
					  [infoPtr->elidePriority]->elide;
d1748 1
a1748 1
	    
d1751 2
a1752 2
		register unsigned char *str = 
                  (unsigned char *) segPtr->body.chars + byteOffset;
d1757 1
a1757 1
		        byteLen = maxBytes - byteOffset;
d1761 1
a1761 1
		
d1763 7
a1769 5
		 * This is a speed sensitive function, so run specially over the
		 * string to count continuous ascii characters before resorting
		 * to the Tcl_NumUtfChars call.  This is a long form of:
		 * stringPtr->numChars = 
		 *   Tcl_NumUtfChars(objPtr->bytes, objPtr->length);
d1772 4
a1775 1
		while (i && (*str < 0xC0)) { i--; str++; }
d1784 1
d1800 2
a1801 3
	 * Go to the next line.  If we are at the end of the text item,
	 * back up one byte (for the terminal '\n' character) and return
	 * that index.
d1803 1
a1803 1
	 
d1810 1
d1818 1
a1818 1
	
d1824 2
a1825 2
 *	Given an index for a text widget, this procedure creates a new
 *	index that points "count" bytes earlier than the source index.
d1831 3
a1833 3
 *	
 *	Returns 1 if we couldn't use all of 'byteCount' because we 
 *	have run into the beginning or end of the text, and zero otherwise.
d1845 1
a1845 1
    int byteCount;		/* How many bytes backward to move.  May be
d1861 2
a1862 2
	 * Move back one line in the text.  If we run off the beginning
	 * of the file then just return the first character in the text.
d1892 7
a1898 8
 *	Given an index for a text widget, this procedure creates a new
 *	index that points "count" items of type given by "type" earlier
 *	than the source index.  "count" can be zero, which is useful in
 *	the case where one wishes to move backward by display
 *	(non-elided) chars or indices or one wishes to move backward by
 *	chars, skipping any intervening indices.  In this case the
 *	returned index *dstPtr will point just _after_ the first
 *	acceptable index which is encountered.
d1901 3
a1903 3
 *	*dstPtr is modified to refer to the character "count" items
 *	before srcPtr, or to the first index in the window if there
 *	aren't sufficient items earlier than srcPtr.
d1913 1
a1913 1
    CONST TkText *textPtr;      /* Overall information about text widget. */
d1915 2
a1916 2
    int charCount;		/* How many characters backward to move.
				 * May be negative. */
d1918 1
a1918 1
    TkTextCountType type;       /* The type of item to count */
d1932 1
a1932 1
	infoPtr = (TkTextElideInfo*)ckalloc((unsigned)sizeof(TkTextElideInfo));
d1935 1
a1935 1
    
d1939 2
a1940 2
     * Find offset within seg that contains byteIndex.
     * Move backward specified number of chars.
d1944 1
a1944 1
    
d1946 1
a1946 1
    
d1954 3
a1956 3
		/* 
		 * Two logical lines merged into one display line 
		 * through eliding of a newline
d1958 1
d1968 2
a1969 1
    /* 
d1972 1
d1976 42
a2017 29
	 * characters/indices, check that first.  If the current
	 * segment isn't visible, then we simply continue the
	 * loop
	 */
	if (checkElided) {
	    if ((segPtr->typePtr == &tkTextToggleOffType)
	      || (segPtr->typePtr == &tkTextToggleOnType)) {		
		TkTextTag *tagPtr = segPtr->body.toggle.tagPtr;
		/*
		 * The elide state only changes if this tag is
		 * either the current highest priority tag
		 * (and is therefore being toggled off), or it's
		 * a new tag with higher priority.
		 */
		if (tagPtr->elideString != NULL) {
		    infoPtr->tagCnts[tagPtr->priority]++;
		    if (infoPtr->tagCnts[tagPtr->priority] & 1) {
			infoPtr->tagPtrs[tagPtr->priority] = tagPtr;
		    }
		    if (tagPtr->priority >= infoPtr->elidePriority) {
			if (segPtr->typePtr == &tkTextToggleOnType) {
			    /* 
			     * If it is being toggled on, and it has
			     * an elide string, it must actually be the
			     * current highest priority tag, so this
			     * check is redundant:
			     */
			    if (tagPtr->priority != infoPtr->elidePriority) {
				Tcl_Panic("Bad tag priority being toggled on");
a2018 16
						    
			    /* 
			     * Find previous elide tag, if any (if not
			     * then elide will be zero, of course).
			     */
			    elide = 0;
			    while (--infoPtr->elidePriority > 0) {
				if (infoPtr->tagCnts[infoPtr->elidePriority] & 1) {
				    elide = infoPtr->tagPtrs
				              [infoPtr->elidePriority]->elide;
				    break;
				}
			    }
			} else {
			    elide = tagPtr->elide;
			    infoPtr->elidePriority = tagPtr->priority;
d2020 3
d2094 1
d2107 2
a2108 2
 *	This procedure handles modifiers like "wordstart" and "lineend"
 *	to adjust indices forwards or backwards.
d2111 4
a2114 4
 *	If the modifier is successfully parsed then the return value
 *	is the address of the first character after the modifier, and
 *	*indexPtr is updated to reflect the modifier. If there is a
 *	syntax error in the modifier then NULL is returned.
d2125 3
a2127 4
    CONST char *string;		/* String to parse for additional info
				 * about modifier (count and units). 
				 * Points to first character of modifer
				 * word. */
d2134 1
a2134 1
    
d2142 1
d2144 2
a2145 2
    if ((*string == 'd') && (strncmp(string, "display", 
				     (length > 7 ? 7 : length)) == 0)) {
d2150 2
a2151 2
    } else if ((*string == 'a') && (strncmp(string, "any", 
					    (length > 3 ? 3 : length)) == 0)) {
d2159 4
a2162 4
    
    /* 
     * If we had a modifier, which we interpreted ok, so now forward
     * to the actual units.
d2164 1
d2169 2
a2170 2
	string = p; 
	while ((*p != '\0') && !isspace(UCHAR(*p)) && (*p != '+') && (*p != '-')) {
d2186 5
a2190 1
	    /* We know '\n' is encoded with a single byte index */
d2207 2
a2208 2
	 * forward one character.  Otherwise move forward until finding
	 * a character that isn't part of a word and stop there.
d2212 2
a2213 2
	    TkTextIndexForwChars(textPtr, indexPtr, 0, indexPtr, 
				 COUNT_DISPLAY_INDICES);
d2218 1
d2221 1
d2236 2
a2237 2
		TkTextIndexForwChars(textPtr, indexPtr, 1, indexPtr, 
				     COUNT_DISPLAY_INDICES);
d2239 2
a2240 2
		TkTextIndexForwChars(NULL, indexPtr, 1, indexPtr, 
				     COUNT_INDICES);
d2249 2
a2250 2
	    TkTextIndexForwChars(NULL, indexPtr, 0, indexPtr, 
				 COUNT_DISPLAY_INDICES);
d2252 1
d2254 4
a2257 4
	 * Starting with the current character, look for one that's not
	 * part of a word and keep moving backward until you find one.
	 * Then if the character found wasn't the first one, move forward
	 * again one position.
d2263 1
d2266 1
d2272 3
a2274 3
		    chSize = (segPtr->body.chars + offset 
			      - Tcl_UtfPrev(segPtr->body.chars + offset, 
					    segPtr->body.chars));
d2289 1
d2292 2
a2293 2
		TkTextIndexForwChars(textPtr, indexPtr, 1, indexPtr, 
				     COUNT_DISPLAY_INDICES);
d2295 2
a2296 2
		TkTextIndexForwChars(NULL, indexPtr, 1, indexPtr, 
				     COUNT_INDICES);
d2302 2
a2303 1
    done:
d2306 8
@


1.20
log
@fix init warnings
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.19 2004/09/10 12:13:42 vincentdarley Exp $
d1019 1
d1024 10
a1033 1
    for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
d1790 3
d1800 1
a1800 1
void
d1812 1
a1812 2
	TkTextIndexForwBytes(textPtr, srcPtr, -byteCount, dstPtr);
	return;
d1829 1
a1829 1
	    return;
d1843 1
d1911 10
a1920 1
	for (segPtr = dstPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
d1992 1
a1992 1
			goto backwadCharDone;
d2003 1
a2003 1
			goto backwadCharDone;
d2034 1
a2034 1
	    goto backwadCharDone;
d2051 1
a2051 1
  backwadCharDone:
@


1.19
log
@text widget 'peer' subcommand -- TIP#169 implementation
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.18 2004/06/04 10:51:18 vincentdarley Exp $
a733 1
    Tcl_HashEntry *hPtr;
d779 1
@


1.18
log
@text widget fixes
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.17 2004/02/28 16:04:43 vincentdarley Exp $
d43 2
a44 1
			    TkText *textPtr, CONST char *string,
d124 1
a124 1
    len = TkTextPrintIndex(indexPtr, buffer);
d182 1
a182 1
	SET_INDEXEPOCH(objPtr, textPtr->stateEpoch);
d205 1
a205 1
	if (epoch == textPtr->stateEpoch) {
d218 1
a218 1
    if (GetIndex(interp, textPtr, Tcl_GetString(objPtr), 
d321 1
a321 1
    indexPtr->tree = textPtr->tree;
d327 3
a329 1
    indexPtr->linePtr = TkBTreeFindPixelLine(textPtr->tree, pixelIndex, 
d336 5
a340 2
	indexPtr->linePtr = TkBTreeFindPixelLine(textPtr->tree, 
			      TkBTreeNumPixels(textPtr->tree)-1, &pixelOffset);
d373 1
a373 1
TkTextMakeByteIndex(tree, lineIndex, byteIndex, indexPtr)
d376 1
d395 1
a395 1
    indexPtr->linePtr = TkBTreeFindLine(tree, lineIndex);
d397 2
a398 1
	indexPtr->linePtr = TkBTreeFindLine(tree, TkBTreeNumLines(tree));
d466 1
a466 1
TkTextMakeCharIndex(tree, lineIndex, charIndex, indexPtr)
d469 1
d488 1
a488 1
    indexPtr->linePtr = TkBTreeFindLine(tree, lineIndex);
d490 2
a491 1
	indexPtr->linePtr = TkBTreeFindLine(tree, TkBTreeNumLines(tree));
d639 30
a668 1
    return GetIndex(interp, textPtr, Tcl_GetString(idxObj), indexPtr, NULL);
d697 1
a697 1
    return GetIndex(interp, textPtr, string, indexPtr, NULL);
d724 1
a724 1
GetIndex(interp, textPtr, string, indexPtr, canCachePtr)
d726 1
a734 2
    TkTextTag *tagPtr;
    TkTextSearch search;
d742 4
d765 1
a765 1
    indexPtr->tree = textPtr->tree;
d778 4
d791 17
a807 4
	*p = 0;
	hPtr = Tcl_FindHashEntry(&textPtr->tagTable, Tcl_DStringValue(&copy));
	*p = '.';
	if (hPtr == NULL) {
d810 4
a813 4
	tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);
	TkTextMakeByteIndex(textPtr->tree, 0, 0, &first);
	TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree), 0,
		&last);
d816 5
d824 1
a824 2
		    Tcl_GetHashKey(&textPtr->tagTable, hPtr), "\"",
			    (char *) NULL);
d882 2
a883 1
	TkTextMakeCharIndex(textPtr->tree, lineIndex, charIndex, indexPtr);
d914 3
a916 2
	TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree),
		0, indexPtr);
d980 3
d1012 2
a1013 1
TkTextPrintIndex(indexPtr, string)
d1039 3
a1041 2
    return sprintf(string, "%d.%d", TkBTreeLineIndex(indexPtr->linePtr) + 1,
	    charIndex);
d1078 9
a1086 2
    line1 = TkBTreeLineIndex(index1Ptr->linePtr);
    line2 = TkBTreeLineIndex(index2Ptr->linePtr);
d1288 1
a1288 1
	    lineIndex = TkBTreeLineIndex(indexPtr->linePtr);
d1317 3
a1319 2
	    TkTextMakeByteIndex(indexPtr->tree, lineIndex, indexPtr->byteIndex,
		    indexPtr);
d1350 2
a1351 1
TkTextIndexForwBytes(srcPtr, byteCount, dstPtr)
d1362 1
a1362 1
	TkTextIndexBackBytes(srcPtr, -byteCount, dstPtr);
d1388 1
a1388 1
	linePtr = TkBTreeNextLine(dstPtr->linePtr);
d1456 10
a1465 1
    segPtr = TkTextIndexToSeg(dstPtr, &byteOffset);
d1561 1
a1561 1
	linePtr = TkBTreeNextLine(dstPtr->linePtr);
d1754 1
a1754 1
	linePtr1 = TkBTreeNextLine(linePtr1);
d1788 2
a1789 1
TkTextIndexBackBytes(srcPtr, byteCount, dstPtr)
d1799 1
a1799 1
	TkTextIndexForwBytes(srcPtr, -byteCount, dstPtr);
d1813 1
a1813 1
	    lineIndex = TkBTreeLineIndex(dstPtr->linePtr);
d1820 1
a1820 1
	dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, lineIndex);
d1893 10
a1902 3
    for (segPtr = dstPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
	if (segSize <= segPtr->size) {
	    break;
a1903 1
	segSize -= segPtr->size;
d1905 3
d2008 1
a2008 1
	    lineIndex = TkBTreeLineIndex(dstPtr->linePtr);
d2015 1
a2015 1
	dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, lineIndex);
@


1.17
log
@remove access to tclInt.h
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.16 2004/01/27 10:09:35 das Exp $
d289 4
d328 4
@


1.16
log
@        * generic/tkTextIndex.c: added '#include <tclInt.h>' since the
        code uses the TclUtfToUniChar macro from that file. [Bug 874745]

        * macosx/Wish.pbproj/project.pbxproj: removed erroneous reference
        to mkpsenc.tcl in bundle resources phase (mkpsenc.tcl is already
        part of the copy files phase to Resources/Scripts).

        * macosx/Makefile: added support for 'xcodebuild' on Mac OS X 10.3.
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.15 2004/01/13 02:06:01 davygrvy Exp $
a19 1
#include <tclInt.h>
d2043 1
a2043 1
		chSize = TclUtfToUniChar(segPtr->body.chars + offset, &ch);
d2085 1
a2085 1
		TclUtfToUniChar(segPtr->body.chars + offset, &ch);
@


1.15
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
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.14 2004/01/07 15:21:02 vincentdarley Exp $
d20 1
@


1.14
log
@utf-8 aware text wordstart and wordend
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.13 2003/12/15 11:51:06 vincentdarley Exp $
d1408 1
a1408 1
				    panic("Bad tag priority being toggled off");
d1576 1
a1576 1
				    panic("Bad tag priority being toggled off");
d1660 1
a1660 1
	    panic("Reached end of text widget when counting characters");
d1833 1
a1833 1
				panic("Bad tag priority being toggled on");
@


1.13
log
@text widget more extensive documentation, and two small code improvements
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.12 2003/11/12 17:19:18 vincentdarley Exp $
a1959 1
    int c, offset;
d2026 1
d2040 1
d2042 3
a2044 2
		c = segPtr->body.chars[offset];
		if (!isalnum(UCHAR(c)) && (c != '_')) {
d2049 2
a2050 2
	    offset += 1;
	    indexPtr->byteIndex += sizeof(char);
d2067 1
d2082 1
d2084 3
a2086 2
		c = segPtr->body.chars[offset];
		if (!isalnum(UCHAR(c)) && (c != '_')) {
d2089 5
d2095 1
d2097 2
a2098 2
	    offset -= 1;
	    indexPtr->byteIndex -= sizeof(char);
@


1.12
log
@old tk text widget bugs fixed
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.11 2003/11/08 17:22:46 vincentdarley Exp $
d1476 1
d1666 1
d1925 1
@


1.11
log
@elide tag handling, once more
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.10 2003/11/07 15:36:26 vincentdarley Exp $
d784 1
a784 1
	TkTextPixelIndex(textPtr, x, y, indexPtr);
@


1.10
log
@better elide tag handling
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.9 2003/10/31 09:02:11 vincentdarley Exp $
d1388 10
a1397 8
		    if (tagPtr->elideString != NULL 
		      && (tagPtr->priority >= infoPtr->elidePriority)) {
			if (elide) {
			    elide = ((segPtr->typePtr == &tkTextToggleOffType)
				     & !tagPtr->elide);
			} else {
			    elide = ((segPtr->typePtr == &tkTextToggleOnType)
				     & tagPtr->elide);
d1399 24
a1422 6
			if (!elide && tagPtr->priority 
			    == infoPtr->elidePriority) {
			    /* Find previous elide tag, if any */
			    while (--infoPtr->elidePriority > 0) {
				if (infoPtr->tagCnts[infoPtr->elidePriority] & 1) {
				    break;
d1424 3
a1427 2
			} else {
			    infoPtr->elidePriority = tagPtr->priority;
d1553 1
a1553 1
		  || (segPtr->typePtr == &tkTextToggleOnType)) {		
d1555 10
a1564 8
		    if (tagPtr->elideString != NULL 
		      && (tagPtr->priority >= infoPtr->elidePriority)) {
			if (elide) {
			    elide = ((segPtr->typePtr == &tkTextToggleOffType)
				     & !tagPtr->elide);
			} else {
			    elide = ((segPtr->typePtr == &tkTextToggleOnType)
				     & tagPtr->elide);
d1566 24
a1589 6
			if (!elide && tagPtr->priority 
			    == infoPtr->elidePriority) {
			    /* Find previous elide tag, if any */
			    while (--infoPtr->elidePriority > 0) {
				if (infoPtr->tagCnts[infoPtr->elidePriority] & 1) {
				    break;
d1591 3
a1594 2
			} else {
			    infoPtr->elidePriority = tagPtr->priority;
d1600 1
a1600 1
			return count;
d1646 1
a1646 1
		return count;
d1663 5
d1811 10
a1820 8
		if (tagPtr->elideString != NULL 
		    && (tagPtr->priority >= infoPtr->elidePriority)) {
		    if (elide) {
			elide = ((segPtr->typePtr == &tkTextToggleOnType)
				 & !tagPtr->elide);
		    } else {
			elide = ((segPtr->typePtr == &tkTextToggleOffType)
				 & tagPtr->elide);
d1822 10
a1831 6
		    if (!elide && tagPtr->priority 
			== infoPtr->elidePriority) {
			/* Find previous elide tag, if any */
			while (--infoPtr->elidePriority > 0) {
			    if (infoPtr->tagCnts[infoPtr->elidePriority] & 1) {
				break;
d1833 16
a1849 2
		    } else {
			infoPtr->elidePriority = tagPtr->priority;
@


1.9
log
@TIP 155 implementation
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.8 2003/10/10 15:56:22 dkf Exp $
d1121 1
a1121 1
	    TkTextIndexForwChars(indexPtr, count, indexPtr, type);
d1123 1
a1123 1
	    TkTextIndexBackChars(indexPtr, count, indexPtr, type);
d1133 1
a1133 1
	    TkTextIndexForwChars(indexPtr, count, indexPtr, type);
d1135 1
a1135 1
	    TkTextIndexBackChars(indexPtr, count, indexPtr, type);
d1150 1
a1150 1
	    if (TkTextIsElided(textPtr, indexPtr)) {
d1152 2
a1153 2
		TkTextIndexForwChars(indexPtr, 0, indexPtr, 
				     COUNT_DISPLAY_INDICES | COUNT_IS_ELIDED);
d1180 2
a1181 3
		    TkTextIndexForwChars(indexPtr, 1, indexPtr, 
					 COUNT_DISPLAY_INDICES
			| (COUNT_IS_ELIDED * TkTextIsElided(textPtr, indexPtr)));
d1191 2
a1192 3
		    TkTextIndexBackChars(indexPtr, 1, indexPtr, 
					 COUNT_DISPLAY_INDICES 
			| (COUNT_IS_ELIDED * TkTextIsElided(textPtr, indexPtr)));
d1336 2
a1337 1
TkTextIndexForwChars(srcPtr, charCount, dstPtr, type)
d1346 1
d1352 1
a1352 1

d1354 1
a1354 1
	TkTextIndexBackChars(srcPtr, -charCount, dstPtr, type);
d1358 2
a1359 1
	elide = ((type & COUNT_IS_ELIDED) ? 1 : 0);
d1370 1
d1386 4
a1389 2
		  || (segPtr->typePtr == &tkTextToggleOnType)) {		
		    if (segPtr->body.toggle.tagPtr->elideString != NULL) {
d1391 2
a1392 2
			    elide = (segPtr->typePtr == &tkTextToggleOffType)
			      & !segPtr->body.toggle.tagPtr->elide;
d1394 13
a1406 2
			    elide = (segPtr->typePtr == &tkTextToggleOnType)
			      & segPtr->body.toggle.tagPtr->elide;
d1419 1
a1419 1
			    return;
d1427 1
a1427 1
			    return;
d1447 1
a1447 1
	    return;
d1453 4
d1496 1
d1513 2
a1514 1
	elide = TkTextIsElided(textPtr, indexPtr1);
d1533 3
a1535 1
		    if (segPtr->body.toggle.tagPtr->elideString != NULL) {
d1537 2
a1538 2
			    elide = (segPtr->typePtr == &tkTextToggleOffType)
			      & !segPtr->body.toggle.tagPtr->elide;
d1540 13
a1552 2
			    elide = (segPtr->typePtr == &tkTextToggleOnType)
			      & segPtr->body.toggle.tagPtr->elide;
d1713 2
a1714 1
TkTextIndexBackChars(srcPtr, charCount, dstPtr, type)
d1722 1
d1729 1
a1729 1
	TkTextIndexForwChars(srcPtr, -charCount, dstPtr, type);
d1733 2
a1734 1
        elide = ((type & COUNT_IS_ELIDED) ? 1 : 0);
d1763 3
a1765 1
		if (segPtr->body.toggle.tagPtr->elideString != NULL) {
d1767 2
a1768 2
			elide = (segPtr->typePtr == &tkTextToggleOnType)
			  & !segPtr->body.toggle.tagPtr->elide;
d1770 13
a1782 2
			elide = (segPtr->typePtr == &tkTextToggleOffType)
			  & segPtr->body.toggle.tagPtr->elide;
d1795 1
a1795 1
			return;
d1806 1
a1806 1
			return;
d1837 1
a1837 1
	    return;
d1854 4
d1965 2
a1966 2
	    TkTextIndexForwChars(indexPtr, 0, indexPtr, COUNT_DISPLAY_INDICES
		| (COUNT_IS_ELIDED * TkTextIsElided(textPtr, indexPtr)));
d1985 2
a1986 2
		TkTextIndexForwChars(indexPtr, 1, indexPtr, COUNT_DISPLAY_INDICES
		    | (COUNT_IS_ELIDED * TkTextIsElided(textPtr, indexPtr)));
d1988 2
a1989 1
		TkTextIndexForwChars(indexPtr, 1, indexPtr, COUNT_INDICES);
d1997 2
a1998 2
	    TkTextIndexForwChars(indexPtr, 0, indexPtr, COUNT_DISPLAY_INDICES
		| (COUNT_IS_ELIDED * TkTextIsElided(textPtr, indexPtr)));
d2028 2
a2029 2
		TkTextIndexForwChars(indexPtr, 1, indexPtr, COUNT_DISPLAY_INDICES
		    | (COUNT_IS_ELIDED * TkTextIsElided(textPtr, indexPtr)));
d2031 2
a2032 1
		TkTextIndexForwChars(indexPtr, 1, indexPtr, COUNT_INDICES);
@


1.8
log
@Register the type of text indices.  Also some exported symbol name policing...
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.7 2003/05/19 13:04:23 vincentdarley Exp $
d28 7
d38 4
a41 4
static CONST char *	ForwBack _ANSI_ARGS_((CONST char *string,
			    TkTextIndex *indexPtr));
static CONST char *	StartEnd _ANSI_ARGS_((CONST char *string,
			    TkTextIndex *indexPtr));
d46 4
a49 1

a108 1

a128 1
    
d139 1
a139 1

d187 1
a187 1

d285 56
d360 1
a360 1
    TkTextBTree tree;		/* Tree that lineIndex and charIndex refer
d894 1
a894 1
	    cp = ForwBack(cp, indexPtr);
d896 1
a896 1
	    cp = StartEnd(cp, indexPtr);
d1032 2
a1033 1
ForwBack(string, indexPtr)
d1042 1
a1042 1
    int count, lineIndex;
d1064 6
a1069 2
     * then parse the unit specifier and update the position
     * accordingly.
d1077 34
d1112 20
d1133 1
a1133 1
	    TkTextIndexForwChars(indexPtr, count, indexPtr);
d1135 1
a1135 1
	    TkTextIndexBackChars(indexPtr, count, indexPtr);
d1138 66
a1203 3
	lineIndex = TkBTreeLineIndex(indexPtr->linePtr);
	if (*string == '+') {
	    lineIndex += count;
d1205 5
a1209 1
	    lineIndex -= count;
d1211 11
d1223 9
a1231 4
	     * The check below retains the character position, even
	     * if the line runs off the start of the file.  Without
	     * it, the character position will get reset to 0 by
	     * TkTextMakeIndex.
d1234 2
a1235 3
	    if (lineIndex < 0) {
		lineIndex = 0;
	    }
a1236 13
	/*
	 * This doesn't work quite right if using a proportional font or
	 * UTF-8 characters with varying numbers of bytes.  The cursor will
	 * bop around, keeping a constant number of bytes (not characters)
	 * from the left edge (but making sure not to split any UTF-8
	 * characters), regardless of the x-position the index corresponds
	 * to.  The proper way to do this is to get the x-position of the
	 * index and then pick the character at the same x-position in the
	 * new line.
	 */

	TkTextMakeByteIndex(indexPtr->tree, lineIndex, indexPtr->byteIndex,
		indexPtr);
d1255 3
d1265 1
a1265 1
void
d1278 1
a1278 1
	return;
d1300 1
a1300 1
	    return;
d1306 1
a1306 1
	    return;
d1318 7
a1324 1
 *	index that points "count" characters ahead of the source index.
d1327 1
a1327 1
 *	*dstPtr is modified to refer to the character "count" characters
d1329 1
a1329 1
 *	aren't "count" characters left in the file.
d1338 1
a1338 1
TkTextIndexForwChars(srcPtr, charCount, dstPtr)
d1343 1
d1350 2
d1354 1
a1354 1
	TkTextIndexBackChars(srcPtr, -charCount, dstPtr);
d1357 3
d1376 17
a1392 7
	    if (segPtr->typePtr == &tkTextCharType) {
		start = segPtr->body.chars + byteOffset;
		end = segPtr->body.chars + segPtr->size;
		for (p = start; p < end; p += Tcl_UtfToUniChar(p, &ch)) {
		    if (charCount == 0) {
			dstPtr->byteIndex += (p - start);
			return;
a1393 1
		    charCount--;
d1395 21
a1415 4
	    } else {
		if (charCount < segPtr->size - byteOffset) {
		    dstPtr->byteIndex += charCount;
		    return;
a1416 1
		charCount -= segPtr->size - byteOffset;
d1418 1
d1443 149
d1659 7
a1665 1
 *	index that points "count" characters earlier than the source index.
d1668 3
a1670 3
 *	*dstPtr is modified to refer to the character "count" characters
 *	before srcPtr, or to the first character in the file if there
 *	aren't "count" characters earlier than srcPtr.
d1679 1
a1679 1
TkTextIndexBackChars(srcPtr, charCount, dstPtr)
d1684 1
d1689 2
d1692 2
a1693 2
    if (charCount <= 0) {
	TkTextIndexForwChars(srcPtr, -charCount, dstPtr);
d1696 4
a1699 1

d1717 34
a1750 7
	if (segPtr->typePtr == &tkTextCharType) {
	    start = segPtr->body.chars;
	    end = segPtr->body.chars + segSize;
	    for (p = end; ; p = Tcl_UtfPrev(p, start)) {
		if (charCount == 0) {
		    dstPtr->byteIndex -= (end - p);
		    return;
d1752 7
a1758 2
		if (p == start) {
		    break;
a1759 6
		charCount--;
	    }
	} else {
	    if (charCount <= segSize) {
		dstPtr->byteIndex -= charCount;
		return;
a1760 1
	    charCount -= segSize;
d1827 2
a1828 1
StartEnd(string, indexPtr)
d1833 1
a1833 1
    TkTextIndex *indexPtr;	/* Index to mdoify based on string. */
d1839 2
a1840 1

d1849 31
d1882 10
a1891 4
	indexPtr->byteIndex = 0;
	for (segPtr = indexPtr->linePtr->segPtr; segPtr != NULL;
		segPtr = segPtr->nextPtr) {
	    indexPtr->byteIndex += segPtr->size;
a1892 1
	indexPtr->byteIndex -= sizeof(char);
d1895 5
a1899 1
	indexPtr->byteIndex = 0;
d1910 4
d1930 6
a1935 1
	    TkTextIndexForwChars(indexPtr, 1, indexPtr);
d1941 4
d1972 6
a1977 1
	    TkTextIndexForwChars(indexPtr, 1, indexPtr);
@


1.7
log
@tip 113 implementation
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.6 2002/08/05 04:30:40 dgp Exp $
d59 1
a59 1
Tcl_ObjType tclTextIndexType = {
d168 1
a168 1
    objPtr->typePtr = &tclTextIndexType;
d190 1
a190 1
    if (objPtr->typePtr == &tclTextIndexType) {
@


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
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.5 2002/06/22 08:21:51 hobbs Exp $
d35 238
d535 28
d586 35
d630 2
a631 1

d642 1
a642 1
	return TCL_OK;
d748 1
d780 1
d839 4
d862 2
a863 1
 *	The characters pointed to by string are modified.
d871 1
a871 1
void
d898 1
a898 1
    sprintf(string, "%d.%d", TkBTreeLineIndex(indexPtr->linePtr) + 1,
@


1.5
log
@	* doc/text.n:                             TIP #93 implementation that
	* generic/tkText.c (TextWidgetCmd):       enhances the text get and
	* generic/tkTextIndex.c (TkTextGetIndex): delete methods to accept
	* tests/text.test:                        multiple range pairs.
	This handles the delete case in an atomic, fixed-index fashion.
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.4 2002/01/17 03:35:00 dgp Exp $
d31 1
a31 1
static char *		ForwBack _ANSI_ARGS_((char *string,
d33 1
a33 1
static char *		StartEnd _ANSI_ARGS_(( char *string,
d317 1
a317 1
    char *string;		/* Textual description of position. */
d327 2
d359 2
a360 1
    p = strrchr(string, '.');
d372 1
a372 1
	hPtr = Tcl_FindHashEntry(&textPtr->tagTable, string);
d388 1
d408 3
a410 3
	p = string+1;
	x = strtol(p, &end, 0);
	if ((end == p) || (*end != ',')) {
d413 3
a415 3
	p = end+1;
	y = strtol(p, &end, 0);
	if (end == p) {
d449 1
a449 1
    for (p = string; *p != 0; p++) {
d462 1
a462 1
	result = TkTextWindowIndex(textPtr, string, indexPtr);
d469 2
a470 1
	    && (strncmp(string, "end", (size_t) (endOfBase-string)) == 0)) {
d485 2
a486 1
	result = TkTextMarkNameToIndex(textPtr, string, indexPtr);
d498 1
a498 1
	result = TkTextImageIndex(textPtr, string, indexPtr);
d516 1
a516 1
    p = endOfBase;
d518 2
a519 2
	while (isspace(UCHAR(*p))) {
	    p++;
d521 1
a521 1
	if (*p == 0) {
d525 2
a526 2
	if ((*p == '+') || (*p == '-')) {
	    p = ForwBack(p, indexPtr);
d528 1
a528 1
	    p = StartEnd(p, indexPtr);
d530 1
a530 1
	if (p == NULL) {
d534 1
d538 1
d658 1
a658 1
static char *
d660 1
a660 1
    char *string;		/* String to parse for additional info
d666 2
a667 2
    register char *p;
    char *end, *units;
d1098 1
a1098 1
static char *
d1100 1
a1100 1
    char *string;		/* String to parse for additional info
d1106 1
a1106 1
    char *p;
@


1.4
log
@	* Updated callers of Tcl_Utf* and Tcl_Regexp* APIs to reflect TIP 27
	API changes (see Tcl Patch 471509). [Patch 471513]
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.3 1999/04/16 01:51:24 stanton Exp $
d380 1
d531 1
@


1.3
log
@* Merged 8.1 branch into the main trunk
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id$
d67 1
a67 1
    char *p, *start;
d980 1
a980 1
    char *p, *start, *end;
@


1.3.14.1
log
@sync with head
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.4 2002/01/17 03:35:00 dgp Exp $
d67 1
a67 1
    CONST char *p, *start;
d980 1
a980 1
    CONST char *p, *start, *end;
@


1.3.18.1
log
@Merge with current TOT
Added support for the wm stackorder command
Fixed crash when selecting Menu items in the Application menu that we didn't
put there.
Added first bits of the use of CG for low-level drawing.  The line-drawing works,
the rest doesn't yet.  You have to set the file static useCGDrawing to 1 and
rebuild to activate this code.
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.3 1999/04/16 01:51:24 stanton Exp $
d67 1
a67 1
    CONST char *p, *start;
d980 1
a980 1
    CONST char *p, *start, *end;
@


1.3.18.2
log
@merged with trunk at tag macosx-8-4-merge-2002-08-20-trunk
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id$
d31 1
a31 1
static CONST char *	ForwBack _ANSI_ARGS_((CONST char *string,
d33 1
a33 1
static CONST char *	StartEnd _ANSI_ARGS_((CONST char *string,
d317 1
a317 1
    CONST char *string;		/* Textual description of position. */
a326 2
    CONST char *cp;
    Tcl_DString copy;
d357 1
a357 2
    Tcl_DStringInit(&copy);
    p = strrchr(Tcl_DStringAppend(&copy, string, -1), '.');
d369 1
a369 1
	hPtr = Tcl_FindHashEntry(&textPtr->tagTable, Tcl_DStringValue(&copy));
a379 1
	    Tcl_ResetResult(interp);
a383 1
	    Tcl_DStringFree(&copy);
d403 3
a405 3
	cp = string+1;
	x = strtol(cp, &end, 0);
	if ((end == cp) || (*end != ',')) {
d408 3
a410 3
	cp = end+1;
	y = strtol(cp, &end, 0);
	if (end == cp) {
d444 1
a444 1
    for (p = Tcl_DStringValue(&copy); *p != 0; p++) {
d457 1
a457 1
	result = TkTextWindowIndex(textPtr, Tcl_DStringValue(&copy), indexPtr);
d464 1
a464 2
	    && (strncmp(string, "end",
	    (size_t) (endOfBase-Tcl_DStringValue(&copy))) == 0)) {
d479 1
a479 2
	result = TkTextMarkNameToIndex(textPtr, Tcl_DStringValue(&copy),
		indexPtr);
d491 1
a491 1
	result = TkTextImageIndex(textPtr, Tcl_DStringValue(&copy), indexPtr);
d509 1
a509 1
    cp = endOfBase;
d511 2
a512 2
	while (isspace(UCHAR(*cp))) {
	    cp++;
d514 1
a514 1
	if (*cp == 0) {
d518 2
a519 2
	if ((*cp == '+') || (*cp == '-')) {
	    cp = ForwBack(cp, indexPtr);
d521 1
a521 1
	    cp = StartEnd(cp, indexPtr);
d523 1
a523 1
	if (cp == NULL) {
a526 1
    Tcl_DStringFree(&copy);
a529 2
    Tcl_DStringFree(&copy);
    Tcl_ResetResult(interp);
d648 1
a648 1
static CONST char *
d650 1
a650 1
    CONST char *string;		/* String to parse for additional info
d656 2
a657 2
    register CONST char *p, *units;
    char *end;
d1088 1
a1088 1
static CONST char *
d1090 1
a1090 1
    CONST char *string;		/* String to parse for additional info
d1096 1
a1096 1
    CONST char *p;
@


1.2
log
@Replaced SCCS strings, fixed binary files
@
text
@d8 1
a8 1
 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
d37 1
a37 1
 *--------------------------------------------------------------
d39 1
a39 1
 * TkTextMakeIndex --
d41 2
a42 2
 *	Given a line index and a character index, look things up
 *	in the B-tree and fill in a TkTextIndex structure.
d45 4
a48 4
 *	The structure at *indexPtr is filled in with information
 *	about the character at lineIndex and charIndex (or the
 *	closest existing character, if the specified one doesn't
 *	exist), and indexPtr is returned as result.
d53 1
a53 1
 *--------------------------------------------------------------
d57 92
a148 1
TkTextMakeIndex(tree, lineIndex, charIndex, indexPtr)
d157 3
a159 1
    int index;
d180 2
a181 2
    for (index = 0, segPtr = indexPtr->linePtr->segPtr; ;
	    segPtr = segPtr->nextPtr) {
d183 7
a189 1
	    indexPtr->charIndex = index-1;
d192 23
a214 4
	index += segPtr->size;
	if (index > charIndex) {
	    indexPtr->charIndex = charIndex;
	    break;
d221 1
a221 1
 *--------------------------------------------------------------
d225 2
a226 2
 *	Given an index, this procedure returns the segment and
 *	offset within segment for the index.
d229 4
a232 5
 *	The return value is a pointer to the segment referred to
 *	by indexPtr;  this will always be a segment with non-zero
 *	size.  The variable at *offsetPtr is set to hold the
 *	integer offset within the segment of the character
 *	given by indexPtr.
d237 1
a237 1
 *--------------------------------------------------------------
d242 3
a244 4
    TkTextIndex *indexPtr;		/* Text index. */
    int *offsetPtr;			/* Where to store offset within
					 * segment, or NULL if offset isn't
					 * wanted. */
d246 1
a246 1
    register TkTextSegment *segPtr;
d249 1
a249 1
    for (offset = indexPtr->charIndex, segPtr = indexPtr->linePtr->segPtr;
d261 1
a261 1
 *--------------------------------------------------------------
d265 2
a266 3
 *	Given a segment pointer and the line containing it, this
 *	procedure returns the offset of the segment within its
 *	line.
d269 2
a270 2
 *	The return value is the offset (within its line) of the
 *	first character in segPtr.
d275 1
a275 1
 *--------------------------------------------------------------
d280 2
a281 2
    TkTextSegment *segPtr;		/* Segment whose offset is desired. */
    TkTextLine *linePtr;		/* Line containing segPtr. */
d283 1
a283 1
    TkTextSegment *segPtr2;
d295 1
a295 1
 *----------------------------------------------------------------------
d299 1
a299 2
 *	Given a string, return the line and character indices that
 *	it describes.
d302 4
a305 4
 *	The return value is a standard Tcl return result.  If
 *	TCL_OK is returned, then everything went well and the index
 *	at *indexPtr is filled in;  otherwise TCL_ERROR is returned
 *	and an error message is left in interp->result.
d310 1
a310 1
 *----------------------------------------------------------------------
d320 1
a320 2
    register char *p;
    char *end, *endOfBase;
d375 2
a376 2
	TkTextMakeIndex(textPtr->tree, 0, 0, &first);
	TkTextMakeIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree), 0,
d440 1
a440 1
	TkTextMakeIndex(textPtr->tree, lineIndex, charIndex, indexPtr);
d469 1
a469 1
	TkTextMakeIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree),
d536 1
a536 1
 *----------------------------------------------------------------------
a538 1
 *
d540 2
a541 2
 *	This procedure generates a string description of an index,
 *	suitable for reading in again later.
d549 1
a549 1
 *----------------------------------------------------------------------
d554 1
a554 1
    TkTextIndex *indexPtr;	/* Pointer to index. */
d558 21
d580 1
a580 1
	    indexPtr->charIndex);
d584 1
a584 1
 *--------------------------------------------------------------
d588 1
a588 2
 *	Compare two indices to see which one is earlier in
 *	the text.
d591 3
a593 3
 *	The return value is 0 if index1Ptr and index2Ptr refer
 *	to the same position in the file, -1 if index1Ptr refers
 *	to an earlier position than index2Ptr, and 1 otherwise.
d598 1
a598 1
 *--------------------------------------------------------------
d603 2
a604 2
    TkTextIndex *index1Ptr;		/* First index. */
    TkTextIndex *index2Ptr;		/* Second index. */
d609 1
a609 1
	if (index1Ptr->charIndex < index2Ptr->charIndex) {
d611 1
a611 1
	} else if (index1Ptr->charIndex > index2Ptr->charIndex) {
d629 1
a629 1
 *----------------------------------------------------------------------
d633 2
a634 2
 *	This procedure handles +/- modifiers for indices to adjust
 *	the index forwards or backwards.
d637 4
a640 4
 *	If the modifier in string is successfully parsed then the
 *	return value is the address of the first character after the
 *	modifier, and *indexPtr is updated to reflect the modifier.
 *	If there is a syntax error in the modifier then NULL is returned.
d645 1
a645 1
 *----------------------------------------------------------------------
d685 1
a685 1
    while ((*p != 0) && !isspace(UCHAR(*p)) && (*p != '+') && (*p != '-')) {
d713 12
a724 1
	TkTextMakeIndex(indexPtr->tree, lineIndex, indexPtr->charIndex,
d733 1
a733 1
 *----------------------------------------------------------------------
d735 1
a735 1
 * TkTextIndexForwChars --
d737 2
a738 3
 *	Given an index for a text widget, this procedure creates a
 *	new index that points "count" characters ahead of the source
 *	index.
d741 3
a743 3
 *	*dstPtr is modified to refer to the character "count" characters
 *	after srcPtr, or to the last character in the file if there aren't
 *	"count" characters left in the file.
d748 1
a748 1
 *----------------------------------------------------------------------
a750 1
	/* ARGSUSED */
d752 5
a756 5
TkTextIndexForwChars(srcPtr, count, dstPtr)
    TkTextIndex *srcPtr;		/* Source index. */
    int count;				/* How many characters forward to
					 * move.  May be negative. */
    TkTextIndex *dstPtr;		/* Destination index: gets modified. */
d762 2
a763 2
    if (count < 0) {
	TkTextIndexBackChars(srcPtr, -count, dstPtr);
d768 1
a768 1
    dstPtr->charIndex += count;
d785 1
a785 1
	if (dstPtr->charIndex < lineLength) {
d788 1
a788 1
	dstPtr->charIndex -= lineLength;
d791 1
a791 1
	    dstPtr->charIndex = lineLength - 1;
d799 1
a799 1
 *----------------------------------------------------------------------
d801 1
a801 1
 * TkTextIndexBackChars --
d803 2
a804 3
 *	Given an index for a text widget, this procedure creates a
 *	new index that points "count" characters earlier than the
 *	source index.
d808 92
a899 2
 *	before srcPtr, or to the first character in the file if there aren't
 *	"count" characters earlier than srcPtr.
d904 1
a904 1
 *----------------------------------------------------------------------
d908 5
a912 5
TkTextIndexBackChars(srcPtr, count, dstPtr)
    TkTextIndex *srcPtr;		/* Source index. */
    int count;				/* How many characters backward to
					 * move.  May be negative. */
    TkTextIndex *dstPtr;		/* Destination index: gets modified. */
d917 2
a918 2
    if (count < 0) {
	TkTextIndexForwChars(srcPtr, -count, dstPtr);
d923 1
a923 1
    dstPtr->charIndex -= count;
d925 1
a925 1
    while (dstPtr->charIndex < 0) {
d935 1
a935 1
	    dstPtr->charIndex = 0;
d947 115
a1061 1
	    dstPtr->charIndex += segPtr->size;
d1063 2
d1111 1
a1111 1
	indexPtr->charIndex = 0;
d1114 1
a1114 1
	    indexPtr->charIndex += segPtr->size;
d1116 1
a1116 1
	indexPtr->charIndex -= 1;
d1119 1
a1119 1
	indexPtr->charIndex = 0;
d1140 1
a1140 1
	    indexPtr->charIndex += 1;
d1169 1
a1169 1
	    indexPtr->charIndex -= 1;
d1171 2
a1172 2
		if (indexPtr->charIndex < 0) {
		    indexPtr->charIndex = 0;
@


1.1
log
@Initial revision
@
text
@d13 1
a13 1
 * SCCS: @@(#) tkTextIndex.c 1.15 97/06/17 17:49:24
@


1.1.4.1
log
@initial tk8.1a2 version
@
text
@d8 1
a8 1
 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
d13 1
a13 1
 * SCCS: @@(#) tkTextIndex.c 1.18 98/01/12 15:33:45
d37 1
a37 1
 *---------------------------------------------------------------------------
d39 1
a39 1
 * TkTextMakeByteIndex --
d41 2
a42 2
 *	Given a line index and a byte index, look things up in the B-tree
 *	and fill in a TkTextIndex structure.
d45 4
a48 4
 *	The structure at *indexPtr is filled in with information about the
 *	character at lineIndex and byteIndex (or the closest existing
 *	character, if the specified one doesn't exist), and indexPtr is
 *	returned as result.
d53 1
a53 1
 *---------------------------------------------------------------------------
d57 1
a57 92
TkTextMakeByteIndex(tree, lineIndex, byteIndex, indexPtr)
    TkTextBTree tree;		/* Tree that lineIndex and charIndex refer
				 * to. */
    int lineIndex;		/* Index of desired line (0 means first
				 * line of text). */
    int byteIndex;		/* Byte index of desired character. */
    TkTextIndex *indexPtr;	/* Structure to fill in. */
{
    TkTextSegment *segPtr;
    int index;
    char *p, *start;
    Tcl_UniChar ch;

    indexPtr->tree = tree;
    if (lineIndex < 0) {
	lineIndex = 0;
	byteIndex = 0;
    }
    if (byteIndex < 0) {
	byteIndex = 0;
    }
    indexPtr->linePtr = TkBTreeFindLine(tree, lineIndex);
    if (indexPtr->linePtr == NULL) {
	indexPtr->linePtr = TkBTreeFindLine(tree, TkBTreeNumLines(tree));
	byteIndex = 0;
    }
    if (byteIndex == 0) {
	indexPtr->byteIndex = byteIndex;
	return indexPtr;
    }

    /*
     * Verify that the index is within the range of the line and points
     * to a valid character boundary.  
     */

    index = 0;
    for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
	if (segPtr == NULL) {
	    /*
	     * Use the index of the last character in the line.  Since
	     * the last character on the line is guaranteed to be a '\n',
	     * we can back up a constant sizeof(char) bytes.
	     */
	     
	    indexPtr->byteIndex = index - sizeof(char);
	    break;
	}
	if (index + segPtr->size > byteIndex) {
	    indexPtr->byteIndex = byteIndex;
	    if ((byteIndex > index) && (segPtr->typePtr == &tkTextCharType)) {
		/*
		 * Prevent UTF-8 character from being split up by ensuring
		 * that byteIndex falls on a character boundary.  If index
		 * falls in the middle of a UTF-8 character, it will be
		 * adjusted to the end of that UTF-8 character.
		 */

		start = segPtr->body.chars + (byteIndex - index);
		p = Tcl_UtfPrev(start, segPtr->body.chars);
		p += Tcl_UtfToUniChar(p, &ch);
		indexPtr->byteIndex += p - start;
	    }
	    break;
	}
	index += segPtr->size;
    }
    return indexPtr;
}

/*
 *---------------------------------------------------------------------------
 *
 * TkTextMakeCharIndex --
 *
 *	Given a line index and a character index, look things up in the
 *	B-tree and fill in a TkTextIndex structure.
 *
 * Results:
 *	The structure at *indexPtr is filled in with information about the
 *	character at lineIndex and charIndex (or the closest existing
 *	character, if the specified one doesn't exist), and indexPtr is
 *	returned as result.
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

TkTextIndex *
TkTextMakeCharIndex(tree, lineIndex, charIndex, indexPtr)
d66 1
a66 3
    char *p, *start, *end;
    int index, offset;
    Tcl_UniChar ch;
d87 2
a88 2
    index = 0;
    for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
d90 1
a90 7
	    /*
	     * Use the index of the last character in the line.  Since
	     * the last character on the line is guaranteed to be a '\n',
	     * we can back up a constant sizeof(char) bytes.
	     */
	     
	    indexPtr->byteIndex = index - sizeof(char);
d93 4
a96 23
	if (segPtr->typePtr == &tkTextCharType) {
	    /*
	     * Turn character offset into a byte offset.
	     */

	    start = segPtr->body.chars;
	    end = start + segPtr->size;
	    for (p = start; p < end; p += offset) {
		if (charIndex == 0) {
		    indexPtr->byteIndex = index;
		    return indexPtr;
		}
		charIndex--;
		offset = Tcl_UtfToUniChar(p, &ch);
		index += offset;
	    }
	} else {
	    if (charIndex < segPtr->size) {
		indexPtr->byteIndex = index;
		break;
	    }
	    charIndex -= segPtr->size;
	    index += segPtr->size;
d103 1
a103 1
 *---------------------------------------------------------------------------
d107 2
a108 2
 *	Given an index, this procedure returns the segment and offset
 *	within segment for the index.
d111 5
a115 4
 *	The return value is a pointer to the segment referred to by
 *	indexPtr; this will always be a segment with non-zero size.  The
 *	variable at *offsetPtr is set to hold the integer offset within
 *	the segment of the character given by indexPtr.
d120 1
a120 1
 *---------------------------------------------------------------------------
d125 4
a128 3
    CONST TkTextIndex *indexPtr;/* Text index. */
    int *offsetPtr;		/* Where to store offset within segment, or
				 * NULL if offset isn't wanted. */
d130 1
a130 1
    TkTextSegment *segPtr;
d133 1
a133 1
    for (offset = indexPtr->byteIndex, segPtr = indexPtr->linePtr->segPtr;
d145 1
a145 1
 *---------------------------------------------------------------------------
d149 3
a151 2
 *	Given a segment pointer and the line containing it, this procedure
 *	returns the offset of the segment within its line.
d154 2
a155 2
 *	The return value is the offset (within its line) of the first
 *	character in segPtr.
d160 1
a160 1
 *---------------------------------------------------------------------------
d165 2
a166 2
    CONST TkTextSegment *segPtr;/* Segment whose offset is desired. */
    CONST TkTextLine *linePtr;	/* Line containing segPtr. */
d168 1
a168 1
    CONST TkTextSegment *segPtr2;
d180 1
a180 1
 *---------------------------------------------------------------------------
d184 2
a185 1
 *	Given a string, return the index that is described.
d188 4
a191 4
 *	The return value is a standard Tcl return result.  If TCL_OK is
 *	returned, then everything went well and the index at *indexPtr is
 *	filled in; otherwise TCL_ERROR is returned and an error message
 *	is left in the interp's result.
d196 1
a196 1
 *---------------------------------------------------------------------------
d206 2
a207 1
    char *p, *end, *endOfBase;
d262 2
a263 2
	TkTextMakeByteIndex(textPtr->tree, 0, 0, &first);
	TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree), 0,
d327 1
a327 1
	TkTextMakeCharIndex(textPtr->tree, lineIndex, charIndex, indexPtr);
d356 1
a356 1
	TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree),
d423 1
a423 1
 *---------------------------------------------------------------------------
d426 1
d428 2
a429 2
 *	This procedure generates a string description of an index, suitable
 *	for reading in again later.
d437 1
a437 1
 *---------------------------------------------------------------------------
d442 1
a442 1
    CONST TkTextIndex *indexPtr;/* Pointer to index. */
a445 21
    TkTextSegment *segPtr;
    int numBytes, charIndex;

    numBytes = indexPtr->byteIndex;
    charIndex = 0;
    for (segPtr = indexPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
	if (numBytes < segPtr->size) {
	    break;
	}
	if (segPtr->typePtr == &tkTextCharType) {
	    charIndex += Tcl_NumUtfChars(segPtr->body.chars, segPtr->size);
	} else {
	    charIndex += segPtr->size;
	}
	numBytes -= segPtr->size;
    }
    if (segPtr->typePtr == &tkTextCharType) {
	charIndex += Tcl_NumUtfChars(segPtr->body.chars, numBytes);
    } else {
	charIndex += numBytes;
    }
d447 1
a447 1
	    charIndex);
d451 1
a451 1
 *---------------------------------------------------------------------------
d455 2
a456 1
 *	Compare two indices to see which one is earlier in the text.
d459 3
a461 3
 *	The return value is 0 if index1Ptr and index2Ptr refer to the same
 *	position in the file, -1 if index1Ptr refers to an earlier position
 *	than index2Ptr, and 1 otherwise.
d466 1
a466 1
 *---------------------------------------------------------------------------
d471 2
a472 2
    CONST TkTextIndex *index1Ptr;		/* First index. */
    CONST TkTextIndex *index2Ptr;		/* Second index. */
d477 1
a477 1
	if (index1Ptr->byteIndex < index2Ptr->byteIndex) {
d479 1
a479 1
	} else if (index1Ptr->byteIndex > index2Ptr->byteIndex) {
d497 1
a497 1
 *---------------------------------------------------------------------------
d501 2
a502 2
 *	This procedure handles +/- modifiers for indices to adjust the
 *	index forwards or backwards.
d505 4
a508 4
 *	If the modifier in string is successfully parsed then the return
 *	value is the address of the first character after the modifier,
 *	and *indexPtr is updated to reflect the modifier.  If there is a
 *	syntax error in the modifier then NULL is returned.
d513 1
a513 1
 *---------------------------------------------------------------------------
d553 1
a553 1
    while ((*p != '\0') && !isspace(UCHAR(*p)) && (*p != '+') && (*p != '-')) {
d581 1
a581 12
	/*
	 * This doesn't work quite right if using a proportional font or
	 * UTF-8 characters with varying numbers of bytes.  The cursor will
	 * bop around, keeping a constant number of bytes (not characters)
	 * from the left edge (but making sure not to split any UTF-8
	 * characters), regardless of the x-position the index corresponds
	 * to.  The proper way to do this is to get the x-position of the
	 * index and then pick the character at the same x-position in the
	 * new line.
	 */

	TkTextMakeByteIndex(indexPtr->tree, lineIndex, indexPtr->byteIndex,
d590 1
a590 1
 *---------------------------------------------------------------------------
d592 1
a592 1
 * TkTextIndexForwBytes --
d594 3
a596 2
 *	Given an index for a text widget, this procedure creates a new
 *	index that points "count" bytes ahead of the source index.
d599 3
a601 3
 *	*dstPtr is modified to refer to the character "count" bytes after
 *	srcPtr, or to the last character in the TkText if there aren't
 *	"count" bytes left.
d606 1
a606 1
 *---------------------------------------------------------------------------
d609 1
d611 5
a615 5
TkTextIndexForwBytes(srcPtr, byteCount, dstPtr)
    CONST TkTextIndex *srcPtr;	/* Source index. */
    int byteCount;		/* How many bytes forward to move.  May be
				 * negative. */
    TkTextIndex *dstPtr;	/* Destination index: gets modified. */
d621 2
a622 2
    if (byteCount < 0) {
	TkTextIndexBackBytes(srcPtr, -byteCount, dstPtr);
d627 1
a627 1
    dstPtr->byteIndex += byteCount;
d644 1
a644 1
	if (dstPtr->byteIndex < lineLength) {
d647 1
a647 1
	dstPtr->byteIndex -= lineLength;
d650 1
a650 1
	    dstPtr->byteIndex = lineLength - 1;
d658 1
a658 1
 *---------------------------------------------------------------------------
d660 1
a660 1
 * TkTextIndexForwChars --
d662 3
a664 2
 *	Given an index for a text widget, this procedure creates a new
 *	index that points "count" characters ahead of the source index.
d668 2
a669 2
 *	after srcPtr, or to the last character in the TkText if there
 *	aren't "count" characters left in the file.
d674 1
a674 91
 *---------------------------------------------------------------------------
 */

void
TkTextIndexForwChars(srcPtr, charCount, dstPtr)
    CONST TkTextIndex *srcPtr;	/* Source index. */
    int charCount;		/* How many characters forward to move.
				 * May be negative. */
    TkTextIndex *dstPtr;	/* Destination index: gets modified. */
{
    TkTextLine *linePtr;
    TkTextSegment *segPtr;
    int byteOffset;
    char *start, *end, *p;
    Tcl_UniChar ch;

    if (charCount < 0) {
	TkTextIndexBackChars(srcPtr, -charCount, dstPtr);
	return;
    }

    *dstPtr = *srcPtr;

    /*
     * Find seg that contains src byteIndex.
     * Move forward specified number of chars.
     */

    segPtr = TkTextIndexToSeg(dstPtr, &byteOffset);
    while (1) {
	/*
	 * Go through each segment in line looking for specified character
	 * index.
	 */

	for ( ; segPtr != NULL; segPtr = segPtr->nextPtr) {
	    if (segPtr->typePtr == &tkTextCharType) {
		start = segPtr->body.chars + byteOffset;
		end = segPtr->body.chars + segPtr->size;
		for (p = start; p < end; p += Tcl_UtfToUniChar(p, &ch)) {
		    if (charCount == 0) {
			dstPtr->byteIndex += (p - start);
			return;
		    }
		    charCount--;
		}
	    } else {
		if (charCount < segPtr->size - byteOffset) {
		    dstPtr->byteIndex += charCount;
		    return;
		}
		charCount -= segPtr->size - byteOffset;
	    }
	    dstPtr->byteIndex += segPtr->size - byteOffset;
	    byteOffset = 0;
	}

	/*
	 * Go to the next line.  If we are at the end of the text item,
	 * back up one byte (for the terminal '\n' character) and return
	 * that index.
	 */
	 
	linePtr = TkBTreeNextLine(dstPtr->linePtr);
	if (linePtr == NULL) {
	    dstPtr->byteIndex -= sizeof(char);
	    return;
	}
	dstPtr->linePtr = linePtr;
	dstPtr->byteIndex = 0;
	segPtr = dstPtr->linePtr->segPtr;
    }
}

/*
 *---------------------------------------------------------------------------
 *
 * TkTextIndexBackBytes --
 *
 *	Given an index for a text widget, this procedure creates a new
 *	index that points "count" bytes earlier than the source index.
 *
 * Results:
 *	*dstPtr is modified to refer to the character "count" bytes before
 *	srcPtr, or to the first character in the TkText if there aren't
 *	"count" bytes earlier than srcPtr.
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
d678 5
a682 5
TkTextIndexBackBytes(srcPtr, byteCount, dstPtr)
    CONST TkTextIndex *srcPtr;	/* Source index. */
    int byteCount;		/* How many bytes backward to move.  May be
				 * negative. */
    TkTextIndex *dstPtr;	/* Destination index: gets modified. */
d687 2
a688 2
    if (byteCount < 0) {
	TkTextIndexForwBytes(srcPtr, -byteCount, dstPtr);
d693 1
a693 1
    dstPtr->byteIndex -= byteCount;
d695 1
a695 1
    while (dstPtr->byteIndex < 0) {
d705 1
a705 1
	    dstPtr->byteIndex = 0;
d717 1
a717 115
	    dstPtr->byteIndex += segPtr->size;
	}
    }
}

/*
 *---------------------------------------------------------------------------
 *
 * TkTextIndexBackChars --
 *
 *	Given an index for a text widget, this procedure creates a new
 *	index that points "count" characters earlier than the source index.
 *
 * Results:
 *	*dstPtr is modified to refer to the character "count" characters
 *	before srcPtr, or to the first character in the file if there
 *	aren't "count" characters earlier than srcPtr.
 *
 * Side effects:
 *	None.
 *
 *---------------------------------------------------------------------------
 */

void
TkTextIndexBackChars(srcPtr, charCount, dstPtr)
    CONST TkTextIndex *srcPtr;	/* Source index. */
    int charCount;		/* How many characters backward to move.
				 * May be negative. */
    TkTextIndex *dstPtr;	/* Destination index: gets modified. */
{
    TkTextSegment *segPtr, *oldPtr;
    int lineIndex, segSize;
    char *p, *start, *end;

    if (charCount <= 0) {
	TkTextIndexForwChars(srcPtr, -charCount, dstPtr);
	return;
    }

    *dstPtr = *srcPtr;

    /*
     * Find offset within seg that contains byteIndex.
     * Move backward specified number of chars.
     */

    lineIndex = -1;
    
    segSize = dstPtr->byteIndex;
    for (segPtr = dstPtr->linePtr->segPtr; ; segPtr = segPtr->nextPtr) {
	if (segSize <= segPtr->size) {
	    break;
	}
	segSize -= segPtr->size;
    }
    while (1) {
	if (segPtr->typePtr == &tkTextCharType) {
	    start = segPtr->body.chars;
	    end = segPtr->body.chars + segSize;
	    for (p = end; ; p = Tcl_UtfPrev(p, start)) {
		if (charCount == 0) {
		    dstPtr->byteIndex -= (end - p);
		    return;
		}
		if (p == start) {
		    break;
		}
		charCount--;
	    }
	} else {
	    if (charCount < segSize) {
		dstPtr->byteIndex -= charCount;
		return;
	    }
	    charCount -= segSize;
	}
	dstPtr->byteIndex -= segSize;

	/*
	 * Move back into previous segment.
	 */

	oldPtr = segPtr;
	segPtr = dstPtr->linePtr->segPtr;
	if (segPtr != oldPtr) {
	    for ( ; segPtr->nextPtr != oldPtr; segPtr = segPtr->nextPtr) {
		/* Empty body. */
	    }
	    segSize = segPtr->size;
	    continue;
	}

	/*
	 * Move back to previous line.
	 */

	if (lineIndex < 0) {
	    lineIndex = TkBTreeLineIndex(dstPtr->linePtr);
	}
	if (lineIndex == 0) {
	    dstPtr->byteIndex = 0;
	    return;
	}
	lineIndex--;
	dstPtr->linePtr = TkBTreeFindLine(dstPtr->tree, lineIndex);

	/*
	 * Compute the length of the line and add that to dstPtr->byteIndex.
	 */

	oldPtr = dstPtr->linePtr->segPtr;
	for (segPtr = oldPtr; segPtr != NULL; segPtr = segPtr->nextPtr) {
	    dstPtr->byteIndex += segPtr->size;
	    oldPtr = segPtr;
a718 2
	segPtr = oldPtr;
	segSize = segPtr->size;
d765 1
a765 1
	indexPtr->byteIndex = 0;
d768 1
a768 1
	    indexPtr->byteIndex += segPtr->size;
d770 1
a770 1
	indexPtr->byteIndex -= sizeof(char);
d773 1
a773 1
	indexPtr->byteIndex = 0;
d794 1
a794 1
	    indexPtr->byteIndex += sizeof(char);
d823 1
a823 1
	    indexPtr->byteIndex -= sizeof(char);
d825 2
a826 2
		if (indexPtr->byteIndex < 0) {
		    indexPtr->byteIndex = 0;
@


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


1.1.4.3
log
@Fixed bug 991, ".t index end-2c"
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.1.4.2 1998/09/30 02:17:25 stanton Exp $
d564 1
a564 1
	if (numBytes <= segPtr->size) {
@


1.1.4.4
log
@Fix in tkTextIndex.c for end-2c for text widget add command.
@
text
@d13 1
a13 1
 * RCS: @@(#) $Id: tkTextIndex.c,v 1.1.4.3 1999/03/27 02:31:39 redman Exp $
d1018 1
a1018 1
	    if (charCount <= segSize) {
@


