Milán Major

netscape-revival

1 branch
Code

lib/mac/TX/CTXTextEngine.cp

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	CTXTextEngine.cp					  ©1995 Netscape. All rights reserved.
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

#include	"CTXTextEngine.h"
#include	"CTXStyleSet.h"

#include	<UMemoryMgr.h>
#include	<UDrawingState.h>
#include	<LDataTube.h>
#include	<PP_KeyCodes.h>
#include	<LHandlerView.h>
#include	<URegions.h>
#include	<UDrawingState.h>

#include	"ObjectsRanges.h"
#include	"Textension.h"
#include	"CPageGeometry.h"

#ifdef	INCLUDE_TX_TSM
	#include	<TSMTextension.h>
#endif

#ifndef		__ERRORS__
#include	<Errors.h>
#endif

#ifndef	__AEREGISTRY__
#include "AERegistry.h"
#endif

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

void	TextRange2TXRange(const TextRangeT &inRange, TOffsetRange *outRange);
void	TXRange2TextRange(const TOffsetRange &inRange, TextRangeT *outRange);

void	TextRange2TXRange(const TextRangeT &inRange, TOffsetRange *outRange)
{
	Assert_(inRange.Length() >= 0);
	
	if (inRange.Length() > 0)
		outRange->Set(inRange.Start(), inRange.End(), false, true);
/*?	Textension doesn't seem to honor its trailing edge parameters everywhere
	else if (inRange.Insertion() == rangeCode_After)
		outRange->Set(inRange.Start(), inRange.Start(), true, true);
*/
	else
		outRange->Set(inRange.Start(), inRange.Start(), false, false);
}

void	TXRange2TextRange(const TOffsetRange &inRange, TextRangeT *outRange)
{
	if (inRange.Len()) {
		*outRange = TextRangeT(inRange.Start(), inRange.End());
	} else {
/*?	Textension doesn't seem to honor its trailing edge parameters everywhere
		outRange->SetStart(inRange.Start());
		if (inRange.rangeStart.trailEdge)
			outRange->SetInsertion(rangeCode_After);
		else
			outRange->SetInsertion(rangeCode_Before);
*/
		*outRange = TextRangeT(inRange.Start());
	}
}


// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ


Boolean	CTXTextEngine::sTXInited = false;

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ


void CTXTextEngine::TXInitialize(void)
{
	OSErr	err;
	
	if (!sTXInited)
		{
		#ifdef	INCLUDE_TX_TSM
			err = CTSMTextension::TSMTextensionStart();
			ThrowIfOSErr_(err);
		#else
			err = CTextension::TextensionStart();
			ThrowIfOSErr_(err);
		#endif
		
		CTXStyleSet *textRun = new CTXStyleSet();

		textRun->Reference();
		CTextension::RegisterRun(textRun);
		}	
		
	SetDefaultRun_(NULL);
	sTXInited = true;
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ


CTXTextEngine::CTXTextEngine()
	:	LTextEngine()
{
	GrafPtr				port = UQDGlobals::GetQDGlobals()->thePort;
	
	mChanges = NULL;
	mText = NULL;
	
	try
		{	
		TXInitialize();
	
		#ifdef	INCLUDE_TX_TSM
			mText = new CTSMTextension;
			((CTSMTextension *)mText)->ITSMTextension(port);
		#else
			mText = new CTextension;
			mText->ITextension(port);
		#endif

		mText->Activate(false, false);
		}
	catch (ExceptionCode inErr)
		{
		delete mText;
		throw inErr;	
		}
	
	mFlags = recalc_FormatAllText;
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

CTXTextEngine::~CTXTextEngine(void)
{
	mText->Free();
	delete mText;
	if ( mChanges )
		delete mChanges;
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

void CTXTextEngine::SetDefaultRun_(CRunObject *inObj)
{
	CTextension::SetDefaultRun(inObj);
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

GrafPtr	CTXTextEngine::GetPort(void)
{
	return mText->GetTextPort();
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

void CTXTextEngine::SetPort(GrafPtr inPort)
{
	mText->SetTextPort(inPort);
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

void CTXTextEngine::SetWrapMode(Uint16 inMode)
{
	mWrappingMode = inMode;
	
	// FIX ME!!! need to force a reformat at this point
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

Uint16 CTXTextEngine::GetWrapMode(void) const
{
	return mWrappingMode;
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

void CTXTextEngine::SetPageFrameSize(const SDimension32& inSize)
{
	CPageGeometry* theGeometry = mText->GetPageGeometry();
	theGeometry->SetPageFrameDimensions(inSize);
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

void CTXTextEngine::SetAttributes(Uint16 inAttributes)
{
//	textAttr_MultiStyle, textAttr_Editable, textAttr_Selectable, textAttr_WordWrap

	Uint16	oldAttributes = GetAttributes();
	
	inherited::SetAttributes(inAttributes);

	//	word wrap
	{
		Boolean			crOn = (inAttributes & textAttr_WordWrap) == 0;
		CDisplayChanges	changes;

		if (crOn != mText->GetCRFlag()) {
			mText->SetCRFlag(crOn, &changes);
			RefreshTX(changes);
		}
	}
	
	//	fixed height
	{
		CFormatter	*formatter = mText->GetFormatter();
		
		ThrowIfNULL_(formatter);
		
		short	lineHeight,
				lineAscent;
		
		formatter->GetFixedLineHeight(&lineHeight, &lineAscent);
		
		Boolean			fhOn = (lineHeight != 0);
		
		if (inAttributes & textAttr_FixedHeight)
			{
			short		height;
			FontInfo	info;

			Focus();	//	handlerview may not be set yet so...

			//	The operations below will mess with the quickdraw state info
			//	because there is no support for getting font metric information
			//	directly.
			//	
			//	We must preserve the quickdraw state info or we could even
			//	change the the quickdraw state info for the system!			
			StColorPortState	saveState(UQDGlobals::GetCurrentPort());

			CTXStyleSet	*defaultStyle = (CTXStyleSet *) GetDefaultStyleSet();	//	unsafe cast
			Assert_(defaultStyle != NULL);  // if you fail here it's because you probably didnt
											// set the default style set yet.
			defaultStyle->SetRunEnv();
			
			GetFontInfo(&info);
			height = info.ascent + info.descent + info.leading;
			
			if ((lineHeight != height) || (lineAscent != info.ascent))
				{
				formatter->SetFixedLineHeight(height, info.ascent);
				Refresh(recalc_FormatAllText);
				}
			}
		else
			{
			if (fhOn)
				{
				formatter->SetFixedLineHeight();
				Refresh(recalc_FormatAllText);
				}
			}
	}
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

void	CTXTextEngine::SetTextMargins(const Rect &inMargins)
{
	CPageGeometry* theGeometry = mText->GetPageGeometry();
	CDisplayChanges	changes;

	inherited::SetTextMargins(inMargins);
		
	theGeometry->SetPageMargins(mMargins, &changes);
	RefreshTX(changes);
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
// NOTE THAT THESE ARE IN PAGE SPACE (which only == image space when the scale is 1/1)

void	CTXTextEngine::GetImageSize(SDimension32* outSize) const
{
	CPageGeometry* theGeometry = mText->GetPageGeometry();
	theGeometry->GetActualPageFrameDimensions(*outSize);
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
// NOTE THAT THESE ARE IN PAGE SPACE (which only == image space when the scale is 1/1)

void	CTXTextEngine::SetImageSize(const SDimension32& inSize)
/*
	InSize.v = 0 --> automatically adjusted.
*/
{
	SDimension32	oldSize;
	
	GetImageSize(&oldSize);
	if ((oldSize.width == inSize.width) && (oldSize.height == inSize.height)) 
		return;
		
	if ((inSize.height == 0) && (oldSize.width == inSize.width))
		return;

	SDimension32	txSize;
	CPageGeometry* theGeometry = mText->GetPageGeometry();

	txSize.width = inSize.width;
	txSize.height = (inSize.height == 0) ? 0 : inSize.height;
	
	//	text image size
	{
		CDisplayChanges	changes;
		theGeometry->SetPageFrameDimensions(txSize, &changes);
		RefreshTX(changes);
	}
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
// This is called by GetRangeScrollVector
void	CTXTextEngine::GetViewRect(
	SPoint32		*outOrigin,
	SDimension32	*outSize) const
{
// FIX ME!!! there's really no need for this to get called
//Assert_(false);

	if (outOrigin)
		mHandlerView->GetScrollPosition(*outOrigin);
	
	if (outSize)
		{
		Rect r;
		mHandlerView->CalcLocalFrameRect(r);
		outSize->height = r.bottom - r.top;
		outSize->width = r.right - r.left;
		}
}


// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
void	CTXTextEngine::RefreshTX(CDisplayChanges &inChanges)
{
	TOffsetPair	pair;
	
	try
		{
		if (!mChanges)
			mChanges = new CDisplayChanges;

		mChanges->SetChangesKind(inChanges.GetChangesKind());
		inChanges.GetFormatRange(&pair);
		mChanges->SetFormatRange(pair);
		}
	catch (ExceptionCode inErr)
		{
		// just in case we couldn't allocate
		}
	
	Refresh(recalc_DisplayChanges);
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

void CTXTextEngine::RecalcSelf(void)
{
	OSErr	err;
	
	if (mFlags & recalc_FormatAllText)
		{
		err = mText->Format();
		mFlags &= ~recalc_FormatAllText;
		ThrowIfOSErr_(err);
		}
	
	if (mFlags & recalc_DisplayChanges)
		{

		ThrowIfNULL_(mChanges);
		short	actions = mText->DisplayChanged(*mChanges);
		delete mChanges;
		mChanges = NULL;
		
		if (actions & kRedrawAll)
			BroadcastMessage(msg_InvalidateImage);
		
		if (actions & kCheckSize)
			{
			//	CheckImageSize();
			//	done in inherited method
			}
		
		if (actions & (~(kRedrawAll | kCheckSize)))
			{
			Assert_(false);		// what?
			}

		mFlags &= ~recalc_DisplayChanges;
		}
	
	LTextEngine::RecalcSelf();
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

void	CTXTextEngine::DrawArea(
	const SPoint32		&inLocation,
	const SDimension32	&inSize)
{
	Rect	r;
	Point	where;
	
	inherited::DrawArea(inLocation, inSize);
	
	if (mHandlerView->CalcLocalFrameRect(r))
		EraseRect(&r);
		
	mHandlerView->ImageToLocalPoint(inLocation, where);
	r.top = where.v;
	r.left = where.h;
	r.right = inSize.width + r.left;
	r.bottom = inSize.height + r.top;

	mText->Draw(mHandlerView, r, kNoDrawFlags);
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

const TextRangeT& CTXTextEngine::GetTotalRange(void) const
{
	CTXTextEngine* me = (CTXTextEngine *)this;
	
	me->mTotalRange.SetStart(0);
	me->mTotalRange.SetEnd(me->mText->CountCharsBytes());
	
	return inherited::GetTotalRange();
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

//	===========================================================================
//	Text get/set

/*
	Note:
	
		This method is MAYBE only suitable for plain text.  Consider using
		one of the tube methods above unless really needing plain text
		from a non tube source.
*/

void	CTXTextEngine::TextGetThruHandle(const TextRangeT &inRange, Handle outTextH)
{
	TextRangeT	range = inRange;
	
	range.Crop(GetTotalRange());

	Assert_(outTextH);
	SetHandleSize(outTextH, range.Length());
	
	void	*p = mText->GetCharPtr(range.Start());
	ThrowIfNULL_(p);
	BlockMoveData(p, *outTextH, range.Length());
}


// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

/*
	Note:
	
		This method is MAYBE only suitable for plain text.  Consider using
		one of the tube methods above unless really needing plain text
		from a non tube source.
*/

void	CTXTextEngine::TextReplaceByPtr(
	const TextRangeT	&inRange,
	const Ptr			inSrcText,
	Int32				inSrcLength)
{
	TextRangeT			range = inRange;
	Int32				start, end;
	TTextDescriptor		txtDescr((const unsigned char*)inSrcText, (long)inSrcLength);
	TReplaceParams		parms(txtDescr);
	OSErr				err;
	
	range.Crop(GetTotalRange());
	start = range.Start();
	end = range.End();

	if (inSrcLength)
		{
		long	bogusLength;
		const	CRunObject	*run = mText->GetNextRun(range.Start(), &bogusLength);
	
		if (run)
			{
			parms.fReplaceObj = (CRunObject *)run;
			}
		else
			{
			ThrowIfNULL_(mDefaultStyleSet);
			parms.fReplaceObj = (CTXStyleSet *)mDefaultStyleSet;
			}
			
		ThrowIfNULL_(parms.fReplaceObj);
		parms.fTakeObjCopy = false;
		parms.fCalcScriptRuns = false;
		}
	
	SetTextChanged();
	Focus();

	{
		StRecalculator	changeMe(this);	//	950508
	
		TextFluxPre(range);
		{
			StEmptyVisRgn	hideDrawing(UQDGlobals::GetCurrentPort());

			TOffsetPair theLineFormatPair;
			err = mText->ReplaceRange(start, end, parms, false, &theLineFormatPair);

			// the replaced caused a reformat that we need to know about.  The display used to be
			// updated out of textension::EndEdit, but we dont do that anymore.
			// FIX ME!!! this is probably a general reformat problem that needs to be dealt with
			// the the textension object.  when we remove the Is-A relationship between the textension
			// object and merge it into this one, we can notify better of reformat change.

			// Perhaps we could unify textension::EndEdit, Refresh, and RefreshTX

			if (theLineFormatPair.firstOffset != theLineFormatPair.lastOffset)
				{
				CFormatter	*formatter = mText->GetFormatter();
				TOffsetRange sRange, eRange;
				TextRangeT startRange, endRange;
				
				formatter->GetLineRange(theLineFormatPair.firstOffset, &sRange);
				formatter->GetLineRange(theLineFormatPair.lastOffset, &eRange);
				TXRange2TextRange(sRange, &startRange);
				TXRange2TextRange(eRange, &endRange);
				
				TextRangeT theLineRange = startRange;
				theLineRange.Union(endRange);
				Refresh(theLineRange);
				}
		}

// This used to reformat everything on any replace (boooooooo!)
//		err = mText->Format();
	
		TextFluxPost(inSrcLength - range.Length());
		
		ThrowIfOSErr_(err);
	}
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

const Handle CTXTextEngine::GetTextHandle(void) const
{
	CArray* theArray = mText->GetArray();
	ThrowIfNULL_(theArray);

	return theArray->GetDataHandle();
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

void CTXTextEngine::SetTextHandle(Handle inHandle)
{
	SetTextChanged();

	//	First delete all the text and let Textension clean up itself.
	TextDelete(sTextAll);
	
	{
		StRecalculator	changeMe(this);

		//	Now set the text...
		TextRangeT	preRange,
					postRange;
		
		preRange = GetTotalRange();
	
		CArray	*array = mText->GetArray();
		ThrowIfNULL_(array);
	
		SetStyleSet(sTextAll, GetDefaultStyleSet());
	
		//	clean the rulersets
		//	???
		
		//	swap the data handle
		TextFluxPre(preRange);
		array->SetDataHandle(inHandle);
		TextFluxPost(GetTotalRange().Length() - preRange.Length(), false);
		Refresh(recalc_FormatAllText);
		
		SetStyleSet(sTextAll, GetDefaultStyleSet());

		//	apply default rulerset
		//	???
	}
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

//	===========================================================================
//	Presentation query

Int32 CTXTextEngine::GetRangeTop(const TextRangeT &inRange) const
{
	CPageGeometry* theGeometry = mText->GetPageGeometry();
	CFormatter	*formatter = mText->GetFormatter();
	TextRangeT	range = inRange,
				totalRange;
				
	totalRange = GetTotalRange();
	range.Crop(totalRange);
	range.Front();
	Int32		offset = range.Start();
	Int32		lineNo = formatter->OffsetToLine(offset);

	SPoint32	Pt;
	
// FIX ME!!! this will need to be an image point instead of a page point
// when we implement scaling
	theGeometry->LineToPagePoint(mHandlerView, lineNo, Pt);

	return Pt.v;
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

Int32 CTXTextEngine::GetRangeHeight(const TextRangeT &inRange) const
{
	Int32			rval = 0;
	CPageGeometry* theGeometry = mText->GetPageGeometry();
	CFormatter		*formatter = mText->GetFormatter();
	CLineHeights	*frameFormatter = mText->GetLineHeights();
	TextRangeT		range,
					totalRange;
	totalRange = GetTotalRange();
	
	//	first line
	range = inRange;
	range.Crop(totalRange);
	range.Front();
	Int32		startLine = formatter->OffsetToLine(range.Start());
	
	//	end line
	range = inRange;
	range.Crop(totalRange);
	Int32	lastChar;
	if (range.Length()) {
		range.Back();
		lastChar = range.End() - PrevCharSize(range.End());
	} else {
		lastChar = range.Start();
	}
	Int32	lastLine = formatter->OffsetToLine(lastChar);
	
	rval = frameFormatter->GetLineRangeHeight(startLine, lastLine);
	
	return rval;
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

Boolean	CTXTextEngine::Range2Image(
	const TextRangeT	&inRange,
	Boolean				inLeadingEdge,
	SPoint32			*outWhere) const
/*
	If inRange is an insertion point, inLeadingEdge is ignored.
*/
{
	TextRangeT	range = inRange;
	
	range.Crop(GetTotalRange());
	
	if (inLeadingEdge)
		range.Front();
	else
		range.Back();
	
//	Assert_(mFlags == 0);
	
	TOffset	offset = range.Start();
	CFormatter			*formatter = mText->GetFormatter();
	long				lineNo = formatter->OffsetToLine(offset);
	TOffsetRange		lineRange;
	formatter->GetLineRange(lineNo, &lineRange);

	Boolean				doEOL =	(range.Insertion() == rangeCode_Before) &&
								(range.Start() == lineRange.Start()) &&
								inRange.Length();

	if (doEOL && lineNo)
		lineNo--;	
	Assert_(lineNo >= 0);
	
	CPageGeometry* theGeometry = mText->GetPageGeometry();
	CTextRenderer	*display = mText->GetDisplayHandler();
	CLineHeights		*frameFormatter = mText->GetLineHeights();
	SPoint32			linePoint;
	
// FIX ME!!! this will need to be an image point instead of a page point
// when we implement scaling
	theGeometry->LineToPagePoint(mHandlerView, lineNo, linePoint);
	
	CLine				*line = display->DoLineLayout(lineNo);

	
	linePoint.v += frameFormatter->GetLineRangeHeight(lineNo, lineNo);
	if (doEOL)
		{
		linePoint.h = mImageSize.width - mMargins.right;
		}
	else
		{
		offset = range.Start();
		linePoint.h += line->CharacterToPixel(offset);
		}

	ThrowIfNULL_(outWhere);
	outWhere->h = linePoint.h;
	outWhere->v = linePoint.v;

	return true;
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

Boolean	CTXTextEngine::Image2Range(
	SPoint32			inWhere,
	Boolean				*outLeadingEdge,
	TextRangeT			*outRange) const
{
	TextRangeT	rangeFound,
				testRange;
	Boolean		leadingEdge;

	//	Get initial rangeFound
	//	independent from mHandlerView...

	TOffsetRange		tRange;

	CTextRenderer	*display = mText->GetDisplayHandler();
	CPageGeometry*		 theGeometry = mText->GetPageGeometry();
	CFormatter			*formatter = mText->GetFormatter();
	CLineHeights		*frameFormatter = mText->GetLineHeights();

	SPoint32			where;
	where.h = inWhere.h;
	where.v = inWhere.v;

	//	¥	Get lineNo
	//	equiv of Display::PointToLine...
//	long				frameNo = frames->PointToFrame(where, &outsideFrames);

	TLongRect thePageText;
	theGeometry->GetPageTextBounds(thePageText);
	Boolean		outsideFrames = !thePageText.IsPointInside(inWhere);
// FIX ME!!! use of inWhere in the above line assumes a zoom scale of 1/1

	Boolean outsideLines = outsideFrames;

	Int32				lineNo;
	TOffsetPair			frameLines;
	//	equiv of CFrame::PointToLine
	if (!frameFormatter->GetTotalLineRange(frameLines))
		{
		lineNo = -1;
		outsideLines = true;
		}
	else
		{
		where.h = inWhere.h;
		where.v = inWhere.v - mMargins.top; //	note correction for margin.

		lineNo = frameFormatter->PixelToLine(&where.v);	// changes where!
		
		if (lineNo > frameLines.lastOffset)
			{
			outsideLines = true;
			lineNo = frameLines.lastOffset;
			}
		}

	//	remainder of CTextensionDisplay::PointToChar...
	if (lineNo < 0)
		{
		tRange.Set(0, 0, true, false);
		} 
	else
		{
		SPoint32			linePoint;
		theGeometry->LineToPagePoint(mHandlerView, lineNo, linePoint);

		where.h = inWhere.h;
		where.v = inWhere.v;
 		
		if (	(lineNo == formatter->CountLines()-1) &&
				where.v > linePoint.v + frameFormatter->GetLineRangeHeight(lineNo, lineNo)
		)	{
				tRange.rangeStart.Set(formatter->GetLineEnd(lineNo), true);
				tRange.rangeEnd = tRange.rangeStart;
			}
		else if (	(lineNo == 0) && (where.v < linePoint.v) )
			{
			tRange.rangeStart.Set(0, false);
			tRange.rangeEnd = tRange.rangeStart;
			}
		else
			{
			CLine	*line = display->DoLineLayout(lineNo);
			line->PixelToCharacter(LongToFixed(where.h - linePoint.h), &tRange);
			}
		}
		
	TXRange2TextRange(tRange, &rangeFound);
	
	if (outsideFrames || outsideLines)
		{
		SPoint32	anchor;
		Boolean		b = Range2Image(rangeFound, true, &anchor);
		
		Assert_(b);
		
		if (inWhere.h <= anchor.h)
			{
			leadingEdge = false;
			rangeFound.SetInsertion(rangeCode_After);
			}
		else
			{
			leadingEdge = true;
			rangeFound.SetInsertion(rangeCode_Before);
			}
		}
	else
		{
		rangeFound.SetLength(ThisCharSize(rangeFound.Start()));		
		
		//	Code from here is paralleled in LTETextEngine...
		SPoint32	anchor,	//	Lower left of found insertion position
					boat;	//	lower left of "next" insertion position

		Range2Image(rangeFound, true, &anchor);
		
		//	normal case
		leadingEdge = where.h >= anchor.h;
		
		if (leadingEdge)
			{
			testRange = rangeFound;
			}
		else
			{
			Int16	len = PrevCharSize(rangeFound.Start());
			
			testRange.SetStart(rangeFound.Start() - len);
			testRange.SetLength(len);
			}
			
		if (testRange.Length())
			{
			Range2Image(testRange, !leadingEdge, &boat);
			if (boat.v == anchor.v)
				rangeFound = testRange;
			}
		}

	//	return results
	rangeFound.Crop(GetTotalRange());

	if (outLeadingEdge)
		*outLeadingEdge = leadingEdge;

	if (outRange)
		*outRange = rangeFound;
		
	return true;
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

Boolean	CTXTextEngine::SpaceForBytes(Int32 inAdditionalBytes) const
{
	return true;
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

//	===========================================================================
//	Parts implementation:

DescType	CTXTextEngine::FindPara(
	const TextRangeT	&inQueryRange,
	TextRangeT			*ioRange,
	Int32				*outEndOffset) const
/*
	Should only be called by FindPart
*/
{
	//	sanity checks
	ThrowIfNULL_(ioRange);

	TOffsetRange	trange;
	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
	mText->CharToParagraph(range.Start(), &trange);
	TXRange2TextRange(trange, &rangeFound);
	
	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	CTXTextEngine::FindLine(
	const TextRangeT	&inQueryRange,
	TextRangeT			*ioRange,
	Int32				*outEndOffset) const
/*
	Should only be called by FindPart
*/
{
	//	sanity checks
	ThrowIfNULL_(ioRange);

	Int32			line;
	TOffsetRange	trange;
	TextRangeT		range = *ioRange,
					rangeFound,
					rangeReported;
	
	//	Initial validity check
	if (!inQueryRange.Contains(range))
		return typeNull;

	Int32	offset = range.Start();
	if ((range.Insertion() == rangeCode_Before) && (offset > 0))
		offset--;	//	Even if it isn't a whole character it will be good enough for CharToLine
	
	//	Find
	line = mText->CharToLine(offset, &trange);
	if (line < 0)
		return typeNull;
	TXRange2TextRange(trange, &rangeFound);
	
	//	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 cLine;
	else
		return cChar;
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

DescType	CTXTextEngine::FindWord(
	const TextRangeT	&inQueryRange,
	TextRangeT			*ioRange,
	Int32				*outEndOffset) const
{
	//	sanity checks
	ThrowIfNULL_(ioRange);

	TOffsetRange	trange;
	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
	mText->CharToWord(ioRange->Start(), &trange);
	TXRange2TextRange(trange, &rangeFound);
	
	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 cChar;
	
	return cWord;
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

Int32	CTXTextEngine::CountLines(const TextRangeT &inQueryRange) const
{
	CFormatter	*formatter = mText->GetFormatter();
	ThrowIfNULL_(formatter);
	
	TOffsetRange	qRange;
	
	Int32			startLine,
					endLine;
	
	TextRange2TXRange(inQueryRange, &qRange);
	startLine = formatter->OffsetToLine(qRange.rangeStart);
	endLine = formatter->OffsetToLine(qRange.rangeEnd);
	
	return endLine - startLine + 1;
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

DescType	CTXTextEngine::FindNthLine(
	const TextRangeT	&inQueryRange,
	Int32				inPartIndex,
	TextRangeT			*outRange) const
{
	CFormatter	*formatter = mText->GetFormatter();
	ThrowIfNULL_(formatter);
	
	TOffsetRange	qRange,
					oRange;
	TextRangeT		rangeFound,
					rangeReported;
	Int32			originLine;
	
	TextRange2TXRange(inQueryRange, &qRange);
	
	originLine = formatter->OffsetToLine(qRange.rangeStart);
	
	originLine += inPartIndex -1;
	
	formatter->GetLineRange(originLine, &oRange);
	TXRange2TextRange(oRange, &rangeFound);
	
	rangeFound.Crop(inQueryRange);

	ThrowIfNULL_(outRange);
	*outRange = rangeFound;
	
	if (*outRange == rangeFound)
		return cLine;
	else
		return cChar;
}

// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	¥	
// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

DescType	CTXTextEngine::FindLineSubRangeIndices(
	const TextRangeT	&inQueryRange,
	const TextRangeT	&inSubRange,
	Int32				*outIndex1,
	Int32				*outIndex2) const
{
	CFormatter	*formatter = mText->GetFormatter();
	ThrowIfNULL_(formatter);
	
	TOffsetRange	tRange,
					sRange,
					eRange;
	TextRangeT		resultRange,
					startRange,
					endRange;
	Int32			originLine,
					startLine,
					endLine;
	
	TextRange2TXRange(inQueryRange, &tRange);
	
	originLine = formatter->OffsetToLine(tRange.rangeStart);
	
	TextRange2TXRange(inSubRange, &tRange);
	startLine = formatter->OffsetToLine(tRange.rangeStart);
	endLine = formatter->OffsetToLine(tRange.rangeEnd);
	
	formatter->GetLineRange(startLine, &sRange);
	formatter->GetLineRange(endLine, &eRange);
	
	TXRange2TextRange(sRange, &startRange);
	TXRange2TextRange(eRange, &endRange);
	
	resultRange = startRange;
	resultRange.Union(endRange);
	
	*outIndex1 = startLine - originLine + 1;
	*outIndex2 = endLine - originLine + 1;
	
	if (resultRange == inSubRange)
		return cLine;
	else
		return cChar;
}