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