// EIKMENUP.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include <eikmenup.h>
#include <eikmenub.h>
#include <eikmobs.h>
#include <eikmenu.hrh>
#include <eikcmds.hrh>
#include <eikpanic.h>
#include <coemain.h>
#include <basched.h>
#include <barsread.h>
#include <eikhkeyt.h>
#include <eikbordr.h>
#include <eikbutb.h>
#include <eikenv.h>
#include <eikon.rsg>
#include <coeutils.h>
#include <eikdutil.h>
#include <eiksfont.h>
#include <eikcolor.h>
#include <eikkeys.h>
#include <eikappui.h>
#include <eiksbfrm.h>
#include <eikscrlb.h>

#include <eikopbut.h>
#include <eikon.mbg>

const TInt KEikNumOfSideButtons=5;
const TInt KEikSidebarPopupXPos=5;
const TInt KMenuSeparatorYOffset=2;
const TInt KMenuItemYOffset=-1;
const TInt KExtraBaselineOffset=1;


TInt CEikMenuPane::TItem::ItemLength() const
	{
	return sizeof(TItem)-(TItem::SData::ENominalTextLength-iData.iExtraText.Length())*sizeof(TText);
	}

class CEikMenuPane::CItemArray : public CArrayVarFlat<CEikMenuPane::TItem>
	{
public:
	CItemArray();
	void AddItemL(TItem& aMenuItem);
	};

CEikMenuPane::CItemArray::CItemArray()
	: CArrayVarFlat<TItem>(10)
	{
	__DECLARE_NAME(_S("CEikMenuPane::CItemArray"));
	}

void CEikMenuPane::CItemArray::AddItemL(TItem& aMenuItem)
	{
	AppendL(aMenuItem,aMenuItem.ItemLength());
	}

class CEikMenuPane::CMenuScroller : public CBase, public MEikScrollBarObserver
	{
public:
	static CMenuScroller* NewL(CEikMenuPane& aMenu);
	~CMenuScroller();

	inline TInt TopItemIndex() const;
	inline void SetTopItemIndex(TInt aIndex);
	inline CIdle* Idle() const;
public: // from MEikScrollBarObserver
	void HandleScrollEventL(CEikScrollBar* aScrollBar, TEikScrollEvent aEventType);
private:
	CMenuScroller(CEikMenuPane& aMenu);
	void ConstructL();
private:
	CEikMenuPane& iMenuPane;
	TInt iTopItemIndex;
	CIdle* iIdle;
	};

CEikMenuPane::CMenuScroller* CEikMenuPane::CMenuScroller::NewL(CEikMenuPane& aMenuPane)
	{ // static
	CMenuScroller* self=new(ELeave)CMenuScroller(aMenuPane);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop();
	return self;
	}

CEikMenuPane::CMenuScroller::~CMenuScroller()
	{
	delete iIdle;
	}

CEikMenuPane::CMenuScroller::CMenuScroller(CEikMenuPane& aMenuPane)
	: iMenuPane(aMenuPane)
	{}

void CEikMenuPane::CMenuScroller::ConstructL()
	{
	iIdle=CIdle::NewL(CActive::EPriorityIdle);
	}

void CEikMenuPane::CMenuScroller::HandleScrollEventL(CEikScrollBar* aScrollBar,TEikScrollEvent aEventType)
	{
	iMenuPane.HandleScrollEventL(aScrollBar,aEventType);
	}

inline TInt CEikMenuPane::CMenuScroller::TopItemIndex() const
	{ return iTopItemIndex; }
inline void CEikMenuPane::CMenuScroller::SetTopItemIndex(TInt aIndex)
	{ iTopItemIndex=aIndex; }
inline CIdle* CEikMenuPane::CMenuScroller::Idle() const
	{ return iIdle; }

//
// class CEikMenuPane
//

#define xMENU_TEXTURED_BACKGROUND

const TInt KEikMenuPaneMaxItems=8;
const TInt KEikMenuFeedbackBlinks=4;
const TInt KFeedBackBlickInterval=400000;			//(was 20000 for blinking system)

const TInt KLeftAdornmentWidth=14;

// the following two "margins" are suptracted from KLeftAdormentWidth
const TInt KPreLeftAdornment=3;
const TInt KPostLeftAdornment=3;


const TInt KRightAdornmentWidth=16;
// the following two "margins" are suptracted from KRighttAdormentWidth
const TInt KPreRightAdornment=3;
const TInt KPostRightAdornment=3;

const TInt KEikMenuPaneTextTopSpace=3;
const TInt KEikMenuPaneTextBottomSpace=3;
const TInt KEikMenuPaneSeparatorAfterSpace=6;
const TInt KEikMenuPaneSeparatorMargin=4;
const TInt KEikMenuCascadeOverlap=8;

const TInt KLeftHighlightGap=2;//4 
const TInt KRightHighlightGap=2;//4
const TInt KTopHighlightGap=2;
const TInt KBottomHighlightGap=2;

const TInt KCascadeAutoSelectionSensitivity=2;
	

const TInt KEikMenuItemRadio=(EEikMenuItemRadioStart|EEikMenuItemRadioMiddle|EEikMenuItemRadioEnd);

inline TBool CEikMenuPane::ItemArrayOwnedExternally()
	{
	return iArrayOwnedExternally;
	}

EXPORT_C CEikMenuPane::~CEikMenuPane()
	{
	CloseCascadeMenu();
	if (!ItemArrayOwnedExternally())
		delete iItemArray;
	if (iLaunchingButton)
		{
		TPointerEvent event;
		event.iType=TPointerEvent::EButton1Up;
		event.iModifiers=0;
		event.iPosition=iLaunchingButton->Position();
		iLaunchingButton->HandlePointerEventL(event); // won't leave
		}
	delete iScroller;
	delete iSBFrame;
	}

EXPORT_C CEikMenuPane::CEikMenuPane(MEikMenuObserver* aMenuObserver)
	: iMenuObserver(aMenuObserver)
	{
	__ASSERT_DEBUG(iMenuObserver,Panic(EEikPanicNullPointer));
	__DECLARE_NAME(_S("CEikMenuPane"));
	iBorder=TEikBorder(TEikBorder::EThickDeepRaisedWithOutline);
	iAllowPointerUpEvents=EFalse;
	iNumberOfDragEvents=0;
	}

EXPORT_C void CEikMenuPane::ConstructL(CEikMenuPane* aOwner)
	{
	iOwner=aOwner;
	CreateWindowL();
	DrawableWindow()->EnableBackup();
    SetAllowStrayPointers();
	iEikonEnv->AddWindowShadow(this);
	EnableDragEvents();
	iItemHeight=iEikonEnv->NormalFont()->HeightInPixels()+KEikMenuPaneTextTopSpace+KEikMenuPaneTextBottomSpace;
	if (iItemHeight%2)
		iItemHeight+=1;
	iBaseLine=iEikonEnv->NormalFont()->AscentInPixels()+KEikMenuPaneTextTopSpace+KExtraBaselineOffset;
	CheckCreateScrollerL();
	}

EXPORT_C void CEikMenuPane::ConstructFromResourceL(TResourceReader& aReader)
	{
	// Window may not be created if OOM earlier during construction
	RWindow* window=&Window();
	if(!window)
		User::Leave(KErrNoMemory);
	//

	Window().SetOrdinalPosition(0);
	if (!ItemArrayOwnedExternally())
		{
		delete iItemArray;
		iItemArray=NULL;
		}
	iItemArray=new(ELeave) CItemArray;
	const TInt count=aReader.ReadInt16();
    for (TInt ii=0;ii<count;++ii)
		{
		TItem item;
		item.iData.iCommandId=aReader.ReadInt32();
		item.iData.iCascadeId=aReader.ReadInt32();
		item.iData.iFlags=aReader.ReadInt32();
		TPtrC txtptr=aReader.ReadTPtrC();
		item.iData.iText=txtptr;
		TPtrC extratxtptr=aReader.ReadTPtrC();
		item.iData.iExtraText=extratxtptr;
		iItemArray->AddItemL(item);
		}
	
	TInt panepos=0;
	TMargins margins=iBorder.Margins();  // assume same margins pane and bar
	CEikMenuBar* menubar=iEikonEnv->EikAppUi()->MenuBar();
	if (menubar && (menubar->IsVisible()))
		panepos = menubar->Position().iY +menubar->Size().iHeight -margins.iTop;
	}

EXPORT_C void CEikMenuPane::Reset()
	{
	if (iItemArray)
		iItemArray->Reset();
	delete iSBFrame;
	iSBFrame=NULL;
	}

EXPORT_C void CEikMenuPane::CloseCascadeMenu()
	{
	if (iCascadeMenuPane==NULL)
		return;
	delete iCascadeMenuPane;
	iCascadeMenuPane=NULL;
	SetFocus(ETrue,EDrawNow);
	}

void CEikMenuPane::TryLaunchCascadeMenuL(const TItem& aItem)
	{
	if (aItem.iData.iFlags&EEikMenuItemDimmed)
		{
		iMenuObserver->HandleAttemptDimmedSelectionL(aItem.iData.iCommandId);
		return;
		}
	LaunchCascadeMenuL(aItem.iData.iCascadeId);
	}

void CEikMenuPane::LaunchCascadeMenuL(TInt aCascadeMenuId)
	{
	iCascadeMenuPane=new(ELeave) CEikMenuPane(iMenuObserver);
	TRAPD(err,DoLaunchCascadeMenuL(aCascadeMenuId));
	if (err)
		{
		CloseCascadeMenu();
		User::Leave(err);
		}
	}

void CEikMenuPane::DoLaunchCascadeMenuL(TInt aCascadeMenuId)
	{
	if (!iItemArray || iItemArray->Count()==0)
		return;
	iCascadeMenuPane->ConstructL(this);
	iMenuObserver->RestoreMenuL(iCascadeMenuPane,aCascadeMenuId,MEikMenuObserver::EMenuPane);
	TItem& item=(*iItemArray)[iSelectedItem];
	TPoint targetPos=Position();
	targetPos.iX+=iSize.iWidth; //Width of menu pane
	TMargins margins=iBorder.Margins();
	targetPos.iX-=margins.iRight;
	targetPos.iY+=item.iPos;
	iCascadeMenuPane->SetSelectedItem(ENothingSelected);
	iCascadeMenuPane->StartDisplayingMenuPane(iHotKeyTable,targetPos,NULL,0);
	}

TSize CEikMenuPane::CalculateSize()
	{
	iFlags|=EInvalidCurrentSize;
	TBuf<32> formatHK;
	TBuf<32> formatSHK;
	iEikonEnv->ReadResource(formatHK,R_EIK_TBUF_MENUP_MOD_CTRL);
	iEikonEnv->ReadResource(formatSHK,R_EIK_TBUF_MENUP_MOD_SHIFT_CTRL);
	
	const CFont* normalFont=iEikonEnv->NormalFont();
	const CFont* annotationFont=iEikonEnv->AnnotationFont();
	TInt count=0;
	if(iItemArray)
		count=iItemArray->Count();
	TMargins margins=iBorder.Margins();
	TSize size=TSize(0,margins.iTop+KTopHighlightGap);
	iHotkeyColWidth=0;
	for (TInt ii=0;ii<count;++ii)
		{
		TItem& item=(*iItemArray)[ii];
		const TInt textWidth=normalFont->TextWidthInPixels(item.iData.iText);
		if (textWidth>size.iWidth)
			size.iWidth=textWidth;
		// Check for hot key
		TInt hotKeyCode=0;
		TInt hotKeyModifiers=0;
		TInt hotKeyTextWidth=0;
		item.iHotKeyCode=0;
		TBool isHotKey = EFalse;
		if (iHotKeyTable && item.iData.iCommandId)
			isHotKey = iHotKeyTable->HotKeyFromCommandId(item.iData.iCommandId,hotKeyCode,hotKeyModifiers);
		if (isHotKey)
			{
			__ASSERT_DEBUG(item.iData.iExtraText.Length()==0,Panic(EEikPanicInvalidMenuPane)); // hotkey and extra txt not allowed. 
			THotKeyDisplayText text;
			item.iHotKeyCode=hotKeyCode;
			switch (hotKeyModifiers)
				{			
			case EModifierCtrl:
				item.iHotKeyCode+=('A'-1);
				item.iData.iFlags|=EEikMenuItemFlagCtrlHK;
				break;
			case (EModifierCtrl|EModifierShift):
				item.iHotKeyCode+=('A'-1);
				item.iData.iFlags|=EEikMenuItemFlagShiftCtrlHK;
				break;
			default:
				break;
				}
			FindHotKeyDisplayText(text, item);
			hotKeyTextWidth=annotationFont->TextWidthInPixels(text);
			}
		else if (item.iData.iExtraText.Length()!=0)
			{
			hotKeyTextWidth=annotationFont->TextWidthInPixels(item.iData.iExtraText);		
			}
		if (hotKeyTextWidth>iHotkeyColWidth)
			iHotkeyColWidth=hotKeyTextWidth;
		item.iPos=size.iHeight;
		size.iHeight+=iItemHeight;
		if (item.iData.iFlags&EEikMenuItemSeparatorAfter)
			size.iHeight+=KEikMenuPaneSeparatorAfterSpace;
		}
	
	//Add the width of the hot key/extra txt column if there is one
	if (iHotkeyColWidth)
		size.iWidth+=iHotkeyColWidth;
	size.iWidth+=KPreLeftAdornment+KLeftAdornmentWidth+KPostLeftAdornment;
	size.iWidth+=KPreRightAdornment+KRightAdornmentWidth+KPostRightAdornment;

	//Add remaining space for borders
	size.iHeight+=margins.iBottom+KBottomHighlightGap;
	size.iWidth+=margins.iLeft+margins.iRight;
	if (iItemArray && iItemArray->Count()>NumberOfItemsThatFitInView())
		size.iWidth+=CEikScrollBar::EScrollbarWidth;
	const TInt availableHeight=iCoeEnv->ScreenDevice()->SizeInPixels().iHeight-iPosition.iY;
	size.iHeight=Min(size.iHeight,availableHeight);
	iFlags&=~EInvalidCurrentSize;
	return size;
	}


EXPORT_C void CEikMenuPane::StartDisplayingMenuPane(const CEikHotKeyTable* aHotKeyTable,const TPoint& aTargetPos, const CEikMenuPaneTitle* aMenuPaneTitle,TInt aMinTitleWidth, TPopupTargetPosType aTargetType)
	{
	iHotKeyTable=aHotKeyTable;
	iMenuPaneTitle=aMenuPaneTitle;
	TSize size=CalculateSize();
	if (size.iWidth<aMinTitleWidth)
		size.iWidth=aMinTitleWidth;
	
	const TSize screenSize=iCoeEnv->ScreenDevice()->SizeInPixels();
	// aTargetPos specifies top left, top right, botton left or bottom right - indicated by value of aTargetType
	// default is top left
	TPoint newPos=aTargetPos;  
	if (aTargetType == EPopupTargetTopRight)
		newPos.iX-=size.iWidth;
	else if (aTargetType == EPopupTargetBottomLeft)
		newPos.iY-=size.iHeight;	
	if (aTargetType == EPopupTargetBottomRight)
		{
		newPos.iX-=size.iWidth;
		newPos.iY-=size.iHeight;
		}

	if (newPos.iX+size.iWidth>screenSize.iWidth)
		{//ensure does not go off right hand of screen
		if (iOwner)
			{// is it a cascade? put it on the left instead.
			TMargins margins=iBorder.Margins();
			const TInt borderWidth=margins.iLeft+margins.iRight;
			newPos.iX-=size.iWidth+iOwner->iSize.iWidth;
			newPos.iX+=borderWidth+KPostRightAdornment+KEikMenuCascadeOverlap;
			if (newPos.iX<=0)// if it does not fit on the left either, then position on right again
				newPos.iX=screenSize.iWidth-borderWidth-size.iWidth;
			}
		else
			newPos.iX=screenSize.iWidth-size.iWidth/*+KEikMenuCascadeOverlap*/;
		}
	else
		{
		if (iOwner)  // cascade
		  newPos.iX-=KEikMenuCascadeOverlap;
		}

	//Check that does not go off left/top of screen
	newPos.iX=Max(0,newPos.iX);
	newPos.iY=Max(0,newPos.iY);
	if ((iOwner || !iMenuPaneTitle) && (newPos.iY+size.iHeight>screenSize.iHeight))
		newPos.iY=screenSize.iHeight-size.iHeight;//only for cascades and popupMenus
	// fix for SW1-795 (Bluebell)
	if (newPos.iY+size.iHeight>screenSize.iHeight)
		size.iHeight=screenSize.iHeight-newPos.iY;
	//
	SetExtentL(newPos,size); // won't fail
	CreateScrollBarFrame();
	MakeVisible(ETrue);
	ActivateL(); // won't fail
	if (iMenuPaneTitle)
		{
		CONST_CAST(CEikMenuPaneTitle*,iMenuPaneTitle)->MakeVisible(ETrue);
		CONST_CAST(CEikMenuPaneTitle*,iMenuPaneTitle)->ActivateL();
		iMenuPaneTitle->DrawNow();
		}
	DrawNow();
	}

void CEikMenuPane::FindHotKeyDisplayText(TDes& aDes,const TItem& aItem) const
	{
	TBool isDelSpaceOrTab=EFalse;
	TBuf<64> format; 
    TInt resId=0;
	if (aItem.iData.iFlags&EEikMenuItemFlagCtrlHK)
        resId=R_EIK_TBUF_MENUP_MOD_CTRL;
	else if (aItem.iData.iFlags&EEikMenuItemFlagShiftCtrlHK)
        resId=R_EIK_TBUF_MENUP_MOD_SHIFT_CTRL;
	else if (aItem.iHotKeyCode==32) // space
		{
		resId=R_EIK_TBUF_MENUP_SPACE;
		isDelSpaceOrTab=ETrue;
		}
	else if (aItem.iHotKeyCode==127) //delete
		{
		resId=R_EIK_TBUF_MENUP_DEL;
		isDelSpaceOrTab=ETrue;
		}
	else if (aItem.iHotKeyCode==9)	//tab
		{
		resId=R_EIK_TBUF_MENUP_TAB;
		isDelSpaceOrTab=ETrue;
		}
	if (resId)
		{
		iEikonEnv->ReadResource(format,resId);
		aDes.Format(format,aItem.iHotKeyCode);
		}
	else if (isDelSpaceOrTab) 
		aDes.Format(format);
	else
		{
		format.Copy(_L("%c"));
		aDes.Format(format,aItem.iHotKeyCode);
		}
	}

EXPORT_C void CEikMenuPane::MoveHighlightTo(TInt aNewSelectedItem)
	{
	if (aNewSelectedItem==iSelectedItem)
		return;
	ActivateGc();
	CWindowGc& gc=SystemGc();
	PrepareGcForDrawingItems(gc);
	if (iSelectedItem!=ENothingSelected) // only draw if item is selected.
		DrawItem(gc,iSelectedItem,ERemoveHighlight);
	iSelectedItem=aNewSelectedItem;
	if (iSelectedItem>=0)
		ScrollToMakeItemVisible(iSelectedItem);
	if (aNewSelectedItem!=ENothingSelected) 
		DrawItem(gc,aNewSelectedItem,EDrawHighlight);
	DeactivateGc();
	}

void CEikMenuPane::PrepareGcForDrawingItems(CGraphicsContext& aGc) const
	{
	aGc.UseFont(iEikonEnv->NormalFont());
	aGc.SetPenColor(iEikonEnv->ControlColor(EEikColorMenuPaneText,*this));
#if defined(MENU_TEXTURED_BACKGROUND)
	iEikonEnv->SetTexturedBrush(aGc);
#else
	aGc.SetBrushStyle(CGraphicsContext::ESolidBrush);
	aGc.SetBrushColor(iEikonEnv->ControlColor(EEikColorMenuPaneBackground,*this));
#endif
	}


void CEikMenuPane::DrawItem(CGraphicsContext& aGc,TInt aItem,THighlightType aHighlight) const
// Pane layout is:
// calculated from left
// border,leftadornment,text,
// calculated from right
// hotkeytext,rightadornment,border
// *Important* Assumes brush is Eikon TexturedBrush (or SolidBrush if not using texture)
//  and pencolor is KEikMenuPaneTextColor
	{
	if (!iItemArray || iItemArray->Count()==0)
		return;
	aGc.SetClippingRect(ViewRect());
	const TItem& item=(*iItemArray)[aItem];
	const TRect innerRect=iBorder.InnerRect(Rect());
	TRect rect(innerRect.iTl.iX+KLeftHighlightGap,item.iPos+KMenuItemYOffset,
					innerRect.iBr.iX-KRightHighlightGap,item.iPos+iItemHeight);
	
	if (iItemArray->Count()>NumberOfItemsThatFitInView())
		rect.iBr.iX-=CEikScrollBar::EScrollbarWidth;
	TRgb textColor=iEikonEnv->ControlColor(EEikColorMenuPaneText,*this); //KEikMenuPaneTextColor;
	TBool brushChanged = EFalse;
	switch (aHighlight)
		{
	case EDrawHighlight:
		brushChanged = ETrue;
		aGc.SetBrushStyle(CGraphicsContext::ESolidBrush);
		textColor=iEikonEnv->ControlColor(EEikColorMenuPaneTextHighlight,*this); //KEikMenuPaneInvertedTextColor;
		if (IsFocused())
			{
			aGc.SetBrushColor(iEikonEnv->ControlColor(EEikColorMenuPaneHighlight,*this)); //KEikMenuPaneHighlightColor);
			if (item.iData.iFlags&EEikMenuItemDimmed)
				textColor=iEikonEnv->ControlColor(EEikColorMenuPaneDimmedTextHighlight,*this); //KEikMenuPaneHighlightedDimmedTextColor;
			}
		else
			{
			aGc.SetBrushColor(iEikonEnv->ControlColor(EEikColorMenuPaneDimmedHighlight,*this)); //KEikMenuPaneDimmedHighlightColor);
			}
		break;
	case ERemoveHighlight:
#if defined(MENU_TEXTURED_BACKGROUND)
		aGc.SetBrushStyle(CGraphicsContext::EPatternedBrush);
#else
		aGc.SetBrushStyle(CGraphicsContext::ESolidBrush);
#endif
		if (item.iData.iFlags&EEikMenuItemDimmed)
			textColor=iEikonEnv->ControlColor(EEikColorMenuPaneDimmedText,*this); //KEikMenuPaneDimmedTextColor;
		break;
	case ENoHighlight:
		aGc.SetBrushStyle(CGraphicsContext::ENullBrush);
#if defined(MENU_TEXTURED_BACKGROUND)
		brushChanged = ETrue;
#endif
		if (item.iData.iFlags&EEikMenuItemDimmed)
			textColor=iEikonEnv->ControlColor(EEikColorMenuPaneDimmedText,*this); //KEikMenuPaneDimmedTextColor;
		break;
	default:
		break;
		}

	if (textColor!=iEikonEnv->ControlColor(EEikColorMenuPaneText,*this)) //KEikMenuPaneTextColor)
		aGc.SetPenColor(textColor);

	// draw menu text
	const TInt leftMargin=KLeftAdornmentWidth;
	if (aHighlight==ENoHighlight)
		{
		aGc.SetPenStyle(CGraphicsContext::ESolidPen);
		//aGc.DrawText(item.iData.iText,TPoint(rect.iTl.iX+leftMargin,rect.iTl.iY+iBaseLine));		
		aGc.DrawText(item.iData.iText,rect,iBaseLine,CGraphicsContext::ELeft,leftMargin);
		}
	else
		{
		aGc.DrawText(item.iData.iText,rect,iBaseLine,CGraphicsContext::ELeft,leftMargin);
		}
	
	// draw hotkey/extra text
	THotKeyDisplayText text;
	if (item.iHotKeyCode)
		FindHotKeyDisplayText(text, item);
	else if (item.iData.iExtraText.Length() !=0)
			text=item.iData.iExtraText;
	if ((item.iHotKeyCode)||(item.iData.iExtraText.Length() !=0))
		{
		aGc.UseFont(iEikonEnv->AnnotationFont());
		aGc.DrawText(text,TPoint(rect.iBr.iX-iHotkeyColWidth-KRightAdornmentWidth,
			rect.iTl.iY+iBaseLine));
		aGc.UseFont(iEikonEnv->NormalFont());
		}


	// draw left adornment
	if (item.iData.iFlags&EEikMenuItemSymbolOn)
		{
		if (item.iData.iFlags&EEikMenuItemCheckBox)
			{
			TBuf<1> buf;
			buf.Append(TChar(ESymFontTick));
			aGc.UseFont(iEikonEnv->SymbolFont());
			aGc.SetPenStyle(CGraphicsContext::ESolidPen);
			// as the tick is big, ignore KPreLeftAdornment and steal 1 pixels from left.
			aGc.DrawText(buf,TPoint(rect.iTl.iX-1,rect.iTl.iY+iBaseLine));
			aGc.UseFont(iEikonEnv->NormalFont());	
			}
		else if (item.iData.iFlags&KEikMenuItemRadio)
			{
			CWindowGc& wgc = SystemGc();
			const CFbsBitmap* bitmap = iEikonEnv->OptionBitmap();
			TSize bitsize = bitmap->SizeInPixels();
			TRect butRect(TPoint(0,0), TPoint(bitsize.iWidth,bitsize.iHeight));
			TInt yoffset = (rect.Size().iHeight-bitsize.iHeight)/2;
			TInt xoffset = KPreLeftAdornment;
			TPoint offset(xoffset,yoffset);
			if (aHighlight==EDrawHighlight)
				bitmap = iEikonEnv->HighlightedOptionBitmap();
			wgc.BitBltMasked((rect.iTl+offset),bitmap,butRect,iEikonEnv->OptionMaskBitmap(),ETrue);
			}
		brushChanged=ETrue;
		}
	// draw right adornment
	if (item.iData.iCascadeId)
		{//Set colour for cascade symbol depending on whether it is dimmed or focused
		const TInt cascadeSize=Min(KRightAdornmentWidth-KPreRightAdornment-KPostRightAdornment,iItemHeight);
		TRgb color=iEikonEnv->ControlColor(EEikColorMenuPaneText,*this); // KEikMenuPaneSymbolColor;
		if (item.iData.iFlags&EEikMenuItemDimmed)
			color=iEikonEnv->ControlColor(EEikColorMenuPaneDimmedText,*this); // KEikMenuPaneDimmedSymbolColor;
		else if (aHighlight==EDrawHighlight)// || !Focused())
			color=iEikonEnv->ControlColor(EEikColorMenuPaneTextHighlight,*this); // KEikMenuPaneInvertedSymbolColor;
		//Sort out rect for cascade symbol
		TRect cascadeRect=TRect(TPoint(rect.iBr.iX-cascadeSize-KPostRightAdornment,item.iPos),
					TSize(cascadeSize,iItemHeight));
		cascadeRect.iTl.iY+=((iItemHeight-cascadeSize)/2);
		cascadeRect.iBr.iY-=((iItemHeight-cascadeSize)/2);
		DrawCascadeSymbol(aGc,cascadeRect,color);
		brushChanged=ETrue;
		textColor=color;// so that it is set back correctly
		}
	if (item.iData.iFlags&EEikMenuItemSeparatorAfter)
		{
		TInt yPos=item.iPos+iItemHeight+KMenuSeparatorYOffset;
		textColor=iEikonEnv->ControlColor(EEikColorMenuPaneText,*this); // KEikMenuPaneTextColor;
		aGc.SetPenColor(textColor);
		aGc.DrawLine(TPoint(innerRect.iTl.iX+KEikMenuPaneSeparatorMargin,yPos),TPoint(innerRect.iTl.iX+innerRect.Width()-KEikMenuPaneSeparatorMargin,yPos));
	//	aGc.SetPenColor(KEikMenuPaneTextColor);
		}
	// put back the assumed brush and text color
	if (brushChanged)
		{
#if defined(MENU_TEXTURED_BACKGROUND)
		aGc.SetBrushStyle(CGraphicsContext::EPatternedBrush);
#else
		aGc.SetBrushStyle(CGraphicsContext::ESolidBrush);
		aGc.SetBrushColor(iEikonEnv->ControlColor(EEikColorMenuPaneBackground,*this)); //KEikMenuBackgroundColor);
#endif
		}
	if (textColor!=iEikonEnv->ControlColor(EEikColorMenuPaneText,*this)); //KEikMenuPaneTextColor)
		aGc.SetPenColor(iEikonEnv->ControlColor(EEikColorMenuPaneText,*this)); //KEikMenuPaneTextColor);
	aGc.CancelClippingRect();
	}

EXPORT_C void CEikMenuPane::Draw(const TRect& /*aRect*/) const
	{
	CWindowGc& gc=SystemGc();
	PrepareGcForDrawingItems(gc);
	const TRect outer=Rect();
	iBorder.Draw(gc,outer);
	const TRect innerRect=iBorder.InnerRect(outer);
    gc.SetPenStyle(CGraphicsContext::ENullPen);
    gc.DrawRect(innerRect);
	if (iMenuPaneTitle)
		{// pane has title, so make sure they are joined up correctly
		const TPoint titlePos = iMenuPaneTitle->Position();
		const TInt width=iMenuPaneTitle->Size().iWidth;
		// left straight join
		gc.SetPenStyle(CGraphicsContext::ESolidPen);
		gc.SetPenColor(KRgbWhite);
		// get rid of the gray bit of border
		gc.DrawLine(TPoint(titlePos.iX-iPosition.iX+5,4),TPoint(titlePos.iX-iPosition.iX+width-5,4));
		// get rid of the black bit of border
		gc.DrawLine(TPoint(titlePos.iX-iPosition.iX+2,0),TPoint(titlePos.iX-iPosition.iX+width-4,0));
		gc.DrawLine(TPoint(titlePos.iX-iPosition.iX+2,1),TPoint(titlePos.iX-iPosition.iX+width-4,1));

		gc.SetPenColor(iEikonEnv->ControlColor(EEikColorMenuPaneBackground, *this));
		gc.DrawLine(TPoint(titlePos.iX-iPosition.iX+5,4),TPoint(titlePos.iX-iPosition.iX+width-5,4));
		gc.DrawLine(TPoint(titlePos.iX-iPosition.iX+KEikMenuPaneSeparatorMargin ,0),TPoint(titlePos.iX-iPosition.iX+width-KEikMenuPaneSeparatorMargin,0));
		gc.DrawLine(TPoint(titlePos.iX-iPosition.iX+KEikMenuPaneSeparatorMargin ,1),TPoint(titlePos.iX-iPosition.iX+width-KEikMenuPaneSeparatorMargin,1));
		gc.DrawLine(TPoint(titlePos.iX-iPosition.iX+KEikMenuPaneSeparatorMargin ,2),TPoint(titlePos.iX-iPosition.iX+width-KEikMenuPaneSeparatorMargin,2));
		gc.DrawLine(TPoint(titlePos.iX-iPosition.iX+KEikMenuPaneSeparatorMargin ,3),TPoint(titlePos.iX-iPosition.iX+width-KEikMenuPaneSeparatorMargin,3));

		// draw in black line seperator
		gc.SetPenColor(KRgbBlack);
		gc.DrawLine(TPoint(titlePos.iX-iPosition.iX+3 +KEikMenuPaneSeparatorMargin ,0),TPoint(titlePos.iX-iPosition.iX+width-3-KEikMenuPaneSeparatorMargin,0));

		gc.SetPenColor(KRgbGray);
		gc.DrawLine(TPoint(titlePos.iX-iPosition.iX+4,0),TPoint(titlePos.iX-iPosition.iX+4,4));

		if (iMenuPaneTitle->Rect().iBr.iX==Rect().iBr.iX)
			{// right straight join
			gc.SetPenColor(KRgbDarkGray);
			gc.DrawLine(TPoint(innerRect.iBr.iX,0),TPoint(innerRect.iBr.iX,5));
			gc.DrawLine(TPoint(innerRect.iBr.iX+1,0),TPoint(innerRect.iBr.iX+1,4));
			gc.DrawLine(TPoint(innerRect.iBr.iX+2,0),TPoint(innerRect.iBr.iX+2,3));
			}
		else
			{// right corner join
			TInt xPos=titlePos.iX-iPosition.iX+width-5;
			gc.SetPenColor(KRgbDarkGray);
			gc.DrawLine(TPoint(xPos,0),TPoint(xPos,5));
			gc.DrawLine(TPoint(xPos+1,0),TPoint(xPos+1,4));
			gc.DrawLine(TPoint(xPos+2,0),TPoint(xPos+2,3));
			}
		}

    gc.SetPenStyle(CGraphicsContext::ESolidPen);
//	gc.SetPenColor(KEikMenuPaneTextColor);
	gc.SetPenColor(iEikonEnv->ControlColor(EEikColorMenuPaneText,*this));
	TInt count=0;
	if(iItemArray)
		count=iItemArray->Count();
	for (TInt ii=0;ii<count;++ii)
		DrawItem(gc,ii,ii==iSelectedItem?EDrawHighlight:ENoHighlight);
	}

void CEikMenuPane::DrawCascadeSymbol(CGraphicsContext& aGc,const TRect& aRect,TRgb aColor) const
	{
	TBuf<1> cascade;
	cascade.Append(TChar(ESymFontMenuCascade));
	aGc.UseFont(iEikonEnv->SymbolFont());
	aGc.SetPenColor(aColor);
	aGc.SetPenStyle(CGraphicsContext::ESolidPen);
	aGc.DrawText(cascade,aRect,aRect.Height(),CGraphicsContext::ERight);
	aGc.UseFont(iEikonEnv->NormalFont());
	}

void CEikMenuPane::ReportSelectionMadeL()
	{
	if (!iItemArray || iItemArray->Count()==0)
		return;
	if (iCascadeMenuPane)
		{
		SetFocus(EFalse,EDrawNow);
		iCascadeMenuPane->SetSelectedItem(0);  // added DMD 
		iCascadeMenuPane->SetFocus(ETrue,EDrawNow);
		return;
		}
	// means the cascade created on the down event is getting the up event
	if (iSelectedItem==ENothingSelected) 
		return;
	TItem& item=(*iItemArray)[iSelectedItem];
	if (item.iData.iFlags&EEikMenuItemDimmed)
		{
		iMenuObserver->HandleAttemptDimmedSelectionL(item.iData.iCommandId);
		}
	else if (!item.iData.iCascadeId)
		{
		GiveVisualFeedback();
		iMenuObserver->ProcessCommandL(item.iData.iCommandId);
		}
	}

void CEikMenuPane::GiveVisualFeedback()
	{
	if (!iItemArray || iItemArray->Count()==0)
		return;
	ActivateGc();
	CWindowGc& gc=SystemGc();
	PrepareGcForDrawingItems(gc);
	TItem& item=(*iItemArray)[iSelectedItem];
	if (item.iData.iFlags&EEikMenuItemCheckBox)
		{
		if (item.iData.iFlags&EEikMenuItemSymbolOn)
			item.iData.iFlags&=(~(EEikMenuItemSymbolOn|EEikMenuItemSymbolIndeterminate));
		else
			{
			item.iData.iFlags&=(~(EEikMenuItemSymbolOn|EEikMenuItemSymbolIndeterminate));
			item.iData.iFlags|=(EEikMenuItemSymbolOn);
			}
		}
	if (item.iData.iFlags&(EEikMenuItemRadioStart|EEikMenuItemRadioMiddle))
		{//Set flag selected radio item
		item.iData.iFlags&=(~(EEikMenuItemSymbolOn|EEikMenuItemSymbolIndeterminate));
		item.iData.iFlags|=(EEikMenuItemSymbolOn);
		//Search below for radio item flags to reset
		const TInt max=iItemArray->Count();
		for (TInt ii=iSelectedItem+1;ii<max;++ii) //ie start with item below the selected item
			{
			TItem& currentItem=(*iItemArray)[ii];
			if ((currentItem.iData.iFlags&(EEikMenuItemRadioMiddle|EEikMenuItemRadioEnd))\
				&&(currentItem.iData.iFlags&EEikMenuItemSymbolOn))
				{
				currentItem.iData.iFlags&=(~(EEikMenuItemSymbolOn|EEikMenuItemSymbolIndeterminate));
				DrawItem(gc,ii,ERemoveHighlight);
				break;
				}
			if (!(currentItem.iData.iFlags&EEikMenuItemRadioMiddle))
				break;	//Reached RadioEnd, ie no activated item in radio group,or found unexpected item type	
			}
		}
	if (item.iData.iFlags&(EEikMenuItemRadioEnd|EEikMenuItemRadioMiddle))
	//Search above for radio item flags to reset
		{//Set flag selected radio item
		item.iData.iFlags&=(~(EEikMenuItemSymbolOn|EEikMenuItemSymbolIndeterminate));
		item.iData.iFlags|=(EEikMenuItemSymbolOn);
		//Search above for radio item flags to reset and redraw
		TInt ii=iSelectedItem-1;	//start with item above the selected item
		while(ii>=0)
			{
			TItem& currentItem=(*iItemArray)[ii];
			if ((currentItem.iData.iFlags&(EEikMenuItemRadioStart|EEikMenuItemRadioMiddle))
				&&(currentItem.iData.iFlags&EEikMenuItemSymbolOn))
				{
				currentItem.iData.iFlags&=(~(EEikMenuItemSymbolOn|EEikMenuItemSymbolIndeterminate));
				DrawItem(gc,ii,ERemoveHighlight);
				break;
				}
			if (!(currentItem.iData.iFlags&EEikMenuItemRadioMiddle))
				break; //Reached RadioStart, ie no activated item in radio group,or found unexpected item type
			--ii;
			}
		}
	// outline when selected
	DrawItem(gc,iSelectedItem,ERemoveHighlight);
	const TRect innerRect=iBorder.InnerRect(Rect());
	const TRect rect(innerRect.iTl.iX+KLeftHighlightGap,item.iPos,
	innerRect.iBr.iX-KRightHighlightGap,item.iPos+iItemHeight);
	gc.SetPenColor(KRgbBlack);
	gc.SetBrushStyle(CGraphicsContext::ENullBrush);
	gc.DrawRect(rect);
	iCoeEnv->Flush(KFeedBackBlickInterval);
	DeactivateGc();
	}


EXPORT_C void CEikMenuPane::FocusChanged(TDrawNow aDrawNow)
	{
	if (!iItemArray || iItemArray->Count()==0)
		return;
	if (aDrawNow)
		{
		// if focused or if current item is a cascade draw highlight (only draw if item is selected)
		if (iSelectedItem !=ENothingSelected)		
			{
			ActivateGc();
			CWindowGc& gc=SystemGc();
			PrepareGcForDrawingItems(gc);	
			const TItem& item=(*iItemArray)[iSelectedItem];
			THighlightType highlight = ERemoveHighlight;
			if (item.iData.iCascadeId||IsFocused()) 
				highlight=EDrawHighlight;
			DrawItem(gc,iSelectedItem,highlight);
			DeactivateGc();
			}
		}
	}

TBool CEikMenuPane::IsHotKeyL(const TInt modifiers,const TInt code)
	{
	if (iHotKeyTable)
		{
		const TInt command=iHotKeyTable->CommandIdFromHotKey(code,modifiers);
		if (command)
			{
			if (iMenuObserver->CheckHotKeyNotDimmedL(command))
				iMenuObserver->ProcessCommandL(command);
			else
				iMenuObserver->HandleAttemptDimmedSelectionL(command);
			return ETrue;
			}
		}
	return EFalse;
	}

EXPORT_C TKeyResponse CEikMenuPane::OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType)
	{ // if were on the control stack then consume all keys
	return OfferKeyEventL(aKeyEvent,aType,ETrue);
	}

EXPORT_C TKeyResponse CEikMenuPane::OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType,TBool aConsumeAllKeys)
	{
	CheckCreateScrollerL();
	const TInt modifiers=(aKeyEvent.iModifiers)&(EModifierCtrl|EModifierShift|EModifierPureKeycode);
	const TInt code=aKeyEvent.iCode;
	TKeyResponse keyResponse= EKeyWasNotConsumed;
	if (iCascadeMenuPane)
		{
		keyResponse=iCascadeMenuPane->OfferKeyEventL(aKeyEvent,aType,EFalse);
		if (keyResponse==EKeyWasNotConsumed && (code==EKeyEscape || code==EKeyLeftArrow))
			{
			CloseCascadeMenu();
			keyResponse=EKeyWasConsumed;
			}
		return keyResponse;
		}
	// if popup menu displaying, needs to check for hotkeys itself.
	if (IsHotKeyL(modifiers, code))
		return EKeyWasConsumed;
	TInt count=0;
	if(iItemArray)
		count=iItemArray->Count();
	const TInt max=count-1;
	TInt newHighlight=iSelectedItem;
	if (iSelectedItem!=ENothingSelected)
		{
		switch (code)
			{
		case EKeySpace:
			iEikonEnv->InfoMsg(R_EIK_TBUF_PRESS_SPACE_MENP);
			return EKeyWasConsumed;
		case EKeyDownArrow:
			MoveHighlightTo(++newHighlight>max? 0: newHighlight);
			keyResponse=EKeyWasConsumed;
			break;
		case EKeyUpArrow:
			MoveHighlightTo(--newHighlight<0? max: newHighlight);
			keyResponse=EKeyWasConsumed;
			break;
		case EKeyRightArrow:
			{
			if(count==0) // Implies empty or undefined item array
				{
				if (aConsumeAllKeys)
					return EKeyWasConsumed;
				return EKeyWasNotConsumed;
				}
			const TItem& item=(*iItemArray)[iSelectedItem];
			if (item.iData.iCascadeId)
				{
				TryLaunchCascadeMenuL(item);
				ReportSelectionMadeL();
				keyResponse=EKeyWasConsumed;
				break;
				} // else fall through
			}	
		case EKeyLeftArrow:
		case EKeyHome:
		case EKeyEnd:
			{
			// only popup menu panes should consume these keys. ???
			if (aConsumeAllKeys)
					return EKeyWasConsumed;
			return EKeyWasNotConsumed;
			}
		case EKeyPageUp:
			{
			TInt newSelectedItem=iScroller->TopItemIndex();
			if (iSelectedItem==newSelectedItem)
				newSelectedItem=Max(0,iScroller->TopItemIndex()-NumberOfItemsThatFitInView());
			MoveHighlightTo(newSelectedItem);
			keyResponse=EKeyWasConsumed;
			}
			break;
		case EKeyPageDown:
			{
			TInt bottomItemIndex=iScroller->TopItemIndex();
			const TRect viewRect(ViewRect());
			TItem* item=NULL;
			while (bottomItemIndex<count-1)
				{
				item=&(*iItemArray)[bottomItemIndex];
				TInt pos=item->iPos+iItemHeight;
				if (item->iData.iFlags&EEikMenuItemSeparatorAfter)
					pos+=KEikMenuPaneSeparatorAfterSpace;
				if (pos>viewRect.iBr.iY)
					break;
				++bottomItemIndex;
				}
			TInt newSelectedItem=bottomItemIndex;
			if (iSelectedItem==--bottomItemIndex)
				newSelectedItem=Min(count-1,bottomItemIndex+NumberOfItemsThatFitInView()-1);
			MoveHighlightTo(newSelectedItem);
			keyResponse=EKeyWasConsumed;
			}
			break;
		case EKeyEnter:
			{
			if (modifiers&(EModifierShift|EModifierCtrl|EModifierFunc))
				return EKeyWasConsumed;
			if (count==0) // Do the same as modifiers (implies empty or undefined item array)
				return EKeyWasConsumed;
			TItem& item=(*iItemArray)[iSelectedItem];
			if (item.iData.iCascadeId)
				TryLaunchCascadeMenuL(item);
			ReportSelectionMadeL();
			return EKeyWasConsumed;
			}
			}
		}

	switch (code)
			{
		case EEikSidebarZoomOutKey:
			iMenuObserver->ProcessCommandL(EEikCmdZoomOut);
			return EKeyWasConsumed;
		case EEikSidebarZoomInKey:
			iMenuObserver->ProcessCommandL(EEikCmdZoomIn);
			return EKeyWasConsumed;
		case EEikSidebarClipKey:
		case EEikSidebarIrdaKey:
			{
			TInt region=1;
			TInt menuId=R_EIK_SIDEBAR_EDIT_MENU;
			if (code==EEikSidebarIrdaKey)
				{
				region=2;
				menuId=R_EIK_SIDEBAR_IRDA_MENU;
				}
			TSize screenSize=iCoeEnv->ScreenDevice()->SizeInPixels();
			TInt sidebarSectorLength=screenSize.iHeight/KEikNumOfSideButtons;
			iMenuObserver->HandleSideBarMenuL(menuId,TPoint(KEikSidebarPopupXPos,region*sidebarSectorLength),modifiers,iHotKeyTable);
			return EKeyWasConsumed;	
			}				
		case EEikSidebarMenuKey:
		case EKeyMenu:
			iMenuObserver->HandleSideBarMenuL(0,TPoint(0,0),modifiers,iHotKeyTable);
			return EKeyWasConsumed;
		case EKeyEscape:
			if (iOwner) 
				return EKeyWasNotConsumed; // owner will destroy the cascade
			ReportCanceled();
			return EKeyWasConsumed;
			}

	if (MoveToItemL(code,modifiers))
		return EKeyWasConsumed;  // must return here because in the case of a cascade it will have been deleted by now. 
		
	if (aConsumeAllKeys)
		return EKeyWasConsumed;
	else
		return keyResponse;
	}
	
TBool CEikMenuPane::MoveToItemL(TInt /*aCode*/, TInt /*aMods*/)
	{
	/* to be resurrected in version 2
	// if char entered which matches hotkey, move to that item.
	TBool found=EFalse;

	if (iHotKeyTable)
		{
		const TInt count=iItemArray->Count();
		const TChar keyCode=aCode; 
		if (keyCode.IsPrint())
			{
			TInt adjustedcode=0;
			//TInt mods=0;
			if (keyCode.IsLower())
				{
				//mods=EModifierCtrl;
				adjustedcode=(aCode -'a'+1); // match Ctrl+key, so 1 for 'a' etc.
				}
			else if (keyCode.IsUpper())
				{
				//mods=EModifierCtrl|EModifierShift;
				adjustedcode=(aCode -'A'+1);
				}
			// add shift to search modifier if shift was pressed
			TInt searchmods = EModifierCtrl;
			searchmods |=aMods;
			TInt command =iHotKeyTable->CommandIdFromHotKey(adjustedcode,searchmods);
			if (command)
				{
				for (TInt ii=0;ii<count;++ii)  // search current pane
					{
					TItem& item=(*iItemArray)[ii];
					if (item.iData.iCommandId==command)
						{
						MoveHighlightTo(ii);
						found =ETrue;
						//keyResponse=EKeyWasConsumed;
						break;
						}
					}
				if (!found) // search other panes
					{
					TInt aPaneindex;
					TInt aItemindex;
					iEikonEnv->EikAppUi()->MenuBar()->FindCommandIdInResourceL(command, aPaneindex, aItemindex);
					if (aItemindex!=ENothingSelected)
						{
						iEikonEnv->EikAppUi()->MenuBar()->MoveHighlightToL(aPaneindex,aItemindex);
						found=ETrue;
						//keyResponse=EKeyWasConsumed;
						}
					}
				}
			}
		}
	return found;*/
	return EFalse;
	}


void CEikMenuPane::ReportCanceled()
	{
	iMenuObserver->ProcessCommandL(EEikCmdCanceled); // shouldn't Leave in practice
	}

EXPORT_C void CEikMenuPane::HandlePointerEventL(const TPointerEvent& aPointerEvent)
	{
	const TRect innerRect=iBorder.InnerRect(Rect());
	if (!innerRect.Contains(aPointerEvent.iPosition))
		{
		if (iMenuPaneTitle && !iOwner && aPointerEvent.iPosition.iY<innerRect.iTl.iY &&
			aPointerEvent.iPosition.iX<innerRect.iBr.iX &&
			aPointerEvent.iPosition.iX>innerRect.iTl.iX)
			{// all pointer events above the main (not cascaded) menu pane will select the 0th item
			MoveHighlightTo(0);
			return;
			}
		switch (aPointerEvent.iType)
			{
		case TPointerEvent::EDrag:
			if (!iAllowPointerUpEvents)
				{
				iNumberOfDragEvents++;
				if (iNumberOfDragEvents>=KCascadeAutoSelectionSensitivity)
					iAllowPointerUpEvents=ETrue;
				}
			else
				MoveHighlightTo(ENothingSelected);
			if (iSelectedItem!=ENothingSelected && iCascadeMenuPane)
				CloseCascadeMenu();
			return;
		case TPointerEvent::EButton1Up:
			if (!iAllowPointerUpEvents)
				return;// disallow item selection until after a drag event has occured
		case TPointerEvent::EButton1Down:
			ReportCanceled();
			return;
		default:
			return;
			}
		}
	const TInt yPos=aPointerEvent.iPosition.iY;
	TInt count=0;
	if(iItemArray)
		count=iItemArray->Count();
	for (TInt ii=0;ii<count;++ii)
		{
		TItem& item=(*iItemArray)[ii];
		if (yPos<item.iPos)
			return;
		if (yPos<item.iPos+iItemHeight)
			{
			if (aPointerEvent.iType==TPointerEvent::EButton1Up)
				{
				ReportSelectionMadeL();
				return;
				}
			TBool launchCascadeMenu=EFalse;
			if (ii!=iSelectedItem
				|| (aPointerEvent.iType==TPointerEvent::EButton1Down && !iCascadeMenuPane))
				{
				launchCascadeMenu=ETrue;
				}
			if (ii==iSelectedItem && !IsFocused())
				{
				if (iOwner)
					iOwner->SetFocus(EFalse,EDrawNow);
				if (iCascadeMenuPane && iCascadeMenuPane->IsFocused())
					iCascadeMenuPane->SetFocus(EFalse,EDrawNow);
				SetFocus(ETrue,EDrawNow);
				}
			if (iCascadeMenuPane 
				&& (launchCascadeMenu || aPointerEvent.iType==TPointerEvent::EButton1Down))
				{
				CloseCascadeMenu();
				}
			MoveHighlightTo(ii);
			if (item.iData.iCascadeId && launchCascadeMenu)
				TryLaunchCascadeMenuL(item);
			}
		}
	}


EXPORT_C void CEikMenuPane::AddMenuItemL(const TItem::SData& aMenuItem)
// For use by Menu extensions
	{
	if (!iItemArray)
		iItemArray=new(ELeave) CItemArray;
	TItem item;
	item.iData=aMenuItem;
	iItemArray->AddItemL(item);
	UpdateScrollBar();
	}

EXPORT_C void CEikMenuPane::DeleteMenuItem(TInt aCommandId)
	{
	TInt count=0;
	if(iItemArray)
		count=iItemArray->Count();
	for (TInt ii=0;ii<count;++ii)
		{
		TItem& item=(*iItemArray)[ii];
		if (item.iData.iCommandId==aCommandId)
			{
			iItemArray->Delete(ii);
			UpdateScrollBar();
			return;
			}
		}
	Panic(EEikPanicNoSuchMenuItem);
	}

EXPORT_C CEikMenuPane::TItem::SData& CEikMenuPane::ItemData(TInt aCommandId)
	{
	TInt pos;
	TItem* item=ItemAndPos(aCommandId,pos);
	return item->iData;
	}

EXPORT_C CEikMenuPane::TItem* CEikMenuPane::ItemAndPos(TInt aCommandId,TInt& aPos)
	{
	TInt count=0;
	if(iItemArray)
		count=iItemArray->Count();
	aPos=0;
	TItem* item;
	FOREVER
		{
		if (aPos==count)
			Panic(EEikPanicNoSuchMenuItem);
		item=&(*iItemArray)[aPos];
		if (item->iData.iCommandId==aCommandId)
			break;
		++aPos;
		}
	return item;
	}

EXPORT_C void CEikMenuPane::SetItemTextL(TInt aCommandId,TInt aRid)
	{
	TBuf<80> tmp;
	iCoeEnv->ReadResource(tmp,aRid);
	SetItemTextL(aCommandId,tmp);
	}

EXPORT_C void CEikMenuPane::SetItemTextL(TInt aCommandId,const TDesC& aDes)
	{
	TInt pos;
	TItem newItem=*ItemAndPos(aCommandId,pos);
	newItem.iData.iText.Copy(aDes);
	const TInt newItemLength=newItem.ItemLength();
	iItemArray->Delete(pos);
	iItemArray->InsertL(pos,newItem,newItemLength);
	}

EXPORT_C void CEikMenuPane::SetItemDimmed(TInt aCommandId,TBool aDimmed)
	{
	TItem::SData& itemData=ItemData(aCommandId);
	if (aDimmed)
		itemData.iFlags|=EEikMenuItemDimmed;
	else
		itemData.iFlags&=(~EEikMenuItemDimmed);
	}

EXPORT_C void CEikMenuPane::SetItemButtonState(TInt aCommandId,TInt aButtonState)
	{
	TItem::SData& itemData=ItemData(aCommandId);
	itemData.iFlags&=(~(EEikMenuItemSymbolOn|EEikMenuItemSymbolIndeterminate));
	if (aButtonState&EEikMenuItemSymbolOn)
		itemData.iFlags|=EEikMenuItemSymbolOn;
	else if (aButtonState&EEikMenuItemSymbolIndeterminate)
		itemData.iFlags|=EEikMenuItemSymbolIndeterminate; 
	}

EXPORT_C void CEikMenuPane::SetSelectedItem(TInt aSelectedItem)
	{
	iSelectedItem=(aSelectedItem<=0 || aSelectedItem>=NumberOfItemsInPane())?0:aSelectedItem;
	UpdateScrollBar();
	}

EXPORT_C TInt CEikMenuPane::SelectedItem() const
	{
	return iSelectedItem;
	}

EXPORT_C void CEikMenuPane::SetItemArray(CItemArray* aItemArray)
	{
	if (!ItemArrayOwnedExternally())
		delete iItemArray;
	iItemArray=aItemArray;
	}

EXPORT_C void CEikMenuPane::SetItemArrayOwnedExternally(TBool aOwnedExternally)
	{
	if (aOwnedExternally)
		iArrayOwnedExternally=ETrue;
	else
		iArrayOwnedExternally=EFalse;
	}

EXPORT_C void CEikMenuPane::SetLaunchingButton(CEikButtonBase* aButton)
	{
	iLaunchingButton=aButton;
	}

EXPORT_C TInt CEikMenuPane::NumberOfItemsInPane() const
	{
	return(iItemArray? iItemArray->Count() : 0);
	}

EXPORT_C void CEikMenuPane::Close()
	{
	if (OwnsWindow())
		CloseWindow();
	}

EXPORT_C void CEikMenuPane::Reserved_1()
	{}
EXPORT_C void CEikMenuPane::Reserved_2()
	{}

void CEikMenuPane::HandleScrollEventL(CEikScrollBar* aScrollBar,TEikScrollEvent aEventType)
	{
	// iScroller must exist as it owns the real scroll observer
	if (iCascadeMenuPane)
		{
		CloseCascadeMenu();
		return;
		}
	TInt newSelectedItem=0;
	switch (aEventType)
		{
	case EEikScrollUp:
		newSelectedItem=Max(0,iScroller->TopItemIndex()-1);
		break;
	case EEikScrollDown:
	case EEikScrollPageDown:
		{
		TInt bottomItemIndex=iScroller->TopItemIndex();
		const TRect viewRect(ViewRect());
		TInt count=0;
		if(iItemArray)
			count=iItemArray->Count();
		TItem* item=NULL;
		while (bottomItemIndex<count-1)
			{
			item=&(*iItemArray)[bottomItemIndex];
			TInt pos=item->iPos+iItemHeight;
			if (item->iData.iFlags&EEikMenuItemSeparatorAfter)
				pos+=KEikMenuPaneSeparatorAfterSpace;
			if (pos>viewRect.iBr.iY)
				break;
			++bottomItemIndex;
			}
		--bottomItemIndex;
		if (aEventType==EEikScrollDown)
			newSelectedItem=bottomItemIndex+1;
		else // page down
			newSelectedItem=Min(count-1,bottomItemIndex+NumberOfItemsThatFitInView()-1);
		break;
		}
	case EEikScrollPageUp:
		newSelectedItem=Max(0,iScroller->TopItemIndex()-NumberOfItemsThatFitInView()+1);
		break;
	case EEikScrollThumbDragVert:
		{
		if(!iItemArray)
			return;
		const TInt amountToScroll=ViewRect().iTl.iY-(*iItemArray)[0].iPos-aScrollBar->ThumbPosition();
		Scroll(amountToScroll);
		return;
		}
	default:
		CBaActiveScheduler::LeaveNoAlert();
		break;
		}
	ScrollToMakeItemVisible(newSelectedItem);
	}

void CEikMenuPane::CreateScrollBarFrame()
	{
	if (!CheckCreateScroller())
		return;
	TRAPD(ignore,
		iSBFrame=new(ELeave) CEikScrollBarFrame(this,iScroller);
		iSBFrame->SetScrollBarVisibilityL(CEikScrollBarFrame::EOff,CEikScrollBarFrame::EAuto);
		);
	UpdateScrollBar();
	}

void CEikMenuPane::UpdateScrollBar()
	{
	if (!CheckCreateScroller())
		return;
	CIdle* idle=iScroller->Idle();
	if (idle && !idle->IsActive())
		idle->Start(TCallBack(CEikMenuPane::UpdateScrollBarCallBackL,this));
	}

TInt CEikMenuPane::UpdateScrollBarCallBackL(TAny* aObj)
	{ // static
	REINTERPRET_CAST(CEikMenuPane*,aObj)->DoUpdateScrollBarL();
	return 0;
	}

void CEikMenuPane::DoUpdateScrollBarL()
	{
	if (!iSBFrame)
		return;
	TEikScrollBarModel hSbarModel;
	TEikScrollBarModel vSbarModel;
	TRect clientRect(ViewRect());
	if(iItemArray && iItemArray->Count()>0)
		vSbarModel.iThumbPosition=clientRect.iTl.iY-(*iItemArray)[0].iPos;
	else
		vSbarModel.iThumbPosition=clientRect.iTl.iY;
	vSbarModel.iScrollSpan=TotalItemHeight();
	vSbarModel.iThumbSpan=ViewRect().Height();
	const TMargins margins=iBorder.Margins();
	TEikScrollBarFrameLayout layout;
	layout.iInclusiveMargin=iBorder.Margins();
	layout.iClientMargin.iTop=margins.iTop+KTopHighlightGap;
	layout.iClientMargin.iBottom=margins.iBottom+KBottomHighlightGap;
	layout.iClientMargin.iLeft=margins.iLeft+KLeftHighlightGap;
	layout.iClientMargin.iRight=margins.iRight+KRightHighlightGap;
	layout.iClientAreaGranularity=TSize(1,iItemHeight); // different lines may be different heights because of line separators !!!
	layout.iTilingMode=TEikScrollBarFrameLayout::EInclusiveRectConstant;
	TRect rect(Rect());
	iSBFrame->TileL(&hSbarModel,&vSbarModel,clientRect,rect,layout);
	}

void CEikMenuPane::ScrollToMakeItemVisible(TInt aItemIndex)
	{
	if(!iItemArray || iItemArray->Count()==0)
		return;
	if (!CheckCreateScroller())
		return;
	const TRect viewRect(ViewRect());
	const TItem& item=(*iItemArray)[aItemIndex];
	const TInt itemYPos=item.iPos;
	if (itemYPos>=viewRect.iTl.iY)
		{
		TInt bottomPos=itemYPos+iItemHeight;
		if (item.iData.iFlags&EEikMenuItemSeparatorAfter)
			bottomPos+=KEikMenuPaneSeparatorAfterSpace;
		if (bottomPos<=viewRect.iBr.iY)
			return; // item is already visible
		}
	const TInt amountToScroll=(itemYPos<viewRect.iTl.iY? viewRect.iTl.iY-itemYPos : viewRect.iBr.iY-(itemYPos+iItemHeight)-
					(item.iData.iFlags&EEikMenuItemSeparatorAfter? KEikMenuPaneSeparatorAfterSpace : 0));
	Scroll(amountToScroll);
	UpdateScrollBar();
	}

void CEikMenuPane::Scroll(TInt aAmount)
	{
	if (!CheckCreateScroller())
		return;
	TInt count=0;
	if(iItemArray)
		count=iItemArray->Count();
	TInt ii=0;
	for (ii=0;ii<count;ii++)
		(*iItemArray)[ii].iPos+=aAmount;
	const TRect viewRect(ViewRect());
	DrawableWindow()->Scroll(viewRect,TPoint(0,aAmount));
	if(iItemArray)
		{
		ii=-1;
		while ((*iItemArray)[++ii].iPos<viewRect.iTl.iY)
			;
		iScroller->SetTopItemIndex(ii);
		}
	else
		iScroller->SetTopItemIndex(0);
	}

TRect CEikMenuPane::ViewRect() const
	{
	TRect rect(iBorder.InnerRect(Rect()));
	rect.iTl.iY+=KTopHighlightGap;
	rect.iBr.iY-=KBottomHighlightGap;
	rect.iTl.iX+=KLeftHighlightGap;
	rect.iBr.iX-=KRightHighlightGap;
	return rect;
	}

TInt CEikMenuPane::NumberOfItemsThatFitInView() const
	{
	TInt height=TotalItemHeight();
	const TInt availableHeight=(iFlags&EInvalidCurrentSize?
							iCoeEnv->ScreenDevice()->SizeInPixels().iHeight-iPosition.iY-
										(iBorder.SizeDelta().iHeight+KBottomHighlightGap+KTopHighlightGap)
							: ViewRect().Height());
	TInt count=0;
	if(iItemArray)
		count=iItemArray->Count();
	if (height>availableHeight)
		{
		const TInt avgHeight=(height+iItemHeight-1)/count;
		return availableHeight/avgHeight;
		}
	return count;
	}

TInt CEikMenuPane::TotalItemHeight() const
	{
	TInt height=0;
	TInt count=0;
	if(iItemArray)
		count=iItemArray->Count();
	for (TInt ii=0;ii<count;++ii)
		{
		height+=iItemHeight;
		if ((*iItemArray)[ii].iData.iFlags&EEikMenuItemSeparatorAfter)
			height+=KEikMenuPaneSeparatorAfterSpace;
		}
	return height;
	}

TBool CEikMenuPane::CheckCreateScroller()
	{
	TInt err=KErrNone;
	if (!iScroller)
		TRAP(err,iScroller=CMenuScroller::NewL(*this));
	return err==KErrNone;
	}

void CEikMenuPane::CheckCreateScrollerL()
	{
	if (!iScroller)
		iScroller=CMenuScroller::NewL(*this);
	}
