mirror of
https://github.com/wxWidgets/wxWidgets.git
synced 2026-03-28 18:19:48 +08:00
The changes of 0dfe37a643 (Add wxMSW-specific wxBitmapBundle accessors
to wxMenuItem, 2025-08-18) broke this as wxMSW-specific function taking
bool hid the base class function.
Just make the wxMSW function compatible with the base class one by
defaulting "bChecked" to false. Alternatively, we could use using
declaration to bring the base class function in scope, but this doesn't
seem to have any advantages and this change seems more clear.
In any case, add a unit test checking that the calls to these functions
at least compile.
See #25685, #25705.
Closes #25719.
869 lines
28 KiB
C++
869 lines
28 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// 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 )
|
|
);
|
|
|
|
REQUIRE( 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;
|
|
|
|
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 );
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_CASE("wxMenuItemAccessors", "[menu]")
|
|
{
|
|
wxMenu menu;
|
|
|
|
wxMenuItem* const item = menu.AppendCheckItem(wxID_ANY, "Check");
|
|
|
|
// Just check that we can call various functions.
|
|
CHECK( !item->GetBitmapBundle().IsOk() );
|
|
CHECK( !item->GetBitmap().IsOk() );
|
|
|
|
#ifdef __WXMSW__
|
|
CHECK( !item->GetBitmapBundle(true).IsOk() );
|
|
CHECK( !item->GetBitmap(true).IsOk() );
|
|
#endif // __WXMSW__
|
|
}
|
|
|
|
#endif
|