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"
22 #pragma implementation "menuitem.h"
25 // For compilers that support precompilation, includes "wx.h".
26 #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
48 // ----------------------
51 #if !USE_SHARED_LIBRARY
52 IMPLEMENT_DYNAMIC_CLASS(wxMenu
, wxEvtHandler
)
53 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
, wxEvtHandler
)
56 // ============================================================================
58 // ============================================================================
62 // Construct a menu with optional title (then use append)
63 wxMenu::wxMenu(const wxString
& Title
, const wxFunction func
)
67 m_eventHandler
= this;
68 m_pInvokingWindow
= NULL
;
72 m_hMenu
= (WXHMENU
) CreatePopupMenu();
74 m_topLevelMenu
= this;
84 // The wxWindow destructor will take care of deleting the submenus.
88 DestroyMenu((HMENU
) m_hMenu
);
91 // Windows seems really bad on Menu de-allocation...
92 // After many try, here is what I do: RemoveMenu() will ensure
93 // that popup are "disconnected" from their parent; then call
94 // delete method on each child (which in turn do a recursive job),
95 // and finally, DestroyMenu()
97 // With that, BoundCheckers is happy, and no complaints...
101 N = GetMenuItemCount(m_hMenu);
103 for (i = N-1; i >= 0; i--)
104 RemoveMenu(m_hMenu, i, MF_BYPOSITION);
107 // How is deleting submenus in this loop any different from deleting
108 // the submenus in the children list, via ~wxWindow ?
109 // I'll reinstate this deletion for now and remove addition
110 // from children list (which doesn't exist now)
112 wxNode
*node
= m_menuItems
.First();
115 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
117 // Delete child menus.
118 // Beware: they must not be appended to children list!!!
119 // (because order of delete is significant)
120 if (item
->GetSubMenu())
121 item
->DeleteSubMenu();
123 wxNode
*next
= node
->Next();
130 DestroyMenu(m_hMenu);
135 void wxMenu::Break(void)
140 // function appends a new item or submenu to the menu
141 void wxMenu::Append(wxMenuItem
*pItem
)
143 wxCHECK_RET( pItem
!= NULL
, "can't append NULL item to the menu" );
145 m_menuItems
.Append(pItem
);
150 flags
|= MF_MENUBREAK
;
154 if ( pItem
->IsSeparator() ) {
155 flags
|= MF_SEPARATOR
;
158 // id is the numeric id for normal menu items and HMENU for submenus
160 wxMenu
*SubMenu
= pItem
->GetSubMenu();
161 if ( SubMenu
!= NULL
) {
162 wxASSERT( SubMenu
->m_hMenu
!= (WXHMENU
) NULL
);
164 id
= (UINT
)SubMenu
->m_hMenu
;
166 SubMenu
->m_topLevelMenu
= m_topLevelMenu
;
167 SubMenu
->m_parent
= this;
168 SubMenu
->m_savehMenu
= (WXHMENU
)id
;
169 SubMenu
->m_hMenu
= 0;
181 if ( pItem
->IsOwnerDrawn() ) { // want to get {Measure|Draw}Item messages?
182 // item draws itself, pass pointer to it in data parameter
183 flags
|= MF_OWNERDRAW
;
184 pData
= (LPCSTR
)pItem
;
189 // menu is just a normal string (passed in data parameter)
191 name
= pItem
->GetName();
192 pData
= (const char*) name
;
195 // VZ: what does this magic -2 mean? I just copied the code but have no idea
196 // about what it does... ###
197 if ( pItem
->GetId() == -2 ) {
198 flags
|= MF_DISABLED
| MF_GRAYED
;
201 HMENU hMenu
= (HMENU
)((m_hMenu
== 0) ? m_savehMenu
: m_hMenu
);
203 if ( !AppendMenu(hMenu
, flags
, id
, pData
) )
205 // wxLogLastError("AppendMenu");
211 void wxMenu::AppendSeparator(void)
213 Append(new wxMenuItem(this, ID_SEPARATOR
));
217 void wxMenu::Append(int Id
, const wxString
& label
, wxMenu
*SubMenu
,
218 const wxString
& helpString
)
220 Append(new wxMenuItem(this, Id
, label
, helpString
, FALSE
, SubMenu
));
223 // Ordinary menu item
224 void wxMenu::Append(int Id
, const wxString
& label
,
225 const wxString
& helpString
, bool checkable
)
227 // 'checkable' parameter is useless for Windows.
228 Append(new wxMenuItem(this, Id
, label
, helpString
, checkable
));
231 void wxMenu::Delete(int id
)
238 for (pos
= 0, node
= m_menuItems
.First(); node
; node
= node
->Next(), pos
++) {
239 item
= (wxMenuItem
*)node
->Data();
240 if (item
->GetId() == id
)
247 menu
= (HMENU
)(m_hMenu
? m_hMenu
: m_savehMenu
);
249 wxMenu
*pSubMenu
= item
->GetSubMenu();
250 if ( pSubMenu
!= NULL
) {
251 RemoveMenu(menu
, (UINT
)pos
, MF_BYPOSITION
);
252 pSubMenu
->m_hMenu
= pSubMenu
->m_savehMenu
;
253 pSubMenu
->m_savehMenu
= 0;
254 pSubMenu
->m_parent
= NULL
;
255 // RemoveChild(item->subMenu);
256 pSubMenu
->m_topLevelMenu
= NULL
;
257 // TODO: Why isn't subMenu deleted here???
258 // Will put this in for now. Assuming this is supposed
259 // to delete the menu, not just remove it.
260 item
->DeleteSubMenu();
263 DeleteMenu(menu
, (UINT
)pos
, MF_BYPOSITION
);
266 m_menuItems
.DeleteNode(node
);
270 void wxMenu::Enable(int Id
, bool Flag
)
272 wxMenuItem
*item
= FindItemForId(Id
);
273 wxCHECK_RET( item
!= NULL
, "can't enable non-existing menu item" );
278 bool wxMenu::Enabled(int Id
) const
280 wxMenuItem
*item
= FindItemForId(Id
);
281 wxCHECK( item
!= NULL
, FALSE
);
283 return item
->IsEnabled();
286 void wxMenu::Check(int Id
, bool Flag
)
288 wxMenuItem
*item
= FindItemForId(Id
);
289 wxCHECK_RET( item
!= NULL
, "can't get status of non-existing menu item" );
294 bool wxMenu::Checked(int Id
) const
296 wxMenuItem
*item
= FindItemForId(Id
);
297 wxCHECK( item
!= NULL
, FALSE
);
299 return item
->IsChecked();
302 void wxMenu::SetTitle(const wxString
& label
)
306 ModifyMenu((HMENU
)m_hMenu
, 0,
307 MF_BYPOSITION
| MF_STRING
| MF_DISABLED
,
308 (UINT
)-2, (const char *)m_title
);
309 else if (m_savehMenu
)
310 ModifyMenu((HMENU
)m_savehMenu
, 0,
311 MF_BYPOSITION
| MF_STRING
| MF_DISABLED
,
312 (UINT
)-2, (const char *)m_title
);
315 const wxString
& wxMenu::GetTitle() const
320 void wxMenu::SetLabel(int Id
, const wxString
& label
)
322 wxMenuItem
*item
= FindItemForId(Id
) ;
326 if (item
->GetSubMenu()==NULL
)
330 UINT was_flag
= GetMenuState((HMENU
)m_hMenu
,Id
,MF_BYCOMMAND
) ;
331 ModifyMenu((HMENU
)m_hMenu
,Id
,MF_BYCOMMAND
|MF_STRING
|was_flag
,Id
,(const char *)label
) ;
333 else if (m_savehMenu
)
335 UINT was_flag
= GetMenuState((HMENU
)m_savehMenu
,Id
,MF_BYCOMMAND
) ;
336 ModifyMenu((HMENU
)m_savehMenu
,Id
,MF_BYCOMMAND
|MF_STRING
|was_flag
,Id
,(const char *)label
) ;
341 wxMenu
*father
= item
->GetSubMenu()->m_topLevelMenu
;
342 wxNode
*node
= father
->m_menuItems
.First() ;
346 wxMenuItem
*matched
= (wxMenuItem
*)node
->Data() ;
350 node
= node
->Next() ;
352 // Here, we have the position.
353 ModifyMenu((HMENU
)father
->m_savehMenu
,i
,
354 MF_BYPOSITION
|MF_STRING
|MF_POPUP
,
355 (UINT
)item
->GetSubMenu()->m_savehMenu
,(const char *)label
) ;
357 item
->SetName(label
);
360 wxString
wxMenu::GetLabel(int Id
) const
362 static char tmp
[128] ;
365 len
= GetMenuString((HMENU
)m_hMenu
,Id
,tmp
,WXSIZEOF(tmp
) - 1,MF_BYCOMMAND
);
366 else if (m_savehMenu
)
367 len
= GetMenuString((HMENU
)m_savehMenu
,Id
,tmp
,WXSIZEOF(tmp
) - 1,MF_BYCOMMAND
);
371 return wxString(tmp
) ;
374 bool wxMenu::MSWCommand(WXUINT
WXUNUSED(param
), WXWORD id
)
376 wxCommandEvent
event(wxEVENT_TYPE_MENU_COMMAND
);
377 event
.SetEventObject( this );
380 ProcessCommand(event
);
384 // Finds the item id matching the given string, -1 if not found.
385 int wxMenu::FindItem (const wxString
& itemString
) const
389 wxStripMenuCodes ((char *)(const char *)itemString
, buf1
);
391 for (wxNode
* node
= m_menuItems
.First (); node
; node
= node
->Next ())
393 wxMenuItem
*item
= (wxMenuItem
*) node
->Data ();
394 if (item
->GetSubMenu())
396 int ans
= item
->GetSubMenu()->FindItem(itemString
);
400 if ( !item
->IsSeparator() )
402 wxStripMenuCodes((char *)item
->GetName().c_str(), buf2
);
403 if (strcmp(buf1
, buf2
) == 0)
404 return item
->GetId();
411 wxMenuItem
*wxMenu::FindItemForId(int itemId
, wxMenu
** itemMenu
) const
415 for (wxNode
* node
= m_menuItems
.First (); node
; node
= node
->Next ())
417 wxMenuItem
*item
= (wxMenuItem
*) node
->Data ();
419 if (item
->GetId() == itemId
)
422 *itemMenu
= (wxMenu
*) this;
426 if (item
->GetSubMenu())
428 wxMenuItem
*ans
= item
->GetSubMenu()->FindItemForId (itemId
, itemMenu
);
439 void wxMenu::SetHelpString(int itemId
, const wxString
& helpString
)
441 wxMenuItem
*item
= FindItemForId (itemId
);
443 item
->SetHelp(helpString
);
446 wxString
wxMenu::GetHelpString (int itemId
) const
448 wxMenuItem
*item
= FindItemForId (itemId
);
450 return (item
== NULL
) ? str
: item
->GetHelp();
453 void wxMenu::ProcessCommand(wxCommandEvent
& event
)
455 bool processed
= FALSE
;
460 (void) (*(m_callback
)) (*this, event
);
464 // Try the menu's event handler
465 if ( !processed
&& GetEventHandler())
467 processed
= GetEventHandler()->ProcessEvent(event
);
470 // Try the window the menu was popped up from (and up
471 // through the hierarchy)
472 if ( !processed
&& GetInvokingWindow())
473 processed
= GetInvokingWindow()->ProcessEvent(event
);
476 extern wxMenu
*wxCurrentPopupMenu
;
477 bool wxWindow::PopupMenu(wxMenu
*menu
, int x
, int y
)
479 menu
->SetInvokingWindow(this);
481 HWND hWnd
= (HWND
) GetHWND();
482 HMENU hMenu
= (HMENU
)menu
->m_hMenu
;
486 ::ClientToScreen(hWnd
, &point
);
487 wxCurrentPopupMenu
= menu
;
488 ::TrackPopupMenu(hMenu
, TPM_RIGHTBUTTON
, point
.x
, point
.y
, 0, hWnd
, NULL
);
490 wxCurrentPopupMenu
= NULL
;
492 menu
->SetInvokingWindow(NULL
);
498 wxMenuBar::wxMenuBar(void)
500 m_eventHandler
= this;
505 m_menuBarFrame
= NULL
;
509 wxMenuBar::wxMenuBar(int N
, wxMenu
*Menus
[], const wxString Titles
[])
511 m_eventHandler
= this;
514 m_titles
= new wxString
[N
];
516 for ( i
= 0; i
< N
; i
++ )
517 m_titles
[i
] = Titles
[i
];
518 m_menuBarFrame
= NULL
;
519 for (i
= 0; i
< N
; i
++)
520 m_menus
[i
]->m_menuBar
= (wxMenuBar
*) this;
525 wxMenuBar::~wxMenuBar(void)
527 // In fact, don't want menu to be destroyed before MDI
528 // shuffling has taken place. Let it be destroyed
529 // automatically when the window is destroyed.
531 // DestroyMenu(menu);
536 // See remarks in ::~wxMenu() method
537 // BEWARE - this may interfere with MDI fixes, so
538 // may need to remove
541 if (m_menuBarFrame && ((m_menuBarFrame->GetWindowStyleFlag() & wxSDI) == wxSDI))
544 N = GetMenuItemCount(menu) ;
545 for (i = N-1; i >= 0; i--)
546 RemoveMenu(menu, i, MF_BYPOSITION);
549 for (i
= 0; i
< m_menuCount
; i
++)
556 /* Don't destroy menu here, in case we're MDI and
557 need to do some shuffling with VALID menu handles.
564 // Must only be used AFTER menu has been attached to frame,
565 // otherwise use individual menus to enable/disable items
566 void wxMenuBar::Enable(int Id
, bool Flag
)
570 ms_flag
= MF_ENABLED
;
574 wxMenu
*itemMenu
= NULL
;
575 wxMenuItem
*item
= FindItemForId(Id
, &itemMenu
) ;
579 if (itemMenu
->m_hMenu
)
580 EnableMenuItem((HMENU
)itemMenu
->m_hMenu
, Id
, MF_BYCOMMAND
| ms_flag
);
581 else if (itemMenu
->m_savehMenu
)
582 EnableMenuItem((HMENU
)itemMenu
->m_savehMenu
, Id
, MF_BYCOMMAND
| ms_flag
);
586 void wxMenuBar::EnableTop(int pos
, bool flag
)
590 ms_flag
= MF_ENABLED
;
594 EnableMenuItem((HMENU
)m_hMenu
, pos
, MF_BYPOSITION
| ms_flag
);
595 DrawMenuBar((HWND
) m_menuBarFrame
->GetHWND()) ;
598 // Must only be used AFTER menu has been attached to frame,
599 // otherwise use individual menus
600 void wxMenuBar::Check(int Id
, bool Flag
)
602 wxMenu
*itemMenu
= NULL
;
603 wxMenuItem
*item
= FindItemForId(Id
, &itemMenu
) ;
607 if (!item
->IsCheckable())
611 ms_flag
= MF_CHECKED
;
613 ms_flag
= MF_UNCHECKED
;
615 if (itemMenu
->m_hMenu
)
616 CheckMenuItem((HMENU
)itemMenu
->m_hMenu
, Id
, MF_BYCOMMAND
| ms_flag
);
617 else if (itemMenu
->m_savehMenu
)
618 CheckMenuItem((HMENU
)itemMenu
->m_savehMenu
, Id
, MF_BYCOMMAND
| ms_flag
);
620 // CheckMenuItem((HMENU)m_hMenu, Id, MF_BYCOMMAND | ms_flag);
623 bool wxMenuBar::Checked(int Id
) const
625 wxMenu
*itemMenu
= NULL
;
626 wxMenuItem
*item
= FindItemForId(Id
, &itemMenu
) ;
632 if (itemMenu
->m_hMenu
)
633 Flag
=GetMenuState((HMENU
)itemMenu
->m_hMenu
, Id
, MF_BYCOMMAND
) ;
634 else if (itemMenu
->m_savehMenu
)
635 Flag
=GetMenuState((HMENU
)itemMenu
->m_savehMenu
, Id
, MF_BYCOMMAND
) ;
637 // Flag=GetMenuState((HMENU)m_hMenu, Id, MF_BYCOMMAND) ;
645 bool wxMenuBar::Enabled(int Id
) const
647 wxMenu
*itemMenu
= NULL
;
648 wxMenuItem
*item
= FindItemForId(Id
, &itemMenu
) ;
654 if (itemMenu
->m_hMenu
)
655 Flag
=GetMenuState((HMENU
)itemMenu
->m_hMenu
, Id
, MF_BYCOMMAND
) ;
656 else if (itemMenu
->m_savehMenu
)
657 Flag
=GetMenuState((HMENU
)itemMenu
->m_savehMenu
, Id
, MF_BYCOMMAND
) ;
666 void wxMenuBar::SetLabel(int Id
, const wxString
& label
)
668 wxMenu
*itemMenu
= NULL
;
669 wxMenuItem
*item
= FindItemForId(Id
, &itemMenu
) ;
674 if (itemMenu
->m_hMenu
)
676 UINT was_flag
= GetMenuState((HMENU
)itemMenu
->m_hMenu
,Id
,MF_BYCOMMAND
) ;
677 ModifyMenu((HMENU
)itemMenu
->m_hMenu
,Id
,MF_BYCOMMAND
|MF_STRING
|was_flag
,Id
,(const char *)label
) ;
679 else if (itemMenu
->m_savehMenu
)
681 UINT was_flag
= GetMenuState((HMENU
)itemMenu
->m_savehMenu
,Id
,MF_BYCOMMAND
) ;
682 ModifyMenu((HMENU
)itemMenu
->m_savehMenu
,Id
,MF_BYCOMMAND
|MF_STRING
|was_flag
,Id
,(const char *)label
) ;
686 wxString
wxMenuBar::GetLabel(int Id
) const
688 wxMenu
*itemMenu
= NULL
;
689 wxMenuItem
*item
= FindItemForId(Id
, &itemMenu
) ;
694 static char tmp
[128] ;
696 if (itemMenu
->m_hMenu
)
698 len
= GetMenuString((HMENU
)itemMenu
->m_hMenu
,Id
,tmp
,127,MF_BYCOMMAND
) ;
700 else if (itemMenu
->m_savehMenu
)
702 len
= GetMenuString((HMENU
)itemMenu
->m_savehMenu
,Id
,tmp
,127,MF_BYCOMMAND
) ;
705 // int len = GetMenuString((HMENU)m_hMenu,Id,tmp,127,MF_BYCOMMAND) ;
707 return wxString(tmp
) ;
710 void wxMenuBar::SetLabelTop(int pos
, const wxString
& label
)
712 UINT was_flag
= GetMenuState((HMENU
)m_hMenu
,pos
,MF_BYPOSITION
) ;
713 if (was_flag
&MF_POPUP
)
716 HMENU popup
= GetSubMenu((HMENU
)m_hMenu
,pos
) ;
717 ModifyMenu((HMENU
)m_hMenu
,pos
,MF_BYPOSITION
|MF_STRING
|was_flag
,(UINT
)popup
,(const char *)label
) ;
720 ModifyMenu((HMENU
)m_hMenu
,pos
,MF_BYPOSITION
|MF_STRING
|was_flag
,pos
,(const char *)label
) ;
723 wxString
wxMenuBar::GetLabelTop(int pos
) const
725 static char tmp
[128] ;
726 int len
= GetMenuString((HMENU
)m_hMenu
,pos
,tmp
,127,MF_BYPOSITION
) ;
728 return wxString(tmp
);
731 bool wxMenuBar::OnDelete(wxMenu
*a_menu
, int pos
)
736 if (RemoveMenu((HMENU
)m_hMenu
, (UINT
)pos
, MF_BYPOSITION
)) {
737 m_menus
[pos
]->m_hMenu
= m_menus
[pos
]->m_savehMenu
;
738 m_menus
[pos
]->m_savehMenu
= 0;
740 if (m_menuBarFrame
) {
741 DrawMenuBar((HWND
) m_menuBarFrame
->GetHWND()) ;
750 bool wxMenuBar::OnAppend(wxMenu
*a_menu
, const char *title
)
752 if (!a_menu
->m_hMenu
)
758 a_menu
->m_savehMenu
= a_menu
->m_hMenu
;
761 AppendMenu((HMENU
)m_hMenu
, MF_POPUP
| MF_STRING
, (UINT
)a_menu
->m_savehMenu
, title
);
763 DrawMenuBar((HWND
) m_menuBarFrame
->GetHWND());
768 void wxMenuBar::Append (wxMenu
* menu
, const wxString
& title
)
770 if (!OnAppend(menu
, title
))
774 wxMenu
**new_menus
= new wxMenu
*[m_menuCount
];
775 wxString
*new_titles
= new wxString
[m_menuCount
];
778 for (i
= 0; i
< m_menuCount
- 1; i
++)
780 new_menus
[i
] = m_menus
[i
];
782 new_titles
[i
] = m_titles
[i
];
791 m_titles
= new_titles
;
793 m_menus
[m_menuCount
- 1] = (wxMenu
*)menu
;
794 m_titles
[m_menuCount
- 1] = title
;
796 ((wxMenu
*)menu
)->m_menuBar
= (wxMenuBar
*) this;
797 ((wxMenu
*)menu
)->SetParent(this);
800 void wxMenuBar::Delete(wxMenu
* menu
, int i
)
806 for (ii
= 0; ii
< m_menuCount
; ii
++) {
807 if (m_menus
[ii
] == menu
)
810 if (ii
>= m_menuCount
)
813 if (ii
< 0 || ii
>= m_menuCount
)
818 if (!OnDelete(menu
, ii
))
821 menu
->SetParent(NULL
);
824 for (j
= ii
; j
< m_menuCount
; j
++) {
825 m_menus
[j
] = m_menus
[j
+ 1];
826 m_titles
[j
] = m_titles
[j
+ 1];
830 // Find the menu menuString, item itemString, and return the item id.
831 // Returns -1 if none found.
832 int wxMenuBar::FindMenuItem (const wxString
& menuString
, const wxString
& itemString
) const
836 wxStripMenuCodes ((char *)(const char *)menuString
, buf1
);
838 for (i
= 0; i
< m_menuCount
; i
++)
840 wxStripMenuCodes ((char *)(const char *)m_titles
[i
], buf2
);
841 if (strcmp (buf1
, buf2
) == 0)
842 return m_menus
[i
]->FindItem (itemString
);
847 wxMenuItem
*wxMenuBar::FindItemForId (int Id
, wxMenu
** itemMenu
) const
852 wxMenuItem
*item
= NULL
;
854 for (i
= 0; i
< m_menuCount
; i
++)
855 if ((item
= m_menus
[i
]->FindItemForId (Id
, itemMenu
)))
860 void wxMenuBar::SetHelpString (int Id
, const wxString
& helpString
)
863 for (i
= 0; i
< m_menuCount
; i
++)
865 if (m_menus
[i
]->FindItemForId (Id
))
867 m_menus
[i
]->SetHelpString (Id
, helpString
);
873 wxString
wxMenuBar::GetHelpString (int Id
) const
876 for (i
= 0; i
< m_menuCount
; i
++)
878 if (m_menus
[i
]->FindItemForId (Id
))
879 return wxString(m_menus
[i
]->GetHelpString (Id
));
884 wxWindow
*wxMenu::GetWindow() const
886 if ( m_pInvokingWindow
!= NULL
)
887 return m_pInvokingWindow
;
888 if ( m_menuBar
!= NULL
)
889 return m_menuBar
->m_menuBarFrame
;
893 WXHMENU
wxMenu::GetHMenu() const
897 else if ( m_savehMenu
!= 0 )