/*
* Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/


#include <e32std.h>
#include <e32base.h>

#include "TXTRICH.H"
#include "TXTINDEX.H"
#include "TXTSTD.H"
#include "TXTRTPFL.H"
#include "ParseLst.h"
#include "TXTCLIPBOARD.H"

#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
#include "TXTETEXT_INTERNAL.H"
#include "TXTRICH_INTERNAL.H"
#endif

EXPORT_C void CRichText::__DbgTestInvariant()const
// Provides class invariants.  Explanations below:
//
	{
#ifdef _DEBUG
// ASSERT: The global format layers are never null.
	__ASSERT_DEBUG(iGlobalParaFormatLayer != NULL, User::Invariant());
	__ASSERT_DEBUG(iGlobalCharFormatLayer != NULL, User::Invariant());
	if (IndexPresent())
		{
// ASSERT: The sum of para lengths == the length as described by the document storage.
		TInt cumulativeParaLength = 0;
		TInt maxPara = iIndex->iParaIx->Count();
		for (TInt offset = 0; offset < maxPara; offset++)
			{
			TParaAttribsEntry entry = (*iIndex->iParaIx)[offset];
			cumulativeParaLength += entry.iLength;
			}
		__ASSERT_DEBUG(cumulativeParaLength == (DocumentLength() + 1), User::Invariant());
		}
		// Change here for defect INC005336.
		// This defect is present when the assertion below fails.
		__ASSERT_DEBUG( (iParserData == NULL) || \
			(!iParserData->HaveRange()) || \
			(iParserData->EndParse() <= DocumentLength()), User::Invariant());
#endif
	}

EXPORT_C CRichText* CRichText::NewL(const CParaFormatLayer* aGlobalParaLayer,const CCharFormatLayer* aGlobalCharLayer,
									TDocumentStorage aStorage,TInt aDefaultTextGranularity,TParaType aParaType)
/** Allocates and constructs an empty rich text object, with a global character
and paragraph format layer. A single end-of-document delimiter is inserted.
No style list is allocated.

@param aGlobalParaLayer Pointer to the paragraph format layer referenced by
the rich text object. Must not be NULL, or a panic occurs.
@param aGlobalCharLayer Pointer to the character format layer referenced by
the rich text object. Must not be NULL, or a panic occurs.
@param aStorage The type of in-memory buffer to use. Defaults to ESegmentedStorage
which should rarely need to be changed.
@param aDefaultTextGranularity Specifies the granularity of the in-memory buffer.
Default is EDefaultTextGranularity bytes (=256), and this should rarely need
to be changed.
@param aParaType This argument indicates whether you are using a single paragraph
or multiple paragraphs, and thus affects the granularity of aggregate objects
used internally for storing paragraph attributes. Default = EMultiPara.
@return The rich text object. */
	{
	// Create new rich text containing just a single end-of-document character.
	__ASSERT_ALWAYS(aGlobalParaLayer != NULL, Panic(ENullFormatLayerHandle));
	__ASSERT_ALWAYS(aGlobalCharLayer != NULL, Panic(ENullFormatLayerHandle));

	CRichText* self = new(ELeave) CRichText(aGlobalParaLayer, aGlobalCharLayer);
	CleanupStack::PushL(self);
	self->ConstructL(aStorage, aDefaultTextGranularity, aParaType);
	CleanupStack::Pop();
	return self;
	}


// Create new rich text that supports Paragraph Styles, containing just a single end-of-document character.
EXPORT_C CRichText* CRichText::NewL(const CParaFormatLayer* aGlobalParaLayer,const CCharFormatLayer* aGlobalCharLayer,
									const CStyleList& aStyleList,
									TDocumentStorage aStorage,TInt aDefaultTextGranularity,TParaType aParaType)
/** Allocates and constructs an empty rich text object which supports styles. It
is constructed with a global character and paragraph format layer and a style
list. A single end-of-document delimiter is inserted. The rich text object
takes ownership of the style list.

Note:

A rich text object not constructed with a style list may still use styles,
by calling SetStyleListExternallyOwned() at any time after construction. In
this case, the rich text object does not own the style list.

@param aGlobalParaLayer Pointer to the paragraph format layer referenced by
the rich text object. Must not be NULL, or a panic occurs.
@param aGlobalCharLayer Pointer to the character format layer referenced by
the rich text object. Must not be NULL, or a panic occurs.
@param aStyleList Style list. Holds the set of paragraph styles which can be
used in the rich text object.
@param aStorage The type of in-memory buffer to use. Defaults to ESegmentedStorage
which should rarely need to be changed.
@param aDefaultTextGranularity Specifies the granularity of the in-memory buffer.
Default is EDefaultTextGranularity bytes (=256), and this should rarely need
to be changed.
@param aParaType This argument indicates whether you are using a single paragraph
or multiple paragraphs, and thus affects the granularity of aggregate objects
used internally for storing paragraph attributes. Default = EMultiPara.
@return The new rich text object. */
	{
	__ASSERT_ALWAYS(aGlobalParaLayer != NULL, Panic(ENullFormatLayerHandle));
	__ASSERT_ALWAYS(aGlobalCharLayer != NULL, Panic(ENullFormatLayerHandle));

	CRichText* self = new(ELeave) CRichText(aGlobalParaLayer, aGlobalCharLayer, CONST_CAST(CStyleList*, &aStyleList));
	CleanupStack::PushL(self);
	self->ConstructL(aStorage, aDefaultTextGranularity, aParaType);
	CleanupStack::Pop();
	return self;
	}


// Restore into a new rich text object, using the specified global layers.
EXPORT_C CRichText* CRichText::NewL(const CStreamStore& aStore,TStreamId aStreamId,
									const CParaFormatLayer* aGlobalParaLayer,const CCharFormatLayer* aGlobalCharLayer,
									MTextFieldFactory* aFieldFactory,
									TDocumentStorage aStorage)
/** Allocates and constructs a rich text object with a field factory. Its text
content is internalized from a stream store.

Note:

A rich text object not constructed with a field factory may still support
the addition of fields, by calling SetFieldFactory(), defined in the base
class CPlainText.

@param aStore Stream store from which the object is restored.
@param aStreamId ID of the stream store.
@param aGlobalParaLayer Pointer to the paragraph format layer referenced by
the rich text object. Must not be NULL, or a panic occurs.
@param aGlobalCharLayer Pointer to the character format layer referenced by
the rich text object. Must not be NULL, or a panic occurs.
@param aFieldFactory Pointer to a field factory. A field factory must be provided
if the text object supports fields.
@param aStorage The type of in-memory buffer to use. Defaults to ESegmentedStorage
which should rarely need to be changed.
@return The new rich text object. */
	{
	__ASSERT_ALWAYS(aGlobalParaLayer != NULL, Panic(ENullFormatLayerHandle));
	__ASSERT_ALWAYS(aGlobalCharLayer != NULL, Panic(ENullFormatLayerHandle));

	CRichText* self = new(ELeave) CRichText(aGlobalParaLayer, aGlobalCharLayer);
	CleanupStack::PushL(self);
	self->ConstructL(aStore, aStreamId, NULL, NULL, aFieldFactory, aStorage);
	CleanupStack::Pop();
	return self;
	}


EXPORT_C CRichText* CRichText::NewL(const CStreamStore& aStore, TStreamId aStreamId,
									const CParaFormatLayer* aGlobalParaLayer, const CCharFormatLayer* aGlobalCharLayer,
									MPictureFactory* aPictureFactory, MRichTextStoreResolver* aStoreResolver,
									MTextFieldFactory* aFieldFactory,
									TDocumentStorage aStorage)
/** Allocates and constructs a rich text object with a field factory and a picture
factory. Its text content is internalized from a stream store.

Note:

A rich text object not constructed with a field factory may still support
the addition of fields, by calling SetFieldFactory(), defined in the base
class CPlainText.

@param aStore Stream store from which the object is restored.
@param aStreamId ID of the stream store.
@param aGlobalParaLayer Pointer to the paragraph format layer referenced by
the rich text object. Must not be NULL, or a panic occurs.
@param aGlobalCharLayer Pointer to the character format layer referenced by
the rich text object. Must not be NULL, or a panic occurs.
@param aPictureFactory The picture factory. This is needed to load pictures
into memory, (see PictureHandleL()). If a store resolver is specified (not
NULL), then a factory must also be specified, or a panic occurs.
@param aStoreResolver A store resolver. This determines which file store the
picture is stored in. It is used to get from a reference to an embedded picture
within a CRichText object to the actual picture itself. Picture loading is
done by the picture factory.
@param aFieldFactory Pointer to a field factory. A field factory must be provided
if the text object supports fields.
@param aStorage The type of in-memory buffer to use. Defaults to ESegmentedStorage
which should rarely need to be changed.
@return The new rich text object. */
	{
	// Restore a new rich text from the specified stream, that uses the specified global layers, and the
	// specified picture header factory and store, if this rich text supports pictures.
	__ASSERT_ALWAYS(!(!aPictureFactory && aStoreResolver), Panic(EInvalidPictureFactorySettings));
	__ASSERT_ALWAYS(aGlobalParaLayer != NULL, Panic(ENullFormatLayerHandle));
	__ASSERT_ALWAYS(aGlobalCharLayer != NULL, Panic(ENullFormatLayerHandle));

	CRichText* self = new(ELeave) CRichText(aGlobalParaLayer, aGlobalCharLayer);
	CleanupStack::PushL(self);
	self->ConstructL(aStore, aStreamId, aPictureFactory, aStoreResolver, aFieldFactory, aStorage);
	CleanupStack::Pop();
	return self;
	}


EXPORT_C CRichText::CRichText(const CParaFormatLayer* aGlobalParaLayer, const CCharFormatLayer* aGlobalCharLayer,
							  CStyleList* aStyleList):
	CGlobalText(aGlobalParaLayer, aGlobalCharLayer),
	iStyleList(aStyleList)
	{
	}


EXPORT_C void CRichText::ConstructL(TDocumentStorage aStorage, TInt aDefaultTextGranularity, TParaType aParaType)
// Initialises and updates the index following the CPlainText::ConstructL
// insertion of the end-of-document character.
//
	{
	CPlainText::ConstructL(aStorage, aDefaultTextGranularity);
	SetParaTypeIsSingle(aParaType == ESinglePara);
	iParserData = new(ELeave) CParserData(DocumentLength());
	TInt a;
	TInt b;
	ParseText(a, b, ETrue);

	__TEST_INVARIANT;
	}


EXPORT_C void CRichText::ConstructL(const CStreamStore& aStore,TStreamId aStreamId,
									MPictureFactory* aPictureFactory, MRichTextStoreResolver* aStoreResolver,
									MTextFieldFactory* aFieldFactory,
									TDocumentStorage aStorage)
// Initialises and updates the index following the CPlainText::ConstructL
// insertion of the end-of-document character.
// Sets the picture header factory if provided.
//
	{
	CPlainText::ConstructL(aStore, aStreamId, aFieldFactory, aStorage);
	SetPictureFactory(aPictureFactory, aStoreResolver);
	if (iParserData == NULL)
		iParserData = new(ELeave) CParserData(DocumentLength());
	TInt a;
	TInt b;
	ParseText(a, b, ETrue);

	__TEST_INVARIANT;
	}


EXPORT_C CRichText::~CRichText()
/** The destructor frees all resources owned by the rich text object, prior to
its destruction. */
	{
	// We cannot call DestroyParserSystem() here because it applies to all instances in the thread
	delete iParserData;
	KillStyleList();
	KillIndex();
	}


void CRichText::KillStyleList()
// Free up the style table
//
	{
	if (StyleListPresent() && !StyleListExternallyOwned())
		{
		delete iStyleList.AsPtr();
		iStyleList = NULL;
		}
	}


void CRichText::KillIndex()
// Delete the rich text index if it's resident in memory.
//
	{
	if (IndexPresent())
		delete iIndex.AsPtr();
	iIndex=NULL;
	}


TBool CRichText::CreateEmptyMarkupComponentL()
// If necessary, creates an empty markup component.
// Returns ETrue if the markup component was created as a result of this function,
// otherwise returns EFalse.
//
	{
	if (IndexPresent())
		return EFalse;
	TInt paraGran = (ParaTypeIsSingle()) ? KSingleParaGranularity : KMultiParaGranularity;
	TInt phrGran = (ParaTypeIsSingle()) ? KSmallPhraseGranularity : KLargePhraseGranularity;
	iIndex = CRichTextIndex::NewL(iGlobalParaFormatLayer, iGlobalCharFormatLayer, *this,paraGran,phrGran);
	return ETrue;
	}


void CRichText::CreateAndGenerateMarkupComponentL()
// Checks if the rich text index needs to be created and does so if necessary.
// Called by all public rich text functions that manipulate specific formatting.
//
	{
	if (CreateEmptyMarkupComponentL())
		{
		TRAPD(ret, GenerateMarkupL());
		if (ret != KErrNone)
			{// destroy this partially set markup component
			delete iIndex.AsPtr();
			iIndex = NULL;
			User::Leave(ret);
			}
		}
	}


void CRichText::GenerateMarkupL()
// Generate markup data corresponding to the current text content.
//
	{
	TInt remainingLength = DocumentLength();
	TInt startPos = 0;
	while (remainingLength)
		{
		TPtrC buf = Read(startPos);
		TInt consumed = buf.Length();
		if (consumed > remainingLength)
			{
			consumed = remainingLength;
			buf.Set(buf.Ptr(), consumed);
			}
		iIndex->InsertL(startPos, buf, *iGlobalParaFormatLayer);
		remainingLength -= consumed;
		startPos += consumed;
		}
	}

EXPORT_C void CRichText::CopyToStoreL(CStreamStore& aStore,CStreamDictionary& aDictionary,TInt aPos,TInt aLength) const
/** Copies a portion of the rich text object, with components to the clipboard.

A panic occurs in the following circumstances:

aPos is an invalid document position

aLength is invalid (zero or less)

the sum of aPos and aLength is greater than or equal to the number of characters
in the document

@param aStore Stream store to which the rich text is written.
@param aDictionary The stream dictionary.
@param aPos The document position from which to begin copying.
@param aLength The number of characters to copy. */
	{
	if (aLength > 0)
		{
		TStreamId plainTextId = CPlainText::DoCopyToStoreL(aStore,aDictionary,aPos,aLength);
		TStreamId id = DoCopyToStoreL(aStore,aPos,aLength,plainTextId,FALSE);
		aDictionary.AssignL(KClipboardUidTypeRichText,id);
		TStreamId idStyles = DoCopyToStoreL(aStore,aPos,aLength,plainTextId,TRUE);
		aDictionary.AssignL(KClipboardUidTypeRichTextWithStyles,idStyles);
		}
	}


// Copy the selected region of rich text, with components, to the specified store.
TStreamId CRichText::DoCopyToStoreL(CStreamStore& aStore,TInt aPos,TInt aLength,TStreamId aPlainTextId,TBool aCopyStyles) const
	{
	__TEST_INVARIANT;

	TInt documentLength = DocumentLength();
	__ASSERT_ALWAYS(aPos >= 0 && aPos <= documentLength,Panic(ECharPosBeyondDocument));
	__ASSERT_ALWAYS(aLength >= 0,Panic(ECopyToClipboardNegativeLength));
	__ASSERT_ALWAYS(aPos + aLength <= documentLength,Panic(ECharPosBeyondDocument));

	if (aLength == 0)
		return KNullStreamId;

	CStoreMap* map = CStoreMap::NewLC(aStore);
	CopyComponentsL(aStore,*map,aPos,aLength,aPlainTextId);
	RStoreWriteStream stream(*map);
	TStreamId id = stream.CreateLC(aStore);
	CopyToStreamL(stream,aPos,aLength,aPlainTextId,aCopyStyles);
	stream.CommitL();
	map->Reset();
	CleanupStack::PopAndDestroy(2);

	__TEST_INVARIANT;
	return id;
	}


EXPORT_C void CRichText::CopyComponentsL(CStreamStore& aStore,CStoreMap& aMap,TInt aPos,TInt aLength,TStreamId aPlainTextId) const
	{
	if (aPlainTextId == KNullStreamId)
		CPlainText::CopyComponentsL(aStore,aMap,aPos,aLength);
	if (IndexPresent())
		iIndex->StorePicturesL(aStore,aMap,aPos,aLength);
	}


// Copy the selected region of rich text and components to the specified stream.
EXPORT_C void CRichText::CopyToStreamL(RWriteStream& aStream,TInt aPos,TInt aLength) const
	{
	__TEST_INVARIANT;
	CopyToStreamL(aStream, aPos, aLength, KNullStreamId);
	}


/*
Copy the selected region of rich text and components to the specified stream.
If aPlainTextId is NULL the plain text component is stored in-line,
otherwise the reference to the plain text stream component is stored.
*/
EXPORT_C void CRichText::CopyToStreamL(RWriteStream& aStream,TInt aPos,TInt aLength,TStreamId aPlainTextId) const
	{
	CopyToStreamL(aStream,aPos,aLength,aPlainTextId,TRUE);
	}


void CRichText::CopyToStreamL(RWriteStream& aStream,TInt aPos,TInt aLength,TStreamId aPlainTextId,TBool aCopyStyles) const
	{
	if (aPlainTextId == KNullStreamId)
		{
		aStream.WriteUint8L(TRUE);
		CPlainText::CopyToStreamL(aStream,aPos,aLength);
		}
	else
		{
		aStream.WriteUint8L(FALSE);
		aStream << aPlainTextId;
		}

	if (!IndexPresent())
		CONST_CAST(CRichText*,this)->CreateAndGenerateMarkupComponentL();
	aStream.WriteUint8L(TRUE);
	iIndex->CopyToStreamL(aStream,aPos,aLength,aCopyStyles);
	}

EXPORT_C TInt CRichText::PasteFromStoreL(const CStreamStore& aStore, const CStreamDictionary& aDictionary, TInt aPos, CParagraphStyle::TStylePasteMode aStylePasteMode)
/** Pastes the contents of the clipboard into the rich text object at the
specified document position. Returns the number of characters pasted.

If the text in the clipboard has been formatted using styles, the
aStylePasteMode argument indicates whether the styles should be preserved
or discarded. If the argument is not specified, the pasted rich text
retains all formatting specified in the styles, and any new style
definitions are added to the style list of the rich text object into which
it is pasted.

@param aStore The stream store from which to paste the rich text.
@param aDictionary The stream dictionary.
@param aPos The document position at which to paste the rich text. Must be
valid, or a panic occurs.
@param aStylePasteMode Indicates whether styles in the pasted text should be
preserved or discarded.
@return The number of characters pasted. */
	{
	return DoPasteRtFromStoreL(aStore, aDictionary, aPos, aStylePasteMode);
	}


EXPORT_C TInt CRichText::PasteFromStoreL(const CStreamStore& aStore, const CStreamDictionary& aDictionary, TInt aPos)
// Paste the lesser of i) aMaxPasteLength ii) the entire clipboard contents.
// Return a count of the number of characters pasted.
//
	{
	return DoPasteRtFromStoreL(aStore, aDictionary, aPos, CParagraphStyle::EAddNewStyles);
	}


TInt CRichText::DoPasteRtFromStoreL(const CStreamStore& aStore, const CStreamDictionary& aDictionary, TInt aPos, CParagraphStyle::TStylePasteMode aStylePasteMode)
// Paste the rich text data from the specified clipboard, inserting it into this
// instance at character position aPos.  Returns the number of characters pasted.
// May be 0.
//
	{
	__TEST_INVARIANT;
	__ASSERT_ALWAYS(aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));

	TUid type = KClipboardUidTypeRichTextWithStyles;
	TStreamId id = aDictionary.At(type);
	if (id == KNullStreamId)
		{
		type = KClipboardUidTypeRichText;
		id = aDictionary.At(type);
		}
	if (id == KNullStreamId)
		{
		type = KClipboardUidTypePlainText;
		id = aDictionary.At(type);
		}
	TInt consumed = 0;
	if (id == KNullStreamId)
		return consumed;
	if (type == KClipboardUidTypeRichText || type == KClipboardUidTypeRichTextWithStyles)
		consumed = PasteRichTextFromStoreL(aStore, aDictionary, id, aPos, aStylePasteMode);
	else if (type == KClipboardUidTypePlainText)
		consumed = PastePlainTextFromStoreL(aStore, id, aPos);

	if (consumed)
		{
		iParserData->MergeRange(aPos,0,consumed);
		CallEditObserver(aPos, consumed);
		}

	SetHasChanged(ETrue);
	__TEST_INVARIANT;
	return consumed;
	}


TInt CRichText::PasteRichTextFromStoreL(const CStreamStore& aStore, const CStreamDictionary& aDictionary, TStreamId& aRichTextStreamId, TInt aPos,CParagraphStyle::TStylePasteMode aStylePasteMode)
// Paste the plain text stream, then open and paste the rich text markup from the specified stream.
//
	{
	if (!IndexPresent())
		CreateAndGenerateMarkupComponentL();  // create the index if it does not already exist
	TStreamId id = aDictionary.At(KClipboardUidTypePlainText);
// ASSERT: We have rich text, so the plain text stream must exist.
	__ASSERT_ALWAYS(id != KNullStreamId,Panic(EClipboardIntegrity));
	TInt consumed = CPlainText::DoPasteFromStoreL(aStore, id, aPos);
	TRAPD(ret, CompletePasteRichTextFromStoreL(aStore, aRichTextStreamId, aPos, aStylePasteMode));
	if (ret != KErrNone)
		{
		CPlainText::Delete(aPos,consumed);
		User::Leave(ret);
		}
	return consumed;
	}


void CRichText::CompletePasteRichTextFromStoreL(const CStreamStore& aStore, TStreamId& aRichTextStreamId, TInt aPos, CParagraphStyle::TStylePasteMode aStylePasteMode)
//
	{
	RStoreReadStream stream;
	stream.OpenLC(aStore, aRichTextStreamId);
	TBool plainTextInline = (TBool)stream.ReadUint8L();
	if (!plainTextInline)
		{
		TStreamId dummy;
		stream >> dummy;
		}
	TBool markupPresentInClipboard = (TBool)stream.ReadUint8L();
	if (markupPresentInClipboard)
		iIndex->PasteFromStreamL(aStore, stream, aPos,aStylePasteMode, iGlobalParaFormatLayer, iGlobalCharFormatLayer);
	CleanupStack::PopAndDestroy();  // stream
	}


TInt CRichText::PastePlainTextFromStoreL(const CStreamStore& aStore, TStreamId& anId, TInt aPos)
// Paste the plain text stream, then update the rich text index to show any paragraph delimiters pasted.
//
	{
	TInt consumed = CPlainText::DoPasteFromStoreL(aStore, anId, aPos);
	//
	if (IndexPresent())
		{
		TRAPD(ret, CompletePastePlainTextL(aPos, consumed));
		if (ret != KErrNone)
			{
			CPlainText::Delete(aPos,consumed);
			User::Leave(ret);
			}
		}
	return consumed;
	}

void CRichText::CompletePastePlainTextL(TInt aPos, TInt aCharacterCount)
// Updates the rich text index following the pasting of global text.
//
	{
	HBufC* buf = HBufC::NewLC(aCharacterCount);
	TPtr bf = buf->Des();
	Extract(bf, aPos, aCharacterCount);
	iIndex->InsertL(aPos, bf, *iGlobalParaFormatLayer);
	CleanupStack::PopAndDestroy();  // buf
	}

EXPORT_C void CRichText::Reset()
/** Resets the document's contents to a single end-of-document delimiter. Also
deletes the style list if owned by the object. */
	{
	// Resets document contents to single end-of-document charcter,
	// and resets the index to match document content.
	TInt length = DocumentLength();
	CPlainText::Reset();  // remove all content bar eod character
	KillStyleList();  // does not get destroyed if externally owned
	KillIndex();
	SetHasChanged(ETrue);
	iParserData->KillRange();
	iParserData->iLastKnownCursor = -1;
	CallEditObserver(0, -length);

	__TEST_INVARIANT;
	}

EXPORT_C void CRichText::InsertL(TInt aPos, const TChar& aChar)
/** Inserts either a single character or a descriptor into the text object
at a specified document position.

The insertion position must be valid or a panic occurs.Note:A panic
occurs if the text object is in an "insert pending" state (i.e.
SetInsertCharFormatL() has been called and has not been
cancelled using CancelInsertCharFormat()) and aPos is not
the same as the insertion point.

@param aPos The document position at which to insert the character/descriptor.
@param aChar The character to insert. Must not be a picture character or a
panic occurs.
@param aBuf The descriptor to insert. */
	{
	__TEST_INVARIANT;
	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
	__ASSERT_ALWAYS(aChar!= EPictureCharacter, Panic(ENonOverloadedInsertCalledWithPictureCharacter));

	TBuf<1> content;
	content.Append(aChar);
	RtInsertL(aPos, content);

	__TEST_INVARIANT;
	}


EXPORT_C void CRichText::InsertL(TInt aPos, const TDesC& aBuf)
// Inserts the contents of aBuf into the text component at character position aPos.
//
	{
	__ETEXT_WATCH(INSERT_DESC);

	RtInsertL(aPos, aBuf);

	__ETEXT_WATCH_END(INSERT_DESC);
	}


EXPORT_C void CRichText::RtInsertL(TInt aPos, const TDesC& aBuf)
// Inserts the contents a aBuf into the text component at position aPos.
// Updates the index accordingly, and accounts for all embedded paragraph delimiters
// in the passed buffer aBuf.
//
	{
	__TEST_INVARIANT;
	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));

	int length = aBuf.Length();
	CPlainText::InsertL(aPos, aBuf);
	if (IndexPresent())
		{
		TRAPD(ret, iIndex->InsertL(aPos, aBuf, *iGlobalParaFormatLayer));
		if (ret != KErrNone)
			{
			CPlainText::Delete(aPos,length);
			User::Leave(ret);
			}
		SetHasChanged(ETrue);
		}

	iParserData->MergeRange(aPos,0,length);
	CallEditObserver(aPos,length);

	__TEST_INVARIANT;
	}


EXPORT_C void CRichText::InsertL(TInt aPos,const TPictureHeader& aHeader)
/**Inserts a picture header into the text object at a specified document
position. The picture header specified must reference a valid picture, or
a panic occurs.

A panic also occurs if the text object is in an "insert pending" state
(SetInsertCharFormatL() has been called and has not been cancelled using
CancelInsertCharFormat()) and aPos is not the same as the insertion point.

This method takes ownership of a picture referenced in aHeader.

@param aPos The document position at which to insert the picture header. Must
be valid, or a panic occurs.
@param aHeader A picture header. This holds a pointer to the picture to insert,
and information about the picture. */
	{
	__TEST_INVARIANT;
	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));

	CleanupStack::PushL(aHeader.iPicture);

	if (!IndexPresent())
		CreateAndGenerateMarkupComponentL();
	TBuf<1> picture;
	picture.Append(EPictureCharacter);
	CPlainText::InsertL(aPos, picture);

    CleanupStack::Pop(aHeader.iPicture);

	TBool pictureOwnershipTaken(EFalse);
	TRAPD(ret, iIndex->InsertL(aPos, aHeader,pictureOwnershipTaken));
	if (ret != KErrNone)
		{
		if(!pictureOwnershipTaken)
		    {
		    const_cast <TPictureHeader&> (aHeader).DeletePicture();
		    }
		CPlainText::Delete(aPos,picture.Length());  // remove the picture place-holder
    	User::Leave(ret);
		}

	SetHasChanged(ETrue);
	iParserData->MergeRange(aPos,0,1);
	CallEditObserver(aPos,1);
	__TEST_INVARIANT;
	}


EXPORT_C TBool CRichText::DeleteL(TInt aPos, TInt aLength)
/** Deletes one or more characters beginning at, and including, the character at
a specified document position. Can leave because paragraphs may be merged
and reformatted by the function.

@param aPos The start of the range of characters to delete. Must be valid
or a panic occurs.
@param aLength The number of characters to delete. Must be positive or a panic
occurs. The sum of aPos and aLength must be less than the document length,
or a panic occurs.
@return Indicates whether two paragraphs have been merged as a result of the
delete, indicating that the formatting of part of the resulting paragraph
may have changed. */
	{
	// Deletes aRange number of characters from the text component
	// and the corresponding index data.
	// Delete commences at, and includes, character position aPos.
	__TEST_INVARIANT;

	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
	__ASSERT_ALWAYS(aLength >= 0, Panic(EDebugDeleteZeroLength));
	__ASSERT_ALWAYS(aPos + (aLength - 1) <= DocumentLength(), Panic(ECharPosBeyondDocument));

	TBool requireMerge = EFalse;
	if (!IndexPresent())
		CPlainText::Delete(aPos,aLength);
	else
		{
		iIndex->CancelInsertCharFormat();
		TIndexDeleteInfo info;
		iIndex->SetForDeleteL(info, aPos, aLength);
		CPlainText::Delete(aPos,aLength);
		requireMerge = iIndex->DeleteNow(info);
		}
	iParserData->MergeRange(aPos,aLength,0);
	CallEditObserver(aPos,-aLength);

	__TEST_INVARIANT;
	return requireMerge;
	}

EXPORT_C void CRichText::DeleteParagraph(TInt aPos, TInt aLength)
/** Deletes one or more whole paragraphs of text. No paragraphs can be merged together
by this function, so it cannot leave it must only be used to delete entire
paragraphs.

@param aPos The document position of the start of the first paragraph to delete.
Must be a valid position or a panic occurs.
@param aLength The number of characters to delete. */
	{
	// Use to remove entire paragraphs of text.
	// Leave-safe if called in this context.
	// MUST NOT CALL if not in this context.
	__TEST_INVARIANT;

	// Store the length of the text before we commence with deletions.
	TInt initialDocLen=DocumentLength();

	__ASSERT_ALWAYS(aPos >= 0 && aPos <= initialDocLen, Panic(ECharPosBeyondDocument));
	__ASSERT_ALWAYS(aPos + aLength <= initialDocLen + 1, Panic(ECharPosBeyondDocument));

	if (aLength <= 0)
		return;

	if (IndexPresent())
		iIndex->DeleteParagraph(aPos, aLength);
	CPlainText::Delete(aPos,aLength);

	// Change here for defect INC005336.
	// Previously the MergeRange call (see else line) was incorrectly handling
	// the scenario where all but the first paragraph was deleted e.g. pasting
	// multi line buffer into a single line text control in a new email on Techview
	// emulator.
	// This was because the deletion in the two lines above would delete the
	// 'end-of-text' maker causing the iEndParse member of CParserData to
	// index the char right after the end of test marker. This would panic
	// plugin parsers thatsubsquently executed.

	if (aPos+aLength > initialDocLen)
		// When deletion includes the end-of-text marker, adjust start
		// supplied so that it appears to MergeRange that we are
		// deleting the paragraph and CR just before it which belongs
		// to the previous paragraph and not the end-of-text marker.
		// This is actually the end result of the deletion anyway!
		iParserData->MergeRange(initialDocLen-aLength,aLength,0);
	else
		// All other deletions which don't.
		iParserData->MergeRange(aPos,aLength,0);

	CallEditObserver(aPos,-aLength);

	__TEST_INVARIANT;
	}

EXPORT_C void CRichText::DeleteFromParagraph(TInt aPos, TInt aLength)
/** Removes a range of characters from within a single paragraph only. Should not
be used for deleting an entire paragraph or paragraphs because this may cause
it to leave. Otherwise, it is guaranteed not to leave.

@param aPos The document position of the start of the range of characters
to delete. Must be a valid document position, or a panic occurs.
@param aLength The number of characters to delete. The sum of aPos and aLength
must be less than the document length, or a panic occurs. */
	{
	// Removes aLength characters from *within* a single paragraph only.
	// Guaranteed not to leave if this pre-condition holds true.
	__TEST_INVARIANT;

	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
	__ASSERT_ALWAYS(aPos + (aLength - 1) <= DocumentLength(), Panic(ECharPosBeyondDocument));

	if (aLength <= 0)
		return;

	if (IndexPresent())
		iIndex->DeleteFromParagraph(aPos, aLength);
	CPlainText::Delete(aPos,aLength);
	iParserData->MergeRange(aPos,aLength,0);
	CallEditObserver(aPos,-aLength);

	__TEST_INVARIANT;
	}

EXPORT_C TInt CRichText::ParagraphCount()const
/** Gets a count of the number of paragraphs in the text object.

Note:

The paragraph delimiter which terminates every text object means this function
always returns a count of at least one.

@return The number of paragraphs in the document. */
	{
	// Returns a count of the number of paragraphs
	// in the document.
	//
	__TEST_INVARIANT;

	if (IndexPresent())
		return iIndex->ParagraphCount();
	else
		return CPlainText::ParagraphCount();
	}


EXPORT_C TEtextComponentInfo CRichText::ComponentInfo() const
/** Gets information about the number of components contained in the text object
(the field count, the picture count and the style count).

@return Contains the component information. */
	{
	return TEtextComponentInfo(FieldCount(), PictureCount(), StyleCount());
	}

EXPORT_C TInt CRichText::CharPosOfParagraph(TInt& aLength, TInt aParaOffset) const
/** Finds the length and the start position of a paragraph identified by its
paragraph number. The first paragraph is numbered zero.

Notes:

if aParaOffset is invalid, (equal to or greater than the total number of
paragraphs), the function's return value is EScanEndOfData (= -1)

@param aLength On return contains the length of the specified paragraph.
@param aParaOffset The paragraph number. The first paragraph is numbered zero.
@return The document position of the first character in the paragraph. */
	{
	// Returns the character position of the first character of paragraph aParaOffset,
	// where aParaOffset specifies the nth paragraph.
	// The length of this nth paragraph is written to aLength.
	//
	// If aParaOffset specifies a paragraph that does not exist, EScanEndOfData is returned.
	//
	__TEST_INVARIANT;

	if (IndexPresent())
		return iIndex->CharPosOfParagraph(aLength, aParaOffset);
	else
		return CPlainText::CharPosOfParagraph(aLength, aParaOffset);
	}

EXPORT_C TInt CRichText::ParagraphNumberForPos(TInt& aPos) const
/** Gets the number of the paragraph which contains a document position.
Paragraph numbering begins at zero.

@param aPos A document position. Must be valid or a panic occurs. On return,
contains the document position of the first character in the paragraph in
which it is located.
@return The number of the paragraph containing the specified document position. */
	{
	// Returns the paragraph offset for the specified character position aPos.
	// aPos is in turn modified to hold the character position of the first character
	// of this paragraph.  If aPos is already on a paragraph boundary then do nothing.
	//
	__TEST_INVARIANT;
	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));

	if (IndexPresent())
		return iIndex->ParagraphNumberForPos(aPos);
	else
		return CPlainText::ParagraphNumberForPos(aPos);
	}

/** Applies character formatting to a zero length selection, for example
turning bold on. This has the effect that the formatting will be applied to
text subsequently inserted at the position. This "insert pending" state is
cancelled by calling CancelInsertCharFormat().

Note 1: After calling this function, if text is inserted at a different
position to aPos, a panic will occur, unless CancelInsertCharFormat() has been
called before the insertion to cancel the "insert pending" state.

Note 2: If the insert character format is being set for the end of the
paragraph, the paragraph delimiter is set to that format as well. This helps
end-of-paragraph behaviour be more similar to other places.

@param aFormat The character format values to apply.
@param aMask Character format mask specifying the attributes affected.
@param aPos The document position at which to insert the character format.
@pre aPos must be a valid position, or a panic will occur. */
EXPORT_C void CRichText::SetInsertCharFormatL(const TCharFormat& aFormat,
	const TCharFormatMask& aMask, TInt aPos)
	{
	SetExtendedInsertCharFormatL(aFormat, aMask,aPos);
	}

void CancelInsertCharFormat(TAny* aCRichTextIndex)
	{
	reinterpret_cast<CRichTextIndex*>(aCRichTextIndex)->CancelInsertCharFormat();
	}

void CRichText::SetExtendedInsertCharFormatL(const TCharFormatX& aFormat, const TCharFormatXMask& aMask, TInt aPos)
	{
	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
	CreateAndGenerateMarkupComponentL();
	CRichTextIndex* index = iIndex.AsPtr();
	if (index->InsertCharFormatIsActive())
		{
		TCharFormatX format = aFormat;
        TCharFormatXMask mask = aMask;
        CCharFormatLayer* currentLayer = index->GetCurrentInsertCharFormat();
        currentLayer->Sense(format,mask);
		CCharFormatLayer* newLayer = CCharFormatLayer::NewCopyBaseL(currentLayer);
		CleanupStack::PushL(newLayer);
        newLayer->SetL(format,mask);
		if (Read(aPos, 1)[0] == EParagraphDelimiter)
			index->ApplyCharFormatL(aFormat, aMask, aPos, 1, EFalse);
		if (index->InsertCharFormatIsActive())
			{
			currentLayer = index->GetCurrentInsertCharFormat();
			currentLayer->Swap(*newLayer);
			}
		CleanupStack::PopAndDestroy(newLayer);
		}
	else
		{
		TCleanupItem cleanup(::CancelInsertCharFormat, index);
		CleanupStack::PushL(cleanup);
		index->NewInsertCharFormatL(aFormat, aMask, aPos);
		if (Read(aPos, 1)[0] == EParagraphDelimiter)
			index->ApplyCharFormatL(aFormat, aMask, aPos, 1, EFalse);
		CleanupStack::Pop();
		}
	SetHasChanged(TRUE);
	}
	
EXPORT_C void CRichText::ExtendedInterface(TAny*& aInterface, TUid aInterfaceId)
/**
Returns the interface corresponding to the specified UID if it exists, or 0 if not. 
Overridden versions should base call rather than returning 0.
For KUidRichText, CRichText will be returned if rich text is supported.

@param aInterfaceId The UID indicating the interface to return
@param aInterface The interface corresponding to aInterfaceId
if it is supported, or 0 if it is not
*/
	{ 
	if(KUidRichText == aInterfaceId) 
		{
		aInterface = REINTERPRET_CAST(TAny*, this);
		}
	else
		{
	    CGlobalText::ExtendedInterface(aInterface, aInterfaceId);
		}
	}

EXPORT_C TBool CRichText::DelSetInsertCharFormatL(TInt aPos, TInt aLength)
/** Deletes a range of characters. The range affected is from aPos to
aPos+(aLength-1) inclusive. It differs from DeleteL() in that this function
preserves the formatting of the deleted character at position aPos, so that
any text subsequently inserted at aPos will have that formatting applied to it.

A panic occurs if:

after calling this function, text is inserted at a different position to aPos,
without calling CancelInsertCharFormat() before the insertion

aPos is invalid

aLength is negative

the range goes beyond the end of the document

@param aPos The document position of the first character to delete.
@param aLength The number of characters to delete.
@return ETrue if two paragraphs have been merged as a result of the deletion;
EFalse if there has been no paragraph merge. */
	{
	// Delete aLength characters, commencing at, and including, aPos.
	// If aPos is on a phrase boundary, and the whole phrase or more is deleted then
	// remember temporarily the phrase format.  This is applied to any content that is
	// immediately inserted.
	//
	__TEST_INVARIANT;
	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
	__ASSERT_ALWAYS(aLength >= 0, Panic(EDebugDeleteZeroLength));
	__ASSERT_ALWAYS(aPos + (aLength - 1) <= DocumentLength(), Panic(ECharPosBeyondDocument));

	TBool parasMerged = EFalse;
	if (!IndexPresent())
		CPlainText::DeleteL(aPos, aLength);
	else
		{
		parasMerged = iIndex->DelSetInsertCharFormatL(aPos, aLength);
		CPlainText::Delete(aPos,aLength);
		SetHasChanged(ETrue);
		}
	iParserData->MergeRange(aPos,aLength,0);
	CallEditObserver(aPos,-aLength);

	__TEST_INVARIANT;
	return parasMerged;
	}

EXPORT_C void CRichText::CancelInsertCharFormat()
/** Cancels the "insert pending" state set by a call to SetInsertCharFormatL()
or DelSetInsertCharFormatL().

This function removes the restriction on the text insertion position imposed
by these two functions. It is recommended that it is called before every cursor
movement, scroll, paste, etc. This call is a small overhead, and has no effect
if not applicable. */
	{
	// Cancels the transitory state where a specified character format
	// is applied *on top of* any inherited formatting.  eg, when bold is on.
	// Cancel when: (1) the text position is altered.  (2) the first character
	// has been inserted following the setting of this state.
	//
	if (IndexPresent() && iIndex->InsertCharFormatIsActive())
		{
		iIndex->CancelInsertCharFormat();
		SetHasChanged(ETrue);
		}
	}

EXPORT_C void CRichText::ApplyParaFormatL(const CParaFormat* aFormat, const TParaFormatMask& aMask, TInt aPos, TInt aLength)
/** Applies paragraph formatting to a range of paragraphs. The attributes which
are set in the mask are taken from aFormat and applied. The attributes which
are not set in the mask are not changed.

The region affected consists of every paragraph containing one or more
characters in the range aPos to aPos+(aLength-1).

@param aFormat Contains the paragraph format attribute values to apply.
@param aMask Specifies which paragraph format attributes should be affected.
@param aPos The document position of the start of the range.
@param aLength The number of characters in the range. */
	{
	// Applies the specified format attributes to the paragraphs covering
	// character position aPos to aPos+aLength-1.
	//
	__TEST_INVARIANT;
	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
	__ASSERT_ALWAYS(aLength >= 0,Panic(EApplyParaFormatNegativeLength));
	__ASSERT_ALWAYS(aPos + (aLength - 1) <= DocumentLength(), Panic(ECharPosBeyondDocument));

	CreateAndGenerateMarkupComponentL();
	iIndex->ApplyParaFormatL(aFormat, aMask, aPos, aLength);
	SetHasChanged(ETrue);

	__TEST_INVARIANT;
	}

EXPORT_C void CRichText::ApplyCharFormatL(const TCharFormat& aFormat, const TCharFormatMask& aMask, TInt aPos, TInt aLength)
/** Applies character formatting to a range of characters. The attributes which
are set in the mask are read from aFormat and applied. The attributes which
are not set in the mask are not changed. The range of characters affected
is from aPos to aPos+(aLength-1) inclusive. The sum of aPos and aLength
must be less than or equal to the document length, or a panic occurs.

@param aFormat Contains the character format attribute values to apply.
@param aMask Bitmask specifying which character format attributes should be
applied.
@param aPos Document position from which to apply the new character formatting.
Must be greater than or equal to zero, or a panic occurs.
@param aLength The number of characters to which the new formatting should
be applied. Must be greater than or equal to zero, or a panic occurs. If the
length is zero, the function has the same effect as SetInsertCharFormatL(). */
	{
	// Applies the specified character formatting to the characters conatined within the range
	// aPos to aPos+(aLength-1).
	// If aLength is zero, the SetInsertCharFormat state is called.
	//
	__ETEXT_WATCH(APPLY_CHAR_FORMAT);
	__TEST_INVARIANT;
	
	TInt document_length = DocumentLength();
	__ASSERT_ALWAYS(aPos >= 0,Panic(ECharPosBeyondDocument));
	__ASSERT_ALWAYS(aLength >= 0,Panic(EApplyCharFormatNegativeLength));
	__ASSERT_ALWAYS(aPos + aLength - 1 <= document_length,Panic(ECharPosBeyondDocument));

	//If some characters are highlighted AND current position + highlighted txt = document length	
	// Fix for INC097216. Compensate for the changes introduced to Form in defect fix INC087637,
	// which now considers the height of the EOD character; meaning that this character now 
	// needs to be formatted along with rest of text.
	if ((aLength > 0) && (aPos + aLength == document_length))
		{
		aLength++;
		}
	
	DoApplyExtendedCharFormatL(aFormat, aMask, aPos, aLength);
	
	__TEST_INVARIANT;
	__ETEXT_WATCH_END(APPLY_CHAR_FORMAT);
	}
	
// This method is used internally only. It does not format the EOD character.
void CRichText::ApplyExtendedCharFormatL(const TCharFormatX& aFormat,const TCharFormatXMask& aMask,TInt aPos,TInt aLength)
	{
	TInt document_length = DocumentLength();
	__ASSERT_ALWAYS(aPos >= 0,Panic(ECharPosBeyondDocument));
	__ASSERT_ALWAYS(aLength >= 0,Panic(EApplyCharFormatNegativeLength));
	__ASSERT_ALWAYS(aPos + aLength - 1 <= document_length,Panic(ECharPosBeyondDocument));
	
	DoApplyExtendedCharFormatL(aFormat, aMask, aPos, aLength);
	}	
	
// Apply the extended character formatting.	
void CRichText::DoApplyExtendedCharFormatL(const TCharFormatX& aFormat,const TCharFormatXMask& aMask,TInt aPos,TInt aLength)
	{
	if (aLength > 0)
		{
		CreateAndGenerateMarkupComponentL();
		iIndex->ApplyCharFormatL(aFormat,aMask,aPos,aLength);
		SetHasChanged(ETrue);
		}
	else
		SetExtendedInsertCharFormatL(aFormat,aMask,aPos);
	}		

EXPORT_C void CRichText::SetStyleListExternallyOwned(const CStyleList& aStyleList)
/** Assigns an externally owned style list to the rich text object.
Replaces any previous style list used by the object. Calls
SetStyleListExternallyOwned(ETrue).

@param aExternallyOwned The style list to assign to this rich text object.
Not owned by the rich text object. */
	{
	CStyleList* styleList = CONST_CAST(CStyleList*, &aStyleList);
	iStyleList = styleList;
	SetStyleListExternallyOwned(ETrue);
	}

EXPORT_C void CRichText::ApplyParagraphStyleL(const CParagraphStyle& aStyle, TInt aPos, TInt aLength, CParagraphStyle::TApplyParaStyleMode aMode)
/** Applies a specified paragraph style to a range of paragraphs. The region
affected consists of every paragraph containing one or more characters in the
range aPos to aPos+(aLength-1).

A panic occurs if:

aPos is invalid, or

aLength is negative, or

the range goes beyond the end of the document, or

the rich text object has no style list

@param aStyle The style to apply.
@param aPos The document position of the start of the range.
@param aLength The number of characters in the range.
@param aMode Controls what specific formatting, if any, should be preserved
when the style is applied. */
	{
	// Applies the specified paragraph style to the paragraphs covering
	// character positions aPos to aPos+aLength-1.
	//
	__TEST_INVARIANT;
	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
	__ASSERT_ALWAYS(aLength >= 0, Panic(EApplyParaStyleNegativeLength));
	__ASSERT_ALWAYS(aPos + (aLength - 1) <= DocumentLength(), Panic(ECharPosBeyondDocument));
	__ASSERT_ALWAYS(StyleListPresent(), Panic(ERichTextNotSetForUsingStyles));

	CreateAndGenerateMarkupComponentL();
	iIndex->ApplyParagraphStyleL(aStyle, aPos, aLength, iGlobalCharFormatLayer, aMode);
	SetHasChanged(ETrue);

	__TEST_INVARIANT;
	}

EXPORT_C void CRichText::NotifyStyleChangedL(const CParagraphStyle* aTo, const CParagraphStyle* aFrom)
/** Removes a style from every paragraph in the document to which it applies,
and replaces it with another.

If style aTo is NULL, aFrom is replaced by the global character and paragraph
format layers, so that in effect, style aFrom is removed. Any specific
formatting which has been applied to the paragraphs is retained.

Notes:

This function should be called on the text content object after changing a
style in the style list.

A panic occurs if the rich text object does not use a style list (this can
be tested for using StyleListPresent()).

@param aTo The new paragraph style to apply.
@param aFrom The paragraph style to remove. */
	{
	// Update the rich text index following the change of an applied paragraph style.
	//
	__TEST_INVARIANT;
	__ASSERT_ALWAYS(StyleListPresent(), Panic(ERichTextNotSetForUsingStyles));

	CreateAndGenerateMarkupComponentL();
	iIndex->NotifyStyleChangedL(aTo, aFrom, *iGlobalParaFormatLayer, *iGlobalCharFormatLayer);
	SetHasChanged(ETrue);

	__TEST_INVARIANT;
	}

EXPORT_C const CParaFormatLayer* CRichText::ParagraphStyle(TBool& aStyleChangesOverRange, TInt aPos, TInt aLength) const
/** Gets a pointer to the first paragraph style encountered in the specified
range.

@param aStyleChangesOverRange On return, set to ETrue if more than one paragraph
style is used over the specified range of characters. Otherwise EFalse
@param aPos The document position of the start of the range. Must be valid.
@param aLength The number of characters in the range. Must be greater than
or equal to zero.
@return Pointer to the paragraph style which applies to the paragraph containing
document position aPos. Its type (returned by CParaFormatLayer::Type())
indicates whether this object is a style, or just a paragraph format layer. */
	{
	__TEST_INVARIANT;
	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
	__ASSERT_ALWAYS(aLength >= 0,Panic(EParagraphStyleNegativeLength));
	__ASSERT_ALWAYS(aPos + (aLength - 1) <= DocumentLength(), Panic(ECharPosBeyondDocument));

	if (IndexPresent())
		return iIndex->ParagraphStyle(aStyleChangesOverRange, aPos, aLength);
	else
		{
		aStyleChangesOverRange = EFalse;
		return iGlobalParaFormatLayer;
		}
	}

EXPORT_C void CRichText::SetHasChanged(TBool aHasChanged)
/** Sets whether the document's content or formatting has changed. This function
is called with an value of ETrue by all functions which modify the text content
or formatting. Use CEditableText::HasChanged() to test whether the document
has changed.

@param aHasChanged ETrue if the text object has been changed, EFalse if not. */
	{
	// Replaces the base class method of the same name.
	//
	if (aHasChanged && IndexPresent())
		iIndex->DocumentChanged();
	CGlobalText::SetHasChanged(aHasChanged);
	}

EXPORT_C void CRichText::RemoveSpecificParaFormatL(TInt aPos, TInt aLength)
/** Removes all specific paragraph formatting from a range of paragraphs. This
does not remove formatting from the object's global paragraph format layer.
The region affected consists of every paragraph containing one or more
characters in the range covered by document position aPos to aPos+(aLength-1)
inclusive.

A panic occurs in the following situations:

the position is negative,

the length is negative,

the range goes beyond the end of the document

@param aPos The document position of the start of the range.
@param aLength The number of characters in the range. */
	{
	// Removes all specific paragraph formatting from the selected region.
	//
	__ETEXT_WATCH(REMOVE_PARA_FORMAT);

	__TEST_INVARIANT;

	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
	__ASSERT_ALWAYS(aLength >= 0, Panic(ERemoveSpecificParaFormatNegativeLength));
	__ASSERT_ALWAYS(aPos + (aLength - 1) <= DocumentLength(), Panic(ECharPosBeyondDocument));

	if (IndexPresent())
		{
		iIndex->RemoveSpecificParaFormatL(aPos, aLength);
		SetHasChanged(ETrue);
		}

	__TEST_INVARIANT;

	__ETEXT_WATCH_END(REMOVE_PARA_FORMAT);
	}

EXPORT_C void CRichText::RemoveSpecificCharFormatL(TInt aPos, TInt aLength)
/** Removes all specific character formatting from a range of characters (does
not remove the formatting which has been taken from the object's global character
format layer). A panic occurs in the following situations:

the position is negative,

the length is negative,

the range goes beyond the end of the document

@param aPos The document position of the start of the region affected.
@param aLength The number of characters in the region affected. If zero, the
function has no effect. */
	{
	__TEST_INVARIANT;
	
	TInt document_length = DocumentLength();
	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
	__ASSERT_ALWAYS(aLength >= 0, Panic(ERemoveSpecificParaFormatNegativeLength));
	__ASSERT_ALWAYS(aPos + (aLength - 1) <= DocumentLength(), Panic(ECharPosBeyondDocument));
	
	// in correspondance to INC097216, character format removing considers the height of end of document
    // character
    if (aPos + aLength == document_length)
   		{
        aLength++;
        }

	if (aLength > 0 && IndexPresent())
		{
		iIndex->RemoveSpecificCharFormatL(aPos, aLength);
		SetHasChanged(ETrue);
		}

	__TEST_INVARIANT;
	}

EXPORT_C void CRichText::GetChars(TPtrC& aText, TCharFormat& aFormat, TInt aPos) const
/** Gets a constant pointer descriptor to a portion of the text object
with constant character formatting.

The view starts at the document position specified, and ends at: the
last character which shares the same character formatting, orthe end
of the document, orthe end of the segment, if segmented storage is
being usedwhichever occurs first. Also fills a character format object
with the character formatting of the range of characters.

@param aView On return, a constant pointer to a portion of the text.
@param aFormat On return, contains the character formatting of the text.
@param aPos The start position for the view. Must be a valid document
position, or a panic occurs. */
	{
	// Get a run of text and its format, starting at aPos.
	__ETEXT_WATCH(GET_CHARS)
	TCharFormatX format;
	GetTextAndExtendedFormat(aText, format, aPos);
	OverrideFormatForParsersIfApplicable(aText, format, aPos);
	aFormat = format.iCharFormat;
	OverrideFormatOfInlineTextIfApplicable(aText, aFormat, aPos);
	__ETEXT_WATCH_END(GET_CHARS)
	}


void CRichText::GetTextAndExtendedFormat(TPtrC& aText,TCharFormatX& aFormat,TInt aPos) const
	{
	__TEST_INVARIANT;
	TInt documentLength = DocumentLength();
	__ASSERT_ALWAYS(aPos >= 0 && aPos <= documentLength, Panic(ECharPosBeyondDocument));
	if (!IndexPresent())
		{
		aText.Set(Read(aPos));
		iGlobalCharFormatLayer->SenseEffective(aFormat);
		}
	else
		{
		int phrase_length = iIndex->GetChars(aFormat,aPos);
		aText.Set(Read(aPos,phrase_length));
		}
	__TEST_INVARIANT;
	}

EXPORT_C TInt CRichText::GetPictureSizeInTwips(TSize& aSize, TInt aPos) const
/** Gets the size of a picture located at a specified document position.

@param aSize On return, contains the size of the picture located at aPos.
@param aPos Document position of the picture. Must be a valid position.
@return KErrNotFound if there is no picture at the specified document position,
KErrNone if there is. */
	{
	__TEST_INVARIANT;
	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));

	if (IndexPresent())
		return iIndex->GetPictureSizeInTwips(aSize, aPos);
	else
		return KErrNotFound;
	}

EXPORT_C CPicture* CRichText::PictureHandleL(TInt aPos, MLayDoc::TForcePictureLoad aForceLoad) const
/** Gets a pointer to the picture located at a specified document position, if
one exists. If the picture is not in memory, the function loads it (if the
second argument has a value of MLayDoc::EForceLoadTrue).

Note:

In order to load the picture, a picture factory and a store resolver must
have been set.

@param aPos Document position of the picture character. Must be a valid position.
@param aForceLoad If the picture is not loaded into memory,
MLayDoc::EForceLoadTrue loads it using the picture factory;
MLayDoc::EForceLoadFalse does not, and in this case, the function returns NULL.
@return A pointer to the picture located at aPos. NULL if aPos does not specify
a picture character, or if there is a picture at aPos which is not in memory,
and the second argument is MLayDoc::EForceLoadFalse. */
	{
	__ETEXT_WATCH(PICTURE_HANDLE);

	__TEST_INVARIANT;
	__ASSERT_ALWAYS(aPos>=0 && aPos<=DocumentLength(),Panic(ECharPosBeyondDocument));

	if (IndexPresent())
		return iIndex->PictureHandleL(aPos, aForceLoad);
	else
		return NULL;

	__ETEXT_WATCH_END(PICTURE_HANDLE);
	}

EXPORT_C void CRichText::GetParagraphFormatL(CParaFormat* aFormat, TInt aPos) const
/** Gets the effective paragraph formatting which applies to the paragraph which
contains a specified document position. On return, aFormat is filled with
values for all paragraph format attributes.

@param aFormat On return, filled with the paragraph's effective paragraph
formatting.
@param aPos Any document position within the paragraph of interest. */
	{
	__ETEXT_WATCH(GET_PARAGRAPH_FORMAT)

	__TEST_INVARIANT;
	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));

	if (!IndexPresent())
		CGlobalText::GetParagraphFormatL(aFormat, aPos);
	else
		{
		aFormat->Reset();
		iIndex->GetParagraphFormatL(aFormat, aPos);
		}

	__ETEXT_WATCH_END(GET_PARAGRAPH_FORMAT)
	}

EXPORT_C void CRichText::GetSpecificParagraphFormatL(CParaFormat* aFormat,
													 TParaFormatMask& aMask,
													 TInt aPos) const
// Fills aFormat with the effective Paragraph format attributes for the paragraph
// in which character position aPos is contained.
//
	{
	__ETEXT_WATCH(GET_PARAGRAPH_FORMAT)

	__TEST_INVARIANT;
	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));

	aFormat->Reset();
	aMask.ClearAll();
	if (IndexPresent())
		iIndex->GetSpecificParagraphFormatL(aFormat, aMask, aPos);

	__ETEXT_WATCH_END(GET_PARAGRAPH_FORMAT)
	}

EXPORT_C void CRichText::GetParaFormatL(CParaFormat* aFormat, TParaFormatMask& aVaries, TInt aPos, TInt aLength,
										CParaFormat::TParaFormatGetMode aMode) const
/** Gets the effective paragraph formatting which applies to a range of paragraphs.
The region involved is every paragraph containing one or more characters in
the range aPos to aPos+(aLength-1) inclusive. On return, aFormat is filled
with values for all paragraph format attributes and the mask indicates the
values that change over the region, and whose value is therefore indeterminate.

Note:

If aMode has a value of EFixedAttributes, the function cannot leave.

@param aFormat Must not be NULL or a panic occurs. On return, contains the
effective paragraph formatting for the range of paragraphs.
@param aVaries On return, a bitmask indicating which paragraph format attributes
vary over the range of characters selected.
@param aPos The document position of the start of the range.
@param aLength The number of characters in the range.
@param aMode The default, EAllAttributes means that values for all paragraph
format attributes are written to aFormat. EFixedAttributes means that tabs,
bullets and borders are not written to aFormat. */
	{
	// Senses the paragraph format of para(s) covered by the region aPos to aPos+aLength-1.
	// aFormat takes the values of all attributes, and the mask aMask indicates those values that change
	// over the selected region, and are therefore *indeterminate*.
	// Application: seeding paragraph formatting dialogs.
	//
	__TEST_INVARIANT;
	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
	__ASSERT_ALWAYS(aLength >= 0, Panic(EGetParaFormatNegativeLength));
	__ASSERT_ALWAYS(aPos + aLength <= DocumentLength(), Panic(ECharPosBeyondDocument));

	if (IndexPresent())
		iIndex->GetParaFormatL(aFormat, aVaries, aPos, aLength, aMode);
	else
		CGlobalText::GetParaFormatL(aFormat, aVaries, aPos, aLength, aMode);
	}

EXPORT_C void CRichText::GetCharFormat(TCharFormat& aFormat, TCharFormatMask& aVaries, TInt aPos, TInt aLength) const
/** Gets the effective character formatting which applies to a range of
characters. The range of characters involved is from aPos to aPos+(aLength-1)
inclusive. On return, aFormat is filled with values for all character format
attributes, and on return, the mask indicates the values that change over the
region, and whose value is therefore indeterminate.

The length value can be zero. In this case, the character formatting sensed
is that of the character immediately to the left of the position specified.

@param aFormat On return, contains the character format values for the range
of characters.
@param aVaries On return, indicates which character format attributes vary
over the range.
@param aPos Document position of the start of the range. Must be valid or a
panic occurs.
@param aLength Number of characters in the range. Must be greater than or equal
to zero, or a panic occurs. */
	{
	// Senses the character formatting of the phrase(s) covered by the region aPos to aPos+aLength-1.
	// May be called with zero length.
	// aFormat takes the values of all character format attributes, and the mask aMask indicates those
	// values that change over the selected region, and are therefore *indeterminate*.
	// Application: seeding character formatting dialogs.
	//
	TCharFormatX format;
	TCharFormatXMask varies;
	GetExtendedCharFormat(format, varies, aPos, aLength);
	aFormat = format.iCharFormat;
	varies.ClearExtendedAttribs();
	aVaries = varies;
	}


void CRichText::GetExtendedCharFormat(TCharFormatX& aFormat, TCharFormatXMask& aVaries, TInt aPos, TInt aLength) const
	{
	__TEST_INVARIANT;
	int document_length = DocumentLength();
	__ASSERT_ALWAYS(aPos >= 0 && aPos <= document_length, Panic(ECharPosBeyondDocument));
	__ASSERT_ALWAYS(aLength >= 0, Panic(EGetCharFormatNegativeLength));
	__ASSERT_ALWAYS(aPos + aLength - 1 <= document_length, Panic(ECharPosBeyondDocument));

	if (IndexPresent())
		iIndex->GetCharFormat(aFormat, aVaries, aPos, aLength);
	else
		{
		iGlobalCharFormatLayer->SenseEffective(aFormat);
		aVaries.ClearAll();
		}
	}


void CRichText::GetSpecificCharFormatLeftRight(TCharFormat& aFormat,
									TCharFormatMask& aMask,
									TInt aPos,
									TBool aLeft) const
	{
	__ETEXT_WATCH(GET_SPECIFIC_CHARS);

	__TEST_INVARIANT;

	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));

	if (IndexPresent())
		{
		TCharFormatX format;
		TCharFormatXMask mask;
		iIndex->GetSpecificCharFormatDirection(format, mask, aPos, aLeft);
		aFormat = format.iCharFormat;
		mask.ClearExtendedAttribs();
		aMask = mask;
		}

	__ETEXT_WATCH_END(GET_SPECIFIC_CHARS);
	}

EXPORT_C void CRichText::GetSpecificCharFormat(TCharFormat& aFormat, TCharFormatMask& aMask, TInt aPos) const
/** Gets the specific character formatting which applies to the character to the
left of a document position. Specific formatting is just the formatting which
has been applied over the object's global format layers - it is not the
same as the effective formatting.

@param aFormat On return contains the specific character formatting which
applies to the character to the left of the document position.
@param aMask On return, indicates which character format attributes have been
applied specifically, (not taken from the global layers).
@param aPos The document position. Must be valid or a panic occurs. */
	{
	// Return the format attributes store in the specific layer only, for the specified document position.
	// THIS IS NOT THE EFFECTIVE FORMAT, BUT THE SPECIFIC FORMATTING ONLY.
	//
	GetSpecificCharFormatLeftRight(aFormat, aMask, aPos, ETrue);
	}

EXPORT_C void CRichText::GetSpecificCharFormatRight(TCharFormat& aFormat, TCharFormatMask& aMask, TInt aPos) const
/** Gets the specific character formatting which applies to the character logically
after a document position. Note that this is not necessarily the character
to the right of the document position, because right to left text is supported.

Specific formatting is just the formatting which has been applied over the
object's global format layers it is not the same as the effective formatting.

@param aFormat On return, contains the specific character formatting which
applies to the character logically after the document position.
@param aMask On return, indicates which character format attributes have been
applied specifically, (not taken from the global layers).
@param aPos The document position. Must be valid or a panic occurs. */
	{
	GetSpecificCharFormatLeftRight(aFormat, aMask, aPos, EFalse);
	}

TBool CRichText::IndexPresent()const
// Returns ETrue if the rich text index is present, otherwise
// returns EFalse.
//
	{
	return iIndex.IsPtr() && iIndex.AsPtr();
	}


EXPORT_C TInt CRichText::PictureCount() const
/** Gets a count of the number of pictures contained in the rich text object.

@return The picture count. */
	{return (IndexPresent()) ? iIndex->iPictureCount : 0;}


void CRichText::SetParaTypeIsSingle(TBool aBool)
	{
	if (aBool)
		iFlags |= KParaTypeIsSingle;
	else
		iFlags &= ~KParaTypeIsSingle;
	}


TBool CRichText::ParaTypeIsSingle() const
	{return iFlags & KParaTypeIsSingle;}


EXPORT_C void CRichText::AppendTakingSolePictureOwnershipL(const CRichText& aSource)
/** Appends a rich text object to this one. The text is appended immediately after
the end-of-text paragraph delimiter. The incoming text's formatting is set
to be based on the global formatting of this rich text object.

Notes:

If this rich text object is empty (e.g. because it is newly initialised, or
has been reset), then the end-of-text delimiter of the incoming rich text
is not appended. This avoids the possibility of having a trailing paragraph
delimiter, giving one more empty line than would typically be desired.

If the incoming rich text contains pictures which have been loaded into memory,
their sole ownership is transferred to the current rich text object. In aSource,
these picture handles are set to NULL.

@param aSource The rich text object to append. */
	{
	// Appends the specified rich text object to this one.
	// If this rich text is empty or has been newly initialised or reset, then the final paragraph delimiter
	// of the incoming rich text is NOT appended, thus avoiding the prospect of having a trailing, empty
	// paragraph delimiter, giving one more empty line than would typically be desired.
	//
	__ETEXT_WATCH(APPEND_RICH_TEXT);

	// Append the text
	const TInt thisDocumentLength = DocumentLength();
	MPictureFactory* factory = NULL;
	MRichTextStoreResolver* resolver = NULL;
	if (thisDocumentLength == 0)  // this is an empty document
		{
		factory = iPictureFactory;
		resolver = iStoreResolver;
		Reset();  // destroy the markup component if it exists.  Makes the job easier
		}
	//
	PrepareAppendMarkupL(aSource);  // requires no rollback - objects in a (bigger) stable state
	//
	TRAPD(ret, DoAppendTakingSolePictureOwnershipL(aSource));
	if (ret != KErrNone)
		{
		CPlainText::Delete(thisDocumentLength,DocumentLength() - thisDocumentLength);
		User::Leave(ret);
		}
	if (thisDocumentLength == 0)
		{
		DeleteParagraph(0, 1);   // remove excess first paragraph from empty documents
		SetPictureFactory(factory, resolver);
		}
	int new_length = DocumentLength() - thisDocumentLength;
	iParserData->MergeRange(thisDocumentLength,0,new_length);
	CallEditObserver(thisDocumentLength,new_length);

	__TEST_INVARIANT;

	__ETEXT_WATCH_END(APPEND_RICH_TEXT);
	}


void CRichText::DoAppendTakingSolePictureOwnershipL(const CRichText& aSource)
	{
	TInt lengthRemaining = aSource.DocumentLength() + 1;  // we want to append the para delimiters also!
	TInt consumed = 0;
	FOREVER
		{
		TPtrC view = aSource.Read(consumed);
		if (view.Length() > lengthRemaining)
			view.Set(view.Ptr(), lengthRemaining);
		CPlainText::DoPtInsertL(DocumentLength() + 1, view);  // insert AFTER the final paragraph delimiter
		TInt viewLength = view.Length();
		lengthRemaining -= viewLength;
		if (lengthRemaining == 0)
			break;
		consumed += viewLength;
		}

	if (IndexPresent())
		{
		__ASSERT_DEBUG(aSource.IndexPresent(), User::Invariant());       // PrepareAppend should have sorted this

		TGlobalLayerInfoAppend info(GlobalParaFormatLayer(), GlobalCharFormatLayer(), aSource.GlobalParaFormatLayer(), aSource.GlobalCharFormatLayer());
		iIndex->AppendTakingSolePictureOwnershipL(aSource.iIndex, info);
		}

	__TEST_INVARIANT;
	}


void CRichText::PrepareAppendMarkupL(const CRichText& aSource)
// Guarantees that both the component and aggregate objects have a valid markup component.
//
	{
	if (aSource.HasMarkupData() && !HasMarkupData())
		CreateAndGenerateMarkupComponentL();
	else if (IndexPresent())
		CONST_CAST(CRichText&, aSource).CreateAndGenerateMarkupComponentL();
	}


EXPORT_C void CRichText::AppendParagraphL(TInt aReplicas)
/** Appends one or more empty paragraphs to the document. The new paragraphs take
on the formatting specified in the global format layers.

@param aReplicas The number of empty paragraphs to append to the document.
By default, a single paragraph. */
	{
	// Inserts an empty paragraph at the end of the document.
	// The new paragraph takes on the format as described
	// by the global format layers.
	//
	__ETEXT_WATCH(APPEND_PARAGRAPH);

	__TEST_INVARIANT;

	TInt documentLength = DocumentLength();
	if (aReplicas == 1)
		CPlainText::InsertL(documentLength, CEditableText::EParagraphDelimiter);
	else
		{
		HBufC* bb = HBufC::NewLC(aReplicas);
		TPtr buf = bb->Des();
		buf.Fill(CEditableText::EParagraphDelimiter, aReplicas);
		CPlainText::InsertL(documentLength, buf);
		CleanupStack::PopAndDestroy();  // bb
		}
	//
	if (IndexPresent())
		{
		TRAPD(ret,
		iIndex->AppendParagraphL(iGlobalParaFormatLayer, iGlobalCharFormatLayer, aReplicas));
		if (ret != KErrNone)
			{
			CPlainText::Delete(DocumentLength() - aReplicas, aReplicas);
			User::Leave(ret);
			}
		SetHasChanged(ETrue);
		}

	int new_length = DocumentLength() - documentLength;
	iParserData->MergeRange(documentLength,0,new_length);
	CallEditObserver(documentLength,new_length);
	__TEST_INVARIANT;

	__ETEXT_WATCH_END(APPEND_PARAGRAPH);
	}
