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 /////////////////////////////////////////////////////////////////////////////
13 // ============================================================================
14 // headers & declarations
15 // ============================================================================
21 #pragma implementation "menu.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
38 #include "wx/ownerdrw.h"
41 #include "wx/msw/private.h"
42 #include "wx/msw/menu.h"
43 #include "wx/menuitem.h"
46 // other standard headers
47 // ----------------------
50 #if !USE_SHARED_LIBRARY
51 IMPLEMENT_DYNAMIC_CLASS(wxMenu
, wxWindow
)
52 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
, wxWindow
)
55 // ============================================================================
57 // ============================================================================
61 // Construct a menu with optional title (then use append)
62 wxMenu::wxMenu(const wxString
& Title
, const wxFunction func
)
66 m_eventHandler
= this;
67 m_pInvokingWindow
= NULL
;
71 m_hMenu
= (WXHMENU
) CreatePopupMenu();
73 m_topLevelMenu
= this;
83 // The wxWindow destructor will take care of deleting the submenus.
87 DestroyMenu((HMENU
) m_hMenu
);
90 // Windows seems really bad on Menu de-allocation...
91 // After many try, here is what I do: RemoveMenu() will ensure
92 // that popup are "disconnected" from their parent; then call
93 // delete method on each child (which in turn do a recursive job),
94 // and finally, DestroyMenu()
96 // With that, BoundCheckers is happy, and no complaints...
100 N = GetMenuItemCount(m_hMenu);
102 for (i = N-1; i >= 0; i--)
103 RemoveMenu(m_hMenu, i, MF_BYPOSITION);
106 // How is deleting submenus in this loop any different from deleting
107 // the submenus in the children list, via ~wxWindow ?
108 // I'll reinstate this deletion for now and remove addition
109 // from children list (which doesn't exist now)
111 wxNode
*node
= m_menuItems
.First();
114 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
116 // Delete child menus.
117 // Beware: they must not be appended to children list!!!
118 // (because order of delete is significant)
119 if (item
->GetSubMenu())
120 item
->DeleteSubMenu();
122 wxNode
*next
= node
->Next();
129 DestroyMenu(m_hMenu);
134 void wxMenu::Break(void)
139 // function appends a new item or submenu to the menu
140 void wxMenu::Append(wxMenuItem
*pItem
)
142 wxCHECK_RET( pItem
!= NULL
, "can't append NULL item to the menu" );
144 m_menuItems
.Append(pItem
);
149 flags
|= MF_MENUBREAK
;
153 if ( pItem
->IsSeparator() ) {
154 flags
|= MF_SEPARATOR
;
157 // id is the numeric id for normal menu items and HMENU for submenus
159 wxMenu
*SubMenu
= pItem
->GetSubMenu();
160 if ( SubMenu
!= NULL
) {
161 wxASSERT( SubMenu
->m_hMenu
!= NULL
);
163 id
= (UINT
)SubMenu
->m_hMenu
;
165 SubMenu
->m_topLevelMenu
= m_topLevelMenu
;
166 SubMenu
->m_parent
= this;
167 SubMenu
->m_savehMenu
= (WXHMENU
)id
;
168 SubMenu
->m_hMenu
= 0;
180 if ( pItem
->IsOwnerDrawn() ) { // want to get {Measure|Draw}Item messages?
181 // item draws itself, pass pointer to it in data parameter
182 flags
|= MF_OWNERDRAW
;
183 pData
= (LPCSTR
)pItem
;
188 // menu is just a normal string (passed in data parameter)
190 name
= pItem
->GetName();
191 pData
= (const char*) name
;
194 // VZ: what does this magic -2 mean? I just copied the code but have no idea
195 // about what it does... ###
196 if ( pItem
->GetId() == -2 ) {
197 flags
|= MF_DISABLED
| MF_GRAYED
;
200 HMENU hMenu
= (HMENU
)((m_hMenu
== 0) ? m_savehMenu
: m_hMenu
);
202 if ( !AppendMenu(hMenu
, flags
, id
, pData
) )
204 // wxLogLastError("AppendMenu");
210 void wxMenu::AppendSeparator(void)
212 Append(new wxMenuItem(this, ID_SEPARATOR
));
216 void wxMenu::Append(int Id
, const wxString
& label
, wxMenu
*SubMenu
,
217 const wxString
& helpString
)
219 Append(new wxMenuItem(this, Id
, label
, helpString
, FALSE
, SubMenu
));
222 // Ordinary menu item
223 void wxMenu::Append(int Id
, const wxString
& label
,
224 const wxString
& helpString
, bool checkable
)
226 // 'checkable' parameter is useless for Windows.
227 Append(new wxMenuItem(this, Id
, label
, helpString
, checkable
));
230 void wxMenu::Delete(int id
)
237 for (pos
= 0, node
= m_menuItems
.First(); node
; node
= node
->Next(), pos
++) {
238 item
= (wxMenuItem
*)node
->Data();
239 if (item
->GetId() == id
)
246 menu
= (HMENU
)(m_hMenu
? m_hMenu
: m_savehMenu
);
248 wxMenu
*pSubMenu
= item
->GetSubMenu();
249 if ( pSubMenu
!= NULL
) {
250 RemoveMenu(menu
, (UINT
)pos
, MF_BYPOSITION
);
251 pSubMenu
->m_hMenu
= pSubMenu
->m_savehMenu
;
252 pSubMenu
->m_savehMenu
= 0;
253 pSubMenu
->m_parent
= NULL
;
254 // RemoveChild(item->subMenu);
255 pSubMenu
->m_topLevelMenu
= NULL
;
256 // TODO: Why isn't subMenu deleted here???
257 // Will put this in for now. Assuming this is supposed
258 // to delete the menu, not just remove it.
259 item
->DeleteSubMenu();
262 DeleteMenu(menu
, (UINT
)pos
, MF_BYPOSITION
);
265 m_menuItems
.DeleteNode(node
);
269 void wxMenu::Enable(int Id
, bool Flag
)
271 wxMenuItem
*item
= FindItemForId(Id
);
272 wxCHECK_RET( item
!= NULL
, "can't enable non-existing menu item" );
277 bool wxMenu::Enabled(int Id
) const
279 wxMenuItem
*item
= FindItemForId(Id
);
280 wxCHECK( item
!= NULL
, FALSE
);
282 return item
->IsEnabled();
285 void wxMenu::Check(int Id
, bool Flag
)
287 wxMenuItem
*item
= FindItemForId(Id
);
288 wxCHECK_RET( item
!= NULL
, "can't get status of non-existing menu item" );
293 bool wxMenu::Checked(int Id
) const
295 wxMenuItem
*item
= FindItemForId(Id
);
296 wxCHECK( item
!= NULL
, FALSE
);
298 return item
->IsChecked();
301 void wxMenu::SetTitle(const wxString
& label
)
305 ModifyMenu((HMENU
)m_hMenu
, 0,
306 MF_BYPOSITION
| MF_STRING
| MF_DISABLED
,
307 (UINT
)-2, (const char *)m_title
);
308 else if (m_savehMenu
)
309 ModifyMenu((HMENU
)m_savehMenu
, 0,
310 MF_BYPOSITION
| MF_STRING
| MF_DISABLED
,
311 (UINT
)-2, (const char *)m_title
);
314 const wxString
& wxMenu::GetTitle() const
319 void wxMenu::SetLabel(int Id
, const wxString
& label
)
321 wxMenuItem
*item
= FindItemForId(Id
) ;
325 if (item
->GetSubMenu()==NULL
)
329 UINT was_flag
= GetMenuState((HMENU
)m_hMenu
,Id
,MF_BYCOMMAND
) ;
330 ModifyMenu((HMENU
)m_hMenu
,Id
,MF_BYCOMMAND
|MF_STRING
|was_flag
,Id
,(const char *)label
) ;
332 else if (m_savehMenu
)
334 UINT was_flag
= GetMenuState((HMENU
)m_savehMenu
,Id
,MF_BYCOMMAND
) ;
335 ModifyMenu((HMENU
)m_savehMenu
,Id
,MF_BYCOMMAND
|MF_STRING
|was_flag
,Id
,(const char *)label
) ;
340 wxMenu
*father
= item
->GetSubMenu()->m_topLevelMenu
;
341 wxNode
*node
= father
->m_menuItems
.First() ;
345 wxMenuItem
*matched
= (wxMenuItem
*)node
->Data() ;
349 node
= node
->Next() ;
351 // Here, we have the position.
352 ModifyMenu((HMENU
)father
->m_savehMenu
,i
,
353 MF_BYPOSITION
|MF_STRING
|MF_POPUP
,
354 (UINT
)item
->GetSubMenu()->m_savehMenu
,(const char *)label
) ;
356 item
->SetName(label
);
359 wxString
wxMenu::GetLabel(int Id
) const
361 static char tmp
[128] ;
364 len
= GetMenuString((HMENU
)m_hMenu
,Id
,tmp
,WXSIZEOF(tmp
) - 1,MF_BYCOMMAND
);
365 else if (m_savehMenu
)
366 len
= GetMenuString((HMENU
)m_savehMenu
,Id
,tmp
,WXSIZEOF(tmp
) - 1,MF_BYCOMMAND
);
370 return wxString(tmp
) ;
373 bool wxMenu::MSWCommand(const WXUINT
WXUNUSED(param
), const WXWORD id
)
375 wxCommandEvent
event(wxEVENT_TYPE_MENU_COMMAND
);
376 event
.SetEventObject( this );
379 ProcessCommand(event
);
383 // Finds the item id matching the given string, -1 if not found.
384 int wxMenu::FindItem (const wxString
& itemString
) const
388 wxStripMenuCodes ((char *)(const char *)itemString
, buf1
);
390 for (wxNode
* node
= m_menuItems
.First (); node
; node
= node
->Next ())
392 wxMenuItem
*item
= (wxMenuItem
*) node
->Data ();
393 if (item
->GetSubMenu())
395 int ans
= item
->GetSubMenu()->FindItem(itemString
);
399 if ( !item
->IsSeparator() )
401 wxStripMenuCodes((char *)item
->GetName().c_str(), buf2
);
402 if (strcmp(buf1
, buf2
) == 0)
403 return item
->GetId();
410 wxMenuItem
*wxMenu::FindItemForId(const int itemId
, wxMenu
** itemMenu
) const
414 for (wxNode
* node
= m_menuItems
.First (); node
; node
= node
->Next ())
416 wxMenuItem
*item
= (wxMenuItem
*) node
->Data ();
418 if (item
->GetId() == itemId
)
421 *itemMenu
= (wxMenu
*) this;
425 if (item
->GetSubMenu())
427 wxMenuItem
*ans
= item
->GetSubMenu()->FindItemForId (itemId
, itemMenu
);
438 void wxMenu::SetHelpString(const int itemId
, const wxString
& helpString
)
440 wxMenuItem
*item
= FindItemForId (itemId
);
442 item
->SetHelp(helpString
);
445 wxString
wxMenu::GetHelpString (const int itemId
) const
447 wxMenuItem
*item
= FindItemForId (itemId
);
449 return (item
== NULL
) ? str
: item
->GetHelp();
452 void wxMenu::ProcessCommand(wxCommandEvent
& event
)
454 bool processed
= FALSE
;
459 (void) (*(m_callback
)) (*this, event
);
463 // Try the menu's event handler
464 if ( !processed
&& GetEventHandler())
466 processed
= GetEventHandler()->ProcessEvent(event
);
469 // Try the window the menu was popped up from (and up
470 // through the hierarchy)
471 if ( !processed
&& GetInvokingWindow())
472 processed
= GetInvokingWindow()->ProcessEvent(event
);
475 extern wxMenu
*wxCurrentPopupMenu
;
476 bool wxWindow::PopupMenu(wxMenu
*menu
, const int x
, const int y
)
478 menu
->SetInvokingWindow(this);
480 HWND hWnd
= (HWND
) GetHWND();
481 HMENU hMenu
= (HMENU
)menu
->m_hMenu
;
485 ::ClientToScreen(hWnd
, &point
);
486 wxCurrentPopupMenu
= menu
;
487 ::TrackPopupMenu(hMenu
, TPM_RIGHTBUTTON
, point
.x
, point
.y
, 0, hWnd
, NULL
);
489 wxCurrentPopupMenu
= NULL
;
491 menu
->SetInvokingWindow(NULL
);
497 wxMenuBar::wxMenuBar(void)
499 m_eventHandler
= this;
504 m_menuBarFrame
= NULL
;
508 wxMenuBar::wxMenuBar(const int N
, wxMenu
*Menus
[], const wxString Titles
[])
510 m_eventHandler
= this;
513 m_titles
= new wxString
[N
];
515 for ( i
= 0; i
< N
; i
++ )
516 m_titles
[i
] = Titles
[i
];
517 m_menuBarFrame
= NULL
;
518 for (i
= 0; i
< N
; i
++)
519 m_menus
[i
]->m_menuBar
= (wxMenuBar
*) this;
524 wxMenuBar::~wxMenuBar(void)
526 // In fact, don't want menu to be destroyed before MDI
527 // shuffling has taken place. Let it be destroyed
528 // automatically when the window is destroyed.
530 // DestroyMenu(menu);
535 // See remarks in ::~wxMenu() method
536 // BEWARE - this may interfere with MDI fixes, so
537 // may need to remove
540 if (m_menuBarFrame && ((m_menuBarFrame->GetWindowStyleFlag() & wxSDI) == wxSDI))
543 N = GetMenuItemCount(menu) ;
544 for (i = N-1; i >= 0; i--)
545 RemoveMenu(menu, i, MF_BYPOSITION);
548 for (i
= 0; i
< m_menuCount
; i
++)
555 /* Don't destroy menu here, in case we're MDI and
556 need to do some shuffling with VALID menu handles.
563 // Must only be used AFTER menu has been attached to frame,
564 // otherwise use individual menus to enable/disable items
565 void wxMenuBar::Enable(const int Id
, const bool Flag
)
569 ms_flag
= MF_ENABLED
;
573 wxMenu
*itemMenu
= NULL
;
574 wxMenuItem
*item
= FindItemForId(Id
, &itemMenu
) ;
578 if (itemMenu
->m_hMenu
)
579 EnableMenuItem((HMENU
)itemMenu
->m_hMenu
, Id
, MF_BYCOMMAND
| ms_flag
);
580 else if (itemMenu
->m_savehMenu
)
581 EnableMenuItem((HMENU
)itemMenu
->m_savehMenu
, Id
, MF_BYCOMMAND
| ms_flag
);
585 void wxMenuBar::EnableTop(const int pos
, const bool flag
)
589 ms_flag
= MF_ENABLED
;
593 EnableMenuItem((HMENU
)m_hMenu
, pos
, MF_BYPOSITION
| ms_flag
);
594 DrawMenuBar((HWND
) m_menuBarFrame
->GetHWND()) ;
597 // Must only be used AFTER menu has been attached to frame,
598 // otherwise use individual menus
599 void wxMenuBar::Check(const int Id
, const bool Flag
)
601 wxMenu
*itemMenu
= NULL
;
602 wxMenuItem
*item
= FindItemForId(Id
, &itemMenu
) ;
606 if (!item
->IsCheckable())
610 ms_flag
= MF_CHECKED
;
612 ms_flag
= MF_UNCHECKED
;
614 if (itemMenu
->m_hMenu
)
615 CheckMenuItem((HMENU
)itemMenu
->m_hMenu
, Id
, MF_BYCOMMAND
| ms_flag
);
616 else if (itemMenu
->m_savehMenu
)
617 CheckMenuItem((HMENU
)itemMenu
->m_savehMenu
, Id
, MF_BYCOMMAND
| ms_flag
);
619 // CheckMenuItem((HMENU)m_hMenu, Id, MF_BYCOMMAND | ms_flag);
622 bool wxMenuBar::Checked(const int Id
) const
624 wxMenu
*itemMenu
= NULL
;
625 wxMenuItem
*item
= FindItemForId(Id
, &itemMenu
) ;
631 if (itemMenu
->m_hMenu
)
632 Flag
=GetMenuState((HMENU
)itemMenu
->m_hMenu
, Id
, MF_BYCOMMAND
) ;
633 else if (itemMenu
->m_savehMenu
)
634 Flag
=GetMenuState((HMENU
)itemMenu
->m_savehMenu
, Id
, MF_BYCOMMAND
) ;
636 // Flag=GetMenuState((HMENU)m_hMenu, Id, MF_BYCOMMAND) ;
644 bool wxMenuBar::Enabled(const int Id
) const
646 wxMenu
*itemMenu
= NULL
;
647 wxMenuItem
*item
= FindItemForId(Id
, &itemMenu
) ;
653 if (itemMenu
->m_hMenu
)
654 Flag
=GetMenuState((HMENU
)itemMenu
->m_hMenu
, Id
, MF_BYCOMMAND
) ;
655 else if (itemMenu
->m_savehMenu
)
656 Flag
=GetMenuState((HMENU
)itemMenu
->m_savehMenu
, Id
, MF_BYCOMMAND
) ;
665 void wxMenuBar::SetLabel(const int Id
, const wxString
& label
)
667 wxMenu
*itemMenu
= NULL
;
668 wxMenuItem
*item
= FindItemForId(Id
, &itemMenu
) ;
673 if (itemMenu
->m_hMenu
)
675 UINT was_flag
= GetMenuState((HMENU
)itemMenu
->m_hMenu
,Id
,MF_BYCOMMAND
) ;
676 ModifyMenu((HMENU
)itemMenu
->m_hMenu
,Id
,MF_BYCOMMAND
|MF_STRING
|was_flag
,Id
,(const char *)label
) ;
678 else if (itemMenu
->m_savehMenu
)
680 UINT was_flag
= GetMenuState((HMENU
)itemMenu
->m_savehMenu
,Id
,MF_BYCOMMAND
) ;
681 ModifyMenu((HMENU
)itemMenu
->m_savehMenu
,Id
,MF_BYCOMMAND
|MF_STRING
|was_flag
,Id
,(const char *)label
) ;
685 wxString
wxMenuBar::GetLabel(const int Id
) const
687 wxMenu
*itemMenu
= NULL
;
688 wxMenuItem
*item
= FindItemForId(Id
, &itemMenu
) ;
693 static char tmp
[128] ;
695 if (itemMenu
->m_hMenu
)
697 len
= GetMenuString((HMENU
)itemMenu
->m_hMenu
,Id
,tmp
,127,MF_BYCOMMAND
) ;
699 else if (itemMenu
->m_savehMenu
)
701 len
= GetMenuString((HMENU
)itemMenu
->m_savehMenu
,Id
,tmp
,127,MF_BYCOMMAND
) ;
704 // int len = GetMenuString((HMENU)m_hMenu,Id,tmp,127,MF_BYCOMMAND) ;
706 return wxString(tmp
) ;
709 void wxMenuBar::SetLabelTop(const int pos
, const wxString
& label
)
711 UINT was_flag
= GetMenuState((HMENU
)m_hMenu
,pos
,MF_BYPOSITION
) ;
712 if (was_flag
&MF_POPUP
)
715 HMENU popup
= GetSubMenu((HMENU
)m_hMenu
,pos
) ;
716 ModifyMenu((HMENU
)m_hMenu
,pos
,MF_BYPOSITION
|MF_STRING
|was_flag
,(UINT
)popup
,(const char *)label
) ;
719 ModifyMenu((HMENU
)m_hMenu
,pos
,MF_BYPOSITION
|MF_STRING
|was_flag
,pos
,(const char *)label
) ;
722 wxString
wxMenuBar::GetLabelTop(const int pos
) const
724 static char tmp
[128] ;
725 int len
= GetMenuString((HMENU
)m_hMenu
,pos
,tmp
,127,MF_BYPOSITION
) ;
727 return wxString(tmp
);
730 bool wxMenuBar::OnDelete(wxMenu
*a_menu
, const int pos
)
735 if (RemoveMenu((HMENU
)m_hMenu
, (UINT
)pos
, MF_BYPOSITION
)) {
736 m_menus
[pos
]->m_hMenu
= m_menus
[pos
]->m_savehMenu
;
737 m_menus
[pos
]->m_savehMenu
= 0;
739 if (m_menuBarFrame
) {
740 DrawMenuBar((HWND
) m_menuBarFrame
->GetHWND()) ;
749 bool wxMenuBar::OnAppend(wxMenu
*a_menu
, const char *title
)
751 if (!a_menu
->m_hMenu
)
757 a_menu
->m_savehMenu
= a_menu
->m_hMenu
;
760 AppendMenu((HMENU
)m_hMenu
, MF_POPUP
| MF_STRING
, (UINT
)a_menu
->m_savehMenu
, title
);
762 DrawMenuBar((HWND
) m_menuBarFrame
->GetHWND());
767 void wxMenuBar::Append (wxMenu
* menu
, const wxString
& title
)
769 if (!OnAppend(menu
, title
))
773 wxMenu
**new_menus
= new wxMenu
*[m_menuCount
];
774 wxString
*new_titles
= new wxString
[m_menuCount
];
777 for (i
= 0; i
< m_menuCount
- 1; i
++)
779 new_menus
[i
] = m_menus
[i
];
781 new_titles
[i
] = m_titles
[i
];
790 m_titles
= new_titles
;
792 m_menus
[m_menuCount
- 1] = (wxMenu
*)menu
;
793 m_titles
[m_menuCount
- 1] = title
;
795 ((wxMenu
*)menu
)->m_menuBar
= (wxMenuBar
*) this;
796 ((wxMenu
*)menu
)->SetParent(this);
799 void wxMenuBar::Delete(wxMenu
* menu
, const int i
)
805 for (ii
= 0; ii
< m_menuCount
; ii
++) {
806 if (m_menus
[ii
] == menu
)
809 if (ii
>= m_menuCount
)
812 if (ii
< 0 || ii
>= m_menuCount
)
817 if (!OnDelete(menu
, ii
))
820 menu
->SetParent(NULL
);
823 for (j
= ii
; j
< m_menuCount
; j
++) {
824 m_menus
[j
] = m_menus
[j
+ 1];
825 m_titles
[j
] = m_titles
[j
+ 1];
829 // Find the menu menuString, item itemString, and return the item id.
830 // Returns -1 if none found.
831 int wxMenuBar::FindMenuItem (const wxString
& menuString
, const wxString
& itemString
) const
835 wxStripMenuCodes ((char *)(const char *)menuString
, buf1
);
837 for (i
= 0; i
< m_menuCount
; i
++)
839 wxStripMenuCodes ((char *)(const char *)m_titles
[i
], buf2
);
840 if (strcmp (buf1
, buf2
) == 0)
841 return m_menus
[i
]->FindItem (itemString
);
846 wxMenuItem
*wxMenuBar::FindItemForId (const int Id
, wxMenu
** itemMenu
) const
851 wxMenuItem
*item
= NULL
;
853 for (i
= 0; i
< m_menuCount
; i
++)
854 if ((item
= m_menus
[i
]->FindItemForId (Id
, itemMenu
)))
859 void wxMenuBar::SetHelpString (const int Id
, const wxString
& helpString
)
862 for (i
= 0; i
< m_menuCount
; i
++)
864 if (m_menus
[i
]->FindItemForId (Id
))
866 m_menus
[i
]->SetHelpString (Id
, helpString
);
872 wxString
wxMenuBar::GetHelpString (const int Id
) const
875 for (i
= 0; i
< m_menuCount
; i
++)
877 if (m_menus
[i
]->FindItemForId (Id
))
878 return wxString(m_menus
[i
]->GetHelpString (Id
));
883 wxWindow
*wxMenu::GetWindow() const
885 if ( m_pInvokingWindow
!= NULL
)
886 return m_pInvokingWindow
;
887 if ( m_menuBar
!= NULL
)
888 return m_menuBar
->m_menuBarFrame
;
892 WXHMENU
wxMenu::GetHMenu() const
896 else if ( m_savehMenu
!= 0 )