// EIKCHMAP.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include <eikchmap.h>
#include <eikenv.h>
#include <eikbordr.h>
#include <coecobs.h>
#include <eikpriv.hrh>
#include <eikrutil.h>

#include <eikcolor.h>
#include <eiklabel.h>
#include <eikon.rsg>
#include <eikdialg.hrh>
#include <barsread.h>

const TInt KEikChMapBorder = 2;
const TInt KEikCharBorder = 2;
const TInt KEikStartCharacter = 128;
const TInt KEikLastCharacter = 255;
const TInt KEikChMapColumns = 16;
const TInt KEikChMapRows = 8;
const TInt KEikExtraCursorHeight=2;
const TInt KEikExtraCursorWidth=6;
const TInt KEikExtraCharWidth=10;
const TInt KEikExtraCharHeight=3;
const TInt KEikHeightOfCharMapFontInTwips=200;
const TInt KEikTotalBorder=3*KEikCharBorder;

const TInt KChMapExtraCharPos1=129;
const TInt KChMapExtraCharPos2=141;
const TInt KChMapExtraCharPos3=143;
const TInt KChMapExtraCharPos4=144;
const TInt KChMapExtraCharPos5=157;
const TInt KChMapExtraCharPos6=160;

EXPORT_C CEikCharMap::CEikCharMap()
 	{
	__DECLARE_NAME(_S("CEikCharMap"));
	}

EXPORT_C CEikCharMap::~CEikCharMap()
 	{
	iCoeEnv->ScreenDevice()->ReleaseFont(iFont);
	}

EXPORT_C void CEikCharMap::ConstructL()
	{
	CommonConstructL();
	}

EXPORT_C void CEikCharMap::ConstructFromResourceL(TResourceReader& /*aReader*/)
	{
	CommonConstructL();
	}

void CEikCharMap::CommonConstructL()
	{
	iFont=EikResourceUtils::CreateScreenFontL(R_EIK_CHMAP_CHAR_FONT,iEikonEnv);
	iCharWidth=iFont->WidthZeroInPixels()+KEikExtraCharWidth; 
	iCharHeight=iFont->HeightInPixels()+KEikExtraCharHeight;
	iRows=(KEikLastCharacter-KEikStartCharacter)/KEikChMapColumns + 1 ;
	iCursorRowCol=TPoint(1,1);
	iEikonEnv->ReadResource(iExtraChars,R_EIK_CHARMAP_EXTRA_CHARS);
	}

EXPORT_C void CEikCharMap::Reserved_1()	//reserved by coecontrol
	{
	}

EXPORT_C void CEikCharMap::Reserved_2()
	{
	}

EXPORT_C void CEikCharMap::SetChar(TInt aChar)
// rare ascii characters between 0-127 
// have been included in the empty spaces between 128-255 (the co-page)
	{
	const TInt pos=iExtraChars.Locate(TChar(aChar));
	if (pos!=KErrNotFound)
		{
		switch (pos)
			{
		case 0:
			aChar=KChMapExtraCharPos1;
			break;
		case 1:
			aChar=KChMapExtraCharPos2;
			break;
		case 2:
			aChar=KChMapExtraCharPos3;
			break;
		case 3:
			aChar=KChMapExtraCharPos4;
			break;
		case 4:
			aChar=KChMapExtraCharPos5;
			break;
		case 5:
			aChar=KChMapExtraCharPos6;
			break;
		case 6:
		case 7:
		case 8:
#if defined(_DEBUG)
			User::Invariant();
#endif
			break;
			}
		}
	SetMapPos(aChar);
	}

void CEikCharMap::SetMapPos(TInt aAsciiIndex)
	{
	iCursorRowCol.iX=((aAsciiIndex-KEikStartCharacter)%KEikChMapColumns)+1;
	iCursorRowCol.iY=((aAsciiIndex-KEikStartCharacter)/KEikChMapColumns)+1;
	}

TInt CEikCharMap::DisplayCharacter(TInt aChar) const
// rare ascii characters between 0-127 
// have been included in the empty spaces between 128-255 (the co-page)
{
	if (aChar==KChMapExtraCharPos1 && iExtraChars.Length()>=1)
		return(iExtraChars[0]);
	else if (aChar==KChMapExtraCharPos2 && iExtraChars.Length()>=2)
		return(iExtraChars[1]);
	else if (aChar==KChMapExtraCharPos3 && iExtraChars.Length()>=3)
		return(iExtraChars[2]);
	else if (aChar==KChMapExtraCharPos4 && iExtraChars.Length()>=4)
		return(iExtraChars[3]);
	else if (aChar==KChMapExtraCharPos5 && iExtraChars.Length()>=5)
		return(iExtraChars[4]);
	else if (aChar==KChMapExtraCharPos6 && iExtraChars.Length()>=6)
		return(iExtraChars[5]);
	else
		return(aChar);
	}

EXPORT_C TInt CEikCharMap::GetChar() const
	{
	TInt ret = KEikStartCharacter-1 + KEikChMapColumns*(iCursorRowCol.iY-1) + iCursorRowCol.iX;
	return(DisplayCharacter(ret));
	}

EXPORT_C TSize CEikCharMap::MinimumSize()
	{
	TSize size;
	size.iWidth=iCharWidth*KEikChMapColumns+2*KEikTotalBorder;
	size.iHeight=iCharHeight*iRows+2*KEikTotalBorder;
	return size;
	}

EXPORT_C TKeyResponse CEikCharMap::OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode /*aModifiers*/)
	{
	TInt oldPos=GetChar();
	TInt code=aKeyEvent.iCode;
	switch (code)
		{
	case EKeyLeftArrow:
		MoveCursor(-1,0);
		break;
	case EKeyRightArrow:
		MoveCursor(1,0);
		break;
	case EKeyUpArrow:
		MoveCursor(0,-1);
		break;
	case EKeyDownArrow:
		MoveCursor(0,1);
		break;
	case EKeyHome:
		iCursorRowCol=TPoint(1,iCursorRowCol.iY);
		break;
	case EKeyEnd:
		iCursorRowCol=TPoint(KEikChMapColumns,iCursorRowCol.iY);
		break;
	case EKeyPageDown:
		iCursorRowCol=TPoint(iCursorRowCol.iX, KEikChMapRows);
		break;
	case EKeyPageUp:
		iCursorRowCol=TPoint(iCursorRowCol.iX, 1);
		break;
	default:
		;
		}
	if (code==EKeyEnter)
		CheckStateChangedL(oldPos);
	else if ((oldPos!=GetChar()) &&
		(code==EKeyLeftArrow || code==EKeyRightArrow || 
		code==EKeyUpArrow || code==EKeyDownArrow ||
		code==EKeyHome || code==EKeyEnd || code==EKeyPageDown || code==EKeyPageUp))
		CheckStateChangedL(oldPos);
	return(EKeyWasConsumed);
	}

EXPORT_C void CEikCharMap::HandlePointerEventL(const TPointerEvent& aPointerEvent)
	{
	if (aPointerEvent.iType==TPointerEvent::EButton1Down)
		{
		TPoint pos = aPointerEvent.iPosition;
		TRect rect=Rect();
		rect.Shrink(KEikTotalBorder,KEikTotalBorder);
		// if the user is clicking in an invalid region do nothing!
		if (!(rect.Contains(pos)))
		   return;
		TInt oldPos=GetChar();
		GetRowColFromPointerPos(pos);
		CheckStateChangedL(oldPos);
		}
	}

EXPORT_C void CEikCharMap::Draw(const TRect& /*aRect*/) const
	{
	CWindowGc& gc=SystemGc();
//	gc.SetBrushStyle(CGraphicsContext::ESolidBrush);
//	gc.SetBrushColor(KEikCharMapBackgroundColor);
	gc.UseFont(iFont);

    TPoint pos = iOffset;
	const TSize size(iCharWidth,iCharHeight);
	const TInt ascent=iFont->AscentInPixels();
	TRect rect=Rect();
	TEikBorder border(TEikBorder::ESingleGray);
	border.Draw(gc,rect);
	rect.Shrink(KEikChMapBorder,KEikChMapBorder);
	gc.DrawRect(rect);
	
	TBuf<1> symbol; 
	for (TInt ii=KEikStartCharacter,charCount=1;ii<=KEikLastCharacter;++ii)
		{
		symbol.Format(_L("%c"),DisplayCharacter(ii));
		gc.DrawText(symbol,TRect(pos,size),ascent,CGraphicsContext::ECenter);
		pos.iX += iCharWidth;
		++charCount;
		if (charCount > KEikChMapColumns) 
			{
			charCount=1;
			pos.iY += iCharHeight; 
			pos.iX = iOffset.iX;
			}
		}
	}

EXPORT_C void CEikCharMap::FocusChanged(TDrawNow aDrawNow)
    {
	if (aDrawNow==ENoDrawNow)
		return;
	if (IsFocused())
		DrawCursor();
    else
        iEikonEnv->HideCursor(this);
    }

EXPORT_C void CEikCharMap::SizeChangedL()
    {
	TPoint pos=Position();
	iOffset.iX = pos.iX + KEikTotalBorder;
	iOffset.iY = pos.iY + KEikTotalBorder+KEikExtraCursorHeight;
	if (IsFocused())
		DrawCursor();
	}

void CEikCharMap::GetRowColFromPointerPos(const TPoint &aPos)
	{
	iCursorRowCol.iX=(aPos.iX-iOffset.iX)/iCharWidth+1;
	iCursorRowCol.iY=(aPos.iY-iOffset.iY)/iCharHeight +1;
	}

TPoint CEikCharMap::GetCursorPosFromRowCol() const
	{
	// From the given row column coordinates work out the point from which the cursor should be drawn
	TPoint aPoint;
	aPoint.iY = (iCursorRowCol.iY-1)*iCharHeight + iOffset.iY;
	aPoint.iX = (iCursorRowCol.iX-1)*iCharWidth  + iOffset.iX;
	return(aPoint);
	}

void CEikCharMap::DrawCursor() 
	{
	TPoint point=GetCursorPosFromRowCol();
	TInt cursorWidth=iFont->MaxNormalCharWidthInPixels()+KEikExtraCursorWidth;
	TInt ascent=iFont->AscentInPixels();
	point.iY+=ascent-KEikExtraCursorHeight;// top left hand corner form character
    iEikonEnv->DrawCursor(this,point,cursorWidth, ascent, iCharHeight);
	}

void CEikCharMap::MoveCursor(TInt aX, TInt aY)
	{
	iCursorRowCol.iX += aX;
	iCursorRowCol.iY += aY;
	if (iCursorRowCol.iX > KEikChMapColumns)
		iCursorRowCol.iX=1;
	if (iCursorRowCol.iY > iRows)
		iCursorRowCol.iY=1;
	if (iCursorRowCol.iX < 1)
		iCursorRowCol.iX=KEikChMapColumns;
	if (iCursorRowCol.iY < 1)
		iCursorRowCol.iY=iRows;
	}

void CEikCharMap::CheckStateChangedL(TInt aOldPos)
	{
	TInt newPos=GetChar();
	if (aOldPos==newPos)
		ReportEventL(MCoeControlObserver::EEventStateChanged);// double click on same char will exit dialog
	else if (aOldPos!=newPos)
		{
		if (IsFocused())
		DrawCursor();	  //Draw the new cursor
		ReportEventL(MCoeControlObserver::EEventStateChanged);
		}
	}

//
// class CEikCharMapDialog
//

EXPORT_C CEikCharMapDialog::CEikCharMapDialog(CCoeControl* aControl)
	{
	iControl=aControl;
	}

void CEikCharMapDialog::HandleControlStateChangeL(TInt /*aResourceId*/)
	{
	TInt ch=((CEikCharMap*)Control(EEikCidSpecialChar))->GetChar();
	TBuf<24> buf;
	iCoeEnv->ReadResource(buf,R_EIK_CHARMAP_LABEL);
	buf.AppendNum(ch);
	CEikLabel* label=STATIC_CAST(CEikLabel*,Control(EEikCidSpecialCharLabel));
	label->SetTextL(buf);
	label->DrawNow();
	TBool tryExit=EFalse;
	if (ch==iLastChar)
		tryExit=ETrue;
	iLastChar=ch;
	if (tryExit)
		TryExitL(EEikBidOk);
	}

TBool CEikCharMapDialog::OkToExitL(TInt )
	{
    TInt specCharCode=((CEikCharMap*)Control(EEikCidSpecialChar))->GetChar();
	TKeyEvent keyEvent;
	keyEvent.iModifiers=0;
	keyEvent.iCode=specCharCode;
    iControl->OfferKeyEventL(keyEvent,EEventKey);
	return(ETrue);
	}

void CEikCharMapDialog::PreLayoutDynInitL()
	{
	}

void CEikCharMapDialog::PostLayoutDynInitL()
	{
	TInt ch=((CEikCharMap*)Control(EEikCidSpecialChar))->GetChar();
	iLastChar=ch;
	TBuf<24> buf;
	iCoeEnv->ReadResource(buf,R_EIK_CHARMAP_LABEL);
	buf.AppendNum(ch);
	CEikLabel* label=STATIC_CAST(CEikLabel*,Control(EEikCidSpecialCharLabel));
	label->CopyControlContextFrom(this);
	label->SetTextL(buf);
	}
