// EIKMFNE.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include <basched.h>
#include <barsread.h>
#include <eikenv.h>
#include <eikcmbut.h>
#include <eikmsg.h>
#include <eiktxtut.h>
#include <eikcolor.h>
#include <eikcal.hrh>
#include <eikmfne.pan>
#include <eikmfne.hrh>
#include <eikmfne.h>
#include <eikon.rsg>


const TInt KMaxLatitudeDegrees=90;
const TInt KMaxLongitudeDegrees=180;
const TInt KMaxMinSecValue=59;
// constants controlling display
enum
	{// EMfneFieldExtraHeight=2 stops pm in time editors leaking
	EGapAboveText=1,
	EMfneFieldExtraHeight=0,
	EGapBelowText=1,
	EGapLeftOfFirstField=2,
	EGapRightOfLastField=2,
	EExtraBaselineOffset=1
	};

// global functions

GLDEF_C void Panic(TEikMfnePanic aPanic)
	{
	User::Panic(_L("EIKON-MFNE"), aPanic);
	}

// CEikMfneField

EXPORT_C CEikMfneField::CEikMfneField()
	:iMinimumWidthInPixels(0)
	{
	__DECLARE_NAME(_S("CEikMfneField"));
	}

void CEikMfneField::Draw(CWindowGc& aGc, const CFont& aFont, const TPoint& aTopLeft) const
	{
	aGc.DrawText(Text(), TRect(aTopLeft, TSize(WidthInPixels(aFont), aFont.HeightInPixels()+EMfneFieldExtraHeight)), aFont.AscentInPixels()+EExtraBaselineOffset, CGraphicsContext::ELeft, 0);
	}

TInt CEikMfneField::WidthInPixels(const CFont& aFont) const
	{
	return Max(iMinimumWidthInPixels, aFont.TextWidthInPixels(Text()));
	}

TInt CEikMfneField::DistanceFromStartOfFieldToEndOfTextInPixels(const CFont& aFont) const
	{
	return aFont.TextWidthInPixels(Text());
	}

EXPORT_C TBool CEikMfneField::IsEditable() const
	{
	return EFalse;
	}

EXPORT_C CEikMfneField::THighlightType CEikMfneField::HighlightType() const
	{
#if defined(_DEBUG)
	Panic(EEikPanicMfneFieldIsNotEditable1);
#endif
	return EInverseVideo; // dummy return to prevent compiler error
	}

EXPORT_C void CEikMfneField::HandleKey(const CFont&, const TKeyEvent&, TBool, TBool&, TInt&)
	{
#if defined(_DEBUG)
	Panic(EEikPanicMfneFieldIsNotEditable2);
#endif
	}

EXPORT_C void CEikMfneField::HandleDeHighlight(const CFont&, CEikonEnv&, TBool&, TBool&)
	{
#if defined(_DEBUG)
	Panic(EEikPanicMfneFieldIsNotEditable3);
#endif
	}

EXPORT_C void CEikMfneField::HandleLeftOrRightArrow(TChar aKey, TBool& aDataAltered, TInt& aHighlightIncrement)
	{
	__ASSERT_DEBUG((aKey==EKeyLeftArrow) || (aKey==EKeyRightArrow), Panic(EEikPanicMfneArrowKeyExpected));
	aHighlightIncrement=(aKey==EKeyLeftArrow)? -1: 1;
	aDataAltered=ETrue;
	}

// CEikMfneSeparator

CEikMfneSeparator::CEikMfneSeparator(HBufC* aText)
	:iText(aText)
	{
	__DECLARE_NAME(_S("CEikMfneSeparator"));
	}

EXPORT_C CEikMfneSeparator::~CEikMfneSeparator()
	{
	delete iText;
	}

EXPORT_C CEikMfneSeparator* CEikMfneSeparator::NewL(TResourceReader& aResourceReader)
	{
	HBufC* text=aResourceReader.ReadHBufCL();
	CleanupStack::PushL(text);
	CEikMfneSeparator* separator=NewL(text);
	CleanupStack::Pop();
	return separator;
	}

EXPORT_C CEikMfneSeparator* CEikMfneSeparator::NewL(HBufC* aText)
	{
	return new(ELeave) CEikMfneSeparator(aText);
	}

EXPORT_C void CEikMfneSeparator::SetText(HBufC* aText)
	{
	__ASSERT_DEBUG(iText==NULL, Panic(EEikPanicMfneSeparatorTextHasAlreadyBeenSet));
	iText=aText;
	}

TInt CEikMfneSeparator::MaximumWidthInPixels(const CFont& aFont, TBool)
	{
	return aFont.TextWidthInPixels(*iText);
	}

const TDesC& CEikMfneSeparator::Text() const
	{
	return *iText;
	}

// CEikMfneNumber

CEikMfneNumber::CEikMfneNumber(TInt aMinimumValue, TInt aMaximumValue, TUint32 aFlags)
	:iMinimumValue(aMinimumValue),
	 iMaximumValue(aMaximumValue),
	 iFlags(aFlags)
	{
	TBuf<16> minText;
	minText.AppendNum(aMinimumValue);
	TBuf<16> maxText;
	maxText.AppendNum(aMaximumValue);
	iMaxDigits=Max(minText.Length(),maxText.Length());
	__DECLARE_NAME(_S("CEikMfneNumber"));
	__ASSERT_ALWAYS(aMinimumValue<=aMaximumValue, Panic(EEikPanicMfneBadNumberMinimumAndMaximum1));
	__ASSERT_ALWAYS((aFlags&~EPublicallySettableFlags)==0, Panic(EEikPanicMfneBadNumberFlags));
	}

EXPORT_C CEikMfneNumber::~CEikMfneNumber()
	{
	delete iText;
	}

EXPORT_C CEikMfneNumber* CEikMfneNumber::NewL(const CFont& aFont, TResourceReader& aResourceReader)
	{
#pragma warning (disable : 4127)
	__ASSERT_DEBUG((EFillWithLeadingZeros==EEikMfneNumberFlagFillWithLeadingZeros) &&
				   (EPreserveOldWidthBeforeEditing==EEikMfneNumberFlagPreserveOldWidthBeforeEditing) &&
				   (EFillWithLeadingZeros==EEikMfneNumberFlagFillWithLeadingZeros), Panic(EEikPanicMfneInconsistentNumberFlags));
#pragma warning (default : 4127)
	TInt minimumValue=aResourceReader.ReadInt32();
	TInt maximumValue=aResourceReader.ReadInt32();
	TUint32 flags=aResourceReader.ReadUint8();
	return NewL(aFont, minimumValue, maximumValue, maximumValue, flags);
	}

EXPORT_C CEikMfneNumber* CEikMfneNumber::NewL(const CFont& aFont, TInt aMinimumValue, TInt aMaximumValue, TInt aInitialValue, TUint32 aFlags)
	{
	__ASSERT_ALWAYS((aInitialValue>=aMinimumValue) && (aInitialValue<=aMaximumValue), Panic(EEikPanicMfneBadNumberMinimumAndMaximum2));
	CEikMfneNumber* number=new(ELeave) CEikMfneNumber(aMinimumValue, aMaximumValue, aFlags);
	CleanupStack::PushL(number);
	number->iText=HBufC::NewL(number->MaximumNumberOfDigits()+((aMinimumValue<0)? 1: 0));
	number->SetTextToValue(aInitialValue, aFont);
	number->ConstructL();
	CleanupStack::Pop();
	return number;
	}

void CEikMfneNumber::ConstructL()
	{
	CEikonEnv* eikEnv=CEikonEnv::Static();
	iNudgeCharMinus=eikEnv->NudgeCharMinus();
	iNudgeCharPlus=eikEnv->NudgeCharPlus();
	}

EXPORT_C void CEikMfneNumber::SetMinimumAndMaximum(TInt aMinimumValue, TInt aMaximumValue, const CFont& aFont)
	{
	TBuf<16> minText;
	minText.AppendNum(aMinimumValue);
	TBuf<16> maxText;
	maxText.AppendNum(aMaximumValue);
	TInt minTextLength=minText.Length();
	TInt maxTextLength=maxText.Length();
	__ASSERT_ALWAYS((aMinimumValue<=aMaximumValue) &&
					(minTextLength<=iMaxDigits) &&
					(maxTextLength<=iMaxDigits), Panic(EEikPanicMfneBadNumberMinimumAndMaximum3));
	//__ASSERT_ALWAYS((aMinimumValue<=aMaximumValue) &&
	//				(aMinimumValue>=iMinimumValueCateredForInFieldWidth) &&
	//				(aMaximumValue<=iMaximumValueCateredForInFieldWidth), Panic(EEikPanicMfneBadNumberMinimumAndMaximum3));
	iMinimumValue=aMinimumValue;
	iMaximumValue=aMaximumValue;
	TInt value=ValueFromText();
	if (value<aMinimumValue)
		SetTextToValue(aMinimumValue, aFont);
	else if (value>aMaximumValue)
		SetTextToValue(aMaximumValue, aFont);
	}

EXPORT_C void CEikMfneNumber::GetMinimumAndMaximum(TInt& aMinimumValue, TInt& aMaximumValue) const
	{
	aMinimumValue=iMinimumValue;
	aMaximumValue=iMaximumValue;
	}

EXPORT_C void CEikMfneNumber::SetValue(TInt aValue, const CFont& aFont)
	{
	SetTextToValue(aValue, aFont);
	}

EXPORT_C TInt CEikMfneNumber::Value() const
	{
	return ValueFromText();
	}

TInt CEikMfneNumber::MaximumWidthInPixels(const CFont& aFont, TBool aShrinkToMinimumSize)
	{
	if (aShrinkToMinimumSize)
		{
		//iMinimumValueCateredForInFieldWidth=iMinimumValue;
		//iMaximumValueCateredForInFieldWidth=iMaximumValue;
		TBuf<16> minText;
		minText.AppendNum(iMinimumValue);
		TBuf<16> maxText;
		maxText.AppendNum(iMaximumValue);
		iMaxDigits=Max(2,Max(minText.Length(),maxText.Length()));
		}
	return (MaximumNumberOfDigits()*TEikFindWidthOfWidestDigit().MaximumWidthInPixels(aFont))/*+((iMinimumValueCateredForInFieldWidth<0)? aFont.TextWidthInPixels(_L("-")): 0)*/;
	}

const TDesC& CEikMfneNumber::Text() const
	{
	return *iText;
	}

TBool CEikMfneNumber::IsEditable() const
	{
	return ETrue;
	}

CEikMfneField::THighlightType CEikMfneNumber::HighlightType() const
	{
	return (iFlags&EIsBeingEditedWithCursor)? ECursor: EInverseVideo;
	}

void CEikMfneNumber::HandleKey(const CFont& aFont, const TKeyEvent& aKeyEvent, TBool aInterpretLeftAndRightAsEarEvents, TBool& aDataAltered, TInt& aHighlightIncrement)
	{
	TChar ch=aKeyEvent.iCode;
	TPtr text=iText->Des();
	TInt textLength=text.Length();
	TBool nudgeTen=EFalse;
	switch (ch)
		{
	case EKeyLeftArrow:
	case EKeyRightArrow:
		if (!aInterpretLeftAndRightAsEarEvents)
			HandleLeftOrRightArrow(ch, aDataAltered, aHighlightIncrement);
		else
			{
		doAdjust:
			const TInt nudge=(nudgeTen? 10 : 1);
			TInt newValue;
			if ((textLength==0) || !TChar(text[textLength-1]).IsDigit() )
				newValue=0;
			else 
				newValue=ValueFromText()+((ch==EKeyLeftArrow)? -nudge: nudge);
			if (newValue>=iMaximumValue)
				newValue=iMaximumValue;
			else if (newValue<=iMinimumValue)
				newValue=iMinimumValue;
			SetTextToValue(newValue, aFont);
			aHighlightIncrement=0;// Always highlight the field being nudged
			aDataAltered=ETrue;
			iFlags&=~EIsBeingEditedWithCursor;
			}
		break;
	case EKeyBackspace:
		if (textLength)
			{
			iFlags|=EIsBeingEditedWithCursor;
			text.SetLength(textLength-1);
			if (text==_L("-"))
				text.SetLength(0);
			aDataAltered=ETrue;
			}
		break;
	case '+':
		break;
	case '-':
		if (~iFlags&EIsBeingEditedWithCursor)
				{
				iFlags|=EIsBeingEditedWithCursor;
				text.SetLength(0);
				}
		if (text.Length()==0)
			{
			text.Append('-');
			aDataAltered=ETrue;
			}
		break;
	default:
		TBuf<2> nudgeTenChars;
		CCoeEnv::Static()->ReadResource(nudgeTenChars,R_EIK_TBUF_NUDGE_TEN_CHARS);
		if (ch.IsDigit())
			{
			if (~iFlags&EIsBeingEditedWithCursor)
				{
				iFlags|=EIsBeingEditedWithCursor;
				text.SetLength(0);
				}
			__ASSERT_DEBUG(NumberOfDigits()<MaximumNumberOfDigits(),Panic(EEikPanicMfneNumberTextInBadState));
			text.Append(aKeyEvent.iCode);
			aDataAltered=ETrue;
			if (NumberOfDigits()>=MaximumNumberOfDigits())
				aHighlightIncrement=1;
			}
		if (ch==iNudgeCharMinus)
			{
			ch=EKeyLeftArrow;
			goto doAdjust;
			}
		else if (ch==iNudgeCharPlus)
			{
			ch=EKeyRightArrow;
			goto doAdjust;
			}
		else if (ch==nudgeTenChars[0])
			{
			ch=EKeyLeftArrow;
			nudgeTen=ETrue;
			goto doAdjust;
			}
		else if (ch==nudgeTenChars[1])
			{
			ch=EKeyRightArrow;
			nudgeTen=ETrue;
			goto doAdjust;
			}
		break;
		}
	}

void CEikMfneNumber::HandleDeHighlight(const CFont& aFont, CEikonEnv& aEikonEnv, TBool& aDataAltered, TBool& aError)
	{
	iFlags&=~EIsBeingEditedWithCursor;
	if (NumberOfDigits()==0)
		{
		SetTextToValue(iMaximumValue, aFont);
		aDataAltered=ETrue;
		aError=ETrue;
		aEikonEnv.InfoMsg(R_EIK_TBUF_NO_NUMBER_ENTERED);
		return;
		}
	TInt value=ValueFromText();
	if ((value<iMinimumValue) && !ConvertsIntoValidValue(value))
		{
		SetTextToValue(iMinimumValue, aFont);
		aDataAltered=ETrue;
		aError=ETrue;
		aEikonEnv.InfoMsg(R_EIK_TBUF_NUMBER_BELOW_MIN, iMinimumValue);
		return;
		}
	if ((value>iMaximumValue) && !ConvertsIntoValidValue(value))
		{
		SetTextToValue(iMaximumValue, aFont);
		aDataAltered=ETrue;
		aError=ETrue;
		aEikonEnv.InfoMsg(R_EIK_TBUF_NUMBER_ABOVE_MAX, iMaximumValue);
		return;
		}
	TBuf<128> oldText=*iText;
	SetTextToValue(value, aFont);
	if (oldText!=*iText)
		aDataAltered=ETrue;
	}

TInt CEikMfneNumber::MaximumNumberOfDigits() const
	{
	return Max(2,iMaxDigits);
	}

TInt CEikMfneNumber::NumberOfDigits() const
	{
	return iText->Length();
	}

void CEikMfneNumber::SetTextToValue(TInt aValue, const CFont& aFont)
	{
	__ASSERT_DEBUG((aValue>=iMinimumValue) && (aValue<=iMaximumValue), Panic(EEikPanicMfneNumberOutsidePermittedRange));
	TPtr text=iText->Des();
	text.SetLength(0);
	if (aValue<0)
		{
		text.Append('-');
		aValue=-aValue;
		}
	TInt firstUnsetCharacter=text.Length();
	text.SetLength(firstUnsetCharacter+MaximumNumberOfDigits());
	TInt lastUnsetCharacter=text.Length()-1;
	if (aValue==0)
		text[lastUnsetCharacter--]='0';
	else
		{
		for (; aValue; aValue/=10)
			text[lastUnsetCharacter--]=(TText)('0'+(aValue%10));
		}
	__ASSERT_DEBUG(lastUnsetCharacter+1>=firstUnsetCharacter, Panic(EEikPanicMfneNumberTextInBadState));
	if (lastUnsetCharacter>=firstUnsetCharacter)
		{
		if (iFlags&EFillWithLeadingZeros)
			while (lastUnsetCharacter>=firstUnsetCharacter) text[lastUnsetCharacter--]='0';
		else
			text.Delete(firstUnsetCharacter, (lastUnsetCharacter+1)-firstUnsetCharacter);
		}
	if (iFlags&EPreserveOldWidthBeforeEditing)
		iMinimumWidthInPixels=aFont.TextWidthInPixels(text);
	}

TInt CEikMfneNumber::ValueFromText() const
	{
	__ASSERT_DEBUG(NumberOfDigits(), Panic(EEikPanicMfneNumberHasNoDigits));
	TInt i=0;
	TInt valueFromText=0;
	TBool isNegative=EFalse;
	switch ((*iText)[i])
		{
	case '-':
		++i;
		isNegative=ETrue;
		break;
		}
	TInt textLength=iText->Length();
	for (; i<textLength; ++i)
		{
		TText digit=(*iText)[i];
		__ASSERT_DEBUG(TChar(digit).IsDigit(), Panic(EEikPanicMfneDigitExpected));
		if (i>=textLength-2)
			{
			if (!isNegative && valueFromText>(KMaxTInt32/10))
				return KMaxTInt32;
			else if (isNegative && (-valueFromText)<(KMinTInt32/10))
				return KMinTInt32;
			}
		valueFromText=(valueFromText*10)+(TInt)(digit-'0');
		}
	if (isNegative==EFalse && valueFromText<0)
		return KMaxTInt32;// Deals with overflow
	if (isNegative)
		valueFromText=-valueFromText;
	return valueFromText;
	}

TBool CEikMfneNumber::ConvertsIntoValidValue(TInt& aValue) const
	{
	if ((iFlags&ERepresentsYear) && (aValue>=0) && (aValue<100))
		{
		TTime homeTime;
		homeTime.HomeTime();
		TInt currentYear=homeTime.DateTime().Year();
		if (currentYear>0)
			{
			TInt yearsSinceStartOfCurrentCentury=currentYear%100;
			TInt newValue=(currentYear-yearsSinceStartOfCurrentCentury)+aValue;
			if (currentYear-newValue>=50)
				newValue+=100;
			else if (newValue-currentYear>=50)
				newValue-=100;
			if ((newValue>=iMinimumValue) && (newValue<=iMaximumValue))
				{
				aValue=newValue;
				return ETrue;
				}
			}
		}
	return EFalse;
	}

// CEikMfneSymbol

CEikMfneSymbol::CEikMfneSymbol(TInt aNumSymbolicItems)
	:iNumSymbolicItems(aNumSymbolicItems),
	 iCurrentSymbolicItem(0)
	{
	__DECLARE_NAME(_S("CEikMfneSymbol"));
	__ASSERT_ALWAYS(aNumSymbolicItems>1, Panic(EEikPanicMfneTooFewSymbolicItems));
	}

EXPORT_C CEikMfneSymbol::~CEikMfneSymbol()
	{
	if (iSymbolicItems)
		{
		for (TInt i=0; i<iNumSymbolicItems; ++i)
			delete iSymbolicItems[i];
		delete [] iSymbolicItems;
		}
	}

EXPORT_C CEikMfneSymbol* CEikMfneSymbol::NewL(TResourceReader& aResourceReader)
	{
	TInt numSymbols=aResourceReader.ReadUint8();
	CEikMfneSymbol* symbol=NewL(numSymbols);
	CleanupStack::PushL(symbol);
	for (TInt i=0; i<numSymbols; ++i)
		symbol->AddSymbolicItem(CItem::NewL(aResourceReader), i==0);
	CleanupStack::Pop();
	return symbol;
	}

EXPORT_C CEikMfneSymbol* CEikMfneSymbol::NewL(TInt aNumSymbolicItems)
	{
	CEikMfneSymbol* symbol=new(ELeave) CEikMfneSymbol(aNumSymbolicItems);
	CleanupStack::PushL(symbol);
	symbol->iSymbolicItems=new(ELeave) CItem*[aNumSymbolicItems];
	for (TInt i=0; i<aNumSymbolicItems; ++i)
		symbol->iSymbolicItems[i]=NULL;
	CleanupStack::Pop();
	return symbol;
	}

EXPORT_C void CEikMfneSymbol::AddSymbolicItem(CItem* aSymbolicItem, TBool aMakeCurrent)
	{
	__ASSERT_DEBUG(iSymbolicItems, Panic(EEikPanicMfneSymbolicItemArrayNotCreated));
	for (TInt i=0; i<iNumSymbolicItems; ++i)
		if (iSymbolicItems[i]==NULL)
			{
			iSymbolicItems[i]=aSymbolicItem;
			if (aMakeCurrent)
				iCurrentSymbolicItem=i;
			return;
			}
#if defined(_DEBUG)
	Panic(EEikPanicMfneTooManySymbolicItemsAdded);
#endif
	}

EXPORT_C void CEikMfneSymbol::SetCurrentSymbolicItemToId(TInt aId)
	{
	for (TInt i=0; i<iNumSymbolicItems; ++i)
		if (iSymbolicItems[i]->iId==aId)
			{
			iCurrentSymbolicItem=i;
			return;
			}
#if defined(_DEBUG)
	Panic(EEikPanicMfneIdOfSymbolicItemNotFound);
#endif
	}

EXPORT_C TInt CEikMfneSymbol::IdOfCurrentSymbolicItem() const
	{
	return iSymbolicItems[iCurrentSymbolicItem]->iId;
	}

TInt CEikMfneSymbol::MaximumWidthInPixels(const CFont& aFont, TBool)
	{
	TInt maximumWidthInPixels=0;
	for (TInt i=0; i<iNumSymbolicItems; ++i)
		{
		TInt widthInPixels=aFont.TextWidthInPixels(*iSymbolicItems[i]->iText);
		if (maximumWidthInPixels<widthInPixels)
			maximumWidthInPixels=widthInPixels;
		}
	return maximumWidthInPixels;
	}

const TDesC& CEikMfneSymbol::Text() const
	{
	return *iSymbolicItems[iCurrentSymbolicItem]->iText;
	}

TBool CEikMfneSymbol::IsEditable() const
	{
	return ETrue;
	}

CEikMfneField::THighlightType CEikMfneSymbol::HighlightType() const
	{
	return EInverseVideo;
	}

void CEikMfneSymbol::HandleKey(const CFont&, const TKeyEvent& aKeyEvent, TBool, TBool& aDataAltered, TInt& aHighlightIncrement)
	{
	TChar ch=aKeyEvent.iCode;
	switch (ch)
		{
	case EKeyLeftArrow:
	case EKeyRightArrow:
		HandleLeftOrRightArrow(ch, aDataAltered, aHighlightIncrement);
		break;
	case EKeySpace:
		iCurrentSymbolicItem=(iCurrentSymbolicItem+1)%iNumSymbolicItems;
		aDataAltered=ETrue;
		break;
	default:
		{
		TCharF foldedKey=User::Fold(ch);
		for (TInt i=0; i<iNumSymbolicItems; ++i)
			{
			if (iSymbolicItems[i]->iKeyToMatch==foldedKey)
				{
				if (iCurrentSymbolicItem!=i)
					{
					iCurrentSymbolicItem=i;
					aDataAltered=ETrue;
					}
				break;
				}
			}
		}
		break;
		}
	}

void CEikMfneSymbol::HandleDeHighlight(const CFont&, CEikonEnv&, TBool&, TBool&)
	{
	}

// CEikMfneSymbol::CItem

CEikMfneSymbol::CItem::CItem(TInt aId, TChar aKeyToMatch, HBufC* aText)
	:iId(aId),
	 iKeyToMatch(aKeyToMatch),
	 iText(aText)
	{
	__DECLARE_NAME(_S("CItem"));
	}

EXPORT_C CEikMfneSymbol::CItem::~CItem()
	{
	delete iText;
	}

EXPORT_C CEikMfneSymbol::CItem* CEikMfneSymbol::CItem::NewL(TResourceReader& aResourceReader)
	{
	TInt id=aResourceReader.ReadInt32();
	TChar keyToMatch=aResourceReader.ReadUint16();
	HBufC* text=aResourceReader.ReadHBufCL();
	CleanupStack::PushL(text);
	CItem* symbolicItem=NewL(id, keyToMatch, text);
	CleanupStack::Pop();
	return symbolicItem;
	}

EXPORT_C CEikMfneSymbol::CItem* CEikMfneSymbol::CItem::NewL(TInt aId, TChar aKeyToMatch, HBufC* aText)
	{
	return new(ELeave) CItem(aId, aKeyToMatch, aText);
	}

EXPORT_C void CEikMfneSymbol::CItem::SetText(HBufC* aText)
	{
	__ASSERT_DEBUG(iText==NULL, Panic(EEikPanicMfneSymbolicItemTextHasAlreadyBeenSet));
	iText=aText;
	}

// CEikMfne

EXPORT_C CEikMfne::CEikMfne()
	:CEikBorderedControl(TEikBorder(TEikBorder::ESingleGray)),
	 iCurrentField(ENullIndex)
	{
	__DECLARE_NAME(_S("CEikMfne"));
	}

EXPORT_C CEikMfne::~CEikMfne()
	{
	if (iFields)
		{
		if (IsFocused() && (iCurrentField!=ENullIndex) && (iFields[iCurrentField]->HighlightType()==CEikMfneField::ECursor))
			HideCursor();
		for (TInt i=0; i<iNumFields; ++i)
			delete iFields[i];
		delete [] iFields;
		}
	}

EXPORT_C void CEikMfne::CreateFieldArrayL(TInt aNumFields)
	{
	__ASSERT_DEBUG(iFields==NULL, Panic(EEikPanicMfneFieldArrayAlreadyCreated));
	__ASSERT_DEBUG(aNumFields>0, Panic(EEikPanicMfneTooFewFields));
	iNumFields=aNumFields;
	iFields=new(ELeave) CEikMfneField*[aNumFields];
	for (TInt i=0; i<aNumFields; ++i)
		iFields[i]=NULL;
	}

EXPORT_C void CEikMfne::AddField(CEikMfneField* aField)
	{
	__ASSERT_DEBUG(iFields, Panic(EEikPanicMfneFieldArrayNotCreated));
	for (TInt i=0; i<iNumFields; ++i)
		if (iFields[i]==NULL)
			{
			iFields[i]=aField;
			if ((iCurrentField==ENullIndex) && aField->IsEditable())
				iCurrentField=i;
			return;
			}
#if defined(_DEBUG)
	Panic(EEikPanicMfneTooManyFieldsAdded);
#endif
	}

EXPORT_C TMargins CEikMfne::BorderMargins() const
	{
	return iBorder.Margins();
	}

EXPORT_C TTime CEikMfne::ReadTime(TResourceReader& aResourceReader)
	{
	TInt second=aResourceReader.ReadUint8();
	TInt minute=aResourceReader.ReadUint8();
	TInt hour=aResourceReader.ReadUint8();
	return TTime(TDateTime(0, EJanuary, 0, hour, minute, second, 0));
	}

EXPORT_C TTime CEikMfne::ReadDate(TResourceReader& aResourceReader)
	{
	TInt day=aResourceReader.ReadUint8();
	TMonth month=(TMonth)aResourceReader.ReadUint8();
	TInt year=aResourceReader.ReadInt16();
	return TTime(TDateTime(year, month, day, 0, 0, 0, 0));
	}

EXPORT_C TTime CEikMfne::ReadTimeAndDate(TResourceReader& aResourceReader)
	{
	TInt second=aResourceReader.ReadUint8();
	TInt minute=aResourceReader.ReadUint8();
	TInt hour=aResourceReader.ReadUint8();
	TInt day=aResourceReader.ReadUint8();
	TMonth month=(TMonth)aResourceReader.ReadUint8();
	TInt year=aResourceReader.ReadInt16();
	return TTime(TDateTime(year, month, day, hour, minute, second, 0));
	}

EXPORT_C TTimeIntervalSeconds CEikMfne::ReadDuration(TResourceReader& aResourceReader)
	{
	TInt seconds=aResourceReader.ReadInt32();
	__ASSERT_ALWAYS(seconds>=0, Panic(EEikPanicMfneNegativeDuration));
	return TTimeIntervalSeconds(seconds);
	}

EXPORT_C TTimeIntervalSeconds CEikMfne::ReadTimeOffset(TResourceReader& aResourceReader)
	{
	TInt seconds=aResourceReader.ReadInt32();
	return TTimeIntervalSeconds(seconds);
	}

EXPORT_C TTimeIntervalSeconds CEikMfne::Convert(const TTime& aTime)
	{
	TDateTime dateTime=aTime.DateTime();
	return TTimeIntervalSeconds(dateTime.Second()+(dateTime.Minute()*60)+(dateTime.Hour()*3600)+(dateTime.Day()*3600*24));
	}

EXPORT_C TTime CEikMfne::Convert(const TTimeIntervalSeconds& aTimeIntervalSeconds)
	{
	TInt timeIntervalSeconds=aTimeIntervalSeconds.Int();
	TInt second=timeIntervalSeconds%60;
	timeIntervalSeconds/=60;
	TInt minute=timeIntervalSeconds%60;
	timeIntervalSeconds/=60;
	TInt hour=timeIntervalSeconds%24;
	return TTime(TDateTime(0, EJanuary, 0, hour, minute, second, 0));
	}

EXPORT_C TKeyResponse CEikMfne::OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType)
	{
	const TInt code=aKeyEvent.iCode;
	if (code==EKeyDownArrow || code==EKeyUpArrow)
		return EKeyWasNotConsumed;
	if ((aType==EEventKey) && (iCurrentField!=ENullIndex))
		{
		const CFont& font=*iEikonEnv->NormalFont();
		CEikMfneField::THighlightType oldHighlightTypeOfOldCurrentField=iFields[iCurrentField]->HighlightType();
		TInt oldWidthInPixelsOfOldCurrentField=iFields[iCurrentField]->WidthInPixels(font);
		TBool dataAltered=EFalse;
		TInt highlightIncrement=0;
		iFields[iCurrentField]->HandleKey(font, aKeyEvent, iNumFields==1, dataAltered, highlightIncrement);
		TInt newCurrentField;
		for (TInt i=iCurrentField+highlightIncrement; ; i+=highlightIncrement)
			{
			if (i<0)
				i=iNumFields-1;
			else if (i>=iNumFields)
				i=0;
			if (iFields[i]->IsEditable())
				{
				newCurrentField=i;
				break;
				}
			}
		TBool error=EFalse;
		HandleInteraction(highlightIncrement, newCurrentField, oldWidthInPixelsOfOldCurrentField, oldHighlightTypeOfOldCurrentField, dataAltered, error);
		}
	return EKeyWasConsumed;
	}

EXPORT_C void CEikMfne::PrepareForFocusLossL()
	{
	if (iCurrentField!=ENullIndex)
		{
		TBool dataAltered=EFalse;
		TBool error=EFalse;
		HandleInteraction(ETrue, iCurrentField, iFields[iCurrentField]->WidthInPixels(*iEikonEnv->NormalFont()), iFields[iCurrentField]->HighlightType(), dataAltered, error);
		if (error)
			CBaActiveScheduler::LeaveNoAlert();
		}
	}

EXPORT_C TSize CEikMfne::MinimumSize()
	{
	return MfneSize(ETrue);
	}

EXPORT_C void CEikMfne::FocusChanged(TDrawNow aDrawNow)
	{
	if (iCurrentField==ENullIndex)
		return;
	if (iFields[iCurrentField]->HighlightType()==CEikMfneField::ECursor)
		{
		if (IsFocused())
			DrawCursor();
		else
			HideCursor();
		return;
		}
	if (aDrawNow)
		{
		ActivateGc();
		DrawRange(PreparedGc(), iCurrentField, iCurrentField);
		DeactivateGc();
		if (!IsFocused()) // N.B. cannot reset iCurrentField to the first editable field on focusing because HandlePointerEventL would already have been called if it was focused by pointer-event
			{
			for (TInt i=0; i<iNumFields; ++i)
				{
				if (iFields[i]->IsEditable())
					{
					iCurrentField=i;
					break;
					}
				}
			}
		}
	}

EXPORT_C void CEikMfne::HandleInteraction(TBool aHandleDeHighlight, TInt aNewCurrentField, TInt aOldWidthInPixelsOfOldCurrentField,
								CEikMfneField::THighlightType aOldHighlightTypeOfOldCurrentField, TBool& aDataAltered, TBool& aError)
	{
	const CFont& font=*iEikonEnv->NormalFont();
	TBool drawAllFields=EFalse;
	if (aHandleDeHighlight)
		{
		iFields[iCurrentField]->HandleDeHighlight(font, *iEikonEnv, aDataAltered, aError);
		if (aError)
			aNewCurrentField=iCurrentField;
		else
			FieldIsAboutToBeDeHighlighted(iFields[iCurrentField], drawAllFields);
		}
	TInt oldCurrentField=iCurrentField;
	iCurrentField=aNewCurrentField;
	ActivateGc();
	CWindowGc& gc=PreparedGc();
	CEikMfneField::THighlightType newHighlightTypeOfOldCurrentField=iFields[oldCurrentField]->HighlightType();
	CEikMfneField::THighlightType highlightTypeOfCurrentField=iFields[iCurrentField]->HighlightType();
	if (drawAllFields)
		DrawRange(gc, 0, iNumFields);
	else
		{
		TInt newWidthInPixelsOfOldCurrentField=iFields[oldCurrentField]->WidthInPixels(font);
		TInt firstFieldToDraw=(aDataAltered ||
							   ((iCurrentField!=oldCurrentField) && (aOldHighlightTypeOfOldCurrentField==CEikMfneField::EInverseVideo)) ||
							   ((iCurrentField==oldCurrentField) && (aOldHighlightTypeOfOldCurrentField!=newHighlightTypeOfOldCurrentField)))?
																	oldCurrentField: oldCurrentField+1;
		TInt lastFieldToDraw=(newWidthInPixelsOfOldCurrentField!=aOldWidthInPixelsOfOldCurrentField)? iNumFields: oldCurrentField;
		DrawRange(gc, firstFieldToDraw, lastFieldToDraw);
		if ((iCurrentField!=oldCurrentField) && ((iCurrentField<firstFieldToDraw) || (iCurrentField>lastFieldToDraw)))
			{
			__ASSERT_DEBUG(highlightTypeOfCurrentField==CEikMfneField::EInverseVideo, Panic(EEikPanicMfneInverseVideoHighlightTypeExpected));
			DrawRange(gc, iCurrentField, iCurrentField);
			}
		}
	if ((aOldHighlightTypeOfOldCurrentField==CEikMfneField::ECursor) && ((iCurrentField!=oldCurrentField) || (newHighlightTypeOfOldCurrentField!=CEikMfneField::ECursor)))
		{
		__ASSERT_DEBUG(highlightTypeOfCurrentField!=CEikMfneField::ECursor, Panic(EEikPanicMfneBadCursorState1));
		HideCursor();
		}
	if ((highlightTypeOfCurrentField==CEikMfneField::ECursor) && ((aOldHighlightTypeOfOldCurrentField!=CEikMfneField::ECursor) || aDataAltered))
		{
		__ASSERT_DEBUG(iCurrentField==oldCurrentField, Panic(EEikPanicMfneBadCursorState2));
		DrawCursor();
		}
	DeactivateGc();
	}

EXPORT_C void CEikMfne::FieldIsAboutToBeDeHighlighted(CEikMfneField*, TBool&)
	{
	}

EXPORT_C void CEikMfne::DrawNowAndLeaveWithTimeDateFormatInfoMsgL(TInt aResourceId, const TTime& aTimeDate) const
	{
	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();
	}

EXPORT_C CEikMfneField* CEikMfne::Field(TInt aField) const
    {
    return (aField<0 || aField>=iNumFields)?NULL:iFields[aField];
    }

EXPORT_C TSize CEikMfne::MfneSize() const
	{
	return ((CEikMfne*)this)->MfneSize(EFalse); // cast away the const-ness
	}

EXPORT_C TSize CEikMfne::MfneSize(TBool aShrinkToMinimumSize)
	{
	const CFont& font=*iEikonEnv->NormalFont();
	TSize size=iBorder.SizeDelta();
	// EMfneFieldExtraHeight is needed to stop leaking
	size.iHeight+=EGapAboveText+EGapBelowText+font.HeightInPixels()+EMfneFieldExtraHeight;
	size.iWidth+=EGapLeftOfFirstField+EGapRightOfLastField;
	for (TInt i=0; i<iNumFields; ++i)
		size.iWidth+=iFields[i]->MaximumWidthInPixels(font, aShrinkToMinimumSize);
	iSize=size;
	return iSize;
	}

EXPORT_C void CEikMfne::Draw(const TRect& /*aRect*/) const
	{
	TSize mfneSize=MfneSize();
	__ASSERT_ALWAYS((iSize.iWidth>=mfneSize.iWidth) && (iSize.iHeight>=mfneSize.iHeight), Panic(EEikPanicMfneBadSize));
	__ASSERT_ALWAYS(iContext || (iSize==mfneSize), Panic(EEikPanicMfneNoControlContext));
	CWindowGc& gc=PreparedGc();
	TRect rect=Rect();
	iBorder.Draw(gc, rect);
	DrawRange(gc, -1, iNumFields);
	gc.SetPenStyle(CGraphicsContext::ENullPen);
	const TRect innerRect=iBorder.InnerRect(rect);
	TRect aboveText(innerRect);
	aboveText.iBr.iY=innerRect.iTl.iY+EGapAboveText;
	gc.DrawRect(aboveText);
	TInt textHeight=iEikonEnv->NormalFont()->HeightInPixels();
	TRect belowText(innerRect);
	belowText.iTl.iY=innerRect.iTl.iY+EGapAboveText+textHeight;
	gc.DrawRect(belowText);
	}

EXPORT_C void CEikMfne::HandlePointerEventL(const TPointerEvent& aPointerEvent)
	{
	CCoeControl::HandlePointerEventL(aPointerEvent);
	if ((aPointerEvent.iType==TPointerEvent::EButton1Down) || (aPointerEvent.iType==TPointerEvent::EDrag))
		{
		const CFont& font=*iEikonEnv->NormalFont();
		TInt newField=ENullIndex;
		TMargins borderMargins=iBorder.Margins();
		TInt leftPositionOfThisField=iPosition.iX+borderMargins.iLeft+EGapLeftOfFirstField;
		TInt leftPositionOfNewField=0; // dummy initialization to prevent compiler warning
		TInt rightPositionOfNewField=0; // dummy initialization to prevent compiler warning
		for (TInt i=0; i<iNumFields; ++i)
			{
			TInt widthOfThisFieldInPixels=iFields[i]->WidthInPixels(font);
			if (iFields[i]->IsEditable())
				{
				TInt rightPositionOfThisField=(leftPositionOfThisField+widthOfThisFieldInPixels)-1;
				if (rightPositionOfThisField<aPointerEvent.iPosition.iX)
					{
					newField=i;
					leftPositionOfNewField=leftPositionOfThisField;
					rightPositionOfNewField=rightPositionOfThisField;
					}
				else
					{
					if ((newField==ENullIndex) || (leftPositionOfThisField-aPointerEvent.iPosition.iX<aPointerEvent.iPosition.iX-rightPositionOfNewField))
						{
						newField=i;
						// leftPositionOfNewField and rightPositionOfNewField do not need to be set as they are not going to be used again
						}
					break;
					}
				}
			leftPositionOfThisField+=widthOfThisFieldInPixels;
			}
		if (newField==ENullIndex)
			newField=iCurrentField;
		TBool createPopoutIfRequired=((newField==iCurrentField) && aPointerEvent.iType==TPointerEvent::EButton1Down && IsFocused());
		TBool dataAltered=EFalse;
		TBool error=EFalse;
		HandleInteraction(ETrue, newField, iFields[iCurrentField]->WidthInPixels(font), iFields[iCurrentField]->HighlightType(), dataAltered, error);
		if (createPopoutIfRequired)
			CreatePopoutIfRequiredL();
		}
	}

EXPORT_C void CEikMfne::CreatePopoutIfRequiredL()
	{
	// does nothing
	}

void CEikMfne::DrawRange(CWindowGc& aGc, TInt aFirstField, TInt aLastField) const
	{
	// N.B. a value of iNumFields for aFirstField or aLastField is permitted, this means draw the space after the last field, and similarly
	// a value of -1 is permitted which means draw the space before the first field
	__ASSERT_DEBUG((aFirstField>=-1) && (aFirstField<=iNumFields) && (aLastField>=-1) && (aLastField<=iNumFields), Panic(EEikPanicMfneBadRangeToDraw));
	// N.B. aLastField is permitted to be less than aFirstField, in which case nothing will be drawn
	if (aFirstField<=aLastField)
		{
		TBool drawtrailingSpace=EFalse;
		if (aLastField==iNumFields)
			{
			drawtrailingSpace=ETrue;
			--aLastField;
			}
		TMargins borderMargins=iBorder.Margins();
		const TPoint topLeftOfMfne(iPosition.iX+borderMargins.iLeft, iPosition.iY+borderMargins.iTop);
		TPoint topLeftOfField(topLeftOfMfne);
		topLeftOfField.iY+=EGapAboveText;
		const CFont& font=*iEikonEnv->NormalFont();
		TInt textHeight=font.HeightInPixels();
		if (aFirstField==-1)
			{
			SetGcToNormalVideo(aGc);
			aGc.Clear(TRect(topLeftOfMfne, TSize(EGapLeftOfFirstField, textHeight+EGapAboveText+EGapBelowText)));
			++aFirstField;
			}
		topLeftOfField.iX+=EGapLeftOfFirstField;
		TBool focused=IsFocused();
		for (TInt i=0; i<=aLastField; ++i)
			{
			if (i>=aFirstField)
				{
				if (focused && (i==iCurrentField) && (iFields[i]->HighlightType()==CEikMfneField::EInverseVideo))
					SetGcToInverseVideo(aGc);
				else if (IsDimmed())
					SetGcToDimmedVideo(aGc);
				else
					SetGcToNormalVideo(aGc);
				iFields[i]->Draw(aGc, font, topLeftOfField);
				}
			topLeftOfField.iX+=iFields[i]->WidthInPixels(font);
			}
		if (drawtrailingSpace)
			{
			SetGcToNormalVideo(aGc);
			aGc.Clear(TRect(TPoint(topLeftOfField.iX,topLeftOfMfne.iY), TPoint((iPosition.iX+iSize.iWidth)-borderMargins.iRight, topLeftOfMfne.iY+textHeight+EGapAboveText+EGapBelowText+EMfneFieldExtraHeight)));
			}
		}
	}

CWindowGc& CEikMfne::PreparedGc() const
	{
	CWindowGc& gc=SystemGc();
	gc.SetBrushStyle(CWindowGc::ESolidBrush);
	gc.UseFont(iEikonEnv->NormalFont());
	return gc;
	}

void CEikMfne::SetGcToNormalVideo(CWindowGc& aGc)
	{
	CEikonEnv* env=CEikonEnv::Static();
	aGc.SetPenColor(env->Color(EEikColorControlText)); //KEikMfneForegroundColor);
	aGc.SetBrushColor(env->Color(EEikColorControlBackground)); //KEikMfneBackgroundColor);
	}

void CEikMfne::SetGcToInverseVideo(CWindowGc& aGc)
	{
	CEikonEnv* env=CEikonEnv::Static();
	aGc.SetPenColor(env->Color(EEikColorControlHighlightText)); //KEikMfneBackgroundColor);
	aGc.SetBrushColor(env->Color(EEikColorControlHighlightBackground)); //KEikMfneForegroundColor);
	}

void CEikMfne::SetGcToDimmedVideo(CWindowGc& aGc)
	{
	CEikonEnv* env=CEikonEnv::Static();
	aGc.SetPenColor(env->Color(EEikColorControlDimmedText)); //KEikMfneDimmedColor);
	aGc.SetBrushColor(env->Color(EEikColorControlBackground)); //KEikMfneBackgroundColor);
	}

void CEikMfne::DrawCursor()
	{
	__ASSERT_DEBUG(IsFocused() && (iFields[iCurrentField]->HighlightType()==CEikMfneField::ECursor), Panic(EEikPanicMfneBadCursorState3));
	const CFont& font=*iEikonEnv->NormalFont();
	TInt ascentInPixels=font.AscentInPixels();
	TMargins borderMargins=iBorder.Margins();
	TPoint cursorPosition(iPosition.iX+borderMargins.iLeft+EGapLeftOfFirstField, iPosition.iY+borderMargins.iTop+EGapAboveText+ascentInPixels+(EMfneFieldExtraHeight/2));
	for (TInt i=0; i<iCurrentField; ++i)
		cursorPosition.iX+=iFields[i]->WidthInPixels(font);
	cursorPosition.iX+=iFields[iCurrentField]->DistanceFromStartOfFieldToEndOfTextInPixels(font);
	iEikonEnv->DrawCursor(this, cursorPosition, KEikDefaultCursorWidth, ascentInPixels+(EMfneFieldExtraHeight/2), font.HeightInPixels()+EMfneFieldExtraHeight);
	}

void CEikMfne::HideCursor()
	{
	iEikonEnv->HideCursor(this);
	}

EXPORT_C void CEikMfne::Reserved_1()
	{
	}

EXPORT_C void CEikMfne::Reserved_2()
	{
	}
// CEikNumberEditor

EXPORT_C CEikNumberEditor::CEikNumberEditor()
	{
	__DECLARE_NAME(_S("CEikNumberEditor"));
	}

EXPORT_C void CEikNumberEditor::ConstructL(TInt aMinimumValue, TInt aMaximumValue, TInt aInitialValue)
	{
	CreateFieldArrayL(1);
	iNumber=CEikMfneNumber::NewL(*iEikonEnv->NormalFont(), aMinimumValue, aMaximumValue, aInitialValue, 0);
	AddField(iNumber);
	}

EXPORT_C void CEikNumberEditor::SetMinimumAndMaximum(TInt aMinimumValue, TInt aMaximumValue)
	{
	iNumber->SetMinimumAndMaximum(aMinimumValue, aMaximumValue, *iEikonEnv->NormalFont());
	}

EXPORT_C void CEikNumberEditor::GetMinimumAndMaximum(TInt& aMinimumValue, TInt& aMaximumValue) const
	{
	iNumber->GetMinimumAndMaximum(aMinimumValue, aMaximumValue);
	}

EXPORT_C void CEikNumberEditor::SetNumber(TInt aNumber)
	{
	iNumber->SetValue(aNumber, *iEikonEnv->NormalFont());
	}

EXPORT_C TInt CEikNumberEditor::Number() const
	{
	return iNumber->Value();
	}

EXPORT_C void CEikNumberEditor::ConstructFromResourceL(TResourceReader& aResourceReader)
	{
	TInt minimumValue=aResourceReader.ReadInt32();
	TInt maximumValue=aResourceReader.ReadInt32();
	ConstructL(minimumValue, maximumValue, maximumValue);
	}

// CEikRangeEditor

EXPORT_C CEikRangeEditor::CEikRangeEditor()
	{
	__DECLARE_NAME(_S("CEikRangeEditor"));
	}

EXPORT_C void CEikRangeEditor::ConstructL(TInt aMinimumValue, TInt aMaximumValue, const SEikRange& aInitialRange, HBufC* aSeparatorText)
	{
	const CFont& font=*iEikonEnv->NormalFont();
	CreateFieldArrayL(3);
	iLowerLimit=CEikMfneNumber::NewL(font, aMinimumValue, aMaximumValue, aInitialRange.iLowerLimit, 0);
	AddField(iLowerLimit);
	CEikMfneSeparator* separator=CEikMfneSeparator::NewL(NULL);
	AddField(separator);
	iUpperLimit=CEikMfneNumber::NewL(font, aMinimumValue, aMaximumValue, aInitialRange.iUpperLimit, 0);
	AddField(iUpperLimit);
	// do stuff that can only be done when all leaving functions have successfully been done
	separator->SetText(aSeparatorText);
	}

EXPORT_C void CEikRangeEditor::SetMinimumAndMaximum(TInt aMinimumValue, TInt aMaximumValue)
	{
	const CFont& font=*iEikonEnv->NormalFont();
	iLowerLimit->SetMinimumAndMaximum(aMinimumValue, aMaximumValue, font);
	iUpperLimit->SetMinimumAndMaximum(aMinimumValue, aMaximumValue, font);
	}

EXPORT_C void CEikRangeEditor::GetMinimumAndMaximum(TInt& aMinimumValue, TInt& aMaximumValue) const
	{
	iLowerLimit->GetMinimumAndMaximum(aMinimumValue, aMaximumValue);
#if defined(_DEBUG)
	TInt minimumValue;
	TInt maximumValue;
	iUpperLimit->GetMinimumAndMaximum(minimumValue, maximumValue);
	__ASSERT_DEBUG((minimumValue==aMinimumValue) && (maximumValue==aMaximumValue), Panic(EEikPanicRangeEditorInconsistentMinimumAndMaximum));
#endif
	}

EXPORT_C void CEikRangeEditor::SetRange(const SEikRange& aRange)
	{
	const CFont& font=*iEikonEnv->NormalFont();
	iLowerLimit->SetValue(aRange.iLowerLimit, font);
	iUpperLimit->SetValue(aRange.iUpperLimit, font);
	}

EXPORT_C SEikRange CEikRangeEditor::Range() const
	{
	SEikRange range;
	range.iLowerLimit=iLowerLimit->Value();
	range.iUpperLimit=iUpperLimit->Value();
	return range;
	}

EXPORT_C void CEikRangeEditor::ConstructFromResourceL(TResourceReader& aResourceReader)
	{
	TInt minimumValue=aResourceReader.ReadInt32();
	TInt maximumValue=aResourceReader.ReadInt32();
	HBufC* separatorText=aResourceReader.ReadHBufCL();
	CleanupStack::PushL(separatorText);
	SEikRange initialRange;
	initialRange.iLowerLimit=maximumValue;
	initialRange.iUpperLimit=maximumValue;
	ConstructL(minimumValue, maximumValue, initialRange, separatorText);
	CleanupStack::Pop();
	}

void CEikRangeEditor::FieldIsAboutToBeDeHighlighted(CEikMfneField* aField, TBool& aDrawAllFields)
	{
	const CFont& font=*iEikonEnv->NormalFont();
	if (aField==iLowerLimit)
		{
		TInt lowerLimitValue=iLowerLimit->Value();
		if (iUpperLimit->Value()<lowerLimitValue)
			{
			iUpperLimit->SetValue(lowerLimitValue, font);
			aDrawAllFields=ETrue;
			}
		}
	else if (aField==iUpperLimit)
		{
		TInt upperLimitValue=iUpperLimit->Value();
		if (iLowerLimit->Value()>upperLimitValue)
			{
			iLowerLimit->SetValue(upperLimitValue, font);
			aDrawAllFields=ETrue;
			}
		}
	}

// CTimeEditor

class CTimeEditor : public CBase
	{
public:
	enum
		{
		EWithoutSecondsField		=1, // same as EEikTimeWithoutSecondsField,
		EWithoutPopoutCalendar		=2, // same as EEikDateWithoutPopoutCalendar,
		EWithoutHoursField			=4, // same as EEikTimeWithoutHoursField,
		EForce24HourFormat			=8, // same as EEikTimeForce24HourFormat,
		};
public:
	CTimeEditor();
	TInt NumFieldsRequired(TUint32 aFlags);
	void ConstructFieldsL(CEikMfne& aOwner, const TTime& aInitialTime, const CFont& aFont);
	void SetTime(const TTime& aTime, const CFont& aFont);
	void SetMinMax(TInt aHourMin,TInt aHourMax,TInt aMinuteMin,TInt aMinuteMax,
		TInt aSecondMin,TInt aSecondMax,const CFont& aFont);
	TTime Time() const;
private:
	void AddAmPmFieldsIfNecessaryL(CEikMfne& aOwner, TAmPm aAmPm, TLocalePos aPos);
private:
	enum // these are needed in case the OS locale changes between NumFieldsRequired and ConstructFieldsL being called
		{
		EIn12HourFormat				=0x10,
		ESpaceBeforeAmPm			=0x20,
		EAmPmIsPositionedBeforeTime	=0x40
		};
private:
	TUint32 iFlags;
	// none of these pointers owns anything
	CEikMfneNumber* iSecond; // may be NULL;
	CEikMfneNumber* iMinute;
	CEikMfneNumber* iHour; // may be NULL;
	CEikMfneSymbol* iAmPm; // may be NULL;
	};

CTimeEditor::CTimeEditor()
	{
	__DECLARE_NAME(_S("CTimeEditor"));
	}

TInt CTimeEditor::NumFieldsRequired(TUint32 aFlags)
	{
	iFlags=aFlags;
	TInt numFieldsRequired=1; // 1 for minute
	if (~iFlags&EWithoutSecondsField)
		numFieldsRequired+=2; // 2 for separator and second
	if (~iFlags&EWithoutHoursField)
		numFieldsRequired+=2; // 2 for hour and separator
	if (~iFlags&EForce24HourFormat)
		{
		TLocale locale;
		TTimeFormat timeFormat=locale.TimeFormat();
		__ASSERT_DEBUG((timeFormat==ETime12) || (timeFormat==ETime24), Panic(EEikPanicDateEditorBadTimeFormat));
		if (timeFormat==ETime12)
			{
			if (locale.AmPmSymbolPosition()==ELocaleBefore)
				iFlags|=EAmPmIsPositionedBeforeTime;
			if (locale.AmPmSpaceBetween())
				{
				++numFieldsRequired;
				iFlags|=ESpaceBeforeAmPm;
				}
			++numFieldsRequired;
			iFlags|=EIn12HourFormat;
			}
		}
	return numFieldsRequired;
	}

void CTimeEditor::ConstructFieldsL(CEikMfne& aOwner, const TTime& aInitialTime, const CFont& aFont)
	{
	TDateTime initialTime=aInitialTime.DateTime();
	TLocale locale;
	TInt timeSeparatorIndex=1;
	const TUint32 flags=CEikMfneNumber::EFillWithLeadingZeros|CEikMfneNumber::EPreserveOldWidthBeforeEditing;
	TInt hour=initialTime.Hour();
	TAmPm amPm=(hour<12)? EAm: EPm;
	TInt minimumHour=0;
	TInt maximumHour=23;
	if (iFlags&EIn12HourFormat)
		{
		minimumHour=1;
		maximumHour=12;
		hour%=12;
		if (hour==0)
			hour=12;
		}
	// create and push the time elements in the opposite order to the order in which they will be added so that they are popped off in the correct order
	AddAmPmFieldsIfNecessaryL(aOwner, amPm, ELocaleBefore);
	if (~iFlags&EWithoutSecondsField)
		{
		iSecond=CEikMfneNumber::NewL(aFont, 0, KMaxMinSecValue, initialTime.Second(), flags);
		CleanupStack::PushL(iSecond); // this needs to be pushed as it would not be destroyed by the class' destructor
		}
	iMinute=CEikMfneNumber::NewL(aFont, 0, KMaxMinSecValue, initialTime.Minute(), flags);
	CleanupStack::PushL(iMinute); // this needs to be pushed as it would not be destroyed by the class' destructor
	if (~iFlags&EWithoutHoursField)
		{
		//iHour=CEikMfneNumber::NewL(aFont, minimumHour, maximumHour, hour, flags&~((iFlags&EIn12HourFormat)? CEikMfneNumber::EFillWithLeadingZeros: 0));
		iHour=CEikMfneNumber::NewL(aFont, minimumHour, maximumHour, hour,flags);
		CleanupStack::PushL(iHour); // this needs to be pushed as it would not be destroyed by the class' destructor
		// add hour
		aOwner.AddField(iHour);
		CleanupStack::Pop();
		// add the first separator
		HBufC* firstSeparatorText=HBufC::NewLC(1); // also pushes on to CleanupStack
		firstSeparatorText->Des().Append(locale.TimeSeparator(timeSeparatorIndex++));
		aOwner.AddField(CEikMfneSeparator::NewL(firstSeparatorText));
		CleanupStack::Pop();
		}
	// add minute
	aOwner.AddField(iMinute);
	CleanupStack::Pop();
	if (~iFlags&EWithoutSecondsField)
		{
		// add the second separator
		HBufC* secondSeparatorText=HBufC::NewLC(1); // also pushes on to CleanupStack
		secondSeparatorText->Des().Append(locale.TimeSeparator(timeSeparatorIndex++));
		aOwner.AddField(CEikMfneSeparator::NewL(secondSeparatorText));
		CleanupStack::Pop();
		// add second
		aOwner.AddField(iSecond);
		CleanupStack::Pop();
		}
	AddAmPmFieldsIfNecessaryL(aOwner, amPm, ELocaleAfter);
	}

void CTimeEditor::SetTime(const TTime& aTime, const CFont& aFont)
	{
	TDateTime time=aTime.DateTime();
	if (iSecond)
		iSecond->SetValue(time.Second(), aFont);
	iMinute->SetValue(time.Minute(), aFont);
	TInt hour=time.Hour();
	if (iAmPm)
		{
		TAmPm amPm=(hour<12)? EAm: EPm;
		hour%=12;
		if (hour==0)
			hour=12;
		iAmPm->SetCurrentSymbolicItemToId(amPm);
		}
	if(iHour)
		iHour->SetValue(hour, aFont);
	}

void CTimeEditor::SetMinMax(TInt /*aHourMin*/,TInt /*aHourMax*/,TInt /*aMinuteMin*/,TInt /*aMinuteMax*/,
		TInt /*aSecondMin*/,TInt /*aSecondMax*/,const CFont& /*aFont*/)
	{
	/*iHour->SetMinimumAndMaximum(aHourMin, aHourMax, aFont); 
	iMinute->SetMinimumAndMaximum(aMinuteMin, aMinuteMax, aFont);
	if (iSecond!=NULL)
		iSecond->SetMinimumAndMaximum(aSecondMin, aSecondMax, aFont);*/
	}

TTime CTimeEditor::Time() const
	{
	TInt dayValue=0;
	TInt hourValue=iHour?iHour->Value():0;
	if (iAmPm)
		{
		hourValue%=12;
		if (iAmPm->IdOfCurrentSymbolicItem()==EPm)
			hourValue+=12;
		}
	return TTime(TDateTime(0, EJanuary, dayValue, hourValue, iMinute->Value(), iSecond? iSecond->Value(): 0, 0));
	}

void CTimeEditor::AddAmPmFieldsIfNecessaryL(CEikMfne& aOwner, TAmPm aAmPm, TLocalePos aPos)
	{
	if ((iFlags&EIn12HourFormat) && (!(iFlags&EAmPmIsPositionedBeforeTime)==!(aPos==ELocaleBefore))) // logical-"not" both sides before comparing for equality
		{
		CEikMfneSeparator* space=NULL;
		if (iFlags&ESpaceBeforeAmPm)
			{
			// add separator consisting of a space
			HBufC* spaceSeparatorText=HBufC::NewLC(1); // also pushes on to CleanupStack
			spaceSeparatorText->Des().Append(TChar(' '));
			space=CEikMfneSeparator::NewL(spaceSeparatorText);
			CleanupStack::Pop();
			CleanupStack::PushL(space);
			}
		iAmPm=CEikMfneSymbol::NewL(2);
		CleanupStack::PushL(iAmPm); // this needs to be pushed as it would not be destroyed by the class' destructor
		TAmPmName amName(EAm);
		TAmPmName pmName(EPm);
		// add the am symbol
		HBufC* amText=HBufC::NewLC(amName.Length()); // also pushes on to CleanupStack
		*amText=amName;
		iAmPm->AddSymbolicItem(CEikMfneSymbol::CItem::NewL(EAm, amName[0], amText), aAmPm==EAm); // !! IS amName[0] SUFFICIENT FOR THE KEY TO MATCH?
		CleanupStack::Pop();
		// add the pm symbol
		HBufC* pmText=HBufC::NewLC(pmName.Length()); // also pushes on to CleanupStack
		*pmText=pmName;
		iAmPm->AddSymbolicItem(CEikMfneSymbol::CItem::NewL(EPm, pmName[0], pmText), aAmPm==EPm); // !! IS pmName[0] SUFFICIENT FOR THE KEY TO MATCH?
		CleanupStack::Pop((iFlags&ESpaceBeforeAmPm)? 3: 2);
		if ((~iFlags&EAmPmIsPositionedBeforeTime) && space)
			aOwner.AddField(space);
		aOwner.AddField(iAmPm);
		if ((iFlags&EAmPmIsPositionedBeforeTime) && space)
			aOwner.AddField(space);
		}
	}

// CDateEditor

class CDateEditor : public CBase
	{
public:
	CDateEditor(CEikMfne& aOwner, MEikCalendarObserver* aCalendarObserver);
	TInt NumFieldsRequired() const;
	void ConstructFieldsL(TInt aMinimumYear, TInt aMaximumYear, const TTime& aInitialDate, TBool aWithoutPopoutCalendar, CEikonEnv& aEikonEnv, const CFont& aFont);
	void SetMinimumAndMaximum(TInt aMinimumYear, TInt aMaximumYear, const CFont& aFont);
	void SetDate(const TTime& aDate, const CFont& aFont);
	TTime Date(const TTime* aTime=NULL) const;
	TKeyResponse OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType);
	void PrepareForFocusLossL(CEikonEnv& aEikonEnv);
	void CreatePopoutIfRequiredL();
	void ExecuteCalendarL();
public:
	TInt Year() const;
	TInt Month() const;
	TInt Day() const;
private:
	class RDateElements // objects of this class must be pushed on to the cleanup-stack
		{
	public:
		RDateElements();
		void Add(CEikMfneNumber* aDateElement, TInt aPosition);
		CEikMfneNumber* Next();
	private:
		static void Cleanup(TAny* aThis);
	public:
		inline operator TCleanupItem() {return TCleanupItem(Cleanup, this);}
	private:
		enum {EMaximumNumberOfDateElements=3};
	private:
		CEikMfneNumber* iDateElements[EMaximumNumberOfDateElements];
		};
private:
	CEikMfne& iOwner;
	MEikCalendarObserver* iCalendarObserver;
	TBool iWithoutPopoutCalendar;
	// none of these pointers owns anything
	CEikMfneNumber* iDay;
	CEikMfneNumber* iMonth;
	CEikMfneNumber* iYear;
	};

CDateEditor::CDateEditor(CEikMfne& aOwner,MEikCalendarObserver* aCalendarObserver)
	:iOwner(aOwner),
	 iCalendarObserver(aCalendarObserver)
	{
	__DECLARE_NAME(_S("CDateEditor"));
	}

TInt CDateEditor::NumFieldsRequired() const
	{
	return 5;
	}

void CDateEditor::ConstructFieldsL(TInt aMinimumYear, TInt aMaximumYear, const TTime& aInitialDate, TBool aWithoutPopoutCalendar, CEikonEnv& aEikonEnv, const CFont& aFont)
	{
	iWithoutPopoutCalendar=aWithoutPopoutCalendar;
	const TUint32 flags=CEikMfneNumber::EFillWithLeadingZeros|CEikMfneNumber::EPreserveOldWidthBeforeEditing;
	TLocale locale;
	TDateFormat dateFormat=locale.DateFormat();
	TInt dateSeparatorIndex=1;
	TInt dayPosition=0, monthPosition=0, yearPosition=0; // dummy initializations to prevent compiler warnings
	switch (dateFormat)
		{
	case EDateAmerican:
		monthPosition=0;
		dayPosition=1;
		yearPosition=2;
		break;
	case EDateEuropean:
		dayPosition=0;
		monthPosition=1;
		yearPosition=2;
		break;
	case EDateJapanese:
		yearPosition=0;
		monthPosition=1;
		dayPosition=2;
		break;
#if defined(_DEBUG)
	default:
		Panic(EEikPanicDateEditorBadDateFormat);
		break;
#endif
		}
	TDateTime initialDate=aInitialDate.DateTime();
	RDateElements dateElements;
	CleanupStack::PushL(dateElements);
	iDay=CEikMfneNumber::NewL(aFont, 1, 31, initialDate.Day()+1, flags);
	dateElements.Add(iDay, dayPosition);
	iMonth=CEikMfneNumber::NewL(aFont, 1, 12, initialDate.Month()+1, flags);
	dateElements.Add(iMonth, monthPosition);
	iYear=CEikMfneNumber::NewL(aFont, aMinimumYear, aMaximumYear, initialDate.Year(), flags|CEikMfneNumber::ERepresentsYear);
	dateElements.Add(iYear, yearPosition);
	// add first date element
	iOwner.AddField(dateElements.Next());
	// add first separator
	HBufC* firstSeparatorText=HBufC::NewLC(1); // also pushes on to CleanupStack
	firstSeparatorText->Des().Append(locale.DateSeparator(dateSeparatorIndex++));
	iOwner.AddField(CEikMfneSeparator::NewL(firstSeparatorText));
	CleanupStack::Pop();
	// add second date element
	iOwner.AddField(dateElements.Next());
	// add second separator
	HBufC* secondSeparatorText=HBufC::NewLC(1); // also pushes on to CleanupStack
	secondSeparatorText->Des().Append(locale.DateSeparator(dateSeparatorIndex++));
	iOwner.AddField(CEikMfneSeparator::NewL(secondSeparatorText));
	CleanupStack::Pop();
	// add third date element
	iOwner.AddField(dateElements.Next());
	CleanupStack::PopAndDestroy();
#pragma warning (disable: 4100) // stupid warning that claims aEikonEnv is not referenced when in fact it is
	}
#pragma warning (default: 4100)

void CDateEditor::SetMinimumAndMaximum(TInt aMinimumYear, TInt aMaximumYear, const CFont& aFont)
	{
	iYear->SetMinimumAndMaximum(aMinimumYear, aMaximumYear, aFont);
	}

void CDateEditor::SetDate(const TTime& aDate, const CFont& aFont)
	{
	TDateTime date=aDate.DateTime();
	iDay->SetValue(date.Day()+1, aFont);
	iMonth->SetValue(date.Month()+1, aFont);
	iYear->SetValue(date.Year(), aFont);
	}

TTime CDateEditor::Date(const TTime* aTime) const
	{
	TInt hour=0;
	TInt minute=0;
	TInt second=0;
	if (aTime)
		{
		TDateTime time=aTime->DateTime();
		hour=time.Hour();
		minute=time.Minute();
		second=time.Second();
		}
	TDateTime date;
	if (date.Set(iYear->Value(), (TMonth)(iMonth->Value()-1), iDay->Value()-1, hour, minute, second, 0)!=KErrNone)
		CEikonEnv::Static()->LeaveWithInfoMsg(R_EIK_TBUF_DATE_INVALID);
	return TTime(TDateTime(iYear->Value(), (TMonth)(iMonth->Value()-1), iDay->Value()-1, hour, minute, second, 0));
	}

TKeyResponse CDateEditor::OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType)
	{
	if ((aType==EEventKey) && (aKeyEvent.iCode==EKeyTab))
		{
		CEikonEnv* eikonEnv=CEikonEnv::Static();
		TDateTime date;
		if (date.Set(iYear->Value(), (TMonth)(iMonth->Value()-1), iDay->Value()-1, 0, 0, 0, 0)!=KErrNone)
			eikonEnv->LeaveWithInfoMsg(R_EIK_TBUF_DATE_INVALID);
		TTime min;
		TTime max;
		TTime initialDate;
		iCalendarObserver->GetMinimumAndMaximumAndInitialDatesForCalendarL(min,max,initialDate);
		if (initialDate<min)
			iOwner.DrawNowAndLeaveWithTimeDateFormatInfoMsgL(R_EIK_TBUF_BEFORE_EARLIEST_ALLOWED_TIME_AND_DATE,min);
		else if (initialDate>max)
			iOwner.DrawNowAndLeaveWithTimeDateFormatInfoMsgL(R_EIK_TBUF_AFTER_LATEST_ALLOWED_TIME_AND_DATE,max);
		ExecuteCalendarL();
		return EKeyWasConsumed;
		}
	return EKeyWasNotConsumed;
	}

void CDateEditor::PrepareForFocusLossL(CEikonEnv& aEikonEnv)
	{
	TDateTime date;
	if (date.Set(iYear->Value(), (TMonth)(iMonth->Value()-1), iDay->Value()-1, 0, 0, 0, 0)!=KErrNone)
		aEikonEnv.LeaveWithInfoMsg(R_EIK_TBUF_DATE_INVALID);
	}

void CDateEditor::CreatePopoutIfRequiredL()
	{
	if (!iWithoutPopoutCalendar)
		ExecuteCalendarL();
	}

void CDateEditor::ExecuteCalendarL()
	{
	CEikCalendar* calendar=new(ELeave) CEikCalendar;
	calendar->SetCalendarObserver(iCalendarObserver);
	calendar->ExecuteLD(R_EIK_ONE_MONTH_CALENDAR);
	}

TInt CDateEditor::Year() const
	{
	return(iYear->Value());
	}

TInt CDateEditor::Month() const
	{
	return(iMonth->Value());
	}

TInt CDateEditor::Day() const
	{
	return(iDay->Value());
	}

// CDateEditor::RDateElements

CDateEditor::RDateElements::RDateElements()
	{
	for (TInt i=0; i<EMaximumNumberOfDateElements; ++i)
		iDateElements[i]=NULL;
	}

void CDateEditor::RDateElements::Add(CEikMfneNumber* aDateElement, TInt aPosition)
	{
	__ASSERT_DEBUG((aPosition>=0) && (aPosition<EMaximumNumberOfDateElements), Panic(EEikPanicDateEditorBadDateElementPosition));
	iDateElements[aPosition]=aDateElement;
	}

CEikMfneNumber* CDateEditor::RDateElements::Next()
	{
	for (TInt i=0; i<EMaximumNumberOfDateElements; ++i)
		if (iDateElements[i])
			{
			CEikMfneNumber* dateElement=iDateElements[i];
			iDateElements[i]=NULL;
			return dateElement;
			}
#if defined(_DEBUG)
	Panic(EEikPanicDateEditorNoMoreDateElements);
#endif
	return NULL; // dummy return to prevent compiler error
	}

void CDateEditor::RDateElements::Cleanup(TAny* aThis)
	{
	for (TInt i=0; i<EMaximumNumberOfDateElements; ++i)
		delete ((RDateElements*)aThis)->iDateElements[i];
	}

// CEikTimeEditor

EXPORT_C CEikTimeEditor::CEikTimeEditor()
	{
	__DECLARE_NAME(_S("CEikTimeEditor"));
	}

EXPORT_C CEikTimeEditor::~CEikTimeEditor()
	{
	delete iTimeEditor;
	}

EXPORT_C void CEikTimeEditor::ConstructL(const TTime& aMinimumTime, const TTime& aMaximumTime, const TTime& aInitialTime, TUint32 aFlags)
	{
	iTimeEditor=new(ELeave) CTimeEditor;
	//CreateFieldArrayL(iTimeEditor->NumFieldsRequired(aWithoutSecondsField? CTimeEditor::EWithoutSecondsField: 0));
	CreateFieldArrayL(iTimeEditor->NumFieldsRequired(aFlags));
	iTimeEditor->ConstructFieldsL(*this, aInitialTime, *iEikonEnv->NormalFont());
	DoSetMinimumAndMaximum(aMinimumTime, aMaximumTime);
	}

EXPORT_C void CEikTimeEditor::SetMinimumAndMaximum(const TTime& aMinimumTime, const TTime& aMaximumTime)
	{
	// __ASSERT_ALWAYS((aMinimumTime>=iMinimumTime) && (aMaximumTime<=iMaximumTime), Panic(EEikPanicTimeEditorBadMinimumAndMaximum1));
	DoSetMinimumAndMaximum(aMinimumTime, aMaximumTime);
	}

EXPORT_C void CEikTimeEditor::GetMinimumAndMaximum(TTime& aMinimumTime, TTime& aMaximumTime) const
	{
	aMinimumTime=iMinimumTime;
	aMaximumTime=iMaximumTime;
	}

EXPORT_C void CEikTimeEditor::SetTime(const TTime& aTime)
	{
	iTimeEditor->SetTime(aTime, *iEikonEnv->NormalFont());
	}

EXPORT_C TTime CEikTimeEditor::Time() const
	{
	return iTimeEditor->Time();
	}

void CEikTimeEditor::SetTTime(const TTime& aTime)
	{
	SetTime(aTime);
	}

TTime CEikTimeEditor::GetTTime() const
	{
	return(Time());
	}

EXPORT_C void CEikTimeEditor::ConstructFromResourceL(TResourceReader& aResourceReader)
	{
	TTime minimumTime=ReadTime(aResourceReader);
	TTime maximumTime=ReadTime(aResourceReader);
	TUint32 flags=aResourceReader.ReadUint8();
	ConstructL(minimumTime, maximumTime, maximumTime, flags);
	}

EXPORT_C void CEikTimeEditor::PrepareForFocusLossL()
	{
	CEikMfne::PrepareForFocusLossL();
	TTime time=Time();
	if (time<iMinimumTime)
		{
		SetTime(iMinimumTime);
		DrawNowAndLeaveWithTimeDateFormatInfoMsgL(R_EIK_TBUF_BEFORE_EARLIEST_ALLOWED_TIME, iMinimumTime);
		}
	else if (time>iMaximumTime)
		{
		SetTime(iMaximumTime);
		DrawNowAndLeaveWithTimeDateFormatInfoMsgL(R_EIK_TBUF_AFTER_LATEST_ALLOWED_TIME, iMaximumTime);
		}
	}

void CEikTimeEditor::DoSetMinimumAndMaximum(const TTime& aMinimumTime, const TTime& aMaximumTime)
	{
	__ASSERT_ALWAYS(aMinimumTime<=aMaximumTime, Panic(EEikPanicTimeEditorBadMinimumAndMaximum2));
	iMinimumTime=aMinimumTime;
	iMaximumTime=aMaximumTime;
	TTime time=Time();
	if (time<iMinimumTime)
		SetTime(iMinimumTime);
	else if (time>iMaximumTime)
		SetTime(iMaximumTime);
	}

// CEikDateEditor

EXPORT_C CEikDateEditor::CEikDateEditor()
	{
	__DECLARE_NAME(_S("CEikDateEditor"));
	}

EXPORT_C CEikDateEditor::~CEikDateEditor()
	{
	delete iDateEditor;
	}

EXPORT_C void CEikDateEditor::ConstructL(const TTime& aMinimumDate, const TTime& aMaximumDate, const TTime& aInitialDate, TBool aWithoutPopoutCalendar)
	{
	iDateEditor=new(ELeave) CDateEditor(*this,this);
	CreateFieldArrayL(iDateEditor->NumFieldsRequired());
	iDateEditor->ConstructFieldsL(aMinimumDate.DateTime().Year(), aMaximumDate.DateTime().Year(), aInitialDate, aWithoutPopoutCalendar, *iEikonEnv, *iEikonEnv->NormalFont());
	DoSetMinimumAndMaximum(aMinimumDate, aMaximumDate);
	}

EXPORT_C void CEikDateEditor::SetMinimumAndMaximum(const TTime& aMinimumDate, const TTime& aMaximumDate)
	{
	// __ASSERT_ALWAYS((aMinimumDate>=iMinimumDate) && (aMaximumDate<=iMaximumDate), Panic(EEikPanicDateEditorBadMinimumAndMaximum1));
	iDateEditor->SetMinimumAndMaximum(aMinimumDate.DateTime().Year(), aMaximumDate.DateTime().Year(), *iEikonEnv->NormalFont());
	DoSetMinimumAndMaximum(aMinimumDate, aMaximumDate);
	}

EXPORT_C void CEikDateEditor::GetMinimumAndMaximum(TTime& aMinimumDate, TTime& aMaximumDate) const
	{
	aMinimumDate=iMinimumDate;
	aMaximumDate=iMaximumDate;
	}

EXPORT_C void CEikDateEditor::SetDate(const TTime& aDate)
	{
	iDateEditor->SetDate(aDate, *iEikonEnv->NormalFont());
	}

EXPORT_C TTime CEikDateEditor::Date() const
	{
	return iDateEditor->Date();
	}

void CEikDateEditor::SetTTime(const TTime& aTime)
	{
	SetDate(aTime);
	}

TTime CEikDateEditor::GetTTime() const
	{
	return(Date());
	}

EXPORT_C TKeyResponse CEikDateEditor::OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType)
	{
	if (iDateEditor->OfferKeyEventL(aKeyEvent, aType)==EKeyWasConsumed)
		return EKeyWasConsumed;
	return CEikMfne::OfferKeyEventL(aKeyEvent, aType);
	}

EXPORT_C void CEikDateEditor::ConstructFromResourceL(TResourceReader& aResourceReader)
	{
	TTime minimumDate=ReadDate(aResourceReader);
	TTime maximumDate=ReadDate(aResourceReader);
	TUint32 flags=aResourceReader.ReadUint8();
	ConstructL(minimumDate, maximumDate, maximumDate, flags&EEikDateWithoutPopoutCalendar);
	}

EXPORT_C void CEikDateEditor::PrepareForFocusLossL()
	{
	CEikMfne::PrepareForFocusLossL();
	iDateEditor->PrepareForFocusLossL(*iEikonEnv);
	TTime date=Date();
	if (date<iMinimumDate)
		{
		SetDate(iMinimumDate);
		DrawNowAndLeaveWithTimeDateFormatInfoMsgL(R_EIK_TBUF_BEFORE_EARLIEST_ALLOWED_DATE, iMinimumDate);
		}
	else if (date>iMaximumDate)
		{
		SetDate(iMaximumDate);
		DrawNowAndLeaveWithTimeDateFormatInfoMsgL(R_EIK_TBUF_AFTER_LATEST_ALLOWED_DATE, iMaximumDate);
		}
	}

void CEikDateEditor::CreatePopoutIfRequiredL()
	{
	iDateEditor->CreatePopoutIfRequiredL();
	}

void CEikDateEditor::GetMinimumAndMaximumAndInitialDatesForCalendarL(TTime& aMinimumDate, TTime& aMaximumDate, TTime& aInitialDate) const
	{
	aMinimumDate=iMinimumDate;
	aMaximumDate=iMaximumDate;
	TDateTime date;
	if (date.Set(iDateEditor->Year(), (TMonth)(iDateEditor->Month()-1), iDateEditor->Day()-1, 0, 0, 0, 0)!=KErrNone)
		iEikonEnv->LeaveWithInfoMsg(R_EIK_TBUF_DATE_INVALID);
	aInitialDate=iDateEditor->Date();
	}

void CEikDateEditor::SetDateFromCalendarAndDrawNow(const TTime& aDate)
	{
	iDateEditor->SetDate(aDate, *iEikonEnv->NormalFont());
	DrawNow();
	}

void CEikDateEditor::DoSetMinimumAndMaximum(const TTime& aMinimumDate, const TTime& aMaximumDate)
	{
	__ASSERT_ALWAYS(aMinimumDate<=aMaximumDate, Panic(EEikPanicDateEditorBadMinimumAndMaximum2));
	TDateTime minimumDate=aMinimumDate.DateTime();
	TDateTime maximumDate=aMaximumDate.DateTime();
	iMinimumDate=TDateTime(minimumDate.Year(), minimumDate.Month(), minimumDate.Day(), 0, 0, 0, 0);
	iMaximumDate=TDateTime(maximumDate.Year(), maximumDate.Month(), maximumDate.Day(), 0, 0, 0, 0);
	TTime date=Date();
	if (date<iMinimumDate)
		SetDate(iMinimumDate);
	else if (date>iMaximumDate)
		SetDate(iMaximumDate);
	}

// CEikTimeAndDateEditor

EXPORT_C CEikTimeAndDateEditor::CEikTimeAndDateEditor()
	{
	__DECLARE_NAME(_S("CEikTimeAndDateEditor"));
	}

EXPORT_C CEikTimeAndDateEditor::~CEikTimeAndDateEditor()
	{
	delete iTimeEditor;
	delete iDateEditor;
	}

EXPORT_C void CEikTimeAndDateEditor::ConstructL(const TTime& aMinimumTimeAndDate, const TTime& aMaximumTimeAndDate,
									const TTime& aInitialTimeAndDate,TUint32 aFlags, HBufC* aInterveningText)
	{
	iTimeEditor=new(ELeave) CTimeEditor;
	iDateEditor=new(ELeave) CDateEditor(*this,this);
	CreateFieldArrayL(iTimeEditor->NumFieldsRequired(aFlags)+(aInterveningText? 1: 0)+iDateEditor->NumFieldsRequired());
	const CFont& font=*iEikonEnv->NormalFont();
	CEikMfneSeparator* interveningText=NULL;
	iTimeEditor->ConstructFieldsL(*this, aInitialTimeAndDate, font);
	if (aInterveningText)
		{
		interveningText=CEikMfneSeparator::NewL(NULL);
		AddField(interveningText);
		}
	iDateEditor->ConstructFieldsL(aMinimumTimeAndDate.DateTime().Year(), aMaximumTimeAndDate.DateTime().Year(), aInitialTimeAndDate, (aFlags&CTimeEditor::EWithoutPopoutCalendar), *iEikonEnv, font);
	DoSetMinimumAndMaximum(aMinimumTimeAndDate, aMaximumTimeAndDate);
	// do stuff that can only be done when all leaving functions have successfully been done
	if (aInterveningText)
		interveningText->SetText(aInterveningText);
	}

EXPORT_C void CEikTimeAndDateEditor::SetMinimumAndMaximum(const TTime& aMinimumTimeAndDate, const TTime& aMaximumTimeAndDate)
	{
	// __ASSERT_ALWAYS((aMinimumTimeAndDate>=iMinimumTimeAndDate) && (aMaximumTimeAndDate<=iMaximumTimeAndDate), Panic(EEikPanicTimeAndDateEditorBadMinimumAndMaximum1));
	iDateEditor->SetMinimumAndMaximum(aMinimumTimeAndDate.DateTime().Year(), aMaximumTimeAndDate.DateTime().Year(), *iEikonEnv->NormalFont());
	DoSetMinimumAndMaximum(aMinimumTimeAndDate, aMaximumTimeAndDate);
	}

EXPORT_C void CEikTimeAndDateEditor::GetMinimumAndMaximum(TTime& aMinimumTimeAndDate, TTime& aMaximumTimeAndDate) const
	{
	aMinimumTimeAndDate=iMinimumTimeAndDate;
	aMaximumTimeAndDate=iMaximumTimeAndDate;
	}

EXPORT_C void CEikTimeAndDateEditor::SetTimeAndDate(const TTime& aTimeAndDate)
	{
	const CFont& font=*iEikonEnv->NormalFont();
	iDateEditor->SetDate(aTimeAndDate, font);
	TDateTime dateTime=aTimeAndDate.DateTime();
	dateTime.SetYear(0);
	dateTime.SetMonth(EJanuary);
	dateTime.SetDay(0);
	TTime timeWithoutDate(dateTime);
	iTimeEditor->SetTime(timeWithoutDate, font);
	}

EXPORT_C TTime CEikTimeAndDateEditor::TimeAndDate() const
	{
	TTime time=iTimeEditor->Time();
	return iDateEditor->Date(&time);
	}

void CEikTimeAndDateEditor::SetTTime(const TTime& aTime)
	{
	SetTimeAndDate(aTime);
	}

TTime CEikTimeAndDateEditor::GetTTime() const
	{
	return(TimeAndDate());
	}

EXPORT_C TKeyResponse CEikTimeAndDateEditor::OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType)
	{
	if (iDateEditor->OfferKeyEventL(aKeyEvent, aType)==EKeyWasConsumed)
		return EKeyWasConsumed;
	return CEikMfne::OfferKeyEventL(aKeyEvent, aType);
	}

EXPORT_C void CEikTimeAndDateEditor::ConstructFromResourceL(TResourceReader& aResourceReader)
	{
	TTime minimumTimeAndDate=ReadTimeAndDate(aResourceReader);
	TTime maximumTimeAndDate=ReadTimeAndDate(aResourceReader);
	TBool flags=aResourceReader.ReadUint8();
	HBufC* interveningText=aResourceReader.ReadHBufCL();
	CleanupStack::PushL(interveningText);
	ConstructL(minimumTimeAndDate, maximumTimeAndDate, maximumTimeAndDate, flags, interveningText);
	CleanupStack::Pop();
	}

EXPORT_C void CEikTimeAndDateEditor::PrepareForFocusLossL()
	{
	CEikMfne::PrepareForFocusLossL();
	iDateEditor->PrepareForFocusLossL(*iEikonEnv);
	TTime timeAndDate=TimeAndDate();
	if (timeAndDate<iMinimumTimeAndDate)
		{
		SetTimeAndDate(iMinimumTimeAndDate);
		DrawNowAndLeaveWithTimeDateFormatInfoMsgL(R_EIK_TBUF_BEFORE_EARLIEST_ALLOWED_TIME_AND_DATE, iMinimumTimeAndDate);
		}
	else if (timeAndDate>iMaximumTimeAndDate)
		{
		SetTimeAndDate(iMaximumTimeAndDate);
		DrawNowAndLeaveWithTimeDateFormatInfoMsgL(R_EIK_TBUF_AFTER_LATEST_ALLOWED_TIME_AND_DATE, iMaximumTimeAndDate);
		}
	}

void CEikTimeAndDateEditor::CreatePopoutIfRequiredL()
	{
	iDateEditor->CreatePopoutIfRequiredL();
	}

void CEikTimeAndDateEditor::GetMinimumAndMaximumAndInitialDatesForCalendarL(TTime& aMinimumDate, TTime& aMaximumDate, TTime& aInitialDate) const
	{
	aMinimumDate=iMinimumTimeAndDate;
	aMaximumDate=iMaximumTimeAndDate;
	TDateTime date;
	if (date.Set(iDateEditor->Year(), (TMonth)(iDateEditor->Month()-1), iDateEditor->Day()-1, 0, 0, 0, 0)!=KErrNone)
		iEikonEnv->LeaveWithInfoMsg(R_EIK_TBUF_DATE_INVALID);
	aInitialDate=iDateEditor->Date();
	}

void CEikTimeAndDateEditor::SetDateFromCalendarAndDrawNow(const TTime& aDate)
	{
	iDateEditor->SetDate(aDate, *iEikonEnv->NormalFont());
	DrawNow();
	}

void CEikTimeAndDateEditor::DoSetMinimumAndMaximum(const TTime& aMinimumTimeAndDate, const TTime& aMaximumTimeAndDate)
	{
	__ASSERT_ALWAYS(aMinimumTimeAndDate<=aMaximumTimeAndDate, Panic(EEikPanicTimeAndDateEditorBadMinimumAndMaximum2));
	iMinimumTimeAndDate=aMinimumTimeAndDate;
	iMaximumTimeAndDate=aMaximumTimeAndDate;
	TTime timeAndDate=TimeAndDate();
	if (timeAndDate<iMinimumTimeAndDate)
		SetTimeAndDate(iMinimumTimeAndDate);
	else if (timeAndDate>iMaximumTimeAndDate)
		SetTimeAndDate(iMaximumTimeAndDate);
	}

// CEikDurationEditor

EXPORT_C CEikDurationEditor::CEikDurationEditor()
	{
	__DECLARE_NAME(_S("CEikDurationEditor"));
	}

EXPORT_C CEikDurationEditor::~CEikDurationEditor()
	{
	delete iTimeEditor;
	}

EXPORT_C void CEikDurationEditor::ConstructL(const TTimeIntervalSeconds& aMinimumDuration, const TTimeIntervalSeconds& aMaximumDuration, const TTimeIntervalSeconds& aInitialDuration, TUint32 aFlags)
	{
	iTimeEditor=new(ELeave) CTimeEditor;
	aFlags|=CTimeEditor::EForce24HourFormat;
	CreateFieldArrayL(iTimeEditor->NumFieldsRequired(aFlags));
	iTimeEditor->ConstructFieldsL(*this, Convert(aInitialDuration), *iEikonEnv->NormalFont());
	DoSetMinimumAndMaximum(aMinimumDuration, aMaximumDuration);
	}

EXPORT_C void CEikDurationEditor::SetMinimumAndMaximum(const TTimeIntervalSeconds& aMinimumDuration, const TTimeIntervalSeconds& aMaximumDuration)
	{
	// __ASSERT_ALWAYS((aMinimumDuration>=iMinimumDuration) && (aMaximumDuration<=iMaximumDuration), Panic(EEikPanicDurationEditorBadMinimumAndMaximum1));
	DoSetMinimumAndMaximum(aMinimumDuration, aMaximumDuration);
	}

EXPORT_C void CEikDurationEditor::GetMinimumAndMaximum(TTimeIntervalSeconds& aMinimumDuration, TTimeIntervalSeconds& aMaximumDuration) const
	{
	aMinimumDuration=iMinimumDuration;
	aMaximumDuration=iMaximumDuration;
	}

EXPORT_C void CEikDurationEditor::SetDuration(const TTimeIntervalSeconds& aDuration)
	{
	iTimeEditor->SetTime(Convert(aDuration), *iEikonEnv->NormalFont());
	}

EXPORT_C TTimeIntervalSeconds CEikDurationEditor::Duration() const
	{
	return Convert(iTimeEditor->Time());
	}

EXPORT_C void CEikDurationEditor::ConstructFromResourceL(TResourceReader& aResourceReader)
	{
	TTimeIntervalSeconds minimumDuration=ReadDuration(aResourceReader);
	TTimeIntervalSeconds maximumDuration=ReadDuration(aResourceReader);
	TUint32 flags=aResourceReader.ReadUint8();
	ConstructL(minimumDuration, maximumDuration, maximumDuration, flags);
	}

EXPORT_C void CEikDurationEditor::PrepareForFocusLossL()
	{
	CEikMfne::PrepareForFocusLossL();
	TTimeIntervalSeconds duration=Duration();
	if (duration<iMinimumDuration)
		{
		SetDuration(iMinimumDuration);
		DrawNowAndLeaveWithTimeDateFormatInfoMsgL(R_EIK_TBUF_BELOW_SHORTEST_ALLOWED_DURATION, Convert(iMinimumDuration));
		}
	else if (duration>iMaximumDuration)
		{
		SetDuration(iMaximumDuration);
		DrawNowAndLeaveWithTimeDateFormatInfoMsgL(R_EIK_TBUF_ABOVE_LONGEST_ALLOWED_DURATION, Convert(iMaximumDuration));
		}
	}

void CEikDurationEditor::DoSetMinimumAndMaximum(const TTimeIntervalSeconds& aMinimumDuration, const TTimeIntervalSeconds& aMaximumDuration)
	{
	__ASSERT_ALWAYS(aMinimumDuration<=aMaximumDuration, Panic(EEikPanicDurationEditorBadMinimumAndMaximum2));
	iMinimumDuration=aMinimumDuration;
	iMaximumDuration=aMaximumDuration;
	TTimeIntervalSeconds duration=Duration();
	if (duration<iMinimumDuration)
		SetDuration(iMinimumDuration);
	else if (duration>iMaximumDuration)
		SetDuration(iMaximumDuration);
	}

// CEikTimeOffsetEditor

EXPORT_C CEikTimeOffsetEditor::CEikTimeOffsetEditor()
	{
	__DECLARE_NAME(_S("CEikTimeOffsetEditor"));
	}

EXPORT_C CEikTimeOffsetEditor::~CEikTimeOffsetEditor()
	{
	delete iTimeEditor;
	}

EXPORT_C void CEikTimeOffsetEditor::ConstructL(const TTimeIntervalSeconds& aMinimumTimeOffset, const TTimeIntervalSeconds& aMaximumTimeOffset, const TTimeIntervalSeconds& aInitialTimeOffset, TUint32 aFlags)
	{
	TTimeIntervalSeconds magnitudeOfInitialTimeOffset=aInitialTimeOffset;
	TBool positive=ETrue;
	if (magnitudeOfInitialTimeOffset.Int()<0)
		{
		magnitudeOfInitialTimeOffset=-magnitudeOfInitialTimeOffset.Int();
		positive=EFalse;
		}
	iTimeEditor=new(ELeave) CTimeEditor;
	aFlags|=CTimeEditor::EForce24HourFormat;
	CreateFieldArrayL(2+iTimeEditor->NumFieldsRequired(aFlags));
	iSign=CEikMfneSymbol::NewL(2);
	CleanupStack::PushL(iSign); // this needs to be pushed as it would not be destroyed by the class' destructor
	// add the "+" symbol
	HBufC* plusText=HBufC::NewLC(1); // also pushes on to CleanupStack
	*plusText=_L("+");
	iSign->AddSymbolicItem(CEikMfneSymbol::CItem::NewL('+', '+', plusText), positive);
	CleanupStack::Pop();
	// add the "-" symbol
	HBufC* minusText=HBufC::NewLC(1); // also pushes on to CleanupStack
	*minusText=_L("-");
	iSign->AddSymbolicItem(CEikMfneSymbol::CItem::NewL('-', '-', minusText), !positive);
	CleanupStack::Pop(2);
	AddField(iSign);
	HBufC* spaceSeparatorText=HBufC::NewLC(1); // also pushes on to CleanupStack
	spaceSeparatorText->Des().Append(TChar(' '));
	AddField(CEikMfneSeparator::NewL(spaceSeparatorText));
	CleanupStack::Pop();
	iTimeEditor->ConstructFieldsL(*this, Convert(magnitudeOfInitialTimeOffset), *iEikonEnv->NormalFont());
	DoSetMinimumAndMaximum(aMinimumTimeOffset, aMaximumTimeOffset);
	}

EXPORT_C void CEikTimeOffsetEditor::SetMinimumAndMaximum(const TTimeIntervalSeconds& aMinimumTimeOffset, const TTimeIntervalSeconds& aMaximumTimeOffset)
	{
	// __ASSERT_ALWAYS((aMinimumTimeOffset>=iMinimumTimeOffset) && (aMaximumTimeOffset<=iMaximumTimeOffset), Panic(EEikPanicTimeOffsetEditorBadMinimumAndMaximum1));
	DoSetMinimumAndMaximum(aMinimumTimeOffset, aMaximumTimeOffset);
	}

EXPORT_C void CEikTimeOffsetEditor::GetMinimumAndMaximum(TTimeIntervalSeconds& aMinimumTimeOffset, TTimeIntervalSeconds& aMaximumTimeOffset) const
	{
	aMinimumTimeOffset=iMinimumTimeOffset;
	aMaximumTimeOffset=iMaximumTimeOffset;
	}

EXPORT_C void CEikTimeOffsetEditor::SetTimeOffset(const TTimeIntervalSeconds& aTimeOffset)
	{
	TTimeIntervalSeconds magnitudeOfTimeOffset=aTimeOffset;
	TInt signId='+';
	if (magnitudeOfTimeOffset.Int()<0)
		{
		magnitudeOfTimeOffset=-magnitudeOfTimeOffset.Int();
		signId='-';
		}
	iTimeEditor->SetTime(Convert(magnitudeOfTimeOffset), *iEikonEnv->NormalFont());
	iSign->SetCurrentSymbolicItemToId(signId);
	}

EXPORT_C TTimeIntervalSeconds CEikTimeOffsetEditor::TimeOffset() const
	{
	TTimeIntervalSeconds timeOffset=Convert(iTimeEditor->Time());
	if (iSign->IdOfCurrentSymbolicItem()=='-')
		timeOffset=-timeOffset.Int();
	return timeOffset;
	}

EXPORT_C void CEikTimeOffsetEditor::ConstructFromResourceL(TResourceReader& aResourceReader)
	{
	TTimeIntervalSeconds minimumTimeOffset=ReadTimeOffset(aResourceReader);
	TTimeIntervalSeconds maximumTimeOffset=ReadTimeOffset(aResourceReader);
	TUint32 flags=aResourceReader.ReadUint8();
	ConstructL(minimumTimeOffset, maximumTimeOffset, maximumTimeOffset, flags);
	}

EXPORT_C void CEikTimeOffsetEditor::PrepareForFocusLossL()
	{
	CEikMfne::PrepareForFocusLossL();
	TTimeIntervalSeconds timeOffset=TimeOffset();
	if (timeOffset<iMinimumTimeOffset)
		{
		SetTimeOffset(iMinimumTimeOffset);
		TInt valueOfMinimumTimeOffset=iMinimumTimeOffset.Int();
		TInt resourceId=R_EIK_TBUF_BELOW_MINIMUM_TIME_OFFSET;
		if (valueOfMinimumTimeOffset<0)
			{
			valueOfMinimumTimeOffset=-valueOfMinimumTimeOffset;
			resourceId=R_EIK_TBUF_BELOW_NEGATIVE_MINIMUM_TIME_OFFSET;
			}
		DrawNowAndLeaveWithTimeDateFormatInfoMsgL(resourceId, Convert(TTimeIntervalSeconds(valueOfMinimumTimeOffset)));
		}
	else if (timeOffset>iMaximumTimeOffset)
		{
		SetTimeOffset(iMaximumTimeOffset);
		TInt valueOfMaximumTimeOffset=iMaximumTimeOffset.Int();
		TInt resourceId=R_EIK_TBUF_ABOVE_MAXIMUM_TIME_OFFSET;
		if (valueOfMaximumTimeOffset<0)
			{
			valueOfMaximumTimeOffset=-valueOfMaximumTimeOffset;
			resourceId=R_EIK_TBUF_ABOVE_NEGATIVE_MAXIMUM_TIME_OFFSET;
			}
		DrawNowAndLeaveWithTimeDateFormatInfoMsgL(resourceId, Convert(TTimeIntervalSeconds(valueOfMaximumTimeOffset)));
		}
	}

void CEikTimeOffsetEditor::DoSetMinimumAndMaximum(const TTimeIntervalSeconds& aMinimumTimeOffset, const TTimeIntervalSeconds& aMaximumTimeOffset)
	{
	__ASSERT_ALWAYS(aMinimumTimeOffset<=aMaximumTimeOffset, Panic(EEikPanicTimeOffsetEditorBadMinimumAndMaximum2));
	iMinimumTimeOffset=aMinimumTimeOffset;
	iMaximumTimeOffset=aMaximumTimeOffset;
	TTimeIntervalSeconds timeOffset=TimeOffset();
	if (timeOffset<iMinimumTimeOffset)
		SetTimeOffset(iMinimumTimeOffset);
	else if (timeOffset>iMaximumTimeOffset)
		SetTimeOffset(iMaximumTimeOffset);
	}

// CEikLatitudeLongitudeEditorBase

CEikLatitudeLongitudeEditorBase::CEikLatitudeLongitudeEditorBase(TInt aMaximumDegrees)
	:iMaximumDegrees(aMaximumDegrees)
	{
	__DECLARE_NAME(_S("CEikLatitudeLongitudeEditorBase"));
	}

void CEikLatitudeLongitudeEditorBase::DoConstructL(HBufC* aDegreeSign, HBufC* aMinuteSign, HBufC* aSecondSign, HBufC* aTextForFirstDirection, TChar aKeyToMatchForFirstDirection, HBufC* aTextForSecondDirection,
																		TChar aKeyToMatchForSecondDirection, const SEikDegreesMinutesDirection& aDegreesMinutesDirection,TInt aFlags)
	{
	__ASSERT_ALWAYS((aDegreesMinutesDirection.iDegrees>=0) &&
					(aDegreesMinutesDirection.iDegrees<=iMaximumDegrees) &&
					(aDegreesMinutesDirection.iMinutes>=0) &&
					(aDegreesMinutesDirection.iMinutes<=KMaxMinSecValue) &&
					((aDegreesMinutesDirection.iDirection==EEikCompassNorth) ||
					 (aDegreesMinutesDirection.iDirection==EEikCompassSouth) ||
					 (aDegreesMinutesDirection.iDirection==EEikCompassWest) ||
					 (aDegreesMinutesDirection.iDirection==EEikCompassEast)), Panic(EEikPanicLatitudeLongitudeEditorBaseBadValues2));
	const CFont& font=*iEikonEnv->NormalFont();
	if (aFlags&EEikMfneLatLongFlagAddSecondsField)
		CreateFieldArrayL(8);
	else
		{
		CreateFieldArrayL(5);
		delete aMinuteSign;
		delete aSecondSign;
		}
	iDegrees=CEikMfneNumber::NewL(font, 0, iMaximumDegrees, aDegreesMinutesDirection.iDegrees, 0);
	AddField(iDegrees);
	CEikMfneSeparator* degreeSign=CEikMfneSeparator::NewL(NULL);
	AddField(degreeSign);
	iMinutes=CEikMfneNumber::NewL(font, 0, KMaxMinSecValue, aDegreesMinutesDirection.iMinutes, 0);
	AddField(iMinutes);
	if (aFlags&EEikMfneLatLongFlagAddSecondsField)
		{
		CEikMfneSeparator* minuteSign=CEikMfneSeparator::NewL(NULL);
		AddField(minuteSign);
		iSeconds=CEikMfneNumber::NewL(font, 0, KMaxMinSecValue, aDegreesMinutesDirection.iSeconds, 0);
		AddField(iSeconds);
		CEikMfneSeparator* secondSign=CEikMfneSeparator::NewL(NULL);
		AddField(secondSign);
		minuteSign->SetText(aMinuteSign);
		secondSign->SetText(aSecondSign);
		}
	HBufC* spaceSeparatorText=HBufC::NewLC(1); // also pushes on to CleanupStack
	*spaceSeparatorText=_L(" ");
	AddField(CEikMfneSeparator::NewL(spaceSeparatorText));
	CleanupStack::Pop();
	iDirection=CEikMfneSymbol::NewL(2);
	CleanupStack::PushL(iDirection); // this needs to be pushed as it would not be destroyed by the class' destructor
	TInt orientation=(aDegreesMinutesDirection.iDirection&EEikOrientationMask);
	TInt firstDirectionId=orientation|EEikDirectionNegative;
	CEikMfneSymbol::CItem* firstDirection=CEikMfneSymbol::CItem::NewL(firstDirectionId, aKeyToMatchForFirstDirection, NULL);
	iDirection->AddSymbolicItem(firstDirection, aDegreesMinutesDirection.iDirection==firstDirectionId);
	TInt secondDirectionId=orientation|EEikDirectionPositive;
	CEikMfneSymbol::CItem* secondDirection=CEikMfneSymbol::CItem::NewL(secondDirectionId, aKeyToMatchForSecondDirection, NULL);
	iDirection->AddSymbolicItem(secondDirection, aDegreesMinutesDirection.iDirection==secondDirectionId);
	AddField(iDirection);
	CleanupStack::Pop();
	// do stuff that can only be done when all leaving functions have successfully been done
	degreeSign->SetText(aDegreeSign);
	firstDirection->SetText(aTextForFirstDirection);
	secondDirection->SetText(aTextForSecondDirection);
	}

void CEikLatitudeLongitudeEditorBase::SetDegreesMinutesDirection(const SEikDegreesMinutesDirection& aDegreesMinutesDirection)
	{
	__ASSERT_ALWAYS((aDegreesMinutesDirection.iDegrees>=0) &&
					(aDegreesMinutesDirection.iDegrees<=iMaximumDegrees) &&
					(aDegreesMinutesDirection.iMinutes>=0) &&
					(aDegreesMinutesDirection.iMinutes<=KMaxMinSecValue) &&
					((aDegreesMinutesDirection.iDirection==EEikCompassNorth) ||
					 (aDegreesMinutesDirection.iDirection==EEikCompassSouth) ||
					 (aDegreesMinutesDirection.iDirection==EEikCompassWest) ||
					 (aDegreesMinutesDirection.iDirection==EEikCompassEast)), Panic(EEikPanicLatitudeLongitudeEditorBaseBadValues2));
	const CFont& font=*iEikonEnv->NormalFont();
	iDegrees->SetValue(aDegreesMinutesDirection.iDegrees, font);
	iMinutes->SetValue(aDegreesMinutesDirection.iMinutes, font);
	if (iSeconds!=NULL)
		iSeconds->SetValue(aDegreesMinutesDirection.iSeconds, font);
	iDirection->SetCurrentSymbolicItemToId(aDegreesMinutesDirection.iDirection);
	}

SEikDegreesMinutesDirection CEikLatitudeLongitudeEditorBase::DegreesMinutesDirection() const
	{
	SEikDegreesMinutesDirection degreesMinutesDirection;
	degreesMinutesDirection.iDegrees=iDegrees->Value();
	degreesMinutesDirection.iMinutes=iMinutes->Value();
	if (iSeconds)
		degreesMinutesDirection.iSeconds=iSeconds->Value();
	degreesMinutesDirection.iDirection=(TEikCompass)iDirection->IdOfCurrentSymbolicItem();
	return degreesMinutesDirection;
	}

// CEikLatitudeEditor

EXPORT_C CEikLatitudeEditor::CEikLatitudeEditor()
	:CEikLatitudeLongitudeEditorBase(KMaxLatitudeDegrees)
	{
	__DECLARE_NAME(_S("CEikLatitudeEditor"));
	}

EXPORT_C void CEikLatitudeEditor::ConstructL(HBufC* aDegreeSign, HBufC* aMinuteSign, HBufC* aSecondSign, HBufC* aTextForNorth, TChar aKeyToMatchForNorth, HBufC* aTextForSouth, TChar aKeyToMatchForSouth, const SEikDegreesMinutesDirection& aLatitude,TInt aFlags)
	{
	DoConstructL(aDegreeSign, aMinuteSign, aSecondSign, aTextForNorth, aKeyToMatchForNorth, aTextForSouth, aKeyToMatchForSouth, aLatitude,aFlags);
	}

EXPORT_C void CEikLatitudeEditor::SetLatitude(const SEikDegreesMinutesDirection& aLatitude)
	{
	__ASSERT_ALWAYS((aLatitude.iDirection==EEikCompassNorth) || (aLatitude.iDirection==EEikCompassSouth), Panic(EEikPanicLatitudeEditorBadDirection));
	SetDegreesMinutesDirection(aLatitude);
	}

EXPORT_C SEikDegreesMinutesDirection CEikLatitudeEditor::Latitude() const
	{
	return DegreesMinutesDirection();
	}

EXPORT_C void CEikLatitudeEditor::ConstructFromResourceL(TResourceReader& aResourceReader)
	{
	TInt flags=aResourceReader.ReadInt8();
	TInt latitudeAndLongitudeResourceId=aResourceReader.ReadInt32();
	TResourceReader resourceReader;
	iCoeEnv->CreateResourceReaderLC(resourceReader, latitudeAndLongitudeResourceId);
	HBufC* degreeSign=resourceReader.ReadHBufCL();
	CleanupStack::PushL(degreeSign);
	HBufC* minuteSign=resourceReader.ReadHBufCL();
	CleanupStack::PushL(minuteSign);
	HBufC* secondSign=resourceReader.ReadHBufCL();
	CleanupStack::PushL(secondSign);
	HBufC* textForNorth=resourceReader.ReadHBufCL();
	CleanupStack::PushL(textForNorth);
	TChar keyToMatchForNorth=resourceReader.ReadUint16();
	HBufC* textForSouth=resourceReader.ReadHBufCL();
	CleanupStack::PushL(textForSouth);
	TChar keyToMatchForSouth=resourceReader.ReadUint16();
	SEikDegreesMinutesDirection latitude;
	latitude.iDegrees=KMaxLatitudeDegrees;
	latitude.iMinutes=0;
	latitude.iSeconds=0;
	latitude.iDirection=EEikCompassNorth;
	ConstructL(degreeSign, minuteSign, secondSign, textForNorth, keyToMatchForNorth, textForSouth, keyToMatchForSouth, latitude,flags);
	CleanupStack::Pop(5); // pop off textForSouth, textForNorth and degreeSign, minutesign,secondsign
	CleanupStack::PopAndDestroy();
	}

EXPORT_C void CEikLatitudeEditor::PrepareForFocusLossL()
	{
	CEikMfne::PrepareForFocusLossL();
	if (iDegrees->Value()==KMaxLatitudeDegrees &&
		(iMinutes->Value()>0 || (iSeconds!=NULL && iSeconds->Value()>0)))
		{
		iMinutes->SetValue(0,*iEikonEnv->NormalFont());
		if (iSeconds!=NULL)
			iSeconds->SetValue(0,*iEikonEnv->NormalFont());
		DrawNow();
		iEikonEnv->LeaveWithInfoMsg(R_EIK_TBUF_NUMBER_ABOVE_MAX, iMaximumDegrees);
		}
	}


// CEikLongitudeEditor

EXPORT_C CEikLongitudeEditor::CEikLongitudeEditor()
	:CEikLatitudeLongitudeEditorBase(KMaxLongitudeDegrees)
	{
	__DECLARE_NAME(_S("CEikLongitudeEditor"));
	}

EXPORT_C void CEikLongitudeEditor::ConstructL(HBufC* aDegreeSign, HBufC* aMinuteSign, HBufC* aSecondSign, HBufC* aTextForEast, TChar aKeyToMatchForEast, HBufC* aTextForWest, TChar aKeyToMatchForWest, const SEikDegreesMinutesDirection& aLongitude,TInt aFlags)
	{
	DoConstructL(aDegreeSign, aMinuteSign, aSecondSign, aTextForEast, aKeyToMatchForEast, aTextForWest, aKeyToMatchForWest, aLongitude,aFlags);
	}

EXPORT_C void CEikLongitudeEditor::SetLongitude(const SEikDegreesMinutesDirection& aLongitude)
	{
	__ASSERT_ALWAYS((aLongitude.iDirection==EEikCompassWest) || (aLongitude.iDirection==EEikCompassEast), Panic(EEikPanicLongitudeEditorBadDirection));
	SetDegreesMinutesDirection(aLongitude);
	}

EXPORT_C SEikDegreesMinutesDirection CEikLongitudeEditor::Longitude() const
	{
	return DegreesMinutesDirection();
	}

EXPORT_C void CEikLongitudeEditor::ConstructFromResourceL(TResourceReader& aResourceReader)
	{
	TInt flags=aResourceReader.ReadInt8();
	TInt latitudeAndLongitudeResourceId=aResourceReader.ReadInt32();
	TResourceReader resourceReader;
	iCoeEnv->CreateResourceReaderLC(resourceReader, latitudeAndLongitudeResourceId);
	HBufC* degreeSign=resourceReader.ReadHBufCL();
	CleanupStack::PushL(degreeSign);
	HBufC* minuteSign=resourceReader.ReadHBufCL();
	CleanupStack::PushL(minuteSign);
	HBufC* secondSign=resourceReader.ReadHBufCL();
	CleanupStack::PushL(secondSign);
	resourceReader.ReadTPtrC(); // read and discard textForNorth
	resourceReader.ReadUint16(); // read and discard keyToMatchForNorth
	resourceReader.ReadTPtrC(); // read and discard textForSouth
	resourceReader.ReadUint16(); // read and discard keyToMatchForSouth
	HBufC* textForEast=resourceReader.ReadHBufCL();
	CleanupStack::PushL(textForEast);
	TChar keyToMatchForEast=resourceReader.ReadUint16();
	HBufC* textForWest=resourceReader.ReadHBufCL();
	CleanupStack::PushL(textForWest);
	TChar keyToMatchForWest=resourceReader.ReadUint16();
	SEikDegreesMinutesDirection longitude;
	longitude.iDegrees=KMaxLongitudeDegrees;
	longitude.iMinutes=0;
	longitude.iSeconds=0;
	longitude.iDirection=EEikCompassEast;
	ConstructL(degreeSign, minuteSign, secondSign, textForEast, keyToMatchForEast, textForWest, keyToMatchForWest, longitude,flags);
	CleanupStack::Pop(5); // pop off textForWest, textForEast and degreeSign, minutesign,secondsign
	CleanupStack::PopAndDestroy();
	}

EXPORT_C void CEikLongitudeEditor::PrepareForFocusLossL()
	{
	CEikMfne::PrepareForFocusLossL();
	if (iDegrees->Value()==KMaxLongitudeDegrees &&
		(iMinutes->Value()>0 || (iSeconds!=NULL && iSeconds->Value()>0)))
		{
		iMinutes->SetValue(0,*iEikonEnv->NormalFont());
		if (iSeconds!=NULL)
			iSeconds->SetValue(0,*iEikonEnv->NormalFont());
		DrawNow();
		iEikonEnv->LeaveWithInfoMsg(R_EIK_TBUF_NUMBER_ABOVE_MAX, iMaximumDegrees);
		}
	}

