textrendering/texthandling/stext/TXTINDEX.CPP
changeset 0 1fb32624e06b
child 16 748ec5531811
equal deleted inserted replaced
-1:000000000000 0:1fb32624e06b
       
     1 /*
       
     2 * Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description: 
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 #include <e32std.h>
       
    20 #include <e32base.h>
       
    21 
       
    22 #include <gdi.h>
       
    23 #include <s32stor.h>
       
    24 #include "TXTFMLYR.H"
       
    25 #include "TXTETEXT.H"
       
    26 #include "TXTLAYDC.H"
       
    27 #include "TXTSTYLE.H"
       
    28 #include "TXTINDEX.H"
       
    29 
       
    30 #ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
       
    31 #include "TXTFMLYR_INTERNAL.H"
       
    32 #include "TXTSTYLE_INTERNAL.H"
       
    33 #endif
       
    34 
       
    35 TGlobalLayerInfoAppend::TGlobalLayerInfoAppend()
       
    36 	: iAggParaFormatLayer(NULL),iAggCharFormatLayer(NULL),iComParaFormatLayer(NULL),iComCharFormatLayer(NULL)
       
    37 	{}
       
    38 
       
    39 TGlobalLayerInfoAppend::TGlobalLayerInfoAppend(const CParaFormatLayer* aAggParaFormatLayer,const CCharFormatLayer* aAggCharFormatLayer,
       
    40 							const CParaFormatLayer* aComParaFormatLayer,const CCharFormatLayer* aComCharFormatLayer)
       
    41 	: iAggParaFormatLayer(aAggParaFormatLayer),iAggCharFormatLayer(aAggCharFormatLayer),
       
    42 	  iComParaFormatLayer(aComParaFormatLayer),iComCharFormatLayer(aComCharFormatLayer)
       
    43 	{}
       
    44 
       
    45 
       
    46 TTextFragment::TTextFragment():
       
    47 	iLength(0),
       
    48 	iPhraseCount(0)
       
    49 	{
       
    50 	}
       
    51 
       
    52 
       
    53 TCurrentIndexRecords::TCurrentIndexRecords()
       
    54 	{
       
    55 	}
       
    56 
       
    57 
       
    58 DLLEXPORT_C void CRichTextIndex::__DbgTestInvariant()const
       
    59 // Provides class invariants.  Explanations below:
       
    60 //
       
    61 	{
       
    62 #ifdef _DEBUG
       
    63 // ASSERT: Every phrase index is consistent with its corresponding paragraph.
       
    64 	TInt zeroLengthPhraseCount=0;
       
    65 	TInt maxPara=iParaIx->Count();
       
    66 	TInt numberOfReferencesToSharedList=0;
       
    67 	TInt currentPhraseElement=0;
       
    68 	for (TInt para=0;para<maxPara;para++)
       
    69 		{
       
    70 		// ASSERT: The basedOn link is valid.
       
    71 		CFormatLayer* thisLayer=(*iParaIx)[para].iParaAttribs->iParaFormat;
       
    72 		CFormatLayer* base=CONST_CAST(CFormatLayer*,thisLayer->SenseBase());
       
    73 		__ASSERT_DEBUG(base!=NULL,User::Invariant());
       
    74 		if ((*iParaIx)[para].iParaAttribs->iRefCount>0)
       
    75 			numberOfReferencesToSharedList++;
       
    76 		TInt paragraphLength=(*iParaIx)[para].iLength;
       
    77 		TInt sumOfPhraseLengths=0;
       
    78 		TInt maxPhrase=(*iParaIx)[para].iParaAttribs->PhraseCount();
       
    79 		for (TInt phrase=0;phrase<maxPhrase;phrase++)
       
    80 			{
       
    81 			if (maxPhrase>1)
       
    82 				{
       
    83 				const RPhraseAttribsEntry* phrase=&(*iPhraseIx)[currentPhraseElement];
       
    84 				CCharFormatLayer* charFormatLayer=phrase->CharFormat();
       
    85 				// ASSERT: The basedOn link is valid.
       
    86 				__ASSERT_DEBUG(charFormatLayer->SenseBase()!=NULL,User::Invariant());
       
    87 				__ASSERT_DEBUG(TInt(charFormatLayer->SenseBase())>0x1000,User::Invariant());
       
    88 				sumOfPhraseLengths+=(*iPhraseIx)[currentPhraseElement].Length();
       
    89 				if ((*iPhraseIx)[currentPhraseElement].Length()==0)
       
    90 					zeroLengthPhraseCount++;
       
    91 				currentPhraseElement++;
       
    92 				}
       
    93 			else
       
    94 				{
       
    95 				CCharFormatLayer* charFormatLayer=(*iParaIx)[para].iParaAttribs->iCharFormat;
       
    96 				// ASSERT: The basedOn link is valid.
       
    97 				__ASSERT_DEBUG(charFormatLayer->SenseBase()!=NULL,User::Invariant());
       
    98 				sumOfPhraseLengths+=(*iParaIx)[para].iLength;
       
    99 				}
       
   100 			}
       
   101 		__ASSERT_DEBUG(sumOfPhraseLengths==paragraphLength,User::Invariant());
       
   102 		}
       
   103 // ASSERT: We have no unexpected phrases left over
       
   104 	__ASSERT_DEBUG(currentPhraseElement==-1 ||
       
   105 					currentPhraseElement==iPhraseIx->Count(),User::Invariant());
       
   106 // ASSERT: There is either zero(0) or one(1) zero length phrase in the whole index
       
   107 	__ASSERT_DEBUG( (zeroLengthPhraseCount==0) ||
       
   108 					(zeroLengthPhraseCount==1 && iPendingNewPhrasePos!=EInsertCharFormatReset),
       
   109 					User::Invariant());
       
   110 // ASSERT: the number of paraEntries with paraAttribs of refCount>0 == the sum of refCounts in the shared list.
       
   111 //			or is only one less - as is set when SetInsertCharFormat is called on a shared paraAttribs.
       
   112 	TInt totalReferenceCount=0;
       
   113 	if (!iSharedParaQueHead.IsEmpty())
       
   114 		{
       
   115 		CParaAttribs* currentSharedPara;
       
   116 		TDblQueIter<CParaAttribs> iterator(((CRichTextIndex*)this)->iSharedParaQueHead);
       
   117 		while ((currentSharedPara=iterator++)!=NULL)
       
   118 			totalReferenceCount+=currentSharedPara->iRefCount;
       
   119 		}
       
   120 	__ASSERT_DEBUG((numberOfReferencesToSharedList==totalReferenceCount) ||
       
   121 				   (numberOfReferencesToSharedList==totalReferenceCount-1),User::Invariant());
       
   122 // ASSERT: iPictureCount corresponds to the number of pictures in the stored in the index.
       
   123 	TInt picCount=0;
       
   124 	TInt phraseCount=(iPhraseIx) ? iPhraseIx->Count() : 0;
       
   125 	for (TInt item=0;item<phraseCount;item++)
       
   126 		picCount+=((*iPhraseIx)[item].IsPicturePhrase())
       
   127 					? 1
       
   128 					: 0;
       
   129 	__ASSERT_DEBUG(iPictureCount==picCount,User::Invariant());
       
   130 #endif
       
   131 	}
       
   132 
       
   133 
       
   134 CRichTextIndex* CRichTextIndex::NewL(const CParaFormatLayer* aGlobalParaLayer,const CCharFormatLayer* aGlobalCharLayer,
       
   135 									 const CRichText& aText,TInt aParaGran,TInt aPhraseGran)
       
   136 // Return a handle to a new instance of this class.
       
   137 // Requires the global format layer handles on which to base the first content.
       
   138 //
       
   139 	{
       
   140 	CRichTextIndex* self=new(ELeave) CRichTextIndex(aText);
       
   141 	CleanupStack::PushL(self);
       
   142 	self->ConstructL(aGlobalParaLayer,aGlobalCharLayer,aParaGran,aPhraseGran);
       
   143 	CleanupStack::Pop();
       
   144 	return self;
       
   145 	}
       
   146 
       
   147 
       
   148 CRichTextIndex::CRichTextIndex(const CRichText& aText):
       
   149 	iText(aText),
       
   150 	iPendingNewPhrasePos(EInsertCharFormatReset),
       
   151 	iSharedParaQueHead(_FOFF(CParaAttribs,link))
       
   152 	{
       
   153 	}
       
   154 
       
   155 
       
   156 void CRichTextIndex::ConstructL(const CParaFormatLayer* aGlobalParaLayer,const CCharFormatLayer* aGlobalCharLayer,TInt aParaGran,TInt aPhraseGran)
       
   157 // Provides a fully initialised rich text index.
       
   158 // Upon construction, the index contains a single paragraph of length 1 (para.terminator).
       
   159 // This first paragraph initially has constant character formatting, ie the shared para list
       
   160 // must have one item in it.
       
   161 //
       
   162 	{
       
   163 	CParaFormatLayer* paraLayer=CParaFormatLayer::NewL();  // Creates empty layer.
       
   164 	paraLayer->SetBase(aGlobalParaLayer);  // Sets basedOn to global default.
       
   165 	CleanupStack::PushL(paraLayer);
       
   166 	CCharFormatLayer* charLayer=CCharFormatLayer::NewL();  // Creates empty layer.
       
   167 	charLayer->SetBase(aGlobalCharLayer);  // Sets basedOn to global default.
       
   168 	CleanupStack::PushL(charLayer);
       
   169 	CParaAttribs* paraAttribs=GetParaAttribsL(paraLayer,charLayer);
       
   170 	CleanupStack::PopAndDestroy(2);
       
   171 	CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,paraAttribs));
       
   172 	iParaIx=new(ELeave) CArrayFixSeg<TParaAttribsEntry>(aParaGran);
       
   173 	TParaAttribsEntry para(1,paraAttribs);
       
   174 	iParaIx->AppendL(para);
       
   175 	CleanupStack::Pop();
       
   176 	iPhraseIx=new(ELeave) CArrayFixSeg<RPhraseAttribsEntry>(aPhraseGran);
       
   177 
       
   178 	__TEST_INVARIANT;
       
   179 	}
       
   180 
       
   181 
       
   182 CRichTextIndex::~CRichTextIndex()
       
   183 // Free up all storage allocated in the rich text index.
       
   184 //
       
   185 	{
       
   186 	TInt count;
       
   187 	if (iPhraseIx)
       
   188 		{// Destroy the phrase index and its contents.
       
   189 		__ASSERT_ALWAYS(iParaIx,Panic(EPhraseIxPresentWithNoParaIx));
       
   190 		count=iPhraseIx->Count();
       
   191 		for (TInt offset=0;offset<count;offset++)
       
   192 			(*iPhraseIx)[offset].Discard();
       
   193 		}
       
   194 	delete iPhraseIx;
       
   195 	if (iParaIx)
       
   196 		{// Destroy the para index and its contents.
       
   197 		count=iParaIx->Count();
       
   198 		for (TInt offset=0;offset<count;offset++)
       
   199 			{
       
   200 			CParaAttribs* pA=(*iParaIx)[offset].iParaAttribs;
       
   201 			if (!pA->IsShared())
       
   202 				pA->Release();
       
   203 			}
       
   204 		}
       
   205 	delete iParaIx;
       
   206 	RebalanceIndex();
       
   207 	//
       
   208 	// Clear the shared list.  (Will usually be empty by now
       
   209 	// unless internalize failed after getting shared list & before getting all para data.
       
   210 	CParaAttribs* currentSharedPara;
       
   211 	TDblQueIter<CParaAttribs> iterator(iSharedParaQueHead);
       
   212 	while ((currentSharedPara=iterator++)!=NULL)
       
   213 		currentSharedPara->Release(currentSharedPara->iRefCount);
       
   214 	__ASSERT_ALWAYS(iSharedParaQueHead.IsEmpty(),Panic(ERichTextIndexIntegrityErr));
       
   215 	}
       
   216 
       
   217 
       
   218 TInt CRichTextIndex::CharPosOfParagraph(TInt& aLength,TInt aParaOffset)const
       
   219 // Returns the character position of the first character of paragraph aParaOffset,
       
   220 // where aParaOffset specifies the nth paragraph.
       
   221 // The length of this nth paragraph is written to aLength.
       
   222 //
       
   223 // If aParaOffset specifies a paragraph that does not exist, EScanEndOfData is returned.
       
   224 //
       
   225 	{
       
   226 	__TEST_INVARIANT;
       
   227 
       
   228 	if (aParaOffset>=iParaIx->Count())
       
   229 		return CPlainText::EScanEndOfData;
       
   230 	TInt pos=0,offset=0;
       
   231 	for (offset=0;offset<aParaOffset;offset++)
       
   232 		pos+=(*iParaIx)[offset].iLength;
       
   233 	aLength=(*iParaIx)[offset].iLength;
       
   234 	return pos;
       
   235 	}
       
   236 
       
   237 
       
   238 TInt CRichTextIndex::ParagraphNumberForPos(TInt& aPos)const
       
   239 // Returns the paragraph offset for the specified character position aPos.
       
   240 // aPos is in turn modified to hold the character position of the first character
       
   241 // of this paragraph.  If aPos is already on a paragraph boundary then do nothing.
       
   242 //
       
   243 	{
       
   244 	__TEST_INVARIANT;
       
   245 
       
   246 	((CRichTextIndex*)this)->ScanToPosition(aPos,EScanToPositionAbsolute);
       
   247 	aPos-=iPos.iParaElementOffset;
       
   248 	return iPos.iParaElement;
       
   249 	}
       
   250 
       
   251 
       
   252 void CRichTextIndex::DocumentChanged()const
       
   253 	{
       
   254  	MUTABLE_CAST(TLogicalPosition&,iLastUsed).Clear();
       
   255 	}
       
   256 
       
   257 
       
   258 void CRichTextIndex::DoSoloInsertL(TInt aPos,TInt aLength)
       
   259 // Updates the index following the insertion of content into a single phrase.
       
   260 // First find the phrase record the governs the insert pos.
       
   261 // (1) If the current phrase is a text phrase, then simply extend the length of the phrase record.
       
   262 // (2) If the current phrase is a picture phrase then this cannot be extended.
       
   263 // There is 1 pathological case here:
       
   264 // (i) The [current] picture phrase is the 1st phrase in the paragraph, and the insert pos
       
   265 // is before this phrase. (paragraph insert pos == 0).
       
   266 // In this case we must insert a new text phrase *before* the picture phrase.
       
   267 // (ii) In normal circumstances the inserted text is after the picture phrase.  So, if the
       
   268 // following phrase is of the same character format as the picture then we can re-use this phrase.
       
   269 // If it is not of the same character format (or is also a picture phrase),
       
   270 // then we must insert a new text phrase of the correct format immediately following the current picture phrase.
       
   271 //
       
   272 	{
       
   273 	RebalanceIndex();
       
   274 	if (!((iPendingNewPhrasePos == EInsertCharFormatReset) || (aPos == iPendingNewPhrasePos)))
       
   275 		CancelInsertCharFormat();
       
   276 
       
   277 	ScanToPosition(aPos,EScanToPositionMatchLeft);
       
   278 	TCurrentIndexRecords current; GetCurrentRecords(current);
       
   279 	if (current.iPhrase && current.iPhrase->IsPicturePhrase())
       
   280 		{// Paragraph has specific char format, and current phrase is picture phrase.
       
   281 		TInt newPhraseRequired=EFalse;
       
   282 		CCharFormatLayer* charLayer=current.iPhrase->CharFormat();
       
   283 		if (FirstPhraseOfParagraph() && iPos.iPhraseElementOffset==0)
       
   284 			newPhraseRequired=ETrue;  // Text is inserted at the start of the para behind the picture.
       
   285 		else
       
   286 			{// Check for re-use of the next text phrase.
       
   287 			iPos.iPhraseElement++;
       
   288 			iPos.iPhraseElementOffset=0;
       
   289 			const RPhraseAttribsEntry& nextPhrase=(*iPhraseIx)[iPos.iPhraseElement];
       
   290 			if (nextPhrase.IsPicturePhrase() || !nextPhrase.CharFormat()->IsIdentical(charLayer,EFalse))
       
   291 				{// Need a new phrase if the formats don't match OR the next phrase is a picture phrase.
       
   292 				newPhraseRequired=ETrue;  // Need to create a new phrase to take this insert.
       
   293 				}
       
   294 			}
       
   295 		if (newPhraseRequired)
       
   296 			{// Insert new phrase & record this fact
       
   297 			CCharFormatLayer* charFormat=CCharFormatLayer::NewCopyBaseL(charLayer);
       
   298 			RPhraseAttribsEntry newPhrase(charFormat);
       
   299 			CleanupStack::PushL(charFormat);
       
   300 			iPhraseIx->InsertL(iPos.iPhraseElement,newPhrase);
       
   301 			CleanupStack::Pop();
       
   302 			current.iParaAttribs->iPhraseCount++;
       
   303 			}
       
   304 		GetCurrentRecords(current);  // Update current records, cos used below.
       
   305 		}
       
   306 	// Extend the lengths in the index.
       
   307 	current.iParaEntry->iLength+=aLength;  // Increase length of paragraph.
       
   308 	if (current.iPhrase)
       
   309 		current.iPhrase->AdjustLength(aLength);  // Increase the length of this phrase.
       
   310 	iPos.iPhraseElementOffset+=aLength;
       
   311 	iPos.iParaElementOffset+=aLength;
       
   312 	iPos.iDocPos+=aLength;
       
   313 	}
       
   314 
       
   315 
       
   316 void CRichTextIndex::InsertL(TInt aPos,const TDesC& aBuf,const CParaFormatLayer& aGlobalParaFormatLayer)
       
   317 // Updates the index following the insertion of a descriptor of text into the document.
       
   318 // Correctly handles embedded paragraph delimiters.
       
   319 //
       
   320 	{
       
   321 	__TEST_INVARIANT;
       
   322 	TInt bufLen=aBuf.Length();
       
   323 
       
   324 	RebalanceIndex();
       
   325 	TInt paragraphCount=0;
       
   326 	const TText* start=aBuf.Ptr();
       
   327 	const TText* end=start+bufLen;
       
   328 	while (start<end)
       
   329 		{
       
   330 		if (*start++==CEditableText::EParagraphDelimiter)
       
   331 			paragraphCount++;
       
   332 		}
       
   333 	if (paragraphCount==0)
       
   334 		{
       
   335 		if (bufLen > 0)
       
   336 			{
       
   337 			DoSoloInsertL(aPos,bufLen);
       
   338 			CancelInsertCharFormat();
       
   339 			}
       
   340 		__TEST_INVARIANT;
       
   341 		return;
       
   342 		}
       
   343 	ScanToPosition(aPos,EScanToPositionMatchLeft);
       
   344 	TLogicalPosition pastePos=iPos;  // Used to adjust paragraph/phrase lengths later
       
   345 
       
   346 	if (paragraphCount>1)
       
   347 		{
       
   348 		TCurrentIndexRecords current;
       
   349 		GetCurrentRecords(current);
       
   350 		const CParaAttribs& paraAttribs=*current.iParaAttribs;
       
   351 		CCharFormatLayer* charLayer=(paraAttribs.IsShared())
       
   352 								? paraAttribs.iCharFormat
       
   353 								: (*iPhraseIx)[iPos.iPhraseElement].CharFormat();
       
   354 		CParaFormatLayer* paraLayer=paraAttribs.iParaFormat;
       
   355 		CParaAttribs* theParaAttribs=GetParaAttribsL(paraLayer,charLayer);
       
   356 		CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,theParaAttribs));
       
   357 		iParaIx->InsertL(pastePos.iParaElement,TParaAttribsEntry(0,theParaAttribs),paragraphCount-1);
       
   358 		CleanupStack::Pop();
       
   359 		theParaAttribs->iRefCount+=paragraphCount-2;	// add the extra references
       
   360 		}
       
   361 	TLogicalPosition pos;
       
   362 	TRAPD(ret,
       
   363 	SplitParagraphAtPastePosL(pastePos,pos,aGlobalParaFormatLayer));
       
   364 		if (ret!=KErrNone)
       
   365 			{
       
   366 			RbRemoveInsertedParaAttribsEntries(pastePos.iParaElement,paragraphCount-1);
       
   367 			User::Leave(ret);
       
   368 			}
       
   369 
       
   370 	if (paragraphCount>1)
       
   371 		{	// swap the entries for the split and first inserted paragraph
       
   372 		TParaAttribsEntry& paraEntry=(*iParaIx)[pastePos.iParaElement+paragraphCount-1];
       
   373 		TParaAttribsEntry& insertEntry=(*iParaIx)[pastePos.iParaElement];
       
   374 		TParaAttribsEntry temp=paraEntry;
       
   375 		paraEntry=insertEntry;
       
   376 		insertEntry=temp;
       
   377 		}// Cos weve inserted new paragraphs in front of the governing one.
       
   378 
       
   379 	// Sort out the front para
       
   380 	TParaAttribsEntry& frontPara=(*iParaIx)[pastePos.iParaElement];
       
   381 	TPtrC buf(aBuf);
       
   382 	TInt lengthOfFirstPara=ParaLengthFromBuffer(buf);
       
   383 	frontPara.iLength+=lengthOfFirstPara;  // Adjust the para length
       
   384 	buf.Set(aBuf.Right(aBuf.Length()-lengthOfFirstPara-1));
       
   385 	if (!frontPara.iParaAttribs->IsShared() && lengthOfFirstPara != 0)
       
   386 		{
       
   387 		RPhraseAttribsEntry* phrase=&(*iPhraseIx)[pastePos.iPhraseElement];
       
   388 		if (phrase->IsPicturePhrase())
       
   389 			{	// move insertion past any picture phrase
       
   390 			pastePos.iPhraseElement++;
       
   391 			pastePos.iPhraseElementOffset=0;
       
   392 			phrase=&(*iPhraseIx)[pastePos.iPhraseElement];
       
   393 			}
       
   394 		phrase->AdjustLength(lengthOfFirstPara);  // Adjust the phrase length
       
   395 		}
       
   396 	for (TInt paraItem=1;paraItem<paragraphCount;paraItem++)
       
   397 		{// For each para inserted between the fist and last
       
   398 		TParaAttribsEntry& para=(*iParaIx)[pastePos.iParaElement+paraItem];
       
   399 		TInt length=ParaLengthFromBuffer(buf)+1;
       
   400 		__ASSERT_DEBUG(length!=KErrNotFound,Panic(EInsertEmbeddedParaErr));
       
   401 		para.iLength=length;
       
   402 		buf.Set(buf.Right(buf.Length()-length));
       
   403 		}
       
   404 	// For final paragrph
       
   405 	TInt trailingTextLen=buf.Length();
       
   406 	if (trailingTextLen>0)
       
   407 		{
       
   408 		TParaAttribsEntry& backPara=(*iParaIx)[pos.iParaElement];
       
   409 		backPara.iLength+=trailingTextLen;
       
   410 		if (!backPara.iParaAttribs->IsShared())
       
   411 			{
       
   412 			RPhraseAttribsEntry& phrase=(*iPhraseIx)[pos.iPhraseElement];
       
   413 			phrase.AdjustLength(trailingTextLen);  // Adjust phrase length
       
   414 			}
       
   415 		}
       
   416 	//
       
   417 	// Now tidy up
       
   418 	if (bufLen>1 && iPendingNewPhrasePos!=EInsertCharFormatReset)
       
   419 		{
       
   420 		iPendingNewPhrasePos=aPos+(bufLen-trailingTextLen);
       
   421 		CancelInsertCharFormat();
       
   422 		}
       
   423 
       
   424 	__TEST_INVARIANT;
       
   425 	}
       
   426 
       
   427 
       
   428 void CRichTextIndex::SplitParagraphAtPastePosL(TLogicalPosition& aPastePos,TLogicalPosition& aNewPos,
       
   429 												const CParaFormatLayer& aGlobalParaFormatLayer)
       
   430 // Breaks the paragraph specified by the logical position aPastePos, inserting a paragraph delimiter.
       
   431 //
       
   432 	{
       
   433 	TInt insertPendingPos = iPendingNewPhrasePos;
       
   434 	DoSoloInsertL(aPastePos.iDocPos,1);
       
   435 	TBool insertCharFormatDeleted = EFalse;
       
   436 	if (InsertCharFormatIsActive())
       
   437 		{
       
   438 		insertCharFormatDeleted = DeleteInsertCharFormat();
       
   439 		iPendingNewPhrasePos = EInsertCharFormatReset;
       
   440 		}
       
   441 	TRAPD(ret,
       
   442 	InsertParagraphL(aPastePos.iDocPos+1,aGlobalParaFormatLayer));  // Split the current para (and maybe phrase index).
       
   443 	if (ret!=KErrNone)
       
   444 		{
       
   445 		// locate the character inserted by DoSoloInsertL() above
       
   446 		ScanToPosition(aPastePos.iDocPos,EScanToPositionAbsolute);
       
   447 		TCurrentIndexRecords current;
       
   448 		GetCurrentRecords(current);
       
   449 		current.iParaEntry->iLength--;
       
   450 		if (current.iPhrase)
       
   451 			{
       
   452 			current.iPhrase->AdjustLength(-1);  // collapse phrase by right amount
       
   453 			if (insertPendingPos!=EInsertCharFormatReset)
       
   454 				{
       
   455 				__ASSERT_DEBUG(current.iPhrase->Length()==0,User::Invariant());
       
   456 				iPendingNewPhrasePos=insertPendingPos;
       
   457 				}
       
   458 			else if (current.iPhrase->Length()==0)
       
   459 				{
       
   460 				RemoveFromPhraseIx(iPos.iPhraseElement,1);
       
   461 				current.iParaAttribs->iPhraseCount--;
       
   462 				__ASSERT_DEBUG(current.iParaAttribs->PhraseCount()>1,User::Invariant());
       
   463 				}
       
   464 			}
       
   465 		User::Leave(ret);
       
   466 		}
       
   467 	if (insertPendingPos != EInsertCharFormatReset)
       
   468 		ConsolidateAt(insertPendingPos, insertCharFormatDeleted?
       
   469 			EPositionOnly : EFollowingPhrase);
       
   470 	ScanToPosition(aPastePos.iDocPos+1,EScanToPositionMatchLeft);  // Gives us the next para.
       
   471 	aNewPos=iPos;
       
   472 	}
       
   473 
       
   474 
       
   475 TInt CRichTextIndex::ParaLengthFromBuffer(TDesC& aBuf)const
       
   476 // Returns the length of the first para found in the buffer.
       
   477 // The returned length excludes the paragraph delimiter character.
       
   478 // Returns KNotFound if there is no paragraph delimiter.
       
   479 //
       
   480 	{return aBuf.Locate(CEditableText::EParagraphDelimiter);}
       
   481 
       
   482 
       
   483 void CRichTextIndex::InsertL(TInt aPos,const TPictureHeader& aPicHdr, TBool& aPictureOwnershipTaken)
       
   484 // Updates the index following the insertion of a picture header object into the text
       
   485 // component.  This is accomplished by creating & inserting a picture phrase at the
       
   486 // relevant place.
       
   487 //
       
   488 	{
       
   489 	__TEST_INVARIANT;
       
   490 
       
   491 	RebalanceIndex();
       
   492 // ASSERT: A valid picture header, referencing a valid picture has been inserted.
       
   493 	__ASSERT_ALWAYS(aPicHdr.iPicture.IsPtr() && aPicHdr.iPicture.AsPtr()!=NULL,Panic(EInsertNullPicHdrData));
       
   494 // ASSERT: The current insert pos hasn't been changed without cancelling SetInsertCharFormat.
       
   495 	__ASSERT_ALWAYS((iPendingNewPhrasePos==EInsertCharFormatReset) || (aPos==iPendingNewPhrasePos),
       
   496 					Panic(ESetInsertCharFormatIntegrityErr));
       
   497 	if (iPendingNewPhrasePos!=EInsertCharFormatReset)
       
   498 		CancelInsertCharFormat();  // Cancel this state before inserting picture. Rebalances the index.
       
   499 	ScanToPosition(aPos,EScanToPositionMatchLeft);
       
   500 	TCurrentIndexRecords current; GetCurrentRecords(current);
       
   501 	TCharFormatX format;
       
   502 	TCharFormatXMask mask;  //...and build up its format.
       
   503 	CCharFormatLayer* baseChar;
       
   504 	GetPhraseFormat(current,format,mask,baseChar);  //...inherit format from prev. phrase.
       
   505 	// Create the picture phrase. Takes ownership of the aPicHdr.iPicture
       
   506 	CPicturePhrase* picture=CPicturePhrase::NewL(aPicHdr,format,mask,baseChar,aPictureOwnershipTaken);
       
   507 	CleanupStack::PushL(picture);
       
   508 	//New reclaimed CParaAttribs instance
       
   509 	CParaAttribs* reclaimed=RequestReclaimShareL(current.iParaAttribs,current.iParaEntry);
       
   510     //Store the old CParaAttribs instance in rollbackParaAttribsHandle
       
   511 	CParaAttribs* rollbackParaAttribsHandle=current.iParaAttribs;
       
   512 	if (reclaimed)
       
   513 		{
       
   514 		CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,reclaimed));
       
   515 		current.iParaEntry->iParaAttribs=reclaimed;  // Use this reclaimed para attribs (the new CParaAttribs instance)
       
   516 		}
       
   517 	GetCurrentRecords(current);
       
   518 //	ASSERT: The reclaim succeeded.  We must always end up with a PhraseIx-not constant char format.
       
   519 	__ASSERT_DEBUG(current.iPhrase!=NULL,Panic(EReclaimShareError));
       
   520 	TRAPD(ret1,
       
   521 	SplitPhraseL(aPos));  // Phrase may not be split if at boundary.
       
   522 
       
   523     if (ret1!=KErrNone)
       
   524         {
       
   525         RbInsertPicture(rollbackParaAttribsHandle);//Restore the old CParaAttribs instance
       
   526         User::Leave(ret1);
       
   527         }
       
   528 
       
   529 	TInt offset=(PhraseSplit())?1:0;  // Insert position of new phrase relative to current.
       
   530 	RPhraseAttribsEntry newPhrase(picture);
       
   531 	TRAPD(ret2,
       
   532 	iPhraseIx->InsertL(iPos.iPhraseElement+offset,newPhrase));
       
   533     if (ret2!=KErrNone)
       
   534         {
       
   535         RbInsertPicture(rollbackParaAttribsHandle);//Restore the old CParaAttribs instance
       
   536         User::Leave(ret2);
       
   537         }
       
   538 
       
   539 	if(reclaimed)
       
   540 	    {
       
   541         CleanupStack::Pop();//"Pop" for CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,reclaimed));
       
   542 	    }
       
   543 	CleanupStack::Pop(picture);
       
   544 	// Update counts etc. - cannot leave now.
       
   545 	current.iParaEntry->iLength+=RPhraseAttribsEntry::EPicturePhraseLength;
       
   546 	current.iParaAttribs->iPhraseCount++;  // for the picture phrase
       
   547 	iPictureCount++;
       
   548 
       
   549 	// Commit
       
   550 	if(reclaimed)
       
   551 	    {
       
   552         rollbackParaAttribsHandle->Release();  // Release hold on original shared paraAttribs.
       
   553 	    }
       
   554 
       
   555 	__TEST_INVARIANT;
       
   556 	//coverity[memory_leak]
       
   557 	}
       
   558 
       
   559 void CRichTextIndex::RbInsertPicture(CParaAttribs* aGoodParaAttribs)
       
   560 // Reinstate the original good paraAttribs.
       
   561 // Then rollback the SplitPhrase() call if it succeeded.
       
   562 //
       
   563 	{
       
   564 	(*iParaIx)[iPos.iParaElement].iParaAttribs=aGoodParaAttribs;
       
   565 	if (PhraseSplit())
       
   566 		{// Rollback the SplitPhrase()
       
   567 		TInt length=(*iPhraseIx)[iPos.iPhraseElement+1].Length();
       
   568 		RemoveFromPhraseIx(iPos.iPhraseElement+1);
       
   569 		(*iPhraseIx)[iPos.iPhraseElement].AdjustLength(length);
       
   570 		}
       
   571 	}
       
   572 
       
   573 
       
   574 // Insert a new paragraph immediately following character position aPos, fixing the length of the preceeding
       
   575 // paragraph.  The new paragraph preserves any explicit paragraph/character formatting, and is based on the
       
   576 // global layers.  (Do not need to rebalance the index here; a previous call to DoSoloInsertL accomplishes this)
       
   577 
       
   578 void CRichTextIndex::InsertParagraphL(TInt aPos,const CParaFormatLayer& aGlobalParaFormatLayer)
       
   579 	{
       
   580 	ScanToPosition(aPos,EScanToPositionMatchLeft);
       
   581 	TCurrentIndexRecords current;
       
   582 	GetCurrentRecords(current);
       
   583 	TParaAttribsEntry newPara;
       
   584 	CCharFormatLayer* charLayer;
       
   585   	if (current.iPhrase)  // entry in phrase index
       
   586 		charLayer=current.iPhrase->CharFormat();
       
   587 	else
       
   588 		charLayer=current.iParaAttribs->iCharFormat;
       
   589 	//
       
   590 	// New para format layer, based on normal, inheriting specific format
       
   591 	CParaFormatLayer* currentParaFormat=current.iParaAttribs->iParaFormat;
       
   592 	CParaFormatLayer* newParaLayer=CParaFormatLayer::NewL(currentParaFormat);
       
   593 	const CParaFormatLayer& currentStyle=STATIC_CAST(const CParaFormatLayer&,*currentParaFormat->SenseBase());
       
   594 	const TUid currentStyleType=currentStyle.Type();
       
   595 
       
   596 	// !!
       
   597 	// Only change to Normal if current style is a built-in one
       
   598 	// or we are not at the end of a heading style.
       
   599 	TBool useNormal;
       
   600 	if (currentStyleType==KNormalParagraphStyleUid)
       
   601 		useNormal=ETrue;
       
   602 	else if (currentStyleType==KUserDefinedParagraphStyleUid)
       
   603 		useNormal=EFalse;
       
   604 	else if (iPos.iParaElementOffset<=(current.iParaEntry->iLength-2))  // cos of previous call to DoSoloInsertL()
       
   605 		useNormal=EFalse;
       
   606 	else
       
   607 		useNormal=ETrue;
       
   608 	newParaLayer->SetBase((useNormal)
       
   609 		? &aGlobalParaFormatLayer
       
   610 		: &currentStyle);
       
   611 	const CCharFormatLayer* newCharBase=(useNormal)
       
   612 		? iText.GlobalCharFormatLayer()
       
   613 		: STATIC_CAST(const CParagraphStyle&,currentStyle).CharFormatLayer();
       
   614 	CleanupStack::PushL(newParaLayer);
       
   615 	//
       
   616 	if (current.iParaAttribs->IsShared())
       
   617 		{// Current para has constant char format - so the new one also has constant char format
       
   618 		// New char format layer, based on normal, inheriting specific format
       
   619 		CCharFormatLayer* newCharLayer=CCharFormatLayer::NewL(charLayer);
       
   620 		newCharLayer->SetBase(newCharBase);
       
   621 		CleanupStack::PushL(newCharLayer);
       
   622 		newPara.iParaAttribs=GetParaAttribsL(newParaLayer,newCharLayer);
       
   623 		CleanupStack::PopAndDestroy(2);  // newCharLayer/newParaLayer
       
   624 		CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,newPara.iParaAttribs));
       
   625 		iParaIx->InsertL(iPos.iParaElement+1,newPara);
       
   626 		CleanupStack::Pop();  // paraAttribs cleanup item
       
   627 		GetCurrentRecords(current);		// could be changed by InsertL() above
       
   628 		}
       
   629 	else  // Do the split myself since this para has specific character formatting.
       
   630 		{// Make the new CParaAttribs
       
   631 		CParaAttribs* newParaAttribs=CParaAttribs::NewL(newParaLayer);
       
   632 		CleanupStack::PopAndDestroy();  // newParaLayer - copy owned by newParaAttribs
       
   633 		CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,newParaAttribs));
       
   634 		//
       
   635 		// Split current phrase & insert if necessary.
       
   636 		// Split even when we are at a phrase boundary -> this introduces an z.l.p. for the insertion point
       
   637 		RPhraseAttribsEntry& insertPhrase=iPhraseIx->At(iPos.iPhraseElement);
       
   638 		TInt insertPendingPos=(iPos.iPhraseElementOffset==insertPhrase.Length()) ? aPos : EInsertCharFormatReset;
       
   639 		DoSplitPhraseL(insertPhrase,iPos.iPhraseElementOffset,current.iParaAttribs);  // Ups iPhraseCount
       
   640 		//
       
   641 		// Insert the new paragraph.
       
   642 		newPara.iParaAttribs=newParaAttribs;
       
   643 		TRAPD(ret,
       
   644 		iParaIx->InsertL(iPos.iParaElement+1,newPara));  // Inserts the new paraAttribsEntry record.
       
   645 			if (ret!=KErrNone)
       
   646 				{
       
   647 				RemoveFromPhraseIx(iPos.iPhraseElement+1,1);	// inserted by DoSplitPhraseL
       
   648 				current.iParaAttribs->iPhraseCount--;
       
   649 				User::Leave(ret);
       
   650 				}
       
   651 
       
   652 		iPendingNewPhrasePos=insertPendingPos;
       
   653 		CleanupStack::Pop();			// newParaAttribs. All OK now
       
   654 		GetCurrentRecords(current);		// could be changed by InsertL() above
       
   655 		//
       
   656 		// Calculate new paraAttribs phrase counts.
       
   657 		TInt remainder=(iPos.iPhraseElement+1)-iPos.iParaBasePhraseElement;
       
   658 		TInt newPhraseCount=current.iParaAttribs->iPhraseCount-remainder;
       
   659 		newParaAttribs->iPhraseCount=newPhraseCount;
       
   660 		current.iParaAttribs->iPhraseCount=remainder;
       
   661 
       
   662 		const CArrayFix<RPhraseAttribsEntry>& phraseIx=*iPhraseIx;
       
   663 		TInt startPhrase=iPos.iPhraseElement+1;
       
   664 		for (TInt ii=startPhrase; ii<startPhrase+newPhraseCount; ii++)
       
   665 			{
       
   666 			RPhraseAttribsEntry phrase=phraseIx[ii];
       
   667 			phrase.CharFormat()->SetBase(newCharBase);
       
   668 			}
       
   669 		//
       
   670 		// The index now reflects the correct state.
       
   671 		// Next, the efficiency thing - see if the new paras can share existing ones.
       
   672 		if (newPhraseCount==1)
       
   673 			Share(iParaIx->At(iPos.iParaElement+1),iPos.iParaBasePhraseElement+remainder);
       
   674 		if (remainder==1)
       
   675 			Share(iParaIx->At(iPos.iParaElement),iPos.iParaBasePhraseElement);
       
   676 		}
       
   677 	// Alter the length of the original paragraph and the new paragraph.
       
   678 	TInt currentLength=current.iParaEntry->iLength;
       
   679 	current.iParaEntry->iLength=iPos.iParaElementOffset;
       
   680 	((*iParaIx)[iPos.iParaElement+1]).iLength+=currentLength-current.iParaEntry->iLength;  // Alters the length of the copy of aNewPara.
       
   681 	}
       
   682 
       
   683 
       
   684 void CRichTextIndex::SetForDeleteL(TIndexDeleteInfo& aInfo,TInt aPos,TInt aLength)
       
   685 //
       
   686 	{
       
   687 	__TEST_INVARIANT;  // Do not need to RebalanceIndex(); part of defined behaviour for delete.
       
   688 
       
   689 	aInfo.iDeleteLength=aLength;
       
   690 	//
       
   691 	// Check for simple cases first
       
   692 	DocumentChanged();  // clears internal position record.
       
   693 	ScanToPosition(aPos,EScanToPositionAbsolute,&iLastUsed);
       
   694 	TCurrentIndexRecords current;
       
   695 	GetCurrentRecords(current);
       
   696 	aInfo.iStartPara=iPos.iParaElement;
       
   697 	aInfo.iEndPara=iPos.iParaElement;  // default
       
   698 	aInfo.iDeletePos=iPos;  // default
       
   699 	//
       
   700 	TInt startParaLength=current.iParaEntry->iLength;
       
   701 	TInt lengthRemainingInPara=startParaLength-iPos.iParaElementOffset;
       
   702 	if (aLength<lengthRemainingInPara)
       
   703 		{// Case is delete-from-paragraph
       
   704 		aInfo.iDeleteType=TIndexDeleteInfo::EDeleteFromParagraph;
       
   705 		return;
       
   706 		}
       
   707 	//
       
   708 	ScanToPosition(aPos+aLength,EScanToPositionMatchLeft,&iLastUsed);  // Forces endPara to be next para when just removing a para delimiter.
       
   709 	aInfo.iEndPara=iPos.iParaElement;
       
   710 //	if (iPos.iParaElementOffset==0)
       
   711 //		{// Can use delete-paragraph
       
   712 //		aInfo.iDeleteType=TIndexDeleteInfo::EDeleteParagraph;
       
   713 //		return;
       
   714 //		}
       
   715 	//
       
   716 	// Set for the general (leaving) delete.
       
   717 	GetCurrentRecords(current);
       
   718 	CParaAttribs* reclaimedEndPara=RequestReclaimShareL(current.iParaAttribs,current.iParaEntry); // does not release share.
       
   719 	TParaAttribsEntry* origEndParaEntry=current.iParaEntry;
       
   720 	CParaAttribs* origParaAttribs=current.iParaAttribs;
       
   721 	TInt endPosPhrase=iPos.iPhraseElement;
       
   722 	if (reclaimedEndPara)
       
   723 		origEndParaEntry->iParaAttribs=reclaimedEndPara;
       
   724 	// Get start para info.
       
   725 	ScanToPosition(aPos,EScanToPositionAbsolute);
       
   726 	GetCurrentRecords(current);
       
   727 	CParaAttribs* reclaimedStartPara=NULL;
       
   728 	TRAPD(ret,
       
   729 	reclaimedStartPara=RequestReclaimShareL(current.iParaAttribs,current.iParaEntry));
       
   730 		if (ret!=KErrNone)
       
   731 			{
       
   732 			if (reclaimedEndPara)
       
   733 				{
       
   734 				reclaimedEndPara->Release();
       
   735 				RemoveFromPhraseIx(endPosPhrase);
       
   736 				origEndParaEntry->iParaAttribs=origParaAttribs;
       
   737 				}
       
   738 			User::Leave(ret);
       
   739 			}
       
   740 	if (reclaimedEndPara)
       
   741 		origParaAttribs->Release();  // Release share on the original end para attribs
       
   742 	if (reclaimedStartPara)
       
   743 		{// Use the specific start  para
       
   744 		current.iParaAttribs->Release();
       
   745 		current.iParaEntry->iParaAttribs=reclaimedStartPara;
       
   746 		ScanToPosition(aPos,EScanToPositionAbsolute);  // Pick up reclaimed phrase.
       
   747 		}
       
   748 	aInfo.iDeletePos=iPos;  // internal position of aPos after any reclaim
       
   749 	// Note: iDeleteType can surely be made obsolete now? TPB 7/11/2000
       
   750 	aInfo.iDeleteType=TIndexDeleteInfo::EDeleteFromParagraph;
       
   751 	
       
   752 	/*
       
   753 	 * Pointer to memory allocated to 'reclaimedEndPara' is assigned to 
       
   754 	 * 'origEndParaEntry->iParaAttribs' on line 706. The memory will be 
       
   755 	 * released in CRichTextIndex's destructor.
       
   756 	 */ 
       
   757 	// coverity[memory_leak]
       
   758 	}
       
   759 
       
   760 
       
   761 TBool CRichTextIndex::DeleteParagraph(TInt aPos,TInt aLength)
       
   762 // Remove aCount entire paragraphs from the text.
       
   763 // Leave-safe
       
   764 // Returns EFalse indicating that no paragraphs were merged together,
       
   765 // as a result of the delete action.
       
   766 // Does NOT preserve any zero-length/insert pending state.
       
   767 //
       
   768 	{
       
   769 	__TEST_INVARIANT;  // Do not need to RebalanceIndex(); part of defined behaviour for delete.
       
   770 
       
   771 	CancelInsertCharFormat();
       
   772 	ScanToPosition(aPos,EScanToPositionAbsolute,&iLastUsed);
       
   773 
       
   774 	__ASSERT_DEBUG(iPos.iParaElementOffset==0,Panic(EDeleteParagraphInvalidStartValue));
       
   775 
       
   776 	TIndexDeleteInfo info;
       
   777 	info.iDeleteType=TIndexDeleteInfo::EDeleteParagraph;
       
   778 	info.iDeletePos=iPos;
       
   779 	info.iStartPara=iPos.iParaElement;
       
   780 	//
       
   781 	TInt documentLength=iText.DocumentLength();
       
   782 	TInt pos=(aPos+aLength>documentLength
       
   783 		? documentLength
       
   784 		: aPos+aLength);
       
   785 	ScanToPosition(pos,EScanToPositionMatchLeft,&iLastUsed);  // Forces endPara to be next para when just removing a para delimiter.
       
   786 
       
   787 	info.iEndPara=iPos.iParaElement;
       
   788 	info.iDeleteLength=aLength;
       
   789 
       
   790 	DeleteNow(info);
       
   791 	// do not want to call TidyAfterDelete()
       
   792 
       
   793 	return EFalse;
       
   794 	}
       
   795 
       
   796 
       
   797 
       
   798 void CRichTextIndex::DeleteFromParagraph(TInt aPos,TInt aLength)
       
   799 // Special case delete for removing content from within a single paragraph only.
       
   800 // Not to be used for deleting an entire paragraph or paragraphs.
       
   801 // Returns EFalse indicating that no paragraphs were merged together,
       
   802 // as a result of the delete action.
       
   803 //
       
   804 	{
       
   805 	__TEST_INVARIANT;  // Do not need to RebalanceIndex(); part of defined behaviour for delete.
       
   806 
       
   807 	ScanToPosition(aPos,EScanToPositionAbsolute);
       
   808 
       
   809 #ifdef _DEBUG
       
   810 	{
       
   811 	TCurrentIndexRecords current;
       
   812 	GetCurrentRecords(current);
       
   813 	TInt startParaLength=current.iParaEntry->iLength;
       
   814 	TInt lengthRemainingInPara=startParaLength-iPos.iParaElementOffset;
       
   815 
       
   816 	__ASSERT_ALWAYS(aLength<lengthRemainingInPara,Panic(EDeleteFromParagraphInvalidRange));
       
   817 	}
       
   818 #endif
       
   819 
       
   820 	TIndexDeleteInfo info;
       
   821 	info.iDeleteLength=aLength;
       
   822 	info.iStartPara=iPos.iParaElement;
       
   823 	info.iEndPara=iPos.iParaElement;
       
   824 	info.iDeletePos=iPos;
       
   825 	info.iDeleteType=TIndexDeleteInfo::EDeleteFromParagraph;
       
   826 
       
   827 	DoDeleteFromParagraph(info);
       
   828 
       
   829 	__TEST_INVARIANT;
       
   830 	}
       
   831 
       
   832 
       
   833 TBool CRichTextIndex::DoDeleteFromParagraph(const TIndexDeleteInfo& aInfo)
       
   834 // Delete content from *within* the boundary of a single paragraph only.0
       
   835 // Returns EFalse indicating that no paragraphs were merged together,
       
   836 // as a result of the delete action.
       
   837 //
       
   838 	{
       
   839 	iPos=aInfo.iDeletePos;
       
   840 	TInt length=aInfo.iDeleteLength;
       
   841 	DeleteParagraphText(length);
       
   842 	TidyAfterDelete(aInfo);
       
   843 
       
   844 	return EFalse;
       
   845 	}
       
   846 
       
   847 
       
   848 TBool CRichTextIndex::DeleteNow(TIndexDeleteInfo& aInfo)
       
   849 // Deletes index data corresponding the info argument.
       
   850 // Returns ETrue is 2 paragraphs are merged as a result of the delete, otherwise returns false.
       
   851 //
       
   852 	{
       
   853 	iPos=aInfo.iDeletePos;
       
   854 	TCurrentIndexRecords current;
       
   855 	GetCurrentRecords(current);
       
   856 	TInt leftToDelete=aInfo.iDeleteLength;
       
   857 	TInt charsLeftInPara=(current.iParaEntry->iLength)-(iPos.iParaElementOffset);
       
   858 	TBool doParaMerge=((iPos.iPhraseElement>0 || iPos.iPhraseElementOffset>0) && leftToDelete>=charsLeftInPara);
       
   859 	// ETrue if the 1st para has content remaining but no paragraph delimiter.
       
   860 	//
       
   861 	TBool firstParaRemoved=(FirstPhraseOfParagraph() && iPos.iPhraseElementOffset==0 && aInfo.iDeleteLength>=current.iParaEntry->iLength);
       
   862 	// ETrue if the 1st para has been *wholly* deleted.
       
   863 	//
       
   864 	DeleteParagraphText(leftToDelete);  // Delete range will be in a minimum of 1 paragraph.
       
   865 	if (aInfo.iStartPara<aInfo.iEndPara)
       
   866 		{// The delete range crosses paragraph boundaries.
       
   867 		for (TInt currentPara=aInfo.iStartPara+1;currentPara<=aInfo.iEndPara;currentPara++)
       
   868 			{
       
   869 			ScanToPosition(aInfo.iDeletePos.iDocPos,EScanToPositionAbsolute);
       
   870 			DeleteParagraphText(leftToDelete);
       
   871 			}
       
   872 		}
       
   873 	// Now tidy up
       
   874 	if (doParaMerge && !firstParaRemoved)
       
   875 		{// Merge the 2 paras together.
       
   876 		TParaAttribsEntry* paraEntry=&(*iParaIx)[aInfo.iStartPara];
       
   877 		TParaAttribsEntry* paraEntryFollowing=&(*iParaIx)[aInfo.iStartPara+1];
       
   878 		paraEntryFollowing->iLength+=paraEntry->iLength;  // Extend length of remaining para.
       
   879 		paraEntryFollowing->iParaAttribs->iPhraseCount+=paraEntry->iParaAttribs->iPhraseCount;  // Extend phrase count
       
   880 		paraEntry->iParaAttribs->Release();
       
   881 		iParaIx->Delete(aInfo.iStartPara);
       
   882 		}
       
   883 	if (aInfo.iDeleteType!=TIndexDeleteInfo::EDeleteParagraph)
       
   884 		TidyAfterDelete(aInfo);
       
   885 
       
   886 	__TEST_INVARIANT;
       
   887 	return doParaMerge;
       
   888 	}
       
   889 
       
   890 
       
   891 void CRichTextIndex::TidyAfterDelete(const TIndexDeleteInfo& aInfo)
       
   892 //
       
   893 //
       
   894 	{
       
   895 	MergePhrases(aInfo.iDeletePos.iDocPos);  // Alters internal position record.
       
   896 	TCurrentIndexRecords current;
       
   897 	GetCurrentRecords(current);  // So must get records again.
       
   898 	if (!current.iParaAttribs->IsShared())
       
   899 		{// May be able to reclaim a share from this *specific* record
       
   900 		CParaAttribs* sharedParaAttribs=RequestShare(iPos);
       
   901 		if (sharedParaAttribs!=NULL && current.iParaAttribs!=sharedParaAttribs)
       
   902 			{// Use this shared record
       
   903 			current.iParaAttribs->Release();
       
   904 			RemoveFromPhraseIx(iPos.iPhraseElement);
       
   905 			current.iParaEntry->iParaAttribs=sharedParaAttribs;
       
   906 			}
       
   907 		}
       
   908 	}
       
   909 
       
   910 
       
   911 void CRichTextIndex::Normalize(TInt aPos)
       
   912 //
       
   913 	{
       
   914 	ScanToPosition(aPos,EScanToPositionAbsolute);
       
   915 	NormalizeNow(iPos);
       
   916 
       
   917 	__TEST_INVARIANT;
       
   918 	}
       
   919 
       
   920 
       
   921 void CRichTextIndex::NormalizeNow(const TLogicalPosition& aNormalizePos)
       
   922 //
       
   923 	{
       
   924 	CParaAttribs* currentParaAttribs=(*iParaIx)[aNormalizePos.iParaElement].iParaAttribs;
       
   925 	if (!currentParaAttribs->IsShared())
       
   926 		{
       
   927 		CParaAttribs* sharedParaAttribs=RequestShare(iPos);
       
   928 		if (sharedParaAttribs!=NULL && currentParaAttribs!=sharedParaAttribs)
       
   929 			{// We must have been given a share on something already in the shared list.  Dump current stuff.
       
   930 			currentParaAttribs->Release();
       
   931 			(*iParaIx)[aNormalizePos.iParaElement].iParaAttribs=sharedParaAttribs;
       
   932 			RemoveFromPhraseIx(aNormalizePos.iParaBasePhraseElement);
       
   933 			}
       
   934 		}
       
   935 	}
       
   936 
       
   937 
       
   938 CParaAttribs* CRichTextIndex::ReserveCellLC()
       
   939 // Returns a handle to a newly created CParaAttribs object.  This may be used
       
   940 // during a call to GetParaAttribsL() as a pre-allocated cell, thus ensuring
       
   941 // that the call cannot possibly leave.
       
   942 // ASSUMES: that the internal position record has been set correctly.
       
   943 //
       
   944 	{
       
   945 	CParaAttribs* reservedCell=ReserveCellL();
       
   946 	CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,reservedCell));
       
   947 	return reservedCell;
       
   948 	}
       
   949 
       
   950 
       
   951 CParaAttribs* CRichTextIndex::ReserveCellL()
       
   952 // Returns a handle to a newly created CParaAttribs object.  This may be used
       
   953 // during a call to GetParaAttribsL() as a pre-allocated cell, thus ensuring
       
   954 // that the call cannot possibly leave.
       
   955 // ASSUMES: that the internal position record has been set correctly.
       
   956 //
       
   957 	{
       
   958 	TCurrentIndexRecords current;
       
   959 	GetCurrentRecords(current);
       
   960 	const CParaAttribs& paraAttribs=*current.iParaAttribs;
       
   961 	CParaFormatLayer* paraLayer=paraAttribs.iParaFormat;
       
   962 	CCharFormatLayer* charLayer=(paraAttribs.IsShared())
       
   963 								? paraAttribs.iCharFormat
       
   964 								: (*iPhraseIx)[iPos.iPhraseElement].CharFormat();
       
   965 	CParaAttribs* reservedCell=CParaAttribs::NewL(paraLayer,charLayer);
       
   966 	return reservedCell;
       
   967 	}
       
   968 
       
   969 
       
   970 TBool CRichTextIndex::DeleteParagraphText(TInt& aLength)
       
   971 // Called once for each paragraph that's included in the delete range.
       
   972 // Assumes the internal position record has already been set correctly.
       
   973 // The delete range may cover: (1) The entire paragraph - so just destroy the paragraph,
       
   974 // (2i) At least a portion of a single phrase, and possibly
       
   975 // (2ii) 0..n whole contiguous phrases, and possibly
       
   976 // (2iii) a trailing partial phrase.
       
   977 // Returns ETrue if the full paragraph is deleted, otherwise returns EFalase.
       
   978 //
       
   979 	{
       
   980 	TCurrentIndexRecords current; GetCurrentRecords(current);
       
   981 	if (FirstPhraseOfParagraph() && iPos.iPhraseElementOffset==0 && aLength>=current.iParaEntry->iLength)
       
   982 		{// The entire paragraph needs to be deleted.
       
   983 		aLength-=current.iParaEntry->iLength;
       
   984 		if (!current.iParaAttribs->IsShared())
       
   985 			RemoveFromPhraseIx(iPos.iParaBasePhraseElement,current.iParaAttribs->iPhraseCount);
       
   986 		current.iParaAttribs->Release();
       
   987 		iParaIx->Delete(iPos.iParaElement);
       
   988 		return ETrue;
       
   989 		}
       
   990 	TInt deleteFromPhrase=CurrentPhraseLength()-iPos.iPhraseElementOffset;
       
   991 	TInt maxPhrase=current.iParaAttribs->PhraseCount();
       
   992 	TInt currentPhrase=(iPos.iPhraseElement-iPos.iParaBasePhraseElement);
       
   993 	while (currentPhrase<maxPhrase)
       
   994 		{
       
   995 		TInt deletable=Min(deleteFromPhrase,aLength);
       
   996 		current.iParaEntry->iLength-=deletable;  // Adjust the paragraph length.
       
   997 		if (current.iPhrase && deletable>=CurrentPhraseLength())
       
   998 			{// Remove the now empty phrase from the phrase index.
       
   999 			RemoveFromPhraseIx(iPos.iPhraseElement);
       
  1000 			current.iParaAttribs->iPhraseCount--;
       
  1001 			}
       
  1002 		else if (current.iPhrase)
       
  1003 			{// Adjust phrase length and move onto the next phrase.
       
  1004 			current.iPhrase->AdjustLength(-deletable);
       
  1005 			iPos.iPhraseElement++;
       
  1006 			iPos.iPhraseElementOffset=0;
       
  1007 			}
       
  1008 		currentPhrase++;
       
  1009 		aLength-=deletable;
       
  1010 		if(aLength==0)
       
  1011 			break;  // Nothing left to delete in this paragraph.
       
  1012 		// Get the data for the next phrase.
       
  1013 		GetCurrentRecords(current);
       
  1014 		deleteFromPhrase=CurrentPhraseLength();
       
  1015 		}
       
  1016 	return EFalse;
       
  1017 	}
       
  1018 
       
  1019 TBool CRichTextIndex::InsertCharFormatIsActive()
       
  1020 	{
       
  1021 	return iPendingNewPhrasePos != EInsertCharFormatReset;
       
  1022 	}
       
  1023 
       
  1024 /** Sets an *InsertPending* state, where format has been inserted into the
       
  1025 text, but no content has yet been inserted. This *state* is cancelled by cursor
       
  1026 movement etc. Split the current phrase at aPos (if necessary) and insert a zero
       
  1027 length phrase, ready to accept the pending content of the specified format.
       
  1028 */
       
  1029 void CRichTextIndex::SetInsertCharFormatL(const TCharFormatX& aFormat,const TCharFormatXMask& aMask,TInt aPos)
       
  1030 	{
       
  1031 	__ASSERT_ALWAYS(!InsertCharFormatIsActive() || aPos==iPendingNewPhrasePos,
       
  1032 					Panic(ESetInsertCharFormatIntegrityErr));
       
  1033 	if (InsertCharFormatIsActive())
       
  1034 		UpdateInsertCharFormatL(aFormat, aMask);
       
  1035 	else
       
  1036 		NewInsertCharFormatL(aFormat, aMask, aPos);
       
  1037 	}
       
  1038 
       
  1039 void CRichTextIndex::NewInsertCharFormatL(const TCharFormatX& aFormat,
       
  1040 	const TCharFormatXMask& aMask, TInt aPos)
       
  1041 	{
       
  1042 	__ASSERT_ALWAYS(!InsertCharFormatIsActive(),
       
  1043 		Panic(ESetInsertCharFormatIntegrityErr));
       
  1044 	ScanToPosition(aPos,EScanToPositionMatchLeft);
       
  1045 	TCurrentIndexRecords current;
       
  1046 	GetCurrentRecords(current);
       
  1047 	CCharFormatLayer* basedOn;
       
  1048 	TCharFormatX applyFormat=aFormat;
       
  1049 	TCharFormatXMask applyMask=aMask;
       
  1050 	GetPhraseFormat(current,applyFormat,applyMask,basedOn);  // Inherit phrase attributes to the left, over what is present.
       
  1051 	TBool origParaAttribsShared=current.iParaAttribs->IsShared();
       
  1052 	if (origParaAttribsShared)
       
  1053 		{// Current paraAttribs is shared.
       
  1054 		iRollbackParaAttribsHandle=current.iParaAttribs;
       
  1055 		current.iParaEntry->iParaAttribs=RequestReclaimShareL(current.iParaAttribs,current.iParaEntry);  // Does not release share.
       
  1056 		ScanToPosition(aPos,EScanToPositionMatchLeft);  // Pick up reclaimed phrase.
       
  1057 		}  // Now current.iParaAttribs has specific character formatting - guaranteed.
       
  1058 	GetCurrentRecords(current);
       
  1059 	TRAPD(ret, DoNewInsertCharFormatL(applyFormat, applyMask,
       
  1060 		basedOn, current.iParaAttribs));
       
  1061 	if (ret!=KErrNone)
       
  1062 		{// Rollback as if this function call never happened.
       
  1063 		if (origParaAttribsShared)
       
  1064 			{// Revert back to sharing the original.
       
  1065 			current.iParaAttribs->Release();
       
  1066 			RemoveFromPhraseIx(iPos.iPhraseElement);
       
  1067 			current.iParaEntry->iParaAttribs=iRollbackParaAttribsHandle;
       
  1068 			}
       
  1069 		else
       
  1070 			{// Restore the original phrase index.
       
  1071 			if (PhraseSplit())
       
  1072 				MergePhrases(aPos);
       
  1073 			}
       
  1074 		User::LeaveNoMemory();
       
  1075 		}
       
  1076 	iPendingNewPhrasePos=aPos;
       
  1077 	}
       
  1078 
       
  1079 void CRichTextIndex::UpdateInsertCharFormatL(const TCharFormatX& aFormat,
       
  1080 	const TCharFormatXMask& aMask)
       
  1081 	{
       
  1082 	CCharFormatLayer* currentLayer = GetCurrentInsertCharFormat();
       
  1083 	CCharFormatLayer* newLayer = CCharFormatLayer::NewCopyBaseL(currentLayer);
       
  1084 	CleanupStack::PushL(newLayer);
       
  1085 	newLayer->SetL(aFormat, aMask);
       
  1086 	currentLayer->Swap(*newLayer);
       
  1087 	CleanupStack::PopAndDestroy(newLayer);
       
  1088 	}
       
  1089 
       
  1090 CCharFormatLayer* CRichTextIndex::GetCurrentInsertCharFormat()
       
  1091 	{
       
  1092 	__ASSERT_DEBUG(InsertCharFormatIsActive(),
       
  1093 		Panic(ESetInsertCharFormatIntegrityErr));
       
  1094 	ScanToPosition(iPendingNewPhrasePos,EScanToPositionMatchLeft);
       
  1095 	TCurrentIndexRecords current;
       
  1096 	GetCurrentRecords(current);
       
  1097 	__ASSERT_DEBUG((*iPhraseIx)[iPos.iPhraseElement].Length() == 0,
       
  1098 		Panic(ESetInsertCharFormatIntegrityErr));
       
  1099 	return (*iPhraseIx)[iPos.iPhraseElement].CharFormat();
       
  1100 	}
       
  1101 
       
  1102 void CRichTextIndex::DoNewInsertCharFormatL(const TCharFormatX& aFormat,const TCharFormatXMask& aMask,
       
  1103 											CCharFormatLayer* aBasedOn,CParaAttribs* aParaAttribs)
       
  1104 	{
       
  1105 	SplitPhraseL(iPos.iPhraseElement,iPos.iPhraseElementOffset,aParaAttribs);
       
  1106 	CCharFormatLayer* layer=CCharFormatLayer::NewL();
       
  1107 	layer->SetBase(aBasedOn);  // must be done before the SetL().
       
  1108 	CleanupStack::PushL(layer);
       
  1109 	layer->SetL(aFormat,aMask);
       
  1110 	RPhraseAttribsEntry pendingNewPhrase(layer);
       
  1111 	TInt pendingNewPhraseElement=(FirstPhraseOfParagraph() && iPos.iPhraseElementOffset==0)
       
  1112 		?iPos.iParaBasePhraseElement:iPos.iPhraseElement+1;
       
  1113 	iPhraseIx->InsertL(pendingNewPhraseElement,pendingNewPhrase);
       
  1114 	CleanupStack::Pop();
       
  1115 	aParaAttribs->iPhraseCount++;
       
  1116 	}
       
  1117 
       
  1118 
       
  1119 void CRichTextIndex::RebalanceIndex()
       
  1120 // Returns the index to a good state, by releasing the extra share taken on the paraAttribs
       
  1121 //
       
  1122 	{
       
  1123 	if (iRollbackParaAttribsHandle)
       
  1124 		{
       
  1125 		// ASSERT: The specified para attribs is indeed in the share list.
       
  1126 		__ASSERT_ALWAYS(iRollbackParaAttribsHandle->IsShared(),Panic(EParaAttribsNotInSharedList));
       
  1127 		iRollbackParaAttribsHandle->Release();
       
  1128 		iRollbackParaAttribsHandle=NULL;
       
  1129 		}
       
  1130 	}
       
  1131 
       
  1132 /** Cancels the transitory state where a specified character format is applied
       
  1133 on top of any inherited formatting. eg, when bold is on. Cancel when: (1) the
       
  1134 text position is altered. (2) the first character (or picture) has been
       
  1135 inserted following the setting of this state. If a zero length phrase is
       
  1136 removed OR has content entered into it, the newly abutting phrases are checked
       
  1137 to see if they can be merged. Then a request share of this para is issued.
       
  1138 */
       
  1139 void CRichTextIndex::CancelInsertCharFormat()
       
  1140 	{
       
  1141 	if (InsertCharFormatIsActive())
       
  1142 		{
       
  1143 		TBool isDeleted = DeleteInsertCharFormat();
       
  1144 		ConsolidateAt(iPendingNewPhrasePos, isDeleted?
       
  1145 			EPositionOnly : EFollowingPhrase);
       
  1146 		iPendingNewPhrasePos = EInsertCharFormatReset;
       
  1147 		}
       
  1148 	}
       
  1149 
       
  1150 /** Attempts to delete a zero-length phrase at the insert character format
       
  1151 position. Does not delete any phrase of non-zero length.
       
  1152 @pre The insert character format must be active
       
  1153 @return ETrue if a zero-length phrase was deleted.
       
  1154 */
       
  1155 TBool CRichTextIndex::DeleteInsertCharFormat()
       
  1156 	{
       
  1157 	__ASSERT_DEBUG(InsertCharFormatIsActive(), User::Invariant());
       
  1158 	ScanToPosition(iPendingNewPhrasePos,EScanToPositionMatchLeft);
       
  1159 	TCurrentIndexRecords current;
       
  1160 	GetCurrentRecords(current);
       
  1161 	if (current.iPhrase && current.iPhrase->Length() == 0)
       
  1162 		{
       
  1163 		RemoveFromPhraseIx(iPos.iPhraseElement);
       
  1164 		current.iParaAttribs->iPhraseCount--;  // Para has 1 less phrase in it now.
       
  1165 		return ETrue;
       
  1166 		}
       
  1167 	return EFalse;
       
  1168 	}
       
  1169 
       
  1170 /** Attempts to merge phrases and share paragraphs.
       
  1171 @param aPosition
       
  1172 	Phrase boundary here is merged if possible, paragraph here is shared if
       
  1173 	possible.
       
  1174 @param aPositionOrPhrase
       
  1175 	If EPositionOnly the phrases either side of aPosition are considered for merging. If EFollowingPhrase,
       
  1176 	the end of the phrase following aPosition is also considered.
       
  1177 */
       
  1178 void CRichTextIndex::ConsolidateAt(TInt aPosition,
       
  1179 	TPositionOrPhrase aPositionOrPhrase)
       
  1180 	{
       
  1181 	ScanToPosition(aPosition, EScanToPositionAbsolute);
       
  1182 	TCurrentIndexRecords current;
       
  1183 	GetCurrentRecords(current);
       
  1184 	if (!current.iPhrase)
       
  1185 		return;
       
  1186 
       
  1187 	TInt length = current.iPhrase->Length();
       
  1188 	MergePhrases(aPosition);
       
  1189 	if (aPositionOrPhrase == EFollowingPhrase)
       
  1190 		{
       
  1191 		ScanToPosition(aPosition, EScanToPositionAbsolute);
       
  1192 		GetCurrentRecords(current);
       
  1193 		if (current.iPhrase)
       
  1194 			MergePhrases(aPosition + length);
       
  1195 		}
       
  1196 	Normalize(aPosition);
       
  1197 	RebalanceIndex();
       
  1198 	}
       
  1199 
       
  1200 TBool CRichTextIndex::DelSetInsertCharFormatL(TInt aPos,TInt aLength)
       
  1201 // Delete aLength characters, commencing at, and including, aPos.
       
  1202 // Adds value by the following behaviour:
       
  1203 // If aPos is on a phrase boundary, then remember temporarily the phrase format.
       
  1204 // This is applied to any content that is immediately inserted.
       
  1205 //
       
  1206 	{
       
  1207 	__TEST_INVARIANT;
       
  1208 
       
  1209 	CancelInsertCharFormat();
       
  1210 	ScanToPosition(aPos,EScanToPositionAbsolute);
       
  1211 	TCurrentIndexRecords current; GetCurrentRecords(current);
       
  1212 	if ((!current.iParaAttribs->IsShared()) && iPos.iPhraseElementOffset==0)
       
  1213 		{// aPos is on phrase boundary so SetState.
       
  1214 		TCharFormatX format;
       
  1215 		TCharFormatXMask mask;
       
  1216 		CCharFormatLayer* charBase;
       
  1217 		GetPhraseFormat(current,format,mask,charBase);
       
  1218 		SetInsertCharFormatL(format,mask,aPos);
       
  1219 		}
       
  1220 	TIndexDeleteInfo deleteInfo;
       
  1221 	SetForDeleteL(deleteInfo,aPos,aLength);
       
  1222 	TBool parasMerged=DeleteNow(deleteInfo);
       
  1223 
       
  1224 	__TEST_INVARIANT;
       
  1225 	return parasMerged;
       
  1226 	}
       
  1227 
       
  1228 
       
  1229 void CRichTextIndex::ApplyParaFormatL(const CParaFormat* aFormat,const TParaFormatMask& aMask,TInt aPos,TInt aLength)
       
  1230 // Applies the specified format attributes to the paragraphs covering character position aPos to aPos+aLength-1.
       
  1231 // Preserves any attributes that are currently stored in this layer.
       
  1232 // If the specified para(s) is in the shared list, a new shared para of the desired format must be created,
       
  1233 // and the usage count of the original decremented.
       
  1234 //
       
  1235 	{
       
  1236 	__TEST_INVARIANT;
       
  1237 
       
  1238 	TInt offset=(aLength==0)?0 :-1;
       
  1239 	TInt endPara=OwningParagraph(aPos+(aLength+offset));
       
  1240 	TInt paraItem=OwningParagraph(aPos);
       
  1241 	TCurrentIndexRecords current; GetCurrentRecords(current);
       
  1242 	CParaFormat* pf=CParaFormat::NewL(*aFormat);  // preserve the desired tablist.
       
  1243 	CleanupStack::PushL(pf);
       
  1244 	for (;paraItem<=endPara;paraItem++)
       
  1245 		{// For each paragraph, apply the specified format.
       
  1246 		TParaFormatMask applyMask=aMask;
       
  1247 		CParaAttribs* currentParaAttribs=(*iParaIx)[paraItem].iParaAttribs;
       
  1248 		TBool shared=currentParaAttribs->IsShared();
       
  1249 		if (!shared)
       
  1250 			{
       
  1251 			currentParaAttribs->iParaFormat->SenseL(pf,applyMask);
       
  1252 			currentParaAttribs->iParaFormat->SetL(pf,applyMask);
       
  1253 			}
       
  1254 		else
       
  1255 			{// Must create a new shared para attribs of the specified format
       
  1256 			// Make a new para format layer
       
  1257 			currentParaAttribs->iParaFormat->SenseL(pf,applyMask);
       
  1258 			CParaFormatLayer* newParaLayer=CParaFormatLayer::NewL(pf,applyMask);
       
  1259 			newParaLayer->SetBase(currentParaAttribs->iParaFormat->SenseBase());
       
  1260 			CleanupStack::PushL(newParaLayer);
       
  1261 			// Make a new char format layer
       
  1262 			CCharFormatLayer* newCharLayer=CCharFormatLayer::NewL(currentParaAttribs->iCharFormat);
       
  1263 			newCharLayer->SetBase(currentParaAttribs->iCharFormat->SenseBase());
       
  1264 			CleanupStack::PushL(newCharLayer);
       
  1265 			//
       
  1266 			CParaAttribs* sharedParaAttribs=GetParaAttribsL(newParaLayer,newCharLayer);
       
  1267 			CleanupStack::PopAndDestroy(2);
       
  1268 			if (sharedParaAttribs)
       
  1269 				(*iParaIx)[paraItem].iParaAttribs=sharedParaAttribs;
       
  1270 			currentParaAttribs->Release();
       
  1271 			}
       
  1272 		}
       
  1273 	CleanupStack::PopAndDestroy();  // pf
       
  1274 	__TEST_INVARIANT;
       
  1275 	}
       
  1276 
       
  1277 
       
  1278 void CRichTextIndex::ApplyParagraphStyleL(const CParagraphStyle& aStyle,TInt aPos,TInt aLength,
       
  1279 										  const CCharFormatLayer* aCharStyleNormal,CParagraphStyle::TApplyParaStyleMode aMode)
       
  1280 // Applies the specified paragraph style to the paragraphs covering
       
  1281 // character positions aPos to aPos+aLength-1.
       
  1282 // Alters the specific formatting of the covered paragraphs as specified by aMode.
       
  1283 //
       
  1284 	{
       
  1285 	__TEST_INVARIANT;
       
  1286 
       
  1287 	TInt offset=(aLength==0)?0 :-1;
       
  1288 	TInt endPara=OwningParagraph(aPos+(aLength+offset));
       
  1289 	TInt paraItem=OwningParagraph(aPos);
       
  1290 	TInt paragraphBasePhrase=iPos.iParaBasePhraseElement;
       
  1291 	TCurrentIndexRecords current;
       
  1292 	GetCurrentRecords(current);
       
  1293 	for (;paraItem<=endPara;paraItem++)
       
  1294 		{// For each paragraph, apply the specified style
       
  1295 		CParaAttribs& currentParaAttribs=*(*iParaIx)[paraItem].iParaAttribs;
       
  1296 		TBool shared=currentParaAttribs.IsShared();
       
  1297 		TUid type=aStyle.Type();
       
  1298 		if (!shared)
       
  1299 			{
       
  1300 			currentParaAttribs.iParaFormat->SetBase(&aStyle);
       
  1301 			TInt phraseCount=currentParaAttribs.PhraseCount();
       
  1302 			for (TInt phraseItem=0;phraseItem<phraseCount;phraseItem++)
       
  1303 				{
       
  1304 				CCharFormatLayer& charLayer=*(*iPhraseIx)[paragraphBasePhrase+phraseItem].CharFormat();
       
  1305 				if (type==KNormalParagraphStyleUid)
       
  1306 					charLayer.SetBase(aCharStyleNormal);
       
  1307 				else
       
  1308 					charLayer.SetBase(aStyle.CharFormatLayer());
       
  1309 				ModifySpecificFormatting(*currentParaAttribs.iParaFormat,charLayer,aMode);
       
  1310 				}
       
  1311 			paragraphBasePhrase+=phraseCount;
       
  1312 			}
       
  1313 		else
       
  1314 			{// Must create a new shared para attribs of the same format, but a different based on link
       
  1315 			// Make a new para format layer
       
  1316 			CParaFormatLayer* newParaLayer=NULL;
       
  1317 			if (aMode==CParagraphStyle::ERetainNoSpecificFormats || aMode==CParagraphStyle::ERetainSpecificCharFormat)
       
  1318 				newParaLayer=CParaFormatLayer::NewL();
       
  1319 			else
       
  1320 				newParaLayer=CParaFormatLayer::NewL(currentParaAttribs.iParaFormat);
       
  1321 			newParaLayer->SetBase(&aStyle);
       
  1322 			CleanupStack::PushL(newParaLayer);
       
  1323 			//
       
  1324 			// Make a new char format layer
       
  1325 			CCharFormatLayer* newCharLayer=NULL;
       
  1326 			if (aMode==CParagraphStyle::ERetainNoSpecificFormats || aMode==CParagraphStyle::ERetainSpecificParaFormat)
       
  1327 				newCharLayer=CCharFormatLayer::NewL();
       
  1328 			else
       
  1329 				newCharLayer=CCharFormatLayer::NewL(currentParaAttribs.iCharFormat);
       
  1330 			if (type==KNormalParagraphStyleUid)
       
  1331 				newCharLayer->SetBase(aCharStyleNormal);
       
  1332 			else
       
  1333 				newCharLayer->SetBase(aStyle.CharFormatLayer());
       
  1334 			CleanupStack::PushL(newCharLayer);
       
  1335 			//
       
  1336 			(*iParaIx)[paraItem].iParaAttribs=GetParaAttribsL(newParaLayer,newCharLayer);
       
  1337 			CleanupStack::PopAndDestroy(2);
       
  1338 			currentParaAttribs.Release();
       
  1339 			}
       
  1340 		}
       
  1341 	}
       
  1342 
       
  1343 
       
  1344 void CRichTextIndex::ModifySpecificFormatting(CParaFormatLayer& aPl,CCharFormatLayer& aCl,CParagraphStyle::TApplyParaStyleMode aMode)
       
  1345 //
       
  1346 //
       
  1347 	{
       
  1348 	switch(aMode)
       
  1349 		{
       
  1350 		case(CParagraphStyle::ERetainNoSpecificFormats):
       
  1351 			aPl.Reset();
       
  1352 			aCl.Reset();
       
  1353 			break;
       
  1354 		case(CParagraphStyle::ERetainSpecificParaFormat):
       
  1355 			aCl.Reset();
       
  1356 			break;
       
  1357 		case(CParagraphStyle::ERetainSpecificCharFormat):
       
  1358 			aPl.Reset();
       
  1359 			break;
       
  1360 		case(CParagraphStyle::ERetainAllSpecificFormats):
       
  1361 		default:
       
  1362 			break;
       
  1363 		}
       
  1364 	}
       
  1365 
       
  1366 
       
  1367 /*
       
  1368 For every paragraph in the document: (i) if it uses the style aFrom, make it use the style aTo,
       
  1369 or the global style if aTo is null; (ii) if any phrase in the paragraph has a character format
       
  1370 based on the character format owned by aFrom, change it so that it is based on the character
       
  1371 format owned by aTo, or the global character format if aTo is null.
       
  1372 
       
  1373 The action described in (ii) should only occur for an 'orphaned' character insertion format; that is
       
  1374 an insertion format left after deletion of a block in a certain style that is itself then deleted.
       
  1375 */
       
  1376 void CRichTextIndex::NotifyStyleChangedL(const CParagraphStyle* aTo,const CParagraphStyle* aFrom,
       
  1377 										 const CParaFormatLayer& aGlobalParaFormatLayer,
       
  1378 										 const CCharFormatLayer& aGlobalCharFormatLayer)
       
  1379 	{
       
  1380 	__TEST_INVARIANT;
       
  1381 
       
  1382 	TInt paraCount=ParagraphCount();
       
  1383 	TInt currentPhrase = 0;
       
  1384 	const CCharFormatLayer* oldCharFormatLayer = aFrom->CharFormatLayer();
       
  1385 	const CParaFormatLayer* newParFormatLayer = aTo ? aTo : &aGlobalParaFormatLayer;
       
  1386 	const CCharFormatLayer* newCharFormatLayer = aTo ? aTo->CharFormatLayer() : &aGlobalCharFormatLayer;
       
  1387 	for (TInt paraItem = 0;paraItem < paraCount; paraItem++)
       
  1388 		{
       
  1389 		TParaAttribsEntry* currentPara = &(*iParaIx)[paraItem];
       
  1390 		CParaAttribs* currentParaAttribs = currentPara->iParaAttribs;
       
  1391 
       
  1392 		TBool changeParStyleBase = currentParaAttribs->iParaFormat->SenseBase() == aFrom;
       
  1393 		if (!currentParaAttribs->IsShared())
       
  1394 			{
       
  1395 			if (changeParStyleBase)
       
  1396 				currentParaAttribs->iParaFormat->SetBase(newParFormatLayer);
       
  1397 			TInt phraseCount = currentParaAttribs->PhraseCount();
       
  1398 			for (TInt phraseItem = currentPhrase; phraseItem < (currentPhrase + phraseCount); phraseItem++)
       
  1399 				{
       
  1400 				CCharFormatLayer* charFormat = (*iPhraseIx)[phraseItem].CharFormat();
       
  1401 				if (charFormat->SenseBase() == oldCharFormatLayer)
       
  1402 					charFormat->SetBase(newCharFormatLayer);
       
  1403 				}
       
  1404 			currentPhrase += phraseCount;
       
  1405 			}
       
  1406 		else
       
  1407 			{	// Maintain the shared list reference
       
  1408 			CParaAttribs* resultantParaAttribs = CParaAttribs::NewL(currentParaAttribs);
       
  1409 			CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,resultantParaAttribs));
       
  1410 			if (changeParStyleBase)
       
  1411 				resultantParaAttribs->iParaFormat->SetBase(newParFormatLayer);
       
  1412 			if (resultantParaAttribs->iCharFormat->SenseBase() == oldCharFormatLayer)
       
  1413 				resultantParaAttribs->iCharFormat->SetBase(newCharFormatLayer);
       
  1414 			CParaAttribs* shared = RequestShareL(resultantParaAttribs); // will return a non-NULL handle
       
  1415 			__ASSERT_DEBUG(shared,Panic(EDebug));
       
  1416 			currentParaAttribs->Release();
       
  1417 			iSharedParaQueHead.AddLast(*resultantParaAttribs); // allows correct release of cell.
       
  1418 			CleanupStack::PopAndDestroy();
       
  1419 			currentPara->iParaAttribs = shared;
       
  1420 			}
       
  1421 		}
       
  1422 
       
  1423 	__TEST_INVARIANT;
       
  1424 	}
       
  1425 
       
  1426 
       
  1427 const CParaFormatLayer* CRichTextIndex::ParagraphStyle(TBool& aStyleChangesOverRange,
       
  1428 															   TInt aPos,
       
  1429 															   TInt aLength)const
       
  1430 // Return the handle of the first paragraph style encountered in the specified range.
       
  1431 // Set aStyleChangesOverRange to ETrue, if different paragraph styles are encountered
       
  1432 // across the specified range, otherwise set it to EFalse.
       
  1433 //
       
  1434 	{
       
  1435 	__TEST_INVARIANT;
       
  1436 
       
  1437 	aStyleChangesOverRange=EFalse;
       
  1438 	CParaFormatLayer* style=NULL;
       
  1439 	TInt para=CONST_CAST(CRichTextIndex*,this)->OwningParagraph(aPos);
       
  1440 	TInt offset=(aLength==0)?0 :-1;
       
  1441 	TInt endPara=CONST_CAST(CRichTextIndex*,this)->OwningParagraph(aPos+(aLength+offset));
       
  1442 	style=(CParaFormatLayer*)(*iParaIx)[para].iParaAttribs->iParaFormat->SenseBase();
       
  1443 	++para;
       
  1444 	for (;para<=endPara;para++)
       
  1445 		{
       
  1446 		CParaFormatLayer* nextStyle=(CParaFormatLayer*)(*iParaIx)[para].iParaAttribs->iParaFormat->SenseBase();
       
  1447 		if (nextStyle!=style)
       
  1448 			aStyleChangesOverRange=ETrue;
       
  1449 		}
       
  1450 
       
  1451 	__TEST_INVARIANT;
       
  1452 	return style;
       
  1453 	}
       
  1454 
       
  1455 
       
  1456 void CRichTextIndex::SplitPhraseL(TInt aPhrase,TInt anOffset,RPhraseAttribsEntry& aPhraseAttribs,CParaAttribs& aParaAttribs)
       
  1457 	{
       
  1458 	__ASSERT_DEBUG(anOffset>0 && anOffset<aPhraseAttribs.Length(),User::Invariant());
       
  1459 //
       
  1460 	CCharFormatLayer* charLayer=CCharFormatLayer::NewCopyBaseL(aPhraseAttribs.CharFormat());
       
  1461 	CleanupStack::PushL(charLayer);
       
  1462 	iPhraseIx->InsertL(aPhrase+1,RPhraseAttribsEntry(charLayer,aPhraseAttribs.Length()-anOffset));
       
  1463 	CleanupStack::Pop();
       
  1464 	//
       
  1465 	// InsertL() has invalidated the phrase index.
       
  1466 	iPhraseIx->At(aPhrase).SetLength(anOffset);  // Adjust the length of the orginal phrase
       
  1467 	aParaAttribs.iPhraseCount++;
       
  1468 	}
       
  1469 
       
  1470 
       
  1471 TBool CRichTextIndex::MergePhrases(TInt aPhrase,RPhraseAttribsEntry& aPhraseAttribs,CParaAttribs& aParaAttribs)
       
  1472 	{
       
  1473 	RPhraseAttribsEntry& prevPhrase=iPhraseIx->At(aPhrase-1);
       
  1474 	if (!aPhraseAttribs.IsIdentical(prevPhrase))
       
  1475 		return EFalse;
       
  1476 	// Merge the abutting phrases together.
       
  1477 	prevPhrase.AdjustLength(aPhraseAttribs.Length());  //  Extend the remaining phrase
       
  1478 	RemoveFromPhraseIx(aPhrase);		// Free the resources taken by the redundant phrase
       
  1479 	aParaAttribs.iPhraseCount--;
       
  1480 	return ETrue;
       
  1481 	}
       
  1482 
       
  1483 
       
  1484 void CRichTextIndex::Share(TParaAttribsEntry& aParaEntry,TInt aPhrase)
       
  1485 //
       
  1486 // aParaEntry is not shared and can be (phrase count 1), aPhrase is the single phrase element
       
  1487 //
       
  1488 	{
       
  1489 	CParaAttribs* paraAttribs=aParaEntry.iParaAttribs;
       
  1490 	__ASSERT_DEBUG(paraAttribs->iPhraseCount==1,User::Invariant());
       
  1491 
       
  1492 	RPhraseAttribsEntry& phraseAttribs=iPhraseIx->At(aPhrase);
       
  1493 	__ASSERT_DEBUG(!phraseAttribs.IsPicturePhrase(),User::Invariant());
       
  1494 
       
  1495 	CParaAttribs* share=GetParaAttribs(paraAttribs,*phraseAttribs.CharFormat());
       
  1496 	if (share!=paraAttribs)
       
  1497 		{	// re-use an existing share, so release the current attribs
       
  1498 		paraAttribs->Release();
       
  1499 		phraseAttribs.Discard();
       
  1500 		aParaEntry.iParaAttribs=share;
       
  1501 		}
       
  1502 	iPhraseIx->Delete(aPhrase);
       
  1503 	}
       
  1504 
       
  1505 
       
  1506 void CRichTextIndex::ApplyCharFormatCleanup(TAny* aPtr)
       
  1507 // CLeanup function for ApplyCharFormatL()
       
  1508 //
       
  1509 	{REINTERPRET_CAST(CRichTextIndex*,aPtr)->ApplyCharFormatRollback();}
       
  1510 
       
  1511 
       
  1512 void CRichTextIndex::ApplyCharFormatRollback()
       
  1513 // Paragraph and phrase we were working on are stored in iPos
       
  1514 // Return them the canonical form
       
  1515 //
       
  1516 	{
       
  1517 	TParaAttribsEntry& paraEntry=iParaIx->At(iPos.iParaElement);
       
  1518 	CParaAttribs* paraAttribs=paraEntry.iParaAttribs;
       
  1519 
       
  1520 	if (paraAttribs->IsShared())
       
  1521 		return;
       
  1522 
       
  1523 	TInt phrase=iPos.iPhraseElement;
       
  1524 	TInt base=iPos.iParaBasePhraseElement;
       
  1525 	__ASSERT_DEBUG(phrase>=base && phrase<base+paraAttribs->iPhraseCount,User::Invariant());
       
  1526 	if (phrase<base+paraAttribs->iPhraseCount-1)	// merge to the right
       
  1527 		MergePhrases(phrase+1,iPhraseIx->At(phrase+1),*paraAttribs);
       
  1528 	if (phrase>base)								// merge to the left
       
  1529 		MergePhrases(phrase,iPhraseIx->At(phrase),*paraAttribs);
       
  1530 	if (paraAttribs->iPhraseCount==1)				// Share the paragraph
       
  1531 		Share(paraEntry,base);
       
  1532 	}
       
  1533 
       
  1534 
       
  1535 void CRichTextIndex::ApplyCharFormatL(const TCharFormatX& aFormat,const TCharFormatXMask& aMask,TInt aPos,TInt aLength,TBool aRemoveSpecific)
       
  1536 // Applies the specified character formatting to the characters contained within the range
       
  1537 // aPos to aPos+(aLength-1).
       
  1538 //
       
  1539 	{
       
  1540 	__ASSERT_DEBUG(aLength>=0,User::Invariant());
       
  1541 	__TEST_INVARIANT;
       
  1542 
       
  1543 	ScanToPosition(aPos,EScanToPositionAbsolute);
       
  1544 	TInt paraOffset=iPos.iParaElementOffset;
       
  1545 	TInt phraseOffset=iPos.iPhraseElementOffset;
       
  1546 	TInt phrase=iPos.iPhraseElement;
       
  1547 
       
  1548 // prepare for failure
       
  1549 	CleanupStack::PushL(TCleanupItem(ApplyCharFormatCleanup,this));
       
  1550 
       
  1551 	for (;;)
       
  1552 		{	// a paragraph at a time
       
  1553 		TParaAttribsEntry& paraEntry=iParaIx->At(iPos.iParaElement);
       
  1554 		CParaAttribs* paraAttribs=paraEntry.iParaAttribs;
       
  1555 		TInt charsToFormat=Min(aLength,paraEntry.iLength-paraOffset);
       
  1556 		aLength-=charsToFormat;
       
  1557 #ifdef _DEBUG
       
  1558 		aPos+=charsToFormat;
       
  1559 #endif
       
  1560 
       
  1561 // STEP 1. Reclaim any shared paragraph into non shared form. Re-use the object if possible
       
  1562 
       
  1563 		if (paraAttribs->IsShared())
       
  1564 			{
       
  1565 			CCharFormatLayer* charLayer=paraAttribs->iCharFormat;
       
  1566 			if (paraAttribs->iRefCount==CParaAttribs::EPrimeSharedCount)
       
  1567 				{	// we are the sole user of this attribute
       
  1568 				iPhraseIx->InsertL(phrase,RPhraseAttribsEntry(charLayer,paraEntry.iLength));
       
  1569 				// adjust attribute to be non-shared
       
  1570 				paraAttribs->link.Deque();
       
  1571 				paraAttribs->iRefCount=CParaAttribs::EPrimeNonSharedCount;
       
  1572 				paraAttribs->iPhraseCount=1;
       
  1573 				}
       
  1574 			else
       
  1575 				{	// create a new para attribs object
       
  1576 				CParaAttribs* newAttribs=CParaAttribs::NewL(paraAttribs->iParaFormat);
       
  1577 				CleanupReleasePushL(*newAttribs);
       
  1578 				charLayer=CCharFormatLayer::NewCopyBaseL(charLayer);
       
  1579 				CleanupStack::PushL(charLayer);
       
  1580 				iPhraseIx->InsertL(phrase,RPhraseAttribsEntry(charLayer,paraEntry.iLength));
       
  1581 				CleanupStack::Pop(2);		// charlayer, newAttribs
       
  1582 				paraAttribs->Release();		// lose a share on the old attribs
       
  1583 				paraEntry.iParaAttribs=paraAttribs=newAttribs;
       
  1584 				}
       
  1585 			phraseOffset=paraOffset; // we are now in the current position
       
  1586 			}
       
  1587 
       
  1588 // STEP 2.	Walk through all affected phrases in this paragraph
       
  1589 //			For each one, we may need to split it, and then apply the new format
       
  1590 
       
  1591 		do
       
  1592 			{
       
  1593 			__ASSERT_DEBUG(phrase<iPos.iParaBasePhraseElement+paraAttribs->iPhraseCount,User::Invariant());
       
  1594 //
       
  1595 			RPhraseAttribsEntry* phraseAttribs=&iPhraseIx->At(phrase);
       
  1596 			TInt len=phraseAttribs->Length();
       
  1597 
       
  1598 // STEP 2.1	Split the phrase at the beginning of the range?
       
  1599 
       
  1600 			if (phraseOffset>0)
       
  1601 				{		// can only happen for the first phrase
       
  1602 				/*
       
  1603 				 * The pointer paraAttribs is also stored in
       
  1604 				 * 'paraEntry.iParaAttribs'. The memory pointed to by this
       
  1605 				 * pointer will be released in CRichTextIndex's destructor.
       
  1606 				 */
       
  1607 				// coverity[leave_without_push]
       
  1608 				SplitPhraseL(phrase,phraseOffset,*phraseAttribs,*paraAttribs);	// inserts new phrase at correct position
       
  1609 				len-=phraseOffset;
       
  1610 				phraseOffset=0;
       
  1611 				iPos.iPhraseElement=++phrase;
       
  1612 				phraseAttribs=&iPhraseIx->At(phrase);
       
  1613 				}
       
  1614 
       
  1615 // STEP 2.2	Split the phrase at the end of the range?
       
  1616 
       
  1617 			if (len>charsToFormat)
       
  1618 				{	// phrase is longer than required format, so split it
       
  1619 				/*
       
  1620 				 * The pointer paraAttribs is also stored in
       
  1621 				 * 'paraEntry.iParaAttribs'. The memory pointed to by this
       
  1622 				 * pointer will be released in CRichTextIndex's destructor.
       
  1623 				 */
       
  1624 				// coverity[leave_without_push]
       
  1625 				SplitPhraseL(phrase,charsToFormat,*phraseAttribs,*paraAttribs);
       
  1626 				len=charsToFormat;
       
  1627 				phraseAttribs=&iPhraseIx->At(phrase);		// SplitPhraseL modifies the index array, we must do this!
       
  1628 				}
       
  1629 
       
  1630 			__ASSERT_DEBUG(phraseAttribs->Length()==len,User::Invariant());
       
  1631 
       
  1632 // STEP 2.3	Change the format of the current phrase layer
       
  1633 
       
  1634 			TCharFormatX format=aFormat;
       
  1635 			TCharFormatXMask mask=aMask;
       
  1636 			CCharFormatLayer* charLayer=phraseAttribs->CharFormat();
       
  1637 			if (!aRemoveSpecific)
       
  1638 				charLayer->Sense(format,mask);  // preserve current specific character formatting
       
  1639 			charLayer->SetL(format,mask);
       
  1640 
       
  1641 // STEP 2.4	Check for merging with previous phrase
       
  1642 
       
  1643 			if (phrase==iPos.iParaBasePhraseElement || !MergePhrases(phrase,*phraseAttribs,*paraAttribs))
       
  1644 				// if we don't merge this phrase, move on to the next one
       
  1645 				iPos.iPhraseElement=++phrase;
       
  1646 
       
  1647 			charsToFormat-=len;
       
  1648 			} while (charsToFormat);
       
  1649 
       
  1650 		__ASSERT_DEBUG(phrase==iPos.iParaBasePhraseElement+paraAttribs->iPhraseCount || aLength==0,User::Invariant());
       
  1651 
       
  1652 // STEP 3	Reduce the paragraph attributes back to canonical form
       
  1653 
       
  1654 // STEP 3.1	Check for merging at the end of the changes
       
  1655 
       
  1656 		if (phrase>iPos.iParaBasePhraseElement && phrase<iPos.iParaBasePhraseElement+paraAttribs->iPhraseCount)
       
  1657 			MergePhrases(phrase,iPhraseIx->At(phrase),*paraAttribs);	// mustn't adjust phrase index to follow merge
       
  1658 
       
  1659 // STEP 3.2	See if we can re-share the paragraph
       
  1660 
       
  1661 		if (paraAttribs->iPhraseCount==1)
       
  1662 			{	// This para has constant character formatting - can be shared.
       
  1663 			iPos.iPhraseElement=--phrase;
       
  1664 			Share(paraEntry,phrase);
       
  1665 			}
       
  1666 
       
  1667 // loop into next paragraph
       
  1668 		if (aLength==0)
       
  1669 			break;
       
  1670 		iPos.iParaElement++;
       
  1671 		paraOffset=0;
       
  1672 		iPos.iParaBasePhraseElement=phrase;
       
  1673 #ifdef _DEBUG
       
  1674 		ScanToPosition(aPos,EScanToPositionAbsolute);
       
  1675 		__ASSERT_DEBUG(iPos.iDocPos==aPos,User::Invariant());
       
  1676 		__ASSERT_DEBUG(iPos.iPhraseElement==phrase,User::Invariant());
       
  1677 		__ASSERT_DEBUG(iPos.iParaElementOffset==paraOffset,User::Invariant());
       
  1678 		__ASSERT_DEBUG(iPos.iPhraseElementOffset==phraseOffset,User::Invariant());
       
  1679 		__TEST_INVARIANT;
       
  1680 #endif
       
  1681 		}
       
  1682 
       
  1683 	CleanupStack::Pop();	// rollback item
       
  1684 
       
  1685 	__TEST_INVARIANT;
       
  1686 	}
       
  1687 
       
  1688 
       
  1689 void CRichTextIndex::RemoveSpecificParaFormatL(TInt aPos,TInt aLength)
       
  1690 // Removes all specific paragraph formatting from the specified region.
       
  1691 // For each paragraph covered by the range, check if its para attribs is in the
       
  1692 // shared list, or not.
       
  1693 // If its not shared, then simply reset the para format layer.
       
  1694 // If it is in the shared list, then a new shared para attribs must be created,
       
  1695 // and the reference count of the original decremented.
       
  1696 //
       
  1697 	{
       
  1698 	__TEST_INVARIANT;
       
  1699 
       
  1700 	TInt endPara=OwningParagraph(aPos+aLength);
       
  1701 	TInt currentPara=OwningParagraph(aPos);
       
  1702 	CParaAttribs* currentParaAttribs=NULL;
       
  1703 	while (currentPara<=endPara)
       
  1704 		{
       
  1705 		currentParaAttribs=(*iParaIx)[currentPara].iParaAttribs;
       
  1706 		if (!currentParaAttribs->IsShared())
       
  1707 			{// Reset specific paragraph format layer to be empty.
       
  1708 			currentParaAttribs->iParaFormat->Reset();  // remove specific formatting.
       
  1709 			}
       
  1710 		else
       
  1711 			{// Maintain the shared list reference
       
  1712 			CParaAttribs* resultantParaAttribs=CParaAttribs::NewL(currentParaAttribs);
       
  1713 			CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,resultantParaAttribs));
       
  1714 			resultantParaAttribs->iParaFormat->Reset();  // remove specific formatting
       
  1715 			CParaAttribs* shared=RequestShareL(resultantParaAttribs);  // Will return a non-NULL handle.
       
  1716 			__ASSERT_DEBUG(shared,Panic(EDebug));
       
  1717 			currentParaAttribs->Release();
       
  1718 			iSharedParaQueHead.AddLast(*resultantParaAttribs);  // Allows correct release of cell.
       
  1719 			CleanupStack::PopAndDestroy();
       
  1720 			(*iParaIx)[currentPara].iParaAttribs=shared;
       
  1721 			}
       
  1722 		currentPara++;
       
  1723 		}
       
  1724 
       
  1725 
       
  1726 	__TEST_INVARIANT;
       
  1727 	}
       
  1728 
       
  1729 
       
  1730 void CRichTextIndex::RemoveSpecificCharFormatL(TInt aPos,TInt aLength)
       
  1731 // Removes all specific character formatting from the specified region.
       
  1732 // For each paragraph covered by the range, check if its para attribs is in the
       
  1733 // shared list, or not.
       
  1734 // If its not shared, then simply reset the para format layer.
       
  1735 // If it is in the shared list, then a new shared para attribs must be created,
       
  1736 // and the reference count of the original decremented.
       
  1737 //
       
  1738 	{
       
  1739 	__TEST_INVARIANT;
       
  1740 
       
  1741 	TCharFormatX format;  // dummy format
       
  1742 	TCharFormatXMask mask;
       
  1743 	mask.ClearAll();
       
  1744 	ApplyCharFormatL(format,mask,aPos,aLength,ETrue);
       
  1745 
       
  1746 	__TEST_INVARIANT;
       
  1747 	}
       
  1748 
       
  1749 
       
  1750 TBool CRichTextIndex::MergePhrases(TInt aPos)
       
  1751 // Checks if the specified character position aPos is a phrase boundary.
       
  1752 // If so, then examines the two abutting phrases at aPos to see if they
       
  1753 // are identical;  in which case they are merged into one phrase that covers
       
  1754 // the sum of their lengths.
       
  1755 //
       
  1756 	{
       
  1757 	ScanToPosition(aPos,EScanToPositionAbsolute);
       
  1758 	return MergePhrases(iPos);
       
  1759 	}
       
  1760 
       
  1761 
       
  1762 TBool CRichTextIndex::MergePhrases(const TLogicalPosition& aPos)
       
  1763 // Checks if the specified character position record is a phrase boundary.
       
  1764 // If so, then examines the two abutting phrases at aPos to see if they
       
  1765 // are identical;  in which case they are merged into one phrase that covers
       
  1766 // the sum of their lengths.
       
  1767 //
       
  1768 	{
       
  1769 	TCurrentIndexRecords current;
       
  1770 	GetCurrentRecords(current);
       
  1771 	TBool phrasesMerged=EFalse;
       
  1772 	if (!FirstPhraseOfParagraph() && iPos.iPhraseElementOffset==0)
       
  1773 		{// Check if the 2 abutting phrases can be merged together.
       
  1774 		TInt rightPhraseElement=iPos.iPhraseElement;
       
  1775 		RPhraseAttribsEntry* rightPhrase=&(*iPhraseIx)[rightPhraseElement];
       
  1776 		RPhraseAttribsEntry* leftPhrase=&(*iPhraseIx)[rightPhraseElement-1];
       
  1777 		if (rightPhrase->IsIdentical(*leftPhrase))
       
  1778 			{// Merge the abutting phrases together.  Cannot merge picture/non-picture/z.l.p. phrase combinations.
       
  1779 			rightPhrase->AdjustLength(leftPhrase->Length());  //  Extend the right phrase length
       
  1780 			RemoveFromPhraseIx(rightPhraseElement-1);  // Free the resources taken by the left phrase - redundant
       
  1781 			(*iParaIx)[iPos.iParaElement].iParaAttribs->iPhraseCount--; // Update phrase count of owning CParaAttribs
       
  1782 			ScanToPosition(aPos.iDocPos,EScanToPositionAbsolute);  // Pick up new phrase index.
       
  1783 			phrasesMerged=ETrue;
       
  1784 			}
       
  1785 		}
       
  1786 	return phrasesMerged;
       
  1787 	}
       
  1788 
       
  1789 
       
  1790 /** Remove phrases from the containing object.  This includes
       
  1791 freeing referenced resources. (pictures etc.)
       
  1792 @param aPhraseIndex The first phrase to be deleted
       
  1793 @param aCount The number of phrases to be deleted
       
  1794 */
       
  1795 void CRichTextIndex::RemoveFromPhraseIx(TInt aPhraseIndex,TInt aCount)
       
  1796 	{
       
  1797  	// if the phrase being deleted is <= iLastPos  
       
  1798  	// then iLastPos will become invalid so should be reset
       
  1799  	if (aPhraseIndex <= iLastUsed.iPhraseElement )
       
  1800  		iLastUsed.Clear();
       
  1801  		
       
  1802 	for (TInt offset=aPhraseIndex;offset<(aPhraseIndex+aCount);offset++)
       
  1803 		{
       
  1804 		// discard phrases & book-keep the picture count
       
  1805 		RPhraseAttribsEntry& phrase=(*iPhraseIx)[offset];
       
  1806 		phrase.Discard();
       
  1807 		if (phrase.IsPicturePhrase())
       
  1808 			iPictureCount--;
       
  1809 		}
       
  1810 	iPhraseIx->Delete(aPhraseIndex,aCount);
       
  1811 	}
       
  1812 
       
  1813 void CRichTextIndex::GetParagraphFormatL(CParaFormat* aFormat,TInt aPos)const
       
  1814 // Fills aFormat with the effective Paragraph format attributes for the paragraph
       
  1815 // in which character position aPos is contained.
       
  1816 //
       
  1817 	{
       
  1818 	__TEST_INVARIANT;
       
  1819 
       
  1820 	TLogicalPosition cachePos(iLastUsed);
       
  1821 	TInt para=CONST_CAST(CRichTextIndex*,this)->OwningParagraph(aPos,&cachePos);
       
  1822 	(*iParaIx)[para].iParaAttribs->iParaFormat->SenseEffectiveL(aFormat);
       
  1823 	}
       
  1824 
       
  1825 void CRichTextIndex::GetSpecificParagraphFormatL(CParaFormat* aFormat,
       
  1826 												 TParaFormatMask& aMask,
       
  1827 												 TInt aPos)const
       
  1828 	{
       
  1829 	__TEST_INVARIANT;
       
  1830 
       
  1831 	TLogicalPosition cachePos(iLastUsed);
       
  1832 	TInt para=CONST_CAST(CRichTextIndex*,this)->OwningParagraph(aPos,&cachePos);
       
  1833 	CParaFormatLayer* pLayer = (*iParaIx)[para].iParaAttribs->iParaFormat;
       
  1834 	pLayer->SenseL(aFormat, aMask);
       
  1835 	}
       
  1836 
       
  1837 TInt CRichTextIndex::GetChars(TCharFormatX& aFormat,TInt aPos) const
       
  1838 // Returns the number of characters, commencing at aStartPos, that occupy the same phrase, and
       
  1839 // modifies aFormat, to hold the effective format of that phrase.
       
  1840 //
       
  1841 	{
       
  1842 	__TEST_INVARIANT;
       
  1843 
       
  1844 	CONST_CAST(CRichTextIndex*,this)->ScanToPosition(aPos,EScanToPositionAbsolute,&MUTABLE_CAST(TLogicalPosition&,iLastUsed));
       
  1845 	TCurrentIndexRecords current;
       
  1846 	GetCurrentRecords(current);
       
  1847 	TInt phraseLength;
       
  1848 	CCharFormatLayer* charFormatLayer;
       
  1849 	if (current.iPhrase)
       
  1850 		{// Specific character formatting held in phrase index.
       
  1851 		charFormatLayer=current.iPhrase->CharFormat();
       
  1852 		phraseLength=(current.iPhrase->Length())-(iPos.iPhraseElementOffset);
       
  1853 		}
       
  1854 	else
       
  1855 		{// Constant character formatting held in the para attribs
       
  1856 		charFormatLayer=current.iParaAttribs->iCharFormat;
       
  1857 		phraseLength=current.iParaEntry->iLength-iPos.iParaElementOffset;
       
  1858 		}
       
  1859 	charFormatLayer->SenseEffective(aFormat);
       
  1860 	return phraseLength;
       
  1861 	}
       
  1862 
       
  1863 
       
  1864 TInt CRichTextIndex::GetPictureSizeInTwips(TSize& aSize,TInt aPos)const
       
  1865 // Get the size of the specified picture into aSize.  The picture is specified by its
       
  1866 // character position.  Return KErrNotFound if there is no picture at the specified
       
  1867 // document position.
       
  1868 //
       
  1869 	{
       
  1870 	__TEST_INVARIANT;
       
  1871 
       
  1872 	CONST_CAST(CRichTextIndex*,this)->ScanToPosition(aPos,EScanToPositionAbsolute,&MUTABLE_CAST(TLogicalPosition&,iLastUsed));
       
  1873 	TCurrentIndexRecords current;
       
  1874 	GetCurrentRecords(current);
       
  1875 
       
  1876 	return (current.iPhrase)
       
  1877 		? current.iPhrase->GetPictureSizeInTwips(aSize)
       
  1878 		: KErrNotFound;
       
  1879 	}
       
  1880 
       
  1881 TPictureHeader* CRichTextIndex::PictureHeaderPtr(TInt aPos)
       
  1882 	{
       
  1883 	__TEST_INVARIANT;
       
  1884 
       
  1885 	CONST_CAST(CRichTextIndex*,this)->ScanToPosition(aPos,EScanToPositionAbsolute);
       
  1886 	TCurrentIndexRecords current;
       
  1887 	GetCurrentRecords(current);
       
  1888 	return current.iPhrase? current.iPhrase->PictureHeaderPtr() : 0;
       
  1889 	}
       
  1890 
       
  1891 TPictureHeader CRichTextIndex::PictureHeader(TInt aPos) const
       
  1892 // Return the picture header describing the picture at character position aPos.
       
  1893 // If there is no picture at character position aPos, a default picture header is returned.
       
  1894 //
       
  1895 	{
       
  1896 	const TPictureHeader* p = const_cast<CRichTextIndex*>(this)->PictureHeaderPtr(aPos);
       
  1897 	return p? *p : TPictureHeader();
       
  1898 	}
       
  1899 
       
  1900 
       
  1901 CPicture* CRichTextIndex::PictureHandleL(TInt aPos,MLayDoc::TForcePictureLoad aForceLoad)const
       
  1902 // Returns the handle of the concrete picture at character position aPos, if one exists;
       
  1903 // otherwise returns NULL.
       
  1904 //
       
  1905 	{
       
  1906 	__TEST_INVARIANT;
       
  1907 
       
  1908 	CONST_CAST(CRichTextIndex*,this)->ScanToPosition(aPos,EScanToPositionAbsolute,&MUTABLE_CAST(TLogicalPosition&,iLastUsed));
       
  1909 	TCurrentIndexRecords current;
       
  1910 	GetCurrentRecords(current);
       
  1911 	return (current.iPhrase)
       
  1912 		? (CPicture*)current.iPhrase->PictureHandleL(iText.PictureFactory(),iText.StoreResolver(),aPos,aForceLoad)
       
  1913 		: NULL;
       
  1914 	}
       
  1915 
       
  1916 
       
  1917 void CRichTextIndex::GetParaFormatL(CParaFormat* aFormat,TParaFormatMask& aVaries,TInt aPos,TInt aLength,CParaFormat::TParaFormatGetMode aMode)const
       
  1918 // Senses the paragraph format of para(s) covered by the region aPos to aPos+aLength-1.
       
  1919 // aFormat takes the values of all attributes, and the mask aMask indicates those values that change
       
  1920 // over the selected region, and are therefore *indeterminate*.
       
  1921 // Application: seeding paragraph formatting dialogs.
       
  1922 //
       
  1923 	{
       
  1924 	__TEST_INVARIANT;
       
  1925 
       
  1926 	TInt para=CONST_CAST(CRichTextIndex*,this)->OwningParagraph(aPos);
       
  1927 	TInt offset=(aLength==0)?0 :-1;
       
  1928 	TInt endPara=CONST_CAST(CRichTextIndex*,this)->OwningParagraph(aPos+(aLength+offset));
       
  1929 	aVaries.ClearAll();
       
  1930 	(*iParaIx)[para].iParaAttribs->iParaFormat->SenseEffectiveL(aFormat,aMode);  // Sense 1st paras' format.
       
  1931 	++para;
       
  1932 	CParaFormat* format=CParaFormat::NewLC();
       
  1933 	for (;para<=endPara;para++)
       
  1934 		{
       
  1935 		(*iParaIx)[para].iParaAttribs->iParaFormat->SenseEffectiveL(format,aMode);
       
  1936 		if (format->iLanguage!=aFormat->iLanguage)
       
  1937 			aVaries.SetAttrib(EAttParaLanguage);
       
  1938 		if (format->iFillColor!=aFormat->iFillColor)
       
  1939 			aVaries.SetAttrib(EAttFillColor);
       
  1940 		if (format->iLeftMarginInTwips!=aFormat->iLeftMarginInTwips)
       
  1941 			aVaries.SetAttrib(EAttLeftMargin);
       
  1942 		if (format->iRightMarginInTwips!=aFormat->iRightMarginInTwips)
       
  1943 			aVaries.SetAttrib(EAttRightMargin);
       
  1944 		if (format->iIndentInTwips!=aFormat->iIndentInTwips)
       
  1945 			aVaries.SetAttrib(EAttIndent);
       
  1946 		if (format->iHorizontalAlignment!=aFormat->iHorizontalAlignment)
       
  1947 			aVaries.SetAttrib(EAttAlignment);
       
  1948 		if (format->iVerticalAlignment!=aFormat->iVerticalAlignment)
       
  1949 			aVaries.SetAttrib(EAttVerticalAlignment);
       
  1950 		if (format->iLineSpacingInTwips!=aFormat->iLineSpacingInTwips)
       
  1951 			aVaries.SetAttrib(EAttLineSpacing);
       
  1952 		if (format->iLineSpacingControl!=aFormat->iLineSpacingControl)
       
  1953 			aVaries.SetAttrib(EAttLineSpacingControl);
       
  1954 		if (format->iSpaceBeforeInTwips!=aFormat->iSpaceBeforeInTwips)
       
  1955 			aVaries.SetAttrib(EAttSpaceBefore);
       
  1956 		if (format->iSpaceAfterInTwips!=aFormat->iSpaceAfterInTwips)
       
  1957 			aVaries.SetAttrib(EAttSpaceAfter);
       
  1958 		if (format->iKeepTogether!=aFormat->iKeepTogether)
       
  1959 			aVaries.SetAttrib(EAttKeepTogether);
       
  1960 		if (format->iKeepWithNext!=aFormat->iKeepWithNext)
       
  1961 			aVaries.SetAttrib(EAttKeepWithNext);
       
  1962 		if (format->iWidowOrphan!=aFormat->iWidowOrphan)
       
  1963 			aVaries.SetAttrib(EAttWidowOrphan);
       
  1964 		if (format->iWrap!=aFormat->iWrap)
       
  1965 			aVaries.SetAttrib(EAttWrap);
       
  1966 		if (format->iBorderMarginInTwips!=aFormat->iBorderMarginInTwips)
       
  1967 			aVaries.SetAttrib(EAttBorderMargin);
       
  1968 		if (format->iDefaultTabWidthInTwips!=aFormat->iDefaultTabWidthInTwips)
       
  1969 			aVaries.SetAttrib(EAttDefaultTabWidth);
       
  1970 		if (aMode==CParaFormat::EAllAttributes)
       
  1971 			{
       
  1972 			// Borders
       
  1973 			for (TInt border=0;border<CParaFormat::EMaxParaBorder;border++)
       
  1974 				{// Check each para border individually.  Assumes border format attributes run consecutively.
       
  1975 				if (!format->IsBorderEqual((CParaFormat::TParaBorderSide)border,*aFormat))
       
  1976 					aVaries.SetAttrib((TTextFormatAttribute)(EAttTopBorder+border));
       
  1977 				}
       
  1978 			// Bullet
       
  1979 			if (!format->iBullet && !aFormat->iBullet)
       
  1980 				{ /* neither para has bullet, so no variation */ }
       
  1981 			else if (!format->iBullet || !aFormat->iBullet
       
  1982 				|| *format->iBullet!=*aFormat->iBullet)
       
  1983 				aVaries.SetAttrib(EAttBullet);
       
  1984 			// The Tab-List
       
  1985 			if (format->TabCount()!=aFormat->TabCount())
       
  1986 				aVaries.SetAttrib(EAttTabStop);  // TabLists are different.
       
  1987 			else
       
  1988 				{// The 2 tablists have the same number of tab stops - but are not necessarily the same.
       
  1989 				TBool matched=ETrue;
       
  1990 				TInt tabCount=format->TabCount();
       
  1991 				for (TInt tabItem=0;tabItem<tabCount;tabItem++)
       
  1992 					{// Compare the 2 tabs.
       
  1993 					TTabStop comp1,comp2;
       
  1994 					comp1=format->TabStop(tabItem);
       
  1995 					comp2=aFormat->TabStop(tabItem);
       
  1996 					if (comp1!=comp2)
       
  1997 						matched=EFalse;
       
  1998 					}
       
  1999 				if (!matched)
       
  2000 					aVaries.SetAttrib(EAttTabStop);
       
  2001 				}
       
  2002 			}
       
  2003 		}
       
  2004 	CleanupStack::PopAndDestroy();
       
  2005 	}
       
  2006 
       
  2007 
       
  2008 // Compare all attributes in two formats and where they differ set the appropriate flag in the aVaries mask.
       
  2009 void CRichTextIndex::CheckForUndetermined(const TCharFormatX& aFormatA,const TCharFormatX& aFormatB,
       
  2010 										  TCharFormatXMask& aVaries) const
       
  2011 	{
       
  2012 	const TCharFormat& a = aFormatA.iCharFormat;
       
  2013 	const TCharFormat& b = aFormatB.iCharFormat;
       
  2014 	if (a.iLanguage!=b.iLanguage)
       
  2015 		aVaries.SetAttrib(EAttCharLanguage);
       
  2016 	if (a.iFontPresentation.iTextColor!=b.iFontPresentation.iTextColor)
       
  2017 		aVaries.SetAttrib(EAttColor);
       
  2018 	if (a.iFontPresentation.iHighlightColor!=b.iFontPresentation.iHighlightColor)
       
  2019 		aVaries.SetAttrib(EAttFontHighlightColor);
       
  2020 	if (a.iFontPresentation.iHighlightStyle!=b.iFontPresentation.iHighlightStyle)
       
  2021 		aVaries.SetAttrib(EAttFontHighlightStyle);
       
  2022 	if (a.iFontPresentation.iStrikethrough!=b.iFontPresentation.iStrikethrough)
       
  2023 		aVaries.SetAttrib(EAttFontStrikethrough);
       
  2024 	if (a.iFontPresentation.iUnderline!=b.iFontPresentation.iUnderline)
       
  2025 		aVaries.SetAttrib(EAttFontUnderline);
       
  2026 	if (a.iFontPresentation.iHiddenText!=b.iFontPresentation.iHiddenText)
       
  2027 		aVaries.SetAttrib(EAttFontHiddenText);
       
  2028 	if (a.iFontPresentation.iPictureAlignment!=b.iFontPresentation.iPictureAlignment)
       
  2029 		aVaries.SetAttrib(EAttFontPictureAlignment);
       
  2030 	if (a.iFontSpec.iHeight!=b.iFontSpec.iHeight)
       
  2031 		aVaries.SetAttrib(EAttFontHeight);
       
  2032 	if (!(a.iFontSpec.iTypeface==b.iFontSpec.iTypeface))
       
  2033 		aVaries.SetAttrib(EAttFontTypeface);
       
  2034 	if (a.iFontSpec.iFontStyle.Posture()!=b.iFontSpec.iFontStyle.Posture())
       
  2035 		aVaries.SetAttrib(EAttFontPosture);
       
  2036 	if (a.iFontSpec.iFontStyle.StrokeWeight()!=b.iFontSpec.iFontStyle.StrokeWeight())
       
  2037 		aVaries.SetAttrib(EAttFontStrokeWeight);
       
  2038 	if (a.iFontSpec.iFontStyle.PrintPosition()!=b.iFontSpec.iFontStyle.PrintPosition())
       
  2039 		aVaries.SetAttrib(EAttFontPrintPos);
       
  2040 	if (aFormatA.iParserTag != aFormatB.iParserTag)
       
  2041 		aVaries.SetAttrib(EAttParserTag);
       
  2042 	}
       
  2043 
       
  2044 
       
  2045 void CRichTextIndex::GetCharFormat(TCharFormatX& aFormat,TCharFormatXMask& aVaries,TInt aPos,TInt aLength)const
       
  2046 // Senses the character formatting of the phrase(s) covered by the region aPos to aPos+aLength-1.
       
  2047 // aFormat takes the values of all character format attributes, and the mask aMask indicates those
       
  2048 // values that change over the selected region, and are therefore *indeterminate*.
       
  2049 // Application: seeding character formatting dialogs.
       
  2050 // If aLength is zero, the character format sensed is that of the charcter immediatley to the left (behind) the cursor.
       
  2051 //
       
  2052 	{
       
  2053 	__TEST_INVARIANT;
       
  2054 
       
  2055 	aVaries.ClearAll();
       
  2056 	if (aLength==0)  // Get the format of the character to the left of the cursor.
       
  2057 		((CRichTextIndex*)this)->ScanToPosition(aPos,EScanToPositionMatchLeft);
       
  2058 	else
       
  2059 		((CRichTextIndex*)this)->ScanToPosition(aPos,EScanToPositionAbsolute);
       
  2060 	// Get char format of first phrase
       
  2061 	TCurrentIndexRecords current;
       
  2062 	GetCurrentRecords(current);
       
  2063 	if (current.iPhrase)
       
  2064 		current.iPhrase->CharFormat()->SenseEffective(aFormat);
       
  2065 	else
       
  2066 		current.iParaAttribs->iCharFormat->SenseEffective(aFormat);
       
  2067 	// Place pos at start of next phrase
       
  2068 	TInt pos=aPos+(CurrentPhraseLength()-CurrentPhraseOffset());
       
  2069 	while (pos<=aPos+aLength-1)
       
  2070 		{// Get the format of the next phrase and check if attributes change value
       
  2071 		((CRichTextIndex*)this)->ScanToPosition(pos,EScanToPositionAbsolute);
       
  2072 		GetCurrentRecords(current);
       
  2073 		TCharFormatX format;
       
  2074 		if (current.iPhrase)
       
  2075 			current.iPhrase->CharFormat()->SenseEffective(format);
       
  2076 		else
       
  2077 			current.iParaAttribs->iCharFormat->SenseEffective(format);
       
  2078 		CheckForUndetermined(format,aFormat,aVaries);
       
  2079 		pos+=CurrentPhraseLength();
       
  2080 		}
       
  2081 	}
       
  2082 
       
  2083 
       
  2084 void CRichTextIndex::GetSpecificCharFormatDirection(TCharFormatX& aFormat,
       
  2085 												   TCharFormatXMask& aMask,
       
  2086 												   TInt aPos,
       
  2087 												   TBool aGetLeft) const
       
  2088 	{
       
  2089 	__TEST_INVARIANT;
       
  2090 
       
  2091 	aMask.ClearAll();
       
  2092 	((CRichTextIndex*)this)->ScanToPosition(aPos,
       
  2093 		aGetLeft? EScanToPositionMatchLeft : EScanToPositionAbsolute);
       
  2094 	TCurrentIndexRecords current;
       
  2095 	GetCurrentRecords(current);
       
  2096 	if (current.iPhrase)
       
  2097 		current.iPhrase->CharFormat()->Sense(aFormat,aMask);
       
  2098 	else
       
  2099 		current.iParaAttribs->iCharFormat->Sense(aFormat,aMask);
       
  2100 	}
       
  2101 
       
  2102 void CRichTextIndex::GetSpecificCharFormat(TCharFormatX& aFormat,TCharFormatXMask& aMask,TInt aPos)const
       
  2103 // Return the format attributes store in the specific layer only, for the specified document position.
       
  2104 // THIS IS NOT THE EFFECTIVE FORMAT, BUT THE SPECIFIC FORMATTING ONLY.
       
  2105 //
       
  2106 	{
       
  2107 	GetSpecificCharFormatDirection(aFormat, aMask, aPos, ETrue);
       
  2108 	}
       
  2109 
       
  2110 
       
  2111 CParaAttribs* CRichTextIndex::RequestReclaimShareL(CParaAttribs* aParaAttribs,TParaAttribsEntry* aParaEntry)
       
  2112 // If the specified para attribs is currently on the shared list, then
       
  2113 // a specific para attribs is *reclaimed* and returned.
       
  2114 // The current share on the CParaAttribs is not *Released*.
       
  2115 // A new CParaAttribs is created of the same paragraph format as the shared one,
       
  2116 // but with a reference count of zero, (as this now has specific character formatting),
       
  2117 // and 1 phrase that is of the same character format as the shared one.
       
  2118 // The reclaimed specific para attribs is attactched to the specified para entry.
       
  2119 // NOTE:
       
  2120 // If the specified CParaAttribs is not currently on the shared list, NULL
       
  2121 // is returned, and aParaAttribs left unchanged..
       
  2122 // Assumes a previous call to ScanToPosition has correctly set the internal position record.
       
  2123 // This function can be called safely in any situation. (Except that it may *LEAVE*).
       
  2124 //
       
  2125 	{
       
  2126 	if (!(aParaAttribs->IsShared()))
       
  2127 		return NULL;  // This para attribs is not currently shared.
       
  2128 	// We are dealing with a shared paraAttribs from now on.
       
  2129 	CParaAttribs* reclaimedPara=CParaAttribs::NewL(aParaAttribs->iParaFormat);  // Create the re-claimed paraAttribs.
       
  2130 	CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,reclaimedPara));
       
  2131 	CCharFormatLayer* newFormat=CCharFormatLayer::NewCopyBaseL(aParaAttribs->iCharFormat);
       
  2132 	CleanupStack::PushL(newFormat);
       
  2133 	RPhraseAttribsEntry phrase(newFormat,aParaEntry->iLength);
       
  2134 	// Now insert this phrase into the index.
       
  2135 	iPhraseIx->InsertL(iPos.iPhraseElement,phrase);
       
  2136 	CleanupStack::Pop(2);  // newFormat, reclaimedPara
       
  2137 	return reclaimedPara;
       
  2138 	}
       
  2139 
       
  2140 
       
  2141 CParaAttribs* CRichTextIndex::RequestShareL(CParaAttribs* aParaAttribs,CCharFormatLayer* aCharFormat,CParaAttribs* aReservedCell)
       
  2142 // Attempts to re-use an existing paraAttribs that matches the one specified.
       
  2143 // Returns the handle of a CParaAttribs, or NULL if the specified argument cannot
       
  2144 // be shared.
       
  2145 // aCharFormat may be NULL, as may aReservedCell.
       
  2146 //
       
  2147 	{
       
  2148 	if (aParaAttribs->iRefCount<=0 && aParaAttribs->iPhraseCount>1)
       
  2149 		return NULL;  // This para has specific character formatting & multiple phrases and so cannot be shared.
       
  2150 	// This para has constant character formatting - can be shared.
       
  2151 	CCharFormatLayer* charFormat;
       
  2152 	if (aCharFormat)
       
  2153 		charFormat=aCharFormat;  // Has a phrase index.
       
  2154 	else
       
  2155 		charFormat=aParaAttribs->iCharFormat;  // Has constant char formatting.
       
  2156 	return GetParaAttribsL(aParaAttribs->iParaFormat,charFormat,aReservedCell);
       
  2157 	}
       
  2158 
       
  2159 
       
  2160 CParaAttribs* CRichTextIndex::RequestShare(const TLogicalPosition& aLogicalPosition)
       
  2161 // Returns a handle to a paraAttribs on the shared list that matches the paragraph at the
       
  2162 // specified position.  Returns NULL if the specified paragraph does not have constant
       
  2163 // character formatting.
       
  2164 //
       
  2165 	{
       
  2166 	CParaAttribs* paraAttribs=(*iParaIx)[aLogicalPosition.iParaElement].iParaAttribs;
       
  2167 	if (paraAttribs->iRefCount<=0 && paraAttribs->iPhraseCount>1)
       
  2168 		return NULL;  // This para has specific character formatting and so cannot be shared.
       
  2169 	// This para has constant character formatting - can be shared.
       
  2170 	return GetParaAttribs(aLogicalPosition);
       
  2171 	}
       
  2172 
       
  2173 
       
  2174 CParaAttribs* CRichTextIndex::GetParaAttribsL(const CParaFormatLayer* aParaFormat,const CCharFormatLayer* aCharFormat,
       
  2175 											  CParaAttribs* aReservedCell)
       
  2176 // Attempts to match the specified arguments to a CParaAttribs in the shared list.
       
  2177 // If matched, the handle of the matched para attribs is returned, and the reference count
       
  2178 // of that item is incremented.
       
  2179 //
       
  2180 // aReservedCell is a pre-allocated CParaAttribs that has been correctly setup.  If no match
       
  2181 // is found, and the reserved cell is specified, this is used in preference to creating a new one.
       
  2182 // If the reserved cell is not specified and there is no match, a new CParaAttribs of the correct
       
  2183 // specification is created and added to the shared list.
       
  2184 //
       
  2185 	{
       
  2186 	CParaAttribs* handle=FindSharedParaAttribs(*aParaFormat,*aCharFormat);
       
  2187 	if (handle)
       
  2188 		return handle;  // match found already in the shared list.
       
  2189 // There is no match, so create new sharedPara and add to list.
       
  2190 	if (!aReservedCell)
       
  2191 		aReservedCell=CParaAttribs::NewL(aParaFormat,aCharFormat);  // Reusing aReservedCell saves an automatic.
       
  2192 	iSharedParaQueHead.AddLast(*aReservedCell);
       
  2193 	return aReservedCell;
       
  2194 	}
       
  2195 
       
  2196 
       
  2197 CParaAttribs* CRichTextIndex::GetParaAttribs(CParaAttribs* aParaAttribs,CCharFormatLayer& aCharFormatLayer)
       
  2198 // Called by Reset.
       
  2199 //
       
  2200 	{
       
  2201 	CParaAttribs* handle=FindSharedParaAttribs(*aParaAttribs->iParaFormat,aCharFormatLayer);
       
  2202 	if (handle)
       
  2203 		return handle;
       
  2204 	else
       
  2205 		{// No match, so piece together new shared paraAttribs and add to shared para list.
       
  2206 		aParaAttribs->iRefCount=1;
       
  2207 		aParaAttribs->iCharFormat=&aCharFormatLayer;
       
  2208 		iSharedParaQueHead.AddLast(*aParaAttribs);
       
  2209 		return aParaAttribs;
       
  2210 		}
       
  2211 	}
       
  2212 
       
  2213 
       
  2214 CParaAttribs* CRichTextIndex::GetParaAttribs(const TLogicalPosition& aLogicalPosition)
       
  2215 // Attempts to match the specified arguments to a CParaAttribs in the shared list.
       
  2216 // If matched, the handle of the matched para attribs is returned, and the reference count
       
  2217 // of that item is incremented. If no match is found, the current para attribs is
       
  2218 // transformed into one that is placed in the shared list.
       
  2219 //
       
  2220 	{
       
  2221 	CParaAttribs* sourceParaAttribs=(*iParaIx)[aLogicalPosition.iParaElement].iParaAttribs;
       
  2222 	RPhraseAttribsEntry* sourcePhrase=&(*iPhraseIx)[aLogicalPosition.iPhraseElement];
       
  2223 	//
       
  2224 	CParaAttribs* handle=FindSharedParaAttribs(*sourceParaAttribs->iParaFormat,*sourcePhrase->CharFormat());
       
  2225 	if (handle)
       
  2226 		return handle;
       
  2227 	else
       
  2228 		{// No match, so piece together new shared paraAttribs and add to shared para list
       
  2229 		sourceParaAttribs->iRefCount=1;
       
  2230 		__ASSERT_ALWAYS(!sourcePhrase->IsPicturePhrase(),Panic(EReleasCharFormatLayerOwnershipCalledOnPicturePhrase));
       
  2231 		sourceParaAttribs->iCharFormat=sourcePhrase->ReleaseCharFormatLayerOwnership();
       
  2232 		sourcePhrase->Discard();
       
  2233 		iPhraseIx->Delete(aLogicalPosition.iPhraseElement);  // remove the deleted phrase from the phrase index.
       
  2234 		iSharedParaQueHead.AddLast(*sourceParaAttribs);
       
  2235 		return sourceParaAttribs;
       
  2236 		}
       
  2237 	}
       
  2238 
       
  2239 
       
  2240 CParaAttribs* CRichTextIndex::FindSharedParaAttribs(const CParaFormatLayer& aParaFormatLayer,const CCharFormatLayer& aCharFormatLayer)
       
  2241 // Attempts to match the specified arguments to an item in the shared para list.
       
  2242 // If found, the handle of the matched para attribs is returned, and the reference count
       
  2243 // of that item is incremented.
       
  2244 // If no match is made NULL is returned.
       
  2245 //
       
  2246 	{
       
  2247 	CParaAttribs* currentSharedPara=NULL;
       
  2248 	TBool matched=EFalse;
       
  2249 	if (!iSharedParaQueHead.IsEmpty())
       
  2250 		{
       
  2251 		TDblQueIter<CParaAttribs> iterator(iSharedParaQueHead);
       
  2252 		while ((currentSharedPara=iterator++)!=NULL)
       
  2253 			{// Try and match each item in the shared para list.
       
  2254 			matched=aParaFormatLayer.IsIdentical(currentSharedPara->iParaFormat);
       
  2255 			if (!matched)
       
  2256 				continue;
       
  2257 			matched=aCharFormatLayer.IsIdentical(currentSharedPara->iCharFormat);
       
  2258 			if (!matched)
       
  2259 				continue;
       
  2260 			// We have a match, so adjust reference count.
       
  2261 			currentSharedPara->iRefCount++;
       
  2262 			return currentSharedPara;
       
  2263 			}
       
  2264 		}
       
  2265 	return currentSharedPara;
       
  2266 	}
       
  2267 
       
  2268 
       
  2269 void CRichTextIndex::ScanToPosition(TInt aCharPos,TScanToPositionMode aMode,TLogicalPosition* aLastUsed/*=NULL*/)
       
  2270 // Move the internal position record to that indicated by the specified character position, aCharPos.
       
  2271 // Behaviour follows:
       
  2272 // aCharPos is considered to be goverened by the phrase covering aCharPos-1,
       
  2273 // except when aCharPos is the fist character of a paragraph, when
       
  2274 // aCharPos is goverened by the phrase coverng aCharPos.
       
  2275 // (If nothing else, aCharPos will be a paragraph delimiter or the end-of-document
       
  2276 // character.
       
  2277 // The implementation below matches to the right as standard, then checks if a left
       
  2278 // phrase match is available.
       
  2279 //
       
  2280 	{
       
  2281 	if (!aLastUsed || aLastUsed->iDocPos-aLastUsed->iParaElementOffset>aCharPos)
       
  2282 		iPos.Clear();  // Reset the internal position record.
       
  2283 	else
       
  2284 		{
       
  2285 		iPos=*aLastUsed;
       
  2286 		if (iPos.iDocPos>aCharPos || (aMode==EScanToPositionMatchLeft && iPos.iDocPos==aCharPos))
       
  2287 			{// reset to the start of paragraph if aPos < cache pos or aPos==chache Pos whilst matching left
       
  2288 			iPos.iDocPos-=iPos.iParaElementOffset;
       
  2289 			iPos.iParaElementOffset=iPos.iPhraseElementOffset=0;
       
  2290 			iPos.iPhraseElement=iPos.iParaBasePhraseElement;
       
  2291 			}
       
  2292 		}
       
  2293 
       
  2294 	TInt phraseElement=iPos.iPhraseElement;
       
  2295 	TInt startOfPara=iPos.iDocPos-iPos.iParaElementOffset;
       
  2296 	TInt paraElement=iPos.iParaElement;
       
  2297 	const CArrayFix<TParaAttribsEntry>& paraIx=*iParaIx;
       
  2298 	const TParaAttribsEntry* para=&paraIx[paraElement];
       
  2299 	TInt len=para->iLength;
       
  2300 	if (aCharPos>=(startOfPara+len))
       
  2301 		{
       
  2302 		iPos.iParaElementOffset=iPos.iPhraseElementOffset=0;
       
  2303 		phraseElement=iPos.iParaBasePhraseElement;
       
  2304 		const TParaAttribsEntry* end=paraIx.End(paraElement);
       
  2305 		do
       
  2306 			{// Find the paragraph...
       
  2307 			startOfPara+=len;
       
  2308 			if (!(para->iParaAttribs->IsShared()))  // Adjust position within phrase index
       
  2309 				phraseElement+=para->iParaAttribs->iPhraseCount;
       
  2310 			++paraElement;
       
  2311 			if (++para==end)
       
  2312 				{
       
  2313 				para=&paraIx[paraElement];
       
  2314 				end=paraIx.End(paraElement);
       
  2315 				}
       
  2316 			len=para->iLength;
       
  2317 			}
       
  2318 				while (aCharPos>=(startOfPara+len) && (paraElement+1) < paraIx.Count());
       
  2319 		iPos.iParaBasePhraseElement=phraseElement;
       
  2320 		iPos.iParaElement=paraElement;
       
  2321 		}
       
  2322 	TInt startOfPhrase=iPos.iParaElementOffset-iPos.iPhraseElementOffset;
       
  2323 // the offset within the paragraph.
       
  2324 	TInt paraElementOffset=aCharPos-startOfPara;
       
  2325 	iPos.iParaElementOffset=paraElementOffset;
       
  2326 
       
  2327 	if (!(para->iParaAttribs->IsShared()))
       
  2328 		{// Find phrase & offset within it.
       
  2329 		TInt lastPhraseLength=-1;  // Record phrase length in case left match required.
       
  2330 		const CArrayFix<RPhraseAttribsEntry>& phraseIx=*iPhraseIx;
       
  2331 		const RPhraseAttribsEntry* phrase=&phraseIx[phraseElement];
       
  2332 		const RPhraseAttribsEntry* end=NULL;
       
  2333 		for (;;)
       
  2334 			{	// Find the phrase in the paragraph...
       
  2335 			len=phrase->Length();
       
  2336 			if (paraElementOffset<(startOfPhrase+len))
       
  2337 				break;
       
  2338 			startOfPhrase+=len;
       
  2339 			lastPhraseLength=len;
       
  2340 			if (end==NULL)
       
  2341 				end=phraseIx.End(phraseElement);
       
  2342 			phraseElement++;
       
  2343 			if (++phrase<end)
       
  2344 				continue;
       
  2345 			phrase=&phraseIx[phraseElement];
       
  2346 			end=phraseIx.End(phraseElement);
       
  2347 			}//...and the offset within this.
       
  2348 		// Check now for match left.
       
  2349 		if ((aMode==EScanToPositionMatchLeft) && lastPhraseLength>=0 && paraElementOffset==startOfPhrase)
       
  2350 			{// Match to the left most phrase if at the start of a phrase.
       
  2351 			phraseElement--;
       
  2352 			iPos.iPhraseElementOffset=lastPhraseLength;
       
  2353 			}
       
  2354 		else
       
  2355 			iPos.iPhraseElementOffset=paraElementOffset-startOfPhrase;
       
  2356 		}
       
  2357 	else
       
  2358 		{
       
  2359 		__ASSERT_DEBUG(iPos.iParaBasePhraseElement==phraseElement,Panic(EDebug));
       
  2360 		}
       
  2361 	iPos.iPhraseElement=phraseElement;
       
  2362 	iPos.iDocPos=aCharPos;
       
  2363 	if (aLastUsed)
       
  2364 		*aLastUsed=iPos;
       
  2365 	}
       
  2366 
       
  2367 
       
  2368 TBool CRichTextIndex::FirstPhraseOfParagraph()const
       
  2369 // Interogates the current internal position record.
       
  2370 // Return ETrue if the current phrase element is the first phrase
       
  2371 // of specific character format in the current paragraph;
       
  2372 // Otherwise return EFalse.
       
  2373 //
       
  2374 	{return iPos.iPhraseElement==iPos.iParaBasePhraseElement;}
       
  2375 
       
  2376 
       
  2377 TInt CRichTextIndex::CurrentPhraseLength()const
       
  2378 // Return the length of the current phrase, where the current
       
  2379 // phrase is specified by the state of the internal position record.
       
  2380 //
       
  2381 	{
       
  2382 	if ((*iParaIx)[iPos.iParaElement].iParaAttribs->IsShared())
       
  2383 		return (*iParaIx)[iPos.iParaElement].iLength;
       
  2384 	else
       
  2385 		return (*iPhraseIx)[iPos.iPhraseElement].Length();
       
  2386 	}
       
  2387 
       
  2388 
       
  2389 TInt CRichTextIndex::CurrentPhraseOffset()const
       
  2390 // Returns the offset within the current phrase, where the current
       
  2391 // phrase is specified by the state of the internal position record.
       
  2392 //
       
  2393 	{
       
  2394 	if ((*iParaIx)[iPos.iParaElement].iParaAttribs->IsShared())
       
  2395 		return iPos.iParaElementOffset;  // only 1 phrase in para.
       
  2396 	else
       
  2397 		return iPos.iPhraseElementOffset;
       
  2398 	}
       
  2399 
       
  2400 
       
  2401 void CRichTextIndex::GetCurrentRecords(TCurrentIndexRecords& aRecord)const
       
  2402 // Package the phrase and paragraph index records that apply to the
       
  2403 // current paragraph and return this package.  It is assumed that
       
  2404 // the caller has already set the internal position record to a valid state.
       
  2405 //
       
  2406 	{
       
  2407 	aRecord.iParaEntry=&(*iParaIx)[iPos.iParaElement];
       
  2408 	aRecord.iParaAttribs=aRecord.iParaEntry->iParaAttribs;
       
  2409 	if (aRecord.iParaAttribs->IsShared())
       
  2410 		aRecord.iPhrase=NULL;
       
  2411 	else
       
  2412 		aRecord.iPhrase=&((*iPhraseIx)[iPos.iPhraseElement]);
       
  2413 	}
       
  2414 
       
  2415 
       
  2416 void CRichTextIndex::GetPhraseFormat(TCurrentIndexRecords& aCurrent,TCharFormatX& aFormat,TCharFormatXMask& aMask,
       
  2417 									 CCharFormatLayer*& aCharBase)const
       
  2418 // Fills aFormat and aMask with the character formatting information of the current record.
       
  2419 // aCharBase is set to the basedOn link if present.
       
  2420 // Encapsulates the concepts of the specific phrase index, and the constant character format.
       
  2421 // Only senses the format in the layer, does *NOT* perform a SenseEffective.
       
  2422 //
       
  2423 	{
       
  2424 	CCharFormatLayer* charFormatLayer=NULL;
       
  2425 	if (aCurrent.iPhrase)
       
  2426 		{// Specific character formatting held by phrase index.
       
  2427 		charFormatLayer=aCurrent.iPhrase->CharFormat();
       
  2428 		}
       
  2429 	else
       
  2430 		{// Constant character formatting held in the para attribs
       
  2431 		charFormatLayer=aCurrent.iParaAttribs->iCharFormat;
       
  2432 		}
       
  2433 	charFormatLayer->Sense(aFormat,aMask);
       
  2434 	aCharBase=(CCharFormatLayer*)(charFormatLayer->SenseBase());
       
  2435 	}
       
  2436 
       
  2437 
       
  2438 TInt CRichTextIndex::OwningParagraph(TInt aPos,TLogicalPosition* aLastUsed/*=NULL*/)const
       
  2439 // Return the paragraph element number that contains character position aPos.
       
  2440 // Assumes the caller has validated aPos.  Alters the internal record position.
       
  2441 //
       
  2442 	{
       
  2443 	((CRichTextIndex*)this)->ScanToPosition(aPos,EScanToPositionMatchLeft,aLastUsed);
       
  2444 	return iPos.iParaElement;
       
  2445 	}
       
  2446 
       
  2447 
       
  2448 void CRichTextIndex::SplitPhraseL(TInt aSplitPos)
       
  2449 // Splits the phrase at the offset aSplitPos, creating a new phrase
       
  2450 // which is filled with the split part of the current phrase, includig aSplitPos.
       
  2451 // The character format applied to the new phrase is the format of the phrase from which it has been split.
       
  2452 // The resulting new phrase is inserted into the phrase index immediately following the
       
  2453 // current element.  If aSplitPos is already at a phrase boundary, then no split is performed.
       
  2454 // (This means that a picture phrase in effect can never be split).
       
  2455 //
       
  2456 	{
       
  2457 	SetPhraseSplit(EFalse);
       
  2458 	ScanToPosition(aSplitPos,EScanToPositionAbsolute);
       
  2459 	if (iPos.iPhraseElementOffset==0)
       
  2460 		return;  // aSplitPos on a phrase boundary; urgo no split.
       
  2461 	TCurrentIndexRecords current; GetCurrentRecords(current);
       
  2462 // ASSERT: This function set can only be called on CParaAttribs that specific char format.
       
  2463 	__ASSERT_ALWAYS(current.iPhrase!=NULL,Panic(ESplitPhraseCalledOnSharedPara));
       
  2464 	DoSplitPhraseL(*current.iPhrase,iPos.iPhraseElementOffset,current.iParaAttribs);
       
  2465 	}
       
  2466 
       
  2467 
       
  2468 void CRichTextIndex::SplitPhraseL(TInt aPhraseElement,TInt aPhraseOffset,CParaAttribs* aParaAttribs)
       
  2469 // Splits the specified phrase at the offset aOffsetInPhrase, creating a new phrase
       
  2470 // which is filled with the split part of the current phrase, includig the split pos.
       
  2471 // The character format applied to the new phrase is the format of the phrase from which it has been split.
       
  2472 // The resulting new phrase is inserted into the phrase index immediately following the
       
  2473 // current element.  If the split pos is already at a phrase boundary, then no split is performed.
       
  2474 // (This means that a picture phrase in effect can never be split).
       
  2475 //
       
  2476 	{
       
  2477 	SetPhraseSplit(EFalse);
       
  2478 	RPhraseAttribsEntry& phrase=(*iPhraseIx)[aPhraseElement];
       
  2479 	if ((aPhraseOffset>0) && (aPhraseOffset<phrase.Length()))
       
  2480 		{// Not at a phrase boundary so split the current phrase.
       
  2481 		DoSplitPhraseL(phrase,aPhraseOffset,aParaAttribs);
       
  2482 		}
       
  2483 	}
       
  2484 
       
  2485 
       
  2486 void CRichTextIndex::DoSplitPhraseL(RPhraseAttribsEntry& aCurrentPhrase,TInt aPhraseOffset,CParaAttribs* aParaAttribs)
       
  2487 // Splits the specified phrase, creating a new phrase of the same character format.
       
  2488 // This new phrase is inserted into the phrase index immediately following the current one.
       
  2489 //
       
  2490 	{
       
  2491 // ASSERT: Cannot split a picture phrase.
       
  2492 	__ASSERT_DEBUG(!aCurrentPhrase.IsPicturePhrase(),Panic(ESplitPhraseCalledOnPicturePhrase));
       
  2493 	CCharFormatLayer* layer=CCharFormatLayer::NewCopyBaseL(aCurrentPhrase.CharFormat());
       
  2494 	CleanupStack::PushL(layer);
       
  2495 	RPhraseAttribsEntry newPhrase(layer,aCurrentPhrase.Length()-aPhraseOffset);
       
  2496 	iPhraseIx->InsertL(iPos.iPhraseElement+1,newPhrase);
       
  2497 	CleanupStack::Pop();
       
  2498 	//
       
  2499 	// InsertL() has invalidated the current internal position record.
       
  2500 	iPhraseIx->At(iPos.iPhraseElement).SetLength(aPhraseOffset);
       
  2501 	SetPhraseSplit(ETrue);
       
  2502 	aParaAttribs->iPhraseCount++;
       
  2503 	}
       
  2504 
       
  2505 
       
  2506 TBool CRichTextIndex::HasMarkupData(const CFormatLayer* aGlobalParaFormatLayer)const
       
  2507 // Returns ETure if this rich text instance has any specific markup,
       
  2508 // otherwise returns EFalse.
       
  2509 // The presence of specific markup is indicated by the following...
       
  2510 // 1) Style list is present (if style table is owned by the rich text)
       
  2511 // 2) Any phrase index content
       
  2512 // 3) >1 shared para attribs
       
  2513 //   or
       
  2514 // 3) 1 shared para attribs that has specific markup
       
  2515 // 4) any paragraph is based on a style other than normal (the global paraformatlayer)
       
  2516 //
       
  2517 	{
       
  2518 	TInt phraseCount=PhraseCount();
       
  2519 	if (phraseCount>0)
       
  2520 		return ETrue;
       
  2521 	//
       
  2522 	TInt sharedParaCount=SharedParaCount(this);
       
  2523 	__ASSERT_ALWAYS(sharedParaCount>=1 || (sharedParaCount==0 && phraseCount>0),Panic(ERichTextIndexIntegrityErr));
       
  2524 	if (sharedParaCount>1)
       
  2525 		return ETrue;
       
  2526 	const CParaAttribs* paraAttribs=iSharedParaQueHead.First();
       
  2527 	if (!paraAttribs->iParaFormat->IsEmpty())
       
  2528 		return ETrue;
       
  2529 	if (!paraAttribs->iCharFormat->IsEmpty())
       
  2530 		return ETrue;
       
  2531 	if (paraAttribs->iParaFormat->SenseBase()!=aGlobalParaFormatLayer)
       
  2532 		return ETrue;
       
  2533 	return EFalse;
       
  2534 	}
       
  2535 
       
  2536 
       
  2537 TInt CRichTextIndex::SharedParaCount(const CRichTextIndex* aSource)const
       
  2538 // Return a count of the number of shared paragraph formats present
       
  2539 // in the specified object.
       
  2540 //
       
  2541 	{
       
  2542 	TInt sharedParaCount = 0;
       
  2543 	TDblQueIter<CParaAttribs> iterator( MUTABLE_CAST(TDblQue<CParaAttribs>&, aSource->iSharedParaQueHead) );
       
  2544 	while ( iterator++ != NULL )
       
  2545 		sharedParaCount++;
       
  2546 	return sharedParaCount;
       
  2547 	}
       
  2548 
       
  2549 
       
  2550 void CRichTextIndex::AppendTakingSolePictureOwnershipL(const CRichTextIndex* aSource,const TGlobalLayerInfoAppend& aGlobalLayerInfo)
       
  2551 // No paragraph style information is appended.
       
  2552 //
       
  2553 	{
       
  2554 	CancelInsertCharFormat();
       
  2555 	CONST_CAST(CRichTextIndex*,aSource)->CancelInsertCharFormat();
       
  2556 
       
  2557 	TInt origParaCount=ParagraphCount();
       
  2558 	TInt origPhraseCount=iPhraseIx->Count();
       
  2559 	TRAPD(ret,
       
  2560 		AppendParaIndexL(aSource,aGlobalLayerInfo);
       
  2561 		AppendPhraseIndexL(aSource,aGlobalLayerInfo);
       
  2562 		);
       
  2563 	if (ret!=KErrNone)
       
  2564 		{
       
  2565 		RemoveFromPhraseIx(origPhraseCount,iPhraseIx->Count()-origPhraseCount);	// remove any added phrases etc.
       
  2566 		RbRemoveInsertedParaAttribsEntries(origParaCount,ParagraphCount()-origParaCount);	// remove any added paragraphs etc.
       
  2567 		NormalizeSharedList();		// remove any added shared paragraph attributes
       
  2568 		User::Leave(ret);
       
  2569 		}
       
  2570 
       
  2571 	__TEST_INVARIANT;
       
  2572 	}
       
  2573 
       
  2574 
       
  2575 void CRichTextIndex::AppendParaIndexL(const CRichTextIndex* aSource,const TGlobalLayerInfoAppend& aGlobalLayerInfo)
       
  2576 //
       
  2577 	{
       
  2578 	CRichTextStoreMap<CParaAttribs>* map=CRichTextStoreMap<CParaAttribs>::NewLC(SharedParaCount(aSource));
       
  2579 
       
  2580 	AppendSharedFormatsL(*map,aSource,aGlobalLayerInfo);
       
  2581 
       
  2582 	// Extend para index by required amount
       
  2583 	TInt originalParaCount=iParaIx->Count();
       
  2584 	TInt requiredParaCount=aSource->iParaIx->Count();
       
  2585 	iParaIx->AppendL(TParaAttribsEntry(),requiredParaCount);
       
  2586 
       
  2587 	for (TInt ii=0;ii<requiredParaCount;ii++)
       
  2588 		{// Copy the paragraph data for each of the appended paragraphs.
       
  2589 		const TParaAttribsEntry& sParaEntry=(*aSource->iParaIx)[ii];
       
  2590 		const CParaAttribs* sParaAttribs=sParaEntry.iParaAttribs;
       
  2591 		CParaAttribs* tParaAttribs;
       
  2592 		if (sParaAttribs->IsShared())
       
  2593 			{
       
  2594 			tParaAttribs=map->Item(sParaAttribs);
       
  2595 			__ASSERT_DEBUG(tParaAttribs!=NULL,Panic(ESharedFormatsMapIntegrityError));
       
  2596 			tParaAttribs->iRefCount++;
       
  2597 			}
       
  2598 		else
       
  2599 			{// Have to build up the specific para attribs
       
  2600 			tParaAttribs=CParaAttribs::NewL(sParaAttribs->iParaFormat);  // sets iRefCount=0, copies the format layer
       
  2601 			tParaAttribs->iParaFormat->SetBase(aGlobalLayerInfo.iAggParaFormatLayer);
       
  2602 			tParaAttribs->iPhraseCount=sParaAttribs->iPhraseCount;
       
  2603 			}
       
  2604 		TParaAttribsEntry& tParaEntry=(*iParaIx)[originalParaCount+ii];
       
  2605 		tParaEntry.iLength=sParaEntry.iLength;
       
  2606 		tParaEntry.iParaAttribs=tParaAttribs;
       
  2607 		
       
  2608 		// tParaAttribs is attached to CRichTextIndex::iParaIx, and will be
       
  2609 		// released in destructor CRichTextIndex::~CRichTextIndex().
       
  2610 		// To prevent Coverity from reporting defect, add a comment:
       
  2611 		// coverity[memory_leak]
       
  2612 		}
       
  2613 
       
  2614 	CleanupStack::PopAndDestroy();  // map
       
  2615 	}
       
  2616 
       
  2617 
       
  2618 void CRichTextIndex::AppendSharedFormatsL(CParaAttribsMap& aMap,const CRichTextIndex* aSource,
       
  2619 											const TGlobalLayerInfoAppend& aGlobalLayerInfo)
       
  2620 // A map is kept, that for each original format specifies the corresponding new one that appended
       
  2621 // paragraphs should use.
       
  2622 //
       
  2623 	{
       
  2624 	TDblQueIter<CParaAttribs> iterator(MUTABLE_CAST(TDblQue<CParaAttribs>&,aSource->iSharedParaQueHead));
       
  2625 	CParaAttribs* currentSharedPara;
       
  2626 	while ((currentSharedPara=iterator++)!=NULL)
       
  2627 		{
       
  2628 		__ASSERT_DEBUG(currentSharedPara->IsShared(),User::Invariant());
       
  2629 
       
  2630 		CParaFormatLayer* sPl=currentSharedPara->iParaFormat;
       
  2631 		CCharFormatLayer* sCl=currentSharedPara->iCharFormat;
       
  2632 		sPl->SetBase(aGlobalLayerInfo.iAggParaFormatLayer);  // alter the original so that the following GetParaAttribsL() call will
       
  2633 		sCl->SetBase(aGlobalLayerInfo.iAggCharFormatLayer);  // match based on our global format layers, not those of aSource's.
       
  2634 		CParaAttribs* newParaAttribs=FindSharedParaAttribs(*sPl,*sCl);
       
  2635 		sPl->SetBase(aGlobalLayerInfo.iComParaFormatLayer);  // set the global format layers back again, cos we don't want to
       
  2636 		sCl->SetBase(aGlobalLayerInfo.iComCharFormatLayer);  // corrupt aSource.
       
  2637 		if (newParaAttribs==NULL)
       
  2638 			{
       
  2639 			newParaAttribs=CParaAttribs::NewL(currentSharedPara);
       
  2640 			// change the based-on links (they are copied in the construction)
       
  2641 			newParaAttribs->iParaFormat->SetBase(aGlobalLayerInfo.iAggParaFormatLayer);
       
  2642 			newParaAttribs->iCharFormat->SetBase(aGlobalLayerInfo.iAggCharFormatLayer);
       
  2643 			iSharedParaQueHead.AddLast(*newParaAttribs);
       
  2644 			}
       
  2645 		newParaAttribs->iRefCount--;  // we have not yet linked incoming para - taken out no shares just yet
       
  2646 		aMap.Bind(currentSharedPara,newParaAttribs);
       
  2647 		}
       
  2648 	}
       
  2649 
       
  2650 void CRichTextIndex::AppendPhraseIndexL(const CRichTextIndex* aSource,const TGlobalLayerInfoAppend& aGlobalLayerInfo)
       
  2651 //
       
  2652 	{
       
  2653 	TInt originalPhraseCount=iPhraseIx->Count();
       
  2654 	TInt requiredPhraseCount=aSource->iPhraseIx->Count();
       
  2655 
       
  2656 	// Extend phrase index by required amount
       
  2657 	iPhraseIx->AppendL(RPhraseAttribsEntry(),requiredPhraseCount);
       
  2658 
       
  2659 	CArrayFixFlat<TInt>* pictureMap=new(ELeave) CArrayFixFlat<TInt>(16);
       
  2660 	CleanupStack::PushL(pictureMap);
       
  2661 
       
  2662 	for (TInt jj=0;jj<requiredPhraseCount;jj++)
       
  2663 		{
       
  2664 		RPhraseAttribsEntry& sPhrase=(*aSource->iPhraseIx)[jj];
       
  2665 		CCharFormatLayer* sCharFormatLayer=sPhrase.CharFormat();
       
  2666 		CCharFormatLayer* charLayer=CCharFormatLayer::NewL(sCharFormatLayer);
       
  2667 		charLayer->SetBase(aGlobalLayerInfo.iAggCharFormatLayer);
       
  2668 		RPhraseAttribsEntry& tPhrase=(*iPhraseIx)[jj+originalPhraseCount];
       
  2669 		if (!sPhrase.IsPicturePhrase())
       
  2670 			tPhrase=RPhraseAttribsEntry(charLayer,sPhrase.Length());
       
  2671 		else
       
  2672 			{
       
  2673 			TPictureHeader hdr;
       
  2674 			hdr=sPhrase.PictureHeader();  // copy the header from the source
       
  2675 			if (hdr.iPicture.IsPtr())
       
  2676 				hdr.iPicture=NULL;		  // pic.ownership transferred later.
       
  2677 			CleanupStack::PushL(charLayer);
       
  2678 			TBool ownershipTaken(EFalse);
       
  2679 			CPicturePhrase* picPhrase=CPicturePhrase::NewL(hdr,charLayer,ownershipTaken);
       
  2680 			CleanupStack::Pop();  // charLayer
       
  2681 			tPhrase=RPhraseAttribsEntry(picPhrase);
       
  2682 			iPictureCount++;
       
  2683 			pictureMap->AppendL(jj);
       
  2684 			}
       
  2685 		}
       
  2686 
       
  2687 	// transfer pictures now
       
  2688 	for (TInt kk=pictureMap->Count();--kk>=0;)
       
  2689 		{
       
  2690 		TInt jj=pictureMap->At(kk);
       
  2691 		TPictureHeader* sHeader=(*aSource->iPhraseIx)[jj].PictureHeaderPtr();
       
  2692 		__ASSERT_DEBUG(sHeader,User::Invariant());
       
  2693 		if (sHeader->iPicture.IsPtr())
       
  2694 			{	// transfer picture to us
       
  2695 			TPictureHeader* tHeader=(*iPhraseIx)[jj+originalPhraseCount].PictureHeaderPtr();
       
  2696 			__ASSERT_DEBUG(tHeader,User::Invariant());
       
  2697 			tHeader->iPicture=sHeader->iPicture.AsPtr();
       
  2698 			sHeader->iPicture=NULL;
       
  2699 			}
       
  2700 		}
       
  2701 
       
  2702 	CleanupStack::PopAndDestroy();	// pictureMap
       
  2703 	}
       
  2704 
       
  2705 
       
  2706 void CRichTextIndex::AppendParagraphL(const CParaFormatLayer* aGlobalParaFormatLayer,
       
  2707 									  const CCharFormatLayer* aGlobalCharFormatLayer,
       
  2708 									  TInt aReplicas)
       
  2709 // Append aReplicas empty paragraphs, the format of which is based on the
       
  2710 // global format layers.
       
  2711 //
       
  2712 	{
       
  2713 	__TEST_INVARIANT;
       
  2714 
       
  2715 	// add the shared para format record
       
  2716 	CParaAttribs* paraAttribs=CParaAttribs::NewL(aGlobalParaFormatLayer,aGlobalCharFormatLayer);
       
  2717 	paraAttribs->iParaFormat->SetBase(aGlobalParaFormatLayer);  // reset the base properly
       
  2718 	paraAttribs->iCharFormat->SetBase(aGlobalCharFormatLayer);  // reset the base properly.
       
  2719 	CleanupStack::PushL(TCleanupItem(ReleaseOnCleanup,paraAttribs));
       
  2720 	CParaAttribs* paUsed=GetParaAttribsL(aGlobalParaFormatLayer,aGlobalCharFormatLayer,paraAttribs);
       
  2721 	// guaranteed not to leave as 3rd argument specified.
       
  2722 	// The refCount of paUsed is incremented here by one share.
       
  2723 	//
       
  2724 	// add the paragraph records
       
  2725 	TParaAttribsEntry paraEntry(1,paUsed);
       
  2726 	iParaIx->AppendL(paraEntry,aReplicas);
       
  2727 	if (paUsed!=paraAttribs)
       
  2728 		CleanupStack::PopAndDestroy();
       
  2729 	else
       
  2730 		CleanupStack::Pop();
       
  2731 	//
       
  2732 	// set the ref count of paUsed
       
  2733 	paUsed->iRefCount+=(aReplicas-1);  // compensate for already adding 1 share.
       
  2734 
       
  2735 	__TEST_INVARIANT;
       
  2736 	}
       
  2737 
       
  2738 
       
  2739 TParaAttribsEntry::TParaAttribsEntry():
       
  2740 	iLength(0),
       
  2741 	iParaAttribs(NULL)
       
  2742 	{
       
  2743 	}
       
  2744 
       
  2745 
       
  2746 TParaAttribsEntry::TParaAttribsEntry(TInt aLength,CParaAttribs* aParaAttribs):
       
  2747 	iLength(aLength),
       
  2748 	iParaAttribs(aParaAttribs)
       
  2749 	{
       
  2750 	}
       
  2751 
       
  2752 
       
  2753 CParaAttribs* CParaAttribs::NewL(const CParaFormatLayer* aParaLayer,const CCharFormatLayer* aCharLayer)
       
  2754 // Returns a handle to a new instance of this class.
       
  2755 // Creates a CParaAttribs with constant character formatting.
       
  2756 //
       
  2757 	{
       
  2758 	CParaAttribs* self=new(ELeave) CParaAttribs();
       
  2759 	CleanupStack::PushL(self);
       
  2760 	self->iParaFormat=CParaFormatLayer::NewCopyBaseL(aParaLayer);
       
  2761 	// iParaFormat will be released in destructor.
       
  2762 	// To prevent Coverity from reporting defect, add a comment:
       
  2763 	// coverity[leave_without_push]
       
  2764 	self->iCharFormat=CCharFormatLayer::NewCopyBaseL(aCharLayer);
       
  2765 	CleanupStack::Pop();
       
  2766 	self->iRefCount=EPrimeSharedCount;
       
  2767 	return self;
       
  2768 	}
       
  2769 
       
  2770 
       
  2771 CParaAttribs* CParaAttribs::NewL(const CParaFormatLayer* aParaLayer)
       
  2772 // Returns a handle to a new instance of this class.
       
  2773 // Creates a CParaAttribs for specific character formatting.
       
  2774 //
       
  2775 	{
       
  2776 	CParaAttribs* self=new(ELeave) CParaAttribs();
       
  2777 	CleanupStack::PushL(self);
       
  2778 	self->iParaFormat=CParaFormatLayer::NewCopyBaseL(aParaLayer);
       
  2779 	CleanupStack::Pop();
       
  2780 	self->iRefCount=EPrimeNonSharedCount;
       
  2781 	self->iPhraseCount=1;
       
  2782 	return self;
       
  2783 	}
       
  2784 
       
  2785 
       
  2786 CParaAttribs* CParaAttribs::NewL(const CParaAttribs* aParaAttribs)
       
  2787 	{
       
  2788 	return NewL(aParaAttribs->iParaFormat,aParaAttribs->iCharFormat);
       
  2789 	}
       
  2790 
       
  2791 
       
  2792 CParaAttribs::CParaAttribs():
       
  2793 	iRefCount(-1)  // Ensures Destruct works correctly when called on a semi-initialised object.
       
  2794 	{
       
  2795 	}
       
  2796 
       
  2797 
       
  2798 void CParaAttribs::Release()
       
  2799 // Release a share on this CParaAttribs.
       
  2800 // If after this, no shares remain, destroy this CParaAttribs.
       
  2801 //
       
  2802 	{
       
  2803 	iRefCount--;
       
  2804 	if (iRefCount<=0)
       
  2805 		delete this;
       
  2806 	}
       
  2807 
       
  2808 
       
  2809 void CParaAttribs::Release(TInt aCount)
       
  2810 // Release aCount number of shares of this CParaAttribs.
       
  2811 // If after this, no shares remain, destroy this CParaAttribs.
       
  2812 //
       
  2813 	{
       
  2814 	iRefCount-=aCount;
       
  2815 	if (iRefCount<=0)
       
  2816 		delete this;
       
  2817 	}
       
  2818 
       
  2819 
       
  2820 CParaAttribs::~CParaAttribs()
       
  2821 // Release the memory associated with this object.
       
  2822 //
       
  2823 	{
       
  2824 	delete iParaFormat;
       
  2825 	if (iRefCount==0)
       
  2826 		{// Constant character formatting - in the shared list.
       
  2827 		delete iCharFormat;
       
  2828 		link.Deque();  // Remove this para attribs from the shared list.
       
  2829 		}
       
  2830 	}
       
  2831 
       
  2832 
       
  2833 TInt CParaAttribs::PhraseCount()const
       
  2834 // Return a count of the number of phrases in this para attribs.
       
  2835 //
       
  2836 	{return (iRefCount>=1)?1:iPhraseCount;}
       
  2837 
       
  2838 
       
  2839 DLLEXPORT_C void RPhraseAttribsEntry::__DbgTestInvariant()const
       
  2840 // Class invariants.
       
  2841 //
       
  2842 	{
       
  2843 #ifdef _DEBUG
       
  2844 // ASSERT: iLength is +ve (applying to character formatting, or	is set to indicate a picture phrase.
       
  2845 	__ASSERT_DEBUG(iLength>=0 || IsPicturePhrase(),User::Invariant());
       
  2846 #endif
       
  2847 	}
       
  2848 
       
  2849 
       
  2850 RPhraseAttribsEntry::RPhraseAttribsEntry():
       
  2851 	iLength(0),
       
  2852 	iCharFormat(NULL)
       
  2853 	{
       
  2854 	}
       
  2855 
       
  2856 
       
  2857 RPhraseAttribsEntry::RPhraseAttribsEntry(CCharFormatLayer* aCharFormat,TInt aLength):
       
  2858 	iLength(aLength),
       
  2859 	iCharFormat(aCharFormat)
       
  2860 	{
       
  2861 	}
       
  2862 
       
  2863 
       
  2864 RPhraseAttribsEntry::RPhraseAttribsEntry(CPicturePhrase* aPicturePhrase):
       
  2865 	iLength(EPictureIndicator),
       
  2866 	iPicturePhrase(aPicturePhrase)
       
  2867 	{
       
  2868 	}
       
  2869 
       
  2870 
       
  2871 void RPhraseAttribsEntry::AssignAndRelease(const RPhraseAttribsEntry& aPhrase)
       
  2872 // Assign the state of the specified object to this,
       
  2873 //
       
  2874 	{
       
  2875 	iLength=aPhrase.iLength;
       
  2876 	iCharFormat=aPhrase.iCharFormat;  // both union members share the same address space, so this is fine.
       
  2877 	}
       
  2878 
       
  2879 
       
  2880 void RPhraseAttribsEntry::Discard()
       
  2881 // Free storage.
       
  2882 //
       
  2883 	{
       
  2884 	__TEST_INVARIANT;
       
  2885 
       
  2886 	if (iLength==EPictureIndicator)
       
  2887 		delete iPicturePhrase;
       
  2888 	else
       
  2889 		delete iCharFormat;
       
  2890 	}
       
  2891 
       
  2892 
       
  2893 void RPhraseAttribsEntry::ExternalizeL(RWriteStream& aStream)const
       
  2894 // Save this phrase into aStream.
       
  2895 //
       
  2896 	{
       
  2897 	TUint8 picIndicator=(TUint8)(IsPicturePhrase()!=EFalse);
       
  2898 	aStream.WriteUint8L(picIndicator);
       
  2899 	aStream.WriteInt32L(Length());
       
  2900 	aStream<< *CharFormat();
       
  2901 	if ((TBool)picIndicator)
       
  2902 		aStream<< *PictureHeaderPtr();
       
  2903 	}
       
  2904 
       
  2905 
       
  2906 CCharFormatLayer* RPhraseAttribsEntry::CharFormat()const
       
  2907  // Returns a pointer the CCharFormatLayer of this phrase.
       
  2908  //
       
  2909  	{return (iLength==EPictureIndicator)?iPicturePhrase->iCharFormat:iCharFormat;}
       
  2910 
       
  2911 
       
  2912 void RPhraseAttribsEntry::SetLength(TInt aLength)
       
  2913 // Sets the phrase length.
       
  2914 //
       
  2915 	{
       
  2916 	__TEST_INVARIANT;
       
  2917 
       
  2918 	iLength=aLength;
       
  2919 	}
       
  2920 
       
  2921 
       
  2922 void RPhraseAttribsEntry::AdjustLength(TInt aIncrement)
       
  2923 // Adjusts the length of the phrase by the signed value aIncrement.
       
  2924 //
       
  2925 	{
       
  2926 // ASSERT: The length of a picture phrase may only be altered by deleting it, in which case
       
  2927 //			the only adjustment made will be an increment of -1 (EPictureIndicator).
       
  2928 	__ASSERT_DEBUG(!IsPicturePhrase() || (IsPicturePhrase() && aIncrement==EPictureIndicator)
       
  2929 									  || (IsPicturePhrase() && aIncrement==0)
       
  2930 									  ,Panic(EModifiedPicturePhraseLength));
       
  2931 	TInt len=iLength;
       
  2932 	iLength=(len==EPictureIndicator)
       
  2933 		? len-aIncrement
       
  2934 		: len+aIncrement;
       
  2935 	}
       
  2936 
       
  2937 
       
  2938 TInt RPhraseAttribsEntry::GetPictureSizeInTwips(TSize& aSize)const
       
  2939 // If this is a picture phrase, write the size of the picture to aSize,
       
  2940 // otherwise return KErrNotFound.
       
  2941 //
       
  2942 	{
       
  2943 	if (!IsPicturePhrase())
       
  2944 		return KErrNotFound;
       
  2945 	if (iPicturePhrase->iPicHdr.iPicture.IsPtr() && iPicturePhrase->iPicHdr.iPicture.AsPtr())
       
  2946 		iPicturePhrase->iPicHdr.iPicture->GetSizeInTwips(aSize);
       
  2947 	else
       
  2948 		aSize=iPicturePhrase->iPicHdr.iSize;
       
  2949 	return KErrNone;
       
  2950 	}
       
  2951 
       
  2952 
       
  2953 TPictureHeader RPhraseAttribsEntry::PictureHeader()const
       
  2954 // Return the picture header describing the picture at character position aPos.
       
  2955 // If there is no picture at character position aPos, a default picture header is returned.
       
  2956 //
       
  2957 	{
       
  2958 	return (IsPicturePhrase())
       
  2959 		? iPicturePhrase->iPicHdr
       
  2960 		: TPictureHeader();
       
  2961 	}
       
  2962 
       
  2963 
       
  2964 const CPicture* RPhraseAttribsEntry::PictureHandleL(const MPictureFactory* aFactory,
       
  2965 													const MRichTextStoreResolver* aResolver,
       
  2966 													TInt aPos,
       
  2967 													MLayDoc::TForcePictureLoad aForceLoad)const
       
  2968 // For a text phrase returns NULL.
       
  2969 // For a picture phrase returns a pointer to the picture object itself.
       
  2970 // May leave since loading of pictures is deferred until this point,
       
  2971 // ie, when the picture is required.
       
  2972 // Also returns NULL if the picture is not in memory *and* TForcePictureLoad is false.
       
  2973 //
       
  2974 	{
       
  2975 	if (iLength!=EPictureIndicator)
       
  2976 		return NULL;
       
  2977 	else
       
  2978 		{// Check if the picture is in memory
       
  2979 		CPicture* handle=NULL;
       
  2980 		if (iPicturePhrase->iPicHdr.iPicture.IsPtr())
       
  2981 			handle=iPicturePhrase->iPicHdr.iPicture;
       
  2982 		else
       
  2983 			{// Check if the picture should be loaded from persistent storage
       
  2984 			if (aForceLoad==MLayDoc::EForceLoadTrue)  // picture not in memory, so load it.
       
  2985 				{
       
  2986 				if (aResolver==NULL || aFactory==NULL)
       
  2987 					return NULL;
       
  2988 				const CStreamStore& store=aResolver->StreamStoreL(aPos);
       
  2989 				aFactory->NewPictureL(iPicturePhrase->iPicHdr,store);
       
  2990 				handle=iPicturePhrase->iPicHdr.iPicture;
       
  2991 				}
       
  2992 			}
       
  2993 		return handle;
       
  2994 		}
       
  2995 	}
       
  2996 
       
  2997 
       
  2998 TPictureHeader* RPhraseAttribsEntry::PictureHeaderPtr()const
       
  2999 // For a [text] phrase returns NULL.
       
  3000 // For a picture phrase returns a pointer to the picture header.
       
  3001 //
       
  3002 	{return (iLength==EPictureIndicator)? &iPicturePhrase->iPicHdr:NULL;}
       
  3003 
       
  3004 
       
  3005 CCharFormatLayer* RPhraseAttribsEntry::ReleaseCharFormatLayerOwnership()
       
  3006 // Return a handle to the character format layer, then set this handle to it to NULL.
       
  3007 // thus giving up ownership of the object.
       
  3008 //
       
  3009 	{
       
  3010 	CCharFormatLayer*& layer=(iLength==EPictureIndicator)?iPicturePhrase->iCharFormat:iCharFormat;
       
  3011 	CCharFormatLayer* charFormatLayer=layer;
       
  3012 	layer=NULL;
       
  3013 	return charFormatLayer;
       
  3014 	}
       
  3015 
       
  3016 
       
  3017 TBool RPhraseAttribsEntry::IsIdentical(const RPhraseAttribsEntry& aPhrase)const
       
  3018 // Returns ETrue if this phrase is identical to the specified phrase.
       
  3019 // otherwise returns EFalse.
       
  3020 // The 2 are equal only if they are both non-zero length text phrases, and of identical character format.
       
  3021 //
       
  3022 	{
       
  3023 	if (iLength<=0)				// picture or zero-length
       
  3024 		return EFalse;
       
  3025 	if (aPhrase.iLength<=0)		// picture or zero-length
       
  3026 		return EFalse;
       
  3027 	return iCharFormat->IsIdentical(aPhrase.iCharFormat,EFalse); // EFalse=do not compare based-on link
       
  3028 	}
       
  3029 
       
  3030 
       
  3031 CPicturePhrase* CPicturePhrase::NewL(const TPictureHeader& aPicHdr,TCharFormatX& aFormat,
       
  3032 									 TCharFormatXMask& aMask,CCharFormatLayer* aCharBase,
       
  3033 									 TBool& aPictureOwnershipTaken)
       
  3034 	{
       
  3035 	CPicturePhrase* self=new(ELeave) CPicturePhrase(aPicHdr,aPictureOwnershipTaken);
       
  3036 	CleanupStack::PushL(self);
       
  3037 	self->ConstructL(aFormat,aMask,aCharBase);
       
  3038 	CleanupStack::Pop();
       
  3039 	return self;
       
  3040 	}
       
  3041 
       
  3042 
       
  3043 // The charLayer is assumed to have a valid based-on link already set.
       
  3044 // Called as part of the rich text index Internalize only.
       
  3045 CPicturePhrase* CPicturePhrase::NewL(const TPictureHeader& aPicHdr,
       
  3046                                      CCharFormatLayer* aCharLayer,
       
  3047                                      TBool& aPictureOwnershipTaken)
       
  3048 	{
       
  3049 	return new(ELeave) CPicturePhrase(aPicHdr,aCharLayer,aPictureOwnershipTaken);
       
  3050 	}
       
  3051 
       
  3052 
       
  3053 CPicturePhrase::CPicturePhrase(const TPictureHeader& aPicHdr,TBool& aPictureOwnershipTaken):
       
  3054 	iPicHdr(aPicHdr)
       
  3055 	{
       
  3056    	aPictureOwnershipTaken=ETrue;
       
  3057 	}
       
  3058 
       
  3059 
       
  3060 CPicturePhrase::CPicturePhrase(const TPictureHeader& aPicHdr,
       
  3061                                CCharFormatLayer* aCharLayer,
       
  3062                                TBool& aPictureOwnershipTaken):
       
  3063 	iCharFormat(aCharLayer),
       
  3064 	iPicHdr(aPicHdr)
       
  3065 	{
       
  3066 	aPictureOwnershipTaken=ETrue;
       
  3067 	}
       
  3068 
       
  3069 
       
  3070 void CPicturePhrase::ConstructL(TCharFormatX& aFormat,TCharFormatXMask& aMask,CCharFormatLayer* aCharBase)
       
  3071 	{
       
  3072 	iCharFormat=CCharFormatLayer::NewL(aFormat,aMask);
       
  3073 	iCharFormat->SetBase(aCharBase);
       
  3074 	}
       
  3075 
       
  3076 
       
  3077 CPicturePhrase::~CPicturePhrase()
       
  3078 	{
       
  3079 	iPicHdr.DeletePicture();
       
  3080 	delete iCharFormat;
       
  3081 	}
       
  3082 
       
  3083 TLogicalPosition::TLogicalPosition():
       
  3084 	iDocPos(0),
       
  3085 	iParaElement(0),
       
  3086 	iParaElementOffset(0),
       
  3087 	iPhraseElement(0),
       
  3088 	iPhraseElementOffset(0)
       
  3089 	{
       
  3090 	}
       
  3091 
       
  3092 
       
  3093 void TLogicalPosition::Clear()
       
  3094 // Reset this logical position record.
       
  3095 //
       
  3096 	{
       
  3097 	*this=TLogicalPosition();
       
  3098 	iParaBasePhraseElement=0;
       
  3099 	}