// EIKRNGED.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include <barsread.h>
#include <bacell.h>
#include <eikenv.h>
#include <eiktxtut.h>
#include <eikmfne.hrh>
#include <eikmfne.h>
#include <eikrnged.h>
#include <eikon.rsg>

enum TEikRngedPanic
	{
	EEikPanicBadCellRefMinimumAndMaximum1,
	EEikPanicBadCellRefMinimumAndMaximum2,
	EEikPanicBadCellRefMinimumAndMaximum3,
	EEikPanicBadCellRefFlags,
	EEikPanicInconsistentCellRefFlags,
	EEikPanicLetterNotExpected,
	EEikPanicDigitNotExpected,
	EEikPanicCellRefOutsidePermittedLimits,
	EEikPanicCellRefHasNoDigits,
	EEikPanicCellRefHasNoLetters,
	EEikPanicDigitExpected,
	EEikPanicNegativeRow,
	EEikPanicNegativeColumn,
	EEikPanicRangeRefEditorInconsistentMinimumAndMaximum
	};

LOCAL_C void Panic(TEikRngedPanic aPanic)
	{
	User::Panic(_L("EIKON-RNGREFED"),aPanic);
	}

// TEikFindWidthOfWidestLetter

class TEikFindWidthOfWidestLetter : public TEikFindWidthOfWidestTextItem
	{
private:
	virtual void GetTextItem(TDes& aText,TInt aIndex) const;
	};

void TEikFindWidthOfWidestLetter::GetTextItem(TDes& aText,TInt aIndex) const
	{
	aText.SetLength(0);
	aText.Append(aIndex);
	}

// TEikFindWidthOfWidestLowerCaseLetter

class TEikFindWidthOfWidestLowerCaseLetter : public TEikFindWidthOfWidestLetter
	{
private:
	virtual void GetFirstAndLastIndex(TInt& aFirstIndex,TInt& aLastIndex) const;
	};

void TEikFindWidthOfWidestLowerCaseLetter::GetFirstAndLastIndex(TInt& aFirstIndex,TInt& aLastIndex) const
	{
	aFirstIndex='a';
	aLastIndex='z';
	}

// TEikFindWidthOfWidestUpperCaseLetter

class TEikFindWidthOfWidestUpperCaseLetter : public TEikFindWidthOfWidestLetter
	{
private:
	virtual void GetFirstAndLastIndex(TInt& aFirstIndex,TInt& aLastIndex) const;
	};

void TEikFindWidthOfWidestUpperCaseLetter::GetFirstAndLastIndex(TInt& aFirstIndex,TInt& aLastIndex) const
	{
	aFirstIndex='A';
	aLastIndex='Z';
	}

// CEikMfneCellRef

CEikMfneCellRef::CEikMfneCellRef(const TCellRef& aMinimumCellRef,const TCellRef& aMaximumCellRef,TUint32 aFlags)
	:iMinimumCellRef(aMinimumCellRef),
	 iMaximumCellRef(aMaximumCellRef),
	 iMinimumCellRefCateredForInFieldWidth(aMinimumCellRef),
	 iMaximumCellRefCateredForInFieldWidth(aMaximumCellRef),
	 iFlags(aFlags)
	{
	__DECLARE_NAME(_S("CEikMfneCellRef"));
	__ASSERT_ALWAYS((aMinimumCellRef.iRow>=0) &&
					(aMinimumCellRef.iCol>=0) &&
					(aMinimumCellRef.iRow<=aMaximumCellRef.iRow) &&
					(aMinimumCellRef.iCol<=aMaximumCellRef.iCol),Panic(EEikPanicBadCellRefMinimumAndMaximum1));
	__ASSERT_ALWAYS((aFlags&~EPublicallySettableFlags)==0,Panic(EEikPanicBadCellRefFlags));
	}

EXPORT_C CEikMfneCellRef::~CEikMfneCellRef()
	{
	delete iText;
	}

EXPORT_C CEikMfneCellRef* CEikMfneCellRef::NewL(const CFont& aFont,TResourceReader& aResourceReader)
	{
#pragma warning (disable : 4127)
	__ASSERT_DEBUG(EPreserveOldWidthBeforeEditing==EEikMfneNumberFlagPreserveOldWidthBeforeEditing,Panic(EEikPanicInconsistentCellRefFlags));
#pragma warning (default : 4127)
	TCellRef minimumCellRef;
	minimumCellRef.iRow=aResourceReader.ReadInt16();
	minimumCellRef.iCol=aResourceReader.ReadInt16();
	TCellRef maximumCellRef;
	maximumCellRef.iRow=aResourceReader.ReadInt16();
	maximumCellRef.iCol=aResourceReader.ReadInt16();
	TUint flags=aResourceReader.ReadUint8();
	return NewL(aFont,minimumCellRef,maximumCellRef,maximumCellRef,flags);
	}

EXPORT_C CEikMfneCellRef* CEikMfneCellRef::NewL(const CFont& aFont,const TCellRef& aMinimumCellRef,const TCellRef& aMaximumCellRef,const TCellRef& aInitialCellRef,TUint32 aFlags)
	{
	__ASSERT_ALWAYS((aInitialCellRef.iRow>=aMinimumCellRef.iRow) &&
					(aInitialCellRef.iCol>=aMinimumCellRef.iCol) &&
					(aInitialCellRef.iRow<=aMaximumCellRef.iRow) &&
					(aInitialCellRef.iCol<=aMaximumCellRef.iCol),Panic(EEikPanicBadCellRefMinimumAndMaximum2));
	CEikMfneCellRef* cellRef=new(ELeave) CEikMfneCellRef(aMinimumCellRef,aMaximumCellRef,aFlags);
	CleanupStack::PushL(cellRef);
	cellRef->iText=HBufC::NewL(cellRef->MaximumNumberOfLetters(ETrue)+cellRef->MaximumNumberOfDigits(ETrue));
	cellRef->SetTextToCellRef(aInitialCellRef,aFont);
	CleanupStack::Pop();
	return cellRef;
	}

EXPORT_C void CEikMfneCellRef::SetMinimumAndMaximum(const TCellRef& aMinimumCellRef,const TCellRef& aMaximumCellRef,const CFont& aFont)
	{
	__ASSERT_ALWAYS((aMinimumCellRef.iRow>=0) &&
					(aMinimumCellRef.iCol>=0) &&
					(aMinimumCellRef.iRow<=aMaximumCellRef.iRow) &&
					(aMinimumCellRef.iCol<=aMaximumCellRef.iCol) &&
					(aMinimumCellRef.iRow>=iMinimumCellRefCateredForInFieldWidth.iRow) &&
					(aMinimumCellRef.iCol>=iMinimumCellRefCateredForInFieldWidth.iCol) &&
					(aMaximumCellRef.iRow<=iMaximumCellRefCateredForInFieldWidth.iRow) &&
					(aMaximumCellRef.iCol<=iMaximumCellRefCateredForInFieldWidth.iCol),Panic(EEikPanicBadCellRefMinimumAndMaximum3));
	iMinimumCellRef=aMinimumCellRef;
	iMaximumCellRef=aMaximumCellRef;
	TCellRef cellRef=CellRefFromText();
	TBool doSet=EFalse;
	if (cellRef.iRow<aMinimumCellRef.iRow)
		{
		cellRef.iRow=aMinimumCellRef.iRow;
		doSet=ETrue;
		}
	if (cellRef.iRow>aMaximumCellRef.iRow)
		{
		cellRef.iRow=aMaximumCellRef.iRow;
		doSet=ETrue;
		}
	if (cellRef.iCol<aMinimumCellRef.iCol)
		{
		cellRef.iCol=aMinimumCellRef.iCol;
		doSet=ETrue;
		}
	if (cellRef.iCol>aMaximumCellRef.iCol)
		{
		cellRef.iCol=aMaximumCellRef.iCol;
		doSet=ETrue;
		}
	if (doSet)
		SetTextToCellRef(cellRef,aFont);
	}

EXPORT_C void CEikMfneCellRef::GetMinimumAndMaximum(TCellRef& aMinimumCellRef,TCellRef& aMaximumCellRef) const
	{
	aMinimumCellRef=iMinimumCellRef;
	aMaximumCellRef=iMaximumCellRef;
	}

EXPORT_C void CEikMfneCellRef::SetCellRef(const TCellRef& aCellRef,const CFont& aFont)
	{
	SetTextToCellRef(aCellRef,aFont);
	}

EXPORT_C TCellRef CEikMfneCellRef::CellRef() const
	{
	return CellRefFromText();
	}

TInt CEikMfneCellRef::MaximumWidthInPixels(const CFont& aFont,TBool aShrinkToMinimumSize)
	{
	if (aShrinkToMinimumSize)
		{
		iMinimumCellRefCateredForInFieldWidth=iMinimumCellRef;
		iMaximumCellRefCateredForInFieldWidth=iMaximumCellRef;
		}
	return (MaximumNumberOfLetters(ETrue)*Max(TEikFindWidthOfWidestLowerCaseLetter().MaximumWidthInPixels(aFont),
											  TEikFindWidthOfWidestUpperCaseLetter().MaximumWidthInPixels(aFont)))+
		   (MaximumNumberOfDigits(ETrue)*TEikFindWidthOfWidestDigit().MaximumWidthInPixels(aFont));
	}

TBool CEikMfneCellRef::IsEditable() const
	{
	return ETrue;
	}

CEikMfneField::THighlightType CEikMfneCellRef::HighlightType() const
	{
	return (iFlags&EIsBeingEditedWithCursor)? ECursor: EInverseVideo;
	}

void CEikMfneCellRef::HandleKey(const CFont& /*aFont*/, const TKeyEvent& aKeyEvent, TBool /*aInterpretLeftAndRightAsEarEvents*/, TBool& aDataAltered, TInt& aHighlightIncrement)
	{
	TChar key(aKeyEvent.iCode);
	TPtr text=iText->Des();
	TInt textLength=text.Length();
	switch (key)
		{
	case EKeyLeftArrow:
		aHighlightIncrement=-1;
		aDataAltered=ETrue;
		break;
	case EKeyRightArrow:
		aHighlightIncrement=1;
		aDataAltered=ETrue;
		break;
	case EKeyBackspace:
		if (textLength)
			{
			iFlags|=EIsBeingEditedWithCursor;
			text.SetLength(textLength-1);
			aDataAltered=ETrue;
			}
		break;
	default:
		if (key.IsAlphaDigit() && !(iFlags&EIsBeingEditedWithCursor))
			{
			iFlags|=EIsBeingEditedWithCursor;
			text.SetLength(0);
			textLength=text.Length();
			aDataAltered=ETrue;
			}
		CEikonEnv* eikEnv=CEikonEnv::Static();
		if (key.IsAlpha())
			{
			if ((textLength>0) && !TChar(text[textLength-1]).IsAlpha())
				{
				TBuf<64> text;
				eikEnv->ReadResource(text,R_EIK_TBUF_RANGE_TEXT_1);
				eikEnv->InfoMsg(text);
				}
			else
				{
				if (NumberOfLetters()==MaximumNumberOfLetters(EFalse))
					{
					TBuf<64> text;
					eikEnv->ReadResource(text,R_EIK_TBUF_RANGE_TEXT_2);
					eikEnv->InfoMsg(text);
					}
				else
					{
					text.Append(key);
					aDataAltered=ETrue;
					}
				}
			}
		else if (key.IsDigit())
			{
			if (NumberOfLetters()==0)
				{
				TBuf<64> text;
				eikEnv->ReadResource(text,R_EIK_TBUF_RANGE_TEXT_3);
				eikEnv->InfoMsg(text);
				}
			else
				{
				text.Append(key);
				aDataAltered=ETrue;
				}
			if (NumberOfDigits()==MaximumNumberOfDigits(EFalse))
				aHighlightIncrement=1;
			}
		break;
		}
	}

void CEikMfneCellRef::HandleDeHighlight(const CFont& aFont,CEikonEnv& aEikonEnv,TBool& aDataAltered,TBool& aError)
	{
	iFlags&=~EIsBeingEditedWithCursor;
	if (iText->Length()==0)
		{
		SetTextToCellRef(iMaximumCellRef,aFont);
		aDataAltered=ETrue;
		aError=ETrue;
		aEikonEnv.InfoMsg(R_EIK_TBUF_NO_CELL_ENTERED);
		return;
		}
	if (NumberOfLetters()==0)
		{
		SetTextToCellRef(iMinimumCellRef,aFont);
		aDataAltered=ETrue;
		aError=ETrue;
		aEikonEnv.InfoMsg(R_EIK_TBUF_NO_COLUMN_SPECIFIED);
		return;
		}
	if (NumberOfDigits()==0)
		{
		SetTextToCellRef(iMinimumCellRef,aFont);
		aDataAltered=ETrue;
		aError=ETrue;
		aEikonEnv.InfoMsg(R_EIK_TBUF_NO_ROW_SPECIFIED);
		return;
		}
	TCellRef cellRef=CellRefFromText();
	TBool outsideMinimumAndMaximum=EFalse;
	if (cellRef.iRow<iMinimumCellRef.iRow)
		{
		cellRef.iRow=iMinimumCellRef.iRow;
		outsideMinimumAndMaximum=ETrue;
		}
	if (cellRef.iCol<iMinimumCellRef.iCol)
		{
		cellRef.iCol=iMinimumCellRef.iCol;
		outsideMinimumAndMaximum=ETrue;
		}
	if (cellRef.iRow>iMaximumCellRef.iRow)
		{
		cellRef.iRow=iMaximumCellRef.iRow;
		outsideMinimumAndMaximum=ETrue;
		}
	if (cellRef.iCol>iMaximumCellRef.iCol)
		{
		cellRef.iCol=iMaximumCellRef.iCol;
		outsideMinimumAndMaximum=ETrue;
		}
	if (outsideMinimumAndMaximum)
		{
		SetTextToCellRef(cellRef,aFont);
		aDataAltered=ETrue;
		aError=ETrue;
		aEikonEnv.InfoMsg(R_EIK_TBUF_CELL_OUT_OF_RANGE);
		return;
		}
	TBuf<128> oldText=*iText;
	SetTextToCellRef(cellRef,aFont);
	if (oldText!=*iText)
		aDataAltered=ETrue;
	}

const TDesC& CEikMfneCellRef::Text() const
	{
	return *iText;
	}

TInt CEikMfneCellRef::MaximumNumberOfLetters(TBool aUseMinimumAndMaximumCateredForInFieldWidth) const
	{
	TInt maximumNumberOfLetters=0;
	TInt i=aUseMinimumAndMaximumCateredForInFieldWidth? iMaximumCellRefCateredForInFieldWidth.iCol: iMaximumCellRef.iCol;
	for (; i>=0; i=(i/ENumLetters)-1)
		++maximumNumberOfLetters;
	return Max(1,maximumNumberOfLetters);
	}

TInt CEikMfneCellRef::MaximumNumberOfDigits(TBool aUseMinimumAndMaximumCateredForInFieldWidth) const
	{
	TInt maximumNumberOfDigits=0;
	TInt i=(aUseMinimumAndMaximumCateredForInFieldWidth? iMaximumCellRefCateredForInFieldWidth.iRow: iMaximumCellRef.iRow)+1;
	for (; i; i/=10)
		++maximumNumberOfDigits;
	return Max(1,maximumNumberOfDigits);
	}

TInt CEikMfneCellRef::NumberOfLetters() const
	{
	TInt numberOfLetters=0;
	const TInt textLength=iText->Length();
	for (TInt i=0; i<textLength; ++i)
		{
		if (TChar((*iText)[i]).IsAlpha())
			++numberOfLetters;
		else
			{
#if defined(_DEBUG)
			for (++i; i<textLength; ++i)
				__ASSERT_DEBUG(!TChar((*iText)[i]).IsAlpha(),Panic(EEikPanicLetterNotExpected));
#endif
			break;
			}
		}
	return numberOfLetters;
	}

TInt CEikMfneCellRef::NumberOfDigits() const
	{
	TInt numberOfDigits=0;
	for (TInt i=iText->Length()-1; i>=0; --i)
		{
		if (TChar((*iText)[i]).IsDigit())
			++numberOfDigits;
		else
			{
#if defined(_DEBUG)
			for (--i; i>=0; --i)
				__ASSERT_DEBUG(!TChar((*iText)[i]).IsDigit(),Panic(EEikPanicDigitNotExpected));
#endif
			break;
			}
		}
	return numberOfDigits;
	}

void CEikMfneCellRef::SetTextToCellRef(const TCellRef& aCellRef,const CFont& aFont)
	{
	__ASSERT_DEBUG((aCellRef.iRow>=0) &&
				   (aCellRef.iCol>=0) &&
				   (aCellRef.iRow>=iMinimumCellRef.iRow) &&
				   (aCellRef.iCol>=iMinimumCellRef.iCol) &&
				   (aCellRef.iRow<=iMaximumCellRef.iRow) &&
				   (aCellRef.iCol<=iMaximumCellRef.iCol),Panic(EEikPanicCellRefOutsidePermittedLimits));
	TPtr text=iText->Des();
	text.SetLength(0);
	TInt i;
	TBuf<1> buffer;
	for (i=aCellRef.iRow+1; i; i/=10)
		{
		buffer.SetLength(0);
		buffer.Append((TText)('0'+(i%10)));
		text.Insert(0,buffer);
		}
	for (i=aCellRef.iCol; i>=0; i=(i/ENumLetters)-1)
		{
		buffer.SetLength(0);
		buffer.Append((TText)('A'+(i%ENumLetters)));
		text.Insert(0,buffer);
		}
	if (iFlags&EPreserveOldWidthBeforeEditing)
		iMinimumWidthInPixels=aFont.TextWidthInPixels(text);
	}

TCellRef CEikMfneCellRef::CellRefFromText() const
	{
	return TCellRef(RowFromText(),ColumnFromText());
	}

TInt CEikMfneCellRef::RowFromText() const
	{
	__ASSERT_DEBUG(NumberOfDigits(),Panic(EEikPanicCellRefHasNoDigits));
	TInt rowFromText=0;
	const TInt textLength=iText->Length();
	TInt i=0;
	for (; i<textLength; ++i)
		{
		TText letter=(*iText)[i];
		if (TChar(letter).IsDigit())
			break;
		}
	for (; i<textLength; ++i)
		{
		TText digit=(*iText)[i];
		__ASSERT_DEBUG(TChar(digit).IsDigit(),Panic(EEikPanicDigitExpected));
		rowFromText=(rowFromText*10)+(TInt)(digit-'0');
		}
	--rowFromText;
	__ASSERT_DEBUG(rowFromText>=0,Panic(EEikPanicNegativeRow));
	return rowFromText;
	}

TInt CEikMfneCellRef::ColumnFromText() const
	{
	__ASSERT_DEBUG(NumberOfLetters(),Panic(EEikPanicCellRefHasNoLetters));
	TInt columnFromText=0;
	const TInt textLength=iText->Length();
	for (TInt i=0; i<textLength; ++i)
		{
		TText letter=(*iText)[i];
		if (!TChar(letter).IsAlpha())
			break;
		columnFromText=(columnFromText*ENumLetters)+(TInt)(User::UpperCase(letter)-'A')+1;
		}
	--columnFromText;
	__ASSERT_DEBUG(columnFromText>=0,Panic(EEikPanicNegativeColumn));
	return columnFromText;
	}

// CEikRangeRefEditor

EXPORT_C CEikRangeRefEditor::CEikRangeRefEditor()
	{
	__DECLARE_NAME(_S("CEikRangeRefEditor"));
	}

EXPORT_C void CEikRangeRefEditor::ConstructL(const TCellRef& aMinimumCellRef,const TCellRef& aMaximumCellRef,const TRangeRef& aInitialRangeRef,HBufC* aSeparatorText)
	{
	const CFont& font=*iEikonEnv->NormalFont();
	CreateFieldArrayL(3);
	iFrom=CEikMfneCellRef::NewL(font,aMinimumCellRef,aMaximumCellRef,aInitialRangeRef.iFrom,0);
	AddField(iFrom);
	CEikMfneSeparator* separator=CEikMfneSeparator::NewL(NULL);
	AddField(separator);
	iTo=CEikMfneCellRef::NewL(font,aMinimumCellRef,aMaximumCellRef,aInitialRangeRef.iTo,0);
	AddField(iTo);
	// do stuff that can only be done when all leaving functions have successfully been done
	separator->SetText(aSeparatorText);
	}

EXPORT_C void CEikRangeRefEditor::SetMinimumAndMaximum(const TCellRef& aMinimumCellRef,const TCellRef& aMaximumCellRef)
	{
	const CFont& font=*iEikonEnv->NormalFont();
	iFrom->SetMinimumAndMaximum(aMinimumCellRef,aMaximumCellRef,font);
	iTo->SetMinimumAndMaximum(aMinimumCellRef,aMaximumCellRef,font);
	}

EXPORT_C void CEikRangeRefEditor::GetMinimumAndMaximum(TCellRef& aMinimumCellRef,TCellRef& aMaximumCellRef) const
	{
	iFrom->GetMinimumAndMaximum(aMinimumCellRef,aMaximumCellRef);
#if defined(_DEBUG)
	TCellRef minimumCellRef;
	TCellRef maximumCellRef;
	iTo->GetMinimumAndMaximum(minimumCellRef,maximumCellRef);
	__ASSERT_DEBUG((minimumCellRef==aMinimumCellRef) && (maximumCellRef==aMaximumCellRef),Panic(EEikPanicRangeRefEditorInconsistentMinimumAndMaximum));
#endif
	}

EXPORT_C void CEikRangeRefEditor::SetRangeRef(const TRangeRef& aRangeRef)
	{
	const CFont& font=*iEikonEnv->NormalFont();
	iFrom->SetCellRef(aRangeRef.iFrom,font);
	iTo->SetCellRef(aRangeRef.iTo,font);
	}

EXPORT_C TRangeRef CEikRangeRefEditor::RangeRef() const
	{
	return TRangeRef(iFrom->CellRef(),iTo->CellRef());
	}

EXPORT_C void CEikRangeRefEditor::ConstructFromResourceL(TResourceReader& aResourceReader)
	{
	TCellRef minimumCellRef;
	minimumCellRef.iRow=aResourceReader.ReadInt16();
	minimumCellRef.iCol=aResourceReader.ReadInt16();
	TCellRef maximumCellRef;
	maximumCellRef.iRow=aResourceReader.ReadInt16();
	maximumCellRef.iCol=aResourceReader.ReadInt16();
	HBufC* separatorText=aResourceReader.ReadHBufCL();
	CleanupStack::PushL(separatorText);
	ConstructL(minimumCellRef,maximumCellRef,TRangeRef(minimumCellRef,minimumCellRef),separatorText);
	CleanupStack::Pop();
	}

void CEikRangeRefEditor::FieldIsAboutToBeDeHighlighted(CEikMfneField* aField,TBool& aDrawAllFields)
	{
	const CFont& font=*iEikonEnv->NormalFont();
	TCellRef fromCellRef=iFrom->CellRef();
	TCellRef toCellRef=iTo->CellRef();
	if (aField==iFrom)
		{
		TBool adjustOtherField=EFalse;
		if (toCellRef.iRow<fromCellRef.iRow)
			{
			toCellRef.iRow=fromCellRef.iRow;
			adjustOtherField=ETrue;
			}
		if (toCellRef.iCol<fromCellRef.iCol)
			{
			toCellRef.iCol=fromCellRef.iCol;
			adjustOtherField=ETrue;
			}
		if (adjustOtherField)
			{
			iTo->SetCellRef(toCellRef,font);
			aDrawAllFields=ETrue;
			}
		}
	else if (aField==iTo)
		{
		TBool adjustOtherField=EFalse;
		if (fromCellRef.iRow>toCellRef.iRow)
			{
			fromCellRef.iRow=toCellRef.iRow;
			adjustOtherField=ETrue;
			}
		if (fromCellRef.iCol>toCellRef.iCol)
			{
			fromCellRef.iCol=toCellRef.iCol;
			adjustOtherField=ETrue;
			}
		if (adjustOtherField)
			{
			iFrom->SetCellRef(fromCellRef,font);
			aDrawAllFields=ETrue;
			}
		}
	}
