Milán Major

netscape-revival

1 branch
Code

lib/mac/VText/V_PowerPlant_Mods/LTextEngine.parts.cp

//	===========================================================================
//	LTextEngine.cp				©1994 Metrowerks Inc. All rights reserved.
//	===========================================================================

#ifdef PowerPlant_PCH
#include PowerPlant_PCH
#endif

#include	"LTextEngine.h"

#ifndef		__AEREGISTRY__
#include	<AERegistry.h>
#endif

#pragma	warn_unusedarg off

/*	===========================================================================

Notes:
------

¥	Part accessing is implemented in a two tier scheme.  The first tier would
	be overridden for performance enhancements.  The second tier is just to
	increase code reuse for this specific implementation.

¥	Client code of text engines should only use:

		CountParts,
		FindNthPart,
		FindSubRangePartIndices, and
		FindPart

¥	White space doesn't count as words (anymore).
		
*/
//	===========================================================================


Int32	LTextEngine::CountParts(
	const TextRangeT	&inQueryRange,
	DescType			inPartType) const
{
	Int32		rval = 0;
	
	TextRangeT	queryRange = inQueryRange;

	queryRange.Crop(GetTotalRange());

	switch (inPartType) {
		case cParagraph:
			rval = CountParas(queryRange);
			break;
		case cLine:
			rval = CountLines(queryRange);
			break;
		case cWord:
			rval = CountWords(queryRange);
			break;
		case cChar:
			rval = CountChars(queryRange);
			break;
		default:
			ThrowOSErr_(errAEUnknownObjectType);
			break;
	}
	
	return rval;
}

DescType	LTextEngine::FindNthPart(
	const TextRangeT	&inQueryRange,
	DescType			inPartType,
	Int32				inPartIndex,
	TextRangeT			*outPartRange) const
/*
	Find the nth part occurance of an inPartType relative to inQueryRange.
	
	OutPartRange returns the range of the found part.
*/
{
	TextRangeT	range;
	DescType	rval = typeNull;
	
	TextRangeT	queryRange = inQueryRange;

	queryRange.Crop(GetTotalRange());
	
	Assert_(inPartIndex > 0);
	Assert_(queryRange.Start() >= 0);
	Assert_(queryRange.Length() >= 0);
	
	switch (inPartType) {

		case cParagraph:
			rval = FindNthPara(queryRange, inPartIndex, &range);
			break;

		case cLine:
			rval = FindNthLine(queryRange, inPartIndex, &range);
			break;

		case cWord:
			rval = FindNthWord(queryRange, inPartIndex, &range);
			break;

		case cChar:
			rval = FindNthChar(queryRange, inPartIndex, &range);
			break;

		default:
			ThrowOSErr_(errAEUnknownObjectType);
			break;
		}

	if (outPartRange && (rval != typeNull))
		*outPartRange = range;
		
	return rval;
}

DescType	LTextEngine::FindSubRangePartIndices(
	const TextRangeT	&inQueryRange,
	const TextRangeT	&inSubRange,
	DescType			inSubRangeType,
	Int32				*outFirstIndex,
	Int32				*outLastIndex) const
/*
	Returns the part range indices (1 based) for inSubRange of part
	type inSubRangeType.
	
	If inSubRange doesn't fall on inSubRangeType part boundaries, the
	closest approximate inSubRangeType part indices will be returned AND
	the function result will indicate the part type of the inSubRange
	boundaries.
	
	If inSubRange does fall on inSubRangeType part boundaries, the
	function result will be inSubRangeType.
	
	Everything is relative to inQueryRange with the first inSubRangeType
	part of inQueryRange numbered 1.  Subsequent indices are
	relative to that first part.  Also, the first position of
	inQueryRange "starts" a new part for all part types and the end of
	inQueryRange "ends" all part types.
	
	If inSubRange doesn't lie within inQueryRange, the function result
	will be typeNull.
	
	If typeNull is returned, outFirstIndex & outLastIndex have not
	been modified.
*/
{
	Int32		index1 = -1,
				index2 = -1;
	DescType	partFoundType = inSubRangeType;

	TextRangeT	queryRange = inQueryRange,
				subRange = inSubRange;

	queryRange.Crop(GetTotalRange());
	subRange.Crop(GetTotalRange());
	
	do {
		//	Sanity checks.
		if (!queryRange.Contains(subRange)) {
			partFoundType = typeNull;
			Assert_(false);
			break;
		}
		
		switch (inSubRangeType) {

			case cParagraph:
				partFoundType = FindParaSubRangeIndices(queryRange, subRange, &index1, &index2);
				break;

			case cLine:
				partFoundType = FindLineSubRangeIndices(queryRange, subRange, &index1, &index2);
				break;

			case cWord:
				partFoundType = FindWordSubRangeIndices(queryRange, subRange, &index1, &index2);
				break;

			case cChar:
				partFoundType = FindCharSubRangeIndices(queryRange, subRange, &index1, &index2);
				break;

			default:
				ThrowOSErr_(errAEUnknownObjectType);
				break;
		}
	} while (false);

	if (partFoundType != typeNull) {
		if (outFirstIndex)
			*outFirstIndex = index1;
		if (outLastIndex) {
			*outLastIndex = index2;
		}
	}

	return partFoundType;
}

DescType	LTextEngine::FindPart(
	const TextRangeT	&inQueryRange,
	TextRangeT			*ioSubRange,
	DescType			inSubPartType,
	Int32				*outNextOffset) const
{
	ThrowIfNULL_(ioSubRange);
	
	TextRangeT	queryRange = inQueryRange;
	DescType	rval = typeNull;
	
	queryRange.Crop(GetTotalRange());
	
	if (!queryRange.Contains(*ioSubRange))
		return typeNull;
		
	if (ioSubRange->Length()) {
		TextRangeT	range = *ioSubRange,
					range2 = range;
		DescType	rval1,
					rval2;
		
		range.Front();
		range2.Back();
		
		do {
			rval1 = FindPart(queryRange, &range, inSubPartType, NULL);
			rval = rval1;
			if (rval1 == typeNull)
				break;
				
			if (range.End() >= range2.Start())
				break;

			rval2 = FindPart(queryRange, &range2, inSubPartType, NULL);
			if (rval2 == typeNull)
				Throw_(paramErr);
			if (rval != rval2)
				rval = cChar;

			range.Union(range2);
		} while (false);
		
		if (rval != typeNull) {
			if (outNextOffset)
				*outNextOffset = range.End();
			Assert_(range.Contains(*ioSubRange));
			*ioSubRange = range;
		}
	} else {
		switch (inSubPartType) {
			case cParagraph:
				rval = FindPara(queryRange, ioSubRange, outNextOffset);
				break;
			case cLine:
				rval = FindLine(queryRange, ioSubRange, outNextOffset);
				break;
			case cWord:
				rval = FindWord(queryRange, ioSubRange, outNextOffset);
				break;
			case cChar:
				rval = FindChar(queryRange, ioSubRange, outNextOffset);
				break;
			default:
				ThrowOSErr_(errAEUnknownObjectType);
				break;
		}
	}

	return rval;
}

//	---------------------------------------------------------------------------
//	CountParts implementation:

Int32	LTextEngine::CountParas(const TextRangeT &inQueryRange) const
{
	return CountParts_(inQueryRange, cParagraph);
}

Int32	LTextEngine::CountLines(const TextRangeT &inQueryRange) const
{
	return CountParts_(inQueryRange, cLine);
}

Int32	LTextEngine::CountWords(const TextRangeT &inQueryRange) const
{
	return CountParts_(inQueryRange, cWord);
}

Int32	LTextEngine::CountChars(const TextRangeT &inQueryRange) const
{
	if (mAttributes & textEngineAttr_MultiByte) {
		return CountParts_(inQueryRange, cChar);
	} else {
		return inQueryRange.Length();
	}
}

//	---------------------------------------------------------------------------
//	FindSubRangePartIndices implementation:

/*
	The following routines should only be used by FindSubRangePartIndices.
	
	Description for all routines:
	
		Return the subrange indices for inSubRange relative to inQueryRange.
		
		index1 & index2 are modified only if corresponding indexes are found.
		
		Assumptions:
			
			inQueryRange falls on part boundaries
			
			inSubRange falls on part boundaries or, if 0 length, corresponds
			to the start of a part
*/

DescType	LTextEngine::FindParaSubRangeIndices(
	const TextRangeT	&inQueryRange,
	const TextRangeT	&inSubRange,
	Int32				*outIndex1,
	Int32				*outIndex2) const
{
	return FindPartSubRangeIndices(inQueryRange, inSubRange, cParagraph, outIndex1, outIndex2);
}

DescType	LTextEngine::FindLineSubRangeIndices(
	const TextRangeT	&inQueryRange,
	const TextRangeT	&inSubRange,
	Int32				*outIndex1,
	Int32				*outIndex2) const
{
	return FindPartSubRangeIndices(inQueryRange, inSubRange, cLine, outIndex1, outIndex2);
}

DescType	LTextEngine::FindWordSubRangeIndices(
	const TextRangeT	&inQueryRange,
	const TextRangeT	&inSubRange,
	Int32				*outIndex1,
	Int32				*outIndex2) const
{
	return FindPartSubRangeIndices(inQueryRange, inSubRange, cWord, outIndex1, outIndex2);
}

DescType	LTextEngine::FindCharSubRangeIndices(
	const TextRangeT	&inQueryRange,
	const TextRangeT	&inSubRange,
	Int32				*outIndex1,
	Int32				*outIndex2) const
{
	if (mAttributes & textEngineAttr_MultiByte) {

		return FindPartSubRangeIndices(inQueryRange, inSubRange, cChar, outIndex1, outIndex2);

	} else {

		//	index1
		*outIndex1 = inSubRange.Start() - inQueryRange.Start() + 1;
		
		//	index2
		if (inSubRange.Length() > 0)
			*outIndex2 = inSubRange.End() - inQueryRange.Start();
		else
			*outIndex2 = *outIndex1;
			
		return cChar;

	}
}

//	---------------------------------------------------------------------------
//	FindNthPart implementation:

DescType	LTextEngine::FindNthPara(
	const TextRangeT	&inQueryRange,
	Int32				inPartIndex,
	TextRangeT			*outRange) const
{
	return FindNthPart_(inQueryRange, inPartIndex, cParagraph, outRange);
}

DescType	LTextEngine::FindNthLine(
	const TextRangeT	&inQueryRange,
	Int32				inPartIndex,
	TextRangeT			*outRange) const
{
	return FindNthPart_(inQueryRange, inPartIndex, cLine, outRange);
}

DescType	LTextEngine::FindNthWord(
	const TextRangeT	&inQueryRange,
	Int32				inPartIndex,
	TextRangeT			*outRange) const
{
	return FindNthPart_(inQueryRange, inPartIndex, cWord, outRange);
}

DescType	LTextEngine::FindNthChar(
	const TextRangeT	&inQueryRange,
	Int32				inPartIndex,
	TextRangeT			*outRange) const
{
	if (mAttributes & textEngineAttr_MultiByte) {

		return FindNthPart_(inQueryRange, inPartIndex, cChar, outRange);

	} else {

		*outRange = TextRangeT(inQueryRange.Start() + inPartIndex -1);
		outRange->SetLength(1);
		
		return cChar;

	}
}

//	---------------------------------------------------------------------------
//	Where the real non cChar work is done by this implementation...

Int32	LTextEngine::CountParts_(const TextRangeT inQueryRange, DescType inPartType) const
{
	Int32		count = 0,
				nextOffset;
	TextRangeT	range = inQueryRange;
	DescType	foundType;
	
	range.Front();

	while (true) {
		foundType = FindPart(inQueryRange, &range, inPartType, &nextOffset);
		if (foundType == typeNull)
			break;

		if (!(inPartType == cWord) || !IsWhiteSpace(range))
			count++;
		
		range.After();
	}

	return count;
}

DescType	LTextEngine::FindPartSubRangeIndices(
	const TextRangeT	&inQueryRange,
	const TextRangeT	&inSubRange,
	DescType			inSubPartType,
	Int32				*outIndex1,
	Int32				*outIndex2) const
{
	TextRangeT	range = inQueryRange;
	Int32		count = 0;
	DescType	partType,
				rval = inSubPartType;
	Boolean		foundIndex1 = false;
	
	if (!inQueryRange.Contains(inSubRange))
		return typeNull;
		
	range.Front();
	
	while (true) {
		partType = FindPart(inQueryRange, &range, inSubPartType, NULL);
		
		if (partType == typeNull)
			break;
		
		if ((inSubPartType == cWord) && IsWhiteSpace(range)) {
			range.After();
			continue;
		}

		count++;
		if (!foundIndex1)
			*outIndex1 = count;
		*outIndex2 = count;
		
		//	index1
		if (!foundIndex1) {
			if (range.Start() == inSubRange.Start()) {
				rval = partType;
				foundIndex1 = true;
			} else if (range.End() > inSubRange.Start()) {
				rval = cChar;
				foundIndex1 = true;
			}
		}
		
		//	index2
		if (foundIndex1) {
			if (range.End() == inSubRange.End()) {
				break;
			} else if (range.End() > inSubRange.End()) {
				rval = cChar;
				break;
			}
		}
	
		//	next
		range.After();
	}
	
	return rval;
}

DescType	LTextEngine::FindNthPart_(
	const TextRangeT	&inQueryRange,
	Int32				inPartIndex,
	DescType			inPartType,
	TextRangeT			*outRange) const
{
	Int32		i,
				nextOffset = inQueryRange.Start();
	TextRangeT	range = inQueryRange;
	DescType	partType;
	
	range.Front();
	i = 0;
	while (true) {
		partType = FindPart(inQueryRange, &range, inPartType, NULL);
		
		if (partType == typeNull)
			ThrowOSErr_(errAEIllegalIndex);
		
		if (!(inPartType == cWord) || !IsWhiteSpace(range))
			i++;
		
		if (i == inPartIndex)
			break;
		
		Assert_(i < inPartIndex);
		
		range.After();
	}
	
	if (partType != typeNull)
		*outRange = range;
	
	return partType;
}

DescType	LTextEngine::FindPara(
	const TextRangeT	&inQueryRange,
	TextRangeT			*ioRange,
	Int32				*outEndOffset) const
/*
	Should only be called by FindPart
*/
{
	return FindPara_(inQueryRange, ioRange, outEndOffset);
}

DescType	LTextEngine::FindPara_(
	const TextRangeT	&inQueryRange,
	TextRangeT			*ioRange,
	Int32				*outEndOffset) const
{
	ThrowIfNULL_(ioRange);

	TextRangeT		range = *ioRange,
					rangeFound,
					rangeReported;
	
	if (range.Insertion() == rangeCode_Before) {
		range.SetStart(range.Start() - PrevCharSize(range.Start()));
		range.SetInsertion(rangeCode_After);
	}
	
	//	Initial validity check
	if (range.Start() < inQueryRange.Start())
		return typeNull;
	if (range.Start() >= inQueryRange.End())
		return typeNull;

	//	Find
	{
		Int32	size,
				offset = range.Start();
		Int16	c;
		
		//	back
		do {
			size = PrevCharSize(offset);
			if (size == 0)
				break;
			c = ThisChar(offset - size);
			if (c == 0x0d)
				break;
			offset -= size;
		} while (true);
		rangeFound = TextRangeT(offset);
		
		//	forward
		offset = range.Start();
		do {
			size = ThisCharSize(offset);
			if (size == 0)
				break;
			c = ThisChar(offset);
			offset += size;
		} while (c != 0x0d);
		rangeFound.SetEnd(offset);
	}
	
	if (rangeFound.Length() <= 0)
		return typeNull;
	
	//	Truncate to query range.
	rangeReported = rangeFound;
	rangeReported.Intersection(inQueryRange);
	if (rangeReported.Undefined())
		return typeNull;
		
	//	Report
	*ioRange = rangeReported;
	
	if (outEndOffset)
		*outEndOffset = rangeReported.End();
	
	if (rangeFound == rangeReported)
		return cParagraph;
	else
		return cChar;
}

DescType	LTextEngine::FindLine(
	const TextRangeT	&inQueryRange,
	TextRangeT			*ioRange,
	Int32				*outEndOffset) const
/*
	Should only be called by FindPart
*/
{
	return typeNull;
}

DescType	LTextEngine::FindWord(
	const TextRangeT	&inQueryRange,
	TextRangeT			*ioRange,
	Int32				*outEndOffset) const
{
	return typeNull;
}

DescType	LTextEngine::FindChar(
	const TextRangeT	&inQueryRange,
	TextRangeT			*ioRange,
	Int32				*outEndOffset) const
{
	if (!inQueryRange.Contains(*ioRange))
		return typeNull;

	Assert_(ioRange->Length() == 0);
	if (ioRange->Insertion() == rangeCode_Before) {
		ioRange->SetLength(PrevCharSize(ioRange->Start()));
		ioRange->SetStart(ioRange->Start() - ioRange->Length());
	} else {
		Assert_(ioRange->Insertion() == rangeCode_After);
		ioRange->SetLength(ThisCharSize(ioRange->Start()));
	}

	ioRange->Crop(inQueryRange);
	if (outEndOffset)
		*outEndOffset = ioRange->End();

	return cChar;
}

Boolean	LTextEngine::IsWhiteSpace(const TextRangeT &inRange) const
{
	Boolean	rval = true;
	Int32	offset = inRange.Start(),
			end = inRange.End();
	Int16	c;
	
	while ((offset < end) && rval) {
		c = ThisChar(offset);
		
		switch (c) {
			case ' ':
			case '\t':
			case 0x0d:
				offset += ThisCharSize(offset);
				break;
			
			default:
				rval = false;
				break;
		}
	}
	
	return rval;
}