sl@0: // Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies).
sl@0: // All rights reserved.
sl@0: // This component and the accompanying materials are made available
sl@0: // under the terms of "Eclipse Public License v1.0"
sl@0: // which accompanies this distribution, and is available
sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0: //
sl@0: // Initial Contributors:
sl@0: // Nokia Corporation - initial contribution.
sl@0: //
sl@0: // Contributors:
sl@0: //
sl@0: // Description:
sl@0: // Sprite
sl@0: // 
sl@0: //
sl@0: 
sl@0: #include <e32std.h>
sl@0: #include "sprite.h"
sl@0: #include "rootwin.h"
sl@0: #include "windowgroup.h"
sl@0: #include "ScrDev.H"
sl@0: #include "wstop.h"
sl@0: #include "ANIM.H"
sl@0: #include "panics.h"
sl@0: #include "EVENT.H"
sl@0: #include "bitgditomwsgraphicscontextmappings.h"
sl@0: #include "../debuglog/DEBUGLOG.H"
sl@0: 
sl@0: static const TInt64 KFlashHalfSecond(500000); //interval for flashing sprites
sl@0: 
sl@0: GLDEF_D CWsDeltaTimer *CWsSpriteBase::iDeltaTimer=NULL;
sl@0: 
sl@0: TInt TimerCallBack(TAny *aPtr)
sl@0: 	{
sl@0: 	((CWsSpriteBase *)aPtr)->TimerExpired();
sl@0: 	return(KErrNone);
sl@0: 	}
sl@0: 
sl@0: #ifndef _DEBUG
sl@0: 
sl@0: #define LOG_SPRITE_REDRAW_START(sprite)
sl@0: #define LOG_SPRITE_REDRAW_END(sprite)
sl@0: #define LOG_SPRITE_FLASH(aSprite)
sl@0: 
sl@0: #else
sl@0: 
sl@0: #define LOG_SPRITE_REDRAW_START(sprite) LogSpriteRedrawStart(sprite)
sl@0: #define LOG_SPRITE_REDRAW_END(sprite) LogSpriteRedrawEnd(sprite)
sl@0: #define LOG_SPRITE_FLASH(sprite) LogSpriteFlash(sprite)
sl@0: 
sl@0: extern CDebugLogBase *wsDebugLog;
sl@0: 
sl@0: 
sl@0: static void LogSpriteRedrawStart(const CWsSpriteBase& aSprite)
sl@0:     {
sl@0:     if (wsDebugLog)
sl@0:         {
sl@0:         _LIT(KAnnotateSpriteRedrawStart, ">> MWsDrawAnnotationObserver::FloatingSpriteRedrawStart [%S][app %d]");
sl@0:         const TDesC& clientName = aSprite.WsOwner()->Client().FullName();
sl@0:         TBuf<LogTBufSize> log;
sl@0:         TTruncateOverflow overflow;
sl@0:         log.AppendFormat(KAnnotateSpriteRedrawStart, &overflow, &clientName, aSprite.WsOwner()->ConnectionHandle());
sl@0:         wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything, log);
sl@0:         }
sl@0:     }
sl@0: 
sl@0: static void LogSpriteRedrawEnd(const CWsSpriteBase& aSprite)
sl@0:     {
sl@0:     if (wsDebugLog)
sl@0:         {
sl@0:         _LIT(KAnnotateSpriteRedrawEnd, "<< MWsDrawAnnotationObserver::FloatingSpriteRedrawEnd [%S][app %d]");
sl@0:         const TDesC& clientName = aSprite.WsOwner()->Client().FullName();
sl@0:         TBuf<LogTBufSize> log;
sl@0:         TTruncateOverflow overflow;
sl@0:         log.AppendFormat(KAnnotateSpriteRedrawEnd, &overflow, &clientName, aSprite.WsOwner()->ConnectionHandle());
sl@0:         wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything, log);
sl@0:         }
sl@0:     }
sl@0: 
sl@0: static void LogSpriteFlash(const CWsSpriteBase& /*aSprite*/)
sl@0:     {
sl@0:     if (wsDebugLog)
sl@0:         {
sl@0:         // The following code causes Exception and is commented out, see defect GFX09962
sl@0:         
sl@0: //      _LIT(KAnnotateSpriteFlash, "-- MWsDrawAnnotationObserver::SpriteFlash:%d [%S][app %d]");
sl@0: //      const TDesC& clientName = aSprite.WsOwner()->Client().FullName();
sl@0: //      TBuf<LogTBufSize> log;
sl@0: //      TTruncateOverflow overflow;
sl@0: //      log.AppendFormat(KAnnotateSpriteFlash, &overflow, &clientName, aSprite.WsOwner()->ConnectionHandle());
sl@0: //      wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything, log);
sl@0:                 
sl@0:         // This code is temporarily here until above problem is resolved
sl@0:         _LIT(KAnnotateSpriteFlash, "-- MWsDrawAnnotationObserver::SpriteFlash");
sl@0:         TBuf<LogTBufSize> log;
sl@0:         log.AppendFormat(KAnnotateSpriteFlash);
sl@0:         wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything, log);
sl@0:         }
sl@0:     }
sl@0: 
sl@0: 
sl@0: #endif
sl@0: 
sl@0: 
sl@0: 
sl@0: static void AnnotateSpriteRedrawStart(const CWsSpriteBase& aSprite, const TRegion& aRegion)
sl@0: 	{
sl@0: 	LOG_SPRITE_REDRAW_START(aSprite);
sl@0: 	MWsDrawAnnotationObserver* annoObs = aSprite.Screen()->DrawAnnotationObserver();
sl@0: 	if(annoObs)
sl@0: 		{
sl@0: 		annoObs->SpriteRedrawStart(aSprite, aRegion);
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: static void AnnotateSpriteRedrawEnd(const CWsSpriteBase& aSprite)
sl@0: 	{
sl@0: 	LOG_SPRITE_REDRAW_END(aSprite);
sl@0: 	MWsDrawAnnotationObserver* annoObs = aSprite.Screen()->DrawAnnotationObserver();
sl@0: 	if(annoObs)
sl@0: 		{
sl@0: 		annoObs->SpriteRedrawEnd(aSprite);
sl@0: 		}		
sl@0: 	}
sl@0: 
sl@0: static void AnnotateSpriteFlash(const CWsSpriteBase& aSprite, TBool aFlashOn)
sl@0: 	{
sl@0: 	LOG_SPRITE_FLASH(aSprite);
sl@0: 	MWsDrawAnnotationObserver* annoObs = aSprite.Screen()->DrawAnnotationObserver();
sl@0: 	if(annoObs)
sl@0: 		{
sl@0: 		annoObs->SpriteFlash(aSprite, aFlashOn);
sl@0: 		}	
sl@0: 	}
sl@0: 
sl@0: 
sl@0: //
sl@0: // CWsSpriteMember
sl@0: //
sl@0: 
sl@0: CWsSpriteMember::CWsSpriteMember()
sl@0: 	{
sl@0: 	}
sl@0: 
sl@0: CWsSpriteMember::~CWsSpriteMember()
sl@0: 	{
sl@0: 	delete iBitmap;
sl@0: 	delete iMaskBitmap;
sl@0: 	}
sl@0: 
sl@0: TBool CWsSpriteMember::SetL(const TCmdSpriteMember &aCmdSpriteMember)
sl@0: 	{
sl@0: 	if (aCmdSpriteMember.iBitmap!=0)	// Null member of sequence, time is only valid field in member data
sl@0: 		{
sl@0: 		iBitmap=new(ELeave) CFbsBitmap();
sl@0: 		TInt ret = iBitmap->Duplicate(aCmdSpriteMember.iBitmap);
sl@0: 		if (ret == KErrNoMemory)
sl@0: 			{
sl@0: 			User::Leave(ret);
sl@0: 			}
sl@0: 		if (ret != KErrNone)
sl@0: 			return(ETrue);
sl@0: 
sl@0: 		if (aCmdSpriteMember.iMaskBitmap)
sl@0: 			{
sl@0: 			iMaskBitmap=new(ELeave) CFbsBitmap();
sl@0: 			TInt ret = iMaskBitmap->Duplicate(aCmdSpriteMember.iMaskBitmap);
sl@0: 			if (ret == KErrNoMemory)
sl@0: 				{
sl@0: 				User::Leave(ret);
sl@0: 				}
sl@0: 			if (ret != KErrNone)
sl@0: 				return(ETrue);
sl@0: 			}
sl@0: 		iInvertMask=aCmdSpriteMember.iInvertMask;
sl@0: 		iDrawMode=aCmdSpriteMember.iDrawMode;
sl@0: 		iOffset=aCmdSpriteMember.iOffset;
sl@0: 		}
sl@0: 	iInterval=aCmdSpriteMember.iInterval;
sl@0: 	return(EFalse);
sl@0: 	}
sl@0: 
sl@0: //
sl@0: // CWsSpriteBase
sl@0: //
sl@0: 
sl@0: TBool CWsSpriteBase::UpdateMemberL(CWsSpriteMember *aMember, const TCmdSpriteMember &aCmdSpriteMember)
sl@0: 	{
sl@0: 	CFbsBitmap *old=aMember->iBitmap;
sl@0: 	CFbsBitmap *oldMask=aMember->iMaskBitmap;
sl@0: 	aMember->iBitmap=NULL;
sl@0: 	aMember->iMaskBitmap=NULL;
sl@0: 	TBool ret=EFalse;
sl@0: 	TRAPD(err,ret=aMember->SetL(aCmdSpriteMember));
sl@0: 	if (err!=KErrNone)
sl@0: 		{
sl@0: um_error:
sl@0: 		delete aMember->iBitmap;
sl@0: 		delete aMember->iMaskBitmap;
sl@0: 		aMember->iBitmap=old;
sl@0: 		aMember->iMaskBitmap=oldMask;
sl@0: 		User::Leave(err);
sl@0: 		}
sl@0: 	TRAP(err,CheckSizesL());
sl@0: 	if (err!=KErrNone)
sl@0: 		goto um_error;
sl@0: 	SetMember(0);
sl@0: 	delete old;
sl@0: 	delete oldMask;
sl@0: 	return(ret);
sl@0: 	}
sl@0: 
sl@0: void CWsSpriteBase::InitStaticsL()
sl@0: 	{
sl@0: 	iDeltaTimer=CWsDeltaTimer::NewL(ESpriteAnimatePriority);
sl@0: 	}
sl@0: 
sl@0: void CWsSpriteBase::DeleteStatics()
sl@0: 	{
sl@0: 	delete iDeltaTimer;
sl@0: 	}
sl@0: 
sl@0: CWsSpriteBase::CWsSpriteBase(CWsClient *owner, WH_HANDLES aType) : CWsScreenObject(owner,aType,owner->Screen())
sl@0: 	{
sl@0: 	}
sl@0: 
sl@0: CWsSpriteBase::~CWsSpriteBase()
sl@0: 	{
sl@0: 	Deactivate();
sl@0: 	if(IsFloating() && iGroupWin)
sl@0: 		{
sl@0: 		iGroupWin->RemoveSprite(this);
sl@0: 		}
sl@0: 	
sl@0: 	//iDeltaTimer->Remove(iDeltaTimerEntry);
sl@0: 	if (iMembers)
sl@0: 		{
sl@0: 		iMembers->ResetAndDestroy();
sl@0: 		delete iMembers;
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: void CWsSpriteBase::ForceRedraw()
sl@0: 	{
sl@0: 	TRegionFix<1> region;
sl@0: 	region.AddRect(Rect());
sl@0: 	Screen()->AddRedrawRegion(region);
sl@0: 	}
sl@0: 
sl@0: void CWsSpriteBase::Deactivate()
sl@0: 	{
sl@0: 	//Disconnect from the sprite list and hide the sprite
sl@0: 	if (iFlags & ESpriteActive)
sl@0: 		{
sl@0: 		iFlags&=~ESpriteActive;
sl@0: 		
sl@0: 		MWsWindowTreeObserver* const windowTreeObserver = Screen()->WindowTreeObserver();
sl@0: 		if (windowTreeObserver)
sl@0: 			{
sl@0: 			windowTreeObserver->NodeReleased(*this);
sl@0: 			}
sl@0: 		
sl@0: 		if (iMembers && iMembers->Count()>1)
sl@0: 			{
sl@0: 			iDeltaTimer->Remove(iDeltaTimerEntry);
sl@0: 			}
sl@0: 		
sl@0: 		if (IsFloating())
sl@0: 			{
sl@0: 			Screen()->SpriteManager()->RemoveFloatingSprite(this);
sl@0: 			if (!Screen()->ChangeTracking())
sl@0: 				{
sl@0: 				ForceRedraw();
sl@0: 				}
sl@0: 			}
sl@0: 
sl@0: 		// Note: This could be a floating sprite attached to the root window (PDEF138379)
sl@0: 		if(iWin)
sl@0: 			{
sl@0: 			iWin->RemoveSprite(this);
sl@0: 			}
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: /** Called from groupwin destructor only */
sl@0: void CWsSpriteBase::DisconnectGroupWin()
sl@0: 	{
sl@0: 	WS_ASSERT_DEBUG(IsFloating(),EWsPanicFloatingSprite);
sl@0: 	iGroupWin=NULL;
sl@0: 	}
sl@0: 
sl@0: void CWsSpriteBase::CheckSizesL()
sl@0: 	{
sl@0: 	iMaxSize.SetSize(0,0);
sl@0: 	for(TInt index=0;index<iMembers->Count();index++)
sl@0: 		{
sl@0: 		CWsSpriteMember *wsm=(*iMembers)[index];
sl@0: 		if (wsm->iBitmap)
sl@0: 			{
sl@0: 			TSize size=wsm->iBitmap->SizeInPixels();
sl@0: 			if (wsm->iMaskBitmap)
sl@0: 				{
sl@0: 				TSize maskSize=wsm->iMaskBitmap->SizeInPixels();
sl@0: 				if (maskSize.iWidth<size.iWidth || maskSize.iHeight<size.iHeight)
sl@0: 					OwnerPanic(EWservPanicMaskSize);
sl@0: 				}
sl@0: 			if (size.iWidth>iMaxSize.iWidth)
sl@0: 				iMaxSize.iWidth=size.iWidth;
sl@0: 			if (size.iHeight>iMaxSize.iHeight)
sl@0: 				iMaxSize.iHeight=size.iHeight;
sl@0: 			}
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: void CWsSpriteBase::ConstructL(TUint aFlags, CWsWindow *aWindow)
sl@0: 	{
sl@0: 	// Common part of construct for both sprites and pointer cursors
sl@0: 	iFlags=aFlags;
sl@0: /*
sl@0: 	if (iFlags&ESpriteNoChildClip)
sl@0: 		iLink.iPriority+=ENoChildPriorityBoost;
sl@0: 	if (iFlags&ESpritePointer)
sl@0: 		iLink.iPriority+=EPointerPriorityBoost;
sl@0: */
sl@0: 	iWin=aWindow;
sl@0: 	TCallBack callback(TimerCallBack,this);
sl@0: 	iDeltaTimerEntry.Set(callback);
sl@0: 	iMembers=new(ELeave) CArrayPtrFlat<CWsSpriteMember>(10);
sl@0: 	}
sl@0: 
sl@0: void CWsSpriteBase::AppendMemberL(const TCmdSpriteMember &aCmdSpriteMember)
sl@0: 	{
sl@0: 	CWsSpriteMember *&pwsm=iMembers->ExtendL();
sl@0: 	pwsm=NULL;	// In case new leaves
sl@0: 	pwsm=new(ELeave) CWsSpriteMember();
sl@0: 	// coverity[leave_without_push]
sl@0: 	// pwsm is not owned by the stack and will be deleted by RWsSpriteBase::Close()
sl@0: 	if (pwsm->SetL(aCmdSpriteMember))
sl@0: 		OwnerPanic(EWservPanicBitmap);
sl@0: 	}
sl@0: 
sl@0: void CWsSpriteBase::CompleteL()
sl@0: 	{
sl@0: 	if (iMembers->Count() <= 0)
sl@0: 		{
sl@0: 		if(iWin)
sl@0: 			iWin->OwnerPanic(EWservPanicNoSpriteMember);
sl@0: 		//Not sure if this is a neccessary fall back if iWin is NULL.
sl@0: 		else if(iWin==NULL && iGroupWin)
sl@0: 			iGroupWin->OwnerPanic(EWservPanicNoSpriteMember);
sl@0: 		}
sl@0: 	else
sl@0: 		{
sl@0: 		iMembers->Compress();
sl@0: 		CheckSizesL();
sl@0: 		SetMember(0);
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: void CWsSpriteBase::Activate()
sl@0: 	{
sl@0: 	if (iFlags&ESpriteDisabled)
sl@0: 		{
sl@0: 		iFlags&=~ESpriteDisabled;
sl@0: 		}
sl@0: 	if (iMembers->Count()>1)
sl@0: 		{
sl@0: 		QueueDeltaTimer();
sl@0: 		iDeltaTimer->Activate();
sl@0: 		}
sl@0: 	iFlags|=ESpriteActive;
sl@0: 	if(iWin)	
sl@0: 		iWin->AddSprite(this);
sl@0: 	SetDirty(ETrue);
sl@0: 	Screen()->SpriteManager()->Schedule(this);
sl@0: 	if(IsFloating())
sl@0: 		{		
sl@0: 		Screen()->SpriteManager()->AddFloatingSprite(this);
sl@0: 		if (!Screen()->ChangeTracking())
sl@0: 			ForceRedraw();
sl@0: 		}
sl@0: 	
sl@0: 
sl@0: 	// As custom text cursors are sprites (CWsCustomTextCursor) and can be activated/deactivated
sl@0: 	// on various different windows during their lifetime, when activating
sl@0: 	// a text cursor, we pretend it's just been created to give us the option
sl@0: 	// of specifying a new parent window. Normal sprites (CWsSprite) are
sl@0: 	// treated the same way just for consistency as it does no harm.
sl@0: 	MWsWindowTreeObserver* const windowTreeObserver = Screen()->WindowTreeObserver();
sl@0: 	if (windowTreeObserver)
sl@0: 		{
sl@0: 		windowTreeObserver->NodeCreated(*this, ParentNode());
sl@0: 		windowTreeObserver->NodeExtentChanged(*this, Rect());
sl@0: 		windowTreeObserver->NodeActivated(*this);
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: void CWsSpriteBase::SetMember(TInt aIndex)
sl@0: 	{
sl@0: 	const TSize oldSize = iSize;
sl@0: 	const TPoint oldPos = iPos;
sl@0: 	if(IsFloating())
sl@0: 		{		
sl@0: 		TRect rect(iPos,iSize);
sl@0: 		}
sl@0: 	iCurIndex=aIndex;
sl@0: 	iPos=iBasePos+(*iMembers)[iCurIndex]->iOffset;
sl@0: 	if ((*iMembers)[iCurIndex]->iBitmap)
sl@0: 		iSize=(*iMembers)[iCurIndex]->iBitmap->SizeInPixels();
sl@0: 	else
sl@0: 		iSize.SetSize(0,0);
sl@0: 
sl@0: 	if (iSize.iWidth > iMaxSize.iWidth || iSize.iHeight > iMaxSize.iHeight)
sl@0: 		{
sl@0: 		WS_ASSERT_DEBUG(EFalse, EWsPanicSpriteBitmapSizeChange);
sl@0: 		CheckSizesL();
sl@0: 		}
sl@0: 
sl@0: 	if(oldSize!=iSize || oldPos!=iPos)
sl@0: 		NotifyExtentChanged();
sl@0: 	
sl@0: 	}
sl@0: 
sl@0: void CWsSpriteBase::SetPos(const TPoint &aPos)
sl@0: 	{
sl@0: 	//Non-floating anim whose window is destroyed 
sl@0: 	if (!IsFloating() && iWin==NULL) 
sl@0: 		{ 
sl@0: 		OwnerPanic(EWservPanicWindowDestroyed); 
sl@0: 		} 
sl@0: 
sl@0: 	//Floating anim whose group window is destroyed 
sl@0: 	if (IsFloating()  && iGroupWin==NULL && iWin->WinType() != EWinTypeRoot) 
sl@0: 		{ 
sl@0: 		OwnerPanic(EWservPanicWindowDestroyed); 
sl@0: 		}
sl@0: 	
sl@0: 	if(IsFloating())
sl@0: 		{		
sl@0: 		TRect rect(iPos,iSize);
sl@0: 		}
sl@0: 
sl@0: 	iBasePos=aPos;
sl@0: 	TPoint newPos(iBasePos+(*iMembers)[iCurIndex]->iOffset);
sl@0: 	if (iPos!=newPos)
sl@0: 		{
sl@0: 		if (!Screen()->ChangeTracking())
sl@0: 			//Ensure the region covered by the sprite before as well as after the move gets scheduled for redraw
sl@0: 			Screen()->SpriteManager()->Schedule(this);
sl@0: 		iPos=newPos;
sl@0: 		if (!Screen()->ChangeTracking())
sl@0: 			Screen()->SpriteManager()->Schedule(this);
sl@0: 		NotifyExtentChanged();
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: void CWsSpriteBase::QueueDeltaTimer()
sl@0: 	{
sl@0: 	iDeltaTimer->Queue((*iMembers)[iCurIndex]->iInterval,iDeltaTimerEntry);
sl@0: 	}
sl@0: 
sl@0: void CWsSpriteBase::TimerExpired()
sl@0: 	{
sl@0: 	if (!Screen()->ChangeTracking())
sl@0: 		Screen()->SpriteManager()->Schedule(this);
sl@0: 	SetMember((iCurIndex+1)==iMembers->Count() ? 0 : iCurIndex+1);
sl@0: 	SetDirty(ETrue);
sl@0: 	Screen()->SpriteManager()->Schedule(this);
sl@0: 	QueueDeltaTimer();
sl@0: 	}
sl@0: 
sl@0: TPoint CWsSpriteBase::Pos() const
sl@0: 	{
sl@0: 	if (iGroupWin || iWin==NULL || iWin->WinType() == EWinTypeRoot )
sl@0: 		{
sl@0: 		return(iPos);
sl@0: 		}
sl@0: 	return(iPos+iWin->Origin());
sl@0: 	}
sl@0: 
sl@0: TRect CWsSpriteBase::Rect() const
sl@0: 	{
sl@0: 	TRect rect;
sl@0: 	rect.iTl=Pos();
sl@0: 	rect.iBr=rect.iTl+iSize;
sl@0: 	return(rect);
sl@0: 	}
sl@0: 
sl@0: MWsSprite::TSpriteType CWsSpriteBase::SpriteType() const
sl@0: 	{
sl@0: 	if (IsFloating())
sl@0: 		return EFloatingSprite;
sl@0: 
sl@0: 	TSpriteType spriteType = EWindowSprite; 
sl@0: 	switch(Type())
sl@0: 		{
sl@0: 	case WS_HANDLE_SPRITE:
sl@0: 		spriteType = EWindowSprite;
sl@0: 		break;
sl@0: 	case WS_HANDLE_POINTER_CURSOR:
sl@0: 		spriteType = EPointerCursorSprite;
sl@0: 		break;
sl@0: 	case WS_HANDLE_TEXT_CURSOR:
sl@0: 		spriteType = ECustomTextCursorSprite;
sl@0: 		break;
sl@0: 	default:
sl@0: 		ASSERT(0);
sl@0: 		}
sl@0: 	return spriteType;
sl@0: 	}
sl@0: 
sl@0: void CWsSpriteBase::CommandL(TInt aOpcode, const TAny *aCmdData)
sl@0: 	{
sl@0: 	TWsSpriteCmdUnion pData;
sl@0: 
sl@0: 	pData.any=aCmdData;
sl@0: 	switch(aOpcode)
sl@0: 		{
sl@0: 		case EWsSpriteOpAppendMember:
sl@0: 			AppendMemberL(*pData.SpriteMember);
sl@0: 			break;
sl@0: 		case EWsSpriteOpActivate:
sl@0: 			if(!(iFlags&ESpriteActive))
sl@0: 				CompleteL();
sl@0: 			break;
sl@0: 		case EWsSpriteOpUpdateMember:
sl@0: 			if (pData.UpdateMember->index==iCurIndex)
sl@0: 				{
sl@0: 				SetDirty(ETrue);
sl@0: 				TRect rect(Pos(), iMaxSize);
sl@0: 				Screen()->SpriteManager()->Schedule(this,&rect);
sl@0: 				}
sl@0: 			break;
sl@0: 		case EWsSpriteOpUpdateMember2:
sl@0: 			{
sl@0: 			SetDirty(ETrue);
sl@0: 			Screen()->SpriteManager()->Schedule(this);
sl@0: 			if (pData.UpdateMember->index<0 || pData.UpdateMember->index>=iMembers->Count())
sl@0: 				User::Leave(KErrArgument);
sl@0: 			CWsSpriteMember *member=(*iMembers)[pData.UpdateMember->index];
sl@0: 			TBool ret=EFalse;
sl@0: 			TRAPD(err,ret=UpdateMemberL(member,pData.UpdateMember->data));
sl@0: 			if (err==KErrNone)
sl@0: 				{
sl@0: 				TRAP(err,CheckSizesL());
sl@0: 				SetMember(0);
sl@0: 				}
sl@0: 			Screen()->SpriteManager()->Schedule(this);
sl@0: 			User::LeaveIfError(err);
sl@0: 			if (ret)
sl@0: 				OwnerPanic(EWservPanicBitmap);
sl@0: 			}
sl@0: 			break;
sl@0: 		default:
sl@0: 			OwnerPanic(EWservPanicOpcode);
sl@0: 			break;
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: TBool CWsSpriteBase::CanBeSeen() const
sl@0: 	{
sl@0: 	if(iWin)
sl@0: 		return (!(iFlags&ESpriteDisabled)) && (!iWin->VisibleRegion().IsEmpty());
sl@0: 	else 
sl@0: 		return (!(iFlags&ESpriteDisabled));
sl@0: 	}
sl@0: 
sl@0: void CWsSpriteBase::CalcRedrawRegion(const TRegion& aSourceRegion, TRegion& aTarget) const
sl@0: 	{
sl@0: 	aTarget.Copy(aSourceRegion);
sl@0: 	if (ClipSprite())
sl@0: 		{
sl@0: 		TPoint origin(0,0);
sl@0: 		if(iWin)
sl@0: 			origin = iWin->Origin();
sl@0: 		TRect rect(iBasePos + origin + iClipOffset, iClipSize);
sl@0: 		aTarget.ClipRect(rect);
sl@0: 		}
sl@0: 	aTarget.ClipRect(RootWindow()->Abs());
sl@0: 
sl@0: 	// Only need to draw if the region being redrawn overlaps the sprite
sl@0: 	const TRect spriteRect(Pos(), iSize);
sl@0: 	STACK_REGION spriteRegion;
sl@0: 	spriteRegion.AddRect(spriteRect);
sl@0: 	aTarget.Intersect(spriteRegion);
sl@0: 	spriteRegion.Close();
sl@0: 	}
sl@0: 
sl@0: /**
sl@0:  @pre	CWsSpriteBase::CalcRedrawRegion(TRegion&,TRegion&) should have been called.
sl@0:  @param aRegion Is the region that will definitely be redrawn if dirty. This param should
sl@0:                 be calculated by calling CalcRedrawRegion(TRegion&,TRegion&)
sl@0:  */
sl@0: void CWsSpriteBase::Redraw(MWsGraphicsContext * aGc, const TRegion& aRegion)
sl@0: 	{
sl@0: 	TFlashState currentState=EFlashOn;
sl@0: 	if(IsFlashingEnabled())
sl@0: 		{
sl@0: 		currentState=Screen()->SpriteManager()->CurrentSpriteFlashState(this);
sl@0: 		AnnotateSpriteFlash(*this, currentState==EFlashOn);
sl@0: 		}
sl@0: 
sl@0: 	if(currentState==EFlashOn && (IsDirty() || HasAnimation()) )
sl@0: 		{
sl@0: 		const TRegion * pr = &aRegion;
sl@0: 
sl@0: 		if (pr->CheckError())
sl@0: 			{
sl@0: 			if(iWin)
sl@0: 				{
sl@0: 				if (Screen()->ChangeTracking())
sl@0: 					pr = &iWin->WindowArea();
sl@0: 				else
sl@0: 					pr = &iWin->VisibleRegion();
sl@0: 				}
sl@0: 			else
sl@0: 				pr = &RootWindow()->WindowArea();
sl@0: 			}
sl@0: 
sl@0: 		if (!pr->IsEmpty())
sl@0: 			{
sl@0: 			CWsSpriteMember *member=(*iMembers)[iCurIndex];
sl@0: 			if (member->iBitmap)
sl@0: 				{
sl@0: 				aGc->SetClippingRegion(*pr);
sl@0: 
sl@0: 				// Calculate which piece (rect) of the bitmap needs to be drawn
sl@0: 				const TRect redrawRect = pr->BoundingRect();
sl@0: 				TRect bitmapRect(Pos(), iSize); // sprite rect relative to screen
sl@0: 				bitmapRect.Intersection(redrawRect);
sl@0: 				bitmapRect.Move(-Pos()); // adjust relative to bitmap origin
sl@0: 
sl@0: 				if (member->iMaskBitmap)
sl@0: 					aGc->BitBltMasked(Pos() + bitmapRect.iTl, *member->iBitmap, bitmapRect, *member->iMaskBitmap, member->iInvertMask);
sl@0: 				else
sl@0: 					{
sl@0: 					aGc->SetDrawMode(BitGdiToMWsGraphicsContextMappings::LossyConvert(member->iDrawMode));
sl@0: 					aGc->BitBlt(Pos() + bitmapRect.iTl, *member->iBitmap, bitmapRect);
sl@0: 					aGc->SetDrawMode(MWsGraphicsContext::EDrawModePEN);
sl@0: 					}
sl@0: 				aGc->ResetClippingRegion();
sl@0: 				}
sl@0: 			}
sl@0: 		if (Screen()->ChangeTracking())
sl@0: 			SetDirty(EFalse);
sl@0: 		
sl@0: 		}
sl@0: 		
sl@0: 	//Flashing sprites need to reschedule themselves after drawing (unless they have
sl@0: 	//an animation, because for animating sprites the rescheduling is done in CWsAnim).
sl@0: 	if(IsFlashingEnabled() && !HasAnimation())
sl@0: 		Screen()->SpriteManager()->Schedule(this);
sl@0: 	}
sl@0: 
sl@0: TBool CWsSpriteBase::IsActivated() const
sl@0: 	{
sl@0: 	return(iFlags&ESpriteActive);
sl@0: 	}
sl@0: 
sl@0: void CWsSpriteBase::SendState(MWsWindowTreeObserver& aWindowTreeObserver) const
sl@0: 	{
sl@0: 	if(iNext)
sl@0: 		iNext->SendState(aWindowTreeObserver);
sl@0: 	
sl@0: 	if(IsActivated())
sl@0: 		{
sl@0: 		//Sprite NodeCreated must only be sent if activated
sl@0: 		aWindowTreeObserver.NodeCreated(*this, ParentNode());
sl@0: 		aWindowTreeObserver.NodeExtentChanged(*this, Rect());
sl@0: 		aWindowTreeObserver.NodeActivated(*this);
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: /** @see MWsWindowTreeNode */
sl@0: MWsWindowTreeNode::TType CWsSpriteBase::NodeType() const
sl@0: 	{
sl@0: 	return MWsWindowTreeNode::EWinTreeNodeSprite; 
sl@0: 	}
sl@0: 
sl@0: /** @see MWsWindowTreeNode */
sl@0: const MWsWindow* CWsSpriteBase::Window() const
sl@0: 	{
sl@0: 	return NULL;
sl@0: 	}
sl@0: 
sl@0: /** @see MWsWindowTreeNode */
sl@0: const MWsSprite* CWsSpriteBase::Sprite() const
sl@0: 	{
sl@0: 	return this;
sl@0: 	}
sl@0: 
sl@0: /** @see MWsWindowTreeNode */
sl@0: const MWsStandardTextCursor* CWsSpriteBase::StandardTextCursor() const
sl@0: 	{
sl@0: 	return NULL;
sl@0: 	}
sl@0: 
sl@0: /** @see MWsWindowTreeNode */
sl@0: const MWsWindowGroup* CWsSpriteBase::WindowGroup() const
sl@0: 	{
sl@0: 	if(iWin)
sl@0: 		return iWin->WindowGroup();
sl@0: 	else if (iGroupWin) 
sl@0: 		return static_cast<MWsWindowGroup*>(iGroupWin); //floating Sprite
sl@0: 	
sl@0: 	WS_ASSERT_DEBUG(EFalse,EWsPanicInvalidOperation);
sl@0: 	return NULL;
sl@0: 	}
sl@0: 
sl@0: /** @see MWsWindowTreeNode */
sl@0: const MWsWindowTreeNode* CWsSpriteBase::ParentNode() const
sl@0: 	{
sl@0: 	if (iWin)
sl@0: 		return iWin;
sl@0: 	else if (iGroupWin)
sl@0: 		return iGroupWin->BaseParent(); //floating Sprite, will return the rootwin
sl@0: 	
sl@0: 	WS_ASSERT_DEBUG(EFalse,EWsPanicInvalidOperation);
sl@0: 	return NULL;
sl@0: 	}
sl@0: 
sl@0: void CWsSpriteBase::NotifyExtentChanged() const
sl@0: 	{
sl@0: 	if (Screen())
sl@0: 		{
sl@0: 		MWsWindowTreeObserver* windowTreeObserver = Screen()->WindowTreeObserver();
sl@0: 		if (windowTreeObserver && iFlags&ESpriteActive)
sl@0: 			{
sl@0: 			windowTreeObserver->NodeExtentChanged(*this, Rect());
sl@0: 			}
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: //
sl@0: // CWsSprite
sl@0: //
sl@0: 
sl@0: CWsSprite::CWsSprite(CWsClient *owner) : CWsSpriteBase(owner,WS_HANDLE_SPRITE)
sl@0: 	{
sl@0: 	}
sl@0: 
sl@0: CWsSprite::~CWsSprite()
sl@0: 	{
sl@0: 	if (!IsFloating() && IsActivated() && iWin && iWin->IsVisible() && !Screen()->ChangeTracking())
sl@0: 	    ForceRedraw();
sl@0: 	if (iAnim)
sl@0: 		CWsAnim::CloseAnim(iAnim);
sl@0: 	}
sl@0: 
sl@0: void CWsSprite::ConstructL(const TWsClCmdCreateSprite &aParams)
sl@0: 	{
sl@0: 	NewObjL();
sl@0: 	CWsWindowBase *win;
sl@0: 	WsOwner()->HandleToWindow(aParams.window,&win);
sl@0: 	WS_ASSERT_DEBUG(win,EWsPanicWindowNull);
sl@0: 	if (win->WinType()==EWinTypeGroup)
sl@0: 		{
sl@0: 		//If a sprite is attached to a group window it is floating. 
sl@0: 		//Floating sprite drawing is performed by the sprite manager.
sl@0: 		iGroupWin=(CWsWindowGroup *)win;
sl@0: 		win=NULL; //Floating sprites aren't associated with any particular window.
sl@0: 		SetIsFloating(ETrue);
sl@0: 		//In case the GroupWin is destroyed before the sprite it needs to call DisconnectGroupWin 
sl@0: 		iGroupWin->AddSprite(this);
sl@0: 		}
sl@0: 	CWsSpriteBase::ConstructL(aParams.flags&ESpriteNonSystemFlags,(CWsWindow *)win);
sl@0: 	iBasePos=aParams.pos;
sl@0: 	}
sl@0: 
sl@0: void CWsSprite::CompleteL()
sl@0: 	{
sl@0: 	CWsSpriteBase::CompleteL();
sl@0: 	Activate();
sl@0: 	}
sl@0: 
sl@0: void CWsSprite::CommandL(TInt aOpcode, const TAny *aCmdData)
sl@0: 	{
sl@0: 	TWsSpriteCmdUnion pData;
sl@0: 	pData.any=aCmdData;
sl@0: 	switch(aOpcode)
sl@0: 		{
sl@0: 		case EWsSpriteOpSetPosition:
sl@0: 			SetPos(*pData.Point);
sl@0: 			break;
sl@0: 		case EWsSpriteOpFree:
sl@0: 			{
sl@0: 			delete this;
sl@0: 			break;
sl@0: 			}
sl@0: 		default:
sl@0: 			CWsSpriteBase::CommandL(aOpcode, aCmdData);
sl@0: 			break;
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: /**
sl@0: @see MAnimSpriteFunctions::UpdateMember
sl@0: @param aFullUpdate	Not used. Wserv2 always do full back to front rendering, so there is no distinction between changes needing aFullUpdate or not 
sl@0:  */
sl@0: void CWsSprite::Update(TInt aMember,TRect aRect,TBool /*aFullUpdate*/) 
sl@0: 	{
sl@0: 	if (iCurIndex!=aMember)
sl@0: 		return;
sl@0: 	aRect.Move(Pos());
sl@0: 	aRect.Intersection(iScreen->CurrentScreenSize());
sl@0: 	SetDirty(ETrue);
sl@0: 	Screen()->SpriteManager()->Schedule(this, &aRect);
sl@0: 	}
sl@0: 
sl@0: //
sl@0: // CWsPointerCursor
sl@0: //
sl@0: 
sl@0: CWsPointerCursor::CWsPointerCursor(CWsClient *owner) : CWsSpriteBase(owner,WS_HANDLE_POINTER_CURSOR)
sl@0: 	{
sl@0: 	}
sl@0: 
sl@0: void CWsPointerCursor::CloseObject()
sl@0: 	{
sl@0: 	RemoveFromIndex();
sl@0: 	Close();
sl@0: 	}
sl@0: 
sl@0: void CWsPointerCursor::Close()
sl@0: 	{
sl@0: 	WS_ASSERT_DEBUG(iAccessCount>0, EWsPanicPointerCursorAccessCount);
sl@0: 	if (--iAccessCount==0)
sl@0: 		delete this;
sl@0: 	}
sl@0: 
sl@0: void CWsPointerCursor::Open()
sl@0: 	{
sl@0: 	iAccessCount++;
sl@0: 	}
sl@0: 
sl@0: CWsPointerCursor::~CWsPointerCursor()
sl@0: 	{
sl@0: 	WS_ASSERT_DEBUG(iAccessCount==0, EWsPanicPointerCursorAccessCount);
sl@0: 	}
sl@0: 
sl@0: void CWsPointerCursor::ConstructL(const TWsClCmdCreatePointerCursor &aParams)
sl@0: 	{
sl@0: 	NewObjL();
sl@0: 	SetIsFloating(ETrue);
sl@0: 	CWsSpriteBase::ConstructL(ESpriteNoShadows|ESpriteNoChildClip|ESpritePointer|(aParams.flags&ESpriteNonSystemFlags),RootWindow());
sl@0: 	Open();
sl@0: 	}
sl@0: 
sl@0: void CWsPointerCursor::CommandL(TInt aOpcode, const TAny *aCmdData)
sl@0: 	{
sl@0: 	switch(aOpcode)
sl@0: 		{
sl@0: 		case EWsSpriteOpFree:
sl@0: 			CloseObject();
sl@0: 			break;
sl@0: 		default:
sl@0: 			CWsSpriteBase::CommandL(aOpcode, aCmdData);
sl@0: 			break;
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: //
sl@0: // CWsCustomTextCursor
sl@0: //
sl@0: 
sl@0: CWsCustomTextCursor::CWsCustomTextCursor (CWsClient *aOwner, RWsSession::TCustomTextCursorAlignment aAlignment) 
sl@0: : CWsSpriteBase(aOwner, WS_HANDLE_TEXT_CURSOR), iAlignment(aAlignment)
sl@0: 	{
sl@0: 	}
sl@0: 
sl@0: CWsCustomTextCursor::~CWsCustomTextCursor()
sl@0: 	{
sl@0: 	}
sl@0: 
sl@0: void CWsCustomTextCursor::ConstructL(TInt aFlags)
sl@0: 	{
sl@0: 	NewObjL();
sl@0: 	CWsSpriteBase::ConstructL(ESpriteNoShadows|ESpriteNoChildClip|ESpritePointer|(aFlags&ESpriteNonSystemFlags), NULL);
sl@0: 	}
sl@0: 
sl@0: void CWsCustomTextCursor::CompleteL(CWsWindow *aWin, TBool aFlash, TBool aClipSprite, const TPoint& aClipOffset, const TSize& aClipSize)
sl@0: 	{
sl@0: 	iWin = aWin;
sl@0: 	iFlags = aFlash ? iFlags | ESpriteFlash : iFlags & ~ESpriteFlash;
sl@0: 	SetClipSprite(aClipSprite);
sl@0: 	iClipOffset = aClipOffset;
sl@0: 	iClipSize = aClipSize;
sl@0: 	CWsSpriteBase::CompleteL();
sl@0: 	}
sl@0: 
sl@0: // Use SetPositionNoRedraw instead of SetPos when you just want to update
sl@0: // the custom text cursor position without redrawing it
sl@0: void CWsCustomTextCursor::SetPositionNoRedraw(const TPoint& aPos)
sl@0: 	{
sl@0: 	iBasePos = aPos;
sl@0: 	TPoint newPos(iBasePos+(*iMembers)[iCurIndex]->iOffset);
sl@0: 	iPos=newPos;
sl@0: 	}
sl@0: 
sl@0: void CWsCustomTextCursor::CommandL(TInt aOpcode, const TAny *aCmdData)
sl@0: 	{
sl@0: 	switch(aOpcode)
sl@0: 		{
sl@0: 		case EWsSpriteOpFree:
sl@0: 			// CWsCustomTextCursor objects are owned by the text cursor list.
sl@0: 			// They are not deleted when the client closes it's R class.
sl@0: 			RemoveFromIndex();
sl@0: 			break;
sl@0: 		default:
sl@0: 			CWsSpriteBase::CommandL(aOpcode, aCmdData);
sl@0: 			break;
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: //
sl@0: // CWsDeltaTimer, nicked from CDeltaTimer and tweaked so it doesn't re-activate			//
sl@0: // the timers until RunL has finished running all ready timers.							//
sl@0: //																						//
sl@0: // This is to stop a problem in Wserv where sprites could hog 100% CPU if the time		//
sl@0: // it took to process them was longer than the time of the timer queued when the first	//
sl@0: // sprite was updated																	//
sl@0: //
sl@0: 
sl@0: CWsDeltaTimer* CWsDeltaTimer::NewL(TInt aPriority)
sl@0: 	{
sl@0: 	CWsDeltaTimer* wsdt=new(ELeave) CWsDeltaTimer(aPriority);
sl@0: 	CleanupStack::PushL(wsdt);
sl@0: 	User::LeaveIfError(wsdt->iTimer.CreateLocal());
sl@0: 	CActiveScheduler::Add(wsdt);
sl@0: 	CleanupStack::Pop(wsdt);
sl@0: 	return(wsdt);
sl@0: 	}
sl@0: 
sl@0: CWsDeltaTimer::CWsDeltaTimer(TInt aPriority) : CActive(aPriority),iQueue(_FOFF(TWsDeltaTimerEntry,iLink))
sl@0: 	{
sl@0: 	}
sl@0: 
sl@0: CWsDeltaTimer::~CWsDeltaTimer()
sl@0: 	{
sl@0: 	Cancel();
sl@0: 	while(iQueue.RemoveFirst()!=NULL)
sl@0: 		{}
sl@0: 	}
sl@0: 
sl@0: void CWsDeltaTimer::Queue(TTimeIntervalMicroSeconds32 aTimeInMicroSeconds,TWsDeltaTimerEntry& anEntry)
sl@0: 	{
sl@0: 	TInt intervals=aTimeInMicroSeconds.Int()/CWsDeltaTimerGranularity;
sl@0: 	if (intervals==0)
sl@0: 		intervals=1;
sl@0: 	iQueue.Add(anEntry,intervals);
sl@0: 	}
sl@0: 
sl@0: void CWsDeltaTimer::Activate()
sl@0: 	{
sl@0: 	// Queue a request on the timer.
sl@0: 	// The timer runs every tenth of a second and decremented the delta of the head of the queue.
sl@0: 	if (IsActive())
sl@0: 		return;
sl@0: 	if (!iQueue.IsEmpty())
sl@0: 		{
sl@0: 		SetActive();
sl@0: 		iTimer.After(iStatus,CWsDeltaTimerGranularity-1); // -1 to compensate for +1 in kernel!
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: void CWsDeltaTimer::RunL()
sl@0: 	{
sl@0: 	// Call all zero delta callbacks
sl@0: 	iQueue.CountDown();
sl@0: 	TWsDeltaTimerEntry* ent=iQueue.RemoveFirst();
sl@0: 	while (ent)
sl@0: 		{       
sl@0: 		ent->iCallBack.CallBack();
sl@0: 		ent=iQueue.RemoveFirst();
sl@0: 		}
sl@0: 	Activate();
sl@0: 	}
sl@0: 
sl@0: void CWsDeltaTimer::DoCancel()
sl@0: 	{
sl@0: 	iTimer.Cancel();
sl@0: 	}
sl@0: 
sl@0: void CWsDeltaTimer::Remove(TWsDeltaTimerEntry& anEntry)
sl@0: 	{
sl@0: 	if (anEntry.IsPending())
sl@0: 		{
sl@0: 		iQueue.Remove(anEntry);
sl@0: 		Activate();
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: 
sl@0: //
sl@0: // CWsSpriteManager -handles floating and flashing sprites including flashing custom text cursors. i.e. cursors
sl@0: //					that have an associated sprite.
sl@0: 
sl@0: 
sl@0: CWsSpriteManager::CWsSpriteManager()
sl@0: 	{
sl@0: 	}
sl@0: 	
sl@0: CWsSpriteManager* CWsSpriteManager::NewL()
sl@0: 	{
sl@0: 	CWsSpriteManager* self = new (ELeave) CWsSpriteManager();
sl@0: 	CleanupStack::PushL(self);
sl@0: 	self->ConstructL();
sl@0: 	CleanupStack::Pop(self);
sl@0: 	return self;	
sl@0: 	}
sl@0: 
sl@0: void CWsSpriteManager::ConstructL()
sl@0: 	{
sl@0: 	}
sl@0: 
sl@0: CWsSpriteManager::~CWsSpriteManager()
sl@0:     {
sl@0:     iFloatingSprites.ResetAndDestroy();
sl@0:     }
sl@0: void CWsSpriteManager::AddFloatingSprite(CWsSpriteBase* aSprite)
sl@0: 	{
sl@0: 	iFloatingSprites.Append(aSprite);
sl@0: 	}
sl@0: 	
sl@0: void CWsSpriteManager::RemoveFloatingSprite(CWsSpriteBase* aSprite)
sl@0: 	{
sl@0: #ifdef _DEBUG 
sl@0: 	TInt removed = 0; 
sl@0: #endif
sl@0: 	for (TInt i=0 ; i<iFloatingSprites.Count() ; i++)
sl@0: 		{
sl@0: 		if(iFloatingSprites[i]==aSprite)
sl@0: 			{
sl@0: 			//Just remove the sprite don't delete it. the manager doesn't have ownership
sl@0: 			iFloatingSprites.Remove(i);
sl@0: #ifdef _DEBUG
sl@0: 			removed++;
sl@0: #else
sl@0: 			break;
sl@0: #endif
sl@0: 			}
sl@0: 		}
sl@0: 	WS_ASSERT_DEBUG(removed==1,EWsPanicFloatingSprite);
sl@0: 	}
sl@0: 
sl@0: void CWsSpriteManager::DrawFloatingSprites(MWsGraphicsContext* aGc,const TRegion& aRegion)
sl@0: 	{
sl@0: 	if (iFloatingSprites.Count() == 0)
sl@0: 		return; //avoid sending events unless necessary
sl@0: 
sl@0: 	for (TInt i = iFloatingSprites.Count() - 1; i >= 0 ; i--)
sl@0: 		{
sl@0: 		STACK_REGION redrawRegion;
sl@0: 		CWsSpriteBase* sprite = iFloatingSprites[i];
sl@0: 		sprite->CalcRedrawRegion(aRegion, redrawRegion);
sl@0: 		if(redrawRegion.CheckError() || !redrawRegion.IsEmpty())
sl@0: 			{
sl@0: 			if (sprite->IsFlashingEnabled() || sprite->IsDirty() || sprite->HasAnimation())
sl@0: 				{
sl@0: 				AnnotateSpriteRedrawStart(*sprite, redrawRegion);
sl@0: 				
sl@0: 				if(sprite->HasAnimation())
sl@0: 					{
sl@0: 					CWsAnim* anim = static_cast<CWsSprite*>(sprite)->iAnim;
sl@0: 					ASSERT(anim);
sl@0: 					
sl@0: 					TRAPD(err, anim->AnimateSpriteAnimL(sprite->Screen()->Now()));
sl@0: 					if(err!=KErrNone)
sl@0: 						{
sl@0: 						AnnotateSpriteRedrawEnd(*sprite);						
sl@0: 						anim->Panic(EWservPanicAnimLeave);
sl@0: 						continue;
sl@0: 						}
sl@0: 					}
sl@0: 				
sl@0: 				aGc->Reset();
sl@0: 				sprite->Redraw(aGc, redrawRegion);
sl@0: 				
sl@0: 				AnnotateSpriteRedrawEnd(*sprite);
sl@0: 				}
sl@0: 			}
sl@0: 		redrawRegion.Close();
sl@0: 		}
sl@0: 	}
sl@0: 	
sl@0: void CWsSpriteManager::Schedule(CWsSpriteBase* aSprite, TRect* aRect)
sl@0: 	{
sl@0: 	if (aRect != NULL && aRect->IsEmpty())
sl@0: 		return;
sl@0: 
sl@0: 	TRect rect = aSprite->Rect();
sl@0: 	if (aRect)
sl@0: 		rect.Intersection(*aRect);
sl@0: 		
sl@0: 	const TAnimType type = aSprite->Win() ? EWindowSprite : EFloatingSprite;
sl@0: 	
sl@0: 	if(aSprite->IsFlashingEnabled())
sl@0: 		{
sl@0: 		aSprite->Screen()->ScheduleAnimation(type, rect,NextSpriteFlashStateChange(aSprite),0,0, aSprite->Win());
sl@0: 		}
sl@0: 	else
sl@0: 		{
sl@0: 		//Scheduling an animation "now" means it will take place at next animation which might 
sl@0: 		//be the full animation grace period into the future (see KAnimationGrace in server.cpp)
sl@0: 		aSprite->Screen()->ScheduleAnimation(type, rect,0,0,0, aSprite->Win());
sl@0: 		}
sl@0: 	}	
sl@0: 
sl@0: // Sprite flashing is clamped to half second intervals in relation to the global time.
sl@0: // For the first half of each second all sprites have the EFlashOn state (visible)
sl@0: // For the second half of each second all sprites have the EFlashOff state (not visible) 
sl@0: TTimeIntervalMicroSeconds CWsSpriteManager::NextSpriteFlashStateChange(const CWsSpriteBase* aSprite) const
sl@0: 	{
sl@0: 	const TTimeIntervalMicroSeconds remainder = aSprite->Screen()->Now().DateTime().MicroSecond();
sl@0: 	return CalculateTimeToNextFlash(remainder);
sl@0: 	}
sl@0: 
sl@0: TTimeIntervalMicroSeconds CWsSpriteManager::NextCursorFlashStateChange() const
sl@0: 	{
sl@0: 	const TTimeIntervalMicroSeconds remainder = CWsTop::CurrentFocusScreen()->Now().DateTime().MicroSecond();
sl@0: 	return CalculateTimeToNextFlash(remainder);
sl@0: 	}
sl@0: 
sl@0: TTimeIntervalMicroSeconds CWsSpriteManager::CalculateTimeToNextFlash(TTimeIntervalMicroSeconds aTime) const
sl@0: 	{
sl@0: 	TInt64 nextStateChange;
sl@0: 	if(aTime<KFlashHalfSecond)
sl@0: 		nextStateChange=KFlashHalfSecond-aTime.Int64();
sl@0: 	else
sl@0: 		nextStateChange=KFlashHalfSecond - (aTime.Int64() - KFlashHalfSecond);
sl@0: 	ASSERT(nextStateChange > 0);
sl@0: 	return TTimeIntervalMicroSeconds(nextStateChange);
sl@0: 	}	
sl@0: 	
sl@0: TFlashState CWsSpriteManager::CurrentSpriteFlashState(const CWsSpriteBase* aSprite) const
sl@0: 	{
sl@0: 	return (aSprite->Screen()->Now().DateTime().MicroSecond()<KFlashHalfSecond)?EFlashOn:EFlashOff;
sl@0: 	}
sl@0: 
sl@0: void CWsSpriteManager::CalcFloatingSpriteRgn( TRegion& aResultRgn, const TRect& aDefaultRect )
sl@0: 	{
sl@0: 	aResultRgn.Clear();
sl@0: 	for (TInt i=0 ; i<iFloatingSprites.Count() && !aResultRgn.CheckError(); i++)
sl@0: 		{
sl@0: 		CWsSpriteBase* sprite = iFloatingSprites[i]; 
sl@0: 		if ( sprite->CanBeSeen() && ( sprite->IsActive() || sprite->IsActivated() ) )
sl@0: 			{
sl@0: 			aResultRgn.AddRect( sprite->Rect() );
sl@0: 			}
sl@0: 		}
sl@0: 	aResultRgn.Tidy();
sl@0: 	if ( aResultRgn.CheckError() && iFloatingSprites.Count() > 0 )
sl@0: 		{
sl@0: 		aResultRgn.Clear();
sl@0: 		aResultRgn.AddRect( aDefaultRect );
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: void CWsSpriteManager::SendState(MWsWindowTreeObserver& aWindowTreeObserver) const
sl@0: 	{
sl@0: 	for(TInt i=iFloatingSprites.Count()-1; i>=0; i--)
sl@0: 		{
sl@0: 		CWsSpriteBase* sprite = iFloatingSprites[i];
sl@0: 		sprite->SendState(aWindowTreeObserver);
sl@0: 		}
sl@0: 	}