textrendering/texthandling/stext/TXTINDEX.CPP
changeset 0 1fb32624e06b
child 40 91ef7621b7fc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/textrendering/texthandling/stext/TXTINDEX.CPP	Tue Feb 02 02:02:46 2010 +0200
@@ -0,0 +1,3099 @@
+/*
+* Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description: 
+*
+*/
+
+
+#include <e32std.h>
+#include <e32base.h>
+
+#include <gdi.h>
+#include <s32stor.h>
+#include "TXTFMLYR.H"
+#include "TXTETEXT.H"
+#include "TXTLAYDC.H"
+#include "TXTSTYLE.H"
+#include "TXTINDEX.H"
+
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
+#include "TXTFMLYR_INTERNAL.H"
+#include "TXTSTYLE_INTERNAL.H"
+#endif
+
+TGlobalLayerInfoAppend::TGlobalLayerInfoAppend()
+	: iAggParaFormatLayer(NULL),iAggCharFormatLayer(NULL),iComParaFormatLayer(NULL),iComCharFormatLayer(NULL)
+	{}
+
+TGlobalLayerInfoAppend::TGlobalLayerInfoAppend(const CParaFormatLayer* aAggParaFormatLayer,const CCharFormatLayer* aAggCharFormatLayer,
+							const CParaFormatLayer* aComParaFormatLayer,const CCharFormatLayer* aComCharFormatLayer)
+	: iAggParaFormatLayer(aAggParaFormatLayer),iAggCharFormatLayer(aAggCharFormatLayer),
+	  iComParaFormatLayer(aComParaFormatLayer),iComCharFormatLayer(aComCharFormatLayer)
+	{}
+
+
+TTextFragment::TTextFragment():
+	iLength(0),
+	iPhraseCount(0)
+	{
+	}
+
+
+TCurrentIndexRecords::TCurrentIndexRecords()
+	{
+	}
+
+
+DLLEXPORT_C void CRichTextIndex::__DbgTestInvariant()const
+// Provides class invariants.  Explanations below:
+//
+	{
+#ifdef _DEBUG
+// ASSERT: Every phrase index is consistent with its corresponding paragraph.
+	TInt zeroLengthPhraseCount=0;
+	TInt maxPara=iParaIx->Count();
+	TInt numberOfReferencesToSharedList=0;
+	TInt currentPhraseElement=0;
+	for (TInt para=0;para<maxPara;para++)
+		{
+		// ASSERT: The basedOn link is valid.
+		CFormatLayer* thisLayer=(*iParaIx)[para].iParaAttribs->iParaFormat;
+		CFormatLayer* base=CONST_CAST(CFormatLayer*,thisLayer->SenseBase());
+		__ASSERT_DEBUG(base!=NULL,User::Invariant());
+		if ((*iParaIx)[para].iParaAttribs->iRefCount>0)
+			numberOfReferencesToSharedList++;
+		TInt paragraphLength=(*iParaIx)[para].iLength;
+		TInt sumOfPhraseLengths=0;
+		TInt maxPhrase=(*iParaIx)[para].iParaAttribs->PhraseCount();
+		for (TInt phrase=0;phrase<maxPhrase;phrase++)
+			{
+			if (maxPhrase>1)
+				{
+				const RPhraseAttribsEntry* phrase=&(*iPhraseIx)[currentPhraseElement];
+				CCharFormatLayer* charFormatLayer=phrase->CharFormat();
+				// ASSERT: The basedOn link is valid.
+				__ASSERT_DEBUG(charFormatLayer->SenseBase()!=NULL,User::Invariant());
+				__ASSERT_DEBUG(TInt(charFormatLayer->SenseBase())>0x1000,User::Invariant());
+				sumOfPhraseLengths+=(*iPhraseIx)[currentPhraseElement].Length();
+				if ((*iPhraseIx)[currentPhraseElement].Length()==0)
+					zeroLengthPhraseCount++;
+				currentPhraseElement++;
+				}
+			else
+				{
+				CCharFormatLayer* charFormatLayer=(*iParaIx)[para].iParaAttribs->iCharFormat;
+				// ASSERT: The basedOn link is valid.
+				__ASSERT_DEBUG(charFormatLayer->SenseBase()!=NULL,User::Invariant());
+				sumOfPhraseLengths+=(*iParaIx)[para].iLength;
+				}
+			}
+		__ASSERT_DEBUG(sumOfPhraseLengths==paragraphLength,User::Invariant());
+		}
+// ASSERT: We have no unexpected phrases left over
+	__ASSERT_DEBUG(currentPhraseElement==-1 ||
+					currentPhraseElement==iPhraseIx->Count(),User::Invariant());
+// ASSERT: There is either zero(0) or one(1) zero length phrase in the whole index
+	__ASSERT_DEBUG( (zeroLengthPhraseCount==0) ||
+					(zeroLengthPhraseCount==1 && iPendingNewPhrasePos!=EInsertCharFormatReset),
+					User::Invariant());
+// ASSERT: the number of paraEntries with paraAttribs of refCount>0 == the sum of refCounts in the shared list.
+//			or is only one less - as is set when SetInsertCharFormat is called on a shared paraAttribs.
+	TInt totalReferenceCount=0;
+	if (!iSharedParaQueHead.IsEmpty())
+		{
+		CParaAttribs* currentSharedPara;
+		TDblQueIter<CParaAttribs> iterator(((CRichTextIndex*)this)->iSharedParaQueHead);
+		while ((currentSharedPara=iterator++)!=NULL)
+			totalReferenceCount+=currentSharedPara->iRefCount;
+		}
+	__ASSERT_DEBUG((numberOfReferencesToSharedList==totalReferenceCount) ||
+				   (numberOfReferencesToSharedList==totalReferenceCount-1),User::Invariant());
+// ASSERT: iPictureCount corresponds to the number of pictures in the stored in the index.
+	TInt picCount=0;
+	TInt phraseCount=(iPhraseIx) ? iPhraseIx->Count() : 0;
+	for (TInt item=0;item<phraseCount;item++)
+		picCount+=((*iPhraseIx)[item].IsPicturePhrase())
+					? 1
+					: 0;
+	__ASSERT_DEBUG(iPictureCount==picCount,User::Invariant());
+#endif
+	}
+
+
+CRichTextIndex* CRichTextIndex::NewL(const CParaFormatLayer* aGlobalParaLayer,const CCharFormatLayer* aGlobalCharLayer,
+									 const CRichText& aText,TInt aParaGran,TInt aPhraseGran)
+// Return a handle to a new instance of this class.
+// Requires the global format layer handles on which to base the first content.
+//
+	{
+	CRichTextIndex* self=new(ELeave) CRichTextIndex(aText);
+	CleanupStack::PushL(self);
+	self->ConstructL(aGlobalParaLayer,aGlobalCharLayer,aParaGran,aPhraseGran);
+	CleanupStack::Pop();
+	return self;
+	}
+
+
+CRichTextIndex::CRichTextIndex(const CRichText& aText):
+	iText(aText),
+	iPendingNewPhrasePos(EInsertCharFormatReset),
+	iSharedParaQueHead(_FOFF(CParaAttribs,link))
+	{
+	}
+
+
+void CRichTextIndex::ConstructL(const CParaFormatLayer* aGlobalParaLayer,const CCharFormatLayer* aGlobalCharLayer,TInt aParaGran,TInt aPhraseGran)
+// Provides a fully initialised rich text index.
+// Upon construction, the index contains a single paragraph of length 1 (para.terminator).
+// This first paragraph initially has constant character formatting, ie the shared para list
+// must have one item in it.
+//
+	{
+	CParaFormatLayer* paraLayer=CParaFormatLayer::NewL();  // Creates empty layer.
+	paraLayer->SetBase(aGlobalParaLayer);  // Sets basedOn to global default.
+	CleanupStack::PushL(paraLayer);
+	CCharFormatLayer* charLayer=CCharFormatLayer::NewL();  // Creates empty layer.
+	charLayer->SetBase(aGlobalCharLayer);  // Sets basedOn to global default.
+	CleanupStack::PushL(charLayer);
+	CParaAttribs* paraAttribs=GetParaAttribsL(paraLayer,charLayer);
+	CleanupStack::PopAndDestroy(2);
+	CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,paraAttribs));
+	iParaIx=new(ELeave) CArrayFixSeg<TParaAttribsEntry>(aParaGran);
+	TParaAttribsEntry para(1,paraAttribs);
+	iParaIx->AppendL(para);
+	CleanupStack::Pop();
+	iPhraseIx=new(ELeave) CArrayFixSeg<RPhraseAttribsEntry>(aPhraseGran);
+
+	__TEST_INVARIANT;
+	}
+
+
+CRichTextIndex::~CRichTextIndex()
+// Free up all storage allocated in the rich text index.
+//
+	{
+	TInt count;
+	if (iPhraseIx)
+		{// Destroy the phrase index and its contents.
+		__ASSERT_ALWAYS(iParaIx,Panic(EPhraseIxPresentWithNoParaIx));
+		count=iPhraseIx->Count();
+		for (TInt offset=0;offset<count;offset++)
+			(*iPhraseIx)[offset].Discard();
+		}
+	delete iPhraseIx;
+	if (iParaIx)
+		{// Destroy the para index and its contents.
+		count=iParaIx->Count();
+		for (TInt offset=0;offset<count;offset++)
+			{
+			CParaAttribs* pA=(*iParaIx)[offset].iParaAttribs;
+			if (!pA->IsShared())
+				pA->Release();
+			}
+		}
+	delete iParaIx;
+	RebalanceIndex();
+	//
+	// Clear the shared list.  (Will usually be empty by now
+	// unless internalize failed after getting shared list & before getting all para data.
+	CParaAttribs* currentSharedPara;
+	TDblQueIter<CParaAttribs> iterator(iSharedParaQueHead);
+	while ((currentSharedPara=iterator++)!=NULL)
+		currentSharedPara->Release(currentSharedPara->iRefCount);
+	__ASSERT_ALWAYS(iSharedParaQueHead.IsEmpty(),Panic(ERichTextIndexIntegrityErr));
+	}
+
+
+TInt CRichTextIndex::CharPosOfParagraph(TInt& aLength,TInt aParaOffset)const
+// Returns the character position of the first character of paragraph aParaOffset,
+// where aParaOffset specifies the nth paragraph.
+// The length of this nth paragraph is written to aLength.
+//
+// If aParaOffset specifies a paragraph that does not exist, EScanEndOfData is returned.
+//
+	{
+	__TEST_INVARIANT;
+
+	if (aParaOffset>=iParaIx->Count())
+		return CPlainText::EScanEndOfData;
+	TInt pos=0,offset=0;
+	for (offset=0;offset<aParaOffset;offset++)
+		pos+=(*iParaIx)[offset].iLength;
+	aLength=(*iParaIx)[offset].iLength;
+	return pos;
+	}
+
+
+TInt CRichTextIndex::ParagraphNumberForPos(TInt& aPos)const
+// Returns the paragraph offset for the specified character position aPos.
+// aPos is in turn modified to hold the character position of the first character
+// of this paragraph.  If aPos is already on a paragraph boundary then do nothing.
+//
+	{
+	__TEST_INVARIANT;
+
+	((CRichTextIndex*)this)->ScanToPosition(aPos,EScanToPositionAbsolute);
+	aPos-=iPos.iParaElementOffset;
+	return iPos.iParaElement;
+	}
+
+
+void CRichTextIndex::DocumentChanged()const
+	{
+ 	MUTABLE_CAST(TLogicalPosition&,iLastUsed).Clear();
+	}
+
+
+void CRichTextIndex::DoSoloInsertL(TInt aPos,TInt aLength)
+// Updates the index following the insertion of content into a single phrase.
+// First find the phrase record the governs the insert pos.
+// (1) If the current phrase is a text phrase, then simply extend the length of the phrase record.
+// (2) If the current phrase is a picture phrase then this cannot be extended.
+// There is 1 pathological case here:
+// (i) The [current] picture phrase is the 1st phrase in the paragraph, and the insert pos
+// is before this phrase. (paragraph insert pos == 0).
+// In this case we must insert a new text phrase *before* the picture phrase.
+// (ii) In normal circumstances the inserted text is after the picture phrase.  So, if the
+// following phrase is of the same character format as the picture then we can re-use this phrase.
+// If it is not of the same character format (or is also a picture phrase),
+// then we must insert a new text phrase of the correct format immediately following the current picture phrase.
+//
+	{
+	RebalanceIndex();
+	if (!((iPendingNewPhrasePos == EInsertCharFormatReset) || (aPos == iPendingNewPhrasePos)))
+		CancelInsertCharFormat();
+
+	ScanToPosition(aPos,EScanToPositionMatchLeft);
+	TCurrentIndexRecords current; GetCurrentRecords(current);
+	if (current.iPhrase && current.iPhrase->IsPicturePhrase())
+		{// Paragraph has specific char format, and current phrase is picture phrase.
+		TInt newPhraseRequired=EFalse;
+		CCharFormatLayer* charLayer=current.iPhrase->CharFormat();
+		if (FirstPhraseOfParagraph() && iPos.iPhraseElementOffset==0)
+			newPhraseRequired=ETrue;  // Text is inserted at the start of the para behind the picture.
+		else
+			{// Check for re-use of the next text phrase.
+			iPos.iPhraseElement++;
+			iPos.iPhraseElementOffset=0;
+			const RPhraseAttribsEntry& nextPhrase=(*iPhraseIx)[iPos.iPhraseElement];
+			if (nextPhrase.IsPicturePhrase() || !nextPhrase.CharFormat()->IsIdentical(charLayer,EFalse))
+				{// Need a new phrase if the formats don't match OR the next phrase is a picture phrase.
+				newPhraseRequired=ETrue;  // Need to create a new phrase to take this insert.
+				}
+			}
+		if (newPhraseRequired)
+			{// Insert new phrase & record this fact
+			CCharFormatLayer* charFormat=CCharFormatLayer::NewCopyBaseL(charLayer);
+			RPhraseAttribsEntry newPhrase(charFormat);
+			CleanupStack::PushL(charFormat);
+			iPhraseIx->InsertL(iPos.iPhraseElement,newPhrase);
+			CleanupStack::Pop();
+			current.iParaAttribs->iPhraseCount++;
+			}
+		GetCurrentRecords(current);  // Update current records, cos used below.
+		}
+	// Extend the lengths in the index.
+	current.iParaEntry->iLength+=aLength;  // Increase length of paragraph.
+	if (current.iPhrase)
+		current.iPhrase->AdjustLength(aLength);  // Increase the length of this phrase.
+	iPos.iPhraseElementOffset+=aLength;
+	iPos.iParaElementOffset+=aLength;
+	iPos.iDocPos+=aLength;
+	}
+
+
+void CRichTextIndex::InsertL(TInt aPos,const TDesC& aBuf,const CParaFormatLayer& aGlobalParaFormatLayer)
+// Updates the index following the insertion of a descriptor of text into the document.
+// Correctly handles embedded paragraph delimiters.
+//
+	{
+	__TEST_INVARIANT;
+	TInt bufLen=aBuf.Length();
+
+	RebalanceIndex();
+	TInt paragraphCount=0;
+	const TText* start=aBuf.Ptr();
+	const TText* end=start+bufLen;
+	while (start<end)
+		{
+		if (*start++==CEditableText::EParagraphDelimiter)
+			paragraphCount++;
+		}
+	if (paragraphCount==0)
+		{
+		if (bufLen > 0)
+			{
+			DoSoloInsertL(aPos,bufLen);
+			CancelInsertCharFormat();
+			}
+		__TEST_INVARIANT;
+		return;
+		}
+	ScanToPosition(aPos,EScanToPositionMatchLeft);
+	TLogicalPosition pastePos=iPos;  // Used to adjust paragraph/phrase lengths later
+
+	if (paragraphCount>1)
+		{
+		TCurrentIndexRecords current;
+		GetCurrentRecords(current);
+		const CParaAttribs& paraAttribs=*current.iParaAttribs;
+		CCharFormatLayer* charLayer=(paraAttribs.IsShared())
+								? paraAttribs.iCharFormat
+								: (*iPhraseIx)[iPos.iPhraseElement].CharFormat();
+		CParaFormatLayer* paraLayer=paraAttribs.iParaFormat;
+		CParaAttribs* theParaAttribs=GetParaAttribsL(paraLayer,charLayer);
+		CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,theParaAttribs));
+		iParaIx->InsertL(pastePos.iParaElement,TParaAttribsEntry(0,theParaAttribs),paragraphCount-1);
+		CleanupStack::Pop();
+		theParaAttribs->iRefCount+=paragraphCount-2;	// add the extra references
+		}
+	TLogicalPosition pos;
+	TRAPD(ret,
+	SplitParagraphAtPastePosL(pastePos,pos,aGlobalParaFormatLayer));
+		if (ret!=KErrNone)
+			{
+			RbRemoveInsertedParaAttribsEntries(pastePos.iParaElement,paragraphCount-1);
+			User::Leave(ret);
+			}
+
+	if (paragraphCount>1)
+		{	// swap the entries for the split and first inserted paragraph
+		TParaAttribsEntry& paraEntry=(*iParaIx)[pastePos.iParaElement+paragraphCount-1];
+		TParaAttribsEntry& insertEntry=(*iParaIx)[pastePos.iParaElement];
+		TParaAttribsEntry temp=paraEntry;
+		paraEntry=insertEntry;
+		insertEntry=temp;
+		}// Cos weve inserted new paragraphs in front of the governing one.
+
+	// Sort out the front para
+	TParaAttribsEntry& frontPara=(*iParaIx)[pastePos.iParaElement];
+	TPtrC buf(aBuf);
+	TInt lengthOfFirstPara=ParaLengthFromBuffer(buf);
+	frontPara.iLength+=lengthOfFirstPara;  // Adjust the para length
+	buf.Set(aBuf.Right(aBuf.Length()-lengthOfFirstPara-1));
+	if (!frontPara.iParaAttribs->IsShared() && lengthOfFirstPara != 0)
+		{
+		RPhraseAttribsEntry* phrase=&(*iPhraseIx)[pastePos.iPhraseElement];
+		if (phrase->IsPicturePhrase())
+			{	// move insertion past any picture phrase
+			pastePos.iPhraseElement++;
+			pastePos.iPhraseElementOffset=0;
+			phrase=&(*iPhraseIx)[pastePos.iPhraseElement];
+			}
+		phrase->AdjustLength(lengthOfFirstPara);  // Adjust the phrase length
+		}
+	for (TInt paraItem=1;paraItem<paragraphCount;paraItem++)
+		{// For each para inserted between the fist and last
+		TParaAttribsEntry& para=(*iParaIx)[pastePos.iParaElement+paraItem];
+		TInt length=ParaLengthFromBuffer(buf)+1;
+		__ASSERT_DEBUG(length!=KErrNotFound,Panic(EInsertEmbeddedParaErr));
+		para.iLength=length;
+		buf.Set(buf.Right(buf.Length()-length));
+		}
+	// For final paragrph
+	TInt trailingTextLen=buf.Length();
+	if (trailingTextLen>0)
+		{
+		TParaAttribsEntry& backPara=(*iParaIx)[pos.iParaElement];
+		backPara.iLength+=trailingTextLen;
+		if (!backPara.iParaAttribs->IsShared())
+			{
+			RPhraseAttribsEntry& phrase=(*iPhraseIx)[pos.iPhraseElement];
+			phrase.AdjustLength(trailingTextLen);  // Adjust phrase length
+			}
+		}
+	//
+	// Now tidy up
+	if (bufLen>1 && iPendingNewPhrasePos!=EInsertCharFormatReset)
+		{
+		iPendingNewPhrasePos=aPos+(bufLen-trailingTextLen);
+		CancelInsertCharFormat();
+		}
+
+	__TEST_INVARIANT;
+	}
+
+
+void CRichTextIndex::SplitParagraphAtPastePosL(TLogicalPosition& aPastePos,TLogicalPosition& aNewPos,
+												const CParaFormatLayer& aGlobalParaFormatLayer)
+// Breaks the paragraph specified by the logical position aPastePos, inserting a paragraph delimiter.
+//
+	{
+	TInt insertPendingPos = iPendingNewPhrasePos;
+	DoSoloInsertL(aPastePos.iDocPos,1);
+	TBool insertCharFormatDeleted = EFalse;
+	if (InsertCharFormatIsActive())
+		{
+		insertCharFormatDeleted = DeleteInsertCharFormat();
+		iPendingNewPhrasePos = EInsertCharFormatReset;
+		}
+	TRAPD(ret,
+	InsertParagraphL(aPastePos.iDocPos+1,aGlobalParaFormatLayer));  // Split the current para (and maybe phrase index).
+	if (ret!=KErrNone)
+		{
+		// locate the character inserted by DoSoloInsertL() above
+		ScanToPosition(aPastePos.iDocPos,EScanToPositionAbsolute);
+		TCurrentIndexRecords current;
+		GetCurrentRecords(current);
+		current.iParaEntry->iLength--;
+		if (current.iPhrase)
+			{
+			current.iPhrase->AdjustLength(-1);  // collapse phrase by right amount
+			if (insertPendingPos!=EInsertCharFormatReset)
+				{
+				__ASSERT_DEBUG(current.iPhrase->Length()==0,User::Invariant());
+				iPendingNewPhrasePos=insertPendingPos;
+				}
+			else if (current.iPhrase->Length()==0)
+				{
+				RemoveFromPhraseIx(iPos.iPhraseElement,1);
+				current.iParaAttribs->iPhraseCount--;
+				__ASSERT_DEBUG(current.iParaAttribs->PhraseCount()>1,User::Invariant());
+				}
+			}
+		User::Leave(ret);
+		}
+	if (insertPendingPos != EInsertCharFormatReset)
+		ConsolidateAt(insertPendingPos, insertCharFormatDeleted?
+			EPositionOnly : EFollowingPhrase);
+	ScanToPosition(aPastePos.iDocPos+1,EScanToPositionMatchLeft);  // Gives us the next para.
+	aNewPos=iPos;
+	}
+
+
+TInt CRichTextIndex::ParaLengthFromBuffer(TDesC& aBuf)const
+// Returns the length of the first para found in the buffer.
+// The returned length excludes the paragraph delimiter character.
+// Returns KNotFound if there is no paragraph delimiter.
+//
+	{return aBuf.Locate(CEditableText::EParagraphDelimiter);}
+
+
+void CRichTextIndex::InsertL(TInt aPos,const TPictureHeader& aPicHdr, TBool& aPictureOwnershipTaken)
+// Updates the index following the insertion of a picture header object into the text
+// component.  This is accomplished by creating & inserting a picture phrase at the
+// relevant place.
+//
+	{
+	__TEST_INVARIANT;
+
+	RebalanceIndex();
+// ASSERT: A valid picture header, referencing a valid picture has been inserted.
+	__ASSERT_ALWAYS(aPicHdr.iPicture.IsPtr() && aPicHdr.iPicture.AsPtr()!=NULL,Panic(EInsertNullPicHdrData));
+// ASSERT: The current insert pos hasn't been changed without cancelling SetInsertCharFormat.
+	__ASSERT_ALWAYS((iPendingNewPhrasePos==EInsertCharFormatReset) || (aPos==iPendingNewPhrasePos),
+					Panic(ESetInsertCharFormatIntegrityErr));
+	if (iPendingNewPhrasePos!=EInsertCharFormatReset)
+		CancelInsertCharFormat();  // Cancel this state before inserting picture. Rebalances the index.
+	ScanToPosition(aPos,EScanToPositionMatchLeft);
+	TCurrentIndexRecords current; GetCurrentRecords(current);
+	TCharFormatX format;
+	TCharFormatXMask mask;  //...and build up its format.
+	CCharFormatLayer* baseChar;
+	GetPhraseFormat(current,format,mask,baseChar);  //...inherit format from prev. phrase.
+	// Create the picture phrase. Takes ownership of the aPicHdr.iPicture
+	CPicturePhrase* picture=CPicturePhrase::NewL(aPicHdr,format,mask,baseChar,aPictureOwnershipTaken);
+	CleanupStack::PushL(picture);
+	//New reclaimed CParaAttribs instance
+	CParaAttribs* reclaimed=RequestReclaimShareL(current.iParaAttribs,current.iParaEntry);
+    //Store the old CParaAttribs instance in rollbackParaAttribsHandle
+	CParaAttribs* rollbackParaAttribsHandle=current.iParaAttribs;
+	if (reclaimed)
+		{
+		CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,reclaimed));
+		current.iParaEntry->iParaAttribs=reclaimed;  // Use this reclaimed para attribs (the new CParaAttribs instance)
+		}
+	GetCurrentRecords(current);
+//	ASSERT: The reclaim succeeded.  We must always end up with a PhraseIx-not constant char format.
+	__ASSERT_DEBUG(current.iPhrase!=NULL,Panic(EReclaimShareError));
+	TRAPD(ret1,
+	SplitPhraseL(aPos));  // Phrase may not be split if at boundary.
+
+    if (ret1!=KErrNone)
+        {
+        RbInsertPicture(rollbackParaAttribsHandle);//Restore the old CParaAttribs instance
+        User::Leave(ret1);
+        }
+
+	TInt offset=(PhraseSplit())?1:0;  // Insert position of new phrase relative to current.
+	RPhraseAttribsEntry newPhrase(picture);
+	TRAPD(ret2,
+	iPhraseIx->InsertL(iPos.iPhraseElement+offset,newPhrase));
+    if (ret2!=KErrNone)
+        {
+        RbInsertPicture(rollbackParaAttribsHandle);//Restore the old CParaAttribs instance
+        User::Leave(ret2);
+        }
+
+	if(reclaimed)
+	    {
+        CleanupStack::Pop();//"Pop" for CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,reclaimed));
+	    }
+	CleanupStack::Pop(picture);
+	// Update counts etc. - cannot leave now.
+	current.iParaEntry->iLength+=RPhraseAttribsEntry::EPicturePhraseLength;
+	current.iParaAttribs->iPhraseCount++;  // for the picture phrase
+	iPictureCount++;
+
+	// Commit
+	if(reclaimed)
+	    {
+        rollbackParaAttribsHandle->Release();  // Release hold on original shared paraAttribs.
+	    }
+
+	__TEST_INVARIANT;
+	//coverity[memory_leak]
+	}
+
+void CRichTextIndex::RbInsertPicture(CParaAttribs* aGoodParaAttribs)
+// Reinstate the original good paraAttribs.
+// Then rollback the SplitPhrase() call if it succeeded.
+//
+	{
+	(*iParaIx)[iPos.iParaElement].iParaAttribs=aGoodParaAttribs;
+	if (PhraseSplit())
+		{// Rollback the SplitPhrase()
+		TInt length=(*iPhraseIx)[iPos.iPhraseElement+1].Length();
+		RemoveFromPhraseIx(iPos.iPhraseElement+1);
+		(*iPhraseIx)[iPos.iPhraseElement].AdjustLength(length);
+		}
+	}
+
+
+// Insert a new paragraph immediately following character position aPos, fixing the length of the preceeding
+// paragraph.  The new paragraph preserves any explicit paragraph/character formatting, and is based on the
+// global layers.  (Do not need to rebalance the index here; a previous call to DoSoloInsertL accomplishes this)
+
+void CRichTextIndex::InsertParagraphL(TInt aPos,const CParaFormatLayer& aGlobalParaFormatLayer)
+	{
+	ScanToPosition(aPos,EScanToPositionMatchLeft);
+	TCurrentIndexRecords current;
+	GetCurrentRecords(current);
+	TParaAttribsEntry newPara;
+	CCharFormatLayer* charLayer;
+  	if (current.iPhrase)  // entry in phrase index
+		charLayer=current.iPhrase->CharFormat();
+	else
+		charLayer=current.iParaAttribs->iCharFormat;
+	//
+	// New para format layer, based on normal, inheriting specific format
+	CParaFormatLayer* currentParaFormat=current.iParaAttribs->iParaFormat;
+	CParaFormatLayer* newParaLayer=CParaFormatLayer::NewL(currentParaFormat);
+	const CParaFormatLayer& currentStyle=STATIC_CAST(const CParaFormatLayer&,*currentParaFormat->SenseBase());
+	const TUid currentStyleType=currentStyle.Type();
+
+	// !!
+	// Only change to Normal if current style is a built-in one
+	// or we are not at the end of a heading style.
+	TBool useNormal;
+	if (currentStyleType==KNormalParagraphStyleUid)
+		useNormal=ETrue;
+	else if (currentStyleType==KUserDefinedParagraphStyleUid)
+		useNormal=EFalse;
+	else if (iPos.iParaElementOffset<=(current.iParaEntry->iLength-2))  // cos of previous call to DoSoloInsertL()
+		useNormal=EFalse;
+	else
+		useNormal=ETrue;
+	newParaLayer->SetBase((useNormal)
+		? &aGlobalParaFormatLayer
+		: &currentStyle);
+	const CCharFormatLayer* newCharBase=(useNormal)
+		? iText.GlobalCharFormatLayer()
+		: STATIC_CAST(const CParagraphStyle&,currentStyle).CharFormatLayer();
+	CleanupStack::PushL(newParaLayer);
+	//
+	if (current.iParaAttribs->IsShared())
+		{// Current para has constant char format - so the new one also has constant char format
+		// New char format layer, based on normal, inheriting specific format
+		CCharFormatLayer* newCharLayer=CCharFormatLayer::NewL(charLayer);
+		newCharLayer->SetBase(newCharBase);
+		CleanupStack::PushL(newCharLayer);
+		newPara.iParaAttribs=GetParaAttribsL(newParaLayer,newCharLayer);
+		CleanupStack::PopAndDestroy(2);  // newCharLayer/newParaLayer
+		CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,newPara.iParaAttribs));
+		iParaIx->InsertL(iPos.iParaElement+1,newPara);
+		CleanupStack::Pop();  // paraAttribs cleanup item
+		GetCurrentRecords(current);		// could be changed by InsertL() above
+		}
+	else  // Do the split myself since this para has specific character formatting.
+		{// Make the new CParaAttribs
+		CParaAttribs* newParaAttribs=CParaAttribs::NewL(newParaLayer);
+		CleanupStack::PopAndDestroy();  // newParaLayer - copy owned by newParaAttribs
+		CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,newParaAttribs));
+		//
+		// Split current phrase & insert if necessary.
+		// Split even when we are at a phrase boundary -> this introduces an z.l.p. for the insertion point
+		RPhraseAttribsEntry& insertPhrase=iPhraseIx->At(iPos.iPhraseElement);
+		TInt insertPendingPos=(iPos.iPhraseElementOffset==insertPhrase.Length()) ? aPos : EInsertCharFormatReset;
+		DoSplitPhraseL(insertPhrase,iPos.iPhraseElementOffset,current.iParaAttribs);  // Ups iPhraseCount
+		//
+		// Insert the new paragraph.
+		newPara.iParaAttribs=newParaAttribs;
+		TRAPD(ret,
+		iParaIx->InsertL(iPos.iParaElement+1,newPara));  // Inserts the new paraAttribsEntry record.
+			if (ret!=KErrNone)
+				{
+				RemoveFromPhraseIx(iPos.iPhraseElement+1,1);	// inserted by DoSplitPhraseL
+				current.iParaAttribs->iPhraseCount--;
+				User::Leave(ret);
+				}
+
+		iPendingNewPhrasePos=insertPendingPos;
+		CleanupStack::Pop();			// newParaAttribs. All OK now
+		GetCurrentRecords(current);		// could be changed by InsertL() above
+		//
+		// Calculate new paraAttribs phrase counts.
+		TInt remainder=(iPos.iPhraseElement+1)-iPos.iParaBasePhraseElement;
+		TInt newPhraseCount=current.iParaAttribs->iPhraseCount-remainder;
+		newParaAttribs->iPhraseCount=newPhraseCount;
+		current.iParaAttribs->iPhraseCount=remainder;
+
+		const CArrayFix<RPhraseAttribsEntry>& phraseIx=*iPhraseIx;
+		TInt startPhrase=iPos.iPhraseElement+1;
+		for (TInt ii=startPhrase; ii<startPhrase+newPhraseCount; ii++)
+			{
+			RPhraseAttribsEntry phrase=phraseIx[ii];
+			phrase.CharFormat()->SetBase(newCharBase);
+			}
+		//
+		// The index now reflects the correct state.
+		// Next, the efficiency thing - see if the new paras can share existing ones.
+		if (newPhraseCount==1)
+			Share(iParaIx->At(iPos.iParaElement+1),iPos.iParaBasePhraseElement+remainder);
+		if (remainder==1)
+			Share(iParaIx->At(iPos.iParaElement),iPos.iParaBasePhraseElement);
+		}
+	// Alter the length of the original paragraph and the new paragraph.
+	TInt currentLength=current.iParaEntry->iLength;
+	current.iParaEntry->iLength=iPos.iParaElementOffset;
+	((*iParaIx)[iPos.iParaElement+1]).iLength+=currentLength-current.iParaEntry->iLength;  // Alters the length of the copy of aNewPara.
+	}
+
+
+void CRichTextIndex::SetForDeleteL(TIndexDeleteInfo& aInfo,TInt aPos,TInt aLength)
+//
+	{
+	__TEST_INVARIANT;  // Do not need to RebalanceIndex(); part of defined behaviour for delete.
+
+	aInfo.iDeleteLength=aLength;
+	//
+	// Check for simple cases first
+	DocumentChanged();  // clears internal position record.
+	ScanToPosition(aPos,EScanToPositionAbsolute,&iLastUsed);
+	TCurrentIndexRecords current;
+	GetCurrentRecords(current);
+	aInfo.iStartPara=iPos.iParaElement;
+	aInfo.iEndPara=iPos.iParaElement;  // default
+	aInfo.iDeletePos=iPos;  // default
+	//
+	TInt startParaLength=current.iParaEntry->iLength;
+	TInt lengthRemainingInPara=startParaLength-iPos.iParaElementOffset;
+	if (aLength<lengthRemainingInPara)
+		{// Case is delete-from-paragraph
+		aInfo.iDeleteType=TIndexDeleteInfo::EDeleteFromParagraph;
+		return;
+		}
+	//
+	ScanToPosition(aPos+aLength,EScanToPositionMatchLeft,&iLastUsed);  // Forces endPara to be next para when just removing a para delimiter.
+	aInfo.iEndPara=iPos.iParaElement;
+//	if (iPos.iParaElementOffset==0)
+//		{// Can use delete-paragraph
+//		aInfo.iDeleteType=TIndexDeleteInfo::EDeleteParagraph;
+//		return;
+//		}
+	//
+	// Set for the general (leaving) delete.
+	GetCurrentRecords(current);
+	CParaAttribs* reclaimedEndPara=RequestReclaimShareL(current.iParaAttribs,current.iParaEntry); // does not release share.
+	TParaAttribsEntry* origEndParaEntry=current.iParaEntry;
+	CParaAttribs* origParaAttribs=current.iParaAttribs;
+	TInt endPosPhrase=iPos.iPhraseElement;
+	if (reclaimedEndPara)
+		origEndParaEntry->iParaAttribs=reclaimedEndPara;
+	// Get start para info.
+	ScanToPosition(aPos,EScanToPositionAbsolute);
+	GetCurrentRecords(current);
+	CParaAttribs* reclaimedStartPara=NULL;
+	TRAPD(ret,
+	reclaimedStartPara=RequestReclaimShareL(current.iParaAttribs,current.iParaEntry));
+		if (ret!=KErrNone)
+			{
+			if (reclaimedEndPara)
+				{
+				reclaimedEndPara->Release();
+				RemoveFromPhraseIx(endPosPhrase);
+				origEndParaEntry->iParaAttribs=origParaAttribs;
+				}
+			User::Leave(ret);
+			}
+	if (reclaimedEndPara)
+		origParaAttribs->Release();  // Release share on the original end para attribs
+	if (reclaimedStartPara)
+		{// Use the specific start  para
+		current.iParaAttribs->Release();
+		current.iParaEntry->iParaAttribs=reclaimedStartPara;
+		ScanToPosition(aPos,EScanToPositionAbsolute);  // Pick up reclaimed phrase.
+		}
+	aInfo.iDeletePos=iPos;  // internal position of aPos after any reclaim
+	// Note: iDeleteType can surely be made obsolete now? TPB 7/11/2000
+	aInfo.iDeleteType=TIndexDeleteInfo::EDeleteFromParagraph;
+	
+	/*
+	 * Pointer to memory allocated to 'reclaimedEndPara' is assigned to 
+	 * 'origEndParaEntry->iParaAttribs' on line 706. The memory will be 
+	 * released in CRichTextIndex's destructor.
+	 */ 
+	// coverity[memory_leak]
+	}
+
+
+TBool CRichTextIndex::DeleteParagraph(TInt aPos,TInt aLength)
+// Remove aCount entire paragraphs from the text.
+// Leave-safe
+// Returns EFalse indicating that no paragraphs were merged together,
+// as a result of the delete action.
+// Does NOT preserve any zero-length/insert pending state.
+//
+	{
+	__TEST_INVARIANT;  // Do not need to RebalanceIndex(); part of defined behaviour for delete.
+
+	CancelInsertCharFormat();
+	ScanToPosition(aPos,EScanToPositionAbsolute,&iLastUsed);
+
+	__ASSERT_DEBUG(iPos.iParaElementOffset==0,Panic(EDeleteParagraphInvalidStartValue));
+
+	TIndexDeleteInfo info;
+	info.iDeleteType=TIndexDeleteInfo::EDeleteParagraph;
+	info.iDeletePos=iPos;
+	info.iStartPara=iPos.iParaElement;
+	//
+	TInt documentLength=iText.DocumentLength();
+	TInt pos=(aPos+aLength>documentLength
+		? documentLength
+		: aPos+aLength);
+	ScanToPosition(pos,EScanToPositionMatchLeft,&iLastUsed);  // Forces endPara to be next para when just removing a para delimiter.
+
+	info.iEndPara=iPos.iParaElement;
+	info.iDeleteLength=aLength;
+
+	DeleteNow(info);
+	// do not want to call TidyAfterDelete()
+
+	return EFalse;
+	}
+
+
+
+void CRichTextIndex::DeleteFromParagraph(TInt aPos,TInt aLength)
+// Special case delete for removing content from within a single paragraph only.
+// Not to be used for deleting an entire paragraph or paragraphs.
+// Returns EFalse indicating that no paragraphs were merged together,
+// as a result of the delete action.
+//
+	{
+	__TEST_INVARIANT;  // Do not need to RebalanceIndex(); part of defined behaviour for delete.
+
+	ScanToPosition(aPos,EScanToPositionAbsolute);
+
+#ifdef _DEBUG
+	{
+	TCurrentIndexRecords current;
+	GetCurrentRecords(current);
+	TInt startParaLength=current.iParaEntry->iLength;
+	TInt lengthRemainingInPara=startParaLength-iPos.iParaElementOffset;
+
+	__ASSERT_ALWAYS(aLength<lengthRemainingInPara,Panic(EDeleteFromParagraphInvalidRange));
+	}
+#endif
+
+	TIndexDeleteInfo info;
+	info.iDeleteLength=aLength;
+	info.iStartPara=iPos.iParaElement;
+	info.iEndPara=iPos.iParaElement;
+	info.iDeletePos=iPos;
+	info.iDeleteType=TIndexDeleteInfo::EDeleteFromParagraph;
+
+	DoDeleteFromParagraph(info);
+
+	__TEST_INVARIANT;
+	}
+
+
+TBool CRichTextIndex::DoDeleteFromParagraph(const TIndexDeleteInfo& aInfo)
+// Delete content from *within* the boundary of a single paragraph only.0
+// Returns EFalse indicating that no paragraphs were merged together,
+// as a result of the delete action.
+//
+	{
+	iPos=aInfo.iDeletePos;
+	TInt length=aInfo.iDeleteLength;
+	DeleteParagraphText(length);
+	TidyAfterDelete(aInfo);
+
+	return EFalse;
+	}
+
+
+TBool CRichTextIndex::DeleteNow(TIndexDeleteInfo& aInfo)
+// Deletes index data corresponding the info argument.
+// Returns ETrue is 2 paragraphs are merged as a result of the delete, otherwise returns false.
+//
+	{
+	iPos=aInfo.iDeletePos;
+	TCurrentIndexRecords current;
+	GetCurrentRecords(current);
+	TInt leftToDelete=aInfo.iDeleteLength;
+	TInt charsLeftInPara=(current.iParaEntry->iLength)-(iPos.iParaElementOffset);
+	TBool doParaMerge=((iPos.iPhraseElement>0 || iPos.iPhraseElementOffset>0) && leftToDelete>=charsLeftInPara);
+	// ETrue if the 1st para has content remaining but no paragraph delimiter.
+	//
+	TBool firstParaRemoved=(FirstPhraseOfParagraph() && iPos.iPhraseElementOffset==0 && aInfo.iDeleteLength>=current.iParaEntry->iLength);
+	// ETrue if the 1st para has been *wholly* deleted.
+	//
+	DeleteParagraphText(leftToDelete);  // Delete range will be in a minimum of 1 paragraph.
+	if (aInfo.iStartPara<aInfo.iEndPara)
+		{// The delete range crosses paragraph boundaries.
+		for (TInt currentPara=aInfo.iStartPara+1;currentPara<=aInfo.iEndPara;currentPara++)
+			{
+			ScanToPosition(aInfo.iDeletePos.iDocPos,EScanToPositionAbsolute);
+			DeleteParagraphText(leftToDelete);
+			}
+		}
+	// Now tidy up
+	if (doParaMerge && !firstParaRemoved)
+		{// Merge the 2 paras together.
+		TParaAttribsEntry* paraEntry=&(*iParaIx)[aInfo.iStartPara];
+		TParaAttribsEntry* paraEntryFollowing=&(*iParaIx)[aInfo.iStartPara+1];
+		paraEntryFollowing->iLength+=paraEntry->iLength;  // Extend length of remaining para.
+		paraEntryFollowing->iParaAttribs->iPhraseCount+=paraEntry->iParaAttribs->iPhraseCount;  // Extend phrase count
+		paraEntry->iParaAttribs->Release();
+		iParaIx->Delete(aInfo.iStartPara);
+		}
+	if (aInfo.iDeleteType!=TIndexDeleteInfo::EDeleteParagraph)
+		TidyAfterDelete(aInfo);
+
+	__TEST_INVARIANT;
+	return doParaMerge;
+	}
+
+
+void CRichTextIndex::TidyAfterDelete(const TIndexDeleteInfo& aInfo)
+//
+//
+	{
+	MergePhrases(aInfo.iDeletePos.iDocPos);  // Alters internal position record.
+	TCurrentIndexRecords current;
+	GetCurrentRecords(current);  // So must get records again.
+	if (!current.iParaAttribs->IsShared())
+		{// May be able to reclaim a share from this *specific* record
+		CParaAttribs* sharedParaAttribs=RequestShare(iPos);
+		if (sharedParaAttribs!=NULL && current.iParaAttribs!=sharedParaAttribs)
+			{// Use this shared record
+			current.iParaAttribs->Release();
+			RemoveFromPhraseIx(iPos.iPhraseElement);
+			current.iParaEntry->iParaAttribs=sharedParaAttribs;
+			}
+		}
+	}
+
+
+void CRichTextIndex::Normalize(TInt aPos)
+//
+	{
+	ScanToPosition(aPos,EScanToPositionAbsolute);
+	NormalizeNow(iPos);
+
+	__TEST_INVARIANT;
+	}
+
+
+void CRichTextIndex::NormalizeNow(const TLogicalPosition& aNormalizePos)
+//
+	{
+	CParaAttribs* currentParaAttribs=(*iParaIx)[aNormalizePos.iParaElement].iParaAttribs;
+	if (!currentParaAttribs->IsShared())
+		{
+		CParaAttribs* sharedParaAttribs=RequestShare(iPos);
+		if (sharedParaAttribs!=NULL && currentParaAttribs!=sharedParaAttribs)
+			{// We must have been given a share on something already in the shared list.  Dump current stuff.
+			currentParaAttribs->Release();
+			(*iParaIx)[aNormalizePos.iParaElement].iParaAttribs=sharedParaAttribs;
+			RemoveFromPhraseIx(aNormalizePos.iParaBasePhraseElement);
+			}
+		}
+	}
+
+
+CParaAttribs* CRichTextIndex::ReserveCellLC()
+// Returns a handle to a newly created CParaAttribs object.  This may be used
+// during a call to GetParaAttribsL() as a pre-allocated cell, thus ensuring
+// that the call cannot possibly leave.
+// ASSUMES: that the internal position record has been set correctly.
+//
+	{
+	CParaAttribs* reservedCell=ReserveCellL();
+	CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,reservedCell));
+	return reservedCell;
+	}
+
+
+CParaAttribs* CRichTextIndex::ReserveCellL()
+// Returns a handle to a newly created CParaAttribs object.  This may be used
+// during a call to GetParaAttribsL() as a pre-allocated cell, thus ensuring
+// that the call cannot possibly leave.
+// ASSUMES: that the internal position record has been set correctly.
+//
+	{
+	TCurrentIndexRecords current;
+	GetCurrentRecords(current);
+	const CParaAttribs& paraAttribs=*current.iParaAttribs;
+	CParaFormatLayer* paraLayer=paraAttribs.iParaFormat;
+	CCharFormatLayer* charLayer=(paraAttribs.IsShared())
+								? paraAttribs.iCharFormat
+								: (*iPhraseIx)[iPos.iPhraseElement].CharFormat();
+	CParaAttribs* reservedCell=CParaAttribs::NewL(paraLayer,charLayer);
+	return reservedCell;
+	}
+
+
+TBool CRichTextIndex::DeleteParagraphText(TInt& aLength)
+// Called once for each paragraph that's included in the delete range.
+// Assumes the internal position record has already been set correctly.
+// The delete range may cover: (1) The entire paragraph - so just destroy the paragraph,
+// (2i) At least a portion of a single phrase, and possibly
+// (2ii) 0..n whole contiguous phrases, and possibly
+// (2iii) a trailing partial phrase.
+// Returns ETrue if the full paragraph is deleted, otherwise returns EFalase.
+//
+	{
+	TCurrentIndexRecords current; GetCurrentRecords(current);
+	if (FirstPhraseOfParagraph() && iPos.iPhraseElementOffset==0 && aLength>=current.iParaEntry->iLength)
+		{// The entire paragraph needs to be deleted.
+		aLength-=current.iParaEntry->iLength;
+		if (!current.iParaAttribs->IsShared())
+			RemoveFromPhraseIx(iPos.iParaBasePhraseElement,current.iParaAttribs->iPhraseCount);
+		current.iParaAttribs->Release();
+		iParaIx->Delete(iPos.iParaElement);
+		return ETrue;
+		}
+	TInt deleteFromPhrase=CurrentPhraseLength()-iPos.iPhraseElementOffset;
+	TInt maxPhrase=current.iParaAttribs->PhraseCount();
+	TInt currentPhrase=(iPos.iPhraseElement-iPos.iParaBasePhraseElement);
+	while (currentPhrase<maxPhrase)
+		{
+		TInt deletable=Min(deleteFromPhrase,aLength);
+		current.iParaEntry->iLength-=deletable;  // Adjust the paragraph length.
+		if (current.iPhrase && deletable>=CurrentPhraseLength())
+			{// Remove the now empty phrase from the phrase index.
+			RemoveFromPhraseIx(iPos.iPhraseElement);
+			current.iParaAttribs->iPhraseCount--;
+			}
+		else if (current.iPhrase)
+			{// Adjust phrase length and move onto the next phrase.
+			current.iPhrase->AdjustLength(-deletable);
+			iPos.iPhraseElement++;
+			iPos.iPhraseElementOffset=0;
+			}
+		currentPhrase++;
+		aLength-=deletable;
+		if(aLength==0)
+			break;  // Nothing left to delete in this paragraph.
+		// Get the data for the next phrase.
+		GetCurrentRecords(current);
+		deleteFromPhrase=CurrentPhraseLength();
+		}
+	return EFalse;
+	}
+
+TBool CRichTextIndex::InsertCharFormatIsActive()
+	{
+	return iPendingNewPhrasePos != EInsertCharFormatReset;
+	}
+
+/** Sets an *InsertPending* state, where format has been inserted into the
+text, but no content has yet been inserted. This *state* is cancelled by cursor
+movement etc. Split the current phrase at aPos (if necessary) and insert a zero
+length phrase, ready to accept the pending content of the specified format.
+*/
+void CRichTextIndex::SetInsertCharFormatL(const TCharFormatX& aFormat,const TCharFormatXMask& aMask,TInt aPos)
+	{
+	__ASSERT_ALWAYS(!InsertCharFormatIsActive() || aPos==iPendingNewPhrasePos,
+					Panic(ESetInsertCharFormatIntegrityErr));
+	if (InsertCharFormatIsActive())
+		UpdateInsertCharFormatL(aFormat, aMask);
+	else
+		NewInsertCharFormatL(aFormat, aMask, aPos);
+	}
+
+void CRichTextIndex::NewInsertCharFormatL(const TCharFormatX& aFormat,
+	const TCharFormatXMask& aMask, TInt aPos)
+	{
+	__ASSERT_ALWAYS(!InsertCharFormatIsActive(),
+		Panic(ESetInsertCharFormatIntegrityErr));
+	ScanToPosition(aPos,EScanToPositionMatchLeft);
+	TCurrentIndexRecords current;
+	GetCurrentRecords(current);
+	CCharFormatLayer* basedOn;
+	TCharFormatX applyFormat=aFormat;
+	TCharFormatXMask applyMask=aMask;
+	GetPhraseFormat(current,applyFormat,applyMask,basedOn);  // Inherit phrase attributes to the left, over what is present.
+	TBool origParaAttribsShared=current.iParaAttribs->IsShared();
+	if (origParaAttribsShared)
+		{// Current paraAttribs is shared.
+		iRollbackParaAttribsHandle=current.iParaAttribs;
+		current.iParaEntry->iParaAttribs=RequestReclaimShareL(current.iParaAttribs,current.iParaEntry);  // Does not release share.
+		ScanToPosition(aPos,EScanToPositionMatchLeft);  // Pick up reclaimed phrase.
+		}  // Now current.iParaAttribs has specific character formatting - guaranteed.
+	GetCurrentRecords(current);
+	TRAPD(ret, DoNewInsertCharFormatL(applyFormat, applyMask,
+		basedOn, current.iParaAttribs));
+	if (ret!=KErrNone)
+		{// Rollback as if this function call never happened.
+		if (origParaAttribsShared)
+			{// Revert back to sharing the original.
+			current.iParaAttribs->Release();
+			RemoveFromPhraseIx(iPos.iPhraseElement);
+			current.iParaEntry->iParaAttribs=iRollbackParaAttribsHandle;
+			}
+		else
+			{// Restore the original phrase index.
+			if (PhraseSplit())
+				MergePhrases(aPos);
+			}
+		User::LeaveNoMemory();
+		}
+	iPendingNewPhrasePos=aPos;
+	}
+
+void CRichTextIndex::UpdateInsertCharFormatL(const TCharFormatX& aFormat,
+	const TCharFormatXMask& aMask)
+	{
+	CCharFormatLayer* currentLayer = GetCurrentInsertCharFormat();
+	CCharFormatLayer* newLayer = CCharFormatLayer::NewCopyBaseL(currentLayer);
+	CleanupStack::PushL(newLayer);
+	newLayer->SetL(aFormat, aMask);
+	currentLayer->Swap(*newLayer);
+	CleanupStack::PopAndDestroy(newLayer);
+	}
+
+CCharFormatLayer* CRichTextIndex::GetCurrentInsertCharFormat()
+	{
+	__ASSERT_DEBUG(InsertCharFormatIsActive(),
+		Panic(ESetInsertCharFormatIntegrityErr));
+	ScanToPosition(iPendingNewPhrasePos,EScanToPositionMatchLeft);
+	TCurrentIndexRecords current;
+	GetCurrentRecords(current);
+	__ASSERT_DEBUG((*iPhraseIx)[iPos.iPhraseElement].Length() == 0,
+		Panic(ESetInsertCharFormatIntegrityErr));
+	return (*iPhraseIx)[iPos.iPhraseElement].CharFormat();
+	}
+
+void CRichTextIndex::DoNewInsertCharFormatL(const TCharFormatX& aFormat,const TCharFormatXMask& aMask,
+											CCharFormatLayer* aBasedOn,CParaAttribs* aParaAttribs)
+	{
+	SplitPhraseL(iPos.iPhraseElement,iPos.iPhraseElementOffset,aParaAttribs);
+	CCharFormatLayer* layer=CCharFormatLayer::NewL();
+	layer->SetBase(aBasedOn);  // must be done before the SetL().
+	CleanupStack::PushL(layer);
+	layer->SetL(aFormat,aMask);
+	RPhraseAttribsEntry pendingNewPhrase(layer);
+	TInt pendingNewPhraseElement=(FirstPhraseOfParagraph() && iPos.iPhraseElementOffset==0)
+		?iPos.iParaBasePhraseElement:iPos.iPhraseElement+1;
+	iPhraseIx->InsertL(pendingNewPhraseElement,pendingNewPhrase);
+	CleanupStack::Pop();
+	aParaAttribs->iPhraseCount++;
+	}
+
+
+void CRichTextIndex::RebalanceIndex()
+// Returns the index to a good state, by releasing the extra share taken on the paraAttribs
+//
+	{
+	if (iRollbackParaAttribsHandle)
+		{
+		// ASSERT: The specified para attribs is indeed in the share list.
+		__ASSERT_ALWAYS(iRollbackParaAttribsHandle->IsShared(),Panic(EParaAttribsNotInSharedList));
+		iRollbackParaAttribsHandle->Release();
+		iRollbackParaAttribsHandle=NULL;
+		}
+	}
+
+/** Cancels the transitory state where a specified character format is applied
+on top of any inherited formatting. eg, when bold is on. Cancel when: (1) the
+text position is altered. (2) the first character (or picture) has been
+inserted following the setting of this state. If a zero length phrase is
+removed OR has content entered into it, the newly abutting phrases are checked
+to see if they can be merged. Then a request share of this para is issued.
+*/
+void CRichTextIndex::CancelInsertCharFormat()
+	{
+	if (InsertCharFormatIsActive())
+		{
+		TBool isDeleted = DeleteInsertCharFormat();
+		ConsolidateAt(iPendingNewPhrasePos, isDeleted?
+			EPositionOnly : EFollowingPhrase);
+		iPendingNewPhrasePos = EInsertCharFormatReset;
+		}
+	}
+
+/** Attempts to delete a zero-length phrase at the insert character format
+position. Does not delete any phrase of non-zero length.
+@pre The insert character format must be active
+@return ETrue if a zero-length phrase was deleted.
+*/
+TBool CRichTextIndex::DeleteInsertCharFormat()
+	{
+	__ASSERT_DEBUG(InsertCharFormatIsActive(), User::Invariant());
+	ScanToPosition(iPendingNewPhrasePos,EScanToPositionMatchLeft);
+	TCurrentIndexRecords current;
+	GetCurrentRecords(current);
+	if (current.iPhrase && current.iPhrase->Length() == 0)
+		{
+		RemoveFromPhraseIx(iPos.iPhraseElement);
+		current.iParaAttribs->iPhraseCount--;  // Para has 1 less phrase in it now.
+		return ETrue;
+		}
+	return EFalse;
+	}
+
+/** Attempts to merge phrases and share paragraphs.
+@param aPosition
+	Phrase boundary here is merged if possible, paragraph here is shared if
+	possible.
+@param aPositionOrPhrase
+	If EPositionOnly the phrases either side of aPosition are considered for merging. If EFollowingPhrase,
+	the end of the phrase following aPosition is also considered.
+*/
+void CRichTextIndex::ConsolidateAt(TInt aPosition,
+	TPositionOrPhrase aPositionOrPhrase)
+	{
+	ScanToPosition(aPosition, EScanToPositionAbsolute);
+	TCurrentIndexRecords current;
+	GetCurrentRecords(current);
+	if (!current.iPhrase)
+		return;
+
+	TInt length = current.iPhrase->Length();
+	MergePhrases(aPosition);
+	if (aPositionOrPhrase == EFollowingPhrase)
+		{
+		ScanToPosition(aPosition, EScanToPositionAbsolute);
+		GetCurrentRecords(current);
+		if (current.iPhrase)
+			MergePhrases(aPosition + length);
+		}
+	Normalize(aPosition);
+	RebalanceIndex();
+	}
+
+TBool CRichTextIndex::DelSetInsertCharFormatL(TInt aPos,TInt aLength)
+// Delete aLength characters, commencing at, and including, aPos.
+// Adds value by the following behaviour:
+// If aPos is on a phrase boundary, then remember temporarily the phrase format.
+// This is applied to any content that is immediately inserted.
+//
+	{
+	__TEST_INVARIANT;
+
+	CancelInsertCharFormat();
+	ScanToPosition(aPos,EScanToPositionAbsolute);
+	TCurrentIndexRecords current; GetCurrentRecords(current);
+	if ((!current.iParaAttribs->IsShared()) && iPos.iPhraseElementOffset==0)
+		{// aPos is on phrase boundary so SetState.
+		TCharFormatX format;
+		TCharFormatXMask mask;
+		CCharFormatLayer* charBase;
+		GetPhraseFormat(current,format,mask,charBase);
+		SetInsertCharFormatL(format,mask,aPos);
+		}
+	TIndexDeleteInfo deleteInfo;
+	SetForDeleteL(deleteInfo,aPos,aLength);
+	TBool parasMerged=DeleteNow(deleteInfo);
+
+	__TEST_INVARIANT;
+	return parasMerged;
+	}
+
+
+void CRichTextIndex::ApplyParaFormatL(const CParaFormat* aFormat,const TParaFormatMask& aMask,TInt aPos,TInt aLength)
+// Applies the specified format attributes to the paragraphs covering character position aPos to aPos+aLength-1.
+// Preserves any attributes that are currently stored in this layer.
+// If the specified para(s) is in the shared list, a new shared para of the desired format must be created,
+// and the usage count of the original decremented.
+//
+	{
+	__TEST_INVARIANT;
+
+	TInt offset=(aLength==0)?0 :-1;
+	TInt endPara=OwningParagraph(aPos+(aLength+offset));
+	TInt paraItem=OwningParagraph(aPos);
+	TCurrentIndexRecords current; GetCurrentRecords(current);
+	CParaFormat* pf=CParaFormat::NewL(*aFormat);  // preserve the desired tablist.
+	CleanupStack::PushL(pf);
+	for (;paraItem<=endPara;paraItem++)
+		{// For each paragraph, apply the specified format.
+		TParaFormatMask applyMask=aMask;
+		CParaAttribs* currentParaAttribs=(*iParaIx)[paraItem].iParaAttribs;
+		TBool shared=currentParaAttribs->IsShared();
+		if (!shared)
+			{
+			currentParaAttribs->iParaFormat->SenseL(pf,applyMask);
+			currentParaAttribs->iParaFormat->SetL(pf,applyMask);
+			}
+		else
+			{// Must create a new shared para attribs of the specified format
+			// Make a new para format layer
+			currentParaAttribs->iParaFormat->SenseL(pf,applyMask);
+			CParaFormatLayer* newParaLayer=CParaFormatLayer::NewL(pf,applyMask);
+			newParaLayer->SetBase(currentParaAttribs->iParaFormat->SenseBase());
+			CleanupStack::PushL(newParaLayer);
+			// Make a new char format layer
+			CCharFormatLayer* newCharLayer=CCharFormatLayer::NewL(currentParaAttribs->iCharFormat);
+			newCharLayer->SetBase(currentParaAttribs->iCharFormat->SenseBase());
+			CleanupStack::PushL(newCharLayer);
+			//
+			CParaAttribs* sharedParaAttribs=GetParaAttribsL(newParaLayer,newCharLayer);
+			CleanupStack::PopAndDestroy(2);
+			if (sharedParaAttribs)
+				(*iParaIx)[paraItem].iParaAttribs=sharedParaAttribs;
+			currentParaAttribs->Release();
+			}
+		}
+	CleanupStack::PopAndDestroy();  // pf
+	__TEST_INVARIANT;
+	}
+
+
+void CRichTextIndex::ApplyParagraphStyleL(const CParagraphStyle& aStyle,TInt aPos,TInt aLength,
+										  const CCharFormatLayer* aCharStyleNormal,CParagraphStyle::TApplyParaStyleMode aMode)
+// Applies the specified paragraph style to the paragraphs covering
+// character positions aPos to aPos+aLength-1.
+// Alters the specific formatting of the covered paragraphs as specified by aMode.
+//
+	{
+	__TEST_INVARIANT;
+
+	TInt offset=(aLength==0)?0 :-1;
+	TInt endPara=OwningParagraph(aPos+(aLength+offset));
+	TInt paraItem=OwningParagraph(aPos);
+	TInt paragraphBasePhrase=iPos.iParaBasePhraseElement;
+	TCurrentIndexRecords current;
+	GetCurrentRecords(current);
+	for (;paraItem<=endPara;paraItem++)
+		{// For each paragraph, apply the specified style
+		CParaAttribs& currentParaAttribs=*(*iParaIx)[paraItem].iParaAttribs;
+		TBool shared=currentParaAttribs.IsShared();
+		TUid type=aStyle.Type();
+		if (!shared)
+			{
+			currentParaAttribs.iParaFormat->SetBase(&aStyle);
+			TInt phraseCount=currentParaAttribs.PhraseCount();
+			for (TInt phraseItem=0;phraseItem<phraseCount;phraseItem++)
+				{
+				CCharFormatLayer& charLayer=*(*iPhraseIx)[paragraphBasePhrase+phraseItem].CharFormat();
+				if (type==KNormalParagraphStyleUid)
+					charLayer.SetBase(aCharStyleNormal);
+				else
+					charLayer.SetBase(aStyle.CharFormatLayer());
+				ModifySpecificFormatting(*currentParaAttribs.iParaFormat,charLayer,aMode);
+				}
+			paragraphBasePhrase+=phraseCount;
+			}
+		else
+			{// Must create a new shared para attribs of the same format, but a different based on link
+			// Make a new para format layer
+			CParaFormatLayer* newParaLayer=NULL;
+			if (aMode==CParagraphStyle::ERetainNoSpecificFormats || aMode==CParagraphStyle::ERetainSpecificCharFormat)
+				newParaLayer=CParaFormatLayer::NewL();
+			else
+				newParaLayer=CParaFormatLayer::NewL(currentParaAttribs.iParaFormat);
+			newParaLayer->SetBase(&aStyle);
+			CleanupStack::PushL(newParaLayer);
+			//
+			// Make a new char format layer
+			CCharFormatLayer* newCharLayer=NULL;
+			if (aMode==CParagraphStyle::ERetainNoSpecificFormats || aMode==CParagraphStyle::ERetainSpecificParaFormat)
+				newCharLayer=CCharFormatLayer::NewL();
+			else
+				newCharLayer=CCharFormatLayer::NewL(currentParaAttribs.iCharFormat);
+			if (type==KNormalParagraphStyleUid)
+				newCharLayer->SetBase(aCharStyleNormal);
+			else
+				newCharLayer->SetBase(aStyle.CharFormatLayer());
+			CleanupStack::PushL(newCharLayer);
+			//
+			(*iParaIx)[paraItem].iParaAttribs=GetParaAttribsL(newParaLayer,newCharLayer);
+			CleanupStack::PopAndDestroy(2);
+			currentParaAttribs.Release();
+			}
+		}
+	}
+
+
+void CRichTextIndex::ModifySpecificFormatting(CParaFormatLayer& aPl,CCharFormatLayer& aCl,CParagraphStyle::TApplyParaStyleMode aMode)
+//
+//
+	{
+	switch(aMode)
+		{
+		case(CParagraphStyle::ERetainNoSpecificFormats):
+			aPl.Reset();
+			aCl.Reset();
+			break;
+		case(CParagraphStyle::ERetainSpecificParaFormat):
+			aCl.Reset();
+			break;
+		case(CParagraphStyle::ERetainSpecificCharFormat):
+			aPl.Reset();
+			break;
+		case(CParagraphStyle::ERetainAllSpecificFormats):
+		default:
+			break;
+		}
+	}
+
+
+/*
+For every paragraph in the document: (i) if it uses the style aFrom, make it use the style aTo,
+or the global style if aTo is null; (ii) if any phrase in the paragraph has a character format
+based on the character format owned by aFrom, change it so that it is based on the character
+format owned by aTo, or the global character format if aTo is null.
+
+The action described in (ii) should only occur for an 'orphaned' character insertion format; that is
+an insertion format left after deletion of a block in a certain style that is itself then deleted.
+*/
+void CRichTextIndex::NotifyStyleChangedL(const CParagraphStyle* aTo,const CParagraphStyle* aFrom,
+										 const CParaFormatLayer& aGlobalParaFormatLayer,
+										 const CCharFormatLayer& aGlobalCharFormatLayer)
+	{
+	__TEST_INVARIANT;
+
+	TInt paraCount=ParagraphCount();
+	TInt currentPhrase = 0;
+	const CCharFormatLayer* oldCharFormatLayer = aFrom->CharFormatLayer();
+	const CParaFormatLayer* newParFormatLayer = aTo ? aTo : &aGlobalParaFormatLayer;
+	const CCharFormatLayer* newCharFormatLayer = aTo ? aTo->CharFormatLayer() : &aGlobalCharFormatLayer;
+	for (TInt paraItem = 0;paraItem < paraCount; paraItem++)
+		{
+		TParaAttribsEntry* currentPara = &(*iParaIx)[paraItem];
+		CParaAttribs* currentParaAttribs = currentPara->iParaAttribs;
+
+		TBool changeParStyleBase = currentParaAttribs->iParaFormat->SenseBase() == aFrom;
+		if (!currentParaAttribs->IsShared())
+			{
+			if (changeParStyleBase)
+				currentParaAttribs->iParaFormat->SetBase(newParFormatLayer);
+			TInt phraseCount = currentParaAttribs->PhraseCount();
+			for (TInt phraseItem = currentPhrase; phraseItem < (currentPhrase + phraseCount); phraseItem++)
+				{
+				CCharFormatLayer* charFormat = (*iPhraseIx)[phraseItem].CharFormat();
+				if (charFormat->SenseBase() == oldCharFormatLayer)
+					charFormat->SetBase(newCharFormatLayer);
+				}
+			currentPhrase += phraseCount;
+			}
+		else
+			{	// Maintain the shared list reference
+			CParaAttribs* resultantParaAttribs = CParaAttribs::NewL(currentParaAttribs);
+			CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,resultantParaAttribs));
+			if (changeParStyleBase)
+				resultantParaAttribs->iParaFormat->SetBase(newParFormatLayer);
+			if (resultantParaAttribs->iCharFormat->SenseBase() == oldCharFormatLayer)
+				resultantParaAttribs->iCharFormat->SetBase(newCharFormatLayer);
+			CParaAttribs* shared = RequestShareL(resultantParaAttribs); // will return a non-NULL handle
+			__ASSERT_DEBUG(shared,Panic(EDebug));
+			currentParaAttribs->Release();
+			iSharedParaQueHead.AddLast(*resultantParaAttribs); // allows correct release of cell.
+			CleanupStack::PopAndDestroy();
+			currentPara->iParaAttribs = shared;
+			}
+		}
+
+	__TEST_INVARIANT;
+	}
+
+
+const CParaFormatLayer* CRichTextIndex::ParagraphStyle(TBool& aStyleChangesOverRange,
+															   TInt aPos,
+															   TInt aLength)const
+// Return the handle of the first paragraph style encountered in the specified range.
+// Set aStyleChangesOverRange to ETrue, if different paragraph styles are encountered
+// across the specified range, otherwise set it to EFalse.
+//
+	{
+	__TEST_INVARIANT;
+
+	aStyleChangesOverRange=EFalse;
+	CParaFormatLayer* style=NULL;
+	TInt para=CONST_CAST(CRichTextIndex*,this)->OwningParagraph(aPos);
+	TInt offset=(aLength==0)?0 :-1;
+	TInt endPara=CONST_CAST(CRichTextIndex*,this)->OwningParagraph(aPos+(aLength+offset));
+	style=(CParaFormatLayer*)(*iParaIx)[para].iParaAttribs->iParaFormat->SenseBase();
+	++para;
+	for (;para<=endPara;para++)
+		{
+		CParaFormatLayer* nextStyle=(CParaFormatLayer*)(*iParaIx)[para].iParaAttribs->iParaFormat->SenseBase();
+		if (nextStyle!=style)
+			aStyleChangesOverRange=ETrue;
+		}
+
+	__TEST_INVARIANT;
+	return style;
+	}
+
+
+void CRichTextIndex::SplitPhraseL(TInt aPhrase,TInt anOffset,RPhraseAttribsEntry& aPhraseAttribs,CParaAttribs& aParaAttribs)
+	{
+	__ASSERT_DEBUG(anOffset>0 && anOffset<aPhraseAttribs.Length(),User::Invariant());
+//
+	CCharFormatLayer* charLayer=CCharFormatLayer::NewCopyBaseL(aPhraseAttribs.CharFormat());
+	CleanupStack::PushL(charLayer);
+	iPhraseIx->InsertL(aPhrase+1,RPhraseAttribsEntry(charLayer,aPhraseAttribs.Length()-anOffset));
+	CleanupStack::Pop();
+	//
+	// InsertL() has invalidated the phrase index.
+	iPhraseIx->At(aPhrase).SetLength(anOffset);  // Adjust the length of the orginal phrase
+	aParaAttribs.iPhraseCount++;
+	}
+
+
+TBool CRichTextIndex::MergePhrases(TInt aPhrase,RPhraseAttribsEntry& aPhraseAttribs,CParaAttribs& aParaAttribs)
+	{
+	RPhraseAttribsEntry& prevPhrase=iPhraseIx->At(aPhrase-1);
+	if (!aPhraseAttribs.IsIdentical(prevPhrase))
+		return EFalse;
+	// Merge the abutting phrases together.
+	prevPhrase.AdjustLength(aPhraseAttribs.Length());  //  Extend the remaining phrase
+	RemoveFromPhraseIx(aPhrase);		// Free the resources taken by the redundant phrase
+	aParaAttribs.iPhraseCount--;
+	return ETrue;
+	}
+
+
+void CRichTextIndex::Share(TParaAttribsEntry& aParaEntry,TInt aPhrase)
+//
+// aParaEntry is not shared and can be (phrase count 1), aPhrase is the single phrase element
+//
+	{
+	CParaAttribs* paraAttribs=aParaEntry.iParaAttribs;
+	__ASSERT_DEBUG(paraAttribs->iPhraseCount==1,User::Invariant());
+
+	RPhraseAttribsEntry& phraseAttribs=iPhraseIx->At(aPhrase);
+	__ASSERT_DEBUG(!phraseAttribs.IsPicturePhrase(),User::Invariant());
+
+	CParaAttribs* share=GetParaAttribs(paraAttribs,*phraseAttribs.CharFormat());
+	if (share!=paraAttribs)
+		{	// re-use an existing share, so release the current attribs
+		paraAttribs->Release();
+		phraseAttribs.Discard();
+		aParaEntry.iParaAttribs=share;
+		}
+	iPhraseIx->Delete(aPhrase);
+	}
+
+
+void CRichTextIndex::ApplyCharFormatCleanup(TAny* aPtr)
+// CLeanup function for ApplyCharFormatL()
+//
+	{REINTERPRET_CAST(CRichTextIndex*,aPtr)->ApplyCharFormatRollback();}
+
+
+void CRichTextIndex::ApplyCharFormatRollback()
+// Paragraph and phrase we were working on are stored in iPos
+// Return them the canonical form
+//
+	{
+	TParaAttribsEntry& paraEntry=iParaIx->At(iPos.iParaElement);
+	CParaAttribs* paraAttribs=paraEntry.iParaAttribs;
+
+	if (paraAttribs->IsShared())
+		return;
+
+	TInt phrase=iPos.iPhraseElement;
+	TInt base=iPos.iParaBasePhraseElement;
+	__ASSERT_DEBUG(phrase>=base && phrase<base+paraAttribs->iPhraseCount,User::Invariant());
+	if (phrase<base+paraAttribs->iPhraseCount-1)	// merge to the right
+		MergePhrases(phrase+1,iPhraseIx->At(phrase+1),*paraAttribs);
+	if (phrase>base)								// merge to the left
+		MergePhrases(phrase,iPhraseIx->At(phrase),*paraAttribs);
+	if (paraAttribs->iPhraseCount==1)				// Share the paragraph
+		Share(paraEntry,base);
+	}
+
+
+void CRichTextIndex::ApplyCharFormatL(const TCharFormatX& aFormat,const TCharFormatXMask& aMask,TInt aPos,TInt aLength,TBool aRemoveSpecific)
+// Applies the specified character formatting to the characters contained within the range
+// aPos to aPos+(aLength-1).
+//
+	{
+	__ASSERT_DEBUG(aLength>=0,User::Invariant());
+	__TEST_INVARIANT;
+
+	ScanToPosition(aPos,EScanToPositionAbsolute);
+	TInt paraOffset=iPos.iParaElementOffset;
+	TInt phraseOffset=iPos.iPhraseElementOffset;
+	TInt phrase=iPos.iPhraseElement;
+
+// prepare for failure
+	CleanupStack::PushL(TCleanupItem(ApplyCharFormatCleanup,this));
+
+	for (;;)
+		{	// a paragraph at a time
+		TParaAttribsEntry& paraEntry=iParaIx->At(iPos.iParaElement);
+		CParaAttribs* paraAttribs=paraEntry.iParaAttribs;
+		TInt charsToFormat=Min(aLength,paraEntry.iLength-paraOffset);
+		aLength-=charsToFormat;
+#ifdef _DEBUG
+		aPos+=charsToFormat;
+#endif
+
+// STEP 1. Reclaim any shared paragraph into non shared form. Re-use the object if possible
+
+		if (paraAttribs->IsShared())
+			{
+			CCharFormatLayer* charLayer=paraAttribs->iCharFormat;
+			if (paraAttribs->iRefCount==CParaAttribs::EPrimeSharedCount)
+				{	// we are the sole user of this attribute
+				iPhraseIx->InsertL(phrase,RPhraseAttribsEntry(charLayer,paraEntry.iLength));
+				// adjust attribute to be non-shared
+				paraAttribs->link.Deque();
+				paraAttribs->iRefCount=CParaAttribs::EPrimeNonSharedCount;
+				paraAttribs->iPhraseCount=1;
+				}
+			else
+				{	// create a new para attribs object
+				CParaAttribs* newAttribs=CParaAttribs::NewL(paraAttribs->iParaFormat);
+				CleanupReleasePushL(*newAttribs);
+				charLayer=CCharFormatLayer::NewCopyBaseL(charLayer);
+				CleanupStack::PushL(charLayer);
+				iPhraseIx->InsertL(phrase,RPhraseAttribsEntry(charLayer,paraEntry.iLength));
+				CleanupStack::Pop(2);		// charlayer, newAttribs
+				paraAttribs->Release();		// lose a share on the old attribs
+				paraEntry.iParaAttribs=paraAttribs=newAttribs;
+				}
+			phraseOffset=paraOffset; // we are now in the current position
+			}
+
+// STEP 2.	Walk through all affected phrases in this paragraph
+//			For each one, we may need to split it, and then apply the new format
+
+		do
+			{
+			__ASSERT_DEBUG(phrase<iPos.iParaBasePhraseElement+paraAttribs->iPhraseCount,User::Invariant());
+//
+			RPhraseAttribsEntry* phraseAttribs=&iPhraseIx->At(phrase);
+			TInt len=phraseAttribs->Length();
+
+// STEP 2.1	Split the phrase at the beginning of the range?
+
+			if (phraseOffset>0)
+				{		// can only happen for the first phrase
+				/*
+				 * The pointer paraAttribs is also stored in
+				 * 'paraEntry.iParaAttribs'. The memory pointed to by this
+				 * pointer will be released in CRichTextIndex's destructor.
+				 */
+				// coverity[leave_without_push]
+				SplitPhraseL(phrase,phraseOffset,*phraseAttribs,*paraAttribs);	// inserts new phrase at correct position
+				len-=phraseOffset;
+				phraseOffset=0;
+				iPos.iPhraseElement=++phrase;
+				phraseAttribs=&iPhraseIx->At(phrase);
+				}
+
+// STEP 2.2	Split the phrase at the end of the range?
+
+			if (len>charsToFormat)
+				{	// phrase is longer than required format, so split it
+				/*
+				 * The pointer paraAttribs is also stored in
+				 * 'paraEntry.iParaAttribs'. The memory pointed to by this
+				 * pointer will be released in CRichTextIndex's destructor.
+				 */
+				// coverity[leave_without_push]
+				SplitPhraseL(phrase,charsToFormat,*phraseAttribs,*paraAttribs);
+				len=charsToFormat;
+				phraseAttribs=&iPhraseIx->At(phrase);		// SplitPhraseL modifies the index array, we must do this!
+				}
+
+			__ASSERT_DEBUG(phraseAttribs->Length()==len,User::Invariant());
+
+// STEP 2.3	Change the format of the current phrase layer
+
+			TCharFormatX format=aFormat;
+			TCharFormatXMask mask=aMask;
+			CCharFormatLayer* charLayer=phraseAttribs->CharFormat();
+			if (!aRemoveSpecific)
+				charLayer->Sense(format,mask);  // preserve current specific character formatting
+			charLayer->SetL(format,mask);
+
+// STEP 2.4	Check for merging with previous phrase
+
+			if (phrase==iPos.iParaBasePhraseElement || !MergePhrases(phrase,*phraseAttribs,*paraAttribs))
+				// if we don't merge this phrase, move on to the next one
+				iPos.iPhraseElement=++phrase;
+
+			charsToFormat-=len;
+			} while (charsToFormat);
+
+		__ASSERT_DEBUG(phrase==iPos.iParaBasePhraseElement+paraAttribs->iPhraseCount || aLength==0,User::Invariant());
+
+// STEP 3	Reduce the paragraph attributes back to canonical form
+
+// STEP 3.1	Check for merging at the end of the changes
+
+		if (phrase>iPos.iParaBasePhraseElement && phrase<iPos.iParaBasePhraseElement+paraAttribs->iPhraseCount)
+			MergePhrases(phrase,iPhraseIx->At(phrase),*paraAttribs);	// mustn't adjust phrase index to follow merge
+
+// STEP 3.2	See if we can re-share the paragraph
+
+		if (paraAttribs->iPhraseCount==1)
+			{	// This para has constant character formatting - can be shared.
+			iPos.iPhraseElement=--phrase;
+			Share(paraEntry,phrase);
+			}
+
+// loop into next paragraph
+		if (aLength==0)
+			break;
+		iPos.iParaElement++;
+		paraOffset=0;
+		iPos.iParaBasePhraseElement=phrase;
+#ifdef _DEBUG
+		ScanToPosition(aPos,EScanToPositionAbsolute);
+		__ASSERT_DEBUG(iPos.iDocPos==aPos,User::Invariant());
+		__ASSERT_DEBUG(iPos.iPhraseElement==phrase,User::Invariant());
+		__ASSERT_DEBUG(iPos.iParaElementOffset==paraOffset,User::Invariant());
+		__ASSERT_DEBUG(iPos.iPhraseElementOffset==phraseOffset,User::Invariant());
+		__TEST_INVARIANT;
+#endif
+		}
+
+	CleanupStack::Pop();	// rollback item
+
+	__TEST_INVARIANT;
+	}
+
+
+void CRichTextIndex::RemoveSpecificParaFormatL(TInt aPos,TInt aLength)
+// Removes all specific paragraph formatting from the specified region.
+// For each paragraph covered by the range, check if its para attribs is in the
+// shared list, or not.
+// If its not shared, then simply reset the para format layer.
+// If it is in the shared list, then a new shared para attribs must be created,
+// and the reference count of the original decremented.
+//
+	{
+	__TEST_INVARIANT;
+
+	TInt endPara=OwningParagraph(aPos+aLength);
+	TInt currentPara=OwningParagraph(aPos);
+	CParaAttribs* currentParaAttribs=NULL;
+	while (currentPara<=endPara)
+		{
+		currentParaAttribs=(*iParaIx)[currentPara].iParaAttribs;
+		if (!currentParaAttribs->IsShared())
+			{// Reset specific paragraph format layer to be empty.
+			currentParaAttribs->iParaFormat->Reset();  // remove specific formatting.
+			}
+		else
+			{// Maintain the shared list reference
+			CParaAttribs* resultantParaAttribs=CParaAttribs::NewL(currentParaAttribs);
+			CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,resultantParaAttribs));
+			resultantParaAttribs->iParaFormat->Reset();  // remove specific formatting
+			CParaAttribs* shared=RequestShareL(resultantParaAttribs);  // Will return a non-NULL handle.
+			__ASSERT_DEBUG(shared,Panic(EDebug));
+			currentParaAttribs->Release();
+			iSharedParaQueHead.AddLast(*resultantParaAttribs);  // Allows correct release of cell.
+			CleanupStack::PopAndDestroy();
+			(*iParaIx)[currentPara].iParaAttribs=shared;
+			}
+		currentPara++;
+		}
+
+
+	__TEST_INVARIANT;
+	}
+
+
+void CRichTextIndex::RemoveSpecificCharFormatL(TInt aPos,TInt aLength)
+// Removes all specific character formatting from the specified region.
+// For each paragraph covered by the range, check if its para attribs is in the
+// shared list, or not.
+// If its not shared, then simply reset the para format layer.
+// If it is in the shared list, then a new shared para attribs must be created,
+// and the reference count of the original decremented.
+//
+	{
+	__TEST_INVARIANT;
+
+	TCharFormatX format;  // dummy format
+	TCharFormatXMask mask;
+	mask.ClearAll();
+	ApplyCharFormatL(format,mask,aPos,aLength,ETrue);
+
+	__TEST_INVARIANT;
+	}
+
+
+TBool CRichTextIndex::MergePhrases(TInt aPos)
+// Checks if the specified character position aPos is a phrase boundary.
+// If so, then examines the two abutting phrases at aPos to see if they
+// are identical;  in which case they are merged into one phrase that covers
+// the sum of their lengths.
+//
+	{
+	ScanToPosition(aPos,EScanToPositionAbsolute);
+	return MergePhrases(iPos);
+	}
+
+
+TBool CRichTextIndex::MergePhrases(const TLogicalPosition& aPos)
+// Checks if the specified character position record is a phrase boundary.
+// If so, then examines the two abutting phrases at aPos to see if they
+// are identical;  in which case they are merged into one phrase that covers
+// the sum of their lengths.
+//
+	{
+	TCurrentIndexRecords current;
+	GetCurrentRecords(current);
+	TBool phrasesMerged=EFalse;
+	if (!FirstPhraseOfParagraph() && iPos.iPhraseElementOffset==0)
+		{// Check if the 2 abutting phrases can be merged together.
+		TInt rightPhraseElement=iPos.iPhraseElement;
+		RPhraseAttribsEntry* rightPhrase=&(*iPhraseIx)[rightPhraseElement];
+		RPhraseAttribsEntry* leftPhrase=&(*iPhraseIx)[rightPhraseElement-1];
+		if (rightPhrase->IsIdentical(*leftPhrase))
+			{// Merge the abutting phrases together.  Cannot merge picture/non-picture/z.l.p. phrase combinations.
+			rightPhrase->AdjustLength(leftPhrase->Length());  //  Extend the right phrase length
+			RemoveFromPhraseIx(rightPhraseElement-1);  // Free the resources taken by the left phrase - redundant
+			(*iParaIx)[iPos.iParaElement].iParaAttribs->iPhraseCount--; // Update phrase count of owning CParaAttribs
+			ScanToPosition(aPos.iDocPos,EScanToPositionAbsolute);  // Pick up new phrase index.
+			phrasesMerged=ETrue;
+			}
+		}
+	return phrasesMerged;
+	}
+
+
+/** Remove phrases from the containing object.  This includes
+freeing referenced resources. (pictures etc.)
+@param aPhraseIndex The first phrase to be deleted
+@param aCount The number of phrases to be deleted
+*/
+void CRichTextIndex::RemoveFromPhraseIx(TInt aPhraseIndex,TInt aCount)
+	{
+ 	// if the phrase being deleted is <= iLastPos  
+ 	// then iLastPos will become invalid so should be reset
+ 	if (aPhraseIndex <= iLastUsed.iPhraseElement )
+ 		iLastUsed.Clear();
+ 		
+	for (TInt offset=aPhraseIndex;offset<(aPhraseIndex+aCount);offset++)
+		{
+		// discard phrases & book-keep the picture count
+		RPhraseAttribsEntry& phrase=(*iPhraseIx)[offset];
+		phrase.Discard();
+		if (phrase.IsPicturePhrase())
+			iPictureCount--;
+		}
+	iPhraseIx->Delete(aPhraseIndex,aCount);
+	}
+
+void CRichTextIndex::GetParagraphFormatL(CParaFormat* aFormat,TInt aPos)const
+// Fills aFormat with the effective Paragraph format attributes for the paragraph
+// in which character position aPos is contained.
+//
+	{
+	__TEST_INVARIANT;
+
+	TLogicalPosition cachePos(iLastUsed);
+	TInt para=CONST_CAST(CRichTextIndex*,this)->OwningParagraph(aPos,&cachePos);
+	(*iParaIx)[para].iParaAttribs->iParaFormat->SenseEffectiveL(aFormat);
+	}
+
+void CRichTextIndex::GetSpecificParagraphFormatL(CParaFormat* aFormat,
+												 TParaFormatMask& aMask,
+												 TInt aPos)const
+	{
+	__TEST_INVARIANT;
+
+	TLogicalPosition cachePos(iLastUsed);
+	TInt para=CONST_CAST(CRichTextIndex*,this)->OwningParagraph(aPos,&cachePos);
+	CParaFormatLayer* pLayer = (*iParaIx)[para].iParaAttribs->iParaFormat;
+	pLayer->SenseL(aFormat, aMask);
+	}
+
+TInt CRichTextIndex::GetChars(TCharFormatX& aFormat,TInt aPos) const
+// Returns the number of characters, commencing at aStartPos, that occupy the same phrase, and
+// modifies aFormat, to hold the effective format of that phrase.
+//
+	{
+	__TEST_INVARIANT;
+
+	CONST_CAST(CRichTextIndex*,this)->ScanToPosition(aPos,EScanToPositionAbsolute,&MUTABLE_CAST(TLogicalPosition&,iLastUsed));
+	TCurrentIndexRecords current;
+	GetCurrentRecords(current);
+	TInt phraseLength;
+	CCharFormatLayer* charFormatLayer;
+	if (current.iPhrase)
+		{// Specific character formatting held in phrase index.
+		charFormatLayer=current.iPhrase->CharFormat();
+		phraseLength=(current.iPhrase->Length())-(iPos.iPhraseElementOffset);
+		}
+	else
+		{// Constant character formatting held in the para attribs
+		charFormatLayer=current.iParaAttribs->iCharFormat;
+		phraseLength=current.iParaEntry->iLength-iPos.iParaElementOffset;
+		}
+	charFormatLayer->SenseEffective(aFormat);
+	return phraseLength;
+	}
+
+
+TInt CRichTextIndex::GetPictureSizeInTwips(TSize& aSize,TInt aPos)const
+// Get the size of the specified picture into aSize.  The picture is specified by its
+// character position.  Return KErrNotFound if there is no picture at the specified
+// document position.
+//
+	{
+	__TEST_INVARIANT;
+
+	CONST_CAST(CRichTextIndex*,this)->ScanToPosition(aPos,EScanToPositionAbsolute,&MUTABLE_CAST(TLogicalPosition&,iLastUsed));
+	TCurrentIndexRecords current;
+	GetCurrentRecords(current);
+
+	return (current.iPhrase)
+		? current.iPhrase->GetPictureSizeInTwips(aSize)
+		: KErrNotFound;
+	}
+
+TPictureHeader* CRichTextIndex::PictureHeaderPtr(TInt aPos)
+	{
+	__TEST_INVARIANT;
+
+	CONST_CAST(CRichTextIndex*,this)->ScanToPosition(aPos,EScanToPositionAbsolute);
+	TCurrentIndexRecords current;
+	GetCurrentRecords(current);
+	return current.iPhrase? current.iPhrase->PictureHeaderPtr() : 0;
+	}
+
+TPictureHeader CRichTextIndex::PictureHeader(TInt aPos) const
+// Return the picture header describing the picture at character position aPos.
+// If there is no picture at character position aPos, a default picture header is returned.
+//
+	{
+	const TPictureHeader* p = const_cast<CRichTextIndex*>(this)->PictureHeaderPtr(aPos);
+	return p? *p : TPictureHeader();
+	}
+
+
+CPicture* CRichTextIndex::PictureHandleL(TInt aPos,MLayDoc::TForcePictureLoad aForceLoad)const
+// Returns the handle of the concrete picture at character position aPos, if one exists;
+// otherwise returns NULL.
+//
+	{
+	__TEST_INVARIANT;
+
+	CONST_CAST(CRichTextIndex*,this)->ScanToPosition(aPos,EScanToPositionAbsolute,&MUTABLE_CAST(TLogicalPosition&,iLastUsed));
+	TCurrentIndexRecords current;
+	GetCurrentRecords(current);
+	return (current.iPhrase)
+		? (CPicture*)current.iPhrase->PictureHandleL(iText.PictureFactory(),iText.StoreResolver(),aPos,aForceLoad)
+		: NULL;
+	}
+
+
+void CRichTextIndex::GetParaFormatL(CParaFormat* aFormat,TParaFormatMask& aVaries,TInt aPos,TInt aLength,CParaFormat::TParaFormatGetMode aMode)const
+// Senses the paragraph format of para(s) covered by the region aPos to aPos+aLength-1.
+// aFormat takes the values of all attributes, and the mask aMask indicates those values that change
+// over the selected region, and are therefore *indeterminate*.
+// Application: seeding paragraph formatting dialogs.
+//
+	{
+	__TEST_INVARIANT;
+
+	TInt para=CONST_CAST(CRichTextIndex*,this)->OwningParagraph(aPos);
+	TInt offset=(aLength==0)?0 :-1;
+	TInt endPara=CONST_CAST(CRichTextIndex*,this)->OwningParagraph(aPos+(aLength+offset));
+	aVaries.ClearAll();
+	(*iParaIx)[para].iParaAttribs->iParaFormat->SenseEffectiveL(aFormat,aMode);  // Sense 1st paras' format.
+	++para;
+	CParaFormat* format=CParaFormat::NewLC();
+	for (;para<=endPara;para++)
+		{
+		(*iParaIx)[para].iParaAttribs->iParaFormat->SenseEffectiveL(format,aMode);
+		if (format->iLanguage!=aFormat->iLanguage)
+			aVaries.SetAttrib(EAttParaLanguage);
+		if (format->iFillColor!=aFormat->iFillColor)
+			aVaries.SetAttrib(EAttFillColor);
+		if (format->iLeftMarginInTwips!=aFormat->iLeftMarginInTwips)
+			aVaries.SetAttrib(EAttLeftMargin);
+		if (format->iRightMarginInTwips!=aFormat->iRightMarginInTwips)
+			aVaries.SetAttrib(EAttRightMargin);
+		if (format->iIndentInTwips!=aFormat->iIndentInTwips)
+			aVaries.SetAttrib(EAttIndent);
+		if (format->iHorizontalAlignment!=aFormat->iHorizontalAlignment)
+			aVaries.SetAttrib(EAttAlignment);
+		if (format->iVerticalAlignment!=aFormat->iVerticalAlignment)
+			aVaries.SetAttrib(EAttVerticalAlignment);
+		if (format->iLineSpacingInTwips!=aFormat->iLineSpacingInTwips)
+			aVaries.SetAttrib(EAttLineSpacing);
+		if (format->iLineSpacingControl!=aFormat->iLineSpacingControl)
+			aVaries.SetAttrib(EAttLineSpacingControl);
+		if (format->iSpaceBeforeInTwips!=aFormat->iSpaceBeforeInTwips)
+			aVaries.SetAttrib(EAttSpaceBefore);
+		if (format->iSpaceAfterInTwips!=aFormat->iSpaceAfterInTwips)
+			aVaries.SetAttrib(EAttSpaceAfter);
+		if (format->iKeepTogether!=aFormat->iKeepTogether)
+			aVaries.SetAttrib(EAttKeepTogether);
+		if (format->iKeepWithNext!=aFormat->iKeepWithNext)
+			aVaries.SetAttrib(EAttKeepWithNext);
+		if (format->iWidowOrphan!=aFormat->iWidowOrphan)
+			aVaries.SetAttrib(EAttWidowOrphan);
+		if (format->iWrap!=aFormat->iWrap)
+			aVaries.SetAttrib(EAttWrap);
+		if (format->iBorderMarginInTwips!=aFormat->iBorderMarginInTwips)
+			aVaries.SetAttrib(EAttBorderMargin);
+		if (format->iDefaultTabWidthInTwips!=aFormat->iDefaultTabWidthInTwips)
+			aVaries.SetAttrib(EAttDefaultTabWidth);
+		if (aMode==CParaFormat::EAllAttributes)
+			{
+			// Borders
+			for (TInt border=0;border<CParaFormat::EMaxParaBorder;border++)
+				{// Check each para border individually.  Assumes border format attributes run consecutively.
+				if (!format->IsBorderEqual((CParaFormat::TParaBorderSide)border,*aFormat))
+					aVaries.SetAttrib((TTextFormatAttribute)(EAttTopBorder+border));
+				}
+			// Bullet
+			if (!format->iBullet && !aFormat->iBullet)
+				{ /* neither para has bullet, so no variation */ }
+			else if (!format->iBullet || !aFormat->iBullet
+				|| *format->iBullet!=*aFormat->iBullet)
+				aVaries.SetAttrib(EAttBullet);
+			// The Tab-List
+			if (format->TabCount()!=aFormat->TabCount())
+				aVaries.SetAttrib(EAttTabStop);  // TabLists are different.
+			else
+				{// The 2 tablists have the same number of tab stops - but are not necessarily the same.
+				TBool matched=ETrue;
+				TInt tabCount=format->TabCount();
+				for (TInt tabItem=0;tabItem<tabCount;tabItem++)
+					{// Compare the 2 tabs.
+					TTabStop comp1,comp2;
+					comp1=format->TabStop(tabItem);
+					comp2=aFormat->TabStop(tabItem);
+					if (comp1!=comp2)
+						matched=EFalse;
+					}
+				if (!matched)
+					aVaries.SetAttrib(EAttTabStop);
+				}
+			}
+		}
+	CleanupStack::PopAndDestroy();
+	}
+
+
+// Compare all attributes in two formats and where they differ set the appropriate flag in the aVaries mask.
+void CRichTextIndex::CheckForUndetermined(const TCharFormatX& aFormatA,const TCharFormatX& aFormatB,
+										  TCharFormatXMask& aVaries) const
+	{
+	const TCharFormat& a = aFormatA.iCharFormat;
+	const TCharFormat& b = aFormatB.iCharFormat;
+	if (a.iLanguage!=b.iLanguage)
+		aVaries.SetAttrib(EAttCharLanguage);
+	if (a.iFontPresentation.iTextColor!=b.iFontPresentation.iTextColor)
+		aVaries.SetAttrib(EAttColor);
+	if (a.iFontPresentation.iHighlightColor!=b.iFontPresentation.iHighlightColor)
+		aVaries.SetAttrib(EAttFontHighlightColor);
+	if (a.iFontPresentation.iHighlightStyle!=b.iFontPresentation.iHighlightStyle)
+		aVaries.SetAttrib(EAttFontHighlightStyle);
+	if (a.iFontPresentation.iStrikethrough!=b.iFontPresentation.iStrikethrough)
+		aVaries.SetAttrib(EAttFontStrikethrough);
+	if (a.iFontPresentation.iUnderline!=b.iFontPresentation.iUnderline)
+		aVaries.SetAttrib(EAttFontUnderline);
+	if (a.iFontPresentation.iHiddenText!=b.iFontPresentation.iHiddenText)
+		aVaries.SetAttrib(EAttFontHiddenText);
+	if (a.iFontPresentation.iPictureAlignment!=b.iFontPresentation.iPictureAlignment)
+		aVaries.SetAttrib(EAttFontPictureAlignment);
+	if (a.iFontSpec.iHeight!=b.iFontSpec.iHeight)
+		aVaries.SetAttrib(EAttFontHeight);
+	if (!(a.iFontSpec.iTypeface==b.iFontSpec.iTypeface))
+		aVaries.SetAttrib(EAttFontTypeface);
+	if (a.iFontSpec.iFontStyle.Posture()!=b.iFontSpec.iFontStyle.Posture())
+		aVaries.SetAttrib(EAttFontPosture);
+	if (a.iFontSpec.iFontStyle.StrokeWeight()!=b.iFontSpec.iFontStyle.StrokeWeight())
+		aVaries.SetAttrib(EAttFontStrokeWeight);
+	if (a.iFontSpec.iFontStyle.PrintPosition()!=b.iFontSpec.iFontStyle.PrintPosition())
+		aVaries.SetAttrib(EAttFontPrintPos);
+	if (aFormatA.iParserTag != aFormatB.iParserTag)
+		aVaries.SetAttrib(EAttParserTag);
+	}
+
+
+void CRichTextIndex::GetCharFormat(TCharFormatX& aFormat,TCharFormatXMask& aVaries,TInt aPos,TInt aLength)const
+// Senses the character formatting of the phrase(s) covered by the region aPos to aPos+aLength-1.
+// aFormat takes the values of all character format attributes, and the mask aMask indicates those
+// values that change over the selected region, and are therefore *indeterminate*.
+// Application: seeding character formatting dialogs.
+// If aLength is zero, the character format sensed is that of the charcter immediatley to the left (behind) the cursor.
+//
+	{
+	__TEST_INVARIANT;
+
+	aVaries.ClearAll();
+	if (aLength==0)  // Get the format of the character to the left of the cursor.
+		((CRichTextIndex*)this)->ScanToPosition(aPos,EScanToPositionMatchLeft);
+	else
+		((CRichTextIndex*)this)->ScanToPosition(aPos,EScanToPositionAbsolute);
+	// Get char format of first phrase
+	TCurrentIndexRecords current;
+	GetCurrentRecords(current);
+	if (current.iPhrase)
+		current.iPhrase->CharFormat()->SenseEffective(aFormat);
+	else
+		current.iParaAttribs->iCharFormat->SenseEffective(aFormat);
+	// Place pos at start of next phrase
+	TInt pos=aPos+(CurrentPhraseLength()-CurrentPhraseOffset());
+	while (pos<=aPos+aLength-1)
+		{// Get the format of the next phrase and check if attributes change value
+		((CRichTextIndex*)this)->ScanToPosition(pos,EScanToPositionAbsolute);
+		GetCurrentRecords(current);
+		TCharFormatX format;
+		if (current.iPhrase)
+			current.iPhrase->CharFormat()->SenseEffective(format);
+		else
+			current.iParaAttribs->iCharFormat->SenseEffective(format);
+		CheckForUndetermined(format,aFormat,aVaries);
+		pos+=CurrentPhraseLength();
+		}
+	}
+
+
+void CRichTextIndex::GetSpecificCharFormatDirection(TCharFormatX& aFormat,
+												   TCharFormatXMask& aMask,
+												   TInt aPos,
+												   TBool aGetLeft) const
+	{
+	__TEST_INVARIANT;
+
+	aMask.ClearAll();
+	((CRichTextIndex*)this)->ScanToPosition(aPos,
+		aGetLeft? EScanToPositionMatchLeft : EScanToPositionAbsolute);
+	TCurrentIndexRecords current;
+	GetCurrentRecords(current);
+	if (current.iPhrase)
+		current.iPhrase->CharFormat()->Sense(aFormat,aMask);
+	else
+		current.iParaAttribs->iCharFormat->Sense(aFormat,aMask);
+	}
+
+void CRichTextIndex::GetSpecificCharFormat(TCharFormatX& aFormat,TCharFormatXMask& aMask,TInt aPos)const
+// Return the format attributes store in the specific layer only, for the specified document position.
+// THIS IS NOT THE EFFECTIVE FORMAT, BUT THE SPECIFIC FORMATTING ONLY.
+//
+	{
+	GetSpecificCharFormatDirection(aFormat, aMask, aPos, ETrue);
+	}
+
+
+CParaAttribs* CRichTextIndex::RequestReclaimShareL(CParaAttribs* aParaAttribs,TParaAttribsEntry* aParaEntry)
+// If the specified para attribs is currently on the shared list, then
+// a specific para attribs is *reclaimed* and returned.
+// The current share on the CParaAttribs is not *Released*.
+// A new CParaAttribs is created of the same paragraph format as the shared one,
+// but with a reference count of zero, (as this now has specific character formatting),
+// and 1 phrase that is of the same character format as the shared one.
+// The reclaimed specific para attribs is attactched to the specified para entry.
+// NOTE:
+// If the specified CParaAttribs is not currently on the shared list, NULL
+// is returned, and aParaAttribs left unchanged..
+// Assumes a previous call to ScanToPosition has correctly set the internal position record.
+// This function can be called safely in any situation. (Except that it may *LEAVE*).
+//
+	{
+	if (!(aParaAttribs->IsShared()))
+		return NULL;  // This para attribs is not currently shared.
+	// We are dealing with a shared paraAttribs from now on.
+	CParaAttribs* reclaimedPara=CParaAttribs::NewL(aParaAttribs->iParaFormat);  // Create the re-claimed paraAttribs.
+	CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,reclaimedPara));
+	CCharFormatLayer* newFormat=CCharFormatLayer::NewCopyBaseL(aParaAttribs->iCharFormat);
+	CleanupStack::PushL(newFormat);
+	RPhraseAttribsEntry phrase(newFormat,aParaEntry->iLength);
+	// Now insert this phrase into the index.
+	iPhraseIx->InsertL(iPos.iPhraseElement,phrase);
+	CleanupStack::Pop(2);  // newFormat, reclaimedPara
+	return reclaimedPara;
+	}
+
+
+CParaAttribs* CRichTextIndex::RequestShareL(CParaAttribs* aParaAttribs,CCharFormatLayer* aCharFormat,CParaAttribs* aReservedCell)
+// Attempts to re-use an existing paraAttribs that matches the one specified.
+// Returns the handle of a CParaAttribs, or NULL if the specified argument cannot
+// be shared.
+// aCharFormat may be NULL, as may aReservedCell.
+//
+	{
+	if (aParaAttribs->iRefCount<=0 && aParaAttribs->iPhraseCount>1)
+		return NULL;  // This para has specific character formatting & multiple phrases and so cannot be shared.
+	// This para has constant character formatting - can be shared.
+	CCharFormatLayer* charFormat;
+	if (aCharFormat)
+		charFormat=aCharFormat;  // Has a phrase index.
+	else
+		charFormat=aParaAttribs->iCharFormat;  // Has constant char formatting.
+	return GetParaAttribsL(aParaAttribs->iParaFormat,charFormat,aReservedCell);
+	}
+
+
+CParaAttribs* CRichTextIndex::RequestShare(const TLogicalPosition& aLogicalPosition)
+// Returns a handle to a paraAttribs on the shared list that matches the paragraph at the
+// specified position.  Returns NULL if the specified paragraph does not have constant
+// character formatting.
+//
+	{
+	CParaAttribs* paraAttribs=(*iParaIx)[aLogicalPosition.iParaElement].iParaAttribs;
+	if (paraAttribs->iRefCount<=0 && paraAttribs->iPhraseCount>1)
+		return NULL;  // This para has specific character formatting and so cannot be shared.
+	// This para has constant character formatting - can be shared.
+	return GetParaAttribs(aLogicalPosition);
+	}
+
+
+CParaAttribs* CRichTextIndex::GetParaAttribsL(const CParaFormatLayer* aParaFormat,const CCharFormatLayer* aCharFormat,
+											  CParaAttribs* aReservedCell)
+// Attempts to match the specified arguments to a CParaAttribs in the shared list.
+// If matched, the handle of the matched para attribs is returned, and the reference count
+// of that item is incremented.
+//
+// aReservedCell is a pre-allocated CParaAttribs that has been correctly setup.  If no match
+// is found, and the reserved cell is specified, this is used in preference to creating a new one.
+// If the reserved cell is not specified and there is no match, a new CParaAttribs of the correct
+// specification is created and added to the shared list.
+//
+	{
+	CParaAttribs* handle=FindSharedParaAttribs(*aParaFormat,*aCharFormat);
+	if (handle)
+		return handle;  // match found already in the shared list.
+// There is no match, so create new sharedPara and add to list.
+	if (!aReservedCell)
+		aReservedCell=CParaAttribs::NewL(aParaFormat,aCharFormat);  // Reusing aReservedCell saves an automatic.
+	iSharedParaQueHead.AddLast(*aReservedCell);
+	return aReservedCell;
+	}
+
+
+CParaAttribs* CRichTextIndex::GetParaAttribs(CParaAttribs* aParaAttribs,CCharFormatLayer& aCharFormatLayer)
+// Called by Reset.
+//
+	{
+	CParaAttribs* handle=FindSharedParaAttribs(*aParaAttribs->iParaFormat,aCharFormatLayer);
+	if (handle)
+		return handle;
+	else
+		{// No match, so piece together new shared paraAttribs and add to shared para list.
+		aParaAttribs->iRefCount=1;
+		aParaAttribs->iCharFormat=&aCharFormatLayer;
+		iSharedParaQueHead.AddLast(*aParaAttribs);
+		return aParaAttribs;
+		}
+	}
+
+
+CParaAttribs* CRichTextIndex::GetParaAttribs(const TLogicalPosition& aLogicalPosition)
+// Attempts to match the specified arguments to a CParaAttribs in the shared list.
+// If matched, the handle of the matched para attribs is returned, and the reference count
+// of that item is incremented. If no match is found, the current para attribs is
+// transformed into one that is placed in the shared list.
+//
+	{
+	CParaAttribs* sourceParaAttribs=(*iParaIx)[aLogicalPosition.iParaElement].iParaAttribs;
+	RPhraseAttribsEntry* sourcePhrase=&(*iPhraseIx)[aLogicalPosition.iPhraseElement];
+	//
+	CParaAttribs* handle=FindSharedParaAttribs(*sourceParaAttribs->iParaFormat,*sourcePhrase->CharFormat());
+	if (handle)
+		return handle;
+	else
+		{// No match, so piece together new shared paraAttribs and add to shared para list
+		sourceParaAttribs->iRefCount=1;
+		__ASSERT_ALWAYS(!sourcePhrase->IsPicturePhrase(),Panic(EReleasCharFormatLayerOwnershipCalledOnPicturePhrase));
+		sourceParaAttribs->iCharFormat=sourcePhrase->ReleaseCharFormatLayerOwnership();
+		sourcePhrase->Discard();
+		iPhraseIx->Delete(aLogicalPosition.iPhraseElement);  // remove the deleted phrase from the phrase index.
+		iSharedParaQueHead.AddLast(*sourceParaAttribs);
+		return sourceParaAttribs;
+		}
+	}
+
+
+CParaAttribs* CRichTextIndex::FindSharedParaAttribs(const CParaFormatLayer& aParaFormatLayer,const CCharFormatLayer& aCharFormatLayer)
+// Attempts to match the specified arguments to an item in the shared para list.
+// If found, the handle of the matched para attribs is returned, and the reference count
+// of that item is incremented.
+// If no match is made NULL is returned.
+//
+	{
+	CParaAttribs* currentSharedPara=NULL;
+	TBool matched=EFalse;
+	if (!iSharedParaQueHead.IsEmpty())
+		{
+		TDblQueIter<CParaAttribs> iterator(iSharedParaQueHead);
+		while ((currentSharedPara=iterator++)!=NULL)
+			{// Try and match each item in the shared para list.
+			matched=aParaFormatLayer.IsIdentical(currentSharedPara->iParaFormat);
+			if (!matched)
+				continue;
+			matched=aCharFormatLayer.IsIdentical(currentSharedPara->iCharFormat);
+			if (!matched)
+				continue;
+			// We have a match, so adjust reference count.
+			currentSharedPara->iRefCount++;
+			return currentSharedPara;
+			}
+		}
+	return currentSharedPara;
+	}
+
+
+void CRichTextIndex::ScanToPosition(TInt aCharPos,TScanToPositionMode aMode,TLogicalPosition* aLastUsed/*=NULL*/)
+// Move the internal position record to that indicated by the specified character position, aCharPos.
+// Behaviour follows:
+// aCharPos is considered to be goverened by the phrase covering aCharPos-1,
+// except when aCharPos is the fist character of a paragraph, when
+// aCharPos is goverened by the phrase coverng aCharPos.
+// (If nothing else, aCharPos will be a paragraph delimiter or the end-of-document
+// character.
+// The implementation below matches to the right as standard, then checks if a left
+// phrase match is available.
+//
+	{
+	if (!aLastUsed || aLastUsed->iDocPos-aLastUsed->iParaElementOffset>aCharPos)
+		iPos.Clear();  // Reset the internal position record.
+	else
+		{
+		iPos=*aLastUsed;
+		if (iPos.iDocPos>aCharPos || (aMode==EScanToPositionMatchLeft && iPos.iDocPos==aCharPos))
+			{// reset to the start of paragraph if aPos < cache pos or aPos==chache Pos whilst matching left
+			iPos.iDocPos-=iPos.iParaElementOffset;
+			iPos.iParaElementOffset=iPos.iPhraseElementOffset=0;
+			iPos.iPhraseElement=iPos.iParaBasePhraseElement;
+			}
+		}
+
+	TInt phraseElement=iPos.iPhraseElement;
+	TInt startOfPara=iPos.iDocPos-iPos.iParaElementOffset;
+	TInt paraElement=iPos.iParaElement;
+	const CArrayFix<TParaAttribsEntry>& paraIx=*iParaIx;
+	const TParaAttribsEntry* para=&paraIx[paraElement];
+	TInt len=para->iLength;
+	if (aCharPos>=(startOfPara+len))
+		{
+		iPos.iParaElementOffset=iPos.iPhraseElementOffset=0;
+		phraseElement=iPos.iParaBasePhraseElement;
+		const TParaAttribsEntry* end=paraIx.End(paraElement);
+		do
+			{// Find the paragraph...
+			startOfPara+=len;
+			if (!(para->iParaAttribs->IsShared()))  // Adjust position within phrase index
+				phraseElement+=para->iParaAttribs->iPhraseCount;
+			++paraElement;
+			if (++para==end)
+				{
+				para=&paraIx[paraElement];
+				end=paraIx.End(paraElement);
+				}
+			len=para->iLength;
+			}
+				while (aCharPos>=(startOfPara+len) && (paraElement+1) < paraIx.Count());
+		iPos.iParaBasePhraseElement=phraseElement;
+		iPos.iParaElement=paraElement;
+		}
+	TInt startOfPhrase=iPos.iParaElementOffset-iPos.iPhraseElementOffset;
+// the offset within the paragraph.
+	TInt paraElementOffset=aCharPos-startOfPara;
+	iPos.iParaElementOffset=paraElementOffset;
+
+	if (!(para->iParaAttribs->IsShared()))
+		{// Find phrase & offset within it.
+		TInt lastPhraseLength=-1;  // Record phrase length in case left match required.
+		const CArrayFix<RPhraseAttribsEntry>& phraseIx=*iPhraseIx;
+		const RPhraseAttribsEntry* phrase=&phraseIx[phraseElement];
+		const RPhraseAttribsEntry* end=NULL;
+		for (;;)
+			{	// Find the phrase in the paragraph...
+			len=phrase->Length();
+			if (paraElementOffset<(startOfPhrase+len))
+				break;
+			startOfPhrase+=len;
+			lastPhraseLength=len;
+			if (end==NULL)
+				end=phraseIx.End(phraseElement);
+			phraseElement++;
+			if (++phrase<end)
+				continue;
+			phrase=&phraseIx[phraseElement];
+			end=phraseIx.End(phraseElement);
+			}//...and the offset within this.
+		// Check now for match left.
+		if ((aMode==EScanToPositionMatchLeft) && lastPhraseLength>=0 && paraElementOffset==startOfPhrase)
+			{// Match to the left most phrase if at the start of a phrase.
+			phraseElement--;
+			iPos.iPhraseElementOffset=lastPhraseLength;
+			}
+		else
+			iPos.iPhraseElementOffset=paraElementOffset-startOfPhrase;
+		}
+	else
+		{
+		__ASSERT_DEBUG(iPos.iParaBasePhraseElement==phraseElement,Panic(EDebug));
+		}
+	iPos.iPhraseElement=phraseElement;
+	iPos.iDocPos=aCharPos;
+	if (aLastUsed)
+		*aLastUsed=iPos;
+	}
+
+
+TBool CRichTextIndex::FirstPhraseOfParagraph()const
+// Interogates the current internal position record.
+// Return ETrue if the current phrase element is the first phrase
+// of specific character format in the current paragraph;
+// Otherwise return EFalse.
+//
+	{return iPos.iPhraseElement==iPos.iParaBasePhraseElement;}
+
+
+TInt CRichTextIndex::CurrentPhraseLength()const
+// Return the length of the current phrase, where the current
+// phrase is specified by the state of the internal position record.
+//
+	{
+	if ((*iParaIx)[iPos.iParaElement].iParaAttribs->IsShared())
+		return (*iParaIx)[iPos.iParaElement].iLength;
+	else
+		return (*iPhraseIx)[iPos.iPhraseElement].Length();
+	}
+
+
+TInt CRichTextIndex::CurrentPhraseOffset()const
+// Returns the offset within the current phrase, where the current
+// phrase is specified by the state of the internal position record.
+//
+	{
+	if ((*iParaIx)[iPos.iParaElement].iParaAttribs->IsShared())
+		return iPos.iParaElementOffset;  // only 1 phrase in para.
+	else
+		return iPos.iPhraseElementOffset;
+	}
+
+
+void CRichTextIndex::GetCurrentRecords(TCurrentIndexRecords& aRecord)const
+// Package the phrase and paragraph index records that apply to the
+// current paragraph and return this package.  It is assumed that
+// the caller has already set the internal position record to a valid state.
+//
+	{
+	aRecord.iParaEntry=&(*iParaIx)[iPos.iParaElement];
+	aRecord.iParaAttribs=aRecord.iParaEntry->iParaAttribs;
+	if (aRecord.iParaAttribs->IsShared())
+		aRecord.iPhrase=NULL;
+	else
+		aRecord.iPhrase=&((*iPhraseIx)[iPos.iPhraseElement]);
+	}
+
+
+void CRichTextIndex::GetPhraseFormat(TCurrentIndexRecords& aCurrent,TCharFormatX& aFormat,TCharFormatXMask& aMask,
+									 CCharFormatLayer*& aCharBase)const
+// Fills aFormat and aMask with the character formatting information of the current record.
+// aCharBase is set to the basedOn link if present.
+// Encapsulates the concepts of the specific phrase index, and the constant character format.
+// Only senses the format in the layer, does *NOT* perform a SenseEffective.
+//
+	{
+	CCharFormatLayer* charFormatLayer=NULL;
+	if (aCurrent.iPhrase)
+		{// Specific character formatting held by phrase index.
+		charFormatLayer=aCurrent.iPhrase->CharFormat();
+		}
+	else
+		{// Constant character formatting held in the para attribs
+		charFormatLayer=aCurrent.iParaAttribs->iCharFormat;
+		}
+	charFormatLayer->Sense(aFormat,aMask);
+	aCharBase=(CCharFormatLayer*)(charFormatLayer->SenseBase());
+	}
+
+
+TInt CRichTextIndex::OwningParagraph(TInt aPos,TLogicalPosition* aLastUsed/*=NULL*/)const
+// Return the paragraph element number that contains character position aPos.
+// Assumes the caller has validated aPos.  Alters the internal record position.
+//
+	{
+	((CRichTextIndex*)this)->ScanToPosition(aPos,EScanToPositionMatchLeft,aLastUsed);
+	return iPos.iParaElement;
+	}
+
+
+void CRichTextIndex::SplitPhraseL(TInt aSplitPos)
+// Splits the phrase at the offset aSplitPos, creating a new phrase
+// which is filled with the split part of the current phrase, includig aSplitPos.
+// The character format applied to the new phrase is the format of the phrase from which it has been split.
+// The resulting new phrase is inserted into the phrase index immediately following the
+// current element.  If aSplitPos is already at a phrase boundary, then no split is performed.
+// (This means that a picture phrase in effect can never be split).
+//
+	{
+	SetPhraseSplit(EFalse);
+	ScanToPosition(aSplitPos,EScanToPositionAbsolute);
+	if (iPos.iPhraseElementOffset==0)
+		return;  // aSplitPos on a phrase boundary; urgo no split.
+	TCurrentIndexRecords current; GetCurrentRecords(current);
+// ASSERT: This function set can only be called on CParaAttribs that specific char format.
+	__ASSERT_ALWAYS(current.iPhrase!=NULL,Panic(ESplitPhraseCalledOnSharedPara));
+	DoSplitPhraseL(*current.iPhrase,iPos.iPhraseElementOffset,current.iParaAttribs);
+	}
+
+
+void CRichTextIndex::SplitPhraseL(TInt aPhraseElement,TInt aPhraseOffset,CParaAttribs* aParaAttribs)
+// Splits the specified phrase at the offset aOffsetInPhrase, creating a new phrase
+// which is filled with the split part of the current phrase, includig the split pos.
+// The character format applied to the new phrase is the format of the phrase from which it has been split.
+// The resulting new phrase is inserted into the phrase index immediately following the
+// current element.  If the split pos is already at a phrase boundary, then no split is performed.
+// (This means that a picture phrase in effect can never be split).
+//
+	{
+	SetPhraseSplit(EFalse);
+	RPhraseAttribsEntry& phrase=(*iPhraseIx)[aPhraseElement];
+	if ((aPhraseOffset>0) && (aPhraseOffset<phrase.Length()))
+		{// Not at a phrase boundary so split the current phrase.
+		DoSplitPhraseL(phrase,aPhraseOffset,aParaAttribs);
+		}
+	}
+
+
+void CRichTextIndex::DoSplitPhraseL(RPhraseAttribsEntry& aCurrentPhrase,TInt aPhraseOffset,CParaAttribs* aParaAttribs)
+// Splits the specified phrase, creating a new phrase of the same character format.
+// This new phrase is inserted into the phrase index immediately following the current one.
+//
+	{
+// ASSERT: Cannot split a picture phrase.
+	__ASSERT_DEBUG(!aCurrentPhrase.IsPicturePhrase(),Panic(ESplitPhraseCalledOnPicturePhrase));
+	CCharFormatLayer* layer=CCharFormatLayer::NewCopyBaseL(aCurrentPhrase.CharFormat());
+	CleanupStack::PushL(layer);
+	RPhraseAttribsEntry newPhrase(layer,aCurrentPhrase.Length()-aPhraseOffset);
+	iPhraseIx->InsertL(iPos.iPhraseElement+1,newPhrase);
+	CleanupStack::Pop();
+	//
+	// InsertL() has invalidated the current internal position record.
+	iPhraseIx->At(iPos.iPhraseElement).SetLength(aPhraseOffset);
+	SetPhraseSplit(ETrue);
+	aParaAttribs->iPhraseCount++;
+	}
+
+
+TBool CRichTextIndex::HasMarkupData(const CFormatLayer* aGlobalParaFormatLayer)const
+// Returns ETure if this rich text instance has any specific markup,
+// otherwise returns EFalse.
+// The presence of specific markup is indicated by the following...
+// 1) Style list is present (if style table is owned by the rich text)
+// 2) Any phrase index content
+// 3) >1 shared para attribs
+//   or
+// 3) 1 shared para attribs that has specific markup
+// 4) any paragraph is based on a style other than normal (the global paraformatlayer)
+//
+	{
+	TInt phraseCount=PhraseCount();
+	if (phraseCount>0)
+		return ETrue;
+	//
+	TInt sharedParaCount=SharedParaCount(this);
+	__ASSERT_ALWAYS(sharedParaCount>=1 || (sharedParaCount==0 && phraseCount>0),Panic(ERichTextIndexIntegrityErr));
+	if (sharedParaCount>1)
+		return ETrue;
+	const CParaAttribs* paraAttribs=iSharedParaQueHead.First();
+	if (!paraAttribs->iParaFormat->IsEmpty())
+		return ETrue;
+	if (!paraAttribs->iCharFormat->IsEmpty())
+		return ETrue;
+	if (paraAttribs->iParaFormat->SenseBase()!=aGlobalParaFormatLayer)
+		return ETrue;
+	return EFalse;
+	}
+
+
+TInt CRichTextIndex::SharedParaCount(const CRichTextIndex* aSource)const
+// Return a count of the number of shared paragraph formats present
+// in the specified object.
+//
+	{
+	TInt sharedParaCount = 0;
+	TDblQueIter<CParaAttribs> iterator( MUTABLE_CAST(TDblQue<CParaAttribs>&, aSource->iSharedParaQueHead) );
+	while ( iterator++ != NULL )
+		sharedParaCount++;
+	return sharedParaCount;
+	}
+
+
+void CRichTextIndex::AppendTakingSolePictureOwnershipL(const CRichTextIndex* aSource,const TGlobalLayerInfoAppend& aGlobalLayerInfo)
+// No paragraph style information is appended.
+//
+	{
+	CancelInsertCharFormat();
+	CONST_CAST(CRichTextIndex*,aSource)->CancelInsertCharFormat();
+
+	TInt origParaCount=ParagraphCount();
+	TInt origPhraseCount=iPhraseIx->Count();
+	TRAPD(ret,
+		AppendParaIndexL(aSource,aGlobalLayerInfo);
+		AppendPhraseIndexL(aSource,aGlobalLayerInfo);
+		);
+	if (ret!=KErrNone)
+		{
+		RemoveFromPhraseIx(origPhraseCount,iPhraseIx->Count()-origPhraseCount);	// remove any added phrases etc.
+		RbRemoveInsertedParaAttribsEntries(origParaCount,ParagraphCount()-origParaCount);	// remove any added paragraphs etc.
+		NormalizeSharedList();		// remove any added shared paragraph attributes
+		User::Leave(ret);
+		}
+
+	__TEST_INVARIANT;
+	}
+
+
+void CRichTextIndex::AppendParaIndexL(const CRichTextIndex* aSource,const TGlobalLayerInfoAppend& aGlobalLayerInfo)
+//
+	{
+	CRichTextStoreMap<CParaAttribs>* map=CRichTextStoreMap<CParaAttribs>::NewLC(SharedParaCount(aSource));
+
+	AppendSharedFormatsL(*map,aSource,aGlobalLayerInfo);
+
+	// Extend para index by required amount
+	TInt originalParaCount=iParaIx->Count();
+	TInt requiredParaCount=aSource->iParaIx->Count();
+	iParaIx->AppendL(TParaAttribsEntry(),requiredParaCount);
+
+	for (TInt ii=0;ii<requiredParaCount;ii++)
+		{// Copy the paragraph data for each of the appended paragraphs.
+		const TParaAttribsEntry& sParaEntry=(*aSource->iParaIx)[ii];
+		const CParaAttribs* sParaAttribs=sParaEntry.iParaAttribs;
+		CParaAttribs* tParaAttribs;
+		if (sParaAttribs->IsShared())
+			{
+			tParaAttribs=map->Item(sParaAttribs);
+			__ASSERT_DEBUG(tParaAttribs!=NULL,Panic(ESharedFormatsMapIntegrityError));
+			tParaAttribs->iRefCount++;
+			}
+		else
+			{// Have to build up the specific para attribs
+			tParaAttribs=CParaAttribs::NewL(sParaAttribs->iParaFormat);  // sets iRefCount=0, copies the format layer
+			tParaAttribs->iParaFormat->SetBase(aGlobalLayerInfo.iAggParaFormatLayer);
+			tParaAttribs->iPhraseCount=sParaAttribs->iPhraseCount;
+			}
+		TParaAttribsEntry& tParaEntry=(*iParaIx)[originalParaCount+ii];
+		tParaEntry.iLength=sParaEntry.iLength;
+		tParaEntry.iParaAttribs=tParaAttribs;
+		
+		// tParaAttribs is attached to CRichTextIndex::iParaIx, and will be
+		// released in destructor CRichTextIndex::~CRichTextIndex().
+		// To prevent Coverity from reporting defect, add a comment:
+		// coverity[memory_leak]
+		}
+
+	CleanupStack::PopAndDestroy();  // map
+	}
+
+
+void CRichTextIndex::AppendSharedFormatsL(CParaAttribsMap& aMap,const CRichTextIndex* aSource,
+											const TGlobalLayerInfoAppend& aGlobalLayerInfo)
+// A map is kept, that for each original format specifies the corresponding new one that appended
+// paragraphs should use.
+//
+	{
+	TDblQueIter<CParaAttribs> iterator(MUTABLE_CAST(TDblQue<CParaAttribs>&,aSource->iSharedParaQueHead));
+	CParaAttribs* currentSharedPara;
+	while ((currentSharedPara=iterator++)!=NULL)
+		{
+		__ASSERT_DEBUG(currentSharedPara->IsShared(),User::Invariant());
+
+		CParaFormatLayer* sPl=currentSharedPara->iParaFormat;
+		CCharFormatLayer* sCl=currentSharedPara->iCharFormat;
+		sPl->SetBase(aGlobalLayerInfo.iAggParaFormatLayer);  // alter the original so that the following GetParaAttribsL() call will
+		sCl->SetBase(aGlobalLayerInfo.iAggCharFormatLayer);  // match based on our global format layers, not those of aSource's.
+		CParaAttribs* newParaAttribs=FindSharedParaAttribs(*sPl,*sCl);
+		sPl->SetBase(aGlobalLayerInfo.iComParaFormatLayer);  // set the global format layers back again, cos we don't want to
+		sCl->SetBase(aGlobalLayerInfo.iComCharFormatLayer);  // corrupt aSource.
+		if (newParaAttribs==NULL)
+			{
+			newParaAttribs=CParaAttribs::NewL(currentSharedPara);
+			// change the based-on links (they are copied in the construction)
+			newParaAttribs->iParaFormat->SetBase(aGlobalLayerInfo.iAggParaFormatLayer);
+			newParaAttribs->iCharFormat->SetBase(aGlobalLayerInfo.iAggCharFormatLayer);
+			iSharedParaQueHead.AddLast(*newParaAttribs);
+			}
+		newParaAttribs->iRefCount--;  // we have not yet linked incoming para - taken out no shares just yet
+		aMap.Bind(currentSharedPara,newParaAttribs);
+		}
+	}
+
+void CRichTextIndex::AppendPhraseIndexL(const CRichTextIndex* aSource,const TGlobalLayerInfoAppend& aGlobalLayerInfo)
+//
+	{
+	TInt originalPhraseCount=iPhraseIx->Count();
+	TInt requiredPhraseCount=aSource->iPhraseIx->Count();
+
+	// Extend phrase index by required amount
+	iPhraseIx->AppendL(RPhraseAttribsEntry(),requiredPhraseCount);
+
+	CArrayFixFlat<TInt>* pictureMap=new(ELeave) CArrayFixFlat<TInt>(16);
+	CleanupStack::PushL(pictureMap);
+
+	for (TInt jj=0;jj<requiredPhraseCount;jj++)
+		{
+		RPhraseAttribsEntry& sPhrase=(*aSource->iPhraseIx)[jj];
+		CCharFormatLayer* sCharFormatLayer=sPhrase.CharFormat();
+		CCharFormatLayer* charLayer=CCharFormatLayer::NewL(sCharFormatLayer);
+		charLayer->SetBase(aGlobalLayerInfo.iAggCharFormatLayer);
+		RPhraseAttribsEntry& tPhrase=(*iPhraseIx)[jj+originalPhraseCount];
+		if (!sPhrase.IsPicturePhrase())
+			tPhrase=RPhraseAttribsEntry(charLayer,sPhrase.Length());
+		else
+			{
+			TPictureHeader hdr;
+			hdr=sPhrase.PictureHeader();  // copy the header from the source
+			if (hdr.iPicture.IsPtr())
+				hdr.iPicture=NULL;		  // pic.ownership transferred later.
+			CleanupStack::PushL(charLayer);
+			TBool ownershipTaken(EFalse);
+			CPicturePhrase* picPhrase=CPicturePhrase::NewL(hdr,charLayer,ownershipTaken);
+			CleanupStack::Pop();  // charLayer
+			tPhrase=RPhraseAttribsEntry(picPhrase);
+			iPictureCount++;
+			pictureMap->AppendL(jj);
+			}
+		}
+
+	// transfer pictures now
+	for (TInt kk=pictureMap->Count();--kk>=0;)
+		{
+		TInt jj=pictureMap->At(kk);
+		TPictureHeader* sHeader=(*aSource->iPhraseIx)[jj].PictureHeaderPtr();
+		__ASSERT_DEBUG(sHeader,User::Invariant());
+		if (sHeader->iPicture.IsPtr())
+			{	// transfer picture to us
+			TPictureHeader* tHeader=(*iPhraseIx)[jj+originalPhraseCount].PictureHeaderPtr();
+			__ASSERT_DEBUG(tHeader,User::Invariant());
+			tHeader->iPicture=sHeader->iPicture.AsPtr();
+			sHeader->iPicture=NULL;
+			}
+		}
+
+	CleanupStack::PopAndDestroy();	// pictureMap
+	}
+
+
+void CRichTextIndex::AppendParagraphL(const CParaFormatLayer* aGlobalParaFormatLayer,
+									  const CCharFormatLayer* aGlobalCharFormatLayer,
+									  TInt aReplicas)
+// Append aReplicas empty paragraphs, the format of which is based on the
+// global format layers.
+//
+	{
+	__TEST_INVARIANT;
+
+	// add the shared para format record
+	CParaAttribs* paraAttribs=CParaAttribs::NewL(aGlobalParaFormatLayer,aGlobalCharFormatLayer);
+	paraAttribs->iParaFormat->SetBase(aGlobalParaFormatLayer);  // reset the base properly
+	paraAttribs->iCharFormat->SetBase(aGlobalCharFormatLayer);  // reset the base properly.
+	CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,paraAttribs));
+	CParaAttribs* paUsed=GetParaAttribsL(aGlobalParaFormatLayer,aGlobalCharFormatLayer,paraAttribs);
+	// guaranteed not to leave as 3rd argument specified.
+	// The refCount of paUsed is incremented here by one share.
+	//
+	// add the paragraph records
+	TParaAttribsEntry paraEntry(1,paUsed);
+	iParaIx->AppendL(paraEntry,aReplicas);
+	if (paUsed!=paraAttribs)
+		CleanupStack::PopAndDestroy();
+	else
+		CleanupStack::Pop();
+	//
+	// set the ref count of paUsed
+	paUsed->iRefCount+=(aReplicas-1);  // compensate for already adding 1 share.
+
+	__TEST_INVARIANT;
+	}
+
+
+TParaAttribsEntry::TParaAttribsEntry():
+	iLength(0),
+	iParaAttribs(NULL)
+	{
+	}
+
+
+TParaAttribsEntry::TParaAttribsEntry(TInt aLength,CParaAttribs* aParaAttribs):
+	iLength(aLength),
+	iParaAttribs(aParaAttribs)
+	{
+	}
+
+
+CParaAttribs* CParaAttribs::NewL(const CParaFormatLayer* aParaLayer,const CCharFormatLayer* aCharLayer)
+// Returns a handle to a new instance of this class.
+// Creates a CParaAttribs with constant character formatting.
+//
+	{
+	CParaAttribs* self=new(ELeave) CParaAttribs();
+	CleanupStack::PushL(self);
+	self->iParaFormat=CParaFormatLayer::NewCopyBaseL(aParaLayer);
+	// iParaFormat will be released in destructor.
+	// To prevent Coverity from reporting defect, add a comment:
+	// coverity[leave_without_push]
+	self->iCharFormat=CCharFormatLayer::NewCopyBaseL(aCharLayer);
+	CleanupStack::Pop();
+	self->iRefCount=EPrimeSharedCount;
+	return self;
+	}
+
+
+CParaAttribs* CParaAttribs::NewL(const CParaFormatLayer* aParaLayer)
+// Returns a handle to a new instance of this class.
+// Creates a CParaAttribs for specific character formatting.
+//
+	{
+	CParaAttribs* self=new(ELeave) CParaAttribs();
+	CleanupStack::PushL(self);
+	self->iParaFormat=CParaFormatLayer::NewCopyBaseL(aParaLayer);
+	CleanupStack::Pop();
+	self->iRefCount=EPrimeNonSharedCount;
+	self->iPhraseCount=1;
+	return self;
+	}
+
+
+CParaAttribs* CParaAttribs::NewL(const CParaAttribs* aParaAttribs)
+	{
+	return NewL(aParaAttribs->iParaFormat,aParaAttribs->iCharFormat);
+	}
+
+
+CParaAttribs::CParaAttribs():
+	iRefCount(-1)  // Ensures Destruct works correctly when called on a semi-initialised object.
+	{
+	}
+
+
+void CParaAttribs::Release()
+// Release a share on this CParaAttribs.
+// If after this, no shares remain, destroy this CParaAttribs.
+//
+	{
+	iRefCount--;
+	if (iRefCount<=0)
+		delete this;
+	}
+
+
+void CParaAttribs::Release(TInt aCount)
+// Release aCount number of shares of this CParaAttribs.
+// If after this, no shares remain, destroy this CParaAttribs.
+//
+	{
+	iRefCount-=aCount;
+	if (iRefCount<=0)
+		delete this;
+	}
+
+
+CParaAttribs::~CParaAttribs()
+// Release the memory associated with this object.
+//
+	{
+	delete iParaFormat;
+	if (iRefCount==0)
+		{// Constant character formatting - in the shared list.
+		delete iCharFormat;
+		link.Deque();  // Remove this para attribs from the shared list.
+		}
+	}
+
+
+TInt CParaAttribs::PhraseCount()const
+// Return a count of the number of phrases in this para attribs.
+//
+	{return (iRefCount>=1)?1:iPhraseCount;}
+
+
+DLLEXPORT_C void RPhraseAttribsEntry::__DbgTestInvariant()const
+// Class invariants.
+//
+	{
+#ifdef _DEBUG
+// ASSERT: iLength is +ve (applying to character formatting, or	is set to indicate a picture phrase.
+	__ASSERT_DEBUG(iLength>=0 || IsPicturePhrase(),User::Invariant());
+#endif
+	}
+
+
+RPhraseAttribsEntry::RPhraseAttribsEntry():
+	iLength(0),
+	iCharFormat(NULL)
+	{
+	}
+
+
+RPhraseAttribsEntry::RPhraseAttribsEntry(CCharFormatLayer* aCharFormat,TInt aLength):
+	iLength(aLength),
+	iCharFormat(aCharFormat)
+	{
+	}
+
+
+RPhraseAttribsEntry::RPhraseAttribsEntry(CPicturePhrase* aPicturePhrase):
+	iLength(EPictureIndicator),
+	iPicturePhrase(aPicturePhrase)
+	{
+	}
+
+
+void RPhraseAttribsEntry::AssignAndRelease(const RPhraseAttribsEntry& aPhrase)
+// Assign the state of the specified object to this,
+//
+	{
+	iLength=aPhrase.iLength;
+	iCharFormat=aPhrase.iCharFormat;  // both union members share the same address space, so this is fine.
+	}
+
+
+void RPhraseAttribsEntry::Discard()
+// Free storage.
+//
+	{
+	__TEST_INVARIANT;
+
+	if (iLength==EPictureIndicator)
+		delete iPicturePhrase;
+	else
+		delete iCharFormat;
+	}
+
+
+void RPhraseAttribsEntry::ExternalizeL(RWriteStream& aStream)const
+// Save this phrase into aStream.
+//
+	{
+	TUint8 picIndicator=(TUint8)(IsPicturePhrase()!=EFalse);
+	aStream.WriteUint8L(picIndicator);
+	aStream.WriteInt32L(Length());
+	aStream<< *CharFormat();
+	if ((TBool)picIndicator)
+		aStream<< *PictureHeaderPtr();
+	}
+
+
+CCharFormatLayer* RPhraseAttribsEntry::CharFormat()const
+ // Returns a pointer the CCharFormatLayer of this phrase.
+ //
+ 	{return (iLength==EPictureIndicator)?iPicturePhrase->iCharFormat:iCharFormat;}
+
+
+void RPhraseAttribsEntry::SetLength(TInt aLength)
+// Sets the phrase length.
+//
+	{
+	__TEST_INVARIANT;
+
+	iLength=aLength;
+	}
+
+
+void RPhraseAttribsEntry::AdjustLength(TInt aIncrement)
+// Adjusts the length of the phrase by the signed value aIncrement.
+//
+	{
+// ASSERT: The length of a picture phrase may only be altered by deleting it, in which case
+//			the only adjustment made will be an increment of -1 (EPictureIndicator).
+	__ASSERT_DEBUG(!IsPicturePhrase() || (IsPicturePhrase() && aIncrement==EPictureIndicator)
+									  || (IsPicturePhrase() && aIncrement==0)
+									  ,Panic(EModifiedPicturePhraseLength));
+	TInt len=iLength;
+	iLength=(len==EPictureIndicator)
+		? len-aIncrement
+		: len+aIncrement;
+	}
+
+
+TInt RPhraseAttribsEntry::GetPictureSizeInTwips(TSize& aSize)const
+// If this is a picture phrase, write the size of the picture to aSize,
+// otherwise return KErrNotFound.
+//
+	{
+	if (!IsPicturePhrase())
+		return KErrNotFound;
+	if (iPicturePhrase->iPicHdr.iPicture.IsPtr() && iPicturePhrase->iPicHdr.iPicture.AsPtr())
+		iPicturePhrase->iPicHdr.iPicture->GetSizeInTwips(aSize);
+	else
+		aSize=iPicturePhrase->iPicHdr.iSize;
+	return KErrNone;
+	}
+
+
+TPictureHeader RPhraseAttribsEntry::PictureHeader()const
+// Return the picture header describing the picture at character position aPos.
+// If there is no picture at character position aPos, a default picture header is returned.
+//
+	{
+	return (IsPicturePhrase())
+		? iPicturePhrase->iPicHdr
+		: TPictureHeader();
+	}
+
+
+const CPicture* RPhraseAttribsEntry::PictureHandleL(const MPictureFactory* aFactory,
+													const MRichTextStoreResolver* aResolver,
+													TInt aPos,
+													MLayDoc::TForcePictureLoad aForceLoad)const
+// For a text phrase returns NULL.
+// For a picture phrase returns a pointer to the picture object itself.
+// May leave since loading of pictures is deferred until this point,
+// ie, when the picture is required.
+// Also returns NULL if the picture is not in memory *and* TForcePictureLoad is false.
+//
+	{
+	if (iLength!=EPictureIndicator)
+		return NULL;
+	else
+		{// Check if the picture is in memory
+		CPicture* handle=NULL;
+		if (iPicturePhrase->iPicHdr.iPicture.IsPtr())
+			handle=iPicturePhrase->iPicHdr.iPicture;
+		else
+			{// Check if the picture should be loaded from persistent storage
+			if (aForceLoad==MLayDoc::EForceLoadTrue)  // picture not in memory, so load it.
+				{
+				if (aResolver==NULL || aFactory==NULL)
+					return NULL;
+				const CStreamStore& store=aResolver->StreamStoreL(aPos);
+				aFactory->NewPictureL(iPicturePhrase->iPicHdr,store);
+				handle=iPicturePhrase->iPicHdr.iPicture;
+				}
+			}
+		return handle;
+		}
+	}
+
+
+TPictureHeader* RPhraseAttribsEntry::PictureHeaderPtr()const
+// For a [text] phrase returns NULL.
+// For a picture phrase returns a pointer to the picture header.
+//
+	{return (iLength==EPictureIndicator)? &iPicturePhrase->iPicHdr:NULL;}
+
+
+CCharFormatLayer* RPhraseAttribsEntry::ReleaseCharFormatLayerOwnership()
+// Return a handle to the character format layer, then set this handle to it to NULL.
+// thus giving up ownership of the object.
+//
+	{
+	CCharFormatLayer*& layer=(iLength==EPictureIndicator)?iPicturePhrase->iCharFormat:iCharFormat;
+	CCharFormatLayer* charFormatLayer=layer;
+	layer=NULL;
+	return charFormatLayer;
+	}
+
+
+TBool RPhraseAttribsEntry::IsIdentical(const RPhraseAttribsEntry& aPhrase)const
+// Returns ETrue if this phrase is identical to the specified phrase.
+// otherwise returns EFalse.
+// The 2 are equal only if they are both non-zero length text phrases, and of identical character format.
+//
+	{
+	if (iLength<=0)				// picture or zero-length
+		return EFalse;
+	if (aPhrase.iLength<=0)		// picture or zero-length
+		return EFalse;
+	return iCharFormat->IsIdentical(aPhrase.iCharFormat,EFalse); // EFalse=do not compare based-on link
+	}
+
+
+CPicturePhrase* CPicturePhrase::NewL(const TPictureHeader& aPicHdr,TCharFormatX& aFormat,
+									 TCharFormatXMask& aMask,CCharFormatLayer* aCharBase,
+									 TBool& aPictureOwnershipTaken)
+	{
+	CPicturePhrase* self=new(ELeave) CPicturePhrase(aPicHdr,aPictureOwnershipTaken);
+	CleanupStack::PushL(self);
+	self->ConstructL(aFormat,aMask,aCharBase);
+	CleanupStack::Pop();
+	return self;
+	}
+
+
+// The charLayer is assumed to have a valid based-on link already set.
+// Called as part of the rich text index Internalize only.
+CPicturePhrase* CPicturePhrase::NewL(const TPictureHeader& aPicHdr,
+                                     CCharFormatLayer* aCharLayer,
+                                     TBool& aPictureOwnershipTaken)
+	{
+	return new(ELeave) CPicturePhrase(aPicHdr,aCharLayer,aPictureOwnershipTaken);
+	}
+
+
+CPicturePhrase::CPicturePhrase(const TPictureHeader& aPicHdr,TBool& aPictureOwnershipTaken):
+	iPicHdr(aPicHdr)
+	{
+   	aPictureOwnershipTaken=ETrue;
+	}
+
+
+CPicturePhrase::CPicturePhrase(const TPictureHeader& aPicHdr,
+                               CCharFormatLayer* aCharLayer,
+                               TBool& aPictureOwnershipTaken):
+	iCharFormat(aCharLayer),
+	iPicHdr(aPicHdr)
+	{
+	aPictureOwnershipTaken=ETrue;
+	}
+
+
+void CPicturePhrase::ConstructL(TCharFormatX& aFormat,TCharFormatXMask& aMask,CCharFormatLayer* aCharBase)
+	{
+	iCharFormat=CCharFormatLayer::NewL(aFormat,aMask);
+	iCharFormat->SetBase(aCharBase);
+	}
+
+
+CPicturePhrase::~CPicturePhrase()
+	{
+	iPicHdr.DeletePicture();
+	delete iCharFormat;
+	}
+
+TLogicalPosition::TLogicalPosition():
+	iDocPos(0),
+	iParaElement(0),
+	iParaElementOffset(0),
+	iPhraseElement(0),
+	iPhraseElementOffset(0)
+	{
+	}
+
+
+void TLogicalPosition::Clear()
+// Reset this logical position record.
+//
+	{
+	*this=TLogicalPosition();
+	iParaBasePhraseElement=0;
+	}