textrendering/texthandling/ttext/TRTCUSTM.CPP
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 16 Apr 2010 16:55:07 +0300
changeset 16 56cd22a7a1cb
parent 0 1fb32624e06b
child 51 a7c938434754
permissions -rw-r--r--
Revision: 201011 Kit: 201015

/*
* 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 <txtrich.h>
#include <txtstyle.h>
#include "TXTMRTSR.H"
#include <e32test.h>
#include <gdi.h>
#include <conpics.h>											   						 
#include <s32mem.h>
#include <s32file.h>
#include <flddef.h>
#include <fldbltin.h>
#include <fldset.h>

GLDEF_C RTest test(_L("TRTCUSTM"));
LOCAL_D RFs theFs;
LOCAL_D CFileStore* TheStore;
LOCAL_D CParaFormatLayer* GlobalParaFormatLayer;
LOCAL_D CCharFormatLayer* GlobalCharFormatLayer;
LOCAL_D CTrapCleanup* TheTrapCleanup;
const TInt KTestCleanupStack=0x500;


class TStoreResolver : public MRichTextStoreResolver
	{
public:
	virtual const CStreamStore& StreamStoreL(TInt /*aPos*/)const {return *iStore;}
public:
	CStreamStore* iStore;
	};


////////////////////////////////////////////////////////////////////////////////////////////
class TTestFieldFactory : public MTextFieldFactory
	{
public:
	// from MTextFieldFactory
	virtual CTextField* NewFieldL(TUid aFieldType); 
	// Creates a field of the type specified
	// Returns NULL if it does not recognise/support the field type
	};

CTextField* TTestFieldFactory::NewFieldL(TUid aFieldType)
// Creates a field (in aHeader) of the type specified in aHeader
// 
	{
	CTextField* field=NULL;
	if (aFieldType==KDateTimeFieldUid)
		field = (CTextField*)new(ELeave) CDateTimeField();
	return field;
	}
/////////////////////////////////////////////////////////////////////////////////////////////

LOCAL_D void WriteInlineL(RWriteStream& aStream,CRichText* aRichText)
	{
	aRichText->CancelInsertCharFormat();
	aRichText->ExternalizeFieldDataL(aStream);
	aRichText->ExternalizeStyleDataL(aStream);
	TBool hasMarkupData=aRichText->HasMarkupData();
	aStream.WriteUint8L((TUint8)hasMarkupData!=EFalse);
	if (hasMarkupData)
		aRichText->ExternalizeMarkupDataL(aStream);	
	aRichText->ExternalizePlainTextL(aStream);
	}

LOCAL_D void ReadInlineL(RReadStream& aStream,CRichText* aRichText)
	{
	aRichText->InternalizeFieldDataL(aStream);
	aRichText->InternalizeStyleDataL(aStream);
	TBool hasMarkupData=(TBool)aStream.ReadUint8L();
	if (hasMarkupData)
		aRichText->InternalizeMarkupDataL(aStream);	
	aRichText->InternalizePlainTextL(aStream);
	}


LOCAL_D TStreamId PerformSaveL(CRichText* aRichText)
//
	{
	RStoreWriteStream out;
	TStreamId id1=out.CreateLC(*TheStore);
	WriteInlineL(out,aRichText);
	delete aRichText;
	out.CommitL();
	CleanupStack::PopAndDestroy();  // out
	TheStore->CommitL();
	return id1;
	}


LOCAL_C CRichText* PerformLoadL(TStreamId aId)
//
	{
	CRichText* text=CRichText::NewL(GlobalParaFormatLayer,GlobalCharFormatLayer);
	RStoreReadStream in;
	in.OpenLC(*TheStore,aId);
	TRAPD(ret,
	ReadInlineL(in,text));
	test(ret==KErrNone);
	CleanupStack::PopAndDestroy();  // in
	return text;
	}


LOCAL_C CStyleList* CreatePopulatedStyleList()
//
	{
	//
	// Create style aswell.
	CStyleList* list=CStyleList::NewL();
	CParagraphStyle* style1=CParagraphStyle::NewL(*GlobalParaFormatLayer,*GlobalCharFormatLayer);
	CParagraphStyle* style2=CParagraphStyle::NewL(*GlobalParaFormatLayer,*GlobalCharFormatLayer);
	CParagraphStyle* style3=CParagraphStyle::NewL(*GlobalParaFormatLayer,*GlobalCharFormatLayer);
	RParagraphStyleInfo info1(style1);
	RParagraphStyleInfo info2(style2);
	RParagraphStyleInfo info3(style3);
	list->AppendL(&info1);
	list->AppendL(&info2);
	list->AppendL(&info3);
	return list;
	}

_LIT(KTRtCustOutputFile,"c:\\etext\\TRTCUSTM.DAT");

LOCAL_C void CustomLoadSave()
	{
// Set up the framework
	theFs.Delete(KTRtCustOutputFile);
	theFs.MkDirAll(KTRtCustOutputFile);
	TheStore=CPermanentFileStore::CreateLC(theFs,KTRtCustOutputFile,EFileRead|EFileWrite);
	TheStore->SetTypeL(TheStore->Layout());

//
// Case (1) Rich text with owned style list. - no markup data
//
	test.Start(_L("RT + no markup + style list owned"));
	//
	// Setup the rich text
	CStyleList* list=CreatePopulatedStyleList();
	CRichText* richText1=CRichText::NewL(GlobalParaFormatLayer,GlobalCharFormatLayer,*list);
	TBool hasMarkupData=richText1->HasMarkupData();
	test(hasMarkupData);
	richText1->InsertL(0,_L("hello"));
	richText1->InsertL(richText1->DocumentLength(),CEditableText::EParagraphDelimiter);
	richText1->InsertL(richText1->DocumentLength(),_L("there"));
	//
	// Save the rich text
	TStreamId id1=PerformSaveL(richText1);
	//
	// Load the rich text
	CRichText* empty=NULL;
	TRAPD(ret,
	empty=PerformLoadL(id1));
	test(ret==KErrNone);
	hasMarkupData=empty->HasMarkupData();
	test(hasMarkupData);
	delete empty;
	empty=NULL;
//
// Case (2) Rich text with referenced style list. - no markup data
//
	test.Next(_L("RT + no markup + style list externally owned"));
	//
	// Setup the rich text
	list=CreatePopulatedStyleList();
	richText1=CRichText::NewL(GlobalParaFormatLayer,GlobalCharFormatLayer,*list,CEditableText::ESegmentedStorage,2);
	richText1->SetStyleListExternallyOwned(ETrue);
	hasMarkupData=richText1->HasMarkupData();
	test(!hasMarkupData);
	richText1->InsertL(0,_L("hello"));
	richText1->InsertL(richText1->DocumentLength(),CEditableText::EParagraphDelimiter);
	richText1->InsertL(richText1->DocumentLength(),_L("there"));
	richText1->InsertL(richText1->DocumentLength(),CEditableText::EParagraphDelimiter);
	hasMarkupData=richText1->HasMarkupData();
	test(!hasMarkupData);
	//
	// Save the rich text
	id1=PerformSaveL(richText1);
	//
	// Load the rich text
	empty=PerformLoadL(id1);
	hasMarkupData=empty->HasMarkupData();
	test(!hasMarkupData);
	TInt paragraphCount=empty->ParagraphCount();
	test(paragraphCount==3);
	test(list->Count()==3);  // the style list is now externally owned.
	delete empty;
	test(list->Count()==3);  // the style list should not have been destroyed by the rich text
	delete list;
	empty=NULL;
//
// Case (3) Rich text with referenced style list. - with markup
//
	test.Next(_L("RT + markup + style list externally owned"));
	// Setup the rich text
	list=CreatePopulatedStyleList();
	richText1=CRichText::NewL(GlobalParaFormatLayer,GlobalCharFormatLayer,*list);
	richText1->SetStyleListExternallyOwned(ETrue);
	hasMarkupData=richText1->HasMarkupData();
	test(!hasMarkupData);
	richText1->InsertL(0,_L("hello"));
	richText1->InsertL(richText1->DocumentLength(),CEditableText::EParagraphDelimiter);
	richText1->InsertL(richText1->DocumentLength(),_L("there"));
	richText1->InsertL(richText1->DocumentLength(),CEditableText::EParagraphDelimiter);
	CParaFormat* paraFormat=CParaFormat::NewLC();
	TParaFormatMask paraMask;
	paraFormat->iHorizontalAlignment=CParaFormat::ERightAlign;
	paraMask.SetAttrib(EAttAlignment);
	richText1->ApplyParaFormatL(paraFormat,paraMask,8,0);
	CleanupStack::PopAndDestroy();  // paraFormat
	hasMarkupData=richText1->HasMarkupData();
	test(hasMarkupData);
	//
	// Save the rich text
	id1=PerformSaveL(richText1);
	//
	// Load the rich text
	empty=PerformLoadL(id1);
	hasMarkupData=empty->HasMarkupData();
	test(hasMarkupData);
	paragraphCount=empty->ParagraphCount();
	test(paragraphCount==3);
	test(list->Count()==3);  // the style list is now externally owned.
	delete empty;
	test(list->Count()==3);  // the style list should not have been destroyed by the rich text
	delete list;
	empty=NULL;
//
// Case (4) Rich text with no style list. - no effective markup
//
	test.Next(_L("RT + delete picture + no style list"));
	// Setup the rich text
	richText1=CRichText::NewL(GlobalParaFormatLayer,GlobalCharFormatLayer);
	hasMarkupData=richText1->HasMarkupData();
	test(!hasMarkupData);
	richText1->InsertL(0,_L("hello"));
	richText1->InsertL(richText1->DocumentLength(),CEditableText::EParagraphDelimiter);
	richText1->InsertL(richText1->DocumentLength(),_L("there"));
	richText1->InsertL(richText1->DocumentLength(),CEditableText::EParagraphDelimiter);
	//
	// Create & insert a picture
	CXzePicture* pic1=CXzePicture::NewL('o');
	TPictureHeader header1;
	TSize size;
	pic1->GetSizeInTwips(size);
	header1.iPictureType=KUidXzePictureType;
	header1.iPicture=pic1;
	header1.iSize=size;
	richText1->InsertL(3,header1);
	hasMarkupData=richText1->HasMarkupData();
	test(hasMarkupData);
	richText1->DeleteL(3,1);
	hasMarkupData=richText1->HasMarkupData();
	test(!hasMarkupData);
	//
	// Save the rich text
	id1=PerformSaveL(richText1);
	//
	// Load the rich text
	empty=PerformLoadL(id1);
	hasMarkupData=empty->HasMarkupData();
	test(!hasMarkupData);
	paragraphCount=empty->ParagraphCount();
	test(paragraphCount==3);
	delete empty;
	empty=NULL;
//
// Case (5) Rich text with SetInsertCharFormat()
//
	test.Next(_L("RT + SetInsertCharFormatL()"));
	// Setup the rich text
	richText1=CRichText::NewL(GlobalParaFormatLayer,GlobalCharFormatLayer);
	hasMarkupData=richText1->HasMarkupData();
	test(!hasMarkupData);
	richText1->InsertL(0,_L("hello"));
	richText1->InsertL(richText1->DocumentLength(),CEditableText::EParagraphDelimiter);
	richText1->InsertL(richText1->DocumentLength(),_L("there"));
	richText1->InsertL(richText1->DocumentLength(),CEditableText::EParagraphDelimiter);
	TCharFormat charFormat;
	TCharFormatMask charMask;
	charFormat.iFontPresentation.iStrikethrough=EStrikethroughOn;
	charMask.SetAttrib(EAttFontStrikethrough);
	richText1->SetInsertCharFormatL(charFormat,charMask,3);
	hasMarkupData=richText1->HasMarkupData();
	test(hasMarkupData);
	//
	// Save the rich text
	id1=PerformSaveL(richText1);
	//
	// Load the rich text
	empty=PerformLoadL(id1);
	hasMarkupData=empty->HasMarkupData();
	test(!hasMarkupData);
	paragraphCount=empty->ParagraphCount();
	test(paragraphCount==3);
	delete empty;
	empty=NULL;
//
// Case (6) Rich text with components - default re/store used.
//

	test.Next(_L("RT + components"));
	// Setup the rich text
	richText1=CRichText::NewL(GlobalParaFormatLayer,GlobalCharFormatLayer);
	hasMarkupData=richText1->HasMarkupData();
	test(!hasMarkupData);
	richText1->InsertL(0,_L("hello"));
	richText1->InsertL(richText1->DocumentLength(),CEditableText::EParagraphDelimiter);
	richText1->InsertL(richText1->DocumentLength(),_L("there"));
	richText1->InsertL(richText1->DocumentLength(),CEditableText::EParagraphDelimiter);
	//
	// Create & insert some fields
	TTestFieldFactory factory;
	richText1->SetFieldFactory(&factory);
	CTextField* field=NULL;
	CTextField* field2=NULL;
	TRAP(ret,
	field=factory.NewFieldL(KDateTimeFieldUid));	test(ret==KErrNone);
	TRAP(ret,
	field2=factory.NewFieldL(KDateTimeFieldUid));    test(ret==KErrNone);
	TRAP(ret,
	richText1->InsertFieldL(0,field,KDateTimeFieldUid));  test(ret==KErrNone);
	TRAP(ret,
	richText1->UpdateFieldL(0));
	test(ret==KErrNone);
	TRAP(ret,
	richText1->InsertFieldL(0,field2,KDateTimeFieldUid));
	test(ret==KErrNone);
	TRAP(ret,
	richText1->UpdateFieldL(0));
	test(ret==KErrNone);
	//
	// Create & insert a picture
	pic1=CXzePicture::NewL('o');
	pic1->GetSizeInTwips(size);
	header1.iPictureType=KUidXzePictureType;
	header1.iPicture=pic1;
	header1.iSize=size;
	richText1->InsertL(0,header1);
	hasMarkupData=richText1->HasMarkupData();
	test(hasMarkupData);
	//
	// Save the rich text
	TStreamId head=richText1->StoreL(*TheStore);
	delete richText1;
	//
	// Load the rich text
	empty=CRichText::NewL(GlobalParaFormatLayer,GlobalCharFormatLayer);
	empty->SetFieldFactory(&factory);
	empty->RestoreL(*TheStore,head);
	//
	// Create the correct factories
	TStoreResolver storeResolver;
	storeResolver.iStore=TheStore;
	MDemPictureFactory* pictureFactory=new(ELeave) MDemPictureFactory;

	empty->SetPictureFactory(pictureFactory,&storeResolver);
	CXzePicture* rPic=(CXzePicture*)empty->PictureHandleL(0);
	test(rPic->iLabel=='o');
	//
	hasMarkupData=empty->HasMarkupData();
	test(hasMarkupData);
	paragraphCount=empty->ParagraphCount();
	test(paragraphCount==3);
	delete empty;
	empty=NULL;
	delete pictureFactory;
//
// Case (7) Rich text with fields with referenced style list with markup
//
	test.Next(_L("RT + fields + markup + style list"));
	// Setup the rich text
	list=CreatePopulatedStyleList();
	richText1=CRichText::NewL(GlobalParaFormatLayer,GlobalCharFormatLayer,*list);
	hasMarkupData=richText1->HasMarkupData();
	test(hasMarkupData);
	richText1->InsertL(0,_L("hello"));
	richText1->InsertL(richText1->DocumentLength(),CEditableText::EParagraphDelimiter);
	richText1->InsertL(richText1->DocumentLength(),_L("there"));
	richText1->InsertL(richText1->DocumentLength(),CEditableText::EParagraphDelimiter);
	paraFormat=CParaFormat::NewLC();
	paraMask.ClearAll();
	paraFormat->iHorizontalAlignment=CParaFormat::ERightAlign;
	paraMask.SetAttrib(EAttAlignment);
	richText1->ApplyParaFormatL(paraFormat,paraMask,8,0);
	CleanupStack::PopAndDestroy();  // paraFormat
	hasMarkupData=richText1->HasMarkupData();
	test(hasMarkupData);
	//
	// Now add a text field to this.
	richText1->SetFieldFactory(&factory);
	field=NULL;
	TRAP(ret,
	field=factory.NewFieldL(KDateTimeFieldUid));
	test(ret==KErrNone);
	TRAP(ret,
	richText1->InsertFieldL(0,field,KDateTimeFieldUid));
	test(ret==KErrNone);
	TRAP(ret,
	richText1->UpdateFieldL(0));
	test(ret==KErrNone);
	//
	// Save the rich text
	//
	// 1st the components.
	CStoreMap* map=CStoreMap::NewLC(*TheStore);
	richText1->StoreFieldComponentsL(*TheStore,*map);
	//
	RStoreWriteStream out(*map);
	id1=out.CreateLC(*TheStore);
	//
	richText1->CancelInsertCharFormat();
	richText1->ExternalizeFieldDataL(out);
	richText1->ExternalizeStyleDataL(out);
	hasMarkupData=richText1->HasMarkupData();
	out.WriteUint8L((TUint8)hasMarkupData!=EFalse);
	if (hasMarkupData)
		richText1->ExternalizeMarkupDataL(out);	
	richText1->ExternalizePlainTextL(out);
	//
	delete richText1;
	out.CommitL();
	//
	map->Reset();
	CleanupStack::PopAndDestroy(2);  // map,out
	TheStore->CommitL();
	//
	// Load the rich text
	empty=CRichText::NewL(GlobalParaFormatLayer,GlobalCharFormatLayer);
	empty->SetFieldFactory(&factory);
	RStoreReadStream in;
	in.OpenLC(*TheStore,id1);
	empty->InternalizeFieldDataL(in);
	empty->InternalizeStyleDataL(in);
	hasMarkupData=(TBool)in.ReadUint8L();
	if (hasMarkupData)
		empty->InternalizeMarkupDataL(in);	
	empty->InternalizePlainTextL(in);
	CleanupStack::PopAndDestroy();  // in
	//	
	empty->RestoreFieldComponentsL(*TheStore);
	hasMarkupData=empty->HasMarkupData();
	test(hasMarkupData);
	paragraphCount=empty->ParagraphCount();
	test(paragraphCount==3);
	test(empty->StyleList()->Count()==3);  // the style list is now externally owned.
	delete empty;
	empty=NULL;
//
// Case (8) Rich text with fields with referenced style list with markup - default store
//
	test.Next(_L("RT + fields + markup + style list - default store"));
	// Setup the rich text
	list=CreatePopulatedStyleList();
	richText1=CRichText::NewL(GlobalParaFormatLayer,GlobalCharFormatLayer,*list);
	hasMarkupData=richText1->HasMarkupData();
	test(hasMarkupData);
	richText1->InsertL(0,_L("hello"));
	richText1->InsertL(richText1->DocumentLength(),CEditableText::EParagraphDelimiter);
	richText1->InsertL(richText1->DocumentLength(),_L("there"));
	richText1->InsertL(richText1->DocumentLength(),CEditableText::EParagraphDelimiter);
	paraFormat=CParaFormat::NewLC();
	paraMask.ClearAll();
	paraFormat->iHorizontalAlignment=CParaFormat::ERightAlign;
	paraMask.SetAttrib(EAttAlignment);
	richText1->ApplyParaFormatL(paraFormat,paraMask,8,0);
	CleanupStack::PopAndDestroy();  // paraFormat
	hasMarkupData=richText1->HasMarkupData();
	test(hasMarkupData);
	//
	// Now add a text field to this.
	richText1->SetFieldFactory(&factory);
	field=NULL;
	TRAP(ret,
	field=factory.NewFieldL(KDateTimeFieldUid));
	test(ret==KErrNone);
	TRAP(ret,
	richText1->InsertFieldL(0,field,KDateTimeFieldUid));
	test(ret==KErrNone);
	TRAP(ret,
	richText1->UpdateFieldL(0));
	test(ret==KErrNone);
	//
	// Save the rich text
	id1=richText1->StoreL(*TheStore);
	delete richText1;
	//
	// Load the rich text
	empty=CRichText::NewL(GlobalParaFormatLayer,GlobalCharFormatLayer);
	empty->SetFieldFactory(&factory);
	empty->RestoreL(*TheStore,id1);
	//
	hasMarkupData=empty->HasMarkupData();
	test(hasMarkupData);
	paragraphCount=empty->ParagraphCount();
	test(paragraphCount==3);
	TEtextComponentInfo ci=empty->ComponentInfo();	
	test(ci.iFieldCount==1);
	test(ci.iStyleCount==3);
	delete empty;
	empty=NULL;	//
//
// Case (9) Rich text with no components whatsoever - default store
//
	test.Next(_L("RT + no markup whatsoever - default store"));
	richText1=CRichText::NewL(GlobalParaFormatLayer,GlobalCharFormatLayer);
	hasMarkupData=richText1->HasMarkupData();
	test(!hasMarkupData);
	richText1->InsertL(0,_L("hello"));
	richText1->InsertL(richText1->DocumentLength(),CEditableText::EParagraphDelimiter);
	richText1->InsertL(richText1->DocumentLength(),_L("there"));
	richText1->InsertL(richText1->DocumentLength(),CEditableText::EParagraphDelimiter);
	//
	// Save the rich text
	id1=richText1->StoreL(*TheStore);
	delete richText1;
	//
	// Load the rich text
	empty=CRichText::NewL(GlobalParaFormatLayer,GlobalCharFormatLayer);
	empty->RestoreL(*TheStore,id1);
	//
	hasMarkupData=empty->HasMarkupData();
	test(!hasMarkupData);
	paragraphCount=empty->ParagraphCount();
	test(paragraphCount==3);
	ci=empty->ComponentInfo();
	test(ci.iFieldCount==0);
	test(ci.iStyleCount==0);
	test(ci.iPictureCount==0);
	delete empty;
	empty=NULL;
	//
	//
	CleanupStack::PopAndDestroy();  // TheStore
	test.End();
	}
	
/**
@SYMTestCaseID 			SYSLIB-ETEXT-CT-3380
@SYMTestCaseDesc  		Inserting and deleting rich text and verify behaviours of DeleteParagraph(), Delete() and NotifyDelete(); 
  ie. when encounter paragraph delimiter and hidden character
@SYMTestPriority  		High
@SYMTestActions 		1. Insert and delete a whole paragraph of rich text
						2. Insert a paragraph and delete some text
						3. Insert 2nd paragraph and DeleteParagraph() both
						4. Insert field to rich text object and delete field
						5. Insert field to rich text object after some plain text and delete it 
						6. Insert field to rich text object in between two plain texts then delete the field to merge the texts
						7. Insert plain text in between two fields to rich text object then delete plain text to merge two fields
@SYMTestExpectedResults	CTextFieldSet, CPlainText, CRichText's APIs for performing various types of deletion function properly when 
  dealing with paragraph delimiter and hidden characters in a paragraph
@SYMDEF					PDEF101757
*/
LOCAL_C void TestDEF101757()
	{	
	TFindFieldInfo info;
	TTestFieldFactory factory;
	CTextField* field=NULL;
	CTextField* field1=NULL;
	CTextField* field2=NULL;
	CTextField* field3=NULL;
	CTextField* field4=NULL;
	
	_LIT(KSample1, "Hello"); // length:5
	_LIT(KSample2, "How are you"); // length:11
	
	test.Start(_L(" @SYMTestCaseID:SYSLIB-ETEXT-CT-3380 Insertion and deletion to rich text object "));
	
	CRichText* richText=CRichText::NewL(GlobalParaFormatLayer,GlobalCharFormatLayer);
	CleanupStack::PushL(richText);
	
	// Case1
	// Insert and delete a whole paragraph of rich text
	richText->InsertL(0, KSample1);
	richText->InsertL(richText->DocumentLength(),CEditableText::EParagraphDelimiter);
	test(richText->DocumentLength()==6);
	
	richText->DeleteParagraph(0, 5); //do not delete para delimiter
	
	richText->InsertL(0, KSample1);
	richText->InsertL(richText->DocumentLength(),CEditableText::EParagraphDelimiter);
	test(richText->DocumentLength()==7);
	
	richText->DeleteParagraph(0, 7); //delete para delimiter
	
	test(richText->DocumentLength()==0);
	test(richText->ParagraphCount()==1); 
	
	// Case2
	// Insert a paragraph and delete some text
	richText->InsertL(0, KSample2);
	richText->InsertL(richText->DocumentLength(),CEditableText::EParagraphDelimiter);
	test(richText->DocumentLength()==12);
	
	richText->DeleteParagraph(10, 1); //delete last char 'u' and not para delimter
	test(richText->DocumentLength()==11);
	
	richText->DeleteParagraph(9, 2); //delete last char 'o' and also para delimter
	test(richText->DocumentLength()==9);
	//should not panic due to fix for PDEF101757
	
	test(richText->ParagraphCount()==1);
	
	// Case3
	// Insert 2nd paragraph and DeleteParagraph() both
	richText->InsertL(richText->DocumentLength(),CEditableText::EParagraphDelimiter);
	test(richText->ParagraphCount()==2);
	richText->InsertL(richText->DocumentLength(),KSample1);
	richText->InsertL(richText->DocumentLength(),CEditableText::EParagraphDelimiter);
	test(richText->DocumentLength()==16);
	test(richText->ParagraphCount()==3); //2 paragraph delimiters including EOD delimiter (always there)

	richText->DeleteParagraph(0, 16);
	test(richText->DocumentLength()==0);
	test(richText->ParagraphCount()==1); //2 paragrsphs deleted
	
	// Case4
	// Insert field to rich text object and delete field
	richText->SetFieldFactory(&factory);
	field=factory.NewFieldL(KDateTimeFieldUid); // TUid KDateTimeFieldUid: length:10
	CleanupStack::PushL(field);
	richText->InsertFieldL(0,field,KDateTimeFieldUid);
	CleanupStack::Pop(field);//richtext has taken ownership successfully
	
	richText->UpdateFieldL(0);
	test(richText->FieldCount()==1);
	richText->DeleteParagraph(0, 10);
	richText->UpdateFieldL(0);
	test(richText->FieldCount()==0);
	
	// Case5
	// Insert field to rich text object after some plain text and delete it 
	richText->InsertL(0, KSample1); 
	field1=factory.NewFieldL(KDateTimeFieldUid);
	CleanupStack::PushL(field1);
	richText->InsertFieldL(5, field1, KDateTimeFieldUid);
	CleanupStack::Pop(field1);
	richText->UpdateFieldL(5);
	test(richText->FieldCount()==1);
	richText->DeleteParagraph(5, 10);
	test(richText->FieldCount()==0);
	
	// Case6
	// Insert field to rich text object in between two plain texts then delete the field to merge the texts
	field2=factory.NewFieldL(KDateTimeFieldUid);
	CleanupStack::PushL(field2);
	richText->InsertFieldL(5, field2, KDateTimeFieldUid);
	CleanupStack::Pop(field2);
	richText->UpdateFieldL(5);
	richText->InsertL(14, KSample1);
	richText->DeleteParagraph(5, 10);
	test(richText->DocumentLength()==10); //two plain texts merged 
	richText->DeleteParagraph(0, 10);
	test(richText->FieldCount()==0);
	test(richText->DocumentLength()==0);
	
	// Case7
	// Insert plain text in between two fields to rich text object then delete plain text to merge two fields
	field3=factory.NewFieldL(KDateTimeFieldUid);
	field4=factory.NewFieldL(KDateTimeFieldUid);
	
	CleanupStack::PushL(field3);
	richText->InsertFieldL(0, field3, KDateTimeFieldUid);
	CleanupStack::Pop(field3);
	richText->UpdateFieldL(0);
	
	richText->InsertL(10, KSample1);
	
	CleanupStack::PushL(field4);
	richText->InsertFieldL(15, field4, KDateTimeFieldUid);
	CleanupStack::Pop(field4);
	richText->UpdateFieldL(15);
	richText->DeleteParagraph(10, 5);
	
	richText->FindFields(info,5);
	test(info.iFirstFieldLen==10);
	test(info.iFirstFieldPos==0);
	richText->FindFields(info,15);
	test(info.iFirstFieldLen==10);
	test(info.iFirstFieldPos==10);
	test(richText->FieldCount()==2);
	
	CleanupStack::PopAndDestroy(richText);
	test.End();
	}
	

//Testcode for INC054540
LOCAL_C void TestINC054540()
	{
	theFs.Delete(KTRtCustOutputFile);
	theFs.MkDirAll(KTRtCustOutputFile);
	TheStore=CPermanentFileStore::CreateLC(theFs,KTRtCustOutputFile,EFileRead|EFileWrite);
	TheStore->SetTypeL(TheStore->Layout());
//
//test 1: Test INC054540 fix for 255 fields
//	
	test.Start(_L("test"));
	
	CRichText* richText1=CRichText::NewL(GlobalParaFormatLayer,GlobalCharFormatLayer);
	//
	// Now add a text field to this.
	TTestFieldFactory factory;
	richText1->SetFieldFactory(&factory);
	CTextField* field=NULL;
	TInt x=0;
	//add 255 fields
	while(x<255)
		{
		TRAPD(ret,field=factory.NewFieldL(KDateTimeFieldUid));
		test(ret==KErrNone);
		TRAP(ret,richText1->InsertFieldL(0,field,KDateTimeFieldUid));
		test(ret==KErrNone);
		TRAP(ret,richText1->UpdateFieldL(0));
		test(ret==KErrNone);
		x++;
		}
	// Save the rich text
	//
	CStoreMap* map=CStoreMap::NewLC(*TheStore);
	richText1->StoreFieldComponentsL(*TheStore,*map);
	//
	RStoreWriteStream out1(*map);
	TStreamId id1=out1.CreateLC(*TheStore);
	//
	richText1->ExternalizeFieldDataL(out1);
	//
	delete richText1;
	out1.CommitL();
	//
	map->Reset();
	CleanupStack::PopAndDestroy(2);  // map,out1
	TheStore->CommitL();
	//
	// Load the rich text
	CRichText* empty=CRichText::NewL(GlobalParaFormatLayer,GlobalCharFormatLayer);
	empty->SetFieldFactory(&factory);
	RStoreReadStream in;
	in.OpenLC(*TheStore,id1);
	empty->InternalizeFieldDataL(in);
	CleanupStack::PopAndDestroy();  // in
	//	
	empty->RestoreFieldComponentsL(*TheStore);
	test(empty->FieldCount()==255);
	delete empty;
	empty=NULL;
	
//
//test 2: Test INC054540 fix for more than 255 fields
//	
	test.Next(_L("test for more than 255 fields"));
	richText1=CRichText::NewL(GlobalParaFormatLayer,GlobalCharFormatLayer);
	// Add the text fields
	richText1->SetFieldFactory(&factory);
	field=NULL;
	x=0;
	//add 256 fields
	while(x<256)
		{
		TRAPD(ret,field=factory.NewFieldL(KDateTimeFieldUid));
		test(ret==KErrNone);
		TRAP(ret,richText1->InsertFieldL(0,field,KDateTimeFieldUid));
		test(ret==KErrNone);
		TRAP(ret,richText1->UpdateFieldL(0));
		test(ret==KErrNone);
		x++;
		}
	// Save the rich text
	//
	map=CStoreMap::NewLC(*TheStore);
	richText1->StoreFieldComponentsL(*TheStore,*map);
	//
	RStoreWriteStream out2(*map);
	id1=out2.CreateLC(*TheStore);
	//
	richText1->ExternalizeFieldDataL(out2);
	//
	delete richText1;
	out2.CommitL();
	//
	map->Reset();	
	CleanupStack::PopAndDestroy(2);  // map,out2
	TheStore->CommitL();
	//
	// Load the rich text
	empty=CRichText::NewL(GlobalParaFormatLayer,GlobalCharFormatLayer);
	empty->SetFieldFactory(&factory);
	in.OpenLC(*TheStore,id1);
	empty->InternalizeFieldDataL(in);
	CleanupStack::PopAndDestroy();  // in
	//	
	empty->RestoreFieldComponentsL(*TheStore);
	test(empty->FieldCount()==256);
	delete empty;
	empty=NULL;
	//
	//
	CleanupStack::PopAndDestroy();  // TheStore
	test.End();
	}


LOCAL_C void doMainL()
	{
	GlobalParaFormatLayer=CParaFormatLayer::NewL();
	GlobalCharFormatLayer=CCharFormatLayer::NewL();
	theFs.Connect();
	//
	CustomLoadSave();
	TestDEF101757();
	TestINC054540();
	//
	delete GlobalParaFormatLayer;
	delete GlobalCharFormatLayer;
	theFs.Close();
	}


LOCAL_C void setupCleanup()
//
// Initialise the cleanup stack.
//
    {
	TheTrapCleanup=CTrapCleanup::New();
	test(TheTrapCleanup!=NULL);
	TRAPD(r,\
		{\
		for (TInt i=KTestCleanupStack;i>0;i--)\
			CleanupStack::PushL((TAny*)0);\
		CleanupStack::Pop(KTestCleanupStack);\
		});
	test(r==KErrNone);
	}


LOCAL_C void DeleteDataFile(const TDesC& aFullName)
	{
	RFs fsSession;
	TInt err = fsSession.Connect();
	if(err == KErrNone)
		{
		TEntry entry;
		if(fsSession.Entry(aFullName, entry) == KErrNone)
			{
			RDebug::Print(_L("Deleting \"%S\" file.\n"), &aFullName);
			err = fsSession.SetAtt(aFullName, 0, KEntryAttReadOnly);
			if(err != KErrNone) 
				{
				RDebug::Print(_L("Error %d changing \"%S\" file attributes.\n"), err, &aFullName);
				}
			err = fsSession.Delete(aFullName);
			if(err != KErrNone) 
				{
				RDebug::Print(_L("Error %d deleting \"%S\" file.\n"), err, &aFullName);
				}
			}
		fsSession.Close();
		}
	else
		{
		RDebug::Print(_L("Error %d connecting file session. File: %S.\n"), err, &aFullName);
		}
	}

GLDEF_C TInt E32Main()
//
// Test permanent file TheStore.
//
    {
	test.Title();
	setupCleanup();
	__UHEAP_MARK;
//
	test.Start(_L("Testing custom save/load optimization"));
	TRAPD(ret,doMainL());	
	test(ret==KErrNone);
	
	::DeleteDataFile(KTRtCustOutputFile); 	//deletion of data files must be before call to End() - DEF047652
	test.End();
//
	__UHEAP_MARKEND;
	delete TheTrapCleanup;
	test.Close();

	return KErrNone;
    }