]> git.saurik.com Git - wxWidgets.git/blob - src/msw/menu.cpp
Fixed crash-on-exit bug due to status bar being deleted twice (MDI apps);
[wxWidgets.git] / src / msw / menu.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: menu.cpp
3 // Purpose: wxMenu, wxMenuBar, wxMenuItem
4 // Author: Julian Smart
5 // Modified by: Vadim Zeitlin
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ===========================================================================
13 // declarations
14 // ===========================================================================
15
16 // ---------------------------------------------------------------------------
17 // headers
18 // ---------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "menu.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #ifndef WX_PRECOMP
32 #include "wx/frame.h"
33 #include "wx/menu.h"
34 #include "wx/utils.h"
35 #include "wx/intl.h"
36 #include "wx/log.h"
37 #endif
38
39 #if wxUSE_OWNER_DRAWN
40 #include "wx/ownerdrw.h"
41 #endif
42
43 #include "wx/msw/private.h"
44
45 // other standard headers
46 #include <string.h>
47
48 // ----------------------------------------------------------------------------
49 // global variables
50 // ----------------------------------------------------------------------------
51
52 extern wxMenu *wxCurrentPopupMenu;
53
54 // ----------------------------------------------------------------------------
55 // constants
56 // ----------------------------------------------------------------------------
57
58 // the (popup) menu title has this special id
59 static const int idMenuTitle = -2;
60
61 // ----------------------------------------------------------------------------
62 // macros
63 // ----------------------------------------------------------------------------
64
65 IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
66 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxWindow)
67
68 // ============================================================================
69 // implementation
70 // ============================================================================
71
72 // ---------------------------------------------------------------------------
73 // wxMenu construction, adding and removing menu items
74 // ---------------------------------------------------------------------------
75
76 // Construct a menu with optional title (then use append)
77 void wxMenu::Init()
78 {
79 m_doBreak = FALSE;
80
81 // create the menu
82 m_hMenu = (WXHMENU)CreatePopupMenu();
83 if ( !m_hMenu )
84 {
85 wxLogLastError("CreatePopupMenu");
86 }
87
88 // if we have a title, insert it in the beginning of the menu
89 if ( !!m_title )
90 {
91 Append(idMenuTitle, m_title);
92 AppendSeparator();
93 }
94 }
95
96 // The wxWindow destructor will take care of deleting the submenus.
97 wxMenu::~wxMenu()
98 {
99 // we should free Windows resources only if Windows doesn't do it for us
100 // which happens if we're attached to a menubar or a submenu of another
101 // menu
102 if ( !IsAttached() && !GetParent() )
103 {
104 if ( !::DestroyMenu(GetHmenu()) )
105 {
106 wxLogLastError("DestroyMenu");
107 }
108 }
109
110 #if wxUSE_ACCEL
111 // delete accels
112 WX_CLEAR_ARRAY(m_accels);
113 #endif // wxUSE_ACCEL
114 }
115
116 void wxMenu::Break()
117 {
118 // this will take effect during the next call to Append()
119 m_doBreak = TRUE;
120 }
121
122 #if wxUSE_ACCEL
123
124 int wxMenu::FindAccel(int id) const
125 {
126 size_t n, count = m_accels.GetCount();
127 for ( n = 0; n < count; n++ )
128 {
129 if ( m_accels[n]->m_command == id )
130 return n;
131 }
132
133 return wxNOT_FOUND;
134 }
135
136 void wxMenu::UpdateAccel(wxMenuItem *item)
137 {
138 // find the (new) accel for this item
139 wxAcceleratorEntry *accel = wxGetAccelFromString(item->GetText());
140 if ( accel )
141 accel->m_command = item->GetId();
142
143 // find the old one
144 int n = FindAccel(item->GetId());
145 if ( n == wxNOT_FOUND )
146 {
147 // no old, add new if any
148 if ( accel )
149 m_accels.Add(accel);
150 else
151 return; // skipping RebuildAccelTable() below
152 }
153 else
154 {
155 // replace old with new or just remove the old one if no new
156 delete m_accels[n];
157 if ( accel )
158 m_accels[n] = accel;
159 else
160 m_accels.Remove(n);
161 }
162
163 if ( IsAttached() )
164 {
165 m_menuBar->RebuildAccelTable();
166 }
167 }
168
169 #endif // wxUSE_ACCEL
170
171 // append a new item or submenu to the menu
172 bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
173 {
174 #if wxUSE_ACCEL
175 UpdateAccel(pItem);
176 #endif // wxUSE_ACCEL
177
178 UINT flags = 0;
179
180 // if "Break" has just been called, insert a menu break before this item
181 // (and don't forget to reset the flag)
182 if ( m_doBreak ) {
183 flags |= MF_MENUBREAK;
184 m_doBreak = FALSE;
185 }
186
187 if ( pItem->IsSeparator() ) {
188 flags |= MF_SEPARATOR;
189 }
190
191 // id is the numeric id for normal menu items and HMENU for submenus as
192 // required by ::AppendMenu() API
193 UINT id;
194 wxMenu *submenu = pItem->GetSubMenu();
195 if ( submenu != NULL ) {
196 wxASSERT_MSG( submenu->GetHMenu(), wxT("invalid submenu") );
197
198 submenu->SetParent(this);
199
200 id = (UINT)submenu->GetHMenu();
201
202 flags |= MF_POPUP;
203 }
204 else {
205 id = pItem->GetId();
206 }
207
208 LPCTSTR pData;
209
210 #if wxUSE_OWNER_DRAWN
211 if ( pItem->IsOwnerDrawn() ) { // want to get {Measure|Draw}Item messages?
212 // item draws itself, pass pointer to it in data parameter
213 flags |= MF_OWNERDRAW;
214 pData = (LPCTSTR)pItem;
215 }
216 else
217 #endif
218 {
219 // menu is just a normal string (passed in data parameter)
220 flags |= MF_STRING;
221
222 pData = (wxChar*)pItem->GetText().c_str();
223 }
224
225 BOOL ok;
226 if ( pos == (size_t)-1 )
227 {
228 ok = ::AppendMenu(GetHmenu(), flags, id, pData);
229 }
230 else
231 {
232 ok = ::InsertMenu(GetHmenu(), pos, flags | MF_BYPOSITION, id, pData);
233 }
234
235 if ( !ok )
236 {
237 wxLogLastError("Insert or AppendMenu");
238
239 return FALSE;
240 }
241 else
242 {
243 // if we just appended the title, highlight it
244 #ifdef __WIN32__
245 if ( (int)id == idMenuTitle )
246 {
247 // visually select the menu title
248 MENUITEMINFO mii;
249 mii.cbSize = sizeof(mii);
250 mii.fMask = MIIM_STATE;
251 mii.fState = MFS_DEFAULT;
252
253 if ( !SetMenuItemInfo(GetHmenu(), (unsigned)id, FALSE, &mii) )
254 {
255 wxLogLastError(wxT("SetMenuItemInfo"));
256 }
257 }
258 #endif // __WIN32__
259
260 // if we're already attached to the menubar, we must update it
261 if ( IsAttached() )
262 {
263 m_menuBar->Refresh();
264 }
265
266 return TRUE;
267 }
268 }
269
270 bool wxMenu::DoAppend(wxMenuItem *item)
271 {
272 return wxMenuBase::DoAppend(item) && DoInsertOrAppend(item);
273 }
274
275 bool wxMenu::DoInsert(size_t pos, wxMenuItem *item)
276 {
277 return wxMenuBase::DoInsert(pos, item) && DoInsertOrAppend(item, pos);
278 }
279
280 wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
281 {
282 // we need to find the items position in the child list
283 size_t pos;
284 wxMenuItemList::Node *node = GetMenuItems().GetFirst();
285 for ( pos = 0; node; pos++ )
286 {
287 if ( node->GetData() == item )
288 break;
289
290 node = node->GetNext();
291 }
292
293 // DoRemove() (unlike Remove) can only be called for existing item!
294 wxCHECK_MSG( node, NULL, wxT("bug in wxMenu::Remove logic") );
295
296 #if wxUSE_ACCEL
297 // remove the corresponding accel from the accel table
298 int n = FindAccel(item->GetId());
299 if ( n != wxNOT_FOUND )
300 {
301 delete m_accels[n];
302
303 m_accels.Remove(n);
304 }
305 //else: this item doesn't have an accel, nothing to do
306 #endif // wxUSE_ACCEL
307
308 // remove the item from the menu
309 if ( !::RemoveMenu(GetHmenu(), (UINT)pos, MF_BYPOSITION) )
310 {
311 wxLogLastError("RemoveMenu");
312 }
313
314 if ( IsAttached() )
315 {
316 // otherwise, the chane won't be visible
317 m_menuBar->Refresh();
318 }
319
320 // and from internal data structures
321 return wxMenuBase::DoRemove(item);
322 }
323
324 // ---------------------------------------------------------------------------
325 // accelerator helpers
326 // ---------------------------------------------------------------------------
327
328 #if wxUSE_ACCEL
329
330 // create the wxAcceleratorEntries for our accels and put them into provided
331 // array - return the number of accels we have
332 size_t wxMenu::CopyAccels(wxAcceleratorEntry *accels) const
333 {
334 size_t count = GetAccelCount();
335 for ( size_t n = 0; n < count; n++ )
336 {
337 *accels++ = *m_accels[n];
338 }
339
340 return count;
341 }
342
343 #endif // wxUSE_ACCEL
344
345 // ---------------------------------------------------------------------------
346 // set wxMenu title
347 // ---------------------------------------------------------------------------
348
349 void wxMenu::SetTitle(const wxString& label)
350 {
351 bool hasNoTitle = m_title.IsEmpty();
352 m_title = label;
353
354 HMENU hMenu = GetHmenu();
355
356 if ( hasNoTitle )
357 {
358 if ( !label.IsEmpty() )
359 {
360 if ( !::InsertMenu(hMenu, 0u, MF_BYPOSITION | MF_STRING,
361 (unsigned)idMenuTitle, m_title) ||
362 !::InsertMenu(hMenu, 1u, MF_BYPOSITION, (unsigned)-1, NULL) )
363 {
364 wxLogLastError("InsertMenu");
365 }
366 }
367 }
368 else
369 {
370 if ( label.IsEmpty() )
371 {
372 // remove the title and the separator after it
373 if ( !RemoveMenu(hMenu, 0, MF_BYPOSITION) ||
374 !RemoveMenu(hMenu, 0, MF_BYPOSITION) )
375 {
376 wxLogLastError("RemoveMenu");
377 }
378 }
379 else
380 {
381 // modify the title
382 if ( !ModifyMenu(hMenu, 0u,
383 MF_BYPOSITION | MF_STRING,
384 (unsigned)idMenuTitle, m_title) )
385 {
386 wxLogLastError("ModifyMenu");
387 }
388 }
389 }
390
391 #ifdef __WIN32__
392 // put the title string in bold face
393 if ( !m_title.IsEmpty() )
394 {
395 MENUITEMINFO mii;
396 mii.cbSize = sizeof(mii);
397 mii.fMask = MIIM_STATE;
398 mii.fState = MFS_DEFAULT;
399
400 if ( !SetMenuItemInfo(hMenu, (unsigned)idMenuTitle, FALSE, &mii) )
401 {
402 wxLogLastError("SetMenuItemInfo");
403 }
404 }
405 #endif // Win32
406 }
407
408 // ---------------------------------------------------------------------------
409 // event processing
410 // ---------------------------------------------------------------------------
411
412 bool wxMenu::MSWCommand(WXUINT WXUNUSED(param), WXWORD id)
413 {
414 // ignore commands from the menu title
415
416 // NB: VC++ generates wrong assembler for `if ( id != idMenuTitle )'!!
417 if ( id != (WXWORD)idMenuTitle )
418 {
419 wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED);
420 event.SetEventObject( this );
421 event.SetId( id );
422 event.SetInt( id );
423 ProcessCommand(event);
424 }
425
426 return TRUE;
427 }
428
429 bool wxMenu::ProcessCommand(wxCommandEvent & event)
430 {
431 bool processed = FALSE;
432
433 #if wxUSE_MENU_CALLBACK
434 // Try a callback
435 if (m_callback)
436 {
437 (void)(*(m_callback))(*this, event);
438 processed = TRUE;
439 }
440 #endif // wxUSE_MENU_CALLBACK
441
442 // Try the menu's event handler
443 if ( !processed && GetEventHandler())
444 {
445 processed = GetEventHandler()->ProcessEvent(event);
446 }
447
448 // Try the window the menu was popped up from (and up through the
449 // hierarchy)
450 wxWindow *win = GetInvokingWindow();
451 if ( !processed && win )
452 processed = win->GetEventHandler()->ProcessEvent(event);
453
454 return processed;
455 }
456
457 // ---------------------------------------------------------------------------
458 // other
459 // ---------------------------------------------------------------------------
460
461 void wxMenu::Attach(wxMenuBar *menubar)
462 {
463 // menu can be in at most one menubar because otherwise they would both
464 // delete the menu pointer
465 wxASSERT_MSG( !m_menuBar, wxT("menu belongs to 2 menubars, expect a crash") );
466
467 m_menuBar = menubar;
468 }
469
470 void wxMenu::Detach()
471 {
472 wxASSERT_MSG( m_menuBar, wxT("can't detach menu if it's not attached") );
473
474 m_menuBar = NULL;
475 }
476
477 wxWindow *wxMenu::GetWindow() const
478 {
479 if ( m_invokingWindow != NULL )
480 return m_invokingWindow;
481 else if ( m_menuBar != NULL)
482 return m_menuBar->GetFrame();
483
484 return NULL;
485 }
486
487 // ---------------------------------------------------------------------------
488 // Menu Bar
489 // ---------------------------------------------------------------------------
490
491 void wxMenuBar::Init()
492 {
493 m_eventHandler = this;
494 m_menuBarFrame = NULL;
495 m_hMenu = 0;
496 }
497
498 wxMenuBar::wxMenuBar()
499 {
500 Init();
501 }
502
503 wxMenuBar::wxMenuBar( long WXUNUSED(style) )
504 {
505 Init();
506 }
507
508 wxMenuBar::wxMenuBar(int count, wxMenu *menus[], const wxString titles[])
509 {
510 Init();
511
512 m_titles.Alloc(count);
513
514 for ( int i = 0; i < count; i++ )
515 {
516 m_menus.Append(menus[i]);
517 m_titles.Add(titles[i]);
518
519 menus[i]->Attach(this);
520 }
521 }
522
523 wxMenuBar::~wxMenuBar()
524 {
525 }
526
527 // ---------------------------------------------------------------------------
528 // wxMenuBar helpers
529 // ---------------------------------------------------------------------------
530
531 void wxMenuBar::Refresh()
532 {
533 wxCHECK_RET( IsAttached(), wxT("can't refresh unattached menubar") );
534
535 DrawMenuBar(GetHwndOf(m_menuBarFrame));
536 }
537
538 WXHMENU wxMenuBar::Create()
539 {
540 if (m_hMenu != 0 )
541 return m_hMenu;
542
543 wxCHECK_MSG( !m_hMenu, TRUE, wxT("menubar already created") );
544
545 m_hMenu = (WXHMENU)::CreateMenu();
546
547 if ( !m_hMenu )
548 {
549 wxLogLastError("CreateMenu");
550 }
551 else
552 {
553 size_t count = GetMenuCount();
554 for ( size_t i = 0; i < count; i++ )
555 {
556 if ( !::AppendMenu((HMENU)m_hMenu, MF_POPUP | MF_STRING,
557 (UINT)m_menus[i]->GetHMenu(),
558 m_titles[i]) )
559 {
560 wxLogLastError("AppendMenu");
561 }
562 }
563 }
564
565 return m_hMenu;
566 }
567
568 // ---------------------------------------------------------------------------
569 // wxMenuBar functions to work with the top level submenus
570 // ---------------------------------------------------------------------------
571
572 // NB: we don't support owner drawn top level items for now, if we do these
573 // functions would have to be changed to use wxMenuItem as well
574
575 void wxMenuBar::EnableTop(size_t pos, bool enable)
576 {
577 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
578
579 int flag = enable ? MF_ENABLED : MF_GRAYED;
580
581 EnableMenuItem((HMENU)m_hMenu, pos, MF_BYPOSITION | flag);
582
583 Refresh();
584 }
585
586 void wxMenuBar::SetLabelTop(size_t pos, const wxString& label)
587 {
588 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
589
590 m_titles[pos] = label;
591
592 if ( !IsAttached() )
593 {
594 return;
595 }
596 //else: have to modify the existing menu
597
598 UINT id;
599 UINT flagsOld = ::GetMenuState((HMENU)m_hMenu, pos, MF_BYPOSITION);
600 if ( flagsOld == 0xFFFFFFFF )
601 {
602 wxLogLastError(wxT("GetMenuState"));
603
604 return;
605 }
606
607 if ( flagsOld & MF_POPUP )
608 {
609 // HIBYTE contains the number of items in the submenu in this case
610 flagsOld &= 0xff;
611 id = (UINT)::GetSubMenu((HMENU)m_hMenu, pos);
612 }
613 else
614 {
615 id = pos;
616 }
617
618 if ( ::ModifyMenu(GetHmenu(), pos, MF_BYPOSITION | MF_STRING | flagsOld,
619 id, label) == (int)0xFFFFFFFF )
620 {
621 wxLogLastError("ModifyMenu");
622 }
623
624 Refresh();
625 }
626
627 wxString wxMenuBar::GetLabelTop(size_t pos) const
628 {
629 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
630 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
631
632 return m_titles[pos];
633 }
634
635 // ---------------------------------------------------------------------------
636 // wxMenuBar construction
637 // ---------------------------------------------------------------------------
638
639 wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
640 {
641 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title);
642 if ( !menuOld )
643 return FALSE;
644 m_titles[pos] = title;
645
646 if ( IsAttached() )
647 {
648 // can't use ModifyMenu() because it deletes the submenu it replaces
649 if ( !::RemoveMenu(GetHmenu(), (UINT)pos, MF_BYPOSITION) )
650 {
651 wxLogLastError("RemoveMenu");
652 }
653
654 if ( !::InsertMenu(GetHmenu(), (UINT)pos,
655 MF_BYPOSITION | MF_POPUP | MF_STRING,
656 (UINT)GetHmenuOf(menu), title) )
657 {
658 wxLogLastError("InsertMenu");
659 }
660
661 #if wxUSE_ACCEL
662 if ( menuOld->HasAccels() || menu->HasAccels() )
663 {
664 // need to rebuild accell table
665 RebuildAccelTable();
666 }
667 #endif // wxUSE_ACCEL
668
669 Refresh();
670 }
671
672 return menuOld;
673 }
674
675 bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
676 {
677 if ( !wxMenuBarBase::Insert(pos, menu, title) )
678 return FALSE;
679
680 m_titles.Insert(title, pos);
681
682 menu->Attach(this);
683
684 if ( IsAttached() )
685 {
686 if ( !::InsertMenu(GetHmenu(), pos,
687 MF_BYPOSITION | MF_POPUP | MF_STRING,
688 (UINT)GetHmenuOf(menu), title) )
689 {
690 wxLogLastError("InsertMenu");
691 }
692
693 #if wxUSE_ACCEL
694 if ( menu->HasAccels() )
695 {
696 // need to rebuild accell table
697 RebuildAccelTable();
698 }
699 #endif // wxUSE_ACCEL
700
701 Refresh();
702 }
703
704 return TRUE;
705 }
706
707 bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
708 {
709 WXHMENU submenu = menu ? menu->GetHMenu() : 0;
710 wxCHECK_MSG( submenu, FALSE, wxT("can't append invalid menu to menubar") );
711
712 if ( !wxMenuBarBase::Append(menu, title) )
713 return FALSE;
714
715 menu->Attach(this);
716
717 m_titles.Add(title);
718
719 if ( IsAttached() )
720 {
721 if ( !::AppendMenu(GetHmenu(), MF_POPUP | MF_STRING,
722 (UINT)submenu, title) )
723 {
724 wxLogLastError(wxT("AppendMenu"));
725 }
726
727 #if wxUSE_ACCEL
728 if ( menu->HasAccels() )
729 {
730 // need to rebuild accell table
731 RebuildAccelTable();
732 }
733 #endif // wxUSE_ACCEL
734
735 Refresh();
736 }
737
738 return TRUE;
739 }
740
741 wxMenu *wxMenuBar::Remove(size_t pos)
742 {
743 wxMenu *menu = wxMenuBarBase::Remove(pos);
744 if ( !menu )
745 return NULL;
746
747 if ( IsAttached() )
748 {
749 if ( !::RemoveMenu(GetHmenu(), (UINT)pos, MF_BYPOSITION) )
750 {
751 wxLogLastError("RemoveMenu");
752 }
753
754 menu->Detach();
755
756 #if wxUSE_ACCEL
757 if ( menu->HasAccels() )
758 {
759 // need to rebuild accell table
760 RebuildAccelTable();
761 }
762 #endif // wxUSE_ACCEL
763
764 Refresh();
765 }
766
767 m_titles.Remove(pos);
768
769 return menu;
770 }
771
772 #if wxUSE_ACCEL
773
774 void wxMenuBar::RebuildAccelTable()
775 {
776 // merge the accelerators of all menus into one accel table
777 size_t nAccelCount = 0;
778 size_t i, count = GetMenuCount();
779 for ( i = 0; i < count; i++ )
780 {
781 nAccelCount += m_menus[i]->GetAccelCount();
782 }
783
784 if ( nAccelCount )
785 {
786 wxAcceleratorEntry *accelEntries = new wxAcceleratorEntry[nAccelCount];
787
788 nAccelCount = 0;
789 for ( i = 0; i < count; i++ )
790 {
791 nAccelCount += m_menus[i]->CopyAccels(&accelEntries[nAccelCount]);
792 }
793
794 m_accelTable = wxAcceleratorTable(nAccelCount, accelEntries);
795
796 delete [] accelEntries;
797 }
798 }
799
800 #endif // wxUSE_ACCEL
801
802 void wxMenuBar::Attach(wxFrame *frame)
803 {
804 // wxASSERT_MSG( !IsAttached(), wxT("menubar already attached!") );
805
806 m_menuBarFrame = frame;
807
808 #if wxUSE_ACCEL
809 RebuildAccelTable();
810 #endif // wxUSE_ACCEL
811 }
812
813 void wxMenuBar::Detach()
814 {
815 // ::DestroyMenu((HMENU)m_hMenu);
816 m_hMenu = (WXHMENU)NULL;
817 m_menuBarFrame = NULL;
818 }
819
820
821 // ---------------------------------------------------------------------------
822 // wxMenuBar searching for menu items
823 // ---------------------------------------------------------------------------
824
825 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
826 int wxMenuBar::FindMenuItem(const wxString& menuString,
827 const wxString& itemString) const
828 {
829 wxString menuLabel = wxStripMenuCodes(menuString);
830 size_t count = GetMenuCount();
831 for ( size_t i = 0; i < count; i++ )
832 {
833 wxString title = wxStripMenuCodes(m_titles[i]);
834 if ( menuString == title )
835 return m_menus[i]->FindItem(itemString);
836 }
837
838 return wxNOT_FOUND;
839 }
840
841 wxMenuItem *wxMenuBar::FindItem(int id, wxMenu **itemMenu) const
842 {
843 if ( itemMenu )
844 *itemMenu = NULL;
845
846 wxMenuItem *item = NULL;
847 size_t count = GetMenuCount();
848 for ( size_t i = 0; !item && (i < count); i++ )
849 {
850 item = m_menus[i]->FindItem(id, itemMenu);
851 }
852
853 return item;
854 }
855