1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxMenu, wxMenuBar, wxMenuItem
4 // Author: Julian Smart
5 // Modified by: Vadim Zeitlin
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
21 #pragma implementation "menu.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
39 #include "wx/ownerdrw.h"
42 #include "wx/msw/private.h"
43 #include "wx/msw/menu.h"
44 #include "wx/menuitem.h"
47 // other standard headers
50 // ----------------------------------------------------------------------------
52 // ----------------------------------------------------------------------------
54 extern wxMenu
*wxCurrentPopupMenu
;
56 // ----------------------------------------------------------------------------
58 // ----------------------------------------------------------------------------
60 // the (popup) menu title has this special id
61 static const int idMenuTitle
= -2;
63 // ----------------------------------------------------------------------------
65 // ----------------------------------------------------------------------------
67 #if !USE_SHARED_LIBRARY
68 IMPLEMENT_DYNAMIC_CLASS(wxMenu
, wxEvtHandler
)
69 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
, wxEvtHandler
)
72 // ============================================================================
74 // ============================================================================
76 // ---------------------------------------------------------------------------
77 // wxMenu construction, adding and removing menu items
78 // ---------------------------------------------------------------------------
80 // Construct a menu with optional title (then use append)
81 void wxMenu::Init(const wxString
& title
, const wxFunction func
)
84 m_eventHandler
= this;
85 m_pInvokingWindow
= NULL
;
89 m_hMenu
= (WXHMENU
) CreatePopupMenu();
91 m_topLevelMenu
= this;
92 m_clientData
= (void*) NULL
;
96 Append(idMenuTitle
, m_title
);
103 // The wxWindow destructor will take care of deleting the submenus.
106 // free Windows resources
109 if ( !::DestroyMenu(GetHmenu()) )
111 wxLogLastError("DestroyMenu");
116 wxNode
*node
= m_menuItems
.First();
119 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
121 // Delete child menus.
122 // Beware: they must not be appended to children list!!!
123 // (because order of delete is significant)
124 if ( item
->IsSubMenu() )
125 item
->DeleteSubMenu();
127 wxNode
*next
= node
->Next();
135 WX_CLEAR_ARRAY(m_accels
);
136 #endif // wxUSE_ACCEL
144 // function appends a new item or submenu to the menu
145 void wxMenu::Append(wxMenuItem
*pItem
)
147 wxCHECK_RET( pItem
!= NULL
, wxT("can't append NULL item to the menu") );
150 wxAcceleratorEntry
*accel
= wxGetAccelFromString(pItem
->GetText());
153 accel
->m_command
= pItem
->GetId();
155 #endif // wxUSE_ACCEL
159 // if "Break" has just been called, insert a menu break before this item
160 // (and don't forget to reset the flag)
162 flags
|= MF_MENUBREAK
;
166 if ( pItem
->IsSeparator() ) {
167 flags
|= MF_SEPARATOR
;
170 // id is the numeric id for normal menu items and HMENU for submenus as
171 // required by ::AppendMenu() API
173 wxMenu
*submenu
= pItem
->GetSubMenu();
174 if ( submenu
!= NULL
) {
175 wxASSERT( submenu
->GetHMenu() != (WXHMENU
) NULL
);
177 id
= (UINT
)submenu
->GetHMenu();
178 submenu
->m_topLevelMenu
= m_topLevelMenu
;
179 submenu
->m_savehMenu
= (WXHMENU
)id
;
180 submenu
->m_hMenu
= 0;
190 #if wxUSE_OWNER_DRAWN
191 if ( pItem
->IsOwnerDrawn() ) { // want to get {Measure|Draw}Item messages?
192 // item draws itself, pass pointer to it in data parameter
193 flags
|= MF_OWNERDRAW
;
194 pData
= (LPCTSTR
)pItem
;
199 // menu is just a normal string (passed in data parameter)
202 pData
= (char*)pItem
->GetText().c_str();
205 if ( !::AppendMenu(GetHmenu(), flags
, id
, pData
) )
207 wxLogLastError("AppendMenu");
212 if ( (int)id
== idMenuTitle
)
214 // visually select the menu title
216 mii
.cbSize
= sizeof(mii
);
217 mii
.fMask
= MIIM_STATE
;
218 mii
.fState
= MFS_DEFAULT
;
220 if ( !SetMenuItemInfo(GetHmenu(), (unsigned)id
, FALSE
, &mii
) )
222 wxLogLastError(wxT("SetMenuItemInfo"));
227 m_menuItems
.Append(pItem
);
232 void wxMenu::AppendSeparator()
234 Append(new wxMenuItem(this, ID_SEPARATOR
));
238 void wxMenu::Append(int id
,
239 const wxString
& label
,
241 const wxString
& helpString
)
243 Append(new wxMenuItem(this, id
, label
, helpString
, FALSE
, SubMenu
));
246 // Ordinary menu item
247 void wxMenu::Append(int id
,
248 const wxString
& label
,
249 const wxString
& helpString
,
252 // 'checkable' parameter is useless for Windows.
253 Append(new wxMenuItem(this, id
, label
, helpString
, checkable
));
257 void wxMenu::Delete(int id
)
259 wxMenuItem
*item
= NULL
;
262 for (pos
= 0, node
= m_menuItems
.First(); node
; node
= node
->Next(), pos
++)
264 item
= (wxMenuItem
*)node
->Data();
265 if ( item
->GetId() == id
)
269 wxCHECK_RET( node
, wxT("wxMenu::Delete(): item doesn't exist") );
271 HMENU menu
= GetHmenu();
273 wxMenu
*pSubMenu
= item
->GetSubMenu();
274 if ( pSubMenu
!= NULL
) {
275 RemoveMenu(menu
, (UINT
)pos
, MF_BYPOSITION
);
276 pSubMenu
->m_hMenu
= pSubMenu
->m_savehMenu
;
277 pSubMenu
->m_savehMenu
= 0;
278 // RemoveChild(item->subMenu);
279 pSubMenu
->m_topLevelMenu
= NULL
;
280 // TODO: Why isn't subMenu deleted here???
281 // Will put this in for now. Assuming this is supposed
282 // to delete the menu, not just remove it.
283 item
->DeleteSubMenu();
286 DeleteMenu(menu
, (UINT
)pos
, MF_BYPOSITION
);
289 m_menuItems
.DeleteNode(node
);
295 // ---------------------------------------------------------------------------
296 // accelerator helpers
297 // ---------------------------------------------------------------------------
299 // create the wxAcceleratorEntries for our accels and put them into provided
300 // array - return the number of accels we have
301 size_t wxMenu::CopyAccels(wxAcceleratorEntry
*accels
) const
303 size_t count
= GetAccelCount();
304 for ( size_t n
= 0; n
< count
; n
++ )
306 *accels
++ = *m_accels
[n
];
312 #endif // wxUSE_ACCEL
314 // ---------------------------------------------------------------------------
315 // wxMenu functions implemented in wxMenuItem
316 // ---------------------------------------------------------------------------
318 void wxMenu::Enable(int id
, bool Flag
)
320 wxMenuItem
*item
= FindItemForId(id
);
321 wxCHECK_RET( item
!= NULL
, wxT("can't enable non-existing menu item") );
326 bool wxMenu::IsEnabled(int id
) const
328 wxMenuItem
*item
= FindItemForId(id
);
329 wxCHECK_MSG( item
!= NULL
, FALSE
, wxT("invalid item id") );
331 return item
->IsEnabled();
334 void wxMenu::Check(int id
, bool Flag
)
336 wxMenuItem
*item
= FindItemForId(id
);
337 wxCHECK_RET( item
!= NULL
, wxT("can't get status of non-existing menu item") );
342 bool wxMenu::IsChecked(int id
) const
344 wxMenuItem
*item
= FindItemForId(id
);
345 wxCHECK_MSG( item
!= NULL
, FALSE
, wxT("invalid item id") );
347 return item
->IsChecked();
350 void wxMenu::SetLabel(int id
, const wxString
& label
)
352 wxMenuItem
*item
= FindItemForId(id
);
353 wxCHECK_RET( item
, wxT("wxMenu::SetLabel: no such item") );
355 item
->SetText(label
);
358 wxString
wxMenu::GetLabel(int id
) const
361 wxMenuItem
*pItem
= FindItemForId(id
);
363 label
= pItem
->GetText();
365 wxFAIL_MSG(wxT("wxMenu::GetLabel: item doesn't exist"));
370 void wxMenu::SetHelpString(int itemId
, const wxString
& helpString
)
372 wxMenuItem
*item
= FindItemForId (itemId
);
374 item
->SetHelp(helpString
);
376 wxFAIL_MSG(wxT("wxMenu::SetHelpString: item doesn't exist"));
379 wxString
wxMenu::GetHelpString (int itemId
) const
382 wxMenuItem
*item
= FindItemForId (itemId
);
384 help
= item
->GetHelp();
386 wxFAIL_MSG(wxT("wxMenu::GetHelpString: item doesn't exist"));
391 // ---------------------------------------------------------------------------
393 // ---------------------------------------------------------------------------
395 void wxMenu::SetTitle(const wxString
& label
)
397 bool hasNoTitle
= m_title
.IsEmpty();
400 HMENU hMenu
= GetHmenu();
404 if ( !label
.IsEmpty() )
406 if ( !InsertMenu(hMenu
, 0u, MF_BYPOSITION
| MF_STRING
,
407 (unsigned)idMenuTitle
, m_title
) ||
408 !InsertMenu(hMenu
, 1u, MF_BYPOSITION
, (unsigned)-1, NULL
) )
410 wxLogLastError(wxT("InsertMenu"));
416 if ( label
.IsEmpty() )
418 // remove the title and the separator after it
419 if ( !RemoveMenu(hMenu
, 0, MF_BYPOSITION
) ||
420 !RemoveMenu(hMenu
, 0, MF_BYPOSITION
) )
422 wxLogLastError("RemoveMenu");
428 if ( !ModifyMenu(hMenu
, 0u,
429 MF_BYPOSITION
| MF_STRING
,
430 (unsigned)idMenuTitle
, m_title
) )
432 wxLogLastError("ModifyMenu");
438 // put the title string in bold face
439 if ( !m_title
.IsEmpty() )
442 mii
.cbSize
= sizeof(mii
);
443 mii
.fMask
= MIIM_STATE
;
444 mii
.fState
= MFS_DEFAULT
;
446 if ( !SetMenuItemInfo(hMenu
, (unsigned)idMenuTitle
, FALSE
, &mii
) )
448 wxLogLastError("SetMenuItemInfo");
454 const wxString
wxMenu::GetTitle() const
459 // ---------------------------------------------------------------------------
461 // ---------------------------------------------------------------------------
463 bool wxMenu::MSWCommand(WXUINT
WXUNUSED(param
), WXWORD id
)
465 // ignore commands from the menu title
467 // NB: VC++ generates wrong assembler for `if ( id != idMenuTitle )'!!
468 if ( id
!= (WXWORD
)idMenuTitle
)
470 wxCommandEvent
event(wxEVT_COMMAND_MENU_SELECTED
);
471 event
.SetEventObject( this );
474 ProcessCommand(event
);
480 bool wxMenu::ProcessCommand(wxCommandEvent
& event
)
482 bool processed
= FALSE
;
487 (void)(*(m_callback
))(*this, event
);
491 // Try the menu's event handler
492 if ( !processed
&& GetEventHandler())
494 processed
= GetEventHandler()->ProcessEvent(event
);
497 // Try the window the menu was popped up from (and up through the
499 wxWindow
*win
= GetInvokingWindow();
500 if ( !processed
&& win
)
501 processed
= win
->GetEventHandler()->ProcessEvent(event
);
506 // ---------------------------------------------------------------------------
508 // ---------------------------------------------------------------------------
510 // Finds the item id matching the given string, -1 if not found.
511 int wxMenu::FindItem (const wxString
& itemString
) const
513 wxString itemLabel
= wxStripMenuCodes(itemString
);
514 for ( wxNode
*node
= m_menuItems
.First(); node
; node
= node
->Next() )
516 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
517 if ( item
->IsSubMenu() )
519 int ans
= item
->GetSubMenu()->FindItem(itemString
);
520 if ( ans
!= wxNOT_FOUND
)
523 else if ( !item
->IsSeparator() )
525 wxString label
= wxStripMenuCodes(item
->GetText());
526 if ( itemLabel
== label
)
527 return item
->GetId();
534 wxMenuItem
*wxMenu::FindItemForId(int itemId
, wxMenu
** itemMenu
) const
539 wxMenuItem
*item
= NULL
;
540 for ( wxNode
*node
= m_menuItems
.First(); node
&& !item
; node
= node
->Next() )
542 item
= (wxMenuItem
*)node
->Data();
544 if ( item
->GetId() == itemId
)
547 *itemMenu
= (wxMenu
*)this;
549 else if ( item
->IsSubMenu() )
551 item
= item
->GetSubMenu()->FindItemForId(itemId
, itemMenu
);
555 // don't exit the loop
563 // ---------------------------------------------------------------------------
565 // ---------------------------------------------------------------------------
567 void wxMenu::Attach(wxMenuBar
*menubar
)
569 // menu can be in at most one menubar because otherwise they would both
570 // delete the menu pointer
571 wxASSERT_MSG( !m_menuBar
, wxT("menu belongs to 2 menubars, expect a crash") );
574 m_savehMenu
= m_hMenu
;
578 void wxMenu::Detach()
580 wxASSERT_MSG( m_menuBar
, wxT("can't detach menu if it's not attached") );
583 m_hMenu
= m_savehMenu
;
587 // ---------------------------------------------------------------------------
589 // ---------------------------------------------------------------------------
591 void wxMenuBar::Init()
593 m_eventHandler
= this;
594 m_menuBarFrame
= NULL
;
598 wxMenuBar::wxMenuBar()
603 wxMenuBar::wxMenuBar( long WXUNUSED(style
) )
608 wxMenuBar::wxMenuBar(int count
, wxMenu
*menus
[], const wxString titles
[])
612 m_titles
.Alloc(count
);
614 for ( int i
= 0; i
< count
; i
++ )
616 m_menus
.Append(menus
[i
]);
617 m_titles
.Add(titles
[i
]);
619 menus
[i
]->Attach(this);
623 wxMenuBar::~wxMenuBar()
627 // ---------------------------------------------------------------------------
629 // ---------------------------------------------------------------------------
631 void wxMenuBar::Refresh()
633 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
635 DrawMenuBar(GetHwndOf(m_menuBarFrame
));
638 WXHMENU
wxMenuBar::Create()
643 wxCHECK_MSG( !m_hMenu
, TRUE
, wxT("menubar already created") );
645 m_hMenu
= (WXHMENU
)::CreateMenu();
649 wxLogLastError("CreateMenu");
653 size_t count
= GetMenuCount();
654 for ( size_t i
= 0; i
< count
; i
++ )
656 if ( !::AppendMenu((HMENU
)m_hMenu
, MF_POPUP
| MF_STRING
,
657 (UINT
)m_menus
[i
]->GetHMenu(),
660 wxLogLastError("AppendMenu");
668 // ---------------------------------------------------------------------------
669 // wxMenuBar functions to work with the top level submenus
670 // ---------------------------------------------------------------------------
672 // NB: we don't support owner drawn top level items for now, if we do these
673 // functions would have to be changed to use wxMenuItem as well
675 void wxMenuBar::EnableTop(size_t pos
, bool enable
)
677 int flag
= enable
? MF_ENABLED
: MF_GRAYED
;;
679 EnableMenuItem((HMENU
)m_hMenu
, pos
, MF_BYPOSITION
| flag
);
684 void wxMenuBar::SetLabelTop(size_t pos
, const wxString
& label
)
687 UINT flagsOld
= ::GetMenuState((HMENU
)m_hMenu
, pos
, MF_BYPOSITION
);
688 if ( flagsOld
== 0xFFFFFFFF )
690 wxLogLastError(wxT("GetMenuState"));
695 if ( flagsOld
& MF_POPUP
)
697 // HIBYTE contains the number of items in the submenu in this case
699 id
= (UINT
)::GetSubMenu((HMENU
)m_hMenu
, pos
);
706 if ( ::ModifyMenu(GetHmenu(), pos
, MF_BYPOSITION
| MF_STRING
| flagsOld
,
707 id
, label
) == (int)0xFFFFFFFF )
709 wxLogLastError("ModifyMenu");
713 wxString
wxMenuBar::GetLabelTop(size_t pos
) const
715 int len
= ::GetMenuString((HMENU
)m_hMenu
, pos
, NULL
, 0, MF_BYCOMMAND
);
717 len
++; // for the NUL character
719 ::GetMenuString(GetHmenu(), pos
, label
.GetWriteBuf(len
), len
, MF_BYCOMMAND
);
720 label
.UngetWriteBuf();
725 int wxMenuBar::FindMenu(const wxString
& title
)
727 wxString menuTitle
= wxStripMenuCodes(title
);
729 size_t count
= GetMenuCount();
730 for ( size_t i
= 0; i
< count
; i
++ )
732 wxString title
= wxStripMenuCodes(m_titles
[i
]);
733 if ( menuTitle
== title
)
741 // ---------------------------------------------------------------------------
742 // wxMenuBar construction
743 // ---------------------------------------------------------------------------
745 wxMenu
*wxMenuBar::Replace(size_t pos
, wxMenu
*menu
, const wxString
& title
)
747 wxMenu
*menuOld
= wxMenuBarBase::Replace(pos
, menu
, title
);
750 m_titles
[pos
] = title
;
754 // can't use ModifyMenu() because it deletes the submenu it replaces
755 if ( !::RemoveMenu(GetHmenu(), (UINT
)pos
, MF_BYPOSITION
) )
757 wxLogLastError("RemoveMenu");
760 if ( !::InsertMenu(GetHmenu(), (UINT
)pos
,
761 MF_BYPOSITION
| MF_POPUP
| MF_STRING
,
762 (UINT
)GetHmenuOf(menu
), title
) )
764 wxLogLastError("InsertMenu");
773 bool wxMenuBar::Insert(size_t pos
, wxMenu
*menu
, const wxString
& title
)
775 if ( !wxMenuBarBase::Insert(pos
, menu
, title
) )
778 m_titles
.Insert(title
, pos
);
784 if ( !::InsertMenu(GetHmenu(), pos
,
785 MF_BYPOSITION
| MF_POPUP
| MF_STRING
,
786 (UINT
)GetHmenuOf(menu
), title
) )
788 wxLogLastError("InsertMenu");
797 bool wxMenuBar::Append(wxMenu
*menu
, const wxString
& title
)
799 WXHMENU submenu
= menu
? menu
->GetHMenu() : 0;
800 wxCHECK_MSG( submenu
, FALSE
, wxT("can't append invalid menu to menubar") );
806 if ( !::AppendMenu(GetHmenu(), MF_POPUP
| MF_STRING
,
807 (UINT
)submenu
, title
) )
809 wxLogLastError(wxT("AppendMenu"));
815 wxMenuBarBase::Append(menu
, title
);
822 wxMenu
*wxMenuBar::Remove(size_t pos
)
824 wxMenu
*menu
= wxMenuBarBase::Remove(pos
);
830 if ( !::RemoveMenu(GetHmenu(), (UINT
)pos
, MF_BYPOSITION
) )
832 wxLogLastError("RemoveMenu");
840 m_titles
.Remove(pos
);
845 void wxMenuBar::Attach(wxFrame
*frame
)
847 wxASSERT_MSG( !IsAttached(), wxT("menubar already attached!") );
849 m_menuBarFrame
= frame
;
852 // create the accel table - we consider that the menubar construction is
854 size_t nAccelCount
= 0;
855 size_t i
, count
= GetMenuCount();
856 for ( i
= 0; i
< count
; i
++ )
858 nAccelCount
+= m_menus
[i
]->GetAccelCount();
863 wxAcceleratorEntry
*accelEntries
= new wxAcceleratorEntry
[nAccelCount
];
866 for ( i
= 0; i
< count
; i
++ )
868 nAccelCount
+= m_menus
[i
]->CopyAccels(&accelEntries
[nAccelCount
]);
871 m_accelTable
= wxAcceleratorTable(nAccelCount
, accelEntries
);
873 delete [] accelEntries
;
875 #endif // wxUSE_ACCEL
878 void wxMenuBar::Detach()
880 // ::DestroyMenu((HMENU)m_hMenu);
881 m_hMenu
= (WXHMENU
)NULL
;
882 m_menuBarFrame
= NULL
;
886 // ---------------------------------------------------------------------------
887 // wxMenuBar searching for menu items
888 // ---------------------------------------------------------------------------
890 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
891 int wxMenuBar::FindMenuItem(const wxString
& menuString
,
892 const wxString
& itemString
) const
894 wxString menuLabel
= wxStripMenuCodes(menuString
);
895 size_t count
= GetMenuCount();
896 for ( size_t i
= 0; i
< count
; i
++ )
898 wxString title
= wxStripMenuCodes(m_titles
[i
]);
899 if ( menuString
== title
)
900 return m_menus
[i
]->FindItem(itemString
);
906 wxMenuItem
*wxMenuBar::FindItem(int id
, wxMenu
**itemMenu
) const
911 wxMenuItem
*item
= NULL
;
912 size_t count
= GetMenuCount();
913 for ( size_t i
= 0; !item
&& (i
< count
); i
++ )
915 item
= m_menus
[i
]->FindItemForId(id
, itemMenu
);
922 // ----------------------------------------------------------------------------
924 // ----------------------------------------------------------------------------
926 wxWindow
*wxMenu::GetWindow() const
928 if ( m_pInvokingWindow
!= NULL
)
929 return m_pInvokingWindow
;
930 else if ( m_menuBar
!= NULL
)
931 return m_menuBar
->GetFrame();
936 WXHMENU
wxMenu::GetHMenu() const
940 else if ( m_savehMenu
!= 0 )
943 wxFAIL_MSG(wxT("wxMenu without HMENU"));
948 // Update a menu and all submenus recursively. source is the object that has
949 // the update event handlers defined for it. If NULL, the menu or associated
950 // window will be used.
951 void wxMenu::UpdateUI(wxEvtHandler
* source
)
953 if (!source
&& GetInvokingWindow())
954 source
= GetInvokingWindow()->GetEventHandler();
956 source
= GetEventHandler();
960 wxNode
* node
= GetItems().First();
963 wxMenuItem
* item
= (wxMenuItem
*) node
->Data();
964 if ( !item
->IsSeparator() )
966 wxWindowID id
= item
->GetId();
967 wxUpdateUIEvent
event(id
);
968 event
.SetEventObject( source
);
970 if (source
->ProcessEvent(event
))
972 if (event
.GetSetText())
973 SetLabel(id
, event
.GetText());
974 if (event
.GetSetChecked())
975 Check(id
, event
.GetChecked());
976 if (event
.GetSetEnabled())
977 Enable(id
, event
.GetEnabled());
980 if (item
->GetSubMenu())
981 item
->GetSubMenu()->UpdateUI(source
);