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());
154 #endif // wxUSE_ACCEL
158 // if "Break" has just been called, insert a menu break before this item
159 // (and don't forget to reset the flag)
161 flags
|= MF_MENUBREAK
;
165 if ( pItem
->IsSeparator() ) {
166 flags
|= MF_SEPARATOR
;
169 // id is the numeric id for normal menu items and HMENU for submenus as
170 // required by ::AppendMenu() API
172 wxMenu
*submenu
= pItem
->GetSubMenu();
173 if ( submenu
!= NULL
) {
174 wxASSERT( submenu
->GetHMenu() != (WXHMENU
) NULL
);
176 id
= (UINT
)submenu
->GetHMenu();
177 submenu
->m_topLevelMenu
= m_topLevelMenu
;
178 submenu
->m_savehMenu
= (WXHMENU
)id
;
179 submenu
->m_hMenu
= 0;
189 #if wxUSE_OWNER_DRAWN
190 if ( pItem
->IsOwnerDrawn() ) { // want to get {Measure|Draw}Item messages?
191 // item draws itself, pass pointer to it in data parameter
192 flags
|= MF_OWNERDRAW
;
193 pData
= (LPCTSTR
)pItem
;
198 // menu is just a normal string (passed in data parameter)
201 pData
= (char*)pItem
->GetText().c_str();
204 if ( !::AppendMenu(GetHmenu(), flags
, id
, pData
) )
206 wxLogLastError("AppendMenu");
211 if ( (int)id
== idMenuTitle
)
213 // visually select the menu title
215 mii
.cbSize
= sizeof(mii
);
216 mii
.fMask
= MIIM_STATE
;
217 mii
.fState
= MFS_DEFAULT
;
219 if ( !SetMenuItemInfo(GetHmenu(), (unsigned)id
, FALSE
, &mii
) )
221 wxLogLastError(wxT("SetMenuItemInfo"));
226 m_menuItems
.Append(pItem
);
231 void wxMenu::AppendSeparator()
233 Append(new wxMenuItem(this, ID_SEPARATOR
));
237 void wxMenu::Append(int id
,
238 const wxString
& label
,
240 const wxString
& helpString
)
242 Append(new wxMenuItem(this, id
, label
, helpString
, FALSE
, SubMenu
));
245 // Ordinary menu item
246 void wxMenu::Append(int id
,
247 const wxString
& label
,
248 const wxString
& helpString
,
251 // 'checkable' parameter is useless for Windows.
252 Append(new wxMenuItem(this, id
, label
, helpString
, checkable
));
256 void wxMenu::Delete(int id
)
258 wxMenuItem
*item
= NULL
;
261 for (pos
= 0, node
= m_menuItems
.First(); node
; node
= node
->Next(), pos
++)
263 item
= (wxMenuItem
*)node
->Data();
264 if ( item
->GetId() == id
)
268 wxCHECK_RET( node
, wxT("wxMenu::Delete(): item doesn't exist") );
270 HMENU menu
= GetHmenu();
272 wxMenu
*pSubMenu
= item
->GetSubMenu();
273 if ( pSubMenu
!= NULL
) {
274 RemoveMenu(menu
, (UINT
)pos
, MF_BYPOSITION
);
275 pSubMenu
->m_hMenu
= pSubMenu
->m_savehMenu
;
276 pSubMenu
->m_savehMenu
= 0;
277 // RemoveChild(item->subMenu);
278 pSubMenu
->m_topLevelMenu
= NULL
;
279 // TODO: Why isn't subMenu deleted here???
280 // Will put this in for now. Assuming this is supposed
281 // to delete the menu, not just remove it.
282 item
->DeleteSubMenu();
285 DeleteMenu(menu
, (UINT
)pos
, MF_BYPOSITION
);
288 m_menuItems
.DeleteNode(node
);
294 // ---------------------------------------------------------------------------
295 // accelerator helpers
296 // ---------------------------------------------------------------------------
298 // create the wxAcceleratorEntries for our accels and put them into provided
299 // array - return the number of accels we have
300 size_t wxMenu::CopyAccels(wxAcceleratorEntry
*accels
) const
302 size_t count
= GetAccelCount();
303 for ( size_t n
= 0; n
< count
; n
++ )
305 *accels
++ = *m_accels
[n
];
311 #endif // wxUSE_ACCEL
313 // ---------------------------------------------------------------------------
314 // wxMenu functions implemented in wxMenuItem
315 // ---------------------------------------------------------------------------
317 void wxMenu::Enable(int id
, bool Flag
)
319 wxMenuItem
*item
= FindItemForId(id
);
320 wxCHECK_RET( item
!= NULL
, wxT("can't enable non-existing menu item") );
325 bool wxMenu::IsEnabled(int id
) const
327 wxMenuItem
*item
= FindItemForId(id
);
328 wxCHECK_MSG( item
!= NULL
, FALSE
, wxT("invalid item id") );
330 return item
->IsEnabled();
333 void wxMenu::Check(int id
, bool Flag
)
335 wxMenuItem
*item
= FindItemForId(id
);
336 wxCHECK_RET( item
!= NULL
, wxT("can't get status of non-existing menu item") );
341 bool wxMenu::IsChecked(int id
) const
343 wxMenuItem
*item
= FindItemForId(id
);
344 wxCHECK_MSG( item
!= NULL
, FALSE
, wxT("invalid item id") );
346 return item
->IsChecked();
349 void wxMenu::SetLabel(int id
, const wxString
& label
)
351 wxMenuItem
*item
= FindItemForId(id
);
352 wxCHECK_RET( item
, wxT("wxMenu::SetLabel: no such item") );
354 item
->SetText(label
);
357 wxString
wxMenu::GetLabel(int id
) const
360 wxMenuItem
*pItem
= FindItemForId(id
);
362 label
= pItem
->GetText();
364 wxFAIL_MSG(wxT("wxMenu::GetLabel: item doesn't exist"));
369 void wxMenu::SetHelpString(int itemId
, const wxString
& helpString
)
371 wxMenuItem
*item
= FindItemForId (itemId
);
373 item
->SetHelp(helpString
);
375 wxFAIL_MSG(wxT("wxMenu::SetHelpString: item doesn't exist"));
378 wxString
wxMenu::GetHelpString (int itemId
) const
381 wxMenuItem
*item
= FindItemForId (itemId
);
383 help
= item
->GetHelp();
385 wxFAIL_MSG(wxT("wxMenu::GetHelpString: item doesn't exist"));
390 // ---------------------------------------------------------------------------
392 // ---------------------------------------------------------------------------
394 void wxMenu::SetTitle(const wxString
& label
)
396 bool hasNoTitle
= m_title
.IsEmpty();
399 HMENU hMenu
= GetHmenu();
403 if ( !label
.IsEmpty() )
405 if ( !InsertMenu(hMenu
, 0u, MF_BYPOSITION
| MF_STRING
,
406 (unsigned)idMenuTitle
, m_title
) ||
407 !InsertMenu(hMenu
, 1u, MF_BYPOSITION
, (unsigned)-1, NULL
) )
409 wxLogLastError(wxT("InsertMenu"));
415 if ( label
.IsEmpty() )
417 // remove the title and the separator after it
418 if ( !RemoveMenu(hMenu
, 0, MF_BYPOSITION
) ||
419 !RemoveMenu(hMenu
, 0, MF_BYPOSITION
) )
421 wxLogLastError("RemoveMenu");
427 if ( !ModifyMenu(hMenu
, 0u,
428 MF_BYPOSITION
| MF_STRING
,
429 (unsigned)idMenuTitle
, m_title
) )
431 wxLogLastError("ModifyMenu");
437 // put the title string in bold face
438 if ( !m_title
.IsEmpty() )
441 mii
.cbSize
= sizeof(mii
);
442 mii
.fMask
= MIIM_STATE
;
443 mii
.fState
= MFS_DEFAULT
;
445 if ( !SetMenuItemInfo(hMenu
, (unsigned)idMenuTitle
, FALSE
, &mii
) )
447 wxLogLastError("SetMenuItemInfo");
453 const wxString
wxMenu::GetTitle() const
458 // ---------------------------------------------------------------------------
460 // ---------------------------------------------------------------------------
462 bool wxMenu::MSWCommand(WXUINT
WXUNUSED(param
), WXWORD id
)
464 // ignore commands from the menu title
466 // NB: VC++ generates wrong assembler for `if ( id != idMenuTitle )'!!
467 if ( id
!= (WXWORD
)idMenuTitle
)
469 wxCommandEvent
event(wxEVT_COMMAND_MENU_SELECTED
);
470 event
.SetEventObject( this );
473 ProcessCommand(event
);
479 bool wxMenu::ProcessCommand(wxCommandEvent
& event
)
481 bool processed
= FALSE
;
486 (void)(*(m_callback
))(*this, event
);
490 // Try the menu's event handler
491 if ( !processed
&& GetEventHandler())
493 processed
= GetEventHandler()->ProcessEvent(event
);
496 // Try the window the menu was popped up from (and up through the
498 wxWindow
*win
= GetInvokingWindow();
499 if ( !processed
&& win
)
500 processed
= win
->GetEventHandler()->ProcessEvent(event
);
505 // ---------------------------------------------------------------------------
507 // ---------------------------------------------------------------------------
509 // Finds the item id matching the given string, -1 if not found.
510 int wxMenu::FindItem (const wxString
& itemString
) const
512 wxString itemLabel
= wxStripMenuCodes(itemString
);
513 for ( wxNode
*node
= m_menuItems
.First(); node
; node
= node
->Next() )
515 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
516 if ( item
->IsSubMenu() )
518 int ans
= item
->GetSubMenu()->FindItem(itemString
);
519 if ( ans
!= wxNOT_FOUND
)
522 else if ( !item
->IsSeparator() )
524 wxString label
= wxStripMenuCodes(item
->GetText());
525 if ( itemLabel
== label
)
526 return item
->GetId();
533 wxMenuItem
*wxMenu::FindItemForId(int itemId
, wxMenu
** itemMenu
) const
538 wxMenuItem
*item
= NULL
;
539 for ( wxNode
*node
= m_menuItems
.First(); node
&& !item
; node
= node
->Next() )
541 item
= (wxMenuItem
*)node
->Data();
543 if ( item
->GetId() == itemId
)
546 *itemMenu
= (wxMenu
*)this;
548 else if ( item
->IsSubMenu() )
550 item
= item
->GetSubMenu()->FindItemForId(itemId
, itemMenu
);
554 // don't exit the loop
562 // ---------------------------------------------------------------------------
564 // ---------------------------------------------------------------------------
566 void wxMenu::Attach(wxMenuBar
*menubar
)
568 // menu can be in at most one menubar because otherwise they would both
569 // delete the menu pointer
570 wxASSERT_MSG( !m_menuBar
, wxT("menu belongs to 2 menubars, expect a crash") );
573 m_savehMenu
= m_hMenu
;
577 void wxMenu::Detach()
579 wxASSERT_MSG( m_menuBar
, wxT("can't detach menu if it's not attached") );
582 m_hMenu
= m_savehMenu
;
586 // ---------------------------------------------------------------------------
588 // ---------------------------------------------------------------------------
590 void wxMenuBar::Init()
592 m_eventHandler
= this;
593 m_menuBarFrame
= NULL
;
597 wxMenuBar::wxMenuBar()
602 wxMenuBar::wxMenuBar( long WXUNUSED(style
) )
607 wxMenuBar::wxMenuBar(int count
, wxMenu
*menus
[], const wxString titles
[])
611 m_titles
.Alloc(count
);
613 for ( int i
= 0; i
< count
; i
++ )
615 m_menus
.Append(menus
[i
]);
616 m_titles
.Add(titles
[i
]);
618 menus
[i
]->Attach(this);
622 wxMenuBar::~wxMenuBar()
626 // ---------------------------------------------------------------------------
628 // ---------------------------------------------------------------------------
630 void wxMenuBar::Refresh()
632 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
634 DrawMenuBar(GetHwndOf(m_menuBarFrame
));
637 WXHMENU
wxMenuBar::Create()
642 wxCHECK_MSG( !m_hMenu
, TRUE
, wxT("menubar already created") );
644 m_hMenu
= (WXHMENU
)::CreateMenu();
648 wxLogLastError("CreateMenu");
652 size_t count
= GetMenuCount();
653 for ( size_t i
= 0; i
< count
; i
++ )
655 if ( !::AppendMenu((HMENU
)m_hMenu
, MF_POPUP
| MF_STRING
,
656 (UINT
)m_menus
[i
]->GetHMenu(),
659 wxLogLastError("AppendMenu");
667 // ---------------------------------------------------------------------------
668 // wxMenuBar functions to work with the top level submenus
669 // ---------------------------------------------------------------------------
671 // NB: we don't support owner drawn top level items for now, if we do these
672 // functions would have to be changed to use wxMenuItem as well
674 void wxMenuBar::EnableTop(size_t pos
, bool enable
)
676 int flag
= enable
? MF_ENABLED
: MF_GRAYED
;;
678 EnableMenuItem((HMENU
)m_hMenu
, pos
, MF_BYPOSITION
| flag
);
683 void wxMenuBar::SetLabelTop(size_t pos
, const wxString
& label
)
686 UINT flagsOld
= ::GetMenuState((HMENU
)m_hMenu
, pos
, MF_BYPOSITION
);
687 if ( flagsOld
== 0xFFFFFFFF )
689 wxLogLastError(wxT("GetMenuState"));
694 if ( flagsOld
& MF_POPUP
)
696 // HIBYTE contains the number of items in the submenu in this case
698 id
= (UINT
)::GetSubMenu((HMENU
)m_hMenu
, pos
);
705 if ( ::ModifyMenu(GetHmenu(), pos
, MF_BYPOSITION
| MF_STRING
| flagsOld
,
706 id
, label
) == (int)0xFFFFFFFF )
708 wxLogLastError("ModifyMenu");
712 wxString
wxMenuBar::GetLabelTop(size_t pos
) const
714 int len
= ::GetMenuString((HMENU
)m_hMenu
, pos
, NULL
, 0, MF_BYCOMMAND
);
716 len
++; // for the NUL character
718 ::GetMenuString(GetHmenu(), pos
, label
.GetWriteBuf(len
), len
, MF_BYCOMMAND
);
719 label
.UngetWriteBuf();
724 int wxMenuBar::FindMenu(const wxString
& title
)
726 wxString menuTitle
= wxStripMenuCodes(title
);
728 size_t count
= GetMenuCount();
729 for ( size_t i
= 0; i
< count
; i
++ )
731 wxString title
= wxStripMenuCodes(m_titles
[i
]);
732 if ( menuTitle
== title
)
740 // ---------------------------------------------------------------------------
741 // wxMenuBar construction
742 // ---------------------------------------------------------------------------
744 wxMenu
*wxMenuBar::Replace(size_t pos
, wxMenu
*menu
, const wxString
& title
)
746 wxMenu
*menuOld
= wxMenuBarBase::Replace(pos
, menu
, title
);
749 m_titles
[pos
] = title
;
753 // can't use ModifyMenu() because it deletes the submenu it replaces
754 if ( !::RemoveMenu(GetHmenu(), (UINT
)pos
, MF_BYPOSITION
) )
756 wxLogLastError("RemoveMenu");
759 if ( !::InsertMenu(GetHmenu(), (UINT
)pos
,
760 MF_BYPOSITION
| MF_POPUP
| MF_STRING
,
761 (UINT
)GetHmenuOf(menu
), title
) )
763 wxLogLastError("InsertMenu");
772 bool wxMenuBar::Insert(size_t pos
, wxMenu
*menu
, const wxString
& title
)
774 if ( !wxMenuBarBase::Insert(pos
, menu
, title
) )
777 m_titles
.Insert(title
, pos
);
783 if ( !::InsertMenu(GetHmenu(), pos
,
784 MF_BYPOSITION
| MF_POPUP
| MF_STRING
,
785 (UINT
)GetHmenuOf(menu
), title
) )
787 wxLogLastError("InsertMenu");
796 bool wxMenuBar::Append(wxMenu
*menu
, const wxString
& title
)
798 WXHMENU submenu
= menu
? menu
->GetHMenu() : 0;
799 wxCHECK_MSG( submenu
, FALSE
, wxT("can't append invalid menu to menubar") );
805 if ( !::AppendMenu(GetHmenu(), MF_POPUP
| MF_STRING
,
806 (UINT
)submenu
, title
) )
808 wxLogLastError(wxT("AppendMenu"));
814 wxMenuBarBase::Append(menu
, title
);
821 wxMenu
*wxMenuBar::Remove(size_t pos
)
823 wxMenu
*menu
= wxMenuBarBase::Remove(pos
);
829 if ( !::RemoveMenu(GetHmenu(), (UINT
)pos
, MF_BYPOSITION
) )
831 wxLogLastError("RemoveMenu");
839 m_titles
.Remove(pos
);
844 void wxMenuBar::Attach(wxFrame
*frame
)
846 wxASSERT_MSG( !IsAttached(), wxT("menubar already attached!") );
848 m_menuBarFrame
= frame
;
851 // create the accel table - we consider that the menubar construction is
853 size_t nAccelCount
= 0;
854 size_t i
, count
= GetMenuCount();
855 for ( i
= 0; i
< count
; i
++ )
857 nAccelCount
+= m_menus
[i
]->GetAccelCount();
862 wxAcceleratorEntry
*accelEntries
= new wxAcceleratorEntry
[nAccelCount
];
865 for ( i
= 0; i
< count
; i
++ )
867 nAccelCount
+= m_menus
[i
]->CopyAccels(&accelEntries
[nAccelCount
]);
870 m_accelTable
= wxAcceleratorTable(nAccelCount
, accelEntries
);
872 delete [] accelEntries
;
874 #endif // wxUSE_ACCEL
877 void wxMenuBar::Detach()
879 // ::DestroyMenu((HMENU)m_hMenu);
880 m_hMenu
= (WXHMENU
)NULL
;
881 m_menuBarFrame
= NULL
;
885 // ---------------------------------------------------------------------------
886 // wxMenuBar searching for menu items
887 // ---------------------------------------------------------------------------
889 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
890 int wxMenuBar::FindMenuItem(const wxString
& menuString
,
891 const wxString
& itemString
) const
893 wxString menuLabel
= wxStripMenuCodes(menuString
);
894 size_t count
= GetMenuCount();
895 for ( size_t i
= 0; i
< count
; i
++ )
897 wxString title
= wxStripMenuCodes(m_titles
[i
]);
898 if ( menuString
== title
)
899 return m_menus
[i
]->FindItem(itemString
);
905 wxMenuItem
*wxMenuBar::FindItem(int id
, wxMenu
**itemMenu
) const
910 wxMenuItem
*item
= NULL
;
911 size_t count
= GetMenuCount();
912 for ( size_t i
= 0; !item
&& (i
< count
); i
++ )
914 item
= m_menus
[i
]->FindItemForId(id
, itemMenu
);
921 // ----------------------------------------------------------------------------
923 // ----------------------------------------------------------------------------
925 wxWindow
*wxMenu::GetWindow() const
927 if ( m_pInvokingWindow
!= NULL
)
928 return m_pInvokingWindow
;
929 else if ( m_menuBar
!= NULL
)
930 return m_menuBar
->GetFrame();
935 WXHMENU
wxMenu::GetHMenu() const
939 else if ( m_savehMenu
!= 0 )
942 wxFAIL_MSG(wxT("wxMenu without HMENU"));
947 // Update a menu and all submenus recursively. source is the object that has
948 // the update event handlers defined for it. If NULL, the menu or associated
949 // window will be used.
950 void wxMenu::UpdateUI(wxEvtHandler
* source
)
952 if (!source
&& GetInvokingWindow())
953 source
= GetInvokingWindow()->GetEventHandler();
955 source
= GetEventHandler();
959 wxNode
* node
= GetItems().First();
962 wxMenuItem
* item
= (wxMenuItem
*) node
->Data();
963 if ( !item
->IsSeparator() )
965 wxWindowID id
= item
->GetId();
966 wxUpdateUIEvent
event(id
);
967 event
.SetEventObject( source
);
969 if (source
->ProcessEvent(event
))
971 if (event
.GetSetText())
972 SetLabel(id
, event
.GetText());
973 if (event
.GetSetChecked())
974 Check(id
, event
.GetChecked());
975 if (event
.GetSetEnabled())
976 Enable(id
, event
.GetEnabled());
979 if (item
->GetSubMenu())
980 item
->GetSubMenu()->UpdateUI(source
);