// TMENU1.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include <coeaui.h>
#include <coemain.h>
#include <coecntrl.h>
#include <e32keys.h>
#include <basched.h>

#include "tmenuasi.h"

enum TMessageControlFontStyle
    {
    EStyleElementBold=1,
    EStyleElementItalic,
    EStyleElementUnderline,
    EStyleElementStrikethrough
    };

enum TMenuCommand
    {
    EMenuCommandBold=EStyleElementBold,
    EMenuCommandItalic=EStyleElementItalic,
    EMenuCommandUnderline=EStyleElementUnderline,
    EMenuCommandStrikethrough=EStyleElementStrikethrough,
    EMenuCommandAddWindow,
    EMenuCommandRemoveWindow,
    EMenuCommandDrag,
	EMenuCommandColor,
    EMenuCommandExit
    };

class MTestMenuObserver
	{
public:
	virtual void MenuSelectionMade(TMenuCommand aSelection)=0;
	};

//
// class CTestMenu
//

class TSimpleMenuItem
    {
private:
    enum { ENominalTextLength=80 };
public:
    TMenuCommand iCommand;
    TBuf<ENominalTextLength> iText; // less than this actually stored
public:
    TInt RealLengthOfData() const;
    };

TInt TSimpleMenuItem::RealLengthOfData() const
    {
    return(sizeof(TSimpleMenuItem)-(ENominalTextLength-iText.Length())*sizeof(TText));
    }
    
class CTestMenu : public CCoeControl
	{
public:
    ~CTestMenu();
    void ConstructL(MTestMenuObserver* aObserver,CCoeAppUi* aStack);
private: // framework
    TKeyResponse OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType);
    void HandlePointerEventL(const TPointerEvent& aPointerEvent);
    void Draw(const TRect& aRect) const;
private: // new functions
    void InitializeHotKeyCommands();
    void CreateFontL();
    void CreateMenuL();
    void CloseMenu();
    void LoadDataL();
    void DoLoadDataL();
    void FreeData();
    void AddMenuItemL(TMenuCommand aCommand,const TDesC& aText);
    void PrepareGcForDrawingItems() const;
    void DrawItem(TInt aItem,TInt aHighlight) const;
    void ReportSelectionMade();
    void HighlightTo(TInt aNewItemSelected);
	void CalculateSize(TSize& aSize);
private:
    enum { EBorderWidth=2 };
    MTestMenuObserver* iObserver;
    CFont* iFont;
    CCoeAppUi* iStack;
    TBool iDisplayed;
    TInt iItemSelected;
    TMenuCommand iHotKeyCommand[27];
    CArrayVarFlat<TSimpleMenuItem>* iItemArray;
    };

CTestMenu::~CTestMenu()
    {
    FreeData();
    iCoeEnv->ReleaseScreenFont(iFont);
    }
    
void CTestMenu::CreateFontL()
    {
    TFontSpec spec(_L("Arial"),240);
    iFont=iCoeEnv->CreateScreenFontL(spec);
    }

void CTestMenu::HandlePointerEventL(const TPointerEvent& aPointerEvent)
    {
    TRect rect=Rect();
    if (!rect.Contains(aPointerEvent.iPosition))
        {
        switch (aPointerEvent.iType)
            {
        case TPointerEvent::EButton1Down:
        case TPointerEvent::EButton1Up:
            CloseMenu();
        default:
            break;
            }
        }
    rect.Shrink(EBorderWidth,EBorderWidth);
    if (!rect.Contains(aPointerEvent.iPosition))
        return;
    TInt n=(aPointerEvent.iPosition.iY-EBorderWidth)/iFont->HeightInPixels();
    HighlightTo(n);
    if (aPointerEvent.iType==TPointerEvent::EButton1Up)
        {
        ReportSelectionMade();
        CloseMenu();
        }
    }

void CTestMenu::Draw(const TRect& /*aRect*/) const
    {
    CWindowGc& gc=SystemGc();
    TRect rect=Rect();
    gc.DrawRect(rect);
    rect.Shrink(1,1);
    gc.SetPenColor(KRgbWhite);
    gc.DrawRect(rect);
    PrepareGcForDrawingItems();
    TInt count=iItemArray->Count();
    for (TInt i=0; i<count; i++)
        DrawItem(i,i==iItemSelected);
    }

void CTestMenu::PrepareGcForDrawingItems() const
    {
    CWindowGc& gc=SystemGc();
    gc.UseFont(iFont);
    gc.SetBrushStyle(CGraphicsContext::ESolidBrush);
    }

void CTestMenu::DrawItem(TInt aItem,TInt aHighlight) const
    {
    TInt itemHeight=iFont->HeightInPixels();
    TPoint tl(2,2+aItem*itemHeight);
    TSize sz(iSize.iWidth-4,itemHeight);
    TRect rect(tl,sz);
    CWindowGc& gc=SystemGc();
    if (!aHighlight)
       	{
        gc.SetPenColor(KRgbBlack);
        gc.SetBrushColor(KRgbWhite);
        }
    else
       	{
        gc.SetPenColor(KRgbWhite);
        gc.SetBrushColor(KRgbBlack);
        }
    TSimpleMenuItem& tmp=(*iItemArray)[aItem];
    gc.DrawText(tmp.iText,rect,iFont->AscentInPixels());
    }
    
void CTestMenu::ReportSelectionMade()
    {
    TSimpleMenuItem& tmp=(*iItemArray)[iItemSelected];
    iObserver->MenuSelectionMade(tmp.iCommand);
    }
    
void CTestMenu::InitializeHotKeyCommands()
    {
    iHotKeyCommand[CTRL('b')]=EMenuCommandBold;
    iHotKeyCommand[CTRL('i')]=EMenuCommandItalic;
    iHotKeyCommand[CTRL('u')]=EMenuCommandUnderline;
    iHotKeyCommand[CTRL('s')]=EMenuCommandStrikethrough;
    iHotKeyCommand[CTRL('a')]=EMenuCommandAddWindow;
    iHotKeyCommand[CTRL('r')]=EMenuCommandRemoveWindow;
    iHotKeyCommand[CTRL('d')]=EMenuCommandDrag;
    iHotKeyCommand[CTRL('e')]=EMenuCommandExit;
    iHotKeyCommand[CTRL('c')]=EMenuCommandColor;
    }
    
void CTestMenu::ConstructL(MTestMenuObserver* aObserver,CCoeAppUi* aStack)
    {
    iObserver=aObserver;
    iStack=aStack;
    InitializeHotKeyCommands();
	CreateFontL();
    }
    
void CTestMenu::FreeData()
    {
    delete(iItemArray);
    iItemArray=NULL;
    }

void CTestMenu::CloseMenu()
    {
    if (!iDisplayed)
        return;
    CloseWindow();
    iDisplayed=EFalse;
    FreeData();
    }
    
void CTestMenu::AddMenuItemL(TMenuCommand aCommand,const TDesC& aText)
    {
    TSimpleMenuItem tmp;
    tmp.iCommand=aCommand;
    tmp.iText=aText;
    iItemArray->AppendL(tmp,tmp.RealLengthOfData());
    }

void CTestMenu::DoLoadDataL()
    {
    AddMenuItemL(EMenuCommandBold,_L("Toggle bold (Ctrl+B)"));
    AddMenuItemL(EMenuCommandItalic,_L("Toggle italic (Ctrl+I)"));
    AddMenuItemL(EMenuCommandUnderline,_L("Toggle underline (Ctrl+U)"));
    AddMenuItemL(EMenuCommandStrikethrough,_L("Toggle strikethrough (Ctrl+S)"));
    AddMenuItemL(EMenuCommandAddWindow,_L("Add window (Ctrl+A)"));
    AddMenuItemL(EMenuCommandRemoveWindow,_L("Remove window (Ctrl+R)"));
    AddMenuItemL(EMenuCommandDrag,_L("Toggle drag enablement (Ctrl+D)"));
    AddMenuItemL(EMenuCommandColor,_L("Override colours (Ctrl+C)"));
    AddMenuItemL(EMenuCommandExit,_L("Exit (Ctrl+E)"));
    }

void CTestMenu::LoadDataL()
    {
    iItemArray=new(ELeave) CArrayVarFlat<TSimpleMenuItem> (16);
    TRAPD(ret,DoLoadDataL());
    if (ret)
        {
        FreeData();
        User::Leave(ret);
        }
    }

void CTestMenu::CalculateSize(TSize& aSize)
    {
    TInt count=iItemArray->Count();
    aSize.iHeight=count*iFont->HeightInPixels();
    aSize.iHeight+=2+2; // 2 pixels at top and 2 at bottom
    for (TInt i=0; i<count; i++)
        {
        TSimpleMenuItem& tmp=(*iItemArray)[i];
        TInt itemWidth=iFont->TextWidthInPixels(tmp.iText);
        if (itemWidth>aSize.iWidth)
            aSize.iWidth=itemWidth;
        }
    aSize.iWidth+=2+2; // 2 pixels at left and 2 at right
    }

void CTestMenu::CreateMenuL()
    {
    LoadDataL();
    TRAPD(err,CreateWindowL());
    if (err)
        {
        FreeData();
        User::Leave(err);
        }
    EnableDragEvents();
    SetPointerCapture();
    iDisplayed=ETrue;
    TSize size;
    CalculateSize(size);
    SetExtentL(TPoint(5,5),size);
    ActivateL();
    }

void CTestMenu::HighlightTo(TInt aNewItemSelected)
    {
    if (aNewItemSelected==iItemSelected)
        return;
    ActivateGc();
    PrepareGcForDrawingItems();
    DrawItem(iItemSelected,EFalse);
    iItemSelected=aNewItemSelected;
    DrawItem(iItemSelected,ETrue);
	DeactivateGc();
    }

TKeyResponse CTestMenu::OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType)
    {
	if (aType!=EEventKey)
		return(EKeyWasNotConsumed);
    TInt modifiers=aKeyEvent.iModifiers;
    TInt code=aKeyEvent.iCode;
    if ((modifiers&EAllStdModifiers)==EModifierCtrl)
        {
        if (code<27)
            {
            TMenuCommand command=iHotKeyCommand[code];
            if (command)
                {
                iObserver->MenuSelectionMade(command);
                CloseMenu();
                }
            }
        return(EKeyWasConsumed);
        }
    if (!iDisplayed)
        {
        if (code==EKeyF9 || code==EKeyTab)
            {
            CreateMenuL();
            return(EKeyWasConsumed);
            }
        }
    else
        {
        TInt count=iItemArray->Count();
        switch (code)
            {
        case EKeyDownArrow:
            if (iItemSelected<count-1)
                HighlightTo(iItemSelected+1);
            break;
        case EKeyUpArrow:
            if (iItemSelected)
                HighlightTo(iItemSelected-1);
            break;
        case EKeyPageUp:
            HighlightTo(0);
            break;
        case EKeyPageDown:
            HighlightTo(count-1);
            break;
        case EKeyEnter:
            ReportSelectionMade();
        case EKeyEscape:
            CloseMenu();
            break;
            }
        return(EKeyWasConsumed);
        }
    return(EKeyWasNotConsumed);
    }
    
//
// class CMessageControl
//

class CMessageControl : public CCoeControl
    {
public:
    ~CMessageControl();
    void ConstructL();
    void ToggleFontStyleL(TMessageControlFontStyle aStyleElement);
    void ToggleDragState();
    void SetMessage(const TDesC8& aMessage);
private: // framework
    TKeyResponse OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType);
    void HandlePointerEventL(const TPointerEvent& aPointerEvent);
    void Draw(const TRect& aRect) const;
	void FocusChanged(TDrawNow aDrawNow);
private: // new functions
    void DrawBorder() const;
    void SetFontL(const TFontSpec& aFontSpec);
    void DrawMessage() const;
    void DrawMessageNow() const;
private:
    TBuf8<40> iMessage;
    TFontSpec iFontSpec;
    TFontUnderline iFontUnderline;
    TFontStrikethrough iFontStrikethrough;
    CFbsFont* iFont;
    TBool iDragsEnabled;
    };

void CMessageControl::ToggleDragState()
    {
    TInt filter=EPointerFilterDrag; // means to filter out drags
    if (iDragsEnabled)
        {
        iDragsEnabled=EFalse;
        SetMessage(_L8("Pointer drag events disabled"));
        }
    else
        {
        iDragsEnabled=ETrue;
        SetMessage(_L8("Pointer drag events enabled"));
        filter=0;
        }
    Window().PointerFilter(EPointerFilterDrag,filter);
    }
    
void CMessageControl::ConstructL()
    {
    CreateBackedUpWindowL(iCoeEnv->RootWin(),EGray2);
    SetExtentL(TPoint(20,20),TSize(600,200));
    TFontSpec spec(_L("Arial"),220);
    SetFontL(spec);
    SetMessage(_L8("Press Tab for Menu..."));
    ActivateL();
    }

CMessageControl::~CMessageControl()
    {
    iCoeEnv->ReleaseScreenFont(iFont);
    }

void CMessageControl::SetFontL(const TFontSpec& aFontSpec)
    {
    CFbsFont* font=iCoeEnv->CreateScreenFontL(aFontSpec);
    iCoeEnv->ReleaseScreenFont(iFont); // after previous line succeeds
    iFont=font;
    iFontSpec=aFontSpec;
    }

void CMessageControl::ToggleFontStyleL(TMessageControlFontStyle aStyleElement)
    {
    TFontSpec spec=iFontSpec;
    TBool doSet=EFalse;
    switch (aStyleElement)
        {
    case EStyleElementBold:
        spec.iFontStyle.SetStrokeWeight(spec.iFontStyle.StrokeWeight()? EStrokeWeightNormal: EStrokeWeightBold);
        doSet=ETrue;
        break;
    case EStyleElementItalic:
        spec.iFontStyle.SetPosture(spec.iFontStyle.Posture()? EPostureUpright: EPostureItalic);
        doSet=ETrue;
        break;
    case EStyleElementUnderline:
        iFontUnderline=(iFontUnderline? EUnderlineOff: EUnderlineOn);
        break;
    case EStyleElementStrikethrough:
        iFontStrikethrough=(iFontStrikethrough? EStrikethroughOff: EStrikethroughOn);
        }
    if (doSet)
        SetFontL(spec); // otherwise change effective at Draw time
    DrawMessageNow();
    }

void CMessageControl::HandlePointerEventL(const TPointerEvent& aPointerEvent)
    {
    iMessage.Format(_L8("Pointer event %d at (%d,%d)"),aPointerEvent.iType,aPointerEvent.iPosition.iX,aPointerEvent.iPosition.iY);
    DrawMessageNow();
    }

void CMessageControl::FocusChanged(TDrawNow aDrawNow)
	{
    if (aDrawNow)
        {
        ActivateGc();
        DrawBorder();
        DeactivateGc();
        }
    }

void CMessageControl::DrawBorder() const
	{
    CWindowGc& gc=SystemGc();
    TRect rect=Rect();
    gc.DrawRect(rect);
    if (!IsFocused())
        gc.SetPenColor(KRgbWhite);
    rect.Shrink(1,1);
    gc.DrawRect(rect);
    rect.Shrink(1,1);
    gc.DrawRect(rect);
	}

void CMessageControl::Draw(const TRect& /*aRect*/) const
    {
    DrawBorder();
    DrawMessage();
    }

void CMessageControl::DrawMessage() const
    {
    TRect rect=Rect();
    rect.Shrink(3,3);
    TInt ascent=(rect.iBr.iY-rect.iTl.iY-iFont->HeightInPixels())/2 + iFont->AscentInPixels();
    CWindowGc& gc=SystemGc();
    gc.SetPenColor(KRgbBlack);
	gc.UseFont(iFont);
    if (iFontUnderline)
        gc.SetUnderlineStyle(iFontUnderline);
    if (iFontStrikethrough)
        gc.SetStrikethroughStyle(iFontStrikethrough);
    gc.SetBrushStyle(CGraphicsContext::ESolidBrush);
#if !defined(_UNICODE)
    gc.DrawText(iMessage,rect,ascent,CGraphicsContext::ECenter);
#else
    TBuf<40> text;
	text.Copy(iMessage);
    gc.DrawText(text,rect,ascent,CGraphicsContext::ECenter);
#endif
    }

void CMessageControl::DrawMessageNow() const
    {
	ActivateGc();
	DrawMessage();
	DeactivateGc();
    }

void CMessageControl::SetMessage(const TDesC8& aMessage)
    {
    iMessage=aMessage;
    DrawMessageNow();
    }

TKeyResponse CMessageControl::OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType)
    {
    if (aType!=EEventKey)
	    return(EKeyWasConsumed);
	TInt modifiers=aKeyEvent.iModifiers;
	TInt code=aKeyEvent.iCode;
	iMessage.Format(_L8("Key 0x%x, modifier 0x%x"),code,modifiers);
	DrawMessageNow();
	if ((modifiers&EAllStdModifiers)==(EModifierShift|EModifierCtrl))
        {
        TPoint pos=Position();
        switch (code)
            {
        case EKeyLeftArrow:
            pos.iX--;
            break;
        case EKeyRightArrow:
            pos.iX++;
            break;
        case EKeyUpArrow:
            pos.iY--;
            break;
        case EKeyDownArrow:
            pos.iY++;
            break;
        default:
            goto exit;
            }
        SetPosition(pos);
        }
exit:
    return(EKeyWasConsumed);
    }

//
// class CTrivialWindow
//

class CTrivialWindow : public CCoeControl
    {
public:
    void ConstructL(const TPoint& aPoint,const TSize& aSize);
private:
    void Draw(const TRect& aRect) const;
    };

void CTrivialWindow::ConstructL(const TPoint& aPoint,const TSize& aSize)
    {
    CreateWindowL();
    SetExtentL(aPoint,aSize);
    Window().SetShadowHeight(4);
    ActivateL();
    }
    
void CTrivialWindow::Draw(const TRect& /*aRect*/) const
    {
    CWindowGc& gc=SystemGc();
    gc.SetBrushStyle(CGraphicsContext::ESolidBrush);
    gc.DrawRect(Rect());
    }

//
// class CTestAppUi
//

class CTestAppUi : public CCoeAppUi, public MTestMenuObserver
    {
public:
    ~CTestAppUi();
    void ConstructL();
private: // from MTestMenuObserver
    void MenuSelectionMade(TMenuCommand aSelection);
private: // from CCoeAppUi
	void HandleApplicationSpecificEventL(TInt aType,const TWsEvent& aEvent);
	void HandleMessageReadyL();
private: // new functions
    void AddWindowL();
    void RemoveWindow();
	void TestColorOverridesL();
    void DoAddWindowL(CTrivialWindow* aWindow,const TPoint& aPoint,const TSize& aSize);
private:
    CMessageControl* iMessageControl;
    CArrayFixFlat<CTrivialWindow*>* iWindows;
    };

CTestAppUi::~CTestAppUi()
    {
    delete(iMessageControl);
    if (!iWindows)
        return;
    TInt i=iWindows->Count();
    while (i--)
        delete((*iWindows)[i]);
    delete(iWindows);
    }
    
void CTestAppUi::DoAddWindowL(CTrivialWindow* aWindow,const TPoint& aPoint,const TSize& aSize)
    {
    aWindow->ConstructL(aPoint,aSize);
    iWindows->AppendL(aWindow);
    }

void CTestAppUi::AddWindowL()
    {
    TInt offset=iWindows->Count()*5;
    TSize size(40,40);
    TPoint pos(640-40-offset,offset);
    CTrivialWindow* window=new(ELeave) CTrivialWindow;
    TRAPD(err,DoAddWindowL(window,pos,size));
    if (err)
        {
        delete(window);
        User::Leave(err);
        }
    }

void CTestAppUi::RemoveWindow()
    {
    TInt i=iWindows->Count();
    if (i--)
        {
        delete((*iWindows)[i]);
        iWindows->Delete(i);
        }
    }

void CTestAppUi::TestColorOverridesL()
	{
	CCoeControl* ctrl=new(ELeave) CCoeControl;
	CleanupStack::PushL(ctrl);
	TRgb color=KRgbDarkYellow;
	if (ctrl->GetColor(0,color))
		User::Invariant();
	ctrl->OverrideColorL(0,KRgbBlack);
	if (!ctrl->GetColor(0,color))
		User::Invariant();
	if (color!=KRgbBlack)
		User::Invariant();
	if (ctrl->GetColor(3,color))
		User::Invariant();
	ctrl->OverrideColorL(6,KRgbMagenta);
	ctrl->OverrideColorL(5,KRgbGreen);
	if (!ctrl->GetColor(5,color))
		User::Invariant();
	if (color!=KRgbGreen)
		User::Invariant();
	if (!ctrl->GetColor(6,color))
		User::Invariant();
	if (color!=KRgbMagenta)
		User::Invariant();
	CleanupStack::PopAndDestroy(); // ctrl
	iMessageControl->SetMessage(_L8("passed tests"));
	}

void CTestAppUi::MenuSelectionMade(TMenuCommand aSelection)
    {
    switch (aSelection)
        {
    case EMenuCommandBold:
    case EMenuCommandItalic:
    case EMenuCommandUnderline:
    case EMenuCommandStrikethrough:
        iMessageControl->ToggleFontStyleL((TMessageControlFontStyle)aSelection);
        break;
    case EMenuCommandDrag:
        iMessageControl->ToggleDragState();
        break;
    case EMenuCommandExit:
        iMessageControl->SetMessage(_L8("About to exit..."));
		iCoeEnv->Flush(200000);
        CBaActiveScheduler::Exit();
        break;
    case EMenuCommandAddWindow:
        AddWindowL();
        break;
    case EMenuCommandRemoveWindow:
        RemoveWindow();
		break;
	case EMenuCommandColor:
		TestColorOverridesL();
		break;
	default:
		break;
        }
    }
    
void CTestAppUi::HandleMessageReadyL()
	{
	TUid uid;
	TPtr8 msgPtr(NULL,0);
	User::LeaveIfError(iCoeEnv->RootWin().FetchMessage(uid,msgPtr));
	iMessageControl->SetMessage(msgPtr);
	User::Free((TAny*)msgPtr.Ptr());
	}

void CTestAppUi::HandleApplicationSpecificEventL(TInt aType,const TWsEvent& aEvent)
	{
	TInt senderWgId=(*(TInt*)aEvent.EventData());
	TInt count=iWindows->Count();
	TMenuAsiEventReply reply=EReplyWindowsCounted;
	switch (aType)
		{
	case ESendCountWindows:
		break;
	case ESendAddWindow:
		reply=EReplyWindowAdded;
		TRAPD(err,AddWindowL());
		if (err)
			reply=EReplyFailedToAddWindow;
		break;
	case ESendRemoveWindow:
		if (!count)
			reply=EReplyNoWindowToRemove;
		else
			{
			RemoveWindow();
			reply=EReplyWindowRemoved;
			}
		break;
		}
	TWsEvent data;
	data.SetType(reply);
	*(TInt*)data.EventData()=count;
	iCoeEnv->WsSession().SendEventToWindowGroup(senderWgId,data);
	}

void CTestAppUi::ConstructL()
    {
    CCoeAppUi::ConstructL();
    iMessageControl=new(ELeave) CMessageControl;
    iMessageControl->ConstructL();
    AddToStackL(iMessageControl);
    CTestMenu* menu=new(ELeave) CTestMenu;
    CleanupStack::PushL(menu);
    menu->ConstructL(this,this);
    AddToStackL(menu,ECoeStackPriorityMenu,ECoeStackFlagRefusesFocus|ECoeStackFlagOwnershipTransfered);
	CleanupStack::Pop();
    iWindows=new(ELeave) CArrayFixFlat<CTrivialWindow*>(4);
	iCoeEnv->RootWin().SetName(TMenu1GroupName);
    }

//
// Main
//

void ConstructAppL(CCoeEnv* aCoe)
    { // runs inside a TRAP harness
    aCoe->ConstructL();
    CTestAppUi* appUi=new(ELeave) CTestAppUi;
    aCoe->SetAppUi(appUi);
    appUi->ConstructL();
    }

#if defined(__WINS__)
EXPORT_C TInt EntryPoint(TAny*)
#else
GLDEF_C TInt E32Main()
#endif
    {
    CCoeEnv* coe=new CCoeEnv;
    TRAPD(err,ConstructAppL(coe));
    if (!err)
        coe->ExecuteD();
    return(err);
    }

#if defined(__WINS__)
GLDEF_C TInt E32Dll(TDllReason)
	{
	return(KErrNone);
	}
#endif
