// EIKCLOCK.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include <clock.h>
#include <barsread.h>
#include <eikenv.h>
#include <eiktxtut.h>
#include <eikfont.h>
#include <eikclock.pan>
#include <eikclock.hrh>
#include <eikclock.h>

#if defined(DO_PROFILING)
#include <eikdef.h>
#define PROFILE_ID PROFILE_POINT_EIKON_CLOCK
#endif

// constants controlling minimum size of digital clocks

enum
	{
	EGapAboveText=1,
	EGapBelowText=0,
	EGapLeftOfText=2,
	EGapRightOfText=2
	};

// local and global functions

LOCAL_C void ClockDestroy(TAny* aClock)
	{
	((RClock*)aClock)->Destroy();
	}

GLDEF_C void Panic(TEikClockPanic aPanic)
	{
	User::Panic(_L("EIKON-CLOCK"), aPanic);
	}

// CEikLocaleConformantClock

EXPORT_C CEikLocaleConformantClock::CEikLocaleConformantClock(MEikClockContainer& aContainer, RAnimDll& aAnimDll, const RWindowBase& aWindow, TInt aType, TBool aTypeNeverChanges)
	:CActive(EPriorityStandard),
	 iContainer(aContainer),
	 iAnimDll(aAnimDll),
	 iWindow(aWindow),
	 iDrawBackground(ETrue)
	{
	__DECLARE_NAME(_S("CEikLocaleConformantClock"));
	CActiveScheduler::Add(this);
	if (aType&EEikUsesDefaultSystemColors)
		{
		iFlags|=EUsesDefaultSystemColors;
		aType &= ~EEikUsesDefaultSystemColors;
		}

	switch (aType)
		{
	case EEikLocaleConformantClockTypeChangesFormatAccordingToLocale:
		__ASSERT_ALWAYS(!aTypeNeverChanges, Panic(EEikPanicClockTypeMustBeAllowedToChange));
		break;
	case EEikLocaleConformantClockTypeDigital:
		iFormat=EClockDigital;
		break;
	case EEikLocaleConformantClockTypeAnalog:
		iFormat=EClockAnalog;
		break;
	default:
		Panic(EEikPanicClockBadFormat1);
		break;
		}
	if (aType!=EEikLocaleConformantClockTypeChangesFormatAccordingToLocale)
		iFlags|=EIgnoresLocaleClockFormat;
	if (aTypeNeverChanges)
		iFlags|=ETypeNeverChanges;

	}

EXPORT_C void CEikLocaleConformantClock::ConstructL()
	{
	__ASSERT_ALWAYS(iClock==NULL, Panic(EEikPanicClockHasAlreadyBeenConstructed));
	User::LeaveIfError(iChangeNotifier.Create());
	TLocale locale;
	if (~iFlags&EIgnoresLocaleUniversalTimeOffsetAndHomeDaylightSavingZone)
		{
		iUniversalTimeOffset=locale.UniversalTimeOffset();
		iDaylightSavingZone=locale.HomeDaylightSavingZone();
		}
	iCompleteUniversalTimeOffset=CalculateCompleteUniversalTimeOffset();
	TClockFormat format=(iFlags&EIgnoresLocaleClockFormat)? iFormat: locale.ClockFormat();
	if (~iFlags&ETypeNeverChanges)
		{
		// first set it to the other format to ensure iMinimumSize is expanded
		TClockFormat otherFormat=EClockDigital; // initialised to avoid gcc warning
		switch (format)
			{
		case EClockAnalog:
			break;
		case EClockDigital:
			otherFormat=EClockAnalog;
			break;
	#if defined(_DEBUG)
		default:
			Panic(EEikPanicClockBadFormat2);
			break;
	#endif
			}
		SetFormatL(otherFormat, EFalse);
		}
	SetFormatL(format);
	LogonChangeNotifier();
	}

EXPORT_C void CEikLocaleConformantClock::ConstructL(TTimeIntervalSeconds aUniversalTimeOffset, TDaylightSavingZone aDaylightSavingZone)
	{
	iUniversalTimeOffset=aUniversalTimeOffset;
	iDaylightSavingZone=aDaylightSavingZone;
	iFlags|=EIgnoresLocaleUniversalTimeOffsetAndHomeDaylightSavingZone;
	ConstructL();
	}

EXPORT_C CEikLocaleConformantClock::~CEikLocaleConformantClock()
	{
	Cancel();
	iChangeNotifier.Close();
	if (iClock)
		iClock->Destroy();
	}

EXPORT_C void CEikLocaleConformantClock::ChangeFormatL(TClockFormat aFormat)
	{
	__ASSERT_ALWAYS(iClock && (iFlags&EIgnoresLocaleClockFormat) && (~iFlags&ETypeNeverChanges), Panic(EEikPanicClockBadChangeFormatCall));
	if (iFormat!=aFormat)
		{
		iMinimumSize.SetSize(0, 0);
		SetFormatL(aFormat);
		}
	}

EXPORT_C TClockFormat CEikLocaleConformantClock::Format() const
	{
	return iFormat;
	}

EXPORT_C void CEikLocaleConformantClock::ChangeUniversalTimeOffsetAndDaylightSavingZone(TTimeIntervalSeconds aUniversalTimeOffset, TDaylightSavingZone aDaylightSavingZone)
	{
	__ASSERT_ALWAYS(iClock && (iFlags&EIgnoresLocaleUniversalTimeOffsetAndHomeDaylightSavingZone), Panic(EEikPanicClockBadChangeUniversalTimeOffsetAndDaylightSavingZoneCall));
	iUniversalTimeOffset=aUniversalTimeOffset;
	iDaylightSavingZone=aDaylightSavingZone;
	if (CompleteUniversalTimeOffsetHasChanged())
		iClock->SetUniversalTimeOffset(iCompleteUniversalTimeOffset);
	}

EXPORT_C void CEikLocaleConformantClock::GetUniversalTimeOffsetAndDaylightSavingZone(TTimeIntervalSeconds& aUniversalTimeOffset, TDaylightSavingZone& aDaylightSavingZone) const
	{
	aUniversalTimeOffset=iUniversalTimeOffset;
	aDaylightSavingZone=iDaylightSavingZone;
	}

EXPORT_C TTimeIntervalSeconds CEikLocaleConformantClock::CompleteUniversalTimeOffset() const
	{
	return iCompleteUniversalTimeOffset;
	}

EXPORT_C void CEikLocaleConformantClock::SetVisible(TBool aVisible)
	{
	iClock->SetVisible(aVisible);
	}

EXPORT_C void CEikLocaleConformantClock::SetPositionAndSize(const TPoint& aPosition, const TSize& aSize)
	{
	TRect innerRect=iBorder.InnerRect(TRect(aPosition, aSize));
	iClock->SetPositionAndSize(innerRect.iTl, innerRect.Size());
	iPosition=aPosition;
	iSize=aSize;
	}

EXPORT_C void CEikLocaleConformantClock::SetPosition(const TPoint& aPosition)
	{
	TMargins margins=iBorder.Margins();
	iClock->SetPosition(TPoint(aPosition.iX+margins.iLeft, aPosition.iY+margins.iTop));
	iPosition=aPosition;
	}

EXPORT_C void CEikLocaleConformantClock::SetSize(const TSize& aSize)
	{
	iClock->SetSize(aSize-iBorder.SizeDelta());
	iSize=aSize;
	}

EXPORT_C void CEikLocaleConformantClock::Draw(CWindowGc& aGc) const
	{
	if (iContainer.ClockIsVisible(*this))
		{
		TRect rect(iPosition, iSize);
		iBorder.Draw(aGc, rect);
		if (iDrawBackground)
			{
			aGc.SetPenStyle(CGraphicsContext::ENullPen);
			aGc.DrawRect(iBorder.InnerRect(rect));
			aGc.SetPenStyle(CGraphicsContext::ESolidPen);
			}
		}
	}

EXPORT_C void CEikLocaleConformantClock::RedrawAll(CWindowGc& aGc) const
	{
	if (iContainer.ClockIsVisible(*this))
		{
		TRect rect(iPosition, iSize);
		iBorder.Draw(aGc, rect);
		if (iDrawBackground)
			{
			aGc.SetPenStyle(CGraphicsContext::ENullPen);
			aGc.DrawRect(iBorder.InnerRect(rect));
			aGc.SetPenStyle(CGraphicsContext::ESolidPen);
			}
		iClock->Draw();
		}
	}

EXPORT_C TBool CEikLocaleConformantClock::HasBorder() const
	{
	return iBorder.HasBorder();
	}

EXPORT_C TBool CEikLocaleConformantClock::IgnoresLocaleClockFormat() const
	{
	return iFlags&EIgnoresLocaleClockFormat;
	}

EXPORT_C TBool CEikLocaleConformantClock::TypeNeverChanges() const
	{
	return iFlags&ETypeNeverChanges;
	}

EXPORT_C TSize CEikLocaleConformantClock::MinimumSize() const
	{
	return iMinimumSize;
	}

EXPORT_C TRect CEikLocaleConformantClock::Rect() const
	{
	return TRect(iPosition, iSize);
	}

EXPORT_C TInt CEikLocaleConformantClock::PotentialWidthOfTextBlock(const TDesC& aTextBlock, const SEikLocaleAndFontData& aLocaleAndfontData)
	{
	TInt potentialWidthOfTextBlock=0;
	TBool fixed=EFalse;
	for (TLex textBlock=aTextBlock; !textBlock.Eos(); )
		{
		TChar character=textBlock.Get();
		TBool abbreviated=EFalse;
		const TInt KUnspecifiedLocalePos=-1;
		TInt localePos=KUnspecifiedLocalePos;
		if (character=='%')
			character=textBlock.Get();
		else
			{
			potentialWidthOfTextBlock+=WidthOfCharacter(*aLocaleAndfontData.iFont, character);
			continue;
			}
		switch (character)
			{
		case '*':
			abbreviated=ETrue;
			break;
		case '+':
			localePos=ELocaleAfter;
			break;
		case '-':
			localePos=ELocaleBefore;
			break;
		default:
			goto startOfMainSwitch;
			}
		character=textBlock.Get();
		startOfMainSwitch:
		switch (character)
			{
		case ':': // local time separator
		case '/': // local date separator
			potentialWidthOfTextBlock+=aLocaleAndfontData.iWidthOfWidestCharacter;
			textBlock.Get(); // ignore the next character as it specifies which separator to use
			break;
		case '1': // 1st element of date, locale order
		case '2': // 2nd element of date, locale order
		case '3': // 3rd element of date, locale order
			{
			TDateFormat dateFormat=aLocaleAndfontData.iLocale->DateFormat();
			if (((character=='1') && (dateFormat==EDateJapanese)) || ((character=='3') && (dateFormat!=EDateJapanese)))
				goto year;
			}
			// N.B. fall through
		case '4': // 1st element of date (no year), locale order
		case '5': // 2nd element of date (no year), locale order
		case 'D': // day in date
		case 'H': // hour in 24 hour time format
		case 'I': // hour in 12 hour time format
		case 'J': // default time format for hour
		case 'M': // month as a number
		case 'S': // seconds
		case 'T': // minutes
		case 'W': // week number in year
			potentialWidthOfTextBlock+=aLocaleAndfontData.iWidthOfWidestDigit*2;
			break;
		case 'B': // am/pm text if local time format is 12 hour clock
			if (aLocaleAndfontData.iLocale->TimeFormat()==ETime24)
				break;
			// N.B. fall through
		case 'A': // am/pm text
			if ((localePos==KUnspecifiedLocalePos) || (localePos==aLocaleAndfontData.iLocale->AmPmSymbolPosition()))
				potentialWidthOfTextBlock+=aLocaleAndfontData.iWidthOfSpace+aLocaleAndfontData.iWidthOfWidestAmPmName;
			break;
		case 'C': // microseconds
			potentialWidthOfTextBlock+=aLocaleAndfontData.iWidthOfWidestDigit*6;
			break;
		case 'E': // day name
			potentialWidthOfTextBlock+=(abbreviated? aLocaleAndfontData.iWidthOfWidestAbbreviatedDayName: aLocaleAndfontData.iWidthOfWidestDayName);
			break;
		case 'F':
			fixed=ETrue;
			break;
		case 'N': // month name
			potentialWidthOfTextBlock+=(abbreviated? aLocaleAndfontData.iWidthOfWidestAbbreviatedMonthName: aLocaleAndfontData.iWidthOfWidestMonthName);
			break;
		case 'X': // day suffix
			if (fixed)
				potentialWidthOfTextBlock+=aLocaleAndfontData.iWidthOfWidestDateSuffix;
			break;
		case 'Y': // year
		year:
			potentialWidthOfTextBlock+=aLocaleAndfontData.iWidthOfWidestDigit*(abbreviated? 2: 4);
			break;
		case 'Z': // day no in year
			potentialWidthOfTextBlock+=aLocaleAndfontData.iWidthOfWidestDigit*3;
			break;
		default:
			if (character!=0)
				potentialWidthOfTextBlock+=WidthOfCharacter(*aLocaleAndfontData.iFont, character);
			break;
			}
		}
	return potentialWidthOfTextBlock;
	}

EXPORT_C TInt CEikLocaleConformantClock::WidthOfCharacter(const CFont& aFont, TChar aCharacter)
	{
	TBuf<1> buffer=_L("");
	buffer.Append(aCharacter);
	return aFont.TextWidthInPixels(buffer);
	}

EXPORT_C void CEikLocaleConformantClock::DoCancel()
	{
	iChangeNotifier.LogonCancel(); // ignore the returned error
	}

EXPORT_C void CEikLocaleConformantClock::RunL()
	{
	if (iStatus.Int()&EChangesLocale)
		{
		TLocale locale;
		if (~iFlags&EIgnoresLocaleUniversalTimeOffsetAndHomeDaylightSavingZone)
			{
			iUniversalTimeOffset=locale.UniversalTimeOffset();
			iDaylightSavingZone=locale.HomeDaylightSavingZone();
			}
		TBool completeUniversalTimeOffsetHasChanged=CompleteUniversalTimeOffsetHasChanged();
		TClockFormat format=locale.ClockFormat();
		TBool formatHasChanged=EFalse;
		if ((~iFlags&EIgnoresLocaleClockFormat) && (iFormat!=format))
			{
			TRAPD(error, SetFormatL(format)); // case A: also handles requirements of cases B and C below
			if (error==KErrNone)
				formatHasChanged=ETrue;
			}
		if (!formatHasChanged)
			{
			if (completeUniversalTimeOffsetHasChanged)
				iClock->SetUniversalTimeOffset(iCompleteUniversalTimeOffset); // case B: also handles requirements of case C below
			else
				iClock->UpdateDisplay(); // case C
			}
		}
	LogonChangeNotifier();
	}

void CEikLocaleConformantClock::LogonChangeNotifier()
	{
	iChangeNotifier.Logon(iStatus);
	SetActive();
	}

TTimeIntervalSeconds CEikLocaleConformantClock::CalculateCompleteUniversalTimeOffset() const
	{
	TUint localeDaylightSaving=TLocale().DaylightSaving();
	TUint daylightSaving=iDaylightSavingZone;
	if (~iFlags&EIgnoresLocaleUniversalTimeOffsetAndHomeDaylightSavingZone)
		daylightSaving|=(localeDaylightSaving&EDstHome);
	return iUniversalTimeOffset.Int()+((daylightSaving&localeDaylightSaving)? 3600: 0);
	}

TBool CEikLocaleConformantClock::CompleteUniversalTimeOffsetHasChanged()
	{
	TTimeIntervalSeconds completeUniversalTimeOffset=CalculateCompleteUniversalTimeOffset();
	if (iCompleteUniversalTimeOffset!=completeUniversalTimeOffset)
		{
		iCompleteUniversalTimeOffset=completeUniversalTimeOffset;
		return ETrue;
		}
	return EFalse;
	}

void CEikLocaleConformantClock::SetFormatL(TClockFormat aFormat, TBool aCompleteSetUp)
	{
	__ASSERT_DEBUG((iFormat!=aFormat) || (iClock==NULL), Panic(EEikPanicClockNoNeedToSetFormat));
	RClock* clock=NULL; // dummy initialization to prevent compiler warning
	TBool drawBackground=ETrue;
	TSize minimumSize(0, 0);
	switch (aFormat)
		{
	case EClockDigital:
		{
		RDigitalClock* digitalClock=new(ELeave) RDigitalClock(iAnimDll, iWindow);
		CleanupStack::PushL(TCleanupItem(ClockDestroy, digitalClock));
		ConstructDigitalClockL(*digitalClock, iBorder, minimumSize);
		clock=digitalClock;
		drawBackground=EFalse;
		}
		break;
	case EClockAnalog:
		{
		RAnalogClock* analogClock=new(ELeave) RAnalogClock(iAnimDll, iWindow);
		CleanupStack::PushL(TCleanupItem(ClockDestroy, analogClock));
		ConstructAnalogClockL(*analogClock, iBorder, minimumSize);
		clock=analogClock;
		}
		break;
#if defined(_DEBUG)
	default:
		Panic(EEikPanicClockBadFormat3);
		break;
#endif
		}
	CleanupStack::Pop();
	if (iClock)
		iClock->Destroy();
	iClock=clock;
	iFormat=aFormat;
	iDrawBackground=drawBackground;
	minimumSize+=iBorder.SizeDelta();
	if (iMinimumSize.iWidth<minimumSize.iWidth)
		iMinimumSize.iWidth=minimumSize.iWidth;
	if (iMinimumSize.iHeight<minimumSize.iHeight)
		iMinimumSize.iHeight=minimumSize.iHeight;
	if (aCompleteSetUp)
		{
		iContainer.GetClockPositionAndSize(iPosition, iSize, *this, iMinimumSize);
		SetPositionAndSize(iPosition, iSize);
		iClock->SetUniversalTimeOffset(iCompleteUniversalTimeOffset);
		if (iContainer.ClockIsVisible(*this))
			iClock->SetVisible(ETrue);
		}
	}

// CEikResourceConstructedClock

EXPORT_C CEikResourceConstructedClock::CEikResourceConstructedClock(MEikClockContainer& aContainer, RAnimDll& aAnimDll, const RWindowBase& aWindow, TInt aType,
											TBool aTypeNeverChanges, CCoeEnv& aCoeEnv, TInt aDigitalResourceId, TInt aAnalogResourceId)
	:CEikLocaleConformantClock(aContainer, aAnimDll, aWindow, aType, aTypeNeverChanges),
	 iCoeEnv(aCoeEnv),
	 iDigitalResourceId(aDigitalResourceId),
	 iAnalogResourceId(aAnalogResourceId)
	{
	__DECLARE_NAME(_S("CEikResourceConstructedClock"));
	}

void CEikResourceConstructedClock::ConstructDigitalClockL(RDigitalClock& aDigitalClock, TEikBorder& aBorder, TSize& aMinimumSize) const
	{
#pragma warning (disable : 4127)
	__ASSERT_DEBUG(((TInt)EDigitalDisplayHorizontalTextAlignmentLeft==(TInt)EEikResourceConstructedClockDigitalDisplayHorizontalTextAlignmentLeft) &&
				   ((TInt)EDigitalDisplayHorizontalTextAlignmentCenter==(TInt)EEikResourceConstructedClockDigitalDisplayHorizontalTextAlignmentCenter) &&
				   ((TInt)EDigitalDisplayHorizontalTextAlignmentRight==(TInt)EEikResourceConstructedClockDigitalDisplayHorizontalTextAlignmentRight),
																	Panic(EEikPanicClockInconsistentHorizontalTextAlignmentValues));
	__ASSERT_DEBUG(((TInt)EDigitalDisplayVerticalTextAlignmentTop==(TInt)EEikResourceConstructedClockDigitalDisplayVerticalTextAlignmentTop) &&
				   ((TInt)EDigitalDisplayVerticalTextAlignmentCenterInclDescent==(TInt)EEikResourceConstructedClockDigitalDisplayVerticalTextAlignmentCenterInclDescent) &&
				   ((TInt)EDigitalDisplayVerticalTextAlignmentCenterExclDescent==(TInt)EEikResourceConstructedClockDigitalDisplayVerticalTextAlignmentCenterExclDescent) &&
				   ((TInt)EDigitalDisplayVerticalTextAlignmentBottomInclDescent==(TInt)EEikResourceConstructedClockDigitalDisplayVerticalTextAlignmentBottomInclDescent) &&
				   ((TInt)EDigitalDisplayVerticalTextAlignmentBottomExclDescent==(TInt)EEikResourceConstructedClockDigitalDisplayVerticalTextAlignmentBottomExclDescent),
																	Panic(EEikPanicClockInconsistentVerticalTextAlignmentValues));
#pragma warning (default : 4127)
#if defined(DO_PROFILING)
	RDebug::ProfileReset(PROFILE_ID, 1);
	RDebug::ProfileStart(PROFILE_ID);
#endif
	SEikLocaleAndFontData localeAndfontData;
	TLocale locale;
	localeAndfontData.iLocale=&locale;
	TResourceReader resourceReader;
	iCoeEnv.CreateResourceReaderLC(resourceReader, iDigitalResourceId); // also pushes on to CleanupStack
	TBool withSingleGrayBorder=resourceReader.ReadUint8();
	aBorder.SetType(withSingleGrayBorder? TEikBorder::ESingleGray: TEikBorder::ENone);
	TTimeIntervalSeconds offsetFromUniversalTime=resourceReader.ReadInt16();
	STimeDeviceShadow shadow=ReadShadow(resourceReader);
	TRgb backgroundColor=ReadColor(resourceReader);
	CEikonEnv* eikonEnv=CEikonEnv::Static();
	if (iFlags&EUsesDefaultSystemColors)
		backgroundColor=eikonEnv->Color(EEikColorControlBackground);

	TInt numTextSections=resourceReader.ReadUint8();
	aDigitalClock.ConstructL(offsetFromUniversalTime, TPoint(), TSize(), aBorder.Margins(), shadow, backgroundColor, numTextSections);
	TInt numFontsPushedOnToCleanupStack=0;
	for (TInt i=0; i<numTextSections; ++i)
		{
		CEikCleanupStackableFont* font=CEikCleanupStackableFont::NewLC(resourceReader, *iCoeEnv.ScreenDevice()); // also pushes on to CleanupStack
		++numFontsPushedOnToCleanupStack;
		CFbsFont& fbsFont=font->Font();
		localeAndfontData.iFont=&fbsFont;
		localeAndfontData.iWidthOfSpace=fbsFont.TextWidthInPixels(_L(" "));
		localeAndfontData.iWidthOfWidestCharacter=fbsFont.MaxNormalCharWidthInPixels();
		localeAndfontData.iWidthOfWidestDigit=TEikFindWidthOfWidestDigit().MaximumWidthInPixels(fbsFont);
		localeAndfontData.iWidthOfWidestAmPmName=TEikFindWidthOfWidestAmPmName().MaximumWidthInPixels(fbsFont);
		localeAndfontData.iWidthOfWidestAbbreviatedDayName=TEikFindWidthOfWidestAbbreviatedDayName().MaximumWidthInPixels(fbsFont);
		localeAndfontData.iWidthOfWidestDayName=TEikFindWidthOfWidestDayName().MaximumWidthInPixels(fbsFont);
		localeAndfontData.iWidthOfWidestAbbreviatedMonthName=TEikFindWidthOfWidestAbbreviatedMonthName().MaximumWidthInPixels(fbsFont);
		localeAndfontData.iWidthOfWidestMonthName=TEikFindWidthOfWidestMonthName().MaximumWidthInPixels(fbsFont);
		localeAndfontData.iWidthOfWidestDateSuffix=TEikFindWidthOfWidestDateSuffix().MaximumWidthInPixels(fbsFont);
		TRgb textColor=ReadColor(resourceReader);
		if (iFlags&EUsesDefaultSystemColors)
			textColor=eikonEnv->Color(EEikColorControlText);

		TDigitalDisplayHorizontalTextAlignment horizontalAlignment=(TDigitalDisplayHorizontalTextAlignment)resourceReader.ReadUint8();
		TDigitalDisplayVerticalTextAlignment verticalAlignment=(TDigitalDisplayVerticalTextAlignment)resourceReader.ReadUint8();
		TInt horizontalMargin=resourceReader.ReadInt16();
		TInt verticalMargin=resourceReader.ReadInt16();
		TSize minimumTextSectionSize(EGapLeftOfText+EGapRightOfText, fbsFont.HeightInPixels()+EGapAboveText+EGapBelowText); // iWidth will be incremented with each text-block
		TInt numTextBlocks=resourceReader.ReadUint8();
		TBuf<48> format=_L("");
		for (TInt j=0; j<numTextBlocks; ++j)
			{
			TPtrC textBlock=resourceReader.ReadTPtrC();
			minimumTextSectionSize.iWidth+=PotentialWidthOfTextBlock(textBlock, localeAndfontData);
			format.Append(textBlock);
			if (j<numTextBlocks-1)
				format.Append(TChar(EDigitalDisplayLayoutCharFlashingBlockDelimiter));
			}
		if (horizontalAlignment!=EDigitalDisplayHorizontalTextAlignmentCenter)
			minimumTextSectionSize.iWidth+=horizontalMargin;
		if ((verticalAlignment!=EDigitalDisplayVerticalTextAlignmentCenterInclDescent) && (verticalAlignment!=EDigitalDisplayVerticalTextAlignmentCenterExclDescent))
			minimumTextSectionSize.iHeight+=verticalMargin;
		if (shadow.iIsOn)
			minimumTextSectionSize+=TSize(Abs(shadow.iOffset.iX), Abs(shadow.iOffset.iY));
		if (aMinimumSize.iWidth<minimumTextSectionSize.iWidth)
			aMinimumSize.iWidth=minimumTextSectionSize.iWidth;
		if (aMinimumSize.iHeight<minimumTextSectionSize.iHeight)
			aMinimumSize.iHeight=minimumTextSectionSize.iHeight;
		TDigitalDisplayTextSection* textSection=new(ELeave) TDigitalDisplayTextSection(fbsFont.Handle(), textColor, horizontalAlignment, verticalAlignment, horizontalMargin, verticalMargin, format); // TDigitalDisplayTextSection is over 1k, therefore put on heap
		CleanupStack::PushL(textSection);
		aDigitalClock.AddTextSectionL(*textSection);
		CleanupStack::PopAndDestroy();
		}
	CleanupStack::PopAndDestroy(numFontsPushedOnToCleanupStack+1); // "+1" to destroy the resource-reader as well
#if defined(DO_PROFILING)
	RDebug::ProfileEnd(PROFILE_ID);
	TProfile profile[1];
	RDebug::ProfileResult(profile, PROFILE_ID, 1);
	TBuf<64> buffer;
	buffer.Format(_L("Time taken - %d.%06d seconds"), profile[0].iTime/1000000, profile[0].iTime%1000000);
	eikonEnv->VerboseInfoMsg(buffer);
#endif
	}

void CEikResourceConstructedClock::ConstructAnalogClockL(RAnalogClock& aAnalogClock, TEikBorder& aBorder, TSize& aMinimumSize) const
	{
#pragma warning (disable : 4127)
	__ASSERT_DEBUG(((TInt)EAnalogDisplayHandOneRevPer12Hours==(TInt)EEikResourceConstructedClockAnalogDisplayHandOneRevPer12Hours) &&
				   ((TInt)EAnalogDisplayHandOneRevPerHour==(TInt)EEikResourceConstructedClockAnalogDisplayHandOneRevPerHour) &&
				   ((TInt)EAnalogDisplayHandOneRevPerMinute==(TInt)EEikResourceConstructedClockAnalogDisplayHandOneRevPerMinute),
																	Panic(EEikPanicClockInconsistentHandTypeValues));
#pragma warning (default : 4127)
#if defined(DO_PROFILING)
	RDebug::ProfileReset(PROFILE_ID, 1);
	RDebug::ProfileStart(PROFILE_ID);
#endif
	TResourceReader resourceReader;
	iCoeEnv.CreateResourceReaderLC(resourceReader, iAnalogResourceId); // also pushes on to CleanupStack
	TBool withSingleGrayBorder=resourceReader.ReadUint8();
	aBorder.SetType(withSingleGrayBorder? TEikBorder::ESingleGray: TEikBorder::ENone);
	TTimeIntervalSeconds offsetFromUniversalTime=resourceReader.ReadInt16();
	STimeDeviceShadow shadow=ReadShadow(resourceReader);
	TPtrC fileContainingFace=resourceReader.ReadTPtrC();
	TInt32 faceId=resourceReader.ReadInt32();
	CFbsBitmap* face=((CEikonEnv&)iCoeEnv).CreateBitmapL(fileContainingFace, faceId);
	CleanupStack::PushL(face);
	aMinimumSize=face->SizeInPixels();
	CFbsBitmap* faceMask=NULL;
	TBool useFaceMask=resourceReader.ReadUint8();
	if (useFaceMask)
		{
		TPtrC fileContainingFaceMask=resourceReader.ReadTPtrC();
		TInt32 faceMaskId=resourceReader.ReadInt32();
		faceMask=((CEikonEnv&)iCoeEnv).CreateBitmapL(fileContainingFaceMask, faceMaskId);
		CleanupStack::PushL(faceMask);
		}
	CEikonEnv* eikonEnv=CEikonEnv::Static();
	TInt numHands=resourceReader.ReadUint8();
	aAnalogClock.ConstructL(offsetFromUniversalTime, TPoint(), TSize(), aBorder.Margins(), shadow, face->Handle(), faceMask? faceMask->Handle(): 0, numHands);
	for (TInt i=0; i<numHands; ++i)
		{
		TAnalogDisplayHand* hand=new(ELeave) TAnalogDisplayHand((TAnalogDisplayHandType)resourceReader.ReadUint8()); // TAnalogDisplayHand is over 1k, therefore put on heap
		CleanupStack::PushL(hand);
		TInt numHandFeatures=resourceReader.ReadUint8();
		for (TInt j=0; j<numHandFeatures; ++j)
			{
			TInt featureType=resourceReader.ReadUint8();
			switch (featureType)
				{
			case EEikResourceConstructedClockAnalogDisplayHandFeatureLine:
				{
				CGraphicsContext::TPenStyle penStyle;
				TRgb penColor;
				TSize penSize;
				ReadPen(resourceReader, penStyle, penColor, penSize);
 				if (iFlags&EUsesDefaultSystemColors)
					penColor = eikonEnv->Color(EEikColorControlText);
				TPoint startPoint=ReadPoint(resourceReader);
				TPoint endPoint=ReadPoint(resourceReader);
				hand->AddLine(penStyle, penColor, penSize, startPoint, endPoint);
				}
				break;
			case EEikResourceConstructedClockAnalogDisplayHandFeaturePolyLine:
				{
				CGraphicsContext::TPenStyle penStyle;
				TRgb penColor;
				TSize penSize;
				ReadPen(resourceReader, penStyle, penColor, penSize);
				CGraphicsContext::TBrushStyle brushStyle;
				TRgb brushColor;
				ReadBrush(resourceReader, brushStyle, brushColor);
				TBool closed=resourceReader.ReadUint8();
				TInt numPoints=resourceReader.ReadUint8();
				CArrayFix<TPoint>* pointList=new(ELeave) CArrayFixFlat<TPoint>(numPoints);
				CleanupStack::PushL(pointList);
				for (TInt k=0; k<numPoints; ++k)
					pointList->AppendL(ReadPoint(resourceReader));
				hand->AddPolyLine(penStyle, penColor, penSize, brushStyle, brushColor, closed, pointList);
				CleanupStack::PopAndDestroy();
				}
				break;
			case EEikResourceConstructedClockAnalogDisplayHandFeatureCircle:
				{
				CGraphicsContext::TPenStyle penStyle;
				TRgb penColor;
				TSize penSize;
				ReadPen(resourceReader, penStyle, penColor, penSize);
				CGraphicsContext::TBrushStyle brushStyle;
				TRgb brushColor;
				ReadBrush(resourceReader, brushStyle, brushColor);
				TPoint circleCenter=ReadPoint(resourceReader);
				TInt radius=resourceReader.ReadInt16();
				hand->AddCircle(penStyle, penColor, penSize, brushStyle, brushColor, circleCenter, radius);
				}
				break;
			default:
				Panic(EEikPanicClockBadHandFeatureType);
				break;
				}
			}
		aAnalogClock.AddHandL(*hand);
		CleanupStack::PopAndDestroy();
		}
	CleanupStack::PopAndDestroy(faceMask? 3: 2);
#if defined(DO_PROFILING)
	RDebug::ProfileEnd(PROFILE_ID);
	TProfile profile[1];
	RDebug::ProfileResult(profile, PROFILE_ID, 1);
	TBuf<64> buffer;
	buffer.Format(_L("Time taken - %d.%06d seconds"), profile[0].iTime/1000000, profile[0].iTime%1000000);
	eikonEnv->VerboseInfoMsg(buffer);
#endif
	}

TPoint CEikResourceConstructedClock::ReadPoint(TResourceReader& aResourceReader)
	{
	TInt x=aResourceReader.ReadInt16();
	TInt y=aResourceReader.ReadInt16();
	return TPoint(x, y);
	}

TRgb CEikResourceConstructedClock::ReadColor(TResourceReader& aResourceReader)
	{
	TInt red=aResourceReader.ReadUint8();
	TInt green=aResourceReader.ReadUint8();
	TInt blue=aResourceReader.ReadUint8();
	return TRgb(red, green, blue);
	}

STimeDeviceShadow CEikResourceConstructedClock::ReadShadow(TResourceReader& aResourceReader)
	{
	STimeDeviceShadow shadow;
	shadow.iIsOn=aResourceReader.ReadUint8();
	if (shadow.iIsOn)
		{
		shadow.iColor=ReadColor(aResourceReader);
		shadow.iOffset.iX=aResourceReader.ReadInt8();
		shadow.iOffset.iY=aResourceReader.ReadInt8();
		}
	return shadow;
	}

void CEikResourceConstructedClock::ReadPen(TResourceReader& aResourceReader, CGraphicsContext::TPenStyle& aStyle, TRgb& aColor, TSize& aSize)
	{
	TBool solidPen=aResourceReader.ReadUint8();
	aStyle=solidPen? CGraphicsContext::ESolidPen: CGraphicsContext::ENullPen;
	if (solidPen)
		{
		aColor=ReadColor(aResourceReader);
		TInt penSize=aResourceReader.ReadUint8();
		aSize.SetSize(penSize, penSize);
		}
	}

void CEikResourceConstructedClock::ReadBrush(TResourceReader& aResourceReader, CGraphicsContext::TBrushStyle& aStyle, TRgb& aColor)
	{
	TBool solidBrush=aResourceReader.ReadUint8();
	aStyle=solidBrush? CGraphicsContext::ESolidBrush: CGraphicsContext::ENullBrush;
	if (solidBrush)
		aColor=ReadColor(aResourceReader);
	}

// CEikClock

EXPORT_C CEikClock::CEikClock()
	{
	__DECLARE_NAME(_S("CEikClock"));
	}

EXPORT_C void CEikClock::SetClock(CEikLocaleConformantClock* aClock)
	{
	__ASSERT_ALWAYS(iClock==NULL, Panic(EEikPanicClockClockAlreadyExists));
	iClock=aClock;
	}

EXPORT_C CEikClock::~CEikClock()
	{
	delete iClock;
	}

EXPORT_C void CEikClock::HandlePointerEventL(const TPointerEvent& aPointerEvent)
	{
	if (!(iClock->TypeNeverChanges()) && (aPointerEvent.iType==TPointerEvent::EButton1Down))
		{
		TClockFormat newClockFormat=EClockDigital; // initialised to avoid gcc warning
		switch (iClock->Format())
			{
		case EClockAnalog:
			break;
		case EClockDigital:
			newClockFormat=EClockAnalog;
			break;
#if defined(_DEBUG)
		default:
			Panic(EEikPanicClockBadFormat4);
			break;
#endif
			}
		if (iClock->IgnoresLocaleClockFormat())
			iClock->ChangeFormatL(newClockFormat);
		else
			{
			TLocale locale;
			locale.SetClockFormat(newClockFormat);
			locale.Set();
			}
		}
	}

EXPORT_C void CEikClock::MakeVisible(TBool aVisible)
	{
	CCoeControl::MakeVisible(aVisible);
	iClock->SetVisible(aVisible);
	}

EXPORT_C void CEikClock::ConstructFromResourceL(TResourceReader& aResourceReader)
	{
	TInt type=aResourceReader.ReadUint8();
	TBool typeNeverChanges=aResourceReader.ReadUint8();
	TInt digitalResourceId=aResourceReader.ReadInt32();
	TInt analogResourceId=aResourceReader.ReadInt32();
	iClock=new(ELeave) CEikResourceConstructedClock(*this, iEikonEnv->ClockDllL(), Window(), type, typeNeverChanges, *iCoeEnv, digitalResourceId, analogResourceId);
	ConstructClockL(*iClock);
	}

EXPORT_C void CEikClock::ActivateL()
	{
	CCoeControl::ActivateL();
	if (IsVisible())
		iClock->SetVisible(ETrue);
	}

EXPORT_C TSize CEikClock::MinimumSize()
	{
	return iClock->MinimumSize();
	}

EXPORT_C void CEikClock::SizeChangedL()
	{
	iClock->SetPositionAndSize(iPosition, iSize);
	}

EXPORT_C void CEikClock::PositionChanged()
	{
	iClock->SetPosition(iPosition);
	}

EXPORT_C TBool CEikClock::HasBorder() const
	{
	return iClock->HasBorder();
	}

EXPORT_C void CEikClock::Draw(const TRect& /*aRect*/) const
	{
	__ASSERT_ALWAYS(iContext, Panic(EEikPanicClockNoControlContext));
	CWindowGc& gc = SystemGc();
	gc.SetPenColor(iEikonEnv->ControlColor(EEikColorControlText,*this));
	gc.SetBrushColor(iEikonEnv->ControlColor(EEikColorControlBackground,*this));
	iClock->Draw(gc);
	}

EXPORT_C void CEikClock::Reserved_1()
	{
	// does nothing
	}

EXPORT_C void CEikClock::Reserved_2()
	{
	// does nothing
	}

EXPORT_C TBool CEikClock::ClockIsVisible(const CEikLocaleConformantClock&) const
	{
	return IsActivated() && IsVisible();
	}

EXPORT_C void CEikClock::GetClockPositionAndSize(TPoint& aPosition, TSize& aSize, const CEikLocaleConformantClock&, const TSize&)
	{
	aPosition=iPosition;
	aSize=iSize;
	}

EXPORT_C void CEikClock::ConstructClockL(CEikLocaleConformantClock& aClock)
	{
	aClock.ConstructL();
	}

