// EIKRTED.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include <s32mem.h>
#include <s32file.h>
#include <txtrich.h>
#include <basched.h>
#include <apparc.h>
#include <apgdoor.h>
#include <apacln.h>
#include <clock.h>
#include <eikrted.h>
#include <eikenv.h>
#include <eikappui.h>
#include <eikembal.h>
#include <eikdoc.h>
#include <eikproc.h>
#include <eikdialg.h>
#include <eikobjfm.h>
#include <eikrubtl.h>
#include <eikchlst.h>
#include <eiktxtut.h>
#include <eikedwin.hrh>
#include <eikpanic.h>
#include <eikedwin.pan>
#include <eikpriv.hrh>
#include <eikon.rsg>

EXPORT_C CEikRichTextEditor::CEikRichTextEditor()
	{
	iEdwinUserFlags=EAllowPictures|EAllowUndo;
	iEdwinInternalFlags=ERichText;
	iDefaultIconicDoorSize=KDefaultIconicDoorSize;
	}

EXPORT_C CEikRichTextEditor::CEikRichTextEditor(const TEikBorder& aBorder)
	: CEikGlobalTextEditor(aBorder)
	{
	iEdwinUserFlags=EAllowPictures|EAllowUndo;
	iEdwinInternalFlags=ERichText;
	iDefaultIconicDoorSize=KDefaultIconicDoorSize;
	}

EXPORT_C CEikRichTextEditor::~CEikRichTextEditor()
	{
	delete iEmbeddedDocUpdate;
	if (iEmbeddedDoc.iPicture.IsPtr())
		delete iEmbeddedDoc.iPicture.AsPtr();
	delete iRubberBand;
	}

EXPORT_C void CEikRichTextEditor::ConstructL(const CCoeControl* aParent,TInt aNumberOfLines,TInt aTextLimit,
											 TInt aEdwinFlags,TInt aFontControlFlags,
											 TInt aFontNameFlags)
	{
	iEmbeddedDocUpdate=CIdle::NewL(EActivePriorityWsEvents+1); // would EActivePriorityRedrawEvents be sufficient ???
	CEikGlobalTextEditor::ConstructL(aParent,aNumberOfLines,aTextLimit,aEdwinFlags,aFontControlFlags,aFontNameFlags);
	}

EXPORT_C void CEikRichTextEditor::ConstructFromResourceL(TResourceReader& aReader)
	{
	iEmbeddedDocUpdate=CIdle::NewL(EActivePriorityWsEvents+1);
	CEikGlobalTextEditor::ConstructFromResourceL(aReader);
	}

EXPORT_C void CEikRichTextEditor::Draw(const TRect& aRect) const
	{
	CEikEdwin::Draw(aRect);
	if (iRubberBand)
		iRubberBand->XorDraw();
	}

EXPORT_C TKeyResponse CEikRichTextEditor::OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType)
	{
	if (aType!=EEventKey)
		return EKeyWasNotConsumed;
	if (iEdwinUserFlags&EDisplayOnly)
		return EKeyWasConsumed;
	const TInt code=aKeyEvent.iCode;
	if ((code==EKeySpace || code==EKeyEnter) && CheckForObjectL())
		return EKeyWasConsumed;
	if (code==EKeyEscape && iRubberBand)
		{
		iRubberBand->Deactivate();
		iTextView->SetSelectionVisibilityL(ETrue);
		delete iRubberBand;
		iRubberBand=NULL;
		IgnoreEventsUntilNextPointerUp(); // provide own version of this instead ???
		return EKeyWasConsumed;
		}
	const TInt modifiers=aKeyEvent.iModifiers;
	if (modifiers&EModifierCtrl)
		{
		TBool consumed=ETrue;
		TBuf<24> buf;
		if (aKeyEvent.iModifiers&EModifierShift)
			iCoeEnv->ReadResource(buf,R_EIK_EDWIN_SHIFT_CTRL_HOTKEYS);
		else
			iCoeEnv->ReadResource(buf,R_EIK_EDWIN_CTRL_HOTKEYS);
		const TInt pos=buf.Locate(TChar(code+'a'-1));
		switch (pos)
			{
		case EHotKeyInsertObject:
			CheckNotReadOnlyL();
			if (!(iEdwinUserFlags&EAllowPictures))
				iEikonEnv->LeaveWithInfoMsg(R_EIK_TBUF_CANNOT_INSERT_OBJECTS);
			InsertObjectL(EGlassIfPossible);
			break;
		case EHotKeyEditObject:
			CheckNotReadOnlyL();
			if (!(iEdwinUserFlags&EAllowPictures))
				return EKeyWasConsumed;
			ReEditObjectL();
			break;
		case EHotKeyFormatObject:
			CheckNotReadOnlyL();
			if (!(iEdwinUserFlags&EAllowPictures))
				return EKeyWasConsumed;
			EditPictureFormatL();
			break;
		default:
			consumed=EFalse;
			}
		if (consumed)
			return EKeyWasConsumed;
		}
	TKeyResponse response=CEikGlobalTextEditor::OfferKeyEventL(aKeyEvent,aType);
	UpdateButtonGroup();
	return response;
	}

EXPORT_C void CEikRichTextEditor::HandlePointerEventL(const TPointerEvent& aPointerEvent)
	{
	if (iEdwinUserFlags&EDisplayOnly)
		return;
	const TCursorSelection selection=iTextView->Selection();
	const TPoint pointerPos=aPointerEvent.iPosition;
	TPoint origPos=pointerPos;
	TInt newCursorPos=iTextView->XyPosToDocPosL(origPos);
	TBool navigation=EFalse;
	switch (aPointerEvent.iType)
		{
	case TPointerEvent::EButton1Down:
		{
		if (iTextView->ViewRect().Contains(pointerPos))
			iEdwinInternalFlags|=ELeftDownInViewRect;
		else
			return;
		if (aPointerEvent.iModifiers&EModifierCtrl)
			iEdwinInternalFlags|=EDragDouble;
		else
			iEdwinInternalFlags&=(~EDragDouble);
		TBool overPicture=EFalse;
		if (iEdwinInternalFlags&EDragDouble)
			CEikEdwin::HandlePointerEventL(aPointerEvent);
		else
			{
			TRect pictRect;
			overPicture=iTextView->GetPictureRectangleL(pointerPos,pictRect);
			if (!overPicture)
				CEikEdwin::HandlePointerEventL(aPointerEvent);
			else
				{
				iLastPointerDocPos=CursorPos();
				TRect frameRect;
				TInt docPos;
				const TBool hasFrame=iTextView->IsPictureFrameSelected(frameRect,docPos);
				TInt frameEdge=TFrameOverlay::ENoEdges;
				TRect* junk;
				iTextView->SetXyPosL(pointerPos,EFalse,junk,frameEdge);
				const TInt lowerPos=Selection().LowerPos();
				const TPictureHeader picHeader=STATIC_CAST(CRichText*,iText)->PictureHeader(lowerPos);
				CPicture* pic=STATIC_CAST(CRichText*,iText)->PictureHandleL(lowerPos);
				const TPictureCapability capability=pic->Capability();
				if (((capability.iScalingType==TPictureCapability::EFullyScaleable || capability.iIsCroppable || 
					(capability.iScalingType==TPictureCapability::EScaleableMaintainingAspectRatio && 
					aPointerEvent.iModifiers&EModifierShift)) && (frameEdge&(TFrameOverlay::EEdgeLeft|
					TFrameOverlay::EEdgeRight|TFrameOverlay::EEdgeTop|TFrameOverlay::EEdgeBottom))) || 
					(capability.iScalingType==TPictureCapability::EScaleableMaintainingAspectRatio && 
					(frameEdge==TFrameOverlay::EEdgeLeft || frameEdge==TFrameOverlay::EEdgeRight ||
					frameEdge==TFrameOverlay::EEdgeTop || frameEdge==TFrameOverlay::EEdgeBottom)))
					{
					CheckNotReadOnlyL();
					iRubberBand=new(ELeave) CEikRubberBand;
					iRubberBand->SetContainerWindowL(*this);
					iTextView->SetSelectionVisibilityL(EFalse); // hide frame
					if (!(aPointerEvent.iModifiers&EModifierCtrl))
						frameEdge|=CEikRubberBand::EMaintainAspect;
					iRubberBand->Activate(pointerPos,pictRect,frameEdge);
					if (aPointerEvent.iModifiers&EModifierShift)
						{
						if (frameEdge&TFrameOverlay::EEdgeLeft)
							iEdwinInternalFlags|=ECropFromLeft;
						else if (frameEdge&TFrameOverlay::EEdgeRight)
							iEdwinInternalFlags|=ECropFromRight;
						if (frameEdge&TFrameOverlay::EEdgeTop)
							iEdwinInternalFlags|=ECropFromTop;
						else if (frameEdge&TFrameOverlay::EEdgeBottom)
							iEdwinInternalFlags|=ECropFromBottom;
						}
					}
				else if (hasFrame && frameRect.Contains(pointerPos) && picHeader.iPictureType==KUidPictureTypeDoor)
					CheckForObjectL();
				}
			}
		break;
		}
	case TPointerEvent::EButtonRepeat:
		CEikEdwin::HandlePointerEventL(aPointerEvent);
		break;
	case TPointerEvent::EDrag:
		if (iRubberBand)
			iRubberBand->NotifyDragPos(pointerPos);
		else
			{
			TRect frameRect;
			TInt docPos;
			if (iTextView->IsPictureFrameSelected(frameRect,docPos) && frameRect.Contains(pointerPos))
				return; // make it dead easy to 'select' a picture with the pen and dead hard to highlight only it
			CEikEdwin::HandlePointerEventL(aPointerEvent);
			}
		break;
	case TPointerEvent::EButton1Up:
		{
		if (!(iEdwinInternalFlags&ELeftDownInViewRect))
			return;
		if (newCursorPos!=iLastPointerDocPos)
			navigation=ETrue;
		iLastPointerDocPos=newCursorPos;
		if (iRubberBand && aPointerEvent.iType==TPointerEvent::EButton1Up)
			{
			iEdwinInternalFlags&=(~ELeftDownInViewRect);
			iRubberBand->Deactivate();
			iTextView->SetSelectionVisibilityL(ETrue);
			TSize oldPicSize;
			CPicture* picHandle=STATIC_CAST(CRichText*,iText)->PictureHandleL(selection.LowerPos());
			picHandle->GetSizeInPixels(iZoomFactor,oldPicSize);
			const TSize newPicSize=iRubberBand->BandRect().Size();
			delete iRubberBand;
			iRubberBand=NULL;
			if (newPicSize!=oldPicSize)
				{
				if (iEdwinInternalFlags&(ECropFromTop|ECropFromBottom|ECropFromLeft|ECropFromRight) && oldPicSize!=newPicSize)
					{
					TInt cropWidth=iZoomFactor->HorizontalPixelsToTwips(oldPicSize.iWidth-newPicSize.iWidth)*1000/picHandle->ScaleFactorWidth();
					TInt cropHeight=iZoomFactor->VerticalPixelsToTwips(oldPicSize.iHeight-newPicSize.iHeight)*1000/picHandle->ScaleFactorHeight();
					TMargins cropMargins;
					picHandle->GetCropInTwips(cropMargins);
					if (iEdwinInternalFlags&ECropFromLeft)
						{
						cropMargins.iLeft+=cropWidth;
						if (cropMargins.iLeft<0)
							cropMargins.iLeft = 0;
						iEdwinInternalFlags&=~ECropFromLeft;
						}
					else if (iEdwinInternalFlags&ECropFromRight)
						{
						cropMargins.iRight+=cropWidth;
						if (cropMargins.iRight<0)
							cropMargins.iRight = 0;
						iEdwinInternalFlags&=~ECropFromRight;
						}
					if (iEdwinInternalFlags&ECropFromTop)
						{
						cropMargins.iTop+=cropHeight;
						if (cropMargins.iTop<0)
							cropMargins.iTop = 0;
						iEdwinInternalFlags&=~ECropFromTop;
						}
					else if (iEdwinInternalFlags&ECropFromBottom)
						{
						cropMargins.iBottom+=cropHeight;
						if (cropMargins.iBottom<0)
							cropMargins.iBottom = 0;
						iEdwinInternalFlags&=~ECropFromBottom;
						}
					picHandle->SetCropInTwips(cropMargins);
					}
				else
					{
					const TInt scaleFactorWidth=picHandle->ScaleFactorWidth();
					const TInt scaleFactorHeight=picHandle->ScaleFactorHeight();
					const TInt64 width=(newPicSize.iWidth*scaleFactorWidth)/oldPicSize.iWidth;
					const TInt64 height=(newPicSize.iHeight*scaleFactorHeight)/oldPicSize.iHeight;
					picHandle->SetScaleFactor(width.Low(),height.Low());
					}
				iText->SetHasChanged(ETrue);
				iTextView->HandleRangeFormatChangeL(selection,EFalse);
				ReportEventL(MCoeControlObserver::EEventStateChanged);
				UpdateScrollBarsL();
				}
			break;
			}
		CEikEdwin::HandlePointerEventL(aPointerEvent);
		}
	default:
		break;
		}
	if (navigation)
		{
		UpdateButtonGroup();
		ReportEdwinEventL(MEikEdwinObserver::EEventNavigation);
		CancelInsertCharFormat();
		}
	}

EXPORT_C void CEikRichTextEditor::ActivateL()
	{
	UpdatePictureFormatL(0,iText->DocumentLength());
	UpdatePictureSizeL(0,iText->DocumentLength());
	CEikGlobalTextEditor::ActivateL();
	}
	
EXPORT_C void CEikRichTextEditor::HandleTextPastedL(TInt aStartPos,TInt& aLength)
	{
	UpdatePictureFormatL(aStartPos,aLength);
	UpdatePictureSizeL(aStartPos,aLength);
	}
	
EXPORT_C void CEikRichTextEditor::NotifyExit(TExitMode aMode)
	{
	if (aMode==MApaEmbeddedDocObserver::EEmpty)
		{
		if (iEdwinInternalFlags&EReEditingObject)
			iEmbeddedDocUpdate->Start(TCallBack(CEikRichTextEditor::TryDeleteEmbeddedDocL, this));
		else
			iEmbeddedDocUpdate->Start(TCallBack(CEikRichTextEditor::DeleteEmbeddedDoc, this));
		}
	else if (aMode==MApaEmbeddedDocObserver::ENoChanges)
		{
		if (iEdwinInternalFlags&EReEditingObject)
			iEmbeddedDoc=TPictureHeader();
		else // apps should call back with EEmpty in this case
			iEmbeddedDocUpdate->Start(TCallBack(CEikRichTextEditor::DeleteEmbeddedDoc, this));
		}
	else
		{
		if (iEdwinInternalFlags&EReEditingObject)
			iEmbeddedDocUpdate->Start(TCallBack(CEikRichTextEditor::UpdateEmbeddedDocL, this));
		else
			iEmbeddedDocUpdate->Start(TCallBack(CEikRichTextEditor::InsertEmbeddedDocL, this));
		}
	}

TInt CEikRichTextEditor::TryDeleteEmbeddedDocL(TAny *aThis)
	{ // static
	CEikRichTextEditor* editor=REINTERPRET_CAST(CEikRichTextEditor*,aThis);
	editor->iEdwinInternalFlags&=~EReEditingObject;
	if (!(CEikonEnv::Static()->QueryWinL(R_EIK_TBUF_DEL_EMPTY_OBJECT_CONFIRM_1,R_EIK_TBUF_DEL_EMPTY_OBJECT_CONFIRM_2)))
		editor->iTextView->HandleRangeFormatChangeL(editor->Selection(),EFalse);
	else
		{
		TInt docPos;
		TRect rect;
		editor->iTextView->IsPictureFrameSelected(rect,docPos);
		TBool changed=EFalse;
		editor->DeleteL(changed,TCursorSelection(docPos,docPos+1),ETrue);
		editor->iTextView->SetPendingSelection(TCursorSelection(docPos,docPos));
		editor->iTextView->HandleInsertDeleteL(TCursorSelection(docPos,docPos),1,changed);
		editor->UpdateScrollBarsL();
		if (changed)
			editor->ReportEdwinEventL(MEikEdwinObserver::EEventFormatChanged);
		}
	editor->ReportEventL(MCoeControlObserver::EEventStateChanged);
	editor->UpdateScrollBarsL();
	return 0;
	}

TInt CEikRichTextEditor::DeleteEmbeddedDoc(TAny *aThis)
	{ // static
	CEikRichTextEditor* editor=REINTERPRET_CAST(CEikRichTextEditor*,aThis);
	if (editor->iEmbeddedDoc.iPicture.IsPtr())
		delete editor->iEmbeddedDoc.iPicture.AsPtr();
	editor->iEmbeddedDoc=TPictureHeader();
	return 0;
	}

TInt CEikRichTextEditor::InsertEmbeddedDocL(TAny *aThis)
	{ // static
	CEikRichTextEditor* editor=REINTERPRET_CAST(CEikRichTextEditor*,aThis);
	TInt err=KErrNone;
	if (editor->iEdwinUserFlags&EShowAllPicturesAsIconic)
		{
		TRAPD(err,STATIC_CAST(CApaDoor*,editor->iEmbeddedDoc.iPicture.AsPtr())->SetFormatToTemporaryIconL());
		if (err)
			goto errorCheck;
		}
	TRAP(err,editor->InsertPictureL(editor->iEmbeddedDoc));
errorCheck:
//	if (err!=KErrNone && editor->iEmbeddedDoc.iPicture.IsPtr())
//		delete editor->iEmbeddedDoc.iPicture.AsPtr();
	editor->iEmbeddedDoc=TPictureHeader();
	User::LeaveIfError(err);
	return 0;
	}

TInt CEikRichTextEditor::UpdateEmbeddedDocL(TAny* aThis)
	{ // static
	CEikRichTextEditor* editor=REINTERPRET_CAST(CEikRichTextEditor*,aThis);
	editor->iEdwinInternalFlags&=~EReEditingObject;
	editor->iText->SetHasChanged(ETrue);
	editor->iTextView->HandleRangeFormatChangeL(editor->Selection(),EFalse);
	editor->ReportEventL(MCoeControlObserver::EEventStateChanged);
	editor->UpdateScrollBarsL();
	return 0;
	}

EXPORT_C void CEikRichTextEditor::CopyDocumentContentL(CGlobalText& aInText,CGlobalText& aOutText)
	{
	__ASSERT_DEBUG(&aInText,Panic(EEikPanicNullPointer));
	__ASSERT_DEBUG(&aOutText,Panic(EEikPanicNullPointer));
	aOutText.Reset();
	aOutText.SetFieldFactory(CONST_CAST(MTextFieldFactory*,aInText.FieldFactory()));
	((CRichText&)aInText).DetachFromStoreL(CPicture::EDetachFull);
	iBufStore=CBufStore::NewLC(4096);
	((CRichText&)aOutText).SetPictureFactory(iEikonEnv->PictureFactory(),this);
	TStreamId streamId=aInText.StoreL(*iBufStore);
	aOutText.RestoreL(*iBufStore,streamId);
	((CRichText&)aOutText).DetachFromStoreL(CPicture::EDetachFull);
	RStoreWriteStream paraWriteStream;
	TStreamId paraStreamId=paraWriteStream.CreateLC(*iBufStore);
	(aInText.GlobalParaFormatLayer())->ExternalizeL(paraWriteStream);
	CleanupStack::PopAndDestroy(); // paraStreamId
	RStoreReadStream paraReadStream;
	paraReadStream.OpenLC(*iBufStore,paraStreamId);
	((CParaFormatLayer*)(aOutText.GlobalParaFormatLayer()))->InternalizeL(paraReadStream);
	CleanupStack::PopAndDestroy(); // paraReadStream
	RStoreWriteStream charWriteStream;
	TStreamId charStreamId=charWriteStream.CreateLC(*iBufStore);
	(aInText.GlobalCharFormatLayer())->ExternalizeL(charWriteStream);
	CleanupStack::PopAndDestroy(); // charStreamId
	RStoreReadStream charReadStream;
	charReadStream.OpenLC(*iBufStore,charStreamId);
	((CCharFormatLayer*)(aOutText.GlobalCharFormatLayer()))->InternalizeL(charReadStream);
	CleanupStack::PopAndDestroy(2); // iBufStore and charReadStream
	iBufStore=NULL;
	}

EXPORT_C const CStreamStore& CEikRichTextEditor::StreamStoreL(TInt /*aPos*/) const
	{
	return *iBufStore;
	}

EXPORT_C void CEikRichTextEditor::NewPictureL(TPictureHeader& aHdr,const CStreamStore& aDeferredPictureStore)const
	{
	iEikonEnv->PictureFactory()->NewPictureL(aHdr,aDeferredPictureStore);
	}

EXPORT_C CRichText* CEikRichTextEditor::RichText() const
	{
	return (CRichText*)iText;
	}

void CEikRichTextEditor::InsertPictureL(const TPictureHeader& aPictureHeader)
	{
	__ASSERT_DEBUG(iText,Panic(EEikPanicEdwinNoText));
	__ASSERT_DEBUG(iTextView,Panic(EEikPanicEdwinNoView));
	TBool formatHasChanged=EFalse;
	TCursorSelection selection=iTextView->Selection();
	const TInt selLength=selection.Length();
	const TInt oldLength=iText->DocumentLength();
	TRAPD(err,DoInsertPictureL(formatHasChanged,aPictureHeader));
	if (err!=KErrNone)
		{
		delete aPictureHeader.iPicture.AsPtr();
		ClearUndo();
		}
	if (selLength==0)
		{
		ClearUndo();
		if (err==KErrNone)
			iTextView->HandleCharEditL(CTextLayout::EFCharacterInsert);
		}
	else
		{
		const TInt lower=selection.LowerPos();
		selection=TCursorSelection(lower,(err==KErrNone? lower+1 : lower));
		if (err==KErrNone)
			SetUndoableText(selection);
		if (err==KErrNone || oldLength>iText->DocumentLength())
			iTextView->HandleInsertDeleteL(selection,selLength,formatHasChanged);
		}
	User::LeaveIfError(err);
	ReportEventL(MCoeControlObserver::EEventStateChanged);
	UpdateScrollBarsL();
	if (iText->DocumentLength()==UpperFullFormattingLength()+1)
		SetAmountToFormatL();
	}

void CEikRichTextEditor::DoInsertPictureL(TBool& aFormatHasChanged,const TPictureHeader& aPictureHeader)
	{
	TCursorSelection selection=iTextView->Selection();
	const TInt selLength=selection.Length();
	if (selLength)
		{
		iTextView->CancelSelectionL();
		SetCursorPosL(selection.LowerPos(),EFalse);
		DeleteL(aFormatHasChanged,selection);
		}
	else if (iTextLimit && TextLength()>=iTextLimit)
		{
		CEikonEnv::Beep();
		iEikonEnv->LeaveWithInfoMsg(R_EIK_TBUF_MAX_CHARACTERS_REACHED);
		}
	STATIC_CAST(CRichText*,iText)->InsertL(CursorPos(),aPictureHeader);
	}

void CEikRichTextEditor::RoomForObjectL()
	{
	if (iTextLimit && TextLength()>=iTextLimit)
		{
		CEikonEnv::Beep();
		iEikonEnv->InfoMsg(R_EIK_TBUF_MAX_CHARACTERS_REACHED);
		CBaActiveScheduler::LeaveNoAlert();
		}
	}

EXPORT_C void CEikRichTextEditor::InsertObjectL(TObjectFormat aFormat)
	{
	RoomForObjectL();
	CEikEmbeddableAppList* list=new(ELeave) CEikEmbeddableAppList;
	CleanupStack::PushL(list);
	list->ConstructL();
	TInt count=list->Count();
	if (!count)
		iEikonEnv->InfoMsg(R_EIK_NO_EMBEDDABLE_APPS_FOUND);
	else
		{
		CEikDialog* dialog=new(ELeave) CEikDialog;
		dialog->PrepareLC(R_EIK_DIALOG_INSERT_OBJECT);
		CEikChoiceList* choiceList=(CEikChoiceList*)dialog->Control(EEikCidInsertObjectApps);
		choiceList->SetArrayL(list);
		TInt choice=0;
		dialog->DeclareAutoChoiceList(EEikCidInsertObjectApps,&choice);
		if (dialog->RunLD())
			{
			if (!OkToDeleteSelectionL())
				{
				CleanupStack::PopAndDestroy(); // list
				return;
				}
			CEikDocument* newDoc=list->CreateEmbeddedDocumentL(choice,iEikonEnv->Process());
			TApaDocCleanupItem cleanup(iEikonEnv->Process(),newDoc);
			CleanupStack::PushL(cleanup);
			newDoc->NewDocumentL();
			CleanupStack::Pop(); // cleanup
			InsertObjectL(newDoc,aFormat);
			}
		}
	CleanupStack::PopAndDestroy(); // list
	}

EXPORT_C void CEikRichTextEditor::InsertObjectL()
	{
	InsertObjectL(EGlassIfPossible);
	}

EXPORT_C void CEikRichTextEditor::InsertObjectL(const TDesC& aAppDllName,TUid aAppDllUid,TObjectFormat aFormat)
	{
	RoomForObjectL();
	if (!OkToDeleteSelectionL())
		return;
	CApaDocument* newDoc=NULL;
	TRAPD(err,newDoc=iEikonEnv->Process()->AddNewDocumentL(aAppDllName,aAppDllUid));
	if (err==KErrNotFound)
		iEikonEnv->LeaveWithInfoMsg(R_EIK_EMBEDDED_GENERIC_APP_NOT_FOUND);
	else if (err!=KErrNone)
		User::Leave(err);
	TApaDocCleanupItem cleanup(iEikonEnv->Process(),newDoc);
	CleanupStack::PushL(cleanup);
	newDoc->NewDocumentL();
	CleanupStack::Pop();
	InsertObjectL(newDoc,aFormat);
	}

void CEikRichTextEditor::InsertObjectL(CApaDocument* aDoc,TObjectFormat aFormat)
	{
	iEmbeddedDoc.iPictureType=KUidPictureTypeDoor;
	iEmbeddedDoc.iPicture=CApaDoor::NewL(*aDoc,iDefaultIconicDoorSize); // takes ownership of newDoc
	// can it draw as glass? change format if so
	if (aFormat==EGlassIfPossible)
		{
		TRAPD(ignore,STATIC_CAST(CApaDoor*,iEmbeddedDoc.iPicture.AsPtr())->SetFormatToGlassL());
		}
	aDoc->EditL(this);
	}

EXPORT_C TBool CEikRichTextEditor::CheckForObjectL()
	{
	TRect frameRect;
	TInt docPos;
	if (!iTextView->IsPictureFrameSelected(frameRect,docPos))
		return EFalse;
	const TPictureHeader header=((CRichText*)iText)->PictureHeader(docPos);
	if (header.iPictureType!=KUidPictureTypeDoor)
		return EFalse;
	// stop multiple pointer events launching the same apps several times ???
	iCoeEnv->WsSession().PurgePointerEvents();
	DoReEditObjectL(docPos);
	return ETrue;
	}

EXPORT_C void CEikRichTextEditor::ReEditObjectL()
	{
	const TInt pos=ObjectCursorPos();
	if (pos!=KErrNotFound)
		DoReEditObjectL(pos);
	}

EXPORT_C TInt CEikRichTextEditor::ObjectCursorPos() const
	{
	if (SelectionLength()>1)
		{
		iEikonEnv->InfoMsg(R_EIK_REGION_SELECTED);
		return KErrNotFound;
		}
	TRect frameRect;
	TInt docPos;
	if (iTextView->IsPictureFrameSelected(frameRect,docPos))
		{
		TPictureHeader header=STATIC_CAST(CRichText*,iText)->PictureHeader(docPos);
		if (header.iPictureType==KUidPictureTypeDoor)
			return docPos;
		}
	iEikonEnv->InfoMsg(R_EIK_NO_OBJECT_PRESENT); // no embedded object found
	return KErrNotFound;
	}

void CEikRichTextEditor::DoReEditObjectL(TInt aDocPos)
	{
	CApaDoor* door=NULL;
	CApaDocument* embeddedDoc=NULL;
	GetEmbeddedAppL(door,embeddedDoc,aDocPos);
	iEdwinInternalFlags|=EReEditingObject;
	embeddedDoc->EditL(this,IsReadOnly());
	}

EXPORT_C void CEikRichTextEditor::EditPictureFormatL()
	{
	const TInt docPos=ObjectCursorPos();
	if (docPos==KErrNotFound)
		return;
	CApaDoor* door=NULL;
	CApaDocument* embeddedDoc=NULL;
	GetEmbeddedAppL(door,embeddedDoc,docPos);
	// if the doc can display as glass open the full dialog
	CApaDocument::TCapability capability=embeddedDoc->Capability();
	if (capability.CanDrawGlass())
		{
		CEikFormatObjectDialog* dialog=new(ELeave) CEikFormatObjectDialog(*door,*embeddedDoc,iDefaultIconicDoorSize);
		if (dialog->ExecuteLD(R_EIK_DIALOG_FORMAT_GLASSABLE_OBJECT))
			{
			iText->SetHasChanged(ETrue);
			iTextView->HandleRangeFormatChangeL(iTextView->Selection(),EFalse);
			UpdateScrollBarsL();
			ReportEventL(MCoeControlObserver::EEventStateChanged);
			// update the picture size in the header when this becomes possible (post B4 etext)
			}
		}
	else
		{// otherwise open the little dialog that does nothing...
		CEikObjectInfoDialog* dialog=new(ELeave) CEikObjectInfoDialog(*(door->Caption()));
		dialog->ExecuteLD(R_EIK_DIALOG_OBJECT_INFO);
		}
	}

void CEikRichTextEditor::GetEmbeddedAppL(CApaDoor*& aDoor,CApaDocument*& aDoc,TInt aDocPos)
	{
	aDoor=STATIC_CAST(CApaDoor*,RichText()->PictureHandleL(aDocPos));
	TRAPD(err,aDoc=aDoor->DocumentL(ETrue)); // always prompt for password
	if (err==KErrNotFound)
		{
		TBuf<80> buf;
		iCoeEnv->ReadResource(buf,R_EIK_EMBEDDED_APP_NOT_FOUND);
		TBuf<128> caption=*aDoor->Caption();
		TextUtils::TruncateToNumChars(caption,RMessageWindow::EMaxTextLength-32); // resource is no longer than 32 chars
		HBufC* message=HBufC::NewLC(caption.Length()+buf.Length());
		TPtr ptr=message->Des();
		ptr.Format(buf,&caption);
		iEikonEnv->InfoMsg(*message);
		CleanupStack::PopAndDestroy(); // message
		CBaActiveScheduler::LeaveNoAlert();
		}
	User::LeaveIfError(err);
	}

EXPORT_C void CEikRichTextEditor::UpdatePictureFormatL()
	{
	UpdatePictureFormatL(0,iText->DocumentLength());
	}

EXPORT_C void CEikRichTextEditor::UpdatePictureFormatL(TInt aStartPos,TInt aLength)
	{
	TInt count=iText->ComponentInfo().iPictureCount;
	if (~iEdwinUserFlags&EShowAllPicturesAsIconic || count==0)
		return;
	const TInt endPos=aStartPos+aLength;
	for (TInt ii=aStartPos;ii<endPos;ii++)
		{
		if (count==0)
			return;
		CApaDoor* door=STATIC_CAST(CApaDoor*,RichText()->PictureHandleL(ii));
		if (door)
			{
			door->SetFormatToTemporaryIconL();
			--count;
			}
		}
	}

EXPORT_C void CEikRichTextEditor::SetDefaultIconicDoorSize(const TSize& aSize)
	{
	iDefaultIconicDoorSize=aSize;
	}

EXPORT_C void CEikRichTextEditor::UpdatePictureSizeL()
	{
	UpdatePictureSizeL(0,iText->DocumentLength());
	}

EXPORT_C void CEikRichTextEditor::UpdatePictureSizeL(TInt aStartPos,TInt aLength)
	{
	TInt count=iText->ComponentInfo().iPictureCount;
	const TInt endPos=aStartPos+aLength;
	for (TInt ii=aStartPos;ii<endPos;ii++)
		{
		if (count==0)
			return;
		CApaDoor* door=STATIC_CAST(CApaDoor*,RichText()->PictureHandleL(ii));
		if (door)
			{
			if (door->Format()!=CApaDoorBase::EGlassDoor)
				door->SetSizeInTwips(iDefaultIconicDoorSize);
			else
				{
				door->SetFormatToTemporaryIconL(ETrue);
				door->SetSizeInTwips(iDefaultIconicDoorSize);
				door->SetFormatToTemporaryIconL(EFalse);
				}
			--count;
			}
		}
	}

EXPORT_C void CEikRichTextEditor::Reserved_1()
	{}

EXPORT_C void CEikRichTextEditor::Reserved_2()
	{}

EXPORT_C void CEikRichTextEditor::Reserved_3()
	{}
