menu.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Name: tests/menu/menu.cpp
  3. // Purpose: wxMenu unit test
  4. // Author: wxWidgets team
  5. // Created: 2010-11-10
  6. // Copyright: (c) 2010 wxWidgets team
  7. ///////////////////////////////////////////////////////////////////////////////
  8. // ----------------------------------------------------------------------------
  9. // headers
  10. // ----------------------------------------------------------------------------
  11. #include "testprec.h"
  12. #ifdef __BORLANDC__
  13. #pragma hdrstop
  14. #endif
  15. #ifndef WX_PRECOMP
  16. #include "wx/wx.h"
  17. #endif // WX_PRECOMP
  18. #include "wx/menu.h"
  19. #include "wx/uiaction.h"
  20. #include <stdarg.h>
  21. // ----------------------------------------------------------------------------
  22. // helper
  23. // ----------------------------------------------------------------------------
  24. namespace
  25. {
  26. enum
  27. {
  28. MenuTestCase_Foo = 10000,
  29. MenuTestCase_Bar,
  30. MenuTestCase_First
  31. };
  32. void PopulateMenu(wxMenu* menu, const wxString& name, size_t& itemcount)
  33. {
  34. // Start at item 1 to make it human-readable ;)
  35. for (int n=1; n<6; ++n, ++itemcount)
  36. {
  37. wxString label = name; label << n;
  38. menu->Append(MenuTestCase_First + itemcount, label, label + " help string");
  39. }
  40. }
  41. void RecursivelyCountMenuItems(const wxMenu* menu, size_t& count)
  42. {
  43. CPPUNIT_ASSERT( menu );
  44. count += menu->GetMenuItemCount();
  45. for (size_t n=0; n < menu->GetMenuItemCount(); ++n)
  46. {
  47. wxMenuItem* item = menu->FindItemByPosition(n);
  48. if (item->IsSubMenu())
  49. {
  50. RecursivelyCountMenuItems(item->GetSubMenu(), count);
  51. }
  52. }
  53. }
  54. } // anon namespace
  55. // ----------------------------------------------------------------------------
  56. // test class
  57. // ----------------------------------------------------------------------------
  58. class MenuTestCase : public CppUnit::TestCase
  59. {
  60. public:
  61. MenuTestCase() {}
  62. virtual void setUp() { CreateFrame(); }
  63. virtual void tearDown() { m_frame->Destroy(); }
  64. private:
  65. CPPUNIT_TEST_SUITE( MenuTestCase );
  66. CPPUNIT_TEST( FindInMenubar );
  67. CPPUNIT_TEST( FindInMenu );
  68. CPPUNIT_TEST( EnableTop );
  69. CPPUNIT_TEST( Count );
  70. CPPUNIT_TEST( Labels );
  71. CPPUNIT_TEST( RadioItems );
  72. CPPUNIT_TEST( RemoveAdd );
  73. WXUISIM_TEST( Events );
  74. CPPUNIT_TEST_SUITE_END();
  75. void CreateFrame();
  76. void FindInMenubar();
  77. void FindInMenu();
  78. void EnableTop();
  79. void Count();
  80. void Labels();
  81. void RadioItems();
  82. void RemoveAdd();
  83. void Events();
  84. wxFrame* m_frame;
  85. // Holds the number of menuitems contained in all the menus
  86. size_t m_itemCount;
  87. // Store here the id of a known submenu item, to be searched for later
  88. int m_submenuItemId;
  89. // and a sub-submenu item
  90. int m_subsubmenuItemId;
  91. wxArrayString m_menuLabels;
  92. // The menu containing the item with MenuTestCase_Bar id.
  93. wxMenu* m_menuWithBar;
  94. DECLARE_NO_COPY_CLASS(MenuTestCase)
  95. };
  96. // register in the unnamed registry so that these tests are run by default
  97. CPPUNIT_TEST_SUITE_REGISTRATION( MenuTestCase );
  98. // also include in its own registry so that these tests can be run alone
  99. CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( MenuTestCase, "MenuTestCase" );
  100. void MenuTestCase::CreateFrame()
  101. {
  102. m_frame = new wxFrame(NULL, wxID_ANY, "test frame");
  103. wxMenu *fileMenu = new wxMenu;
  104. wxMenu *helpMenu = new wxMenu;
  105. wxMenu *subMenu = new wxMenu;
  106. wxMenu *subsubMenu = new wxMenu;
  107. size_t itemcount = 0;
  108. PopulateMenu(subsubMenu, "Subsubmenu item ", itemcount);
  109. // Store one of its IDs for later
  110. m_subsubmenuItemId = MenuTestCase_First + itemcount - 2;
  111. PopulateMenu(subMenu, "Submenu item ", itemcount);
  112. // Store one of its IDs for later
  113. m_submenuItemId = MenuTestCase_First + itemcount - 2;
  114. subMenu->AppendSubMenu(subsubMenu, "Subsubmen&u", "Test a subsubmenu");
  115. // Check GetTitle() returns the correct string _before_ appending to the bar
  116. fileMenu->SetTitle("&Foo\tCtrl-F");
  117. CPPUNIT_ASSERT_EQUAL( "&Foo\tCtrl-F", fileMenu->GetTitle() );
  118. PopulateMenu(fileMenu, "Filemenu item ", itemcount);
  119. fileMenu->Append(MenuTestCase_Foo, "&Foo\tCtrl-F", "Test item to be found");
  120. PopulateMenu(helpMenu, "Helpmenu item ", itemcount);
  121. helpMenu->Append(MenuTestCase_Bar, "Bar\tF1");
  122. m_menuWithBar = helpMenu;
  123. helpMenu->AppendSubMenu(subMenu, "Sub&menu", "Test a submenu");
  124. // +2 for "Foo" and "Bar", +2 for the 2 submenus
  125. m_itemCount = itemcount + 4;
  126. // Use an arraystring here, to help with future tests
  127. m_menuLabels.Add("&File");
  128. m_menuLabels.Add("&Help");
  129. wxMenuBar *menuBar = new wxMenuBar();
  130. menuBar->Append(fileMenu, m_menuLabels[0]);
  131. menuBar->Append(helpMenu, m_menuLabels[1]);
  132. m_frame->SetMenuBar(menuBar);
  133. }
  134. void MenuTestCase::FindInMenubar()
  135. {
  136. wxMenuBar* bar = m_frame->GetMenuBar();
  137. // Find by name:
  138. CPPUNIT_ASSERT( bar->FindMenu("File") != wxNOT_FOUND );
  139. CPPUNIT_ASSERT( bar->FindMenu("&File") != wxNOT_FOUND );
  140. CPPUNIT_ASSERT( bar->FindMenu("&Fail") == wxNOT_FOUND );
  141. // Find by menu name plus item name:
  142. CPPUNIT_ASSERT( bar->FindMenuItem("File", "Foo") != wxNOT_FOUND );
  143. CPPUNIT_ASSERT( bar->FindMenuItem("&File", "&Foo") != wxNOT_FOUND );
  144. // and using the menu label
  145. int index = bar->FindMenu("&File");
  146. CPPUNIT_ASSERT( index != wxNOT_FOUND );
  147. wxString menulabel = bar->GetMenuLabel(index);
  148. CPPUNIT_ASSERT( bar->FindMenuItem(menulabel, "&Foo") != wxNOT_FOUND );
  149. // and title
  150. wxString menutitle = bar->GetMenu(index)->GetTitle();
  151. CPPUNIT_ASSERT( bar->FindMenuItem(menutitle, "&Foo") != wxNOT_FOUND );
  152. // Find by position:
  153. for (size_t n=0; n < bar->GetMenuCount(); ++n)
  154. {
  155. CPPUNIT_ASSERT( bar->GetMenu(n) );
  156. }
  157. // Find by id:
  158. wxMenu* menu = NULL;
  159. wxMenuItem* item = NULL;
  160. item = bar->FindItem(MenuTestCase_Foo, &menu);
  161. CPPUNIT_ASSERT( item );
  162. CPPUNIT_ASSERT( menu );
  163. // Check that the correct menu was found
  164. CPPUNIT_ASSERT( menu->FindChildItem(MenuTestCase_Foo) );
  165. // Find submenu item:
  166. item = bar->FindItem(m_submenuItemId, &menu);
  167. CPPUNIT_ASSERT( item );
  168. CPPUNIT_ASSERT( menu );
  169. // and, for completeness, a subsubmenu one:
  170. item = bar->FindItem(m_subsubmenuItemId, &menu);
  171. CPPUNIT_ASSERT( item );
  172. CPPUNIT_ASSERT( menu );
  173. }
  174. void MenuTestCase::FindInMenu()
  175. {
  176. wxMenuBar* bar = m_frame->GetMenuBar();
  177. // Find by name:
  178. wxMenu* menuFind = bar->GetMenu(0);
  179. CPPUNIT_ASSERT( menuFind->FindItem("Foo") != wxNOT_FOUND );
  180. CPPUNIT_ASSERT( menuFind->FindItem("&Foo") != wxNOT_FOUND );
  181. // and for submenus
  182. wxMenu* menuHelp = bar->GetMenu(1);
  183. CPPUNIT_ASSERT( menuHelp->FindItem("Submenu") != wxNOT_FOUND );
  184. CPPUNIT_ASSERT( menuHelp->FindItem("Sub&menu") != wxNOT_FOUND );
  185. // Find by position:
  186. size_t n;
  187. for (n=0; n < menuHelp->GetMenuItemCount(); ++n)
  188. {
  189. CPPUNIT_ASSERT( menuHelp->FindItemByPosition(n) );
  190. }
  191. // Find by id:
  192. CPPUNIT_ASSERT( menuHelp->FindItem(MenuTestCase_Bar) );
  193. CPPUNIT_ASSERT( menuHelp->FindItem(MenuTestCase_Foo) == NULL );
  194. for (n=0; n < menuHelp->GetMenuItemCount(); ++n)
  195. {
  196. size_t locatedAt;
  197. wxMenuItem* itemByPos = menuHelp->FindItemByPosition(n);
  198. CPPUNIT_ASSERT( itemByPos );
  199. wxMenuItem* itemById = menuHelp->FindChildItem(itemByPos->GetId(), &locatedAt);
  200. CPPUNIT_ASSERT_EQUAL( itemByPos, itemById );
  201. CPPUNIT_ASSERT_EQUAL( locatedAt, n );
  202. }
  203. // Find submenu item:
  204. for (n=0; n < menuHelp->GetMenuItemCount(); ++n)
  205. {
  206. wxMenuItem* item = menuHelp->FindItemByPosition(n);
  207. if (item->IsSubMenu())
  208. {
  209. wxMenu* submenu;
  210. wxMenuItem* submenuItem = menuHelp->FindItem(m_submenuItemId, &submenu);
  211. CPPUNIT_ASSERT( submenuItem );
  212. CPPUNIT_ASSERT( item->GetSubMenu() == submenu );
  213. }
  214. }
  215. }
  216. void MenuTestCase::EnableTop()
  217. {
  218. wxMenuBar* const bar = m_frame->GetMenuBar();
  219. CPPUNIT_ASSERT( bar->IsEnabledTop(0) );
  220. bar->EnableTop( 0, false );
  221. CPPUNIT_ASSERT( !bar->IsEnabledTop(0) );
  222. bar->EnableTop( 0, true );
  223. CPPUNIT_ASSERT( bar->IsEnabledTop(0) );
  224. }
  225. void MenuTestCase::Count()
  226. {
  227. wxMenuBar* bar = m_frame->GetMenuBar();
  228. // I suppose you could call this "counting menubars" :)
  229. CPPUNIT_ASSERT( bar );
  230. CPPUNIT_ASSERT_EQUAL( bar->GetMenuCount(), 2 );
  231. size_t count = 0;
  232. for (size_t n=0; n < bar->GetMenuCount(); ++n)
  233. {
  234. RecursivelyCountMenuItems(bar->GetMenu(n), count);
  235. }
  236. CPPUNIT_ASSERT_EQUAL( count, m_itemCount );
  237. }
  238. void MenuTestCase::Labels()
  239. {
  240. wxMenuBar* bar = m_frame->GetMenuBar();
  241. CPPUNIT_ASSERT( bar );
  242. wxMenu* filemenu;
  243. wxMenuItem* itemFoo = bar->FindItem(MenuTestCase_Foo, &filemenu);
  244. CPPUNIT_ASSERT( itemFoo );
  245. CPPUNIT_ASSERT( filemenu );
  246. // These return labels including mnemonics/accelerators:
  247. // wxMenuBar
  248. CPPUNIT_ASSERT_EQUAL( "&File", bar->GetMenuLabel(0) );
  249. CPPUNIT_ASSERT_EQUAL( "&Foo\tCtrl-F", bar->GetLabel(MenuTestCase_Foo) );
  250. // wxMenu
  251. CPPUNIT_ASSERT_EQUAL( "&File", filemenu->GetTitle() );
  252. CPPUNIT_ASSERT_EQUAL( "&Foo\tCtrl-F", filemenu->GetLabel(MenuTestCase_Foo) );
  253. // wxMenuItem
  254. CPPUNIT_ASSERT_EQUAL( "&Foo\tCtrl-F", itemFoo->GetItemLabel() );
  255. // These return labels stripped of mnemonics/accelerators:
  256. // wxMenuBar
  257. CPPUNIT_ASSERT_EQUAL( "File", bar->GetMenuLabelText(0) );
  258. // wxMenu
  259. CPPUNIT_ASSERT_EQUAL( "Foo", filemenu->GetLabelText(MenuTestCase_Foo) );
  260. // wxMenuItem
  261. CPPUNIT_ASSERT_EQUAL( "Foo", itemFoo->GetItemLabelText() );
  262. CPPUNIT_ASSERT_EQUAL( "Foo", wxMenuItem::GetLabelText("&Foo\tCtrl-F") );
  263. }
  264. void MenuTestCase::RadioItems()
  265. {
  266. wxMenuBar * const bar = m_frame->GetMenuBar();
  267. wxMenu * const menu = new wxMenu;
  268. bar->Append(menu, "&Radio");
  269. // Adding consecutive radio items creates a radio group.
  270. menu->AppendRadioItem(MenuTestCase_First, "Radio 0");
  271. menu->AppendRadioItem(MenuTestCase_First + 1, "Radio 1");
  272. // First item of a radio group is checked by default.
  273. CPPUNIT_ASSERT( menu->IsChecked(MenuTestCase_First) );
  274. // Checking the second one make the first one unchecked however.
  275. menu->Check(MenuTestCase_First + 1, true);
  276. CPPUNIT_ASSERT( !menu->IsChecked(MenuTestCase_First) );
  277. CPPUNIT_ASSERT( menu->IsChecked(MenuTestCase_First + 1) );
  278. // Adding more radio items after a separator creates another radio group...
  279. menu->AppendSeparator();
  280. menu->AppendRadioItem(MenuTestCase_First + 2, "Radio 2");
  281. menu->AppendRadioItem(MenuTestCase_First + 3, "Radio 3");
  282. menu->AppendRadioItem(MenuTestCase_First + 4, "Radio 4");
  283. // ... which is independent from the first one.
  284. CPPUNIT_ASSERT( menu->IsChecked(MenuTestCase_First + 2) );
  285. menu->Check(MenuTestCase_First + 3, true);
  286. CPPUNIT_ASSERT( menu->IsChecked(MenuTestCase_First + 3) );
  287. CPPUNIT_ASSERT( !menu->IsChecked(MenuTestCase_First + 2) );
  288. CPPUNIT_ASSERT( menu->IsChecked(MenuTestCase_First + 1) );
  289. // Insert an item in the middle of an existing radio group.
  290. menu->InsertRadioItem(4, MenuTestCase_First + 5, "Radio 5");
  291. CPPUNIT_ASSERT( menu->IsChecked(MenuTestCase_First + 3) );
  292. menu->Check( MenuTestCase_First + 5, true );
  293. CPPUNIT_ASSERT( !menu->IsChecked(MenuTestCase_First + 3) );
  294. // Prepend a couple of items before the first group.
  295. menu->PrependRadioItem(MenuTestCase_First + 6, "Radio 6");
  296. menu->PrependRadioItem(MenuTestCase_First + 7, "Radio 7");
  297. menu->Check(MenuTestCase_First + 7, true);
  298. CPPUNIT_ASSERT( !menu->IsChecked(MenuTestCase_First + 1) );
  299. // Check that the last radio group still works as expected.
  300. menu->Check(MenuTestCase_First + 4, true);
  301. CPPUNIT_ASSERT( !menu->IsChecked(MenuTestCase_First + 5) );
  302. }
  303. void MenuTestCase::RemoveAdd()
  304. {
  305. wxMenuBar* bar = m_frame->GetMenuBar();
  306. wxMenu* menu0 = bar->GetMenu(0);
  307. wxMenu* menu1 = bar->GetMenu(1);
  308. wxMenuItem* item = new wxMenuItem(menu0, MenuTestCase_Foo + 100, "t&ext\tCtrl-E");
  309. menu0->Insert(0, item);
  310. CPPUNIT_ASSERT( menu0->FindItemByPosition(0) == item );
  311. menu0->Remove(item);
  312. CPPUNIT_ASSERT( menu0->FindItemByPosition(0) != item );
  313. menu1->Insert(0, item);
  314. CPPUNIT_ASSERT( menu1->FindItemByPosition(0) == item );
  315. menu1->Remove(item);
  316. CPPUNIT_ASSERT( menu1->FindItemByPosition(0) != item );
  317. menu0->Insert(0, item);
  318. CPPUNIT_ASSERT( menu0->FindItemByPosition(0) == item );
  319. menu0->Delete(item);
  320. }
  321. void MenuTestCase::Events()
  322. {
  323. #ifdef __WXGTK__
  324. // FIXME: For some reason, we sporadically fail to get the event in
  325. // buildbot slave builds even though the test always passes locally.
  326. // There is undoubtedly something wrong here but without being able
  327. // to debug it, I have no idea what is it, so let's just disable
  328. // this test when running under buildbot to let the entire test
  329. // suite pass.
  330. if ( IsAutomaticTest() )
  331. return;
  332. #endif // __WXGTK__
  333. #if wxUSE_UIACTIONSIMULATOR
  334. class MenuEventHandler : public wxEvtHandler
  335. {
  336. public:
  337. MenuEventHandler(wxWindow* win)
  338. : m_win(win)
  339. {
  340. m_win->Connect(wxEVT_MENU,
  341. wxCommandEventHandler(MenuEventHandler::OnMenu),
  342. NULL,
  343. this);
  344. m_gotEvent = false;
  345. m_event = NULL;
  346. }
  347. virtual ~MenuEventHandler()
  348. {
  349. m_win->Disconnect(wxEVT_MENU,
  350. wxCommandEventHandler(MenuEventHandler::OnMenu),
  351. NULL,
  352. this);
  353. delete m_event;
  354. }
  355. const wxCommandEvent& GetEvent()
  356. {
  357. CPPUNIT_ASSERT( m_gotEvent );
  358. m_gotEvent = false;
  359. return *m_event;
  360. }
  361. private:
  362. void OnMenu(wxCommandEvent& event)
  363. {
  364. CPPUNIT_ASSERT( !m_gotEvent );
  365. delete m_event;
  366. m_event = static_cast<wxCommandEvent*>(event.Clone());
  367. m_gotEvent = true;
  368. }
  369. wxWindow* const m_win;
  370. wxCommandEvent* m_event;
  371. bool m_gotEvent;
  372. };
  373. MenuEventHandler handler(m_frame);
  374. // Invoke the accelerator.
  375. m_frame->Show();
  376. m_frame->SetFocus();
  377. wxYield();
  378. wxUIActionSimulator sim;
  379. sim.KeyDown(WXK_F1);
  380. sim.KeyUp(WXK_F1);
  381. wxYield();
  382. const wxCommandEvent& ev = handler.GetEvent();
  383. CPPUNIT_ASSERT_EQUAL( static_cast<int>(MenuTestCase_Bar), ev.GetId() );
  384. wxObject* const src = ev.GetEventObject();
  385. CPPUNIT_ASSERT( src );
  386. CPPUNIT_ASSERT_EQUAL( "wxMenu",
  387. wxString(src->GetClassInfo()->GetClassName()) );
  388. CPPUNIT_ASSERT_EQUAL( static_cast<wxObject*>(m_menuWithBar),
  389. src );
  390. #endif // wxUSE_UIACTIONSIMULATOR
  391. }