// EIKCAL.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include <basched.h>
#include <barsread.h>
#include <eikenv.h>
#include <eikcolor.h>
#include <eiktxtut.h>
#include <eikmsg.h>
#include <eikmfne.h>
#include <eikfont.h>
#include <eikbtpan.h>
#include <eikcmbut.h>
#include <eikdialg.hrh>
#include <eikcal.pan>
#include <eikcal.hrh>
#include <eikcal.h>
#include <eikon.rsg>
#include <eikon.mbg>
#include <eiksfont.h>
#include <eiklabel.h>
#include <eikbordr.h>
#include <eikbutb.h>
#include <eikappui.h>

#if defined(DO_PROFILING)
#include <eikdef.h>
#define PROFILE_ID PROFILE_POINT_EIKON_CALENDER
#endif

// global functions

const TInt KExtraCalBorder=12;// prevents the calendar deleting when pressing just outside the dogear

GLDEF_C void Panic(TEikCalendarPanic aPanic)
	{
	User::Panic(_L("EIKON-CALENDAR"), aPanic);
	}

// TFindWidthOfWidestDayNameInitial

class TFindWidthOfWidestDayNameInitial : public TEikFindWidthOfWidestTextItem
	{
public:
	TFindWidthOfWidestDayNameInitial(const TText aInitials[]);
private:
	virtual void GetFirstAndLastIndex(TInt& aFirstIndex, TInt& aLastIndex) const;
	virtual void GetTextItem(TDes& aText, TInt aIndex) const;
private:
	const TText* iInitials;
	};

TFindWidthOfWidestDayNameInitial::TFindWidthOfWidestDayNameInitial(const TText aInitials[])
	:iInitials(aInitials)
	{
	}

void TFindWidthOfWidestDayNameInitial::GetFirstAndLastIndex(TInt& aFirstIndex, TInt& aLastIndex) const
	{
	aFirstIndex=0;
	aLastIndex=6;
	}

void TFindWidthOfWidestDayNameInitial::GetTextItem(TDes& aText, TInt aIndex) const
	{
	aText.SetLength(0);
	aText.Append(TChar(iInitials[aIndex]));
	}

// CEikCalendar

enum {EEikCalendarFlagIsWaiting=4};// follow on from eikcal.hrh

EXPORT_C CEikCalendar::CEikCalendar()
	:CEikBorderedControl(TEikBorder(KEikEditableControlBorder))
	{
	__DECLARE_NAME(_S("CEikCalendar"));
#if defined(_DEBUG)
	// test-code for Divide function
	TInt result;
	TInt remainder;
	TBool success=ETrue;
	Divide(-2, 1, &result, &remainder);
	success=(success && (result==-2) && (remainder==0));
	Divide(-1, 1, &result, &remainder);
	success=(success && (result==-1) && (remainder==0));
	Divide(0, 1, &result, &remainder);
	success=(success && (result==0) && (remainder==0));
	Divide(1, 1, &result, &remainder);
	success=(success && (result==1) && (remainder==0));
	Divide(2, 1, &result, &remainder);
	success=(success && (result==2) && (remainder==0));
	Divide(-7, 3, &result, &remainder);
	success=(success && (result==-3) && (remainder==2));
	Divide(-6, 3, &result, &remainder);
	success=(success && (result==-2) && (remainder==0));
	Divide(-5, 3, &result, &remainder);
	success=(success && (result==-2) && (remainder==1));
	Divide(-4, 3, &result, &remainder);
	success=(success && (result==-2) && (remainder==2));
	Divide(-3, 3, &result, &remainder);
	success=(success && (result==-1) && (remainder==0));
	Divide(-2, 3, &result, &remainder);
	success=(success && (result==-1) && (remainder==1));
	Divide(-1, 3, &result, &remainder);
	success=(success && (result==-1) && (remainder==2));
	Divide(0, 3, &result, &remainder);
	success=(success && (result==0) && (remainder==0));
	Divide(1, 3, &result, &remainder);
	success=(success && (result==0) && (remainder==1));
	Divide(2, 3, &result, &remainder);
	success=(success && (result==0) && (remainder==2));
	Divide(3, 3, &result, &remainder);
	success=(success && (result==1) && (remainder==0));
	Divide(4, 3, &result, &remainder);
	success=(success && (result==1) && (remainder==1));
	Divide(5, 3, &result, &remainder);
	success=(success && (result==1) && (remainder==2));
	Divide(6, 3, &result, &remainder);
	success=(success && (result==2) && (remainder==0));
	Divide(7, 3, &result, &remainder);
	success=(success && (result==2) && (remainder==1));
	__ASSERT_ALWAYS(success, Panic(EEikPanicCalendarDivideTestCodeFailed));
#endif
	TLocale locale;
	iStartOfWeek=locale.StartOfWeek();
	iWorkDays=locale.WorkDays();
	for (TInt i=0; i<7; ++i)
		iDayNameInitials[i]=TDayName((TDay)i)[0];
	TTime today;
	today.HomeTime();
	TDateTime todayAsDateTime=today.DateTime();
	iDayOfToday=todayAsDateTime.Day();
	iMonthOfToday=todayAsDateTime.Month();
	iYearOfToday=todayAsDateTime.Year();
	}

EXPORT_C void CEikCalendar::ExecuteLD(TInt aResourceId)
	{
	TTime min;
	TTime max;
	TTime initialDate;
	CleanupStack::PushL(this);
	iCalendarObserver->GetMinimumAndMaximumAndInitialDatesForCalendarL(min,max,initialDate);
	SetMinimumAndMaximum(min,max);
	if (initialDate<min)
		initialDate=min;
	else if (initialDate>max)
		initialDate=max;
	//
	BaseConstructL();
	ConstructMonthViewL(aResourceId,initialDate);
	ActivateL();
	CleanupStack::Pop();// this
	//
	iFlags|=EEikCalendarFlagIsWaiting;
	CActiveScheduler::Start();
	}


void CEikCalendar::BaseConstructL()
	{
	CreateWindowL();
	EnableDragEvents();
	SetPointerCapture(RWindowBase::TCaptureFlagEnabled);
	iEikonEnv->AddWindowShadow(this);
	iEikonEnv->AddDialogLikeControlToStackL(this);
	//
	TPtrC bmpFile;
	iLeftDogEarBitmap=iEikonEnv->CreateBitmapL(bmpFile,EMbmEikonDogearl);
	iLeftDogEarBitmapMask=iEikonEnv->CreateBitmapL(bmpFile,EMbmEikonDogearlm);
	iRightDogEarBitmap=iEikonEnv->CreateBitmapL(bmpFile,EMbmEikonDogearr);
	iRightDogEarBitmapMask=iEikonEnv->CreateBitmapL(bmpFile,EMbmEikonDogearrm);
	//
	iOneMonthCommandButtonBitmap=iEikonEnv->CreateBitmapL(bmpFile,EMbmEikonCalbut1);
	iOneMonthCommandButtonBitmapMask=iEikonEnv->CreateBitmapL(bmpFile,EMbmEikonCalbut1);
	iThreeMonthCommandButtonBitmap=iEikonEnv->CreateBitmapL(bmpFile,EMbmEikonCalbut2);
	iThreeMonthCommandButtonBitmapMask=iEikonEnv->CreateBitmapL(bmpFile,EMbmEikonCalbut2);
	iTwelveMonthCommandButtonBitmap=iEikonEnv->CreateBitmapL(bmpFile,EMbmEikonCalbut3);
	iTwelveMonthCommandButtonBitmapMask=iEikonEnv->CreateBitmapL(bmpFile,EMbmEikonCalbut3);
	//
	iCommandButtonOneMonth =new(ELeave) CEikCommandButton;
	iCommandButtonOneMonth->SetContainerWindowL(*(CCoeControl*)this);
	iCommandButtonOneMonth->SetObserver((MCoeControlObserver*)this);
	iCommandButtonOneMonth->SetPictureL(iOneMonthCommandButtonBitmap,iOneMonthCommandButtonBitmapMask);
	iCommandButtonOneMonth->SetBehavior(EEikButtonLatches);
	iCommandButtonOneMonth->SetCoordinator(&iButCoord);
	iCommandButtonThreeMonth =new(ELeave) CEikCommandButton;
	iCommandButtonThreeMonth->SetContainerWindowL(*(CCoeControl*)this);
	iCommandButtonThreeMonth->SetObserver((MCoeControlObserver*)this);
	iCommandButtonThreeMonth->SetPictureL(iThreeMonthCommandButtonBitmap,iThreeMonthCommandButtonBitmapMask);
	iCommandButtonThreeMonth->SetBehavior(EEikButtonLatches);
	iCommandButtonThreeMonth->SetCoordinator(&iButCoord);
	iCommandButtonTwelveMonth =new(ELeave) CEikCommandButton;
	iCommandButtonTwelveMonth->SetContainerWindowL(*(CCoeControl*)this);
	iCommandButtonTwelveMonth->SetObserver((MCoeControlObserver*)this);
	iCommandButtonTwelveMonth->SetPictureL(iTwelveMonthCommandButtonBitmap,iTwelveMonthCommandButtonBitmapMask);
	iCommandButtonTwelveMonth->SetBehavior(EEikButtonLatches);
	iCommandButtonTwelveMonth->SetCoordinator(&iButCoord);
	iCommandButtonOneMonth->SetAdjacent(ECoeAdjRight);
	iCommandButtonThreeMonth->SetAdjacent(ECoeAdjRight);
	//
	SetBorder(TEikBorder::EThickDeepRaisedWithOutline);
	}

void CEikCalendar::ConstructMonthViewL(TInt aResourceId,const TTime& aInitialDate)
	{ 
	ReadResourceL(aResourceId);// Set the calendar data
	SetDateL(aInitialDate,EFalse);
	//
	__ASSERT_DEBUG(iNumMonths==1 || iNumMonths==3 || iNumMonths==12, Panic(EEikPanicCalendarBadNumMonths));
	__ASSERT_DEBUG(iNumMonths%iNumMonthsPerRowOfMonths==0, Panic(EEikPanicCalendarBadNumMonths));
	__ASSERT_DEBUG((~iFlags&EEikCalendarFlagStartMonthIsAlwaysJanuary) || (iNumMonths>=12), Panic(EEikPanicCalendarCannotDisplayAllMonths));
	// allow for button minimum size in calendar header
	iSizeOfYearText.iHeight=iYearFont->HeightInPixels();
	TInt buttonHeight=iCommandButtonOneMonth->MinimumSize().iHeight;
	TInt yearPlusSpacesHeight=iVerticalSpaceAboveYear+iSizeOfYearText.iHeight+iVerticalSpaceBetweenYearAndTopRowOfMonths;
	if (buttonHeight>yearPlusSpacesHeight)
		{// the height of the calendar header must be heightened
		TInt diff=buttonHeight-yearPlusSpacesHeight;
		iVerticalSpaceAboveYear+=diff/2;
		iVerticalSpaceBetweenYearAndTopRowOfMonths+=diff/2;
		if (diff%2)// if odd
			iVerticalSpaceBetweenYearAndTopRowOfMonths+=1;
		}
	iVerticalSpaceBetweenYearAndTopRowOfMonths+=EExtraHeightForHighlightSquare;
	// button should only be latched after unlatched buttonHeight is taken
	CEikCommandButton* initialLatchedButton=iCommandButtonOneMonth;
	if (iNumMonths==3)
		initialLatchedButton=iCommandButtonThreeMonth;
	else if (iNumMonths==12)
		initialLatchedButton=iCommandButtonTwelveMonth;
	initialLatchedButton->SetState(CEikButtonBase::ESet);
	//
	TFindWidthOfWidestDayNameInitial findWidthOfWidestDayNameInitial(iDayNameInitials);
	TEikFindWidthOfWidestDigit findWidthOfWidestDigit;
	iHeightOfWeekDayInitials=iWeekDayInitialFont->HeightInPixels();
	iSizeOfSingleDay.iWidth=Max(findWidthOfWidestDayNameInitial.MaximumWidthInPixels(*iWeekDayInitialFont),findWidthOfWidestDigit.MaximumWidthInPixels(*iNumeralFont)*2);
	iSizeOfSingleDay.iHeight=iNumeralFont->HeightInPixels();
	// Make iSizeOfSingleDay a square
	// This is necessary so that todays date can easily be circled
	if (iSizeOfSingleDay.iWidth>iSizeOfSingleDay.iHeight)
		iSizeOfSingleDay.iHeight=iSizeOfSingleDay.iWidth;
	else if (iSizeOfSingleDay.iHeight>iSizeOfSingleDay.iWidth)
		iSizeOfSingleDay.iWidth=iSizeOfSingleDay.iHeight;
	// 
	if (iNumMonths==12)// !! kludge // stretch the width by 2 pixels
		iSizeOfSingleDay.iWidth=14;
	//
	iSizeOfSingleMonth.iWidth=((7-1)*(iSizeOfSingleDay.iWidth+iHorizontalSpaceBetweenEachColumnOfDays))+iSizeOfSingleDay.iWidth;
	iSizeOfSingleMonth.iHeight=iMonthFont->HeightInPixels()+iVerticalSpaceBetweenMonthNameAndDayNameInitials+iHeightOfWeekDayInitials+iVerticalSpaceBetweenDayNameInitialsAndTopRowOfDays+
								((6-1)*(iSizeOfSingleDay.iHeight+iVerticalSpaceBetweenEachRowOfDays))+iSizeOfSingleDay.iHeight;
	TInt widthForOneYear=findWidthOfWidestDigit.MaximumWidthInPixels(*iYearFont)*4;
	iSizeOfYearText.iWidth=((iNumMonths==1) || ((iNumMonths<=12) && (iFlags&EEikCalendarFlagStartMonthIsAlwaysJanuary)))? widthForOneYear:
												(widthForOneYear*2)+iYearFont->TextWidthInPixels(*iTextSeparatingYearRange);
	
	//
	iCalendarSize.iWidth=iHorizontalSpaceOnEachSideOfMonthMatrix+
					  ((iNumMonthsPerRowOfMonths-1)*(iSizeOfSingleMonth.iWidth+iHorizontalSpaceBetweenEachColumnOfMonths))+
					  iSizeOfSingleMonth.iWidth+iHorizontalSpaceOnEachSideOfMonthMatrix;
	iCalendarSize.iHeight=iVerticalSpaceAboveYear+iSizeOfYearText.iHeight+iVerticalSpaceBetweenYearAndTopRowOfMonths+
					  (((iNumMonths/iNumMonthsPerRowOfMonths)-1)*(iSizeOfSingleMonth.iHeight+iVerticalSpaceBetweenEachRowOfMonths))+
					  iSizeOfSingleMonth.iHeight+iVerticalSpaceBeneathBottomRowOfMonths;
	iCalendarSize+=iBorder.SizeDelta();
	SetExtentL(CalendarPosition(),iCalendarSize);

	// construct and initialize numeral and weekDayInitial bitmap
	TSize bitmapSize((iSizeOfSingleDay.iWidth*31)+(iHorizontalSpaceBetweenEachColumnOfDays*32)+
						(iSizeOfSingleDay.iWidth*7)+(iHorizontalSpaceBetweenEachColumnOfDays*7),
						 Max((iSizeOfSingleDay.iHeight*3)+(iVerticalSpaceBetweenEachRowOfDays*2)+Max(iVerticalSpaceBetweenEachRowOfMonths, iVerticalSpaceBeneathBottomRowOfMonths),
						 iHeightOfWeekDayInitials+iVerticalSpaceBetweenDayNameInitialsAndTopRowOfDays));
	delete iBitmap;
	iBitmap=NULL;
	iBitmap=new(ELeave) CWsBitmap(iCoeEnv->WsSession());
	TInt gray;
	TInt color;
	TDisplayMode mode = iEikonEnv->WsSession().GetDefModeMaxNumColors(color, gray);
	User::LeaveIfError(iBitmap->Create(bitmapSize, mode));
	CFbsBitGc* bitGc=CFbsBitGc::NewL();
	CleanupStack::PushL(bitGc);
	CFbsBitmapDevice* device=CFbsBitmapDevice::NewL(iBitmap);
	CleanupStack::PushL(device);
	bitGc->Activate(device);
	bitGc->SetBrushStyle(CGraphicsContext::ESolidBrush);
	bitGc->SetBrushColor(iEikonEnv->Color(EEikColorControlBackground));

	TPoint position(0,0);
	TInt baselineOffset=iNumeralFont->AscentInPixels();
	// Centre the bitmap text horizontally
	TInt yCoordOffset=0;
	if (iSizeOfSingleDay.iHeight>baselineOffset)
		yCoordOffset=(iSizeOfSingleDay.iHeight-baselineOffset)/2;
	position.iY+=yCoordOffset;
	//
	TBuf<2> buffer;
	bitGc->UseFont(iNumeralFont);
	for (TInt i=0; i<31; ++i)
		{
		bitGc->SetPenStyle(CGraphicsContext::ENullPen);
		bitGc->SetPenColor(iEikonEnv->Color(EEikColorControlText));
		bitGc->DrawRect(TRect(TPoint(position.iX, position.iY + yCoordOffset - 4), TSize(iHorizontalSpaceBetweenEachColumnOfDays, bitmapSize.iHeight +3)));
		position.iX+=iHorizontalSpaceBetweenEachColumnOfDays;
		buffer.Format(_L("%d"), i+1);
		bitGc->SetPenStyle(CGraphicsContext::ESolidPen);
		bitGc->DrawText(buffer, TRect(TPoint(position.iX, position.iY - yCoordOffset), TSize(iSizeOfSingleDay.iWidth, bitmapSize.iHeight)), baselineOffset, CGraphicsContext::ECenter);
		position.iX+=iSizeOfSingleDay.iWidth;
		}
	//
	CFont* fontBeingUsed=NULL;// Sunday is usually start of week
	baselineOffset=iWeekDayInitialFont->AscentInPixels();
	TBuf<1> dayNameBuffer;
	position.iY-=yCoordOffset;// reset the position when drawing the day Initials to the bitmap
	bitGc->UseFont(iWeekDayInitialFont);
	fontBeingUsed=iWeekDayInitialFont;
	for (TInt j=0; j<7; ++j)
		{
		bitGc->SetPenStyle(CGraphicsContext::ENullPen);
		bitGc->DrawRect(TRect(position, TSize(iHorizontalSpaceBetweenEachColumnOfDays , bitmapSize.iHeight)));
		position.iX+=iHorizontalSpaceBetweenEachColumnOfDays;
		TInt thisDay=(iStartOfWeek+j)%7;
		dayNameBuffer.Append(iDayNameInitials[thisDay]);
		bitGc->SetPenStyle(CGraphicsContext::ESolidPen);
		bitGc->DrawText(dayNameBuffer, TRect(position, TSize(iSizeOfSingleDay.iWidth, bitmapSize.iHeight)), baselineOffset, CGraphicsContext::ECenter);
		dayNameBuffer.Zero();
		position.iX+=iSizeOfSingleDay.iWidth;
		}
	bitGc->SetPenStyle(CGraphicsContext::ENullPen);
	bitGc->DrawRect(TRect(position, TSize(iHorizontalSpaceBetweenEachColumnOfDays, bitmapSize.iHeight)));
	CleanupStack::PopAndDestroy(2); // destroy device & gc
	}

void CEikCalendar::ReadResourceL(TInt aResourceId)
	{
	TResourceReader resourceReader;
	iCoeEnv->CreateResourceReaderLC(resourceReader, aResourceId); // also pushes on to CleanupStack
	resourceReader.ReadUint8();// the number of calendars // not used
	const TBool isWaiting=(iFlags&EEikCalendarFlagIsWaiting);
	iFlags=resourceReader.ReadUint16();
	if (isWaiting)
		iFlags|=EEikCalendarFlagIsWaiting;
	iNumMonths=resourceReader.ReadUint8();
	iNumMonthsPerRowOfMonths=resourceReader.ReadUint8();
	iVerticalSpaceAboveYear=resourceReader.ReadInt16();
	iVerticalSpaceBetweenYearAndTopRowOfMonths=resourceReader.ReadInt16();
	iVerticalSpaceBetweenEachRowOfMonths=resourceReader.ReadInt16();
	iVerticalSpaceBeneathBottomRowOfMonths=resourceReader.ReadInt16();
	iHorizontalSpaceOnEachSideOfMonthMatrix=resourceReader.ReadInt16();
	iHorizontalSpaceBetweenEachColumnOfMonths=resourceReader.ReadInt16();
	iVerticalSpaceBetweenMonthNameAndDayNameInitials=resourceReader.ReadInt16();
	iVerticalSpaceBetweenDayNameInitialsAndTopRowOfDays=resourceReader.ReadInt16();
	iVerticalSpaceBetweenEachRowOfDays=resourceReader.ReadInt16();
	iHorizontalSpaceBetweenEachColumnOfDays=resourceReader.ReadInt16();
	//
	CWsScreenDevice& screenDevice=*iCoeEnv->ScreenDevice();
	CEikCleanupStackableFont* yearFont=CEikCleanupStackableFont::NewLC(resourceReader, screenDevice); // also pushes on to CleanupStack
	CEikCleanupStackableFont* monthFont=CEikCleanupStackableFont::NewLC(resourceReader, screenDevice); // also pushes on to CleanupStack
	CEikCleanupStackableFont* weekDayInitialFont=CEikCleanupStackableFont::NewLC(resourceReader, screenDevice); // also pushes on to CleanupStack
	CEikCleanupStackableFont* numeralFont=CEikCleanupStackableFont::NewLC(resourceReader, screenDevice); // also pushes on to CleanupStack
	//
	screenDevice.ReleaseFont(iYearFont);
	screenDevice.ReleaseFont(iMonthFont);
	screenDevice.ReleaseFont(iWeekDayInitialFont);
	screenDevice.ReleaseFont(iNumeralFont);
	//
	iYearFont=yearFont->TakeOwnershipOfFont();
	iMonthFont=monthFont->TakeOwnershipOfFont();
	iWeekDayInitialFont=weekDayInitialFont->TakeOwnershipOfFont();
	iNumeralFont=numeralFont->TakeOwnershipOfFont();
	CleanupStack::PopAndDestroy(4);
	//
	HBufC* buf=resourceReader.ReadHBufCL();
	delete iTextSeparatingYearRange;
	iTextSeparatingYearRange=buf;
	CleanupStack::PopAndDestroy();// resourceReader
	// essential extra space
	TSize dogearSize=iLeftDogEarBitmap->SizeInPixels();
	iHorizontalSpaceOnEachSideOfMonthMatrix+=dogearSize.iWidth/2;
	iVerticalSpaceBeneathBottomRowOfMonths+=(2*dogearSize.iHeight)/3;
	if (iVerticalSpaceBetweenMonthNameAndDayNameInitials==0)
		iVerticalSpaceBetweenMonthNameAndDayNameInitials+=1;
	}


EXPORT_C void CEikCalendar::SetCalendarObserver(MEikCalendarObserver* aObserver)
	{
	iCalendarObserver=aObserver;
	}
	
EXPORT_C CEikCalendar::~CEikCalendar()
	{
	// N.B. do not "delete" any of the fonts
	CWsScreenDevice& screenDevice=*iCoeEnv->ScreenDevice();
	screenDevice.ReleaseFont(iYearFont);
	screenDevice.ReleaseFont(iMonthFont);
	screenDevice.ReleaseFont(iWeekDayInitialFont);
	screenDevice.ReleaseFont(iNumeralFont);
	delete iTextSeparatingYearRange;
	delete iBitmap;
	delete iLeftDogEarBitmap;
	delete iLeftDogEarBitmapMask;
	delete iRightDogEarBitmap;
	delete iRightDogEarBitmapMask;
	//
	delete iCommandButtonOneMonth;
	delete iCommandButtonThreeMonth;
	delete iCommandButtonTwelveMonth;
	// button bitmaps deleted by the ~ of the command buttons
	iEikonEnv->RemoveFromStack(this);
	if (iFlags&EEikCalendarFlagIsWaiting)
		CActiveScheduler::Stop();
	}

EXPORT_C void CEikCalendar::SetMinimumAndMaximum(const TTime& aMinimumDate, const TTime& aMaximumDate, TBool* aMonthsWereReshuffled)
	{
	iMinimumDate=aMinimumDate;
	iMaximumDate=aMaximumDate;
	__ASSERT_ALWAYS(aMinimumDate<=aMaximumDate
					,Panic(EEikPanicCalendarBadMinimumAndMaximum));
	TTime date=Date();
	if (aMonthsWereReshuffled)
		*aMonthsWereReshuffled=EFalse; // this is done in case neither of the two SetDate calls below get called
	if (date<iMinimumDate)
		SetDateL(iMinimumDate, ETrue, aMonthsWereReshuffled); // won't leave
	else if (date>iMaximumDate)
		SetDateL(iMaximumDate, ETrue, aMonthsWereReshuffled); // won't leave
	}

EXPORT_C void CEikCalendar::GetMinimumAndMaximum(TTime& aMinimumDate, TTime& aMaximumDate) const
	{
	aMinimumDate=iMinimumDate;
	aMaximumDate=iMaximumDate;
	}

EXPORT_C void CEikCalendar::SetDateL(const TTime& aDate, TBool aDoNotReshuffleMonthsUnlessNecessary, TBool* aMonthsWereReshuffled)
	{
	TMonth oldStartMonth=iStartMonth;
	TInt oldStartYear=iStartYear;
	TDateTime newCurrentDate=aDate.DateTime();
	iCurrentDay=newCurrentDate.Day();
	TMonth newCurrentMonth=newCurrentDate.Month();
	TInt newCurrentYear=newCurrentDate.Year();
	TBool doLayout=!aDoNotReshuffleMonthsUnlessNecessary;
	if (aDoNotReshuffleMonthsUnlessNecessary)
		{
		TInt newOffsetFromStartMonthToCurrentMonth=((newCurrentYear-iStartYear)*12)+newCurrentMonth-oldStartMonth;
		if ((newOffsetFromStartMonthToCurrentMonth>=0) && (newOffsetFromStartMonthToCurrentMonth<iNumMonths))
			iOffsetFromStartMonthToCurrentMonth=newOffsetFromStartMonthToCurrentMonth;
		else
			doLayout=ETrue;
		}
	if (doLayout)
		{
		iOffsetFromStartMonthToCurrentMonth=(iFlags&EEikCalendarFlagStartMonthIsAlwaysJanuary)? newCurrentMonth: (iNumMonthsPerRowOfMonths-1)/2;
		TInt result;
		TInt remainder;
		Divide(newCurrentMonth-iOffsetFromStartMonthToCurrentMonth, 12, &result, &remainder);
		iStartMonth=(TMonth)remainder;
		iStartYear=newCurrentYear+result;
		}
	if (aMonthsWereReshuffled)
		*aMonthsWereReshuffled=((oldStartMonth!=iStartMonth) || (oldStartYear!=iStartYear));
	CheckDateIsWithinCalendarRangeL(aDate);
	}

EXPORT_C TTime CEikCalendar::Date() const
	{
	return Date(iOffsetFromStartMonthToCurrentMonth, iCurrentDay);
	}

EXPORT_C TKeyResponse CEikCalendar::OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType)
	{
	if (aType==EEventKey)
		{
		CWindowGc& gc=SystemGc();
		gc.Activate(*DrawableWindow());
		DrawCursor(gc);// Remove the previous cursor position
		gc.Deactivate();
		//
		TMonth oldStartMonth=iStartMonth;
		TInt oldStartYear=iStartYear;
		TInt previousMonth=iOffsetFromStartMonthToCurrentMonth;
		{ // braces to prevent excessive stack wind-up
		TInt offsetFromOldStartMonthToNewCurrentMonth=iOffsetFromStartMonthToCurrentMonth;
		TInt numColumnsBlankAtStartOfTopRowOfDaysOfCurrentMonth;
		TInt numDaysInCurrentMonth;
		GetMonthData(iOffsetFromStartMonthToCurrentMonth, &numColumnsBlankAtStartOfTopRowOfDaysOfCurrentMonth, &numDaysInCurrentMonth);
		TInt cellOfCurrentDay=numColumnsBlankAtStartOfTopRowOfDaysOfCurrentMonth+iCurrentDay;
		TInt rowOfCurrentDay=cellOfCurrentDay/7;
		TInt columnOfCurrentDay=cellOfCurrentDay%7;
		iFlags &= ~EEikCalendarFlagNavigateChronologically;// turn chrono nav off
		if (aKeyEvent.iModifiers&EModifierShift)
			iFlags|=EEikCalendarFlagNavigateChronologically;
		TBool navigateChronologically=(iFlags&EEikCalendarFlagNavigateChronologically);
		switch (aKeyEvent.iCode)
			{
	#if defined(_DEBUG)
		case CTRL('n'):
			{
			iFlags^=EEikCalendarFlagNavigateChronologically;
			iEikonEnv->InfoMsg(_L("Toggled navigate-chronologically"));
			gc.Activate(*DrawableWindow());
			DrawCursor(gc);
			gc.Deactivate();
			return EKeyWasConsumed;
			}
	#endif
		case EKeyEnter:
			iCalendarObserver->SetDateFromCalendarAndDrawNow(Date());
		case EKeyEscape:
			delete this;
			return EKeyWasConsumed;
		case EKeySpace:
			{
			TTime today;
			today.HomeTime();
			SetDateL(today, ETrue);
			}
			break;
		case EKeyTab:
			{
			CEikCommandButton* latchedButton=iCommandButtonOneMonth;
			if (aKeyEvent.iModifiers&EModifierShift)
				{
				if (iNumMonths==12)
					latchedButton=iCommandButtonThreeMonth;
				else if (iNumMonths==3)
					latchedButton=iCommandButtonOneMonth;
				else if (iNumMonths==1)
					latchedButton=iCommandButtonTwelveMonth;
				}
			else
				{
				if (iNumMonths==12)
					latchedButton=iCommandButtonOneMonth;
				else if (iNumMonths==3)
					latchedButton=iCommandButtonTwelveMonth;
				else if (iNumMonths==1)
					latchedButton=iCommandButtonThreeMonth;
				}
			HandleControlEventL(latchedButton,EEventStateChanged);
			return EKeyWasConsumed;
			}
			break;
		case EKeyHome:
			iOffsetFromStartMonthToCurrentMonth=0;
			iCurrentDay=DayAtRowAndColumn(iOffsetFromStartMonthToCurrentMonth,0,0, ERuleAdjustColumn);
			break;
		case EKeyEnd:
			iOffsetFromStartMonthToCurrentMonth=iNumMonths-1;
			iCurrentDay=DayAtRowAndColumn(iOffsetFromStartMonthToCurrentMonth,5, 6, ERuleAdjustColumn);
			break;
		case EKeyPageUp:
			IncrementStartMonth(-iNumMonths);
			goto processArrowKey;
			break;
		case EKeyPageDown:
			IncrementStartMonth(iNumMonths);
			goto processArrowKey;
			break;
		case EKeyLeftArrow:
			if (aKeyEvent.iModifiers&EModifierShift && aKeyEvent.iModifiers&EModifierCtrl)
				{
				IncrementStartMonth(-12);
				goto processArrowKey;
				}
			else if (aKeyEvent.iModifiers&EModifierCtrl)
				{
				/* not possible as shift+Ctrl is used for +12 months
				if (navigateChronologically)
					{
					TInt numDaysInMonthBeforeCurrentMonth;
					GetMonthData(iOffsetFromStartMonthToCurrentMonth-1, NULL, &numDaysInMonthBeforeCurrentMonth);
					iCurrentDay-=Max(numDaysInMonthBeforeCurrentMonth, iCurrentDay+1);
					goto processDayIncrement;
					}
				else*/
					//{
					MoveOneMonthToLeft(offsetFromOldStartMonthToNewCurrentMonth);
					iCurrentDay=DayAtRowAndColumn(offsetFromOldStartMonthToNewCurrentMonth, rowOfCurrentDay, columnOfCurrentDay, ERuleAdjustColumn);
					goto processArrowKey;
					//}
				}
			else
				{
				if (iNumMonths==1 || navigateChronologically || (columnOfCurrentDay>((rowOfCurrentDay==0)? numColumnsBlankAtStartOfTopRowOfDaysOfCurrentMonth: 0)))
					{
					iCurrentDay-=1;
					goto processDayIncrement;
					}
				else // hopping to an adjacent month
					{
					MoveOneMonthToLeft(offsetFromOldStartMonthToNewCurrentMonth);
					iCurrentDay=DayAtRowAndColumn(offsetFromOldStartMonthToNewCurrentMonth, rowOfCurrentDay, 6, ERuleAdjustColumn);
					goto processArrowKey;
					}
				}
			break;
		case EKeyRightArrow:
			if (aKeyEvent.iModifiers&EModifierShift && aKeyEvent.iModifiers&EModifierCtrl)
				{
				IncrementStartMonth(12);
				goto processArrowKey;
				}
			else if (aKeyEvent.iModifiers&EModifierCtrl)
				{
				MoveOneMonthToRight(offsetFromOldStartMonthToNewCurrentMonth);
				iCurrentDay=DayAtRowAndColumn(offsetFromOldStartMonthToNewCurrentMonth, rowOfCurrentDay, columnOfCurrentDay, ERuleAdjustColumn);
				goto processArrowKey;
				}
			else
				{
				TInt cellOfLastDayInCurrentMonth=numColumnsBlankAtStartOfTopRowOfDaysOfCurrentMonth+(numDaysInCurrentMonth-1);
				if (iNumMonths==1 || navigateChronologically || (columnOfCurrentDay<((rowOfCurrentDay==cellOfLastDayInCurrentMonth/7)? cellOfLastDayInCurrentMonth%7: 6)))
					{
					iCurrentDay+=1;
					goto processDayIncrement;
					}
				else // hopping to an adjacent month
					{
					MoveOneMonthToRight(offsetFromOldStartMonthToNewCurrentMonth);
					iCurrentDay=DayAtRowAndColumn(offsetFromOldStartMonthToNewCurrentMonth, rowOfCurrentDay, 0, ERuleAdjustColumn);
					goto processArrowKey;
					}
				}
			break;
		case EKeyUpArrow:
			if (aKeyEvent.iModifiers&EModifierShift && aKeyEvent.iModifiers&EModifierCtrl)
				{
				IncrementStartMonth(-12);
				goto processArrowKey;
				}
			else if (aKeyEvent.iModifiers&EModifierCtrl)
				{
				MoveOneMonthUp(offsetFromOldStartMonthToNewCurrentMonth);
				iCurrentDay=DayAtRowAndColumn(offsetFromOldStartMonthToNewCurrentMonth, rowOfCurrentDay, columnOfCurrentDay, ERuleAdjustColumn);
				goto processArrowKey;
				}
			else
				{
				if (iNumMonths==3 || navigateChronologically || (iCurrentDay>=7))
					{
					iCurrentDay-=7;
					goto processDayIncrement;
					}
				else // hopping to an adjacent month
					{
					MoveOneMonthUp(offsetFromOldStartMonthToNewCurrentMonth);
					iCurrentDay=DayAtRowAndColumn(offsetFromOldStartMonthToNewCurrentMonth, 5, columnOfCurrentDay, ERuleAdjustRow);
					goto processArrowKey;
					}
				}
			break;
		case EKeyDownArrow:
			if (aKeyEvent.iModifiers&EModifierShift && aKeyEvent.iModifiers&EModifierCtrl)
				{
				IncrementStartMonth(12);
				goto processArrowKey;
				}
			else if (aKeyEvent.iModifiers&EModifierCtrl)
				{
				MoveOneMonthDown(offsetFromOldStartMonthToNewCurrentMonth);
				iCurrentDay=DayAtRowAndColumn(offsetFromOldStartMonthToNewCurrentMonth, rowOfCurrentDay, columnOfCurrentDay, ERuleAdjustColumn);
				goto processArrowKey;
				}
			else
				{
				if (iNumMonths==3 || navigateChronologically || (iCurrentDay<numDaysInCurrentMonth-7))
					{
					iCurrentDay+=7;
					goto processDayIncrement;
					}
				else // hopping to an adjacent month
					{
					MoveOneMonthDown(offsetFromOldStartMonthToNewCurrentMonth);
					iCurrentDay=DayAtRowAndColumn(offsetFromOldStartMonthToNewCurrentMonth, 0, columnOfCurrentDay, ERuleAdjustRow);
					goto processArrowKey;
					}
				}
			break;
		processDayIncrement: // assumes iCurrentDay has been set
			{
			TInt numDaysInThisMonth;
			for (; iCurrentDay<0; GetMonthData(offsetFromOldStartMonthToNewCurrentMonth, NULL, &numDaysInThisMonth), iCurrentDay+=numDaysInThisMonth)
				--offsetFromOldStartMonthToNewCurrentMonth;
			for (; GetMonthData(offsetFromOldStartMonthToNewCurrentMonth, NULL, &numDaysInThisMonth), iCurrentDay>=numDaysInThisMonth; iCurrentDay-=numDaysInThisMonth)
				++offsetFromOldStartMonthToNewCurrentMonth;
			TBool startMonthIsAlwaysJanuary=(iFlags&EEikCalendarFlagStartMonthIsAlwaysJanuary);
			TMonth newCurrentMonth=Date(offsetFromOldStartMonthToNewCurrentMonth, 0).DateTime().Month();
			iOffsetFromStartMonthToCurrentMonth=(offsetFromOldStartMonthToNewCurrentMonth<0)?
													(startMonthIsAlwaysJanuary? newCurrentMonth: 0):
												(offsetFromOldStartMonthToNewCurrentMonth<iNumMonths)?
													offsetFromOldStartMonthToNewCurrentMonth:
													(startMonthIsAlwaysJanuary? (((iNumMonths/12)-1)*12)+newCurrentMonth: iNumMonths-1);
			}
			goto processArrowKey;
		processArrowKey: // assumes iCurrentDay, iOffsetFromStartMonthToCurrentMonth and offsetFromOldStartMonthToNewCurrentMonth have been set
			TInt result;
			TInt remainder;
			Divide((iStartMonth+offsetFromOldStartMonthToNewCurrentMonth)-iOffsetFromStartMonthToCurrentMonth, 12, &result, &remainder);
			iStartMonth=(TMonth)remainder;
			iStartYear+=result;
			break;
		default:
			goto propagateEvent;
			}
		}
		CheckDateIsWithinCalendarRangeL(Date());
		if ((oldStartMonth!=iStartMonth) || (oldStartYear!=iStartYear))
			DrawNow();
		else
			{
			gc.Activate(*DrawableWindow());
			if (previousMonth!=iOffsetFromStartMonthToCurrentMonth)
				{
				ClearOldSquare(gc,previousMonth);
				DrawNewSquare(gc);
				}
			DrawCursor(gc);// new cursor position
			gc.Deactivate();
			}
		__ASSERT_DEBUG((~iFlags&EEikCalendarFlagStartMonthIsAlwaysJanuary) || (iStartMonth==EJanuary), Panic(EEikPanicCalendarBadStartMonth1));
		return EKeyWasConsumed;
	propagateEvent:
		CheckDateIsWithinCalendarRangeL(Date());
		gc.Activate(*DrawableWindow());
		DrawCursor(gc);// redraw cursor on the same position
		gc.Deactivate();
		}
	return EKeyWasConsumed;
	}

void CEikCalendar::DrawNewSquare(CWindowGc& aGc) const
	{
	if (iNumMonths==12)
		{
		TPoint pos;
		TSize size;
		GetHighlightSquarePositionAndSize(iOffsetFromStartMonthToCurrentMonth,pos,size);
		aGc.SetPenColor(iEikonEnv->ControlColor(EEikColorControlText, *this)); //KEikCalendarForegroundColor);
		aGc.SetBrushStyle(CGraphicsContext::ENullBrush);
		aGc.DrawRect(TRect(pos,size));
		aGc.SetBrushStyle(CGraphicsContext::ESolidBrush);
		}
	}

void CEikCalendar::ClearOldSquare(CWindowGc& aGc,TInt aPreviousMonth) const
	{
	if (iNumMonths==12)
		{
		TPoint pos;
		TSize size;
		GetHighlightSquarePositionAndSize(aPreviousMonth,pos,size);
		aGc.SetPenColor(iEikonEnv->ControlColor(EEikColorControlBackground, *this)); //KEikCalendarBackgroundColor);
		aGc.SetBrushStyle(CGraphicsContext::ENullBrush);
		aGc.DrawRect(TRect(pos,size));
		aGc.SetBrushStyle(CGraphicsContext::ESolidBrush);
		}
	}

void CEikCalendar::GetHighlightSquarePositionAndSize(TInt aMonth,TPoint& aPos,TSize& aSize) const
	{
	aPos=Rect().iTl;
	TMargins margins=iBorder.Margins();
	aPos.iX+=margins.iLeft+iHorizontalSpaceOnEachSideOfMonthMatrix+
						((aMonth%iNumMonthsPerRowOfMonths)*(iSizeOfSingleMonth.iWidth+iHorizontalSpaceBetweenEachColumnOfMonths));
	aPos.iY+=margins.iTop+iVerticalSpaceAboveYear+iSizeOfYearText.iHeight+iVerticalSpaceBetweenYearAndTopRowOfMonths+
						((aMonth/iNumMonthsPerRowOfMonths)*(iSizeOfSingleMonth.iHeight+iVerticalSpaceBetweenEachRowOfMonths));
	// Tweak the position and size
	aPos.iX-=2;
	aPos.iY-=2;
	aSize=iSizeOfSingleMonth;
	aSize.iWidth+=4;
	aSize.iHeight+=2;
	}

void CEikCalendar::CheckDateIsWithinCalendarRangeL(const TTime& aDate)
	{
	if (aDate<iMinimumDate)
		{
		TDateTime dateTime=iMinimumDate.DateTime();
		iCurrentDay=dateTime.Day();
		iStartYear=dateTime.Year();
		TMonth month=dateTime.Month();
		if (iNumMonths==12)
			{
			iStartMonth=EJanuary;
			iOffsetFromStartMonthToCurrentMonth=month;
			}
		else
			{
			iStartMonth=month;
			iOffsetFromStartMonthToCurrentMonth=0;
			}
		UpdateDisplayWithInfoMsgL(ETrue, R_EIK_TBUF_BEFORE_EARLIEST_ALLOWED_DATE, iMinimumDate);
		}
	else if (aDate>iMaximumDate)
		{
		TDateTime dateTime=iMaximumDate.DateTime();
		iCurrentDay=dateTime.Day();
		iStartYear=dateTime.Year();
		TInt month=(TInt)(dateTime.Month());
		if (iNumMonths==12)
			{
			iStartMonth=EJanuary;
			iOffsetFromStartMonthToCurrentMonth=month;
			}
		else if (iNumMonths==3)
			{
			iStartMonth=(TMonth)(month-2);
			if (iStartMonth<0)
				{
				iStartMonth=(TMonth)(month-2+12);
				iStartYear-=1;
				}
			iOffsetFromStartMonthToCurrentMonth=2;
			}
		else if (iNumMonths==1)
			{
			iStartMonth=dateTime.Month();
			iOffsetFromStartMonthToCurrentMonth=0;
			}
		UpdateDisplayWithInfoMsgL(ETrue, R_EIK_TBUF_AFTER_LATEST_ALLOWED_DATE, iMaximumDate);
		}
	}

EXPORT_C TSize CEikCalendar::MinimumSize()
	{
	return iCalendarSize;
	}

EXPORT_C void CEikCalendar::HandlePointerEventL(const TPointerEvent& aPointerEvent)
	{
	TRect rect=Rect();
	CCoeControl::HandlePointerEventL(aPointerEvent);
	if (IsLeftDogEarPressed(aPointerEvent.iPosition) || IsRightDogEarPressed(aPointerEvent.iPosition))
		{
		if (aPointerEvent.iType==TPointerEvent::EButton1Up)
			{
			TInt increment=iNumMonths;// True for (IsRightDogEarPressed(aPointerEvent.iPosition))
			if (IsLeftDogEarPressed(aPointerEvent.iPosition))
				increment*=-1;
			IncrementStartMonth(increment);
			CheckDateIsWithinCalendarRangeL(Date());
			DrawNow();
			__ASSERT_DEBUG(!(iFlags&EEikCalendarFlagStartMonthIsAlwaysJanuary) || (iStartMonth==EJanuary), Panic(EEikPanicCalendarBadStartMonth2));
			}
		return;
		}
	if ( !rect.Contains(aPointerEvent.iPosition))
		{// pointer event outside calendar
		if (aPointerEvent.iType==TPointerEvent::EDrag)
			return;
		TRect effectiveCalRect=rect;
		effectiveCalRect.Grow(KExtraCalBorder,KExtraCalBorder);
		if (! effectiveCalRect.Contains(aPointerEvent.iPosition))
			{
			delete this;
			return;
			}
		else
			{// just outside cal border
			TInt increment=0;
			TPoint tl(rect.iTl.iX-KExtraCalBorder,rect.iBr.iY-iLeftDogEarBitmap->SizeInPixels().iHeight);
			TPoint br(rect.iTl.iX+iLeftDogEarBitmap->SizeInPixels().iWidth,rect.iBr.iY+KExtraCalBorder);
			TRect rectJustOutsideDogear(tl,br);
			if (rectJustOutsideDogear.Contains(aPointerEvent.iPosition))
				increment=-iNumMonths;// left
			rectJustOutsideDogear.iTl.iX=rect.iBr.iX-iLeftDogEarBitmap->SizeInPixels().iWidth;
			rectJustOutsideDogear.iTl.iY=rect.iBr.iY-iLeftDogEarBitmap->SizeInPixels().iHeight;
			rectJustOutsideDogear.iBr.iX=rect.iBr.iX+KExtraCalBorder;
			rectJustOutsideDogear.iBr.iY=rect.iBr.iY+KExtraCalBorder;
			if (rectJustOutsideDogear.Contains(aPointerEvent.iPosition))
				increment=iNumMonths;// right
			if (increment==0)
				{
				delete this;
				return;
				}
			else if (aPointerEvent.iType==TPointerEvent::EButton1Up)
				{// just outside dogear
				IncrementStartMonth(increment);
				CheckDateIsWithinCalendarRangeL(Date());
				DrawNow();
				__ASSERT_DEBUG(!(iFlags&EEikCalendarFlagStartMonthIsAlwaysJanuary) || (iStartMonth==EJanuary), Panic(EEikPanicCalendarBadStartMonth2));
				}
			return;
			}
		}
	if (GrabbingComponent())
		return;
	TSize dogearSize=iLeftDogEarBitmap->SizeInPixels();
	TRect topDeadSpace(rect.iTl.iX+EWidthOfButton*ENumberOfButtons,
						rect.iTl.iY,
						rect.iBr.iX,
						rect.iTl.iY+iVerticalSpaceAboveYear+iSizeOfYearText.iHeight+iVerticalSpaceBetweenYearAndTopRowOfMonths);
	TRect bottomDeadSpace(rect.iTl.iX+dogearSize.iWidth,
						rect.iBr.iY-dogearSize.iHeight,
						rect.iBr.iX-dogearSize.iWidth,
						rect.iBr.iY);
	if (topDeadSpace.Contains(aPointerEvent.iPosition) || bottomDeadSpace.Contains(aPointerEvent.iPosition))
		return;
	if (aPointerEvent.iType==TPointerEvent::EButton1Down || aPointerEvent.iType==TPointerEvent::EDrag)
		{
		CWindowGc& gc=SystemGc();
		gc.Activate(*DrawableWindow());
		DrawCursor(gc);// erase old cursor
		gc.Deactivate();
		TInt previousMonth=iOffsetFromStartMonthToCurrentMonth;
		TInt newOffsetFromStartMonthToCurrentMonth;
		TInt newCurrentDay;
		GetNearestDateFromPosition(aPointerEvent.iPosition, newOffsetFromStartMonthToCurrentMonth, newCurrentDay);
		iOffsetFromStartMonthToCurrentMonth=newOffsetFromStartMonthToCurrentMonth;
		iCurrentDay=newCurrentDay;
		gc.Activate(*DrawableWindow());
		DrawCursor(gc);// draw new cursor
		if (previousMonth!=newOffsetFromStartMonthToCurrentMonth)
			{
			ClearOldSquare(gc,previousMonth);
			DrawNewSquare(gc);
			}
		gc.Deactivate();
		}
	else if (aPointerEvent.iType==TPointerEvent::EButton1Up 
		&& rect.Contains(aPointerEvent.iPosition))
		{
		CheckDateIsWithinCalendarRangeL(Date());// resets date and leaves if outside range
		if (iCommandButtonOneMonth->Rect().Contains(aPointerEvent.iPosition)// Close calendar on buttonUp
			|| iCommandButtonThreeMonth->Rect().Contains(aPointerEvent.iPosition)
			|| iCommandButtonTwelveMonth->Rect().Contains(aPointerEvent.iPosition))
			return;// pointer up event on buttons
		else
			{
			iCalendarObserver->SetDateFromCalendarAndDrawNow(Date());
			delete this;
			return;
			}
		}
	}

TBool CEikCalendar::IsLeftDogEarPressed(const TPoint aPos) const
	{
	TPoint tl(Rect().iTl.iX,Rect().iBr.iY-iLeftDogEarBitmap->SizeInPixels().iHeight);
	TPoint br(Rect().iTl.iX+iLeftDogEarBitmap->SizeInPixels().iWidth,Rect().iBr.iY);
	TRect dogEar(tl,br);
	if (dogEar.Contains(aPos))
		return ETrue;
	return EFalse;
	}

TBool CEikCalendar::IsRightDogEarPressed(const TPoint aPos) const
	{
	TPoint tl(Rect().iBr.iX-iRightDogEarBitmap->SizeInPixels().iWidth,Rect().iBr.iY-iRightDogEarBitmap->SizeInPixels().iHeight);
	TPoint br(Rect().iBr.iX,Rect().iBr.iY);
	TRect dogEar(tl,br);
	if (dogEar.Contains(aPos))
		return ETrue;
	return EFalse;
	}

EXPORT_C void CEikCalendar::SizeChangedL()
	{
	__ASSERT_ALWAYS((iSize.iWidth>=iCalendarSize.iWidth) && (iSize.iHeight>=iCalendarSize.iHeight), Panic(EEikPanicCalendarBadSize));
	TMargins margins=iBorder.Margins();
	TPoint buttonPosition=Rect().iTl;
	TInt heightOfButton=iVerticalSpaceAboveYear+iSizeOfYearText.iHeight+iVerticalSpaceBetweenYearAndTopRowOfMonths;
	heightOfButton-=EExtraHeightForHighlightSquare;
	buttonPosition.iX+=margins.iLeft;
	buttonPosition.iY+=margins.iTop;
	iCommandButtonOneMonth->SetExtentL(buttonPosition,TSize(EWidthOfButton,heightOfButton));
	buttonPosition.iX+=EWidthOfButton;
	iCommandButtonThreeMonth->SetExtentL(buttonPosition,TSize(EWidthOfButton,heightOfButton));
	buttonPosition.iX+=EWidthOfButton;
	iCommandButtonTwelveMonth->SetExtentL(buttonPosition,TSize(EWidthOfButton,heightOfButton));
	}

EXPORT_C TInt CEikCalendar::CountComponentControls() const
	{
	return 3;
	}

EXPORT_C CCoeControl* CEikCalendar::ComponentControl(TInt aIndex) const
	{
	TInt i=0;
	if (i==aIndex)
		return iCommandButtonOneMonth;
	++i;
	if (i==aIndex)
		return iCommandButtonThreeMonth;
	++i;
	if (i==aIndex)
		return iCommandButtonTwelveMonth;
	Panic(EEikPanicCalendarBadComponentControlIndex);
	return NULL; // dummy return to prevent compiler error
	}

EXPORT_C void CEikCalendar::Draw(const TRect& /*aRect*/) const
	{
#if defined(DO_PROFILING)
	RDebug::ProfileReset(PROFILE_ID, 1);
	RDebug::ProfileStart(PROFILE_ID);
#endif
	__ASSERT_ALWAYS(iContext || (iSize==iCalendarSize), Panic(EEikPanicCalendarNoControlContext));
	CEikBorderedControl::Draw(Rect());
	CWindowGc& gc=SystemGc();
	TMargins margins=iBorder.Margins();
	gc.SetPenStyle(CGraphicsContext::ESolidPen);
	gc.SetPenColor(iEikonEnv->ControlColor(EEikColorControlText, *this)); //KEikCalendarForegroundColor);
	gc.SetBrushStyle(CGraphicsContext::ESolidBrush);
	gc.SetBrushColor(iEikonEnv->ControlColor(EEikColorControlBackground, *this)); //KEikCalendarBackgroundColor);
	TInt ascentInPixelsOfFontBeingUsed;
	const CFont* fontBeingUsed=NULL;
	DrawYear(gc, ascentInPixelsOfFontBeingUsed, fontBeingUsed);
	DrawEmptyAreas(gc);
	// draw dogears
	TRect dogEarRect(TPoint(0,0),iLeftDogEarBitmap->SizeInPixels());
	TPoint leftDogEarPosition(Rect().iTl.iX+margins.iLeft,Rect().iBr.iY-margins.iBottom-dogEarRect.Height());
	TPoint rightDogEarPosition(Rect().iBr.iX-margins.iRight-dogEarRect.Width(),Rect().iBr.iY-margins.iBottom-dogEarRect.Height());
	gc.BitBltMasked(leftDogEarPosition,iLeftDogEarBitmap,
					dogEarRect,iLeftDogEarBitmapMask,ETrue);
	gc.BitBltMasked(rightDogEarPosition,iRightDogEarBitmap,
					dogEarRect,iRightDogEarBitmapMask,ETrue);
	DrawMonths(gc, ascentInPixelsOfFontBeingUsed, fontBeingUsed);
	DrawCursor(gc);
	
#if defined(DO_PROFILING)
	gc.Deactivate();
	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);
	CEikonEnv::Static()->VerboseInfoMsg(buffer);
	gc.Activate(*DrawableWindow());
#endif
	}

void CEikCalendar::DrawCursor(CWindowGc& aGc) const
	{
	TMargins margins=iBorder.Margins();
	aGc.SetDrawMode(CGraphicsContext::EDrawModeNOTSCREEN);
	aGc.SetBrushStyle(CGraphicsContext::ESolidBrush);
	// Blank out the cursor on its last position
	TInt numColumnsBlankAtStartOfTopRowOfDays;
	GetMonthData(iOffsetFromStartMonthToCurrentMonth, &numColumnsBlankAtStartOfTopRowOfDays, NULL);
	TInt cellOfCurrentDay=numColumnsBlankAtStartOfTopRowOfDays+iCurrentDay;
	TPoint cursorPosition=Rect().iTl;
	cursorPosition.iX+=margins.iLeft+iHorizontalSpaceOnEachSideOfMonthMatrix+
						((iOffsetFromStartMonthToCurrentMonth%iNumMonthsPerRowOfMonths)*(iSizeOfSingleMonth.iWidth+iHorizontalSpaceBetweenEachColumnOfMonths))+
						((cellOfCurrentDay%7)*(iSizeOfSingleDay.iWidth+iHorizontalSpaceBetweenEachColumnOfDays));
	cursorPosition.iY+=margins.iTop+iVerticalSpaceAboveYear+iSizeOfYearText.iHeight+iVerticalSpaceBetweenYearAndTopRowOfMonths+
						((iOffsetFromStartMonthToCurrentMonth/iNumMonthsPerRowOfMonths)*(iSizeOfSingleMonth.iHeight+iVerticalSpaceBetweenEachRowOfMonths))+
						iMonthFont->HeightInPixels()+iVerticalSpaceBetweenMonthNameAndDayNameInitials+iHeightOfWeekDayInitials+iVerticalSpaceBetweenDayNameInitialsAndTopRowOfDays+
						((cellOfCurrentDay/7)*(iSizeOfSingleDay.iHeight+iVerticalSpaceBetweenEachRowOfDays));
	//
	if (iDayOfToday==iCurrentDay && 
		((iMonthOfToday==iStartMonth+iOffsetFromStartMonthToCurrentMonth && iYearOfToday==iStartYear)
		|| (iMonthOfToday==iStartMonth+iOffsetFromStartMonthToCurrentMonth-12 && iYearOfToday==iStartYear+1)))
		{
		TPoint circlePosition(cursorPosition);
		TSize daySize(iSizeOfSingleDay);
		// !! Customize the sizes of the today circles for the different views
		circlePosition.iX-=2;
		circlePosition.iY-=4;
		daySize.iWidth+=4;
		daySize.iHeight+=4;
		if (iNumMonths==12)
			{// The iSizeOfSingleDay is not a square for 12 month view
			circlePosition.iX+=2;
			circlePosition.iY+=1;
			daySize.iWidth-=4;
			daySize.iHeight-=2;
			}
		//
		__ASSERT_DEBUG(daySize.iWidth==daySize.iHeight,Panic(EEikPanicCalendarBadSize));
		aGc.SetPenStyle(CGraphicsContext::ENullPen);
		aGc.DrawEllipse(TRect(circlePosition,daySize));
		aGc.SetPenStyle(CGraphicsContext::ESolidPen);
		}
	else
		{
		// Customize the size and position of the cursors for the different views
		TSize sizeOfCursor;
		if (iNumMonths==12)
			{
			sizeOfCursor.iWidth=iSizeOfSingleDay.iWidth-1;
			sizeOfCursor.iHeight=iSizeOfSingleDay.iHeight -3;
			cursorPosition.iY-=1; //1;
			cursorPosition.iX+=1;
			}
		else
			{
			sizeOfCursor.iWidth=iSizeOfSingleDay.iWidth+2;
			sizeOfCursor.iHeight=iSizeOfSingleDay.iHeight;
			cursorPosition.iX-=1;
			cursorPosition.iY-=2;
			}

		aGc.DrawRect(TRect(cursorPosition,sizeOfCursor));
		}
	aGc.SetDrawMode(CGraphicsContext::EDrawModePEN);
	}

struct SCalResizeCleanup { CEikCalendar* iCal; };

LOCAL_C void HandleResizeFailure(TAny* aPtr)
	{
	((SCalResizeCleanup*)aPtr)->iCal->MakeVisible(ETrue);
	}

EXPORT_C void CEikCalendar::HandleControlEventL(CCoeControl* aControl, TCoeEvent aEventType)
	{
	// reset the data members for the 1,3,12 month view
	// deals with the differences between the 1,3,12 month resource files
	if (aEventType==EEventStateChanged)
		{
		TInt lastView=iNumMonths;
		if ((lastView==1 && aControl==iCommandButtonOneMonth)
			|| (lastView==3 && aControl==iCommandButtonThreeMonth)
			|| (lastView==12 && aControl==iCommandButtonTwelveMonth))
			return;
		TInt id=R_EIK_ONE_MONTH_CALENDAR;
		if (aControl==iCommandButtonOneMonth)
			id=R_EIK_ONE_MONTH_CALENDAR;
		else if (aControl==iCommandButtonThreeMonth)
			id=R_EIK_THREE_MONTH_CALENDAR;
		else if (aControl==iCommandButtonTwelveMonth)
			id=R_EIK_TWELVE_MONTH_CALENDAR;
		else
			goto propagateEvent;
		Window().Invalidate();
		MakeVisible(EFalse);
		SCalResizeCleanup cleanup;
		cleanup.iCal=this;
		CleanupStack::PushL(TCleanupItem(HandleResizeFailure,&cleanup));
		ConstructMonthViewL(id,Date());
		CleanupStack::Pop(); // cleanup
		((CEikCommandButton*)aControl)->SetState(CEikButtonBase::ESet);
		MakeVisible(ETrue);
		CheckDateIsWithinCalendarRangeL(Date());
		DrawNow();
//		__ASSERT_DEBUG(!(iFlags&EEikCalendarFlagStartMonthIsAlwaysJanuary) || (iStartMonth==EJanuary), Panic(EEikPanicCalendarBadStartMonth2));
		return;
		}
	propagateEvent:
	ReportEventL(aEventType);
	}

TTime CEikCalendar::Date(TInt aOffsetFromStartMonthToThisMonth, TInt aThisDay) const
	{
	TInt result;
	TInt remainder;
	Divide(iStartMonth+aOffsetFromStartMonthToThisMonth, 12, &result, &remainder);
	return TTime(TDateTime(iStartYear+result, (TMonth)remainder, aThisDay, 0, 0, 0, 0));
	}

TInt CEikCalendar::DayAtRowAndColumn(TInt aOffsetFromStartMonthToThisMonth, TInt aRow, TInt aColumn, TRule aRule) const
	{
	__ASSERT_DEBUG((aRow>=0) && (aRow<6) && (aColumn>=0) && (aColumn<7), Panic(EEikPanicCalendarBadRowOrColumn));
	TInt numColumnsBlankAtStartOfTopRowOfDays;
	TInt numDaysInThisMonth;
	GetMonthData(aOffsetFromStartMonthToThisMonth, &numColumnsBlankAtStartOfTopRowOfDays, &numDaysInThisMonth);
	TInt cellOfLastDayInThisMonth=numColumnsBlankAtStartOfTopRowOfDays+(numDaysInThisMonth-1);
	TInt lastRowInThisMonth=cellOfLastDayInThisMonth/7;
	if (aRow>lastRowInThisMonth)
		aRow=lastRowInThisMonth;
	if (aRow==0)
		{
		if (aColumn<numColumnsBlankAtStartOfTopRowOfDays)
			{
			switch (aRule)
				{
			case ERuleAdjustRow:
				++aRow;
				break;
			case ERuleAdjustColumn:
				aColumn=numColumnsBlankAtStartOfTopRowOfDays;
				break;
			default:
#if defined(_DEBUG)
				Panic(EEikPanicCalendarBadRule1);
#endif
				break;
				}
			}
		}
	if (aRow==lastRowInThisMonth)
		{
		TInt columnOfLastDayInThisMonth=cellOfLastDayInThisMonth%7;
		if (aColumn>columnOfLastDayInThisMonth)
			{
			switch (aRule)
				{
			case ERuleAdjustRow:
				--aRow;
				break;
			case ERuleAdjustColumn:
				aColumn=columnOfLastDayInThisMonth;
				break;
			default:
#if defined(_DEBUG)
				Panic(EEikPanicCalendarBadRule2);
#endif
				break;
				}
			}
		}
	return ((aRow*7)+aColumn)-numColumnsBlankAtStartOfTopRowOfDays;
	}

void CEikCalendar::IncrementStartMonth(TInt aIncrement)
	{
	TInt result;
	TInt remainder;
	Divide(iStartMonth+aIncrement, 12, &result, &remainder);
	iStartMonth=(TMonth)remainder;
	iStartYear+=result;
	TInt numDaysInNewCurrentMonth;
	GetMonthData(iOffsetFromStartMonthToCurrentMonth, NULL, &numDaysInNewCurrentMonth);
	TInt lastDayInNewCurrentMonth=numDaysInNewCurrentMonth-1;
	if (iCurrentDay>lastDayInNewCurrentMonth)
		iCurrentDay=lastDayInNewCurrentMonth;
	}

void CEikCalendar::MoveOneMonthToLeft(TInt& aOffsetFromOldStartMonthToNewCurrentMonth)
	{
	if (iOffsetFromStartMonthToCurrentMonth>EJanuary)
		{
		--iOffsetFromStartMonthToCurrentMonth;
		aOffsetFromOldStartMonthToNewCurrentMonth=iOffsetFromStartMonthToCurrentMonth;
		}
	else
		{
		__ASSERT_DEBUG(iOffsetFromStartMonthToCurrentMonth==EJanuary,Panic(EEikPanicCalendarBadNumMonths));
		iOffsetFromStartMonthToCurrentMonth=iNumMonths-1;
		aOffsetFromOldStartMonthToNewCurrentMonth=-1;
		}
	}

void CEikCalendar::MoveOneMonthToRight(TInt& aOffsetFromOldStartMonthToNewCurrentMonth)
	{
	if (iOffsetFromStartMonthToCurrentMonth<iNumMonths-1)
		{
		++iOffsetFromStartMonthToCurrentMonth;
		aOffsetFromOldStartMonthToNewCurrentMonth=iOffsetFromStartMonthToCurrentMonth;
		}
	else
		{
		__ASSERT_DEBUG(iOffsetFromStartMonthToCurrentMonth==iNumMonths-1,Panic(EEikPanicCalendarBadNumMonths));
		iOffsetFromStartMonthToCurrentMonth=0;
		aOffsetFromOldStartMonthToNewCurrentMonth=iNumMonths;
		}
	}

void CEikCalendar::MoveOneMonthUp(TInt& aOffsetFromOldStartMonthToNewCurrentMonth)
	{
	if (iOffsetFromStartMonthToCurrentMonth/iNumMonthsPerRowOfMonths>0)
		{
		iOffsetFromStartMonthToCurrentMonth-=iNumMonthsPerRowOfMonths;
		aOffsetFromOldStartMonthToNewCurrentMonth=iOffsetFromStartMonthToCurrentMonth;
		}
	else
		{
		iOffsetFromStartMonthToCurrentMonth=(iNumMonthsPerRowOfMonths*((iNumMonths/iNumMonthsPerRowOfMonths)-1))+(iOffsetFromStartMonthToCurrentMonth%iNumMonthsPerRowOfMonths);
		aOffsetFromOldStartMonthToNewCurrentMonth=(-iNumMonths)+iOffsetFromStartMonthToCurrentMonth;
		}
	}

void CEikCalendar::MoveOneMonthDown(TInt& aOffsetFromOldStartMonthToNewCurrentMonth)
	{
	if (iOffsetFromStartMonthToCurrentMonth/iNumMonthsPerRowOfMonths<(iNumMonths/iNumMonthsPerRowOfMonths)-1)
		{
		iOffsetFromStartMonthToCurrentMonth+=iNumMonthsPerRowOfMonths;
		aOffsetFromOldStartMonthToNewCurrentMonth=iOffsetFromStartMonthToCurrentMonth;
		}
	else
		{
		iOffsetFromStartMonthToCurrentMonth=iOffsetFromStartMonthToCurrentMonth%iNumMonthsPerRowOfMonths;
		aOffsetFromOldStartMonthToNewCurrentMonth=iNumMonths+iOffsetFromStartMonthToCurrentMonth;
		}
	}

void CEikCalendar::UpdateDisplayWithInfoMsgL(TBool aFullDraw, TInt aResourceId, const TTime& aTimeDate) const
	{
	if (aFullDraw)
		DrawNow();
	TEikInfoMsgBuf format;
	iEikonEnv->ReadResource(format, aResourceId);
	TEikInfoMsgBuf text;
	TRAPD(notUsed, aTimeDate.FormatL(text, format)); // clip the text to the buffer-length, ignoring any overflow
	iEikonEnv->InfoMsg(text);
	CBaActiveScheduler::LeaveNoAlert();
	}

TPoint CEikCalendar::CalendarPosition() const
	{
	TSize sizeOfScreen=iCoeEnv->ScreenDevice()->SizeInPixels();
	return TPoint((sizeOfScreen.iWidth-iCalendarSize.iWidth)/2,(sizeOfScreen.iHeight-iCalendarSize.iHeight)/2);
	}

void CEikCalendar::DrawYear(CWindowGc& aGc, TInt& aAscentInPixelsOfFontBeingUsed, const CFont*& aFontBeingUsed) const
	{
	TInt offsetFromStartYearToEndYear=(iStartMonth+(iNumMonths-1))/12;
	TBuf<64> buffer;
	buffer.Format(_L("%d"), iStartYear);
	if (offsetFromStartYearToEndYear>0)
		{
		buffer.Append(*iTextSeparatingYearRange);
		buffer.AppendFormat(_L("%d"), iStartYear+offsetFromStartYearToEndYear);
		}
	TRect drawableRect;
	TPoint tl;
	TPoint br;
	TRect rect=iBorder.InnerRect(Rect());
	TInt heightOfCalendarHeader=iVerticalSpaceAboveYear+iSizeOfYearText.iHeight+iVerticalSpaceBetweenYearAndTopRowOfMonths;	
	aGc.SetBrushStyle(CGraphicsContext::ESolidBrush);
	aGc.SetBrushColor(iEikonEnv->ControlColor(EEikColorControlBackground, *this)); //KEikCalendarBackgroundColor);
	if (iNumMonths==1)
		{
		// buttons on the left and year on right
		tl.iX=rect.iTl.iX+ENumberOfButtons*EWidthOfButton;
		tl.iY=rect.iTl.iY;
		br.iX=rect.iBr.iX;
		br.iY=rect.iTl.iY+heightOfCalendarHeader;
		drawableRect.iTl=tl;
		drawableRect.iBr=br;
		aGc.SetPenStyle(CGraphicsContext::ESolidPen);
		UseFontIfNotBeingUsed(aGc, aAscentInPixelsOfFontBeingUsed, aFontBeingUsed, iYearFont);
		aGc.DrawText(buffer, drawableRect, iVerticalSpaceAboveYear+iYearFont->AscentInPixels(), CGraphicsContext::ECenter);
		}
	else
		{
		// the top right blank area
		// buttons are positioned in the corresponding left area
		tl.iX=rect.iBr.iX-ENumberOfButtons*EWidthOfButton;
		tl.iY=rect.iTl.iY;
		br.iX=rect.iBr.iX;
		br.iY=rect.iTl.iY+heightOfCalendarHeader;
		drawableRect.iTl=tl;
		drawableRect.iBr=br;
		aGc.SetPenStyle(CGraphicsContext::ENullPen);
		aGc.DrawRect(drawableRect);
		// the rect containing the year
		tl.iX=rect.iTl.iX+ENumberOfButtons*EWidthOfButton;
		tl.iY=rect.iTl.iY;
		br.iX=rect.iBr.iX-ENumberOfButtons*EWidthOfButton;
		br.iY=rect.iTl.iY+heightOfCalendarHeader;
		drawableRect.iTl=tl;
		drawableRect.iBr=br;
		aGc.SetPenStyle(CGraphicsContext::ESolidPen);
		UseFontIfNotBeingUsed(aGc, aAscentInPixelsOfFontBeingUsed, aFontBeingUsed, iYearFont);
		aGc.DrawText(buffer, drawableRect, iVerticalSpaceAboveYear+iYearFont->AscentInPixels(), CGraphicsContext::ECenter);
		}
	// the area beneath the buttons
	TInt heightOfButton=heightOfCalendarHeader-EExtraHeightForHighlightSquare;
	tl.iX=rect.iTl.iX;
	tl.iY=rect.iTl.iY+heightOfButton;
	br.iX=rect.iTl.iX+ENumberOfButtons*EWidthOfButton;
	br.iY=rect.iTl.iY+heightOfCalendarHeader;
	drawableRect.iTl=tl;
	drawableRect.iBr=br;
	aGc.SetPenStyle(CGraphicsContext::ENullPen);
	aGc.DrawRect(drawableRect);
	aGc.SetPenStyle(CGraphicsContext::ESolidPen);
	}

void CEikCalendar::DrawEmptyAreas(CWindowGc& aGc) const
	{
	TRect rect=iBorder.InnerRect(Rect());
	TInt heightOfCalendarHeader=iVerticalSpaceAboveYear+iSizeOfYearText.iHeight+iVerticalSpaceBetweenYearAndTopRowOfMonths;	
	aGc.SetBrushStyle(CGraphicsContext::ESolidBrush);
	aGc.SetBrushColor(iEikonEnv->ControlColor(EEikColorControlBackground, *this)); //KEikCalendarBackgroundColor);
	aGc.SetPenStyle(CGraphicsContext::ENullPen);
	TPoint position(rect.iTl.iX,rect.iTl.iY+heightOfCalendarHeader);
	TSize dogearSize=iLeftDogEarBitmap->SizeInPixels();
	TSize sizeOfSpaceOnEachSideOfMonthMatrix(iHorizontalSpaceOnEachSideOfMonthMatrix, rect.Height()-heightOfCalendarHeader-dogearSize.iHeight);
	sizeOfSpaceOnEachSideOfMonthMatrix.iHeight+=1;// needed as dogear height is odd
	TSize sizeOfSpaceBetweenEachColumnOfMonths(iHorizontalSpaceBetweenEachColumnOfMonths, rect.Height()-heightOfCalendarHeader);
	// left side blank area // blank areas between month columns // right side blank area
	if (sizeOfSpaceOnEachSideOfMonthMatrix.iWidth)
		{
		aGc.DrawRect(TRect(position, sizeOfSpaceOnEachSideOfMonthMatrix));
		position.iX+=sizeOfSpaceOnEachSideOfMonthMatrix.iWidth;
		}
	for (TInt i=0; ; )
		{
		position.iX+=iSizeOfSingleMonth.iWidth;
		++i;
		if (i>=iNumMonthsPerRowOfMonths)
			break;
		if (sizeOfSpaceBetweenEachColumnOfMonths.iWidth)
			{
			aGc.DrawRect(TRect(position, sizeOfSpaceBetweenEachColumnOfMonths));
			position.iX+=sizeOfSpaceBetweenEachColumnOfMonths.iWidth;
			}
		}
	if (sizeOfSpaceOnEachSideOfMonthMatrix.iWidth)
		{
		aGc.DrawRect(TRect(position, sizeOfSpaceOnEachSideOfMonthMatrix));
		position.iX+=sizeOfSpaceOnEachSideOfMonthMatrix.iWidth;
		}
	// Space at bottom
	TPoint tl;
	TPoint br;
	tl.iX=rect.iTl.iX+dogearSize.iWidth;
	tl.iY=rect.iBr.iY-iVerticalSpaceBeneathBottomRowOfMonths;
	br.iX=rect.iBr.iX-dogearSize.iWidth;
	br.iY=rect.iBr.iY;
	aGc.DrawRect(TRect(tl,br));
	aGc.SetPenStyle(CGraphicsContext::ESolidPen);
	}


void CEikCalendar::DrawMonths(CWindowGc& aGc, TInt& aAscentInPixelsOfFontBeingUsed, const CFont*& aFontBeingUsed) const
	{
	TMargins margins=iBorder.Margins();
	TPoint calendarPosition=Rect().iTl;
	TInt leftEdgeOfMonthMatrix=calendarPosition.iX+margins.iLeft+iHorizontalSpaceOnEachSideOfMonthMatrix;
	TPoint positionOfMonth(leftEdgeOfMonthMatrix, calendarPosition.iY+margins.iTop+iVerticalSpaceAboveYear+iSizeOfYearText.iHeight+iVerticalSpaceBetweenYearAndTopRowOfMonths);
	TInt verticalSpaceBeneathMonth=iVerticalSpaceBetweenEachRowOfMonths;
	TInt firstColumnOfBottomRowOfMonths=iNumMonths-iNumMonthsPerRowOfMonths;
	for (TInt i=0; i<iNumMonths; )
		{
		
		if (i==firstColumnOfBottomRowOfMonths)
			verticalSpaceBeneathMonth=iVerticalSpaceBeneathBottomRowOfMonths;
		
		TSize size(iSizeOfSingleMonth.iWidth, iMonthFont->HeightInPixels()+iVerticalSpaceBetweenMonthNameAndDayNameInitials);
		TInt offsetFromStartOfStartYearToThisMonth=iStartMonth+i;
		TMonth thisMonth=(TMonth)(offsetFromStartOfStartYearToThisMonth%12);
		//TInt thisYear=iStartYear+(offsetFromStartOfStartYearToThisMonth/12);

		// draw the month name
		UseFontIfNotBeingUsed(aGc, aAscentInPixelsOfFontBeingUsed, aFontBeingUsed, iMonthFont);
		aGc.DrawText(TMonthName(thisMonth), TRect(positionOfMonth, size), iMonthFont->AscentInPixels(), CGraphicsContext::ECenter);
		// blit the day initials to the calendar screen
		TPoint positionOfInitialsOnCalendar=positionOfMonth;
		positionOfInitialsOnCalendar.iY+=size.iHeight;
		size.iWidth=WidthOfRangeOfColumnsOfDays(0,7-1);
		size.iHeight=iHeightOfWeekDayInitials+iVerticalSpaceBetweenDayNameInitialsAndTopRowOfDays;
		const TPoint positionOfInitialsInBitmap(31*iSizeOfSingleDay.iWidth+32*iHorizontalSpaceBetweenEachColumnOfDays,0);
		aGc.BitBlt(positionOfInitialsOnCalendar,iBitmap,TRect(positionOfInitialsInBitmap,size));
		// prepare to draw the month 
		TPoint blitFromBitmapPosition;
		TPoint blitToScreenPosition(positionOfMonth.iX,positionOfInitialsOnCalendar.iY+iHeightOfWeekDayInitials+iVerticalSpaceBetweenDayNameInitialsAndTopRowOfDays);
		//
		TInt heightOfSingleDayPlusVerticalSpaceBetweenEachRowOfDays=iSizeOfSingleDay.iHeight+iVerticalSpaceBetweenEachRowOfDays;
		size.iHeight=heightOfSingleDayPlusVerticalSpaceBetweenEachRowOfDays;
		TInt numColumnsBlankAtStartOfTopRowOfDays;
		TInt numDaysInThisMonth;
		GetMonthData(i, &numColumnsBlankAtStartOfTopRowOfDays, &numDaysInThisMonth);
		TInt numRowsOfDays=(numColumnsBlankAtStartOfTopRowOfDays+numDaysInThisMonth+6)/7;
		TInt numCellsInDayMatrix=numRowsOfDays*7;
		TInt firstColumnOfBottomRowOfDays=numCellsInDayMatrix-7;
		// do bliting of numbers and clearing of rects 
		for (TInt j=0; j<numCellsInDayMatrix; )// either 28,35,42 days
			{
			TInt column=j%7;
			if (j==firstColumnOfBottomRowOfDays)
				{
				size.iHeight=((6-numRowsOfDays)*heightOfSingleDayPlusVerticalSpaceBetweenEachRowOfDays)+iSizeOfSingleDay.iHeight;
				if (i<iNumMonthsPerRowOfMonths && iNumMonths>iNumMonthsPerRowOfMonths)
					size.iHeight+=verticalSpaceBeneathMonth;
				}
			TInt thisDay=j-numColumnsBlankAtStartOfTopRowOfDays;
			//
			if (thisDay<0)
				{
				size.iWidth=WidthOfRangeOfColumnsOfDays(0, numColumnsBlankAtStartOfTopRowOfDays-1);
				aGc.Clear(TRect(blitToScreenPosition, size));// cleared area before the 1st of the month
				j+=numColumnsBlankAtStartOfTopRowOfDays;
				}
			else if (thisDay<numDaysInThisMonth)
				{
				TInt row=j/7;
				TInt numDaysInRow;
				if (row==0)
					numDaysInRow=7-numColumnsBlankAtStartOfTopRowOfDays;
				else if (row==numRowsOfDays-1)
					{
					numDaysInRow=(numColumnsBlankAtStartOfTopRowOfDays+numDaysInThisMonth)%7;
					if (numDaysInRow==0)
						numDaysInRow=7;
					}
				else
					numDaysInRow=7;
				
				blitFromBitmapPosition.iX=(thisDay*(iHorizontalSpaceBetweenEachColumnOfDays+iSizeOfSingleDay.iWidth))+((column%2==0)? iHorizontalSpaceBetweenEachColumnOfDays: 0);//iY=0
				size.iWidth=WidthOfRangeOfColumnsOfDays(column, column+(numDaysInRow-1));
				aGc.BitBlt(blitToScreenPosition,iBitmap,TRect(blitFromBitmapPosition,size));
				// Redraw highlight square here to reduce flicker
				if (i==iOffsetFromStartMonthToCurrentMonth && iNumMonths==12)
					DrawNewSquare(aGc);
				j+=numDaysInRow;
				}
			else
				{
				size.iWidth=WidthOfRangeOfColumnsOfDays(column, 7-1);
				aGc.Clear(TRect(blitToScreenPosition, size));
				if (i==iOffsetFromStartMonthToCurrentMonth && iNumMonths==12)
					DrawNewSquare(aGc);
				break;
				}
			if (j%7>0) // N.B. uses freshly incremented j
				blitToScreenPosition.iX+=size.iWidth;
			else
				{
				blitToScreenPosition.iX=positionOfMonth.iX;
				blitToScreenPosition.iY+=size.iHeight;
				}
			}
		if ((iMonthOfToday==iStartMonth+i && iYearOfToday==iStartYear)
			|| (iMonthOfToday==iStartMonth+i-12 && iYearOfToday==iStartYear+1))
			{
			TInt cellOfToday=iDayOfToday+numColumnsBlankAtStartOfTopRowOfDays;
			TPoint circlePosition;
			circlePosition.iX+=positionOfMonth.iX+
						((cellOfToday%7)*(iSizeOfSingleDay.iWidth+iHorizontalSpaceBetweenEachColumnOfDays));
			circlePosition.iY+=positionOfMonth.iY+
						iMonthFont->HeightInPixels()+iVerticalSpaceBetweenMonthNameAndDayNameInitials+iHeightOfWeekDayInitials+iVerticalSpaceBetweenDayNameInitialsAndTopRowOfDays+
						((cellOfToday/7)*(iSizeOfSingleDay.iHeight+iVerticalSpaceBetweenEachRowOfDays));
			// Customize the sizes of the today circles for the different views
			TSize daySize(iSizeOfSingleDay);
			circlePosition.iX-=2;
			circlePosition.iY-=4;
			daySize.iWidth+=4;
			daySize.iHeight+=4;
			if (iNumMonths==12)
				{// The iSizeOfSingleDay is not a square for 12 month view
				circlePosition.iX+=2;
				circlePosition.iY+=1;
				daySize.iWidth-=4;
				daySize.iHeight-=2;
				}
			//
			__ASSERT_DEBUG(daySize.iWidth==daySize.iHeight,Panic(EEikPanicCalendarBadSize));
			aGc.SetBrushStyle(CGraphicsContext::ENullBrush);
			aGc.DrawEllipse(TRect(circlePosition,daySize));
			aGc.SetBrushStyle(CGraphicsContext::ESolidBrush);
			}
		++i;// Increment the month
		if (i%iNumMonthsPerRowOfMonths>0) // N.B. uses freshly incremented i
			positionOfMonth.iX+=iSizeOfSingleMonth.iWidth+iHorizontalSpaceBetweenEachColumnOfMonths;
		else
			{
			positionOfMonth.iX=leftEdgeOfMonthMatrix;
			positionOfMonth.iY+=iSizeOfSingleMonth.iHeight+verticalSpaceBeneathMonth;
			}
		}
	}

void CEikCalendar::UseFontIfNotBeingUsed(CWindowGc& aGc, TInt& aAscentInPixelsOfFontBeingUsed, const CFont*& aFontBeingUsed, const CFont* aFontToUse)
	{
	if (aFontBeingUsed!=aFontToUse)
		{
		aGc.UseFont(aFontToUse);
		aAscentInPixelsOfFontBeingUsed=aFontToUse->AscentInPixels();
		aFontBeingUsed=aFontToUse;
		}
	}

TInt CEikCalendar::WidthOfRangeOfColumnsOfDays(TInt aFirstColumn, TInt aLastColumn) const
	{
	__ASSERT_DEBUG((aFirstColumn>=0) && (aLastColumn<7) && (aFirstColumn<=aLastColumn), Panic(EEikPanicCalendarBadRangeOfColumns));
	TInt widthOfRangeOfColumnsOfDays=0;
	for (TInt i=aFirstColumn; i<=aLastColumn; ++i)
		widthOfRangeOfColumnsOfDays+=iSizeOfSingleDay.iWidth+((i%2==0)? 0: iHorizontalSpaceBetweenEachColumnOfDays*2); // even-numbered columns include the space
	return widthOfRangeOfColumnsOfDays;
	}

void CEikCalendar::GetMonthData(TInt aOffsetFromStartMonthToThisMonth, TInt* aNumColumnsBlankAtStartOfTopRowOfDays, TInt* aNumDaysInThisMonth) const
	{
	TTime firstDayOfThisMonth=Date(aOffsetFromStartMonthToThisMonth, 0);
	if (aNumColumnsBlankAtStartOfTopRowOfDays)
		*aNumColumnsBlankAtStartOfTopRowOfDays=((firstDayOfThisMonth.DayNoInWeek()+7)-iStartOfWeek)%7;
	if (aNumDaysInThisMonth)
		*aNumDaysInThisMonth=firstDayOfThisMonth.DaysInMonth();
	}

void CEikCalendar::GetNearestDateFromPosition(const TPoint& aPosition, TInt& aOffsetFromStartMonthToThisMonth, TInt& aThisDay) const
	{
	TInt squaredDistanceToCenterOfNearestCell=KMaxTInt;
	TPoint offsetToCenterOfCell(iSizeOfSingleDay.iWidth/2, iSizeOfSingleDay.iHeight/2);
	TMargins margins=iBorder.Margins();
	TPoint calendarPosition=Rect().iTl;
	TInt leftEdgeOfMonthMatrix=calendarPosition.iX+margins.iLeft+iHorizontalSpaceOnEachSideOfMonthMatrix;
	TPoint positionOfTopRowOfDaysInMonth(leftEdgeOfMonthMatrix, calendarPosition.iY+margins.iTop+iVerticalSpaceAboveYear+iSizeOfYearText.iHeight+
											iVerticalSpaceBetweenYearAndTopRowOfMonths+iMonthFont->HeightInPixels()+
											iVerticalSpaceBetweenMonthNameAndDayNameInitials+iHeightOfWeekDayInitials+
											iVerticalSpaceBetweenDayNameInitialsAndTopRowOfDays);
	for (TInt i=0; i<iNumMonths; )
		{
		TPoint positionOfCell=positionOfTopRowOfDaysInMonth;
		TInt numColumnsBlankAtStartOfTopRowOfDays;
		TInt numDaysInThisMonth;
		GetMonthData(i, &numColumnsBlankAtStartOfTopRowOfDays, &numDaysInThisMonth);
		TInt numRowsOfDays=(numColumnsBlankAtStartOfTopRowOfDays+numDaysInThisMonth+6)/7;
		TInt numCellsInDayMatrix=numRowsOfDays*7;
		for (TInt j=0; j<numCellsInDayMatrix; )
			{
			TInt increment=1;
			TInt thisDay=j-numColumnsBlankAtStartOfTopRowOfDays;
			if (thisDay<0)
				increment=numColumnsBlankAtStartOfTopRowOfDays;
			else if (thisDay<numDaysInThisMonth)
				{
				TPoint offsetFromCenterOfCellToRequiredPosition=aPosition-(positionOfCell+offsetToCenterOfCell);
				TInt squaredDistanceToCenterOfThisCell=(offsetFromCenterOfCellToRequiredPosition.iX*offsetFromCenterOfCellToRequiredPosition.iX)+
													   (offsetFromCenterOfCellToRequiredPosition.iY*offsetFromCenterOfCellToRequiredPosition.iY);
				if (squaredDistanceToCenterOfNearestCell>squaredDistanceToCenterOfThisCell)
					{
					aOffsetFromStartMonthToThisMonth=i;
					aThisDay=thisDay;
					squaredDistanceToCenterOfNearestCell=squaredDistanceToCenterOfThisCell;
					}
				}
			else
				break;
			j+=increment;
			if (j%7>0) // N.B. uses freshly incremented j
				positionOfCell.iX+=(iSizeOfSingleDay.iWidth+iHorizontalSpaceBetweenEachColumnOfDays)*increment;
			else
				{
				positionOfCell.iX=positionOfTopRowOfDaysInMonth.iX;
				positionOfCell.iY+=iSizeOfSingleDay.iHeight+iVerticalSpaceBetweenEachRowOfDays;
				}
			}
		++i;
		if (i%iNumMonthsPerRowOfMonths>0) // N.B. uses freshly incremented i
			positionOfTopRowOfDaysInMonth.iX+=iSizeOfSingleMonth.iWidth+iHorizontalSpaceBetweenEachColumnOfMonths;
		else
			{
			positionOfTopRowOfDaysInMonth.iX=leftEdgeOfMonthMatrix;
			positionOfTopRowOfDaysInMonth.iY+=iSizeOfSingleMonth.iHeight+iVerticalSpaceBetweenEachRowOfMonths;
			}
		}
	}

void CEikCalendar::Divide(TInt aDividend, TInt aDivisor, TInt* aResult, TInt* aRemainder)
	{
	__ASSERT_DEBUG(aDivisor>0, Panic(EEikPanicCalendarBadDivisor));
	TInt result=0;
	TInt remainder=aDividend;
	for (; remainder<0; remainder+=aDivisor)
		--result;
	for (; remainder>=aDivisor; remainder-=aDivisor)
		++result;
	if (aResult)
		*aResult=result;
	if (aRemainder)
		*aRemainder=remainder;
	}

EXPORT_C void CEikCalendar::Reserved_1()
	{}
EXPORT_C void CEikCalendar::Reserved_2()
	{}
