// EIKEDWIN.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//
   
#include <s32mem.h>
#include <s32file.h>
#include <s32std.h>
#include <fldinfo.h>
#include <basched.h>
#include <baclipb.h>
#include <txtglobl.h>
#include <txtrich.h>
#include <txtfmlyr.h>
#include <txtfrmat.h>
#include <frmtview.h>
#include <barsread.h>
#include <coecobs.h>
#include <coemain.h>

#include <eikirda.h>
#include <eikbmstr.h>
#include <eikedwin.pan>
#include <eikpanic.h>
#include <eikdef.hrh>
#include <eikdutil.h>
#include <eiktxtut.h>
#include <eikchmap.h>
#include <eikfindd.h>
#include <eikenv.h>
#include <eikedwin.h>
#include <eikedwin.hrh>

#include <eikon.rsg>

#include <eikcolor.h>

GLDEF_C void Panic(TEikEdwinPanic aPanic)
	{
	User::Panic(_L("EIKON-EDWIN"),aPanic);
	}

//
// class CEikEdwinBeamer
//

CEikEdwinBeamer::~CEikEdwinBeamer()
	{
	delete iBs;
	}

TInt CEikEdwinBeamer::SendLD()
	{
	TCursorSelection selection=iEdwin->Selection();
	if (!selection.Length())
		{
		delete this;
		return KErrNotFound;
		}
	iBs=CBeamingStore::NewForSendingL();
	iEdwin->CopyToStoreL(iBs->Store(),iBs->StreamDictionary());
	iBs->ReadyToSendL();
	CEikIrDataSender* sender=CEikIrDataSender::NewL();
	sender->SetIrObserver(this);
	sender->SendLD(iBs->Buf());
	return KErrNone;
	}

void CEikEdwinBeamer::ReceiveLD()
	{
	iType=EReceiver;
	iBs=CBeamingStore::NewForReceivingL();
	CEikIrDataReceiver* receiver=CEikIrDataReceiver::NewL();
	receiver->SetIrObserver(this);
	receiver->ReceiveLD(iBs->Buf());
	// Asynch - completes in HandleIrEventL()
	}

EXPORT_C void CEikEdwinBeamer::HandleIrEventL(CEikIrMain* /*aIrMain*/, TEikIrEvent aEvent)
	{
	if (aEvent==ETransferComplete)
		{
		if (iType==EReceiver)
			{
			iBs->ReceiveCompleteL();
			iEdwin->SetPasteFromIrStore(ETrue);
			iEdwin->PasteFromStoreL(iBs->Store(), iBs->StreamDictionary());
			iEdwin->UpdateScrollBarsL();
			}
		}
	delete this;
	}

//
// class CEikEdwin::CUndoBuffer
//

class CEikEdwin::CUndoBuffer : public CBase
	{
public:
	static CUndoBuffer* NewL();
	~CUndoBuffer();
	CBufStore& Store() const;
	CStreamDictionary& Dictionary() const;
	TCursorSelection NewText() const;
	void SetNewText(const TCursorSelection& aSelection);
	TInt OldCursorPos() const;
	void SetOldCursorPos(TInt aPos);
private:
	void ConstructL();
private:
	CBufStore* iStore;
	CStreamDictionary* iDictionary;
	TCursorSelection iNewText;
	TInt iOldCursorPos;
	};

CEikEdwin::CUndoBuffer* CEikEdwin::CUndoBuffer::NewL()
	{ // static
	CUndoBuffer* self=new(ELeave) CUndoBuffer;
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop();  // self
	return self;
	}

CEikEdwin::CUndoBuffer::~CUndoBuffer()
	{
	delete iStore;
	delete iDictionary;
	}

void CEikEdwin::CUndoBuffer::ConstructL()
	{
	iStore=CBufStore::NewL(512);
	iDictionary=CStreamDictionary::NewL();
	}

CBufStore& CEikEdwin::CUndoBuffer::Store() const
	{return *iStore;}
CStreamDictionary& CEikEdwin::CUndoBuffer::Dictionary() const
	{return *iDictionary;}
TCursorSelection CEikEdwin::CUndoBuffer::NewText() const
	{return iNewText;}
void CEikEdwin::CUndoBuffer::SetNewText(const TCursorSelection& aSelection)
	{iNewText=aSelection;}
TInt CEikEdwin::CUndoBuffer::OldCursorPos() const
	{return iOldCursorPos;}
void CEikEdwin::CUndoBuffer::SetOldCursorPos(TInt aPos)
	{iOldCursorPos=aPos;}

//
// class CEikEdwinFepSupport
//

class CEikEdwinFepSupport : public CBase, public MCoeFepAwareItem, public MFepItemSupportingFepInlineEditing, public MFepItemSupportingFepContextInformation
	{
public:
	static CEikEdwinFepSupport* New(CEikEdwin& aEdwin);
	virtual ~CEikEdwinFepSupport();
	void FocusChanged();
private:
	CEikEdwinFepSupport(CEikEdwin& aEdwin);
	void UpdateTextViewAfterChangeInInlineTextL(TBool aParagraphContainingStartPositionOfInlineTextHasChangedFormat,TInt aNumberOfCharactersSuccessfullyDeleted,TInt aNumberOfCharactersSuccessfullyInserted,TInt aError);
	void UpdateTextViewAfterChangeInInlineTextL(TBool aParagraphContainingStartPositionOfInlineTextHasChangedFormat,TInt aNumberOfCharactersSuccessfullyDeleted,TInt aNumberOfCharactersSuccessfullyInserted,TInt aError,TInt aSelectionAnchorPosition);
	// from MCoeFepAwareItem
	virtual TBool IsFocused() const;
	virtual MFepItemSupportingFepInlineEditing* ItemSupportingFepInlineEditing();
	virtual MFepItemSupportingFepContextInformation* ItemSupportingFepContextInformation();
	virtual void MCoeFepAwareItem_Reserved_1();
	virtual void MCoeFepAwareItem_Reserved_2();
	// from MFepItemSupportingFepInlineEditing
	virtual void StartFepInlineEditL(const TDesC& aInitialInlineText,TInt aPositionOfInsertionPointInInlineText,MFepGetterOfFormatOfFepInlineText& aGetterOfFormatOfFepInlineText);
	virtual void UpdateFepInlineTextL(const TDesC& aNewInlineText,TInt aPositionOfInsertionPointInInlineText);
	virtual void CommitFepInlineEditL();
	virtual void CancelFepInlineEdit();
	virtual void GetFormatBeforeInsertionPointForFepInlineEdit(TCharFormat& aFormat) const;
	virtual void GetFormatAfterInsertionPointForFepInlineEdit(TCharFormat& aFormat) const;
	virtual void MFepItemSupportingFepInlineEditing_Reserved_1();
	virtual void MFepItemSupportingFepInlineEditing_Reserved_2();
	// from MFepItemSupportingFepContextInformation
	virtual void GetFepContextTextBeforeInsertionPoint(TDes& aTextBeforeInsertionPoint) const;
	virtual void GetFepContextTextAfterInsertionPoint(TDes& aTextAfterInsertionPoint) const;
	virtual TInt FepContextLengthOfSelection() const;
	virtual void GetFepContextSelection(TDes& aSelection) const;
	virtual void MFepItemSupportingFepContextInformation_Reserved_1();
	virtual void MFepItemSupportingFepContextInformation_Reserved_2();
private:
	CEikEdwin& iEdwin;
	TCursorSelection iOriginalSelection;
	TInt iPositionOfInsertionPointInDocument;
	TInt iPositionOfInlineTextInDocument;
	};

CEikEdwinFepSupport* CEikEdwinFepSupport::New(CEikEdwin& aEdwin)
	{
	return new CEikEdwinFepSupport(aEdwin);
	}

CEikEdwinFepSupport::~CEikEdwinFepSupport()
	{
	iEdwin.ControlEnv()->FepAwareItemIsNotFocusedOrIsBeingDestroyed(this);
	}

void CEikEdwinFepSupport::FocusChanged()
	{
	CCoeEnv* coeEnv=iEdwin.ControlEnv();
	if (iEdwin.IsFocused())
		{
		coeEnv->SetFocusedFepAwareItem(this,FepAwareItemIsFocused);
		}
	else
		{
		coeEnv->FepAwareItemIsNotFocusedOrIsBeingDestroyed(this);
		}
	}

CEikEdwinFepSupport::CEikEdwinFepSupport(CEikEdwin& aEdwin)
	:iEdwin(aEdwin),
	 iOriginalSelection(-1,-1),
	 iPositionOfInsertionPointInDocument(-1),
	 iPositionOfInlineTextInDocument(-1)
	{
	__DECLARE_NAME(_S("CEikEdwinFepSupport"));
	}

TBool CEikEdwinFepSupport::IsFocused() const
	{
	return iEdwin.IsFocused();
	}

MFepItemSupportingFepInlineEditing* CEikEdwinFepSupport::ItemSupportingFepInlineEditing()
	{
	return this;
	}

MFepItemSupportingFepContextInformation* CEikEdwinFepSupport::ItemSupportingFepContextInformation()
	{
	return this;
	}

void CEikEdwinFepSupport::MCoeFepAwareItem_Reserved_1()
	{
	}

void CEikEdwinFepSupport::MCoeFepAwareItem_Reserved_2()
	{
	}

void CEikEdwinFepSupport::UpdateTextViewAfterChangeInInlineTextL(TBool aParagraphContainingStartPositionOfInlineTextHasChangedFormat,TInt aNumberOfCharactersSuccessfullyDeleted,TInt aNumberOfCharactersSuccessfullyInserted,TInt aError)
	{
	UpdateTextViewAfterChangeInInlineTextL(aParagraphContainingStartPositionOfInlineTextHasChangedFormat,aNumberOfCharactersSuccessfullyDeleted,aNumberOfCharactersSuccessfullyInserted,aError,iPositionOfInsertionPointInDocument);
	}

void CEikEdwinFepSupport::UpdateTextViewAfterChangeInInlineTextL(TBool aParagraphContainingStartPositionOfInlineTextHasChangedFormat,TInt aNumberOfCharactersSuccessfullyDeleted,TInt aNumberOfCharactersSuccessfullyInserted,TInt aError,TInt aSelectionAnchorPosition)
	{
	iEdwin.iTextView->SetPendingSelection(TCursorSelection(iPositionOfInsertionPointInDocument,aSelectionAnchorPosition));
	iEdwin.iTextView->HandleInsertDeleteL(TCursorSelection(iPositionOfInlineTextInDocument+aNumberOfCharactersSuccessfullyInserted,iPositionOfInlineTextInDocument),aNumberOfCharactersSuccessfullyDeleted,aParagraphContainingStartPositionOfInlineTextHasChangedFormat);
	User::LeaveIfError(aError);
	iEdwin.ReportEventL(MCoeControlObserver::EEventStateChanged);
	if (aParagraphContainingStartPositionOfInlineTextHasChangedFormat)
		{
		iEdwin.ReportEdwinEventL(MEikEdwinObserver::EEventFormatChanged);
		}
	}

void CEikEdwinFepSupport::StartFepInlineEditL(const TDesC& aInitialInlineText,TInt aPositionOfInsertionPointInInlineText,MFepGetterOfFormatOfFepInlineText& aGetterOfFormatOfFepInlineText)
	{
	iEdwin.CheckNotReadOnlyL();
	const TCursorSelection selection=iEdwin.Selection();
	iOriginalSelection=selection;
	iPositionOfInsertionPointInDocument=selection.iCursorPos;
	iPositionOfInlineTextInDocument=selection.LowerPos();
	TBool paragraphContainingStartPositionOfInlineTextHasChangedFormat;
	TInt numberOfCharactersSuccessfullyDeleted;
	TInt numberOfCharactersSuccessfullyInserted;
	TRAPD(error,iEdwin.iText->StartFepInlineEditL(paragraphContainingStartPositionOfInlineTextHasChangedFormat,numberOfCharactersSuccessfullyDeleted,numberOfCharactersSuccessfullyInserted,iPositionOfInsertionPointInDocument,iPositionOfInlineTextInDocument+aPositionOfInsertionPointInInlineText,aInitialInlineText,iPositionOfInlineTextInDocument,selection.Length(),aGetterOfFormatOfFepInlineText));
	UpdateTextViewAfterChangeInInlineTextL(paragraphContainingStartPositionOfInlineTextHasChangedFormat,numberOfCharactersSuccessfullyDeleted,numberOfCharactersSuccessfullyInserted,error);
	}

void CEikEdwinFepSupport::UpdateFepInlineTextL(const TDesC& aNewInlineText,TInt aPositionOfInsertionPointInInlineText)
	{
	TBool paragraphContainingStartPositionOfInlineTextHasChangedFormat;
	TInt numberOfCharactersSuccessfullyDeleted;
	TInt numberOfCharactersSuccessfullyInserted;
	TRAPD(error,iEdwin.iText->UpdateFepInlineTextL(paragraphContainingStartPositionOfInlineTextHasChangedFormat,numberOfCharactersSuccessfullyDeleted,numberOfCharactersSuccessfullyInserted,iPositionOfInsertionPointInDocument,iPositionOfInlineTextInDocument+aPositionOfInsertionPointInInlineText,aNewInlineText));
	UpdateTextViewAfterChangeInInlineTextL(paragraphContainingStartPositionOfInlineTextHasChangedFormat,numberOfCharactersSuccessfullyDeleted,numberOfCharactersSuccessfullyInserted,error);
	}

void CEikEdwinFepSupport::CommitFepInlineEditL()
	{
	TBool paragraphContainingStartPositionOfInlineTextHasChangedFormat;
	TInt numberOfCharactersSuccessfullyDeleted;
	TInt numberOfCharactersSuccessfullyInserted;
	TRAPD(error1,iEdwin.iText->CommitFepInlineEditL(paragraphContainingStartPositionOfInlineTextHasChangedFormat,numberOfCharactersSuccessfullyDeleted,numberOfCharactersSuccessfullyInserted,iPositionOfInsertionPointInDocument,iPositionOfInsertionPointInDocument));
	TRAPD(error2,UpdateTextViewAfterChangeInInlineTextL(paragraphContainingStartPositionOfInlineTextHasChangedFormat,numberOfCharactersSuccessfullyDeleted,numberOfCharactersSuccessfullyInserted,error1));
	if (iEdwin.iUndoStore!=NULL)
		{
		iEdwin.iUndoStore->SetNewText(TCursorSelection(iPositionOfInlineTextInDocument+numberOfCharactersSuccessfullyInserted,iPositionOfInlineTextInDocument));
		}
	iOriginalSelection.SetSelection(-1,-1);
	iPositionOfInsertionPointInDocument=-1;
	iPositionOfInlineTextInDocument=-1;
	User::LeaveIfError(error2);
	}

void CEikEdwinFepSupport::CancelFepInlineEdit()
	{
	if (iOriginalSelection.iCursorPos>=0)
		{
		TBool paragraphContainingStartPositionOfInlineTextHasChangedFormat;
		TInt numberOfCharactersSuccessfullyDeleted;
		TInt numberOfCharactersSuccessfullyInserted;
		iEdwin.iText->CancelFepInlineEdit(paragraphContainingStartPositionOfInlineTextHasChangedFormat,numberOfCharactersSuccessfullyDeleted,numberOfCharactersSuccessfullyInserted,iPositionOfInsertionPointInDocument,iOriginalSelection.iCursorPos);
		TRAPD(notUsed,UpdateTextViewAfterChangeInInlineTextL(paragraphContainingStartPositionOfInlineTextHasChangedFormat,numberOfCharactersSuccessfullyDeleted,numberOfCharactersSuccessfullyInserted,KErrNone,iOriginalSelection.iAnchorPos));
		iOriginalSelection.SetSelection(-1,-1);
		iPositionOfInsertionPointInDocument=-1;
		iPositionOfInlineTextInDocument=-1;
		}
	}

void CEikEdwinFepSupport::GetFormatBeforeInsertionPointForFepInlineEdit(TCharFormat& aFormat) const
	{
	TCharFormatMask notUsed;
	TInt position=iEdwin.Selection().LowerPos();
	if (position>0)
		{
		--position;
		}
	STATIC_CAST(CGlobalText*,iEdwin.iText)->GetCharFormat(aFormat,notUsed,position,Min(iEdwin.TextLength(),1));
	}

void CEikEdwinFepSupport::GetFormatAfterInsertionPointForFepInlineEdit(TCharFormat& aFormat) const
	{
	TCharFormatMask notUsed;
	TInt position=iEdwin.Selection().HigherPos();
	const TInt textLength=iEdwin.TextLength();
	if (position<textLength)
		{
		++position;
		}
	STATIC_CAST(CGlobalText*,iEdwin.iText)->GetCharFormat(aFormat,notUsed,position,Min(textLength,1));
	}

void CEikEdwinFepSupport::MFepItemSupportingFepInlineEditing_Reserved_1()
	{
	}

void CEikEdwinFepSupport::MFepItemSupportingFepInlineEditing_Reserved_2()
	{
	}

void CEikEdwinFepSupport::GetFepContextTextBeforeInsertionPoint(TDes& aTextBeforeInsertionPoint) const
	{
	const TInt lowerPositionOfSelection=iEdwin.Selection().LowerPos();
	const TInt lengthOfTextToExtract=Min(lowerPositionOfSelection,aTextBeforeInsertionPoint.MaxLength());
	iEdwin.iText->Extract(aTextBeforeInsertionPoint,lowerPositionOfSelection-lengthOfTextToExtract,lengthOfTextToExtract);
	}

void CEikEdwinFepSupport::GetFepContextTextAfterInsertionPoint(TDes& aTextAfterInsertionPoint) const
	{
	iEdwin.iText->Extract(aTextAfterInsertionPoint,iEdwin.Selection().HigherPos(),aTextAfterInsertionPoint.MaxLength());
	}

TInt CEikEdwinFepSupport::FepContextLengthOfSelection() const
	{
	return iEdwin.Selection().Length();
	}

void CEikEdwinFepSupport::GetFepContextSelection(TDes& aSelection) const
	{
	const TCursorSelection selection=iEdwin.Selection();
	iEdwin.iText->Extract(aSelection,selection.LowerPos(),selection.Length());
	}

void CEikEdwinFepSupport::MFepItemSupportingFepContextInformation_Reserved_1()
	{
	}

void CEikEdwinFepSupport::MFepItemSupportingFepContextInformation_Reserved_2()
	{
	}

//
// class CEikEdwinExtra
//

class CEikEdwinExtra : public CBase
	{
public:
	MEikEdwinObserver* iEdwinObserver;
	CEikEdwinFepSupport* iEdwinFepSupport;
	};

//
// class CEikEdwin
//

const TInt KMaxInternalSegmentLength=80;
const TInt KFullFormattingUpperThreshold=2000;
const TInt KPartialFormattingLowerThreshold=1900;
const TInt KDefaultMargin=1;
const TInt KPointerRepeatRate=50000;
// const TInt KBlockDeleteWarningSize=300; // minimum block delete that will elicit a query

EXPORT_C CEikEdwin::~CEikEdwin()
	{
	if (iExtra!=NULL)
		{
		delete iExtra->iEdwinFepSupport;
		delete iExtra;
		}
	if (!(iEdwinUserFlags&EKeepDocument))
		delete iText;
	delete iTextView;
	delete iLayout;
	delete iSetScrollBar;
	delete iSBFrame;
	delete iUndoStore;
	}

EXPORT_C CEikEdwin::CEikEdwin()
	: CEikBorderedControl(TEikBorder(TEikBorder::ESingleGray)),
	iZoomFactor(iEikonEnv->ScreenDevice()),
	iBackground(KRgbWhite),
	iAvgCharsPerLine(1)
	{
	__DECLARE_NAME(_S("CEikEdwin"));
	iMargins.SetAllValuesTo(KDefaultMargin);
	iExtra=new CEikEdwinExtra; // don't leave
	if (iExtra!=NULL)
		{
		iExtra->iEdwinFepSupport=CEikEdwinFepSupport::New(*this); // don't leave
		}
	}

EXPORT_C CEikEdwin::CEikEdwin(const TEikBorder& aBorder)
	: CEikBorderedControl(aBorder),
	iZoomFactor(iEikonEnv->ScreenDevice()),
	iBackground(KRgbWhite),
	iAvgCharsPerLine(1)
	{
	__DECLARE_NAME(_S("CEikEdwin"));
	iMargins.SetAllValuesTo(KDefaultMargin);
	iExtra=new CEikEdwinExtra; // don't leave
	if (iExtra!=NULL)
		{
		iExtra->iEdwinFepSupport=CEikEdwinFepSupport::New(*this); // don't leave
		}
	}

EXPORT_C void CEikEdwin::ConstructFromResourceL(TResourceReader& aReader)
	{
	iEdwinUserFlags=aReader.ReadInt32();
	CalculateWidth(aReader.ReadInt16());
	iNumberOfLines=aReader.ReadInt16();
	iTextLimit=aReader.ReadInt16();
	BaseConstructL();
	}

EXPORT_C void CEikEdwin::BaseConstructL()
	{
	if (iEdwinUserFlags&EUserSuppliedText)
		{
		if (iNumberOfLines)
			iSize.iHeight=iNumberOfLines*iEikonEnv->NormalFont()->HeightInPixels()+iBorder.SizeDelta().iHeight
							+iMargins.iTop+iMargins.iBottom;
		return;
		}
	CParaFormatLayer* paraFormatLayer=(iNumberOfLines==1)
						? iEikonEnv->SystemSingleLineParaFormatLayerL()
						: iEikonEnv->SystemParaFormatLayerL();
	CCharFormatLayer* charFormatLayer=iEikonEnv->SystemCharFormatLayerL();
	CreateTextAndLayoutL(paraFormatLayer,charFormatLayer);
	}

EXPORT_C void CEikEdwin::CreateTextAndLayoutL(CParaFormatLayer* aParaFormatLayer,CCharFormatLayer* aCharFormatLayer)
	{
	CancelFepTransaction();
	CEditableText::TDocumentStorage storage=(iEdwinUserFlags&ESegmentedStorage)?
								CEditableText::ESegmentedStorage: CEditableText::EFlatStorage;
	CGlobalText* globalText = (iEdwinInternalFlags&ERichText)
						? CRichText::NewL(aParaFormatLayer,aCharFormatLayer,storage)
						: CGlobalText::NewL(aParaFormatLayer,aCharFormatLayer,storage);
	iText=globalText;
	CreateLayoutL(globalText);
	CalcHeightForNumOfLinesL(*globalText);
	}

EXPORT_C void CEikEdwin::ConstructL(TInt aEdwinFlags,TInt aWidthInChars,TInt aTextLimit,TInt aNumberOfLines)
	{
	iEdwinUserFlags=aEdwinFlags;
	CalculateWidth(aWidthInChars);
	iTextLimit=aTextLimit;
	iNumberOfLines=aNumberOfLines;
	BaseConstructL();
	}

EXPORT_C void CEikEdwin::SetEdwinObserver(MEikEdwinObserver* aEdwinObserver)
	{
	if (iExtra!=NULL)
		{
		iExtra->iEdwinObserver=aEdwinObserver;
		}
	}

EXPORT_C void CEikEdwin::SetDocumentContentL(CGlobalText& aText,TSetContent aContent)
	{
	__ASSERT_DEBUG(&aText,Panic(EEikPanicNullPointer));
	CancelFepTransaction();
	if (aContent==ECopyText)
		CopyDocumentContentL(aText,*((CGlobalText*)iText));
	else
		{
		iText=&aText; // relies on any existing iText having been deleted by the caller
		if (iLayout)
			iLayout->SetLayDoc(&aText);
		else
			CreateLayoutL(&aText);
		CalcHeightForNumOfLinesL(aText);
		}
	CheckRemovePictures(0,iText->DocumentLength());
	SetAmountToFormatL(ETrue);
	}

EXPORT_C void CEikEdwin::CopyDocumentContentL(CGlobalText& aInText,CGlobalText& aOutText)
	{
	__ASSERT_DEBUG(&aInText,Panic(EEikPanicNullPointer));
	__ASSERT_DEBUG(&aOutText,Panic(EEikPanicNullPointer));
	aOutText.Reset();
	CBufStore* store=CBufStore::NewLC(1024);
	TStreamId streamId=aInText.StoreL(*store);
	aOutText.RestoreL(*store,streamId);
	RStoreWriteStream paraWriteStream;
	TStreamId paraStreamId=paraWriteStream.CreateLC(*store);
	(aInText.GlobalParaFormatLayer())->ExternalizeL(paraWriteStream);
	CleanupStack::PopAndDestroy(); // paraStreamId
	RStoreReadStream paraReadStream;
	paraReadStream.OpenLC(*store,paraStreamId);
	((CParaFormatLayer*)(aOutText.GlobalParaFormatLayer()))->InternalizeL(paraReadStream);
	CleanupStack::PopAndDestroy(); // paraReadStream
	RStoreWriteStream charWriteStream;
	TStreamId charStreamId=charWriteStream.CreateLC(*store);
	(aInText.GlobalCharFormatLayer())->ExternalizeL(charWriteStream);
	CleanupStack::PopAndDestroy(); // charStreamId
	RStoreReadStream charReadStream;
	charReadStream.OpenLC(*store,charStreamId);
	((CCharFormatLayer*)(aOutText.GlobalCharFormatLayer()))->InternalizeL(charReadStream);
	CleanupStack::PopAndDestroy(2); // store and charReadStream
	}

EXPORT_C void CEikEdwin::CalculateWidth(TInt aWidthInChars)
	{
	const CFont* font=iEikonEnv->NormalFont();
	iSize.iWidth=aWidthInChars;
	if (!(iEdwinUserFlags&EWidthInPixels))
		iSize.iWidth*=(iEdwinInternalFlags&ENumericCharacters? font->WidthZeroInPixels(): font->MaxNormalCharWidthInPixels());
	iSize.iWidth+=iBorder.SizeDelta().iWidth+KEikDefaultCursorWidth+LineCursorWidth()+iMargins.iLeft+iMargins.iRight;
	}

void CEikEdwin::CalcHeightForNumOfLinesL(CGlobalText& aText)
	{
	if (iNumberOfLines==0)
		return;
	const TInt fontHeight=((100+CLayoutData::EFFontHeightIncreaseFactor)*iEikonEnv->NormalFont()->HeightInPixels())/100; // FORM adds EFFontHeightIncreaseFactor% to line height
	if (iNumberOfLines==1)
		iSize.iHeight=fontHeight;
	else if (iNumberOfLines>1)
		{
		CParaFormat* format=CParaFormat::NewLC();
		TParaFormatMask mask;
		aText.GetParaFormatL(format,mask,0,aText.DocumentLength());
		const TInt twips=iEikonEnv->NormalFont()->FontSpecInTwips().iHeight;
		const TInt lineHeight=Max(((KEikDefaultLineSpacingInTwips*fontHeight)+twips-1)/twips,		// temp code
			((format->iLineSpacingInTwips*fontHeight)+twips-1)/twips);
		iSize.iHeight=iNumberOfLines*lineHeight;
		CleanupStack::PopAndDestroy(); // format
		}
	iSize.iHeight+=iBorder.SizeDelta().iHeight+iMargins.iTop+iMargins.iBottom;
	}

EXPORT_C TKeyResponse CEikEdwin::OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType)
	{
	if (aType!=EEventKey)
		return EKeyWasNotConsumed;
	TInt code=aKeyEvent.iCode;
	const TCursorSelection selection=Selection();
	const TInt selectionLength=selection.Length();
	TInt cursorPos=CursorPos();
	const TInt docLength=iText->DocumentLength();
	if ((code==EKeyUpArrow || code==EKeyDownArrow) && iNumberOfLines==1)
		return EKeyWasNotConsumed;
	if (selectionLength==0)
		{
		TBool consume=!(code==EKeyUpArrow && cursorPos==0 && iLayout->FirstDocPosFullyInBand()==0);
		if (consume && code==EKeyDownArrow && cursorPos==docLength)
			{
			TPoint pos;
			if (iLayout->PosInBand(docLength,pos))
				{
				TRect paraRect=iLayout->ParagraphRectL(docLength);
				consume=!(paraRect.iBr.iY<=iTextView->ViewRect().iBr.iY);
				}
			}
		if (!consume)
			return EKeyWasNotConsumed;
		}
	if (iEdwinUserFlags&EDisplayOnly)
		return EKeyWasConsumed;
	const TInt modifiers=aKeyEvent.iModifiers;
	TBool navigation=EFalse;
	TBool formatChange=EFalse;
	TBool select=modifiers&EModifierShift;
	TBool magnify=modifiers&EModifierCtrl;
	const TInt oldLength=iText->DocumentLength();
	if (magnify && code<100 && code!=' ')//!!! magic number
		{
		TClipboardFunc clipboardFunc=ENoClipboard;
		TBuf<24> buf;
		if (select)
			iCoeEnv->ReadResource(buf,R_EIK_EDWIN_SHIFT_CTRL_HOTKEYS);
		else
			iCoeEnv->ReadResource(buf,R_EIK_EDWIN_CTRL_HOTKEYS);
		const TInt ret=buf.Locate(TChar(code+'a'-1));
		switch (ret)
			{
		case EHotKeyCut:
			CheckNotReadOnlyL();
			clipboardFunc=ECut;
			formatChange=ETrue;
			break;
		case EHotKeyCopy:
			clipboardFunc=ECopy;
			break;
		case EHotKeyPaste:
			CheckNotReadOnlyL();
			clipboardFunc=EPaste;
			formatChange=ETrue;
			break;
		case EHotKeyUndo:
			UndoL();
			return EKeyWasConsumed;
		case EHotKeyFind:
			if (iNumberOfLines==1)
				return EKeyWasConsumed; 
			if (!TextLength())
				iEikonEnv->InfoMsg(R_EIK_TBUF_NO_TEXT);
			else if (!FindL(NULL))
				iEikonEnv->InfoMsg(R_EIK_TBUF_TEXT_NOT_FOUND);
			else
				navigation=ETrue;
			break;
		case EHotKeyInsertChar:
			RunCharMapDialogL();
			return EKeyWasConsumed;
		default:
			;
			}
  		if (clipboardFunc>ENoClipboard)
			{
			ClipboardL(clipboardFunc);
			return EKeyWasConsumed;
			}
		}
	TBool reportChange=EFalse;
	TBool formatHasChanged;
	TInt charEditType=CTextLayout::EFCharacterInsert;
	switch (code)
		{
	case EKeyPageUp:
	case EKeyPageDown:
		if (magnify)
			SetCursorPosL((code==EKeyPageUp)? 0: TextLength(),select);
		else
			MoveCursorL((code==EKeyPageUp)? TCursorPosition::EFPageUp: TCursorPosition::EFPageDown, select);
		CancelInsertCharFormat();
		navigation=ETrue;
		break;
	case EKeyHome:
	case EKeyEnd:
		{
		const TBool cancelFormat=!((code==EKeyHome && selection.iCursorPos==0) ||
							(code==EKeyEnd && selection.iCursorPos==docLength));
		if (magnify)
			SetCursorPosL((code==EKeyHome? 0 : docLength),select);
		else
			MoveCursorL((code==EKeyHome)? TCursorPosition::EFLineBeg: TCursorPosition::EFLineEnd, select);
		if (cancelFormat)
			{
			CancelInsertCharFormat();
			navigation=ETrue;
			}
		break;
		}
	case EKeyUpArrow:
	case EKeyDownArrow:
		{
		const TBool cancelFormat=!((code==EKeyUpArrow && selection.iCursorPos==0) ||
							(code==EKeyDownArrow && selection.iCursorPos==docLength));
		if (selectionLength && !select)
			CancelSelectionL(code==EKeyUpArrow? EStart: EEnd);
		else
			{
			if (magnify)
				MoveCursorToChunkStartL(select,EChunkPara,(code==EKeyUpArrow)? EStart: EEnd);
			else
				MoveCursorL((code==EKeyUpArrow)? TCursorPosition::EFLineUp: TCursorPosition::EFLineDown, select);
			}
		if (cancelFormat)
			{
			CancelInsertCharFormat();
			navigation=ETrue;
			}
		break;
		}
	case EKeyLeftArrow:
	case EKeyRightArrow:
		{
		const TBool cancelFormat=!((code==EKeyLeftArrow && selection.iCursorPos==0) ||
						(code==EKeyRightArrow && selection.iCursorPos==docLength));
		if (selectionLength && !select)
			CancelSelectionL(code==EKeyLeftArrow? EStart: EEnd);
		else
			{
			if (magnify)
				MoveCursorToChunkStartL(select,EChunkWord,(code==EKeyLeftArrow)? EStart: EEnd);
			else
				MoveCursorL((code==EKeyLeftArrow)? TCursorPosition::EFLeft: TCursorPosition::EFRight, select);
			}
		if (cancelFormat)
			{
			CancelInsertCharFormat();
			navigation=ETrue;
			}
		break;
		}
	case EKeyEscape:
		if (selectionLength)
			iTextView->CancelSelectionL();
		break;
	case EKeyBackspace:
		if (magnify && !(modifiers&EModifierPureKeycode))
			break; // prevent Ctrl-H deleting
		if (select)
			code=EKeyDelete; // fall through
	case EKeyDelete:
		{
		CheckNotReadOnlyL();
		if (selectionLength)
			{
			DeleteHighlightL(formatHasChanged,code==EKeyBackspace);
			const TInt lower=selection.LowerPos();
			const TCursorSelection sel(lower,lower);
			iTextView->SetPendingSelection(sel);
			iTextView->HandleInsertDeleteL(sel,selectionLength,formatHasChanged);
			reportChange=ETrue;
			}
		else
			{
			TInt pos=-1;
			if (code==EKeyDelete)
				pos=cursorPos;
			else if (code==EKeyBackspace)
				{
				pos=cursorPos;
				--pos;
				}
			TBool isPicture=EFalse;
			if (pos>=0)
				{
				TPtrC text=iText->Read(pos,1); // look at the single character
				if (text[0]==CEditableText::EPictureCharacter)
					{
					if (!iEikonEnv->QueryWinL(R_EIK_CONFIRM_DELETE_OBJECT_TITLE))
						return EKeyWasConsumed;
					isPicture=ETrue;
					}
				}
			TBool doDelete=EFalse;
			if (code==EKeyBackspace)
				{
				charEditType=CTextLayout::EFLeftDelete;
				if (cursorPos>0)
					{
					doDelete=ETrue;
					--cursorPos;
					}
				}
			else
				{
				charEditType=CTextLayout::EFRightDelete;
				if (cursorPos<TextLength())
					doDelete=ETrue;
				}
			if (doDelete)
				{
				TCursorSelection selection=(isPicture? TCursorSelection(cursorPos,cursorPos+1) : TCursorSelection(cursorPos,cursorPos));
				DeleteL(formatHasChanged,selection,code==EKeyBackspace,isPicture);
				if (!isPicture)
					ClearUndo();
				iTextView->HandleCharEditL(charEditType,formatHasChanged);
				reportChange=ETrue;
				formatChange=formatHasChanged;
				}
			}
		}
		break;
	case '-':
		if (select|magnify)
			code=CEditableText::ENonBreakingHyphen;
		goto InChar;
	case EKeySpace:
		if (select)
			code=CEditableText::ENonBreakingSpace;
		goto InChar;
	case EKeyEnter:
		if (iEdwinUserFlags&ENoLineOrParaBreaks)
			return EKeyWasNotConsumed;
		CheckNotReadOnlyL();
		if (iNumberOfLines==1)
			break;
		code=(select? CEditableText::ELineBreak : CEditableText::EParagraphDelimiter);
		charEditType=(select? CTextLayout::EFCharacterInsert : CTextLayout::EFParagraphDelimiter);
		goto TestMagnify;
	case EKeyTab:
		code=CEditableText::ETabCharacter;
TestMagnify:
		if (magnify && !(modifiers&EModifierPureKeycode))
			break; // prevent eg Ctrl-I inserting a tab
		goto InChar;
	default:
		if (!TChar(code).IsPrint())
			break;
InChar: CheckNotReadOnlyL();
		TChar character(code);
		if (selectionLength)
			{
			TInt pos=DeleteHighlightL(formatHasChanged);
			TRAPD(err,iText->InsertL(pos,character));
			TCursorSelection selection=iTextView->Selection();
			if (err==KErrNone && iUndoStore)
				iUndoStore->SetNewText(TCursorSelection(selection.LowerPos(),selection.LowerPos()+1));
			TCursorSelection pending;
			pending.iCursorPos=selection.LowerPos()+1;
			pending.iAnchorPos=pending.iCursorPos;
			iTextView->SetPendingSelection(pending);
			selection=pending;
			--selection.iAnchorPos;
			iTextView->HandleInsertDeleteL(selection,selectionLength,formatHasChanged);
			User::LeaveIfError(err);
			reportChange=ETrue;
			formatChange=formatHasChanged;
			break;
			}
		if (!iTextLimit || TextLength()<iTextLimit)
			{
			iText->InsertL(CursorPos(),character);
			ClearUndo();
			iTextView->HandleCharEditL(charEditType);
			reportChange=ETrue;
			}
		else
			{
			ClearUndo();
			CEikonEnv::Beep();
			iEikonEnv->InfoMsg(R_EIK_TBUF_MAX_CHARACTERS_REACHED);
			}
		}
	if (reportChange)
		ReportEventL(MCoeControlObserver::EEventStateChanged);
	if (navigation)
		ReportEdwinEventL(MEikEdwinObserver::EEventNavigation);
	if (formatChange)
		ReportEdwinEventL(MEikEdwinObserver::EEventFormatChanged);
	const TInt length=iText->DocumentLength();
	if ((length>KFullFormattingUpperThreshold && oldLength<=KFullFormattingUpperThreshold) ||
		(length<=KPartialFormattingLowerThreshold && oldLength>KPartialFormattingLowerThreshold))
		SetAmountToFormatL();
	UpdateScrollBarsL();
	return EKeyWasConsumed;
	}

EXPORT_C void CEikEdwin::HandlePointerEventL(const TPointerEvent& aPointerEvent)
	{
	CancelFepTransaction();
	if (iEdwinUserFlags&EDisplayOnly)
		return;
	const TCursorSelection selection=iTextView->Selection();
	const TPoint pointerPos=aPointerEvent.iPosition;
	TPoint origPos=pointerPos;
	TInt newCursorPos=iTextView->XyPosToDocPosL(origPos);
	TBool select=(aPointerEvent.iModifiers&EModifierShift);
	TBool navigation=EFalse;
	switch (aPointerEvent.iType)
		{
	case TPointerEvent::EButton1Down:
		{
		iLastPointerDocPos=CursorPos();
		if (iTextView->ViewRect().Contains(pointerPos))
			iEdwinInternalFlags|=ELeftDownInViewRect;
		else
			return;
		if (aPointerEvent.iModifiers&EModifierCtrl)
			iEdwinInternalFlags|=EDragDouble;
		else
			iEdwinInternalFlags&=(~EDragDouble);
		TBool overPicture=EFalse;
		if (iEdwinInternalFlags&ERichText && !(iEdwinInternalFlags&EDragDouble))
			{
			TRect pictRect;
			overPicture=iTextView->GetPictureRectangleL(pointerPos,pictRect);
			}
		if (iEdwinInternalFlags&EDragDouble)
			{
			TInt startPos,length;
			GetWordInfo(newCursorPos,startPos,length);
			SetSelectionL(startPos+length,startPos);
			}
		else if (!overPicture)
			SetCursorPosL(newCursorPos,select);
		break;
		}
	case TPointerEvent::EButtonRepeat:
	case TPointerEvent::EDrag:
	case TPointerEvent::EButton1Up:
		{
		if (!(iEdwinInternalFlags&ELeftDownInViewRect))
			return;
		if (aPointerEvent.iType==TPointerEvent::EButton1Up)
			{
			if (newCursorPos!=iLastPointerDocPos)
				navigation=ETrue;
			iLastPointerDocPos=newCursorPos;
			iEdwinInternalFlags&=(~ELeftDownInViewRect);
			}
		TInt origWordStartPos, origWordLength;
		GetWordInfo(selection.iAnchorPos,origWordStartPos,origWordLength);
		TRect pictRect;
		if (aPointerEvent.iType==TPointerEvent::EButton1Up &&
			iTextView->GetPictureRectangleL(selection.LowerPos(),pictRect))
			break;
		select=ETrue;
		const TRect viewRect=iTextView->ViewRect();
		if (aPointerEvent.iType!=TPointerEvent::EButton1Up &&
			(pointerPos.iY>viewRect.iBr.iY || pointerPos.iY<viewRect.iTl.iY))
			{
			const TRect viewRect=iTextView->ViewRect();
			TRect rect(TPoint(viewRect.iTl.iX-1000,viewRect.iBr.iY),TSize(viewRect.Width()+2000,2000));
			if (pointerPos.iY>viewRect.iBr.iY)
				MoveCursorL(TCursorPosition::EFLineDown,select);
			else
				{
				MoveCursorL(TCursorPosition::EFLineUp,select);
				rect.Move(0,-(viewRect.Height()+2000));
				}
			Window().RequestPointerRepeatEvent(KPointerRepeatRate,rect);
			}
		else if (iEdwinInternalFlags&EDragDouble)
			{
			TInt newWordStartPos,newWordLength;
			GetWordInfo(newCursorPos,newWordStartPos,newWordLength);
			if (selection.iCursorPos>selection.iAnchorPos)
				{
				if (newCursorPos<selection.iAnchorPos)
					SetSelectionL(newWordStartPos,origWordStartPos+origWordLength);
				else
					SetCursorPosL(newWordStartPos+newWordLength,select);
				}
			else
				{
				if (newCursorPos>selection.iAnchorPos)
					SetSelectionL(newWordStartPos+newWordLength,origWordStartPos);
				else
					SetCursorPosL(newWordStartPos,select);
				}
			}
		else
			SetCursorPosL(newCursorPos,select);
		break;
		}
	default:
		break;
		}
	if (navigation)
		{
		ReportEdwinEventL(MEikEdwinObserver::EEventNavigation);
		CancelInsertCharFormat();
		}
	}

EXPORT_C void CEikEdwin::FocusChanged(TDrawNow /*aDrawNow*/)
	{
	if (iExtra!=NULL)
		{
		iExtra->iEdwinFepSupport->FocusChanged();
		}
	if (!iTextView)
		return;
	const TBool focused=IsFocused();
	TRAPD(ignored,SetCursorVisibilityL(focused));
	if (!focused && iEdwinUserFlags&EAlwaysShowSelection)
		;
	else
		TRAP(ignored,iTextView->SetSelectionVisibilityL(focused)); // !! inefficient
	}

EXPORT_C void CEikEdwin::ActivateL()
	{
	User::LeaveIfNull(iExtra);
	User::LeaveIfNull(iExtra->iEdwinFepSupport);
	iExtra->iEdwinFepSupport->FocusChanged();
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	EnableDragEvents();
	Window().SetPointerGrab(ETrue);
	if (iEdwinUserFlags&EJustAutoCurEnd)
		SetCursorPosL(TextLength(),EFalse);
	if (IsFocused())
		SetCursorVisibilityL(ETrue);
	else
		iTextView->SetSelectionVisibilityL(EFalse);
	UpdateScrollBarsL();
	ForceScrollBarUpdateL();
	CCoeControl::ActivateL();
	}

EXPORT_C void CEikEdwin::CreateTextViewL()
	{
	if (iTextView)
		return;
	__ASSERT_DEBUG(iLayout,Panic(EEikPanicEdwinNoLayout));
	if (iNumberOfLines==1 && !(iEdwinUserFlags&ENoHorizScrolling))
		iEdwinUserFlags|=ENoWrap;
	if (iEdwinUserFlags&ENoWrap)
		iLayout->ForceNoWrapping();
	iTextView=CTextView::NewL(iLayout,iBorder.InnerRect(Rect()),iEikonEnv->ScreenDevice(),
								iZoomFactor,&Window(),&iEikonEnv->RootWin(),&iEikonEnv->WsSession());
	if (LineCursorWidth())
		SetLineCursorDetailsL();
	if (iText->DocumentLength()>KFullFormattingUpperThreshold)
		SetAmountToFormatL(ETrue);
	FormatTextL();
	ApplyAutoSelectionL();
	}

EXPORT_C void CEikEdwin::SizeChangedL()
	{
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	if (IsReadyToDraw())
		TrappedSizeChanged();
	else
		HandleSizeChangedL();
	}

EXPORT_C void CEikEdwin::TrappedSizeChanged()
	{
	TRAPD(err,HandleSizeChangedL());
	if (err)
		{
		CWindowGc& gc=SystemGc();
		gc.Activate(Window());
		gc.Clear(iTextView->ViewRect());
		gc.Deactivate();
		iEikonEnv->NotifyIdleErrorWhileRedrawing(err);
		}
	}

EXPORT_C void CEikEdwin::HandleSizeChangedL()
	{
	if (!iTextView)
		CreateTextViewL();
	TRect displayRect=iBorder.InnerRect(Rect());
	displayRect=iMargins.InnerRect(displayRect);
	iTextView->SetViewRect(displayRect);
	if (!(iEdwinUserFlags&ENoWrap))
		iLayout->SetWrapWidth(LayoutWidth());
	TViewYPosQualifier yPosQualifier;
	yPosQualifier.SetFillScreen();
	yPosQualifier.SetMakeLineFullyVisible();
	iTextView->HandleGlobalChangeNoRedrawL(yPosQualifier);
	if (!IsReadyToDraw())
		ApplyAutoSelectionL();
	SetAmountToFormatL();
	UpdateScrollBarsL();
	}

EXPORT_C TInt CEikEdwin::CountComponentControls() const
	{
	TInt count=CEikBorderedControl::CountComponentControls();
	if (iSBFrame)
		count+=iSBFrame->CountComponentControls();
	return count;
	}

EXPORT_C CCoeControl* CEikEdwin::ComponentControl(TInt aIndex) const
	{
	TInt baseCount=CEikBorderedControl::CountComponentControls();
	if (aIndex<baseCount)
		return CEikBorderedControl::ComponentControl(aIndex);
	aIndex-=baseCount;
	return iSBFrame->ComponentControl(aIndex);
	}

EXPORT_C void CEikEdwin::SetAmountToFormatL(TBool aIsNewDoc)
	{
	if (!iTextView)
		return;
	const TInt chars=iText->DocumentLength();
	if (chars>KFullFormattingUpperThreshold)
		{
		if (aIsNewDoc)
			{
			iLayout->SetAmountToFormat();
			iTextView->FormatTextL();
			const TInt formattedLines=iLayout->NumFormattedLines();
			const TInt formattedHeight=iLayout->FormattedHeightInPixels();
			iAvgCharsPerLine=iLayout->FormattedLength()/formattedLines;
			iAvgLinesInViewRect=Max(1,(iTextView->ViewRect().Height()*formattedLines)/formattedHeight);
			}
		else
			{
			const TInt numLines=iLayout->NumFormattedLines();
			const TInt docHeight=iLayout->FormattedHeightInPixels();
			iAvgCharsPerLine=iLayout->FormattedLength()/numLines;
			iAvgLinesInViewRect=Max(1,(iTextView->ViewRect().Height()*numLines)/docHeight);
			iLayout->SetAmountToFormat();
			iTextView->HandleGlobalChangeNoRedrawL();
			}
		if (IsReadyToDraw() && iSBFrame && iSBFrame->VScrollBarVisibility()!=CEikScrollBarFrame::EOff)
			SetScrollBarsL();
		}
	else
		{
		iAvgCharsPerLine=1;
		iAvgLinesInViewRect=0;
		iLayout->SetAmountToFormat(CTextLayout::EFFormatAllText);
		if (aIsNewDoc)
			iTextView->FormatTextL();
		else
			iTextView->HandleGlobalChangeNoRedrawL();
		}
	}

EXPORT_C void CEikEdwin::ReportEdwinEventL(MEikEdwinObserver::TEdwinEvent aEventType)
	{
	User::LeaveIfNull(iExtra);
	MEikEdwinObserver* edwinObserver=iExtra->iEdwinObserver;
	if (edwinObserver!=NULL)
		edwinObserver->HandleEdwinEventL(this,aEventType);
	}

EXPORT_C void CEikEdwin::NotifyNewDocumentL()
	{
	__ASSERT_DEBUG(iTextView,Panic(EEikPanicEdwinNoView));
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	TViewYPosQualifier yPosQ;
	yPosQ.SetMakeLineFullyVisible();
	yPosQ.SetFillScreen(); // ???
	iTextView->HandleGlobalChangeL(yPosQ);
	TInt topLeftYPos=iTextView->ViewRect().iTl.iY;
	iTextView->SetViewL(0,topLeftYPos,yPosQ);
	iTextView->SetDocPosL(0);
	SetAmountToFormatL(ETrue);
	UpdateScrollBarsL();
	}

EXPORT_C void CEikEdwin::NotifyNewFormatL()
	{
	__ASSERT_DEBUG(iTextView,Panic(EEikPanicEdwinNoView));
	TViewYPosQualifier yPosQualifier;
	yPosQualifier.SetMakeLineFullyVisible();
	yPosQualifier.SetFillScreen();
	iTextView->HandleGlobalChangeNoRedrawL(yPosQualifier);
	ForceScrollBarUpdateL();
	iTextView->DrawL(iTextView->ViewRect());
	}

EXPORT_C void CEikEdwin::SetCursorPosL(TInt aDocPos,TBool aSelect)
	{
	CancelFepTransaction();
	if (!iTextView)
		CreateTextViewL();
	iTextView->SetDocPosL(aDocPos,aSelect);
	UpdateVertScrollBarThumbL();
	UpdateHorizScrollBarThumb();
	}

EXPORT_C void CEikEdwin::SetDocumentOwnership(TOwnershipType aOwner)
	{
	if (aOwner==EOwnsText)
		iEdwinUserFlags&=~EKeepDocument;
	else
		iEdwinUserFlags|=EKeepDocument;
	}

EXPORT_C void CEikEdwin::SetSelectionL(TInt aCursorPos,TInt aAnchorPos)
	{
	CancelFepTransaction();
	if (!iTextView)
		CreateTextViewL();
	iTextView->SetSelectionL(TCursorSelection(aCursorPos,aAnchorPos));
	CancelInsertCharFormat();
	UpdateVertScrollBarThumbL();
	UpdateHorizScrollBarThumb();
	}

EXPORT_C void CEikEdwin::SelectAllL()
	{
	CancelFepTransaction();
	SetSelectionL(0,TextLength());
	}

TBool CEikEdwin::OwnsScrollBars() const
	{
	if (!iSBFrame)
		return EFalse;
	return ((iSBFrame->HScrollBarVisibility()!=CEikScrollBarFrame::EOff)||(iSBFrame->VScrollBarVisibility()!=CEikScrollBarFrame::EOff));
	}

EXPORT_C void CEikEdwin::SetWordWrapL(TBool aWrapIsOn)
	{
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	__ASSERT_DEBUG(iLayout,Panic(EEikPanicEdwinNoLayout));
	if (aWrapIsOn)
		{
		iEdwinUserFlags&=~ENoWrap;
		iLayout->ForceNoWrapping(EFalse);
		iLayout->SetWrapWidth(LayoutWidth());
		}
	else
		{
		iEdwinUserFlags|=ENoWrap;
		iLayout->ForceNoWrapping();
		}
	NotifyNewFormatL();
	}

EXPORT_C TInt CEikEdwin::LineCursorWidth() const
	{
	return 0;
	}

EXPORT_C void CEikEdwin::SetLineCursorDetailsL()
	{
	__ASSERT_DEBUG(iTextView,Panic(EEikPanicEdwinNoView));
	iTextView->SetMarginWidths(0,LineCursorWidth());
	}

EXPORT_C void CEikEdwin::SetTextLimit(TInt aLimit)
	{
#if defined(_DEBUG)
	if (aLimit<iText->DocumentLength())
		Panic(EEikPanicEdwinInvalidTextLimit);
#endif
	iTextLimit=aLimit;
	}

EXPORT_C TMargins8 CEikEdwin::Margins() const
	{
	return iMargins;
	}

void CEikEdwin::ApplyAutoSelectionL()
	{
	const TInt length=TextLength();
	if (iEdwinUserFlags&EJustAutoCurEnd)
		SetCursorPosL(length,EFalse);
	else if (!(iEdwinUserFlags&ENoAutoSelection))
		SetSelectionL(length,0);
	else
		SetCursorPosL(0,EFalse);
	}

EXPORT_C TSize CEikEdwin::MinimumSize()
	{
	return iSize;
	}

EXPORT_C void CEikEdwin::SetContainerWindowL(const CCoeControl& aParent)
	{
	if (iEdwinUserFlags&EOwnsWindow && !OwnsWindow())
		CreateWindowL(&aParent);
	else
		CCoeControl::SetContainerWindowL(aParent);
	}

EXPORT_C void CEikEdwin::SetContainerWindowL()
	{
	__ASSERT_DEBUG(iEdwinUserFlags&EOwnsWindow, Panic(EEikPanicEdwinNoWindow));
	if (!OwnsWindow())
		CreateWindowL();
	}

EXPORT_C void CEikEdwin::FormatTextL()
	{
	if (iTextView) // else FormatTextL() will get called later, in ActivateL()
		iTextView->FormatTextL();
	}

EXPORT_C void CEikEdwin::CreateLayoutL(MLayDoc* aLayDoc)
	{
	__ASSERT_DEBUG(aLayDoc,Panic(EEikPanicEdwinNoText));
	iLayout=CTextLayout::NewL(aLayDoc,0); // supply real wrapping width later
	}

EXPORT_C void CEikEdwin::Draw(const TRect& /*aRect*/) const
	{
	const TRect rect=Rect();
	CWindowGc& gc=SystemGc();
	iBorder.Draw(gc,rect);
	const TRect viewRect=iTextView->ViewRect();
	gc.SetBrushColor(iBackground);
	EikDrawUtils::ClearBetweenRects(gc,iBorder.InnerRect(rect),viewRect);
	TrappedDraw(viewRect);
	}

EXPORT_C void CEikEdwin::TrappedDraw(const TRect& aViewRect) const
	{
	TRAPD(err,iTextView->DrawL(aViewRect));
	if (err)
		{
		SystemGc().Clear(aViewRect);
		iEikonEnv->NotifyIdleErrorWhileRedrawing(err);
		}
	}

EXPORT_C void CEikEdwin::SetDimmed(TBool aDimmed)
	{
	CCoeControl::SetDimmed(aDimmed);
	TRgb dimmedColor=iEikonEnv->ControlColor(EEikColorControlDimmedText,*this); // KEikEdwinDimmedTextColor
	TRgb* pointer=(&dimmedColor);
	if (!aDimmed)
		pointer=NULL;
	if (!iTextView)
		{
		TRAPD(err,CreateTextViewL());
		if (err)
			{
			SystemGc().Clear(iTextView->ViewRect());
			iEikonEnv->NotifyIdleErrorWhileRedrawing(err);
			}
		}
	iTextView->SetTextColorOverride(pointer);
	}

EXPORT_C void CEikEdwin::DrawContents()
	{
	const TRect rect=iBorder.InnerRect(Rect());
	TrappedDraw(rect);
	}

EXPORT_C void CEikEdwin::CancelSelectionL(TEnd aEndOfSelectionToLeaveCursor)
	{
	CancelFepTransaction();
	__ASSERT_DEBUG(iTextView,Panic(EEikPanicEdwinNoView));
	TCursorSelection cursorSelection=iTextView->Selection();
	SetCursorPosL((aEndOfSelectionToLeaveCursor==EStart)? cursorSelection.LowerPos(): cursorSelection.HigherPos(),EFalse);
	}

EXPORT_C void CEikEdwin::MoveCursorL(TCursorPosition::TMovementType aMovement,TBool aSelect)
	{
	CancelFepTransaction();
	__ASSERT_DEBUG(iTextView,Panic(EEikPanicEdwinNoView));
	TPoint movePoint=iTextView->MoveCursorL(aMovement,aSelect);
	if (movePoint.iX)
		UpdateHorizScrollBarThumb();
	if (movePoint.iY)
		UpdateVertScrollBarThumbL();
	}

EXPORT_C void CEikEdwin::MoveDisplayL(TCursorPosition::TMovementType aMovement)
	{
	__ASSERT_DEBUG(iTextView,Panic(EEikPanicEdwinNoView));
	const TInt move=iTextView->ScrollDisplayL(aMovement,CTextLayout::EFAllowScrollingBlankSpace);
	if (move)
		{
		switch (aMovement)
			{
		case TCursorPosition::EFLeft:
		case TCursorPosition::EFRight:
		case TCursorPosition::EFLineBeg:
		case TCursorPosition::EFLineEnd:
			UpdateHorizScrollBarThumb();
			break;
		case TCursorPosition::EFLineUp:
		case TCursorPosition::EFLineDown:
		case TCursorPosition::EFPageUp:
		case TCursorPosition::EFPageDown:
			UpdateVertScrollBarThumbL();
			break;
		default:
			break;
			}
		}
	}

EXPORT_C void CEikEdwin::CancelInsertCharFormat()
	{
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	if (iEdwinInternalFlags&ERichText)
		STATIC_CAST(CRichText*,iText)->CancelInsertCharFormat();
	}

EXPORT_C void CEikEdwin::MoveCursorToChunkStartL(TBool aSelect,TChunkSize aChunkSize,TEnd aEndScanningTowards)
	{
	CancelFepTransaction();
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	TInt cursorPos=CursorPos();
	TUint flags=CPlainText::EScanToUnitStart|CPlainText::EScanJoinDelimiters;
	if (aEndScanningTowards==EStart)
		flags|=CPlainText::EScanBackwards;
	switch (aChunkSize)
		{
		case EChunkWord:
			iText->ScanWords(cursorPos,flags);
			break;
		case EChunkPara:
			iText->ScanParas(cursorPos,flags);
			break;
		}
	if (cursorPos==CPlainText::EScanEndOfData)
		cursorPos=TextLength();
	SetCursorPosL(cursorPos,aSelect);
	}

EXPORT_C TInt CEikEdwin::CursorPos() const
	{
	__ASSERT_DEBUG(iTextView,Panic(EEikPanicEdwinNoView));
	TCursorSelection selection=iTextView->Selection();
	return(selection.iCursorPos);
	}

EXPORT_C TInt CEikEdwin::SelectionLength() const
	{
	__ASSERT_DEBUG(iTextView,Panic(EEikPanicEdwinNoView));
	TCursorSelection selection=iTextView->Selection();
	return(selection.Length());
	}

EXPORT_C TInt CEikEdwin::DeleteHighlightL(TBool& aChanged,TBool aIsBackSpace,TBool aPromptConfirmation)
	{
	__ASSERT_DEBUG(iTextView,Panic(EEikPanicEdwinNoView));
	CancelFepTransaction();
	const TCursorSelection selection=iTextView->Selection();
	if (aPromptConfirmation)
		{
		if (!OkToDeleteSelectionL())
			CBaActiveScheduler::LeaveNoAlert();
		}
	iTextView->CancelSelectionL();
	SetCursorPosL(selection.LowerPos(),EFalse);
	DeleteL(aChanged,selection,aIsBackSpace);
	return(selection.LowerPos());
	}

EXPORT_C TBool CEikEdwin::OkToDeleteSelectionL()
	{
	const TCursorSelection selection=iTextView->Selection();
	const TInt selectionLength=selection.Length();
//	if (selectionLength>=KBlockDeleteWarningSize)
//		return iEikonEnv->QueryWinL(R_EIK_TBUF_BLOCK_DELETE_CONFIRM_1,0);
	if (selectionLength)
		{
		TInt lower=selection.LowerPos();
		TInt lengthRemaining=selectionLength;
		while (lengthRemaining>0)
			{
			TPtrC ptr=iText->Read(lower,lengthRemaining);
			TInt amountRead=ptr.Length();
			lower+=amountRead;
			lengthRemaining-=amountRead;
			for (TInt ii=0;ii<amountRead;ii++)
				{
				if (ptr[ii]==CEditableText::EPictureCharacter)
					return iEikonEnv->QueryWinL(R_EIK_CONFIRM_DELETE_OBJECT_TITLE);
				}
			}
		}
	return ETrue;
	}

struct STempCleanup { CEikEdwin* iEdwin; };

LOCAL_C void DeleteTemp(TAny* aPtr)
	{ STATIC_CAST(STempCleanup*,aPtr)->iEdwin->ClearUndo(); }

EXPORT_C void CEikEdwin::DeleteL(TBool& aChanged,const TCursorSelection& aSelection,TBool aIsBackSpace,TBool aAllowUndo)
	{
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	CancelFepTransaction();
	if (aAllowUndo && !SetUndoBufferL(aSelection))
		CBaActiveScheduler::LeaveNoAlert();
	STempCleanup tempCleanup;
	tempCleanup.iEdwin=this;
	CleanupStack::PushL(TCleanupItem(DeleteTemp,&tempCleanup));
	const TInt length=Max(1,aSelection.Length());
	if ((iEdwinInternalFlags&ERichText) && aIsBackSpace)
		aChanged=STATIC_CAST(CRichText*,iText)->DelSetInsertCharFormatL(aSelection.LowerPos(),length);
	else
		{
		if (iEdwinInternalFlags&ERichText)
			STATIC_CAST(CRichText*,iText)->CancelInsertCharFormat();
		aChanged=iText->DeleteL(aSelection.LowerPos(),length);
		}
	CleanupStack::Pop();
	}

EXPORT_C TInt CEikEdwin::TextLength() const
	{
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	return(iText->DocumentLength());
	}

void CEikEdwin::SetCursorVisibilityL(TBool aEmphasis)
	{
	const TCursor::TVisibility textCursor=(aEmphasis? TCursor::EFCursorFlashing : TCursor::EFCursorInvisible);
	const TCursor::TVisibility lineCursor=((iEdwinUserFlags&ELineCursor && aEmphasis)? 
										TCursor::EFCursorVisible : TCursor::EFCursorInvisible);
	iTextView->SetCursorVisibilityL(lineCursor,textCursor);
	}

EXPORT_C CPlainText* CEikEdwin::Text() const
	{
	return iText;
	}

EXPORT_C CTextView* CEikEdwin::TextView() const
	{
	return iTextView;
	}

EXPORT_C CTextLayout* CEikEdwin::TextLayout() const
	{
	return iLayout;
	}
	
EXPORT_C void CEikEdwin::SetZoomFactorL(TZoomFactor* aZoomFactor)
	{
	iZoomFactor=aZoomFactor;
	SetAmountToFormatL(ETrue); // isn't new doc at all !!!
	}

EXPORT_C void CEikEdwin::SetBorderViewMargins(TMargins8 aMargins)
	{
	iMargins=aMargins;
	}

EXPORT_C void CEikEdwin::SetBackgroundColorL(TRgb aBackground)
	{
	iBackground=aBackground;
	if (!iTextView)
		CreateTextViewL();
	iTextView->SetBackgroundColor(aBackground);
	}

EXPORT_C void CEikEdwin::SetWysiwygModeOn(TInt aLayoutWidth,MGraphicsDeviceMap* aDevice)
	{
	__ASSERT_DEBUG(iLayout,Panic(EEikPanicEdwinNoLayout));
	iLayoutWidth=aLayoutWidth;
	iEdwinInternalFlags|=EWysiwygOn;
	iLayout->SetFormatMode(CLayoutData::EFWysiwygMode,aLayoutWidth,aDevice);
	}

EXPORT_C void CEikEdwin::SetWysiwygModeOff()
	{
	__ASSERT_DEBUG(iLayout,Panic(EEikPanicEdwinNoLayout));
	iLayoutWidth=0;
	iEdwinInternalFlags&=~EWysiwygOn;
	iLayout->SetFormatMode(CLayoutData::EFScreenMode,LayoutWidth(),NULL);
	}

EXPORT_C void CEikEdwin::UpdateLayoutWidth(TInt aLayoutWidth)
	{
	__ASSERT_DEBUG(iLayout,Panic(EEikPanicEdwinNoLayout));
	iLayoutWidth=aLayoutWidth;
	iLayout->SetWrapWidth(aLayoutWidth);
	}

EXPORT_C TInt CEikEdwin::UpperFullFormattingLength() const
	{
	return KFullFormattingUpperThreshold;
	}

EXPORT_C TInt CEikEdwin::LowerPartialFormattingLength() const
	{
	return KPartialFormattingLowerThreshold;
	}

EXPORT_C void CEikEdwin::SetReadOnly(TBool aReadOnly)
	{
	if (aReadOnly)
		iEdwinUserFlags|=EReadOnly;
	else
		iEdwinUserFlags&=~EReadOnly;
	}

EXPORT_C TBool CEikEdwin::IsReadOnly() const
	{
	return iEdwinUserFlags&EReadOnly;
	}

EXPORT_C void CEikEdwin::CheckNotReadOnlyL()
	{
	if (IsReadOnly())
		{
		NotifyInvalidOperationOnReadOnlyL();
		CBaActiveScheduler::LeaveNoAlert();
		}
	}

EXPORT_C void CEikEdwin::NotifyInvalidOperationOnReadOnlyL()
	{
	iEikonEnv->InfoMsg(R_EIK_TBUF_READONLYFILE);
	}

EXPORT_C void CEikEdwin::SetRightWrapGutter(TInt aGap)
	{
	iRightWrapGutter=aGap;
	}

EXPORT_C void CEikEdwin::GetText(TDes& aDes) const
	{
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	iText->Extract(aDes); // caller's responsibility to provide long enough buffer
	}

EXPORT_C HBufC* CEikEdwin::GetTextInHBufL() const
	{
	HBufC* ret=NULL;
	TInt textLength=TextLength();
	if (textLength)
		{
		ret=HBufC::NewL(textLength);
		TPtr ptr=ret->Des();
		GetText(ptr);
		}
	return(ret);
	}

EXPORT_C void CEikEdwin::ClearSelectionL()
	{ // !! must ensure with CTextView that this never leaves
	CancelFepTransaction();
	if (iTextView)
		iTextView->CancelSelectionL();
	}

EXPORT_C void CEikEdwin::SetTextL(const TDesC* aDes)
	{
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	CancelFepTransaction();
	ClearSelectionL();

	TInt err=0;
	if (aDes && aDes->Length())
		{
		TInt oldLength=iText->DocumentLength();
		iText->InsertL(oldLength,*aDes);
		TRAP(err,iText->DeleteL(0,oldLength));
		}
	else
		{
		iText->Reset(); // Duplicates previous behaviour where null pointer argument reset text object
		}

	CheckRemovePictures(0,iText->DocumentLength());
	FormatTextL();
	TInt left, right=0;
	if (iLayout && iLayout->CalculateHorizontalExtremesL(left,right,ETrue))
		{
		TInt width=iTextView->ViewRect().Width();
		TInt labels=0, cursor=0;
		iTextView->MarginWidths(labels,cursor);
		width-=(labels+cursor+labels+cursor+KEikDefaultCursorWidth+iRightWrapGutter);
		if (left+right<width)
			iTextView->SetLeftTextMargin(0);
		}
	if (IsReadyToDraw())
		{
		ApplyAutoSelectionL();
		UpdateScrollBarsL();
		}
	SetAmountToFormatL(ETrue);
	User::LeaveIfError(err);
	}

EXPORT_C TCursorSelection CEikEdwin::Selection()	const
	{
	if (iTextView)
		return(iTextView->Selection());
	return TCursorSelection(0,0);
	}

EXPORT_C TInt CEikEdwin::CountWords()
	{
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	return(iText->WordCount());
	}

EXPORT_C void CEikEdwin::SetAllowUndo(TBool aAllow)
	{
	if (aAllow)
		iEdwinUserFlags|=EAllowUndo;
	else
		iEdwinUserFlags&=~EAllowUndo;
	}

EXPORT_C TBool CEikEdwin::SupportsUndo() const
	{
	return iEdwinUserFlags&EAllowUndo;
	}

EXPORT_C TBool CEikEdwin::CanUndo() const
	{
	return (iUndoStore!=NULL);
	}
	
EXPORT_C void CEikEdwin::UndoL()
	{
	CancelFepTransaction();
	if (!SupportsUndo())
		return;
	if (!iUndoStore)
		iEikonEnv->InfoMsg(R_EIK_TBUF_NOTHING_TO_UNDO);
	else
		{
		TBool changed=EFalse;
		TCursorSelection newText=iUndoStore->NewText();
		const TInt oldLength=iText->DocumentLength();
		if (newText.Length())
			DeleteL(changed,newText,EFalse,EFalse);
		const TInt lower=newText.LowerPos();
		const TInt undoneLength=iText->PasteFromStoreL(iUndoStore->Store(),iUndoStore->Dictionary(),lower);
		const TInt cursorPos=iUndoStore->OldCursorPos();
		iTextView->SetPendingSelection(TCursorSelection(cursorPos,cursorPos));
		TRAPD(err,iTextView->HandleInsertDeleteL(TCursorSelection(lower,lower+undoneLength),newText.Length(),changed));
		ClearUndo();
		const TInt length=iText->DocumentLength();
		if ((length>KFullFormattingUpperThreshold && oldLength<=KFullFormattingUpperThreshold) ||
			(length<=KPartialFormattingLowerThreshold && oldLength>KPartialFormattingLowerThreshold))
			SetAmountToFormatL();
		ForceScrollBarUpdateL();
		User::LeaveIfError(err);
		ReportEventL(MCoeControlObserver::EEventStateChanged);
		}
	}

EXPORT_C void CEikEdwin::ClearUndo()
	{
	delete iUndoStore;
	iUndoStore=NULL;
	}

EXPORT_C TBool CEikEdwin::SetUndoBufferL(const TCursorSelection& aSelection)
	{
	if (iEdwinUserFlags&EAllowUndo)
		{
		TRAPD(err,DoSetUndoBufferL(aSelection));
		if (err!=KErrNone && iUndoStore!=NULL)
			ClearUndo();
		if (err==KErrNoMemory)
			return iEikonEnv->QueryWinL(R_EIK_TBUF_CANNOT_UNDO_CONFIRM_1,R_EIK_TBUF_CANNOT_UNDO_CONFIRM_2);
		User::LeaveIfError(err);
		}
	return ETrue;
	}

void CEikEdwin::DoSetUndoBufferL(const TCursorSelection& aSelection)
	{
	if (iUndoStore)
		ClearUndo();
	iUndoStore=CUndoBuffer::NewL();
	iText->CopyToStoreL(iUndoStore->Store(),iUndoStore->Dictionary(),aSelection.LowerPos(),aSelection.Length());
	const TInt lowerPos=aSelection.LowerPos();
	iUndoStore->SetNewText(TCursorSelection(lowerPos,lowerPos));
	iUndoStore->SetOldCursorPos(aSelection.iCursorPos);
	}

EXPORT_C void CEikEdwin::SetUndoableText(const TCursorSelection& aSelection)
	{
	iUndoStore->SetNewText(aSelection);
	}

const TInt KReadBufSize=512;

EXPORT_C void CEikEdwin::InsertFromTextFileL(const TFileName& aFileName,const CPlainText::TTextOrganisation aTextOrganisation)
	{
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	CancelFepTransaction();
	const TInt oldLength=iText->DocumentLength();
	const TInt cursorPos=CursorPos();
	iEikonEnv->BusyMsgL(R_EIK_TBUF_IMPORTING,500000); // after 0.5 seconds
	iText->ImportTextFileL(cursorPos,aFileName,aTextOrganisation);
	const TInt newLength=iText->DocumentLength();
	const TInt newCursorPos=cursorPos+newLength-oldLength;
	iTextView->SetPendingSelection(TCursorSelection(newCursorPos,newCursorPos));
	if (newLength>KFullFormattingUpperThreshold &&
		oldLength<=KFullFormattingUpperThreshold)
		SetAmountToFormatL();
	else
		iTextView->HandleInsertDeleteL(TCursorSelection(newCursorPos,cursorPos),0,ETrue);
	DrawContents();
	UpdateScrollBarsL();
	ReportEventL(MCoeControlObserver::EEventStateChanged);
	iEikonEnv->BusyMsgCancel();
	}

EXPORT_C void CEikEdwin::ClipboardL(TClipboardFunc aClipboardFunc)
	{
	__ASSERT_DEBUG(iTextView,Panic(EEikPanicEdwinNoView));
	CancelFepTransaction();
	TBool reportChange=EFalse;
	switch (aClipboardFunc)
		{	
	case ECut:
	case ECopy:
		{
		TCursorSelection selection=iTextView->Selection();
		const TInt selLength=SelectionLength();
		if (!selLength)
			iEikonEnv->InfoMsg(aClipboardFunc==ECut? R_EIK_TBUF_NOTHING_TO_CUT: R_EIK_TBUF_NOTHING_TO_COPY);
		else
			{
			PlaceDataOnClipboardL();
			if (aClipboardFunc==ECopy)
				iEikonEnv->InfoMsg(R_EIK_TBUF_COPIED);
			else
				{
				TBool formatHasChanged;
				DeleteHighlightL(formatHasChanged,EFalse,EFalse);
				const TInt lower=selection.LowerPos();
				const TCursorSelection pending(lower,lower);
				iTextView->SetPendingSelection(pending);
				selection.iAnchorPos=lower;
				selection.iCursorPos=lower;
				iTextView->HandleInsertDeleteL(selection,selLength,formatHasChanged);
				reportChange=ETrue;
				}
			}
		break;
		}
	case EPaste:
		RetrieveDataFromClipboardL();
		reportChange=ETrue;
		break;
	default:
#if defined(_DEBUG)
		Panic(EEikPanicEdwinBadClipboardFunc);
#endif
		break;
		}
	if (reportChange)
		{
		ReportEventL(MCoeControlObserver::EEventStateChanged);
		UpdateScrollBarsL();
		}
	}

EXPORT_C void CEikEdwin::PlaceDataOnClipboardL()
	{
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	CancelFepTransaction();
	CClipboard* cb=CClipboard::NewForWritingLC(iCoeEnv->FsSession());
	CopyToStoreL(cb->Store(),cb->StreamDictionary());
	cb->CommitL();
	CleanupStack::PopAndDestroy();
	}

void CEikEdwin::RetrieveDataFromClipboardL()
	{
	CancelFepTransaction();
	CClipboard* cb=NULL;
	TRAPD(err,cb=CClipboard::NewForReadingL(iCoeEnv->FsSession()));
	CleanupStack::PushL(cb);
	if (err==KErrPathNotFound || err==KErrNotFound)
		iEikonEnv->LeaveWithInfoMsg(R_EIK_TBUF_NOTHING_TO_PASTE);
	User::LeaveIfError(err);
	PasteFromStoreL(cb->Store(), cb->StreamDictionary());
	CleanupStack::PopAndDestroy();	// cb
	}

EXPORT_C void CEikEdwin::SendDataOverIrL()
	{
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	CancelFepTransaction();
	CEikEdwinBeamer* edwinBeamer=new(ELeave) CEikEdwinBeamer(this);
	CleanupStack::PushL(edwinBeamer);
	TInt err=edwinBeamer->SendLD();
	if (err==KErrNotFound)
		iEikonEnv->InfoMsg(R_EIK_TBUF_NOTHING_TO_SEND);
	CleanupStack::Pop();
	}

EXPORT_C void CEikEdwin::ReceiveDataOverIrL()
	{ // !! doesn't cope with multi-para into single-para case
	CancelFepTransaction();
	CEikEdwinBeamer* edwinBeamer=new(ELeave) CEikEdwinBeamer(this);
	CleanupStack::PushL(edwinBeamer);
	edwinBeamer->ReceiveLD();
	CleanupStack::Pop();
	}

EXPORT_C void CEikEdwin::CopyToStoreL(CStreamStore& aStore,CStreamDictionary& aDict)
	{
	CancelFepTransaction();
	TCursorSelection selection=Selection();
	iText->CopyToStoreL(aStore, aDict, selection.LowerPos(),selection.Length());
	}

EXPORT_C void CEikEdwin::PasteFromStoreL(CStreamStore& aStore,CStreamDictionary& aDict)
	{
	CancelFepTransaction();
	TStreamId streamId=aDict.At(KClipboardUidTypeRichText);
	if (streamId==KNullStreamId)
		streamId=aDict.At(KClipboardUidTypePlainText);
	TInt nothingResId=(iEdwinInternalFlags&EPasteFromIrStore)? R_EIK_TBUF_UNSUITABLE_IR_TYPE : R_EIK_TBUF_NOTHING_TO_PASTE;
	SetPasteFromIrStore(EFalse); // nothing to leave before this line
	if (streamId==KNullStreamId)
		iEikonEnv->LeaveWithInfoMsg(nothingResId);
	iEikonEnv->BusyMsgL(R_EIK_TBUF_PASTING,500000);
	TCursorSelection selection=iTextView->Selection();
	const TInt oldTextLength=TextLength();
	const TInt selLength=selection.Length();
	TBool formatHasChanged=EFalse;
	if (selLength)
		DeleteHighlightL(formatHasChanged);
	else
		ClearUndo();
	const TInt lengthBeforePaste=TextLength();
	TRAPD(err,DoPasteFromStoreL(aStore,aDict));
	TInt newLength=iText->DocumentLength();
	TInt pastedLength=newLength-lengthBeforePaste;
	if (err!=KErrNone && pastedLength==0)
		{
		ClearUndo();
		User::Leave(err);
		}
	const TInt higher=selection.HigherPos()+newLength-oldTextLength;
	iTextView->SetPendingSelection(TCursorSelection(higher,higher));
	selection.iAnchorPos=selection.LowerPos();
	selection.iCursorPos=selection.iAnchorPos+pastedLength;
	if (iUndoStore)
		iUndoStore->SetNewText(selection);
	if ((newLength>KFullFormattingUpperThreshold && oldTextLength<=KFullFormattingUpperThreshold) ||
		(newLength<=KPartialFormattingLowerThreshold && oldTextLength>KPartialFormattingLowerThreshold))
		{
		SetAmountToFormatL();
		DrawContents();
		}
	else
		iTextView->HandleInsertDeleteL(selection,selLength,formatHasChanged);
	iEikonEnv->BusyMsgCancel();
	User::LeaveIfError(err);
	}

void CEikEdwin::DoPasteFromStoreL(const CStreamStore& aStore,const CStreamDictionary& aDict)
	{
	const TInt lengthBeforePaste=iText->DocumentLength();
	const TInt cursorPos=CursorPos();
	iText->PasteFromStoreL(aStore,aDict,cursorPos);
	TInt newLength=iText->DocumentLength();
	TInt pastedLength=newLength-lengthBeforePaste;
	CheckRemovePictures(cursorPos,pastedLength);
	const TInt tmp=iText->DocumentLength();
	if (tmp<newLength)
		iEikonEnv->InfoMsg(R_EIK_TBUF_CANNOT_PASTE_OBJECTS);
	newLength=tmp;
	pastedLength=newLength-lengthBeforePaste;
	if (iNumberOfLines==1 || iEdwinUserFlags&ENoLineOrParaBreaks)
		DeleteExtraParasL(cursorPos,pastedLength);
	newLength=iText->DocumentLength();
	pastedLength=newLength-lengthBeforePaste;
	const TInt extraChars=newLength-iTextLimit;
	if (iTextLimit && extraChars>0)
		{
		const TInt pos=cursorPos+pastedLength-extraChars;
		DeleteExtraParasL(pos,extraChars);
		const TInt length=iText->DocumentLength()-iTextLimit;
		if (iEdwinInternalFlags&ERichText && length>0)
			STATIC_CAST(CRichText*,iText)->DeleteFromParagraph(pos,length);
		else
			iText->DeleteL(pos,iText->DocumentLength()-iTextLimit); // won't leave
		CEikonEnv::Beep();
		iEikonEnv->InfoMsg(R_EIK_TBUF_MAX_CHARACTERS_REACHED);
		}
	pastedLength=iText->DocumentLength()-lengthBeforePaste;
	HandleTextPastedL(cursorPos,pastedLength);
	}

void CEikEdwin::DeleteExtraParasL(TInt aStartPos,TInt aLength)
	{
	TInt pos=aStartPos;
	TInt charsRead=0;
	const TBool pastedAtEnd=(aStartPos+aLength==iText->DocumentLength());
	while (charsRead<aLength)
		{
		TPtrC ptr=iText->Read(pos,Min(aLength-charsRead,10));		
		const TInt paraDelimiter=ptr.Locate(TChar(CEditableText::EParagraphDelimiter));
		const TInt lineDelimiter=ptr.Locate(TChar(CEditableText::ELineBreak));
		if (!(iEdwinInternalFlags&ERichText))
			{
			const TInt delimiter=(paraDelimiter==-1? lineDelimiter : (lineDelimiter==-1? 
												paraDelimiter : Min(lineDelimiter,paraDelimiter)));
			if (delimiter!=-1)
				{
				iText->DeleteL(pos+delimiter,aLength-(charsRead+delimiter)); // won't leave
				break;
				}
			}
		else
			{
			if (paraDelimiter!=-1)
				{
				if (pastedAtEnd)
					STATIC_CAST(CRichText*,iText)->DeleteParagraph(pos+paraDelimiter+1,aLength-(charsRead+paraDelimiter));
				else
					{
					TChar delimiter(CEditableText::EParagraphDelimiter);
					TInt startPos=pos+paraDelimiter+1;
					TInt length=aLength-(charsRead+paraDelimiter)-1;
					TInt lastParaDelimiter=KErrNotFound;
					TInt charPos=LocateChar(delimiter,startPos,length);
					while (charPos!=KErrNotFound)
						{
						lastParaDelimiter=startPos+charPos;
						startPos+=(charPos+1);
						length-=(charPos+1);
						charPos=LocateChar(delimiter,startPos,length);
						}
					TInt len=aLength-(startPos-aStartPos);
					if (len>0)
						STATIC_CAST(CRichText*,iText)->DeleteFromParagraph(startPos,len);
					if (lastParaDelimiter!=KErrNotFound)
						{
						const TInt start=pos+paraDelimiter+1;
						length=lastParaDelimiter-start+1;
						STATIC_CAST(CRichText*,iText)->DeleteParagraph(start,length);
						}
					}
				}
			if (lineDelimiter!=-1 && (paraDelimiter==-1 || lineDelimiter<paraDelimiter))
				{
				TInt length=aLength-(charsRead+lineDelimiter);
				if (paraDelimiter!=-1)
					length=paraDelimiter-lineDelimiter;
				STATIC_CAST(CRichText*,iText)->DeleteFromParagraph(pos+lineDelimiter,length);
				break;
				}
			if (paraDelimiter!=-1)
				{
				if (!pastedAtEnd)
					iText->DeleteL(pos+paraDelimiter,1);
				break;
				}
			}
		const TInt ptrLen=ptr.Length();
		pos+=ptrLen;
		charsRead+=ptrLen;
		}
	}

TInt CEikEdwin::LocateChar(TChar aChar,TInt aStartPos,TInt aLength)
	{
	TInt pos=aStartPos;
	TInt charsRead=0;
	while (charsRead<aLength)
		{
		TPtrC ptr=iText->Read(pos,Min(aLength-charsRead,10));		
		const TInt charPos=ptr.Locate(aChar);
		if (charPos!=KErrNotFound)
			return (charsRead+charPos);
		const TInt ptrLen=ptr.Length();
		pos+=ptrLen;
		charsRead+=ptrLen;
		}
	return KErrNotFound;
	}

void CEikEdwin::SetPasteFromIrStore(TBool aPasteFromIrStore)
	{
	if (aPasteFromIrStore)
		iEdwinInternalFlags|=EPasteFromIrStore;
	else
		iEdwinInternalFlags&=(~EPasteFromIrStore);
	}

EXPORT_C void CEikEdwin::HandleTextPastedL(TInt /*aStartPos*/,TInt& /*aLength*/)
	{
	}

EXPORT_C void CEikEdwin::CancelFepTransaction()
	{
	CCoeFep* fep=iCoeEnv->Fep();
	if (fep!=NULL)
		{
		fep->CancelTransaction();
		}
	}

EXPORT_C void CEikEdwin::HandleTextChangedL()
	{
	ClearSelectionL(); // ?? maybe too late here!!
	FormatTextL();
	if (IsReadyToDraw())
		{
		ApplyAutoSelectionL();
		DrawContents();
		UpdateScrollBarsL();
		}
	}

EXPORT_C void CEikEdwin::RunCharMapDialogL()
	{
	CancelFepTransaction();
	CEikCharMapDialog* dialog=new(ELeave) CEikCharMapDialog(this);
	dialog->ExecuteLD(R_EIK_DIALOG_CHARMAP);
	}

EXPORT_C void CEikEdwin::ForceScrollBarUpdateL()
	{
	SetScrollBarsL();
	}

EXPORT_C void CEikEdwin::UpdateScrollBarsL()
	{
	if (!iSetScrollBar && OwnsScrollBars())
		iSetScrollBar=CIdle::NewL(CTextView::EFBackgroundFormattingPriority-1);
	if (iSetScrollBar && !iSetScrollBar->IsActive())
		iSetScrollBar->Start(TCallBack(CEikEdwin::IdleL,this));
	}

void CEikEdwin::UpdateHorizScrollBarThumb()
	{
	__ASSERT_DEBUG(iTextView,Panic(EEikPanicEdwinNoView));
	if (iSBFrame)
		iSBFrame->MoveHorizThumbTo(iTextView->LeftTextMargin());
	}	

void CEikEdwin::UpdateVertScrollBarThumbL()
	{
	__ASSERT_DEBUG(iTextView,Panic(EEikPanicEdwinNoView));
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoLayout));
	if (iSBFrame)
		{
		if (!(iLayout->IsFormattingBand()))
			iSBFrame->MoveVertThumbTo(iLayout->PixelsAboveBand());
		else
			{
			TEikScrollBarModel vertModel;
			SetVertScrollBarModelByCharactersL(vertModel);
			iSBFrame->MoveVertThumbTo(vertModel.iThumbPosition);
			}
		}
	}

EXPORT_C CEikScrollBarFrame* CEikEdwin::CreateScrollBarFrameL()
	{
	if (!iSBFrame)
		iSBFrame=new(ELeave) CEikScrollBarFrame(this, this);
	return iSBFrame;
	}

void CEikEdwin::CreateScrollBarFrameLayout(TEikScrollBarFrameLayout& aLayout) const
	{
	aLayout.iInclusiveMargin=iBorder.Margins();
	const TRect inner=iBorder.InnerRect(Rect());
	const TRect& viewRect=iTextView->ViewRect();
	aLayout.iClientMargin.iLeft=viewRect.iTl.iX-inner.iTl.iX;
	aLayout.iClientMargin.iRight=inner.iBr.iX-viewRect.iBr.iX;
	aLayout.iClientMargin.iTop=viewRect.iTl.iY-inner.iTl.iY;
	aLayout.iClientMargin.iBottom=inner.iBr.iY-viewRect.iBr.iY;
	//	if any client margins are being used set them here
	aLayout.iTilingMode=(iEdwinUserFlags&EInclusiveSizeFixed)? TEikScrollBarFrameLayout::EInclusiveRectConstant : TEikScrollBarFrameLayout::EClientRectConstant;
	}

EXPORT_C TInt CEikEdwin::IdleL(TAny *anObj)
	{
	((CEikEdwin *)anObj)->SetScrollBarsL();
	return EFalse;
	}

void CEikEdwin::SetScrollBarsL()
	{
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoLayout));
	if (!iSBFrame)
		return;
	if (iSetScrollBar->IsActive())
		iSetScrollBar->Cancel();
	TEikScrollBarModel hSbarModel;
	TEikScrollBarModel vSbarModel;
	TRect rect=iTextView->ViewRect();
	if (!(iEdwinUserFlags&EInclusiveSizeFixed))
		rect=iBorder.InnerRect(Rect());
	else
		rect=iMargins.OuterRect(rect);
		// Ignore scrollbars presence to set the model, Scrollbar Frame will change it as required
	if (iSBFrame->VScrollBarVisibility()!=CEikScrollBarFrame::EOff)
		{
		if (iLayout->IsFormattingBand())
			{
			SetVertScrollBarModelByCharactersL(vSbarModel);
			iSBFrame->SetAdjustsVerticalModel(ETrue); // is this correct ??
//			iSBFrame->SetAdjustsVerticalModel(EFalse);
			}
		else
			{
			vSbarModel.iScrollSpan=iLayout->FormattedHeightInPixels(); //+iTextView->ViewRect().Height();
			vSbarModel.iThumbSpan=iTextView->ViewRect().Height();
			vSbarModel.iThumbPosition=iLayout->PixelsAboveBand();
			if (vSbarModel.iScrollSpan<vSbarModel.iThumbSpan && vSbarModel.iThumbPosition)
				{
				vSbarModel.iScrollSpan=vSbarModel.iThumbSpan+vSbarModel.iThumbPosition;
				iEdwinInternalFlags|=EUnderOneScreenFormattedText;
				}
			else
				iEdwinInternalFlags&=~EUnderOneScreenFormattedText;
			iSBFrame->SetAdjustsVerticalModel(ETrue);
			}
		}
	if (iSBFrame->HScrollBarVisibility()!=CEikScrollBarFrame::EOff)
		{
		if (iEdwinInternalFlags&EWysiwygOn)
			{
			hSbarModel.iScrollSpan=iZoomFactor->HorizontalTwipsToPixels(LayoutWidth());
			TInt labels=0, cursor=0;
			iTextView->MarginWidths(labels,cursor);
			hSbarModel.iScrollSpan+=labels+cursor+LineCursorWidth();
			}
		else
			hSbarModel.iScrollSpan=LayoutWidth();	
		hSbarModel.iThumbSpan=rect.Width();
		hSbarModel.iThumbPosition=iTextView->LeftTextMargin();
		if (!(iEdwinUserFlags&ENoWrap))
			{// wrapping text
			if (hSbarModel.iScrollSpan<=hSbarModel.iThumbSpan)
				// wrapping to within viewRect
				iSBFrame->SetAdjustsHorizontalModel(EFalse);
			}
		else 
			iSBFrame->SetAdjustsHorizontalModel(ETrue);
		}
	TRect inclusiveRect=Rect();
	TRect clientRect=rect; // claim client is as large as possible
	TEikScrollBarFrameLayout layout;
	CreateScrollBarFrameLayout(layout);
	if (vSbarModel.iThumbSpan)
		{
		const TInt granularityHeight=iTextView->ViewRect().Height()/vSbarModel.iThumbSpan;
		if (granularityHeight)
			layout.iClientAreaGranularity.iHeight=granularityHeight;
		}
	TBool sizeChanged=iSBFrame->TileL(&hSbarModel, &vSbarModel, clientRect, inclusiveRect, layout);
	if (!OwnsScrollBars())
		{
		delete iSBFrame;
		iSBFrame=NULL;
		}
	if (!sizeChanged)
		return;
	// else size of client/inclusive rect has changed
	if (layout.iTilingMode==TEikScrollBarFrameLayout::EClientRectConstant)
		{
		iSize=inclusiveRect.Size();
		DrawNow();
		}
	else
		{
		clientRect=iMargins.InnerRect(clientRect);
		iTextView->SetViewRect(clientRect);
		if (!(iEdwinUserFlags&ENoWrap))
			iLayout->SetWrapWidth(LayoutWidth());
		TViewYPosQualifier yPosQualifier;
		yPosQualifier.SetMakeLineFullyVisible();
		iTextView->HandleGlobalChangeNoRedrawL(yPosQualifier);
		// !! This can (and does!) change the scrollspan !!!!
		CWindowGc& gc=SystemGc();
		ActivateGc();
		gc.SetBrushColor(iBackground);
		EikDrawUtils::ClearBetweenRects(gc,iBorder.InnerRect(Rect()),clientRect);
		DeactivateGc();
		if (iEdwinInternalFlags&ELockScrollBarState)
			{
			iTextView->HandleGlobalChangeNoRedrawL(yPosQualifier);
			CEikScrollBarFrame::TScrollBarVisibility vis=iSBFrame->VScrollBarVisibility();
			CEikScrollBarFrame::TScrollBarVisibility vVis=(vis==CEikScrollBarFrame::EAuto? CEikScrollBarFrame::EOn : vis);
			vis=iSBFrame->HScrollBarVisibility();
			CEikScrollBarFrame::TScrollBarVisibility hVis=(vis==CEikScrollBarFrame::EAuto? CEikScrollBarFrame::EOn : vis);
			iSBFrame->SetScrollBarVisibilityL(hVis,vVis);
			}
		else
			iEdwinInternalFlags|=ELockScrollBarState;
		SetAmountToFormatL(); // will call SetScrollBarsL() again if need be
		if (!iLayout->IsFormattingBand())
			SetScrollBarsL();
		iEdwinInternalFlags&=~ELockScrollBarState;
		if (iEdwinInternalFlags&EWysiwygOn && iSBFrame->HScrollBarVisibility()==CEikScrollBarFrame::EAuto)
			{
			CEikScrollBar* hSBar=iSBFrame->GetScrollBarHandle(CEikScrollBar::EHorizontal);
			if (!hSBar || !hSBar->IsVisible())
				iTextView->SetLeftTextMargin(0);
			}
		iTextView->DrawL(clientRect);
		}
	}

void CEikEdwin::SetVertScrollBarModelByCharactersL(TEikScrollBarModel& aVertModel) const
	{
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	__ASSERT_DEBUG(iLayout,Panic(EEikPanicEdwinNoLayout));
	const TInt topLeftDocPos=iLayout->FirstDocPosFullyInBand();
	const TInt totalChars=iText->DocumentLength() + iAvgCharsPerLine*(iAvgLinesInViewRect/*+1*/);
	const TInt approxTotalLines=totalChars/iAvgCharsPerLine;// + iAvgLinesInViewRect-1;
	TInt approxTopVisibleLine=topLeftDocPos/iAvgCharsPerLine;
	if (approxTopVisibleLine==0 && topLeftDocPos>0)
		approxTopVisibleLine=1; // could this be more exact ??
	aVertModel.iScrollSpan=approxTotalLines;
	aVertModel.iThumbSpan=iAvgLinesInViewRect;
	aVertModel.iThumbPosition=approxTopVisibleLine;
	}

EXPORT_C void CEikEdwin::HandleScrollEventL(CEikScrollBar* aScrollBar,TEikScrollEvent aEventType)
	{
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	__ASSERT_DEBUG(iLayout,Panic(EEikPanicEdwinNoLayout));
	TInt thumbPosition=aScrollBar->ThumbPosition();
	switch (aEventType&KEikScrollEventBarMask)
		{ 
	case KEikScrollEventFromHBar:
		switch (aEventType)
			{
		case EEikScrollLeft:
		case EEikScrollRight:
			MoveDisplayL((aEventType==EEikScrollLeft)? TCursorPosition::EFLeft: TCursorPosition::EFRight);
			break;
		case EEikScrollPageLeft:
		case EEikScrollPageRight:
			{
			TInt singleScrollJump=iTextView->HorizontalScrollJump();
			if (aEventType==EEikScrollPageLeft)
				singleScrollJump=-singleScrollJump;
			TInt leftMargin=iTextView->LeftTextMargin();
			leftMargin+=5*singleScrollJump; // ?? scroll how far!!
			if (leftMargin < 0)
				leftMargin=0;
			else if (leftMargin>LayoutWidth()-iTextView->ViewRect().Width())
				leftMargin=LayoutWidth()-iTextView->ViewRect().Width();
			iTextView->SetLeftTextMargin(leftMargin); // !! this doesn't actually scroll anything so...
			DrawContents();
			UpdateScrollBarsL();
			UpdateHorizScrollBarThumb();
			}
			break;
		case EEikScrollThumbDragHoriz:
//		case EEikScrollThumbReleaseHoriz:
			iTextView->SetLeftTextMargin(thumbPosition);
			DrawContents();
//			if (aEventType==EEikScrollThumbReleaseHoriz)
//				UpdateHorizScrollBarThumb();
			break;
		default:
			break;
			}
		break;
	case KEikScrollEventFromVBar:
		switch (aEventType)
			{
		default:
			break;
		case EEikScrollUp:
		case EEikScrollDown:
			MoveDisplayL((aEventType==EEikScrollUp) ? TCursorPosition::EFLineUp : TCursorPosition::EFLineDown);
			break;
		case EEikScrollPageUp:
		case EEikScrollPageDown:
			MoveDisplayL((aEventType==EEikScrollPageUp) ? TCursorPosition::EFPageUp :	TCursorPosition::EFPageDown);
			break;
		case EEikScrollTop:
		case EEikScrollBottom:
			{
			TInt docPos=(aEventType==EEikScrollTop)? 0 : iText->DocumentLength();
			TInt yPos=iPosition.iY+iBorder.Margins().iLeft;
			TViewYPosQualifier yPosQ;
			yPosQ.SetMakeLineFullyVisible();
			yPosQ.SetFillScreen();
			iTextView->SetViewL(docPos, yPos, yPosQ);
			UpdateVertScrollBarThumbL();
			UpdateHorizScrollBarThumb();// can also change horizontally
			}
			break;
		case EEikScrollThumbDragVert:
			if (!(iLayout->IsFormattingBand()))
				{
				TInt oldPixelPos=iLayout->PixelsAboveBand();
				TInt vertPixelScroll=oldPixelPos-thumbPosition;
				iTextView->ScrollDisplayPixelsL(vertPixelScroll);
				}
			else
				{
				TInt yPos=iTextView->ViewRect().iTl.iY;
				TViewYPosQualifier yPosQ;
				yPosQ.SetMakeLineFullyVisible();
				const TInt64 totalChars=iText->DocumentLength() + iAvgCharsPerLine*(iAvgLinesInViewRect/*+1*/);
				const TInt64 approxTotalLines=totalChars/iAvgCharsPerLine;//  + iAvgLinesInViewRect-1;
//				const TInt topLeftDocPos=Min((totalChars*thumbPosition)/approxTotalLines,iText->DocumentLength());
				const TInt64 topLeftDocPos=(totalChars*thumbPosition)/approxTotalLines;
				if ((TInt)topLeftDocPos.Low()<=iText->DocumentLength()) // safe cast since doc length can't be any bigger
					iTextView->SetViewL((TInt)topLeftDocPos.Low(), yPos, yPosQ);
				}
			break;
		case EEikScrollThumbReleaseVert:
			UpdateVertScrollBarThumbL();
			break;
			}
		if (aEventType!=EEikScrollThumbDragVert && iEdwinInternalFlags&EUnderOneScreenFormattedText)
			UpdateScrollBarsL();
		break;
		}
	}

EXPORT_C TInt CEikEdwin::LayoutWidth() const
	{
	if (iLayoutWidth)
		return iLayoutWidth;
	if (iEdwinUserFlags&ENoWrap)
		return 10000; // UNREASONABLY_LARGE_HORIZONTAL_SCROLL_VALUE; removed from frmtview.h !!!
	TMargins margins=iBorder.Margins();
	TInt labels=0, cursor=0;
	if (iTextView)
		iTextView->MarginWidths(labels,cursor);
	TInt width=(iTextView? iTextView->ViewRect().Width() : iSize.iWidth);
	return width-(margins.iLeft+margins.iRight+labels+cursor+KEikDefaultCursorWidth+iRightWrapGutter);
	}

void CEikEdwin::DoReplaceAllL(SEdwinFindModel* aModel,TBool& aTextFound,TBool& aReplaced)
	{
	aReplaced=EFalse;
	aTextFound=EFalse;
	TInt flags=aModel->iFlags;
	//text is found when called from replace dialog.
	if (SelectionLength())
		ClearSelectionL();
	TInt startPos=CursorPos();
	if (flags&EFindDirectionUp)
		startPos+=aModel->iText.Length();
	else
		startPos-=aModel->iText.Length();
	TInt count=-1;
FindAgain:
	count=FindTextL(&aModel->iText,startPos,aModel->iFlags);		
	if (count!=KErrNotFound)
		{
		aReplaced=ETrue;
		aTextFound=ETrue;
		iText->InsertL(count,aModel->iReplaceText);
		iText->DeleteL(count+aModel->iReplaceText.Length(),aModel->iText.Length());
		// should handle if it leaves.
		if (!(flags&EFindDirectionUp))
			startPos=count+aModel->iReplaceText.Length();
		else
			startPos=count;
		goto FindAgain;
		}
	}

EXPORT_C void CEikEdwin::ReplaceAllL(SEdwinFindModel* aModel)
	{
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	__ASSERT_DEBUG(iTextView,Panic(EEikPanicEdwinNoView));
	CancelFepTransaction();
	TBool textFound=EFalse;
	TBool replaced=EFalse;
	const TBool undoEnabled=SupportsUndo();
	if (undoEnabled)
		SetAllowUndo(EFalse);
	TRAPD(ret,DoReplaceAllL(aModel,textFound,replaced));
	if (undoEnabled)
		SetAllowUndo(ETrue);
	if (ret==KErrNone)
		{
		if (!textFound)
			DisplayFindTextNotFound(aModel->iText);
		if (replaced)
			{
			ReportEventL(MCoeControlObserver::EEventStateChanged);
			NotifyNewFormatL();	//do global format
			}
		if (SelectionLength())
			ClearSelectionL();  
		}
	else
		{
		iEikonEnv->BusyMsgCancel();
		ReportEventL(MCoeControlObserver::EEventStateChanged); // if it failed half way through!!
		NotifyNewFormatL();//this will fail too if oom.
		User::Leave(ret);
		}
	}	 
	  
EXPORT_C void CEikEdwin::ReplaceL(SEdwinFindModel* aModel)
	{
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	__ASSERT_DEBUG(iTextView,Panic(EEikPanicEdwinNoView));
	
	CancelFepTransaction();
	const TInt oldLength=iText->DocumentLength();
	TBool formatHasChanged;
	TCursorSelection selection=iTextView->Selection();
	const TBool undoEnabled=SupportsUndo();
	if (undoEnabled)
		SetAllowUndo(EFalse);
	TInt startPos=DeleteHighlightL(formatHasChanged,EFalse,EFalse);
	if (undoEnabled)
		SetAllowUndo(ETrue);
	TRAPD(err,iText->InsertL(startPos,aModel->iReplaceText));
	if (aModel->iFlags&EFindDirectionUp)
		{
		selection.iCursorPos=selection.LowerPos();
		selection.iAnchorPos=selection.iCursorPos+aModel->iReplaceText.Length();
		}
	else
		{
		selection.iAnchorPos=selection.LowerPos();
		selection.iCursorPos=selection.iAnchorPos+aModel->iReplaceText.Length();
		}
	const TCursorSelection pending(selection.iCursorPos,selection.iCursorPos);
	iTextView->SetPendingSelection(pending);
	iTextView->HandleInsertDeleteL(selection,aModel->iText.Length(),formatHasChanged);
	const TInt length=iText->DocumentLength();
	if ((length>KFullFormattingUpperThreshold && oldLength<=KFullFormattingUpperThreshold) ||
		(length<=KPartialFormattingLowerThreshold && oldLength>KPartialFormattingLowerThreshold))
		SetAmountToFormatL();
	ReportEventL(MCoeControlObserver::EEventStateChanged);
	User::LeaveIfError(err);
	}	 

EXPORT_C TBool CEikEdwin::FindL(const TDesC* aFindText,TInt aFindFlags)
	{ 
	TBuf<EEikEdwinFindStringMaxLen>* findText=new(ELeave) TBuf<EEikEdwinFindStringMaxLen>;
	CleanupStack::PushL(findText);
	if (!aFindText)
		{
		GetFindText(findText);
		if (!findText->Length())
			{
			CleanupStack::PopAndDestroy();
			if (SelectionLength())
				ClearSelectionL();
			return EFalse;
			}
		}
	else
		*findText=*aFindText;
	TBool noBusyMessage=(aFindFlags&ENoBusyMessage);
	if(!noBusyMessage)
		iEikonEnv->BusyMsgL(R_EIK_TBUF_SEARCHING,500000); // 0.5s delay
	TInt startPos=CursorPos();
	TBool isUp=(aFindFlags&EFindDirectionUp);
	TBool findAgain=(aFindFlags&EFindAgain);
	if ((isUp==EFalse) != (findAgain==EFalse))
		startPos=Selection().HigherPos();
	else
		startPos=Selection().LowerPos();
	TInt pos=FindTextL(findText,startPos,aFindFlags);
	if (pos==KErrNotFound) //ie not found
		{
		CleanupStack::PopAndDestroy(); // findText
		if (SelectionLength())
			ClearSelectionL();
		if(!noBusyMessage)
			iEikonEnv->BusyMsgCancel();
		return EFalse;
		}
	if (aFindFlags&EFindDirectionUp)
		SetSelectionL(pos,pos+findText->Length());
	else
		SetSelectionL(pos+findText->Length(),pos);
	CleanupStack::PopAndDestroy(); // findText
	if(!noBusyMessage)
		iEikonEnv->BusyMsgCancel();
	return ETrue;
	}

EXPORT_C TInt CEikEdwin::FindTextL(const TDesC* aFindText,TInt aPos,TInt aFindFlags)
	{ // !! this implementation can be improved by an order of magnitude
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	const TInt docLength=TextLength();
	if (docLength<aPos)
		return KErrNotFound;
	TInt findParams=aFindFlags;
	TBuf<EEikEdwinFindStringMaxLen>* findText=new(ELeave) TBuf<EEikEdwinFindStringMaxLen>;
	CleanupStack::PushL(findText);
	if (aFindText)
		*findText=*aFindText;
	else
		{
		GetFindText(findText);
		findParams&=(~EFindCaseSensitive);
		}
	const TInt findLen=findText->Length();
	if (!findLen || docLength<findLen)
		{
		CleanupStack::PopAndDestroy(); // findText
		return KErrNotFound;
		}
	if (!(findParams&EFindCaseSensitive))
		findText->LowerCase();
	TBuf<EEikEdwinFindStringMaxLen>* docText=new(ELeave) TBuf<EEikEdwinFindStringMaxLen>;
	CleanupStack::PushL(docText);
	TInt count=aPos;
	if (findParams&EFindDirectionUp)
		{
		if (aPos<findText->Length())
			{
			CleanupStack::PopAndDestroy(2); // findText, docText
			return KErrNotFound; 
			}
		count=aPos-findLen;
		}
	FOREVER
		{
		if (count<0 || (count>(docLength-findLen) && !(findParams&EFindDirectionUp)))
			{
			CleanupStack::PopAndDestroy(2); // findText, docText
			return KErrNotFound; 
			}
		*docText=iText->Read(count,findLen);
		TInt docLen=docText->Length();
		while (docLen<findLen) // tried to read across a segment boundary
			{
			docText->Append(iText->Read(count+docLen,findLen-docLen));
			docLen=docText->Length();
			}
		if (!(findParams&EFindCaseSensitive))
			docText->LowerCase();
		if (*findText==*docText)
			{
			if (findParams&EFindWholeWord)
				{
				if ((count==0 || !TCharF(*((iText->Read(count-1,1)).Ptr())).IsAlphaDigit()) &&
				(count+findLen==docLength || !TCharF(*((iText->Read(count+findLen,1)).Ptr())).IsAlphaDigit()))
					{
					break;
					}
				}
			else
				{
				break;
				}
			}
		if (findParams&EFindDirectionUp)
			count--;
		else
			count++;
		}
	CleanupStack::PopAndDestroy(2); // findText, docText
	return count;
	}

EXPORT_C void CEikEdwin::DisplayFindTextNotFound(TDes& aFindText)
	{
	TBuf<80> tmp;
	iCoeEnv->ReadResource(tmp,R_EIK_TBUF_CANNOT_FIND_TEXT);
	TInt rem=tmp.MaxLength()-tmp.Length()-1;
	if (aFindText.Length()>rem)
		TextUtils::TruncateToNumChars(aFindText,rem);
	TBuf<80> buf;
	buf.Format(tmp,&aFindText);
	TextUtils::ClipToFit(buf,*iCoeEnv->NormalFont(),iCoeEnv->ScreenDevice()->SizeInPixels().iWidth-20); // -20 allows for borders on infomsg
	iEikonEnv->InfoMsg(buf);
	}

EXPORT_C void CEikEdwin::GetFindText(TDes* aSearchText)
	{
	CancelFepTransaction();
	TInt startPos=0;
	TInt length=0;
	if (SelectionLength())
		{
		TCursorSelection selection=Selection();
		startPos=selection.LowerPos();
		length=SelectionLength();
		if (length>EEikEdwinFindStringMaxLen)
			goto GetWord;
		}
	else
		{
GetWord:
		GetWordInfo(CursorPos(),startPos,length);
		}
	if (!length)
		return;
	length=Min(length,EEikEdwinFindStringMaxLen);
	*aSearchText=iText->Read(startPos,length);
	TInt searchLen=aSearchText->Length();
	while (searchLen<length) // tried to read across a segment boundary
		{
		aSearchText->Append(iText->Read(startPos+searchLen,length-searchLen));
		searchLen=aSearchText->Length();
		}
	}

EXPORT_C void CEikEdwin::SetAllowPictures(TBool aAllow)
	{
	if (aAllow)
		iEdwinUserFlags|=EAllowPictures;
	else
		iEdwinUserFlags&=~EAllowPictures;
	}

EXPORT_C void CEikEdwin::CheckRemovePictures(TInt aStartPos,TInt aLength)
	{
	if (iEdwinUserFlags&EAllowPictures)
		return;
	TInt charsRead=0;
	while (charsRead<aLength)
		{
		TPtrC ptr=iText->Read(aStartPos,Min(aLength-charsRead,10));
		const TInt ptrLength=ptr.Length();
		TInt located=0;
		TInt pos=ptr.Locate(TChar(CEditableText::EPictureCharacter));
		while(pos!=-1)
			{
			if (iEdwinInternalFlags&ERichText)
				STATIC_CAST(CRichText*,iText)->DeleteFromParagraph(aStartPos+pos,1);
			else
				iText->DeleteL(aStartPos+pos,1); // won't leave
			++located;
			ptr.Set(ptr.Ptr(),ptr.Length()-1);
			pos=ptr.Locate(TChar(CEditableText::EPictureCharacter));
			}
		aStartPos+=(ptrLength-located);
		charsRead+=ptrLength;
		}
	}

EXPORT_C void CEikEdwin::SetWordDelimiters(TBool aPicture,TBool aPunctuation)
	{
	if (aPicture)
		iEdwinInternalFlags|=EPictureDelimits;
	else
		iEdwinInternalFlags&=~EPictureDelimits;
	if (aPunctuation)
		iEdwinInternalFlags|=EPunctuationDelimits;
	else
		iEdwinInternalFlags&=~EPunctuationDelimits;
	}

EXPORT_C void CEikEdwin::GetWordInfo(TInt aCurrentPos,TInt& aStartPos,TInt& aLength) const
	{
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	iText->GetWordInfo(aCurrentPos,aStartPos,aLength,iEdwinInternalFlags&EPictureDelimits,iEdwinInternalFlags&EPunctuationDelimits);
	}




EXPORT_C void CEikEdwin::InsertFieldL(CTextField* aField,TUid aFieldType)
// Takes ownership of aField
// Inserts if there is space for the insert
//
	{
	__ASSERT_DEBUG(aField,Panic(EEikPanicFieldDoesNotExist));
	__ASSERT_DEBUG(iTextView,Panic(EEikPanicEdwinNoView));
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	
	CancelFepTransaction();
	TPtr ptr(NULL,0); // a null TPtr
	TInt fieldLength=aField->Value(ptr);
	CleanupStack::PushL(aField);
	if (!((iTextLimit>(TextLength()+fieldLength)) || !iTextLimit))
		{
		iEikonEnv->InfoMsg(R_EIK_TBUF_MAX_CHARACTERS_REACHED);
		CleanupStack::PopAndDestroy(); // aField
		}
	else
		{
		TCursorSelection selection=iTextView->Selection();
		const TInt selectionLength=selection.Length();
		TBool formatHasChanged=EFalse;
		if (selectionLength)
			DeleteHighlightL(formatHasChanged);
		const TInt oldLength=TextLength();
		const TInt cursorPos=CursorPos();
		CleanupStack::Pop(); // InsertFieldL takes ownership of aField
		TRAPD(err,iText->InsertFieldL(cursorPos,aField,aFieldType));
		TRAP(err,iText->UpdateFieldL(cursorPos));
		const TInt length=iText->DocumentLength();
		if ((length>KFullFormattingUpperThreshold && oldLength<=KFullFormattingUpperThreshold) ||
			(length<=KPartialFormattingLowerThreshold && oldLength>KPartialFormattingLowerThreshold))
			TRAP(err,SetAmountToFormatL());
		const TInt fieldLength=TextLength()-oldLength;
		const TInt newCursorPos=selection.LowerPos()+fieldLength;
		iTextView->SetPendingSelection(TCursorSelection(newCursorPos,newCursorPos));
		const TInt anchor=selection.LowerPos();
		selection.iAnchorPos=anchor;
		selection.iCursorPos=anchor+fieldLength;
		if (iUndoStore)
			iUndoStore->SetNewText(selection);
		iTextView->HandleInsertDeleteL(selection,selectionLength,formatHasChanged);
		ReportEventL(MCoeControlObserver::EEventStateChanged);
		User::LeaveIfError(err);
		}
	}

EXPORT_C void CEikEdwin::UpdateAllFieldsL()
// move to the start of the document then update all fields
// (must move to the start first because doc may get much shorter)
//
	{
	__ASSERT_DEBUG(iTextView,Panic(EEikPanicEdwinNoView));
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));

	CancelFepTransaction();
	SetCursorPosL(0,EFalse); // kludge for the moment
	const TInt oldLength=iText->DocumentLength();
	iText->UpdateAllFieldsL();
	const TInt length=iText->DocumentLength();
	if ((length>KFullFormattingUpperThreshold && oldLength<=KFullFormattingUpperThreshold) ||
		(length<=KPartialFormattingLowerThreshold && oldLength>KPartialFormattingLowerThreshold))
	SetAmountToFormatL();
	TViewYPosQualifier yPosQualifier;
	yPosQualifier.SetMakeLineFullyVisible();
	iTextView->HandleGlobalChangeL(yPosQualifier);
	ReportEventL(MCoeControlObserver::EEventStateChanged);
	}

EXPORT_C void CEikEdwin::UpdateCurrentFieldL()
// if we're in a field move to the start of it and update it, else do nothing
//
	{
	__ASSERT_DEBUG(iTextView,Panic(EEikPanicEdwinNoView));
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));

	CancelFepTransaction();
	TInt pos = CursorPos();
	TFindFieldInfo info;
	if (iText->FindFields(info,pos))
		{// we are in a field
		// move cursor to start of field
		SetCursorPosL(info.iFirstFieldPos,EFalse);
		const TInt oldLength=iText->DocumentLength();
		// update the field
		iText->UpdateFieldL(pos);
		const TInt length=iText->DocumentLength();
		if ((length>KFullFormattingUpperThreshold && oldLength<=KFullFormattingUpperThreshold) ||
			(length<=KPartialFormattingLowerThreshold && oldLength>KPartialFormattingLowerThreshold))
		SetAmountToFormatL();
		iTextView->HandleRangeFormatChangeL(TCursorSelection(info.iFirstFieldPos,info.iFirstFieldLen),EFalse);
		ReportEventL(MCoeControlObserver::EEventStateChanged);
		}
	}

EXPORT_C void CEikEdwin::Reserved_1()
	{}

EXPORT_C void CEikEdwin::Reserved_2()
	{}

EXPORT_C void CEikEdwin::Reserved_3()
	{}
