///////////////////////////////////////////////////////////////////////////////
// Name:        tests/menu/menu.cpp
// Purpose:     wxMenu unit test
// Author:      wxWidgets team
// Created:     2010-11-10
// Copyright:   (c) 2010 wxWidgets team
///////////////////////////////////////////////////////////////////////////////

// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------

#include "testprec.h"

#if wxUSE_MENUBAR


#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif // WX_PRECOMP

#include "wx/menu.h"
#include "wx/translation.h"
#include "wx/uiaction.h"

#include "waitfor.h"

#include <stdarg.h>

#include <memory>

// ----------------------------------------------------------------------------
// helper
// ----------------------------------------------------------------------------

namespace
{

enum
{
    MenuTestCase_Foo = 10000,
    MenuTestCase_SelectAll,
    MenuTestCase_Bar,
    MenuTestCase_ExtraAccel,
    MenuTestCase_ExtraAccels,
    MenuTestCase_First
};

void PopulateMenu(wxMenu* menu, const wxString& name,  size_t& itemcount)
{
    // Start at item 1 to make it human-readable ;)
    for (int n=1; n<6; ++n, ++itemcount)
    {
        wxString label = name; label << n;
        menu->Append(MenuTestCase_First + itemcount, label, label + " help string");
    }
}

void RecursivelyCountMenuItems(const wxMenu* menu, size_t& count)
{
    CPPUNIT_ASSERT( menu );

    count += menu->GetMenuItemCount();
    for (size_t n=0; n < menu->GetMenuItemCount(); ++n)
    {
        wxMenuItem* item = menu->FindItemByPosition(n);
        if (item->IsSubMenu())
        {
            RecursivelyCountMenuItems(item->GetSubMenu(), count);
        }
    }
}

} // anon namespace


// ----------------------------------------------------------------------------
// test class
// ----------------------------------------------------------------------------

class MenuTestCase : public CppUnit::TestCase
{
public:
    MenuTestCase() {}

    virtual void setUp() override { CreateFrame(); }
    virtual void tearDown() override { m_frame->Destroy(); }

private:
    CPPUNIT_TEST_SUITE( MenuTestCase );
        CPPUNIT_TEST( FindInMenubar );
        CPPUNIT_TEST( FindInMenu );
        CPPUNIT_TEST( EnableTop );
        CPPUNIT_TEST( Count );
        CPPUNIT_TEST( Labels );
#if wxUSE_INTL
        CPPUNIT_TEST( TranslatedMnemonics );
#endif // wxUSE_INTL
        CPPUNIT_TEST( RadioItems );
        CPPUNIT_TEST( RemoveAdd );
        CPPUNIT_TEST( ChangeBitmap );
        WXUISIM_TEST( Events );
    CPPUNIT_TEST_SUITE_END();

    void CreateFrame();

    void FindInMenubar();
    void FindInMenu();
    void EnableTop();
    void Count();
    void Labels();
#if wxUSE_INTL
    void TranslatedMnemonics();
#endif // wxUSE_INTL
    void RadioItems();
    void RemoveAdd();
    void ChangeBitmap();
    void Events();

    wxFrame* m_frame;

    // Holds the number of menuitems contained in all the menus
    size_t m_itemCount;

    // Store here the id of a known submenu item, to be searched for later
    int m_submenuItemId;

    // and a sub-submenu item
    int m_subsubmenuItemId;

    wxArrayString m_menuLabels;

    // The menu containing the item with MenuTestCase_Bar id.
    wxMenu* m_menuWithBar;

    wxDECLARE_NO_COPY_CLASS(MenuTestCase);
};

// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( MenuTestCase );

// also include in its own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( MenuTestCase, "MenuTestCase" );

void MenuTestCase::CreateFrame()
{
    m_frame = new wxFrame(wxTheApp->GetTopWindow(), wxID_ANY, "test frame");

    wxMenu *fileMenu = new wxMenu;
    wxMenu *helpMenu = new wxMenu;
    wxMenu *subMenu = new wxMenu;
    wxMenu *subsubMenu = new wxMenu;

    m_itemCount = 0;

    PopulateMenu(subsubMenu, "Subsubmenu item ", m_itemCount);

    // Store one of its IDs for later
    m_subsubmenuItemId = MenuTestCase_First + m_itemCount - 2;

    PopulateMenu(subMenu, "Submenu item ", m_itemCount);

    // Store one of its IDs for later
    m_submenuItemId = MenuTestCase_First + m_itemCount - 2;

    subMenu->AppendSubMenu(subsubMenu, "Subsubmen&u", "Test a subsubmenu");
    m_itemCount++;

    // Check GetTitle() returns the correct string _before_ appending to the bar
    fileMenu->SetTitle("&Foo\tCtrl-F");
    CPPUNIT_ASSERT_EQUAL( "&Foo\tCtrl-F", fileMenu->GetTitle() );

    PopulateMenu(fileMenu, "Filemenu item ", m_itemCount);

    fileMenu->Append(MenuTestCase_Foo, "&Foo\tCtrl-F", "Test item to be found");
    m_itemCount++;
    fileMenu->Append(MenuTestCase_SelectAll, "Select &all\tCtrl-A",
                     "Accelerator conflicting with wxTextCtrl");
    m_itemCount++;

    // Test adding an extra accelerator to the item before adding it to the menu.
    wxAcceleratorEntry entry;

    wxMenuItem* const
        extraAccel = new wxMenuItem(fileMenu, MenuTestCase_ExtraAccel, "Extra accels");

    CHECK( entry.FromString("Ctrl-U") );
    extraAccel->SetAccel(&entry);

    CHECK( entry.FromString("Ctrl-V") );
    extraAccel->AddExtraAccel(entry);

    fileMenu->Append(extraAccel);
    m_itemCount++;

    // And now also test adding 2 of them after creating the initial menu item.
    wxMenuItem* const
        extraAccels = fileMenu->Append(MenuTestCase_ExtraAccels, "Extra accels 2");
    m_itemCount++;

    CHECK( entry.FromString("Ctrl-T") );
    extraAccels->AddExtraAccel(entry);

    CHECK(entry.FromString("Shift-Ctrl-W"));
    extraAccels->AddExtraAccel(entry);

    CHECK(entry.FromString("Ctrl-W"));
    extraAccels->SetAccel(&entry);

    PopulateMenu(helpMenu, "Helpmenu item ", m_itemCount);
    helpMenu->Append(MenuTestCase_Bar, "Bar\tF1");
    m_itemCount++;
    m_menuWithBar = helpMenu;
    helpMenu->AppendSubMenu(subMenu, "Sub&menu", "Test a submenu");
    m_itemCount++;

    // Use an arraystring here, to help with future tests
    m_menuLabels.Add("&File");
    m_menuLabels.Add("&Help");

    wxMenuBar *menuBar = new wxMenuBar();
    menuBar->Append(fileMenu, m_menuLabels[0]);
    menuBar->Append(helpMenu, m_menuLabels[1]);
    m_frame->SetMenuBar(menuBar);
}

void MenuTestCase::FindInMenubar()
{
    wxMenuBar* bar = m_frame->GetMenuBar();

    // Find by name:
    CPPUNIT_ASSERT( bar->FindMenu("File") != wxNOT_FOUND );
    CPPUNIT_ASSERT( bar->FindMenu("&File") != wxNOT_FOUND );
    CPPUNIT_ASSERT( bar->FindMenu("&Fail") == wxNOT_FOUND );

    // Find by menu name plus item name:
    CPPUNIT_ASSERT( bar->FindMenuItem("File", "Foo") != wxNOT_FOUND );
    CPPUNIT_ASSERT( bar->FindMenuItem("&File", "&Foo") != wxNOT_FOUND );
    // and using the menu label
    int index = bar->FindMenu("&File");
    CPPUNIT_ASSERT( index != wxNOT_FOUND );
    wxString menulabel = bar->GetMenuLabel(index);
    CPPUNIT_ASSERT( bar->FindMenuItem(menulabel, "&Foo") != wxNOT_FOUND );
    // and title
    wxString menutitle = bar->GetMenu(index)->GetTitle();
    CPPUNIT_ASSERT( bar->FindMenuItem(menutitle, "&Foo") != wxNOT_FOUND );

    // Find by position:
    for (size_t n=0; n < bar->GetMenuCount(); ++n)
    {
        CPPUNIT_ASSERT( bar->GetMenu(n) );
    }

    // Find by id:
    wxMenu* menu = nullptr;
    wxMenuItem* item = nullptr;
    item = bar->FindItem(MenuTestCase_Foo, &menu);
    CPPUNIT_ASSERT( item );
    CPPUNIT_ASSERT( menu );
    // Check that the correct menu was found
    CPPUNIT_ASSERT( menu->FindChildItem(MenuTestCase_Foo) );

    // Find submenu item:
    item = bar->FindItem(m_submenuItemId, &menu);
    CPPUNIT_ASSERT( item );
    CPPUNIT_ASSERT( menu );
    // and, for completeness, a subsubmenu one:
    item = bar->FindItem(m_subsubmenuItemId, &menu);
    CPPUNIT_ASSERT( item );
    CPPUNIT_ASSERT( menu );
}

void MenuTestCase::FindInMenu()
{
    wxMenuBar* bar = m_frame->GetMenuBar();

    // Find by name:
    wxMenu* menuFind = bar->GetMenu(0);
    CPPUNIT_ASSERT( menuFind->FindItem("Foo") != wxNOT_FOUND );
    CPPUNIT_ASSERT( menuFind->FindItem("&Foo") != wxNOT_FOUND );
    // and for submenus
    wxMenu* menuHelp = bar->GetMenu(1);
    CPPUNIT_ASSERT( menuHelp->FindItem("Submenu") != wxNOT_FOUND );
    CPPUNIT_ASSERT( menuHelp->FindItem("Sub&menu") != wxNOT_FOUND );

    // Find by position:
    size_t n;
    for (n=0; n < menuHelp->GetMenuItemCount(); ++n)
    {
        CPPUNIT_ASSERT( menuHelp->FindItemByPosition(n) );
    }

    // Find by id:
    CPPUNIT_ASSERT( menuHelp->FindItem(MenuTestCase_Bar) );
    CPPUNIT_ASSERT( !menuHelp->FindItem(MenuTestCase_Foo) );

    for (n=0; n < menuHelp->GetMenuItemCount(); ++n)
    {
        size_t locatedAt;
        wxMenuItem* itemByPos = menuHelp->FindItemByPosition(n);
        CPPUNIT_ASSERT( itemByPos );
        wxMenuItem* itemById = menuHelp->FindChildItem(itemByPos->GetId(), &locatedAt);
        CPPUNIT_ASSERT_EQUAL( itemByPos, itemById );
        CPPUNIT_ASSERT_EQUAL( locatedAt, n );
    }

    // Find submenu item:
    for (n=0; n < menuHelp->GetMenuItemCount(); ++n)
    {
        wxMenuItem* item = menuHelp->FindItemByPosition(n);
        if (item->IsSubMenu())
        {
            wxMenu* submenu;
            wxMenuItem* submenuItem = menuHelp->FindItem(m_submenuItemId, &submenu);
            CPPUNIT_ASSERT( submenuItem );
            CPPUNIT_ASSERT( item->GetSubMenu() == submenu );
        }
    }
}

void MenuTestCase::EnableTop()
{
    wxMenuBar* const bar = m_frame->GetMenuBar();
    CPPUNIT_ASSERT( bar->IsEnabledTop(0) );
    bar->EnableTop( 0, false );
    CPPUNIT_ASSERT( !bar->IsEnabledTop(0) );
    bar->EnableTop( 0, true );
    CPPUNIT_ASSERT( bar->IsEnabledTop(0) );
}

void MenuTestCase::Count()
{
    wxMenuBar* bar = m_frame->GetMenuBar();
    // I suppose you could call this "counting menubars" :)
    CPPUNIT_ASSERT( bar );

    CPPUNIT_ASSERT_EQUAL( bar->GetMenuCount(), 2 );

    size_t count = 0;
    for (size_t n=0; n < bar->GetMenuCount(); ++n)
    {
        RecursivelyCountMenuItems(bar->GetMenu(n), count);
    }
    CPPUNIT_ASSERT_EQUAL( count, m_itemCount );
}

void MenuTestCase::Labels()
{
    wxMenuBar* bar = m_frame->GetMenuBar();
    CPPUNIT_ASSERT( bar );
    wxMenu* filemenu;
    wxMenuItem* itemFoo = bar->FindItem(MenuTestCase_Foo, &filemenu);
    CPPUNIT_ASSERT( itemFoo );
    CPPUNIT_ASSERT( filemenu );

    // These return labels including mnemonics/accelerators:

    // wxMenuBar
    CPPUNIT_ASSERT_EQUAL( "&File", bar->GetMenuLabel(0) );
    CPPUNIT_ASSERT_EQUAL( "&Foo\tCtrl-F", bar->GetLabel(MenuTestCase_Foo) );

    // wxMenu
    CPPUNIT_ASSERT_EQUAL( "&File", filemenu->GetTitle() );
    CPPUNIT_ASSERT_EQUAL( "&Foo\tCtrl-F", filemenu->GetLabel(MenuTestCase_Foo) );

    // wxMenuItem
    CPPUNIT_ASSERT_EQUAL( "&Foo\tCtrl-F", itemFoo->GetItemLabel() );

    // These return labels stripped of mnemonics/accelerators:

    // wxMenuBar
    CPPUNIT_ASSERT_EQUAL( "File", bar->GetMenuLabelText(0) );

    // wxMenu
    CPPUNIT_ASSERT_EQUAL( "Foo", filemenu->GetLabelText(MenuTestCase_Foo) );

    // wxMenuItem
    CPPUNIT_ASSERT_EQUAL( "Foo", itemFoo->GetItemLabelText() );
    CPPUNIT_ASSERT_EQUAL( "Foo", wxMenuItem::GetLabelText("&Foo\tCtrl-F") );
}

#if wxUSE_INTL

static wxString
GetTranslatedString(const wxTranslations& trans, const wxString& s)
{
    const wxString* t = trans.GetTranslatedString(s);
    return t ? *t : s;
}

void MenuTestCase::TranslatedMnemonics()
{
    // Check that appended mnemonics are correctly stripped;
    // see https://github.com/wxWidgets/wxWidgets/issues/16736
    wxTranslations trans;
    trans.SetLanguage(wxLANGUAGE_JAPANESE);
    wxFileTranslationsLoader::AddCatalogLookupPathPrefix("./intl");
    CPPUNIT_ASSERT( trans.AddCatalog("internat") );

    // Check the translation is being used:
    CPPUNIT_ASSERT( wxString("&File") != GetTranslatedString(trans, "&File") );

    wxString filemenu = m_frame->GetMenuBar()->GetMenuLabel(0);
    CPPUNIT_ASSERT_EQUAL
    (
         wxStripMenuCodes(GetTranslatedString(trans, "&File"), wxStrip_Menu),
         wxStripMenuCodes(GetTranslatedString(trans, filemenu), wxStrip_Menu)
    );

    // Test strings that have shortcuts. Duplicate non-mnemonic translations
    // exist for both "Edit" and "View", for ease of comparison
    CPPUNIT_ASSERT_EQUAL
    (
         GetTranslatedString(trans, "Edit"),
         wxStripMenuCodes(GetTranslatedString(trans, "E&dit\tCtrl+E"), wxStrip_Menu)
    );

    // "Vie&w" also has a space before the (&W)
    CPPUNIT_ASSERT_EQUAL
    (
         GetTranslatedString(trans, "View"),
         wxStripMenuCodes(GetTranslatedString(trans, "Vie&w\tCtrl+V"), wxStrip_Menu)
    );

    // Test a 'normal' mnemonic too: the translation is "Preten&d"
    CPPUNIT_ASSERT_EQUAL
    (
         "Pretend",
         wxStripMenuCodes(GetTranslatedString(trans, "B&ogus"), wxStrip_Menu)
    );
}
#endif // wxUSE_INTL

void MenuTestCase::RadioItems()
{
    wxMenuBar * const bar = m_frame->GetMenuBar();
    wxMenu * const menu = new wxMenu;
    bar->Append(menu, "&Radio");

    // Adding consecutive radio items creates a radio group.
    menu->AppendRadioItem(MenuTestCase_First, "Radio 0");
    menu->AppendRadioItem(MenuTestCase_First + 1, "Radio 1");

    // First item of a radio group is checked by default.
    CPPUNIT_ASSERT( menu->IsChecked(MenuTestCase_First) );

    // Subsequent items in a group are not checked.
    CPPUNIT_ASSERT( !menu->IsChecked(MenuTestCase_First + 1) );

    // Checking the second one make the first one unchecked however.
    menu->Check(MenuTestCase_First + 1, true);
    CPPUNIT_ASSERT( !menu->IsChecked(MenuTestCase_First) );
    CPPUNIT_ASSERT( menu->IsChecked(MenuTestCase_First + 1) );
    menu->Check(MenuTestCase_First, true);

    // Adding more radio items after a separator creates another radio group...
    menu->AppendSeparator();
    menu->AppendRadioItem(MenuTestCase_First + 2, "Radio 2");
    menu->AppendRadioItem(MenuTestCase_First + 3, "Radio 3");
    menu->AppendRadioItem(MenuTestCase_First + 4, "Radio 4");

    // ... which is independent from the first one.
    CPPUNIT_ASSERT( menu->IsChecked(MenuTestCase_First) );
    CPPUNIT_ASSERT( menu->IsChecked(MenuTestCase_First + 2) );

    menu->Check(MenuTestCase_First + 3, true);
    CPPUNIT_ASSERT( menu->IsChecked(MenuTestCase_First + 3) );
    CPPUNIT_ASSERT( !menu->IsChecked(MenuTestCase_First + 2) );

    CPPUNIT_ASSERT( menu->IsChecked(MenuTestCase_First) );
    menu->Check(MenuTestCase_First + 2, true);

    // Insert an item in the middle of an existing radio group.
    menu->InsertRadioItem(4, MenuTestCase_First + 5, "Radio 5");
    CPPUNIT_ASSERT( menu->IsChecked(MenuTestCase_First + 2) );
    CPPUNIT_ASSERT( !menu->IsChecked(MenuTestCase_First + 5) );

    menu->Check( MenuTestCase_First + 5, true );
    CPPUNIT_ASSERT( !menu->IsChecked(MenuTestCase_First + 3) );

    menu->Check( MenuTestCase_First + 3, true );

    // Prepend a couple of items before the first group.
    menu->PrependRadioItem(MenuTestCase_First + 6, "Radio 6");
    menu->PrependRadioItem(MenuTestCase_First + 7, "Radio 7");
    CPPUNIT_ASSERT( !menu->IsChecked(MenuTestCase_First + 6) );
    CPPUNIT_ASSERT( !menu->IsChecked(MenuTestCase_First + 7) );

    menu->Check(MenuTestCase_First + 7, true);
    CPPUNIT_ASSERT( !menu->IsChecked(MenuTestCase_First + 1) );


    // Check that the last radio group still works as expected.
    menu->Check(MenuTestCase_First + 4, true);
    CPPUNIT_ASSERT( !menu->IsChecked(MenuTestCase_First + 5) );
}

void MenuTestCase::RemoveAdd()
{
    wxMenuBar* bar = m_frame->GetMenuBar();

    wxMenu* menu0 = bar->GetMenu(0);
    wxMenu* menu1 = bar->GetMenu(1);
    wxMenuItem* item = new wxMenuItem(menu0, MenuTestCase_Foo + 100, "t&ext\tCtrl-E");
    menu0->Insert(0, item);
    CPPUNIT_ASSERT( menu0->FindItemByPosition(0) == item );
    menu0->Remove(item);
    CPPUNIT_ASSERT( menu0->FindItemByPosition(0) != item );
    menu1->Insert(0, item);
    CPPUNIT_ASSERT( menu1->FindItemByPosition(0) == item );
    menu1->Remove(item);
    CPPUNIT_ASSERT( menu1->FindItemByPosition(0) != item );
    menu0->Insert(0, item);
    CPPUNIT_ASSERT( menu0->FindItemByPosition(0) == item );
    menu0->Delete(item);
}

void MenuTestCase::ChangeBitmap()
{
    wxMenu *menu = new wxMenu;

    wxMenuItem *item = new wxMenuItem(menu, wxID_ANY, "Item");
    menu->Append(item);

    // On Windows Vista (and later) calling SetBitmap, *after* the menu
    // item has already been added, used to result in a stack overflow:
    // [Do]SetBitmap can call GetHBitmapForMenu which will call SetBitmap
    // again etc...
    item->SetBitmap( wxBitmap(1, 1) );


    // Force owner drawn usage by having a bitmap that's wider than the
    // standard size. This results in rearranging the parent menu which
    // hasn't always worked properly and lead to a null pointer exception.
    item->SetBitmap( wxBitmap(512, 1) );

    wxDELETE(menu);
}

#if wxUSE_UIACTIONSIMULATOR

// In C++98 this class can't be defined inside Events() method, unfortunately,
// as its OnMenu() method wouldn't be usable with template Bind() then.
class MenuEventHandler : public wxEvtHandler
{
public:
    MenuEventHandler(wxWindow* win)
        : m_win(win)
    {
        m_win->Bind(wxEVT_MENU, &MenuEventHandler::OnMenu, this);

        m_event = nullptr;
    }

    virtual ~MenuEventHandler()
    {
        m_win->Unbind(wxEVT_MENU, &MenuEventHandler::OnMenu, this);

        delete m_event;
    }

    // Check that we received an event with the given ID and return the event
    // object if we did (otherwise fail the test and return nullptr).
    const wxObject* CheckGot(int expectedId)
    {
        if ( !m_event )
        {
            FAIL("Event not generated");
            return nullptr;
        }

        CHECK( m_event->GetId() == expectedId );

        const wxObject* const src = m_event->GetEventObject();

        delete m_event;
        m_event = nullptr;

        return src;
    }

    bool GotEvent() const
    {
        return m_event != nullptr;
    }

private:
    void OnMenu(wxCommandEvent& event)
    {
        CHECK( !m_event );

        m_event = static_cast<wxCommandEvent*>(event.Clone());
    }

    wxWindow* const m_win;
    wxCommandEvent* m_event;
};

#endif // wxUSE_UIACTIONSIMULATOR

void MenuTestCase::Events()
{
#if wxUSE_UIACTIONSIMULATOR
    MenuEventHandler handler(m_frame);

    // Invoke the accelerator.
    m_frame->Show();
    m_frame->SetFocus();

    // Wait for m_frame to become focused. Because (at least under wxQt when running
    // the entire test suite) the first test below would fail due to the simulation
    // starts before the frame become focused.
    WaitFor("the frame to become focused", [this]() {
        return m_frame->HasFocus();
    });

    wxUIActionSimulator sim;
    sim.KeyDown(WXK_F1);
    sim.KeyUp(WXK_F1);
    wxYield();

    INFO("Expecting event for F1");
    if ( const wxObject* const src = handler.CheckGot(MenuTestCase_Bar) )
    {
        CHECK( wxString(src->GetClassInfo()->GetClassName()) == "wxMenu" );
        CHECK( src == m_menuWithBar );
    }

    // Invoke another accelerator, it should also work.
    sim.Char('A', wxMOD_CONTROL);
    wxYield();

    INFO("Expecting event for Ctrl-A");
    handler.CheckGot(MenuTestCase_SelectAll);

    // Invoke accelerator and extra accelerators, all of them should work.
    sim.Char('U', wxMOD_CONTROL);
    wxYield();
    INFO("Expecting event for Ctrl-U");
    handler.CheckGot(MenuTestCase_ExtraAccel);

    sim.Char('V', wxMOD_CONTROL);
    wxYield();
    INFO("Expecting event for Ctrl-V");
    handler.CheckGot(MenuTestCase_ExtraAccel);

    sim.Char('W', wxMOD_CONTROL);
    wxYield();
    INFO("Expecting event for Ctrl-W");
    handler.CheckGot(MenuTestCase_ExtraAccels);

    sim.Char('T', wxMOD_CONTROL);
    wxYield();
    INFO("Expecting event for Ctrl-T");
    handler.CheckGot(MenuTestCase_ExtraAccels);

    sim.Char('W', wxMOD_CONTROL | wxMOD_SHIFT);
    wxYield();
    INFO("Expecting event for Ctrl-Shift-W");
    handler.CheckGot(MenuTestCase_ExtraAccels);

    // Now create a text control which uses the same accelerator for itself and
    // check that when the text control has focus, the accelerator does _not_
    // work.
    wxTextCtrl* const text = new wxTextCtrl(m_frame, wxID_ANY, "Testing");
    text->SetFocus();

    sim.Char('A', wxMOD_CONTROL);
    wxYield();

    CHECK( !handler.GotEvent() );
#endif // wxUSE_UIACTIONSIMULATOR
}

namespace
{

void VerifyAccelAssigned( wxString labelText, int keycode )
{
    const std::unique_ptr<wxAcceleratorEntry> entry(
        wxAcceleratorEntry::Create( labelText )
    );

    CHECK( entry );
    CHECK( entry->GetKeyCode() == keycode );
}

struct key
{
    int      keycode;
    wxString name;
    bool     skip;
};
key modKeys[] =
{
    { wxACCEL_NORMAL, "Normal", false },
    { wxACCEL_CTRL,   "Ctrl",   false },
    { wxACCEL_SHIFT,  "Shift",  false },
    { wxACCEL_ALT,    "Alt",    false }
};
/*
 The keys marked as skip below are not supported as accelerator
 keys on GTK.
 */
key specialKeys[] =
{
    { WXK_F1,               "WXK_F1",               false },
    { WXK_F2,               "WXK_F2",               false },
    { WXK_F3,               "WXK_F3",               false },
    { WXK_F4,               "WXK_F4",               false },
    { WXK_F5,               "WXK_F5",               false },
    { WXK_F6,               "WXK_F6",               false },
    { WXK_F7,               "WXK_F7",               false },
    { WXK_F8,               "WXK_F8",               false },
    { WXK_F9,               "WXK_F9",               false },
    { WXK_F10,              "WXK_F10",              false },
    { WXK_F11,              "WXK_F11",              false },
    { WXK_F12,              "WXK_F12",              false },
    { WXK_F13,              "WXK_F13",              false },
    { WXK_F14,              "WXK_F14",              false },
    { WXK_F15,              "WXK_F15",              false },
    { WXK_F16,              "WXK_F16",              false },
    { WXK_F17,              "WXK_F17",              false },
    { WXK_F18,              "WXK_F18",              false },
    { WXK_F19,              "WXK_F19",              false },
    { WXK_F20,              "WXK_F20",              false },
    { WXK_F21,              "WXK_F21",              false },
    { WXK_F22,              "WXK_F22",              false },
    { WXK_F23,              "WXK_F23",              false },
    { WXK_F24,              "WXK_F24",              false },
    { WXK_INSERT,           "WXK_INSERT",           false },
    { WXK_DELETE,           "WXK_DELETE",           false },
    { WXK_UP,               "WXK_UP",               false },
    { WXK_DOWN,             "WXK_DOWN",             false },
    { WXK_PAGEUP,           "WXK_PAGEUP",           false },
    { WXK_PAGEDOWN,         "WXK_PAGEDOWN",         false },
    { WXK_LEFT,             "WXK_LEFT",             false },
    { WXK_RIGHT,            "WXK_RIGHT",            false },
    { WXK_HOME,             "WXK_HOME",             false },
    { WXK_END,              "WXK_END",              false },
    { WXK_RETURN,           "WXK_RETURN",           false },
    { WXK_BACK,             "WXK_BACK",             false },
    { WXK_TAB,              "WXK_TAB",              true },
    { WXK_ESCAPE,           "WXK_ESCAPE",           false },
    { WXK_SPACE,            "WXK_SPACE",            false },
    { WXK_MULTIPLY,         "WXK_MULTIPLY",         false },
    { WXK_ADD,              "WXK_ADD",              true },
    { WXK_SEPARATOR,        "WXK_SEPARATOR",        true },
    { WXK_SUBTRACT,         "WXK_SUBTRACT",         true },
    { WXK_DECIMAL,          "WXK_DECIMAL",          true },
    { WXK_DIVIDE,           "WXK_DIVIDE",           true },
    { WXK_CANCEL,           "WXK_CANCEL",           false },
    { WXK_CLEAR,            "WXK_CLEAR",            false },
    { WXK_MENU,             "WXK_MENU",             false },
    { WXK_PAUSE,            "WXK_PAUSE",            false },
    { WXK_CAPITAL,          "WXK_CAPITAL",          true },
    { WXK_SELECT,           "WXK_SELECT",           false },
    { WXK_PRINT,            "WXK_PRINT",            false },
    { WXK_EXECUTE,          "WXK_EXECUTE",          false },
    { WXK_SNAPSHOT,         "WXK_SNAPSHOT",         true },
    { WXK_HELP,             "WXK_HELP",             false },
    { WXK_NUMLOCK,          "WXK_NUMLOCK",          true },
    { WXK_SCROLL,           "WXK_SCROLL",           true },
    { WXK_NUMPAD_INSERT,    "WXK_NUMPAD_INSERT",    false },
    { WXK_NUMPAD_DELETE,    "WXK_NUMPAD_DELETE",    false },
    { WXK_NUMPAD_SPACE,     "WXK_NUMPAD_SPACE",     false },
    { WXK_NUMPAD_TAB,       "WXK_NUMPAD_TAB",       true },
    { WXK_NUMPAD_ENTER,     "WXK_NUMPAD_ENTER",     false },
    { WXK_NUMPAD_F1,        "WXK_NUMPAD_F1",        false },
    { WXK_NUMPAD_F2,        "WXK_NUMPAD_F2",        false },
    { WXK_NUMPAD_F3,        "WXK_NUMPAD_F3",        false },
    { WXK_NUMPAD_F4,        "WXK_NUMPAD_F4",        false },
    { WXK_NUMPAD_HOME,      "WXK_NUMPAD_HOME",      false },
    { WXK_NUMPAD_LEFT,      "WXK_NUMPAD_LEFT",      false },
    { WXK_NUMPAD_UP,        "WXK_NUMPAD_UP",        false },
    { WXK_NUMPAD_RIGHT,     "WXK_NUMPAD_RIGHT",     false },
    { WXK_NUMPAD_DOWN,      "WXK_NUMPAD_DOWN",      false },
    { WXK_NUMPAD_PAGEUP,    "WXK_NUMPAD_PAGEUP",    false },
    { WXK_NUMPAD_PAGEDOWN,  "WXK_NUMPAD_PAGEDOWN",  false },
    { WXK_NUMPAD_END,       "WXK_NUMPAD_END",       false },
    { WXK_NUMPAD_BEGIN,     "WXK_NUMPAD_BEGIN",     false },
    { WXK_NUMPAD_EQUAL,     "WXK_NUMPAD_EQUAL",     false },
    { WXK_NUMPAD_MULTIPLY,  "WXK_NUMPAD_MULTIPLY",  false },
    { WXK_NUMPAD_ADD,       "WXK_NUMPAD_ADD",       false },
    { WXK_NUMPAD_SEPARATOR, "WXK_NUMPAD_SEPARATOR", false },
    { WXK_NUMPAD_SUBTRACT,  "WXK_NUMPAD_SUBTRACT",  false },
    { WXK_NUMPAD_DECIMAL,   "WXK_NUMPAD_DECIMAL",   false },
    { WXK_NUMPAD_DIVIDE,    "WXK_NUMPAD_DIVIDE",    false },
    { WXK_NUMPAD0,          "WXK_NUMPAD0",          false },
    { WXK_NUMPAD1,          "WXK_NUMPAD1",          false },
    { WXK_NUMPAD2,          "WXK_NUMPAD2",          false },
    { WXK_NUMPAD3,          "WXK_NUMPAD3",          false },
    { WXK_NUMPAD4,          "WXK_NUMPAD4",          false },
    { WXK_NUMPAD5,          "WXK_NUMPAD5",          false },
    { WXK_NUMPAD6,          "WXK_NUMPAD6",          false },
    { WXK_NUMPAD7,          "WXK_NUMPAD7",          false },
    { WXK_NUMPAD8,          "WXK_NUMPAD8",          false },
    { WXK_NUMPAD9,          "WXK_NUMPAD9",          false },
    { WXK_WINDOWS_LEFT,     "WXK_WINDOWS_LEFT",     true },
    { WXK_WINDOWS_RIGHT,    "WXK_WINDOWS_RIGHT",    true },
    { WXK_WINDOWS_MENU,     "WXK_WINDOWS_MENU",     false },
    { WXK_COMMAND,          "WXK_COMMAND",          true }
};

}

TEST_CASE( "wxMenuItemAccelEntry", "[menu][accelentry]" )
{
    wxMenu* menu = new wxMenu;

    menu->Append( wxID_ANY, "Test" );
    wxMenuItem* item = menu->FindItemByPosition( 0 );

    SECTION( "Modifier keys" )
    {
        for ( unsigned i = 0; i < WXSIZEOF(modKeys); i++ )
        {
            const key& k = modKeys[i];

            INFO( wxString::Format( "Modifier: %s",  k.name ) );
            wxAcceleratorEntry accelEntry( k.keycode, 'A' , wxID_ANY, item );
            item->SetAccel( &accelEntry );

            wxString labelText = item->GetItemLabel();
            INFO( wxString::Format( "Label text: %s", labelText ) );

            VerifyAccelAssigned( labelText, 'A' );
        }
    }

    SECTION( "Special keys" )
    {
        for ( unsigned i = 0; i < WXSIZEOF(specialKeys); i++ )
        {
            const key& k = specialKeys[i];

            if( k.skip )
                continue;

            INFO( wxString::Format( "Keycode: %s",  k.name ) );
            wxAcceleratorEntry accelEntry( wxACCEL_CTRL, k.keycode, wxID_ANY, item );
            item->SetAccel( &accelEntry );

            wxString labelText = item->GetItemLabel();
            INFO( wxString::Format( "Label text: %s", labelText ) );

            VerifyAccelAssigned( labelText, k.keycode );
        }
    }
}

#endif
