1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxMenu, wxMenuBar, wxMenuItem
4 // Author: Stefan Csomor
8 // Copyright: (c) Stefan Csomor
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
14 #pragma implementation "menu.h"
15 #pragma implementation "menuitem.h"
18 // ============================================================================
19 // headers & declarations
20 // ============================================================================
27 #include "wx/menuitem.h"
28 #include "wx/window.h"
33 #include "wx/mac/uma.h"
35 // other standard headers
36 // ----------------------
39 #if !USE_SHARED_LIBRARY
40 IMPLEMENT_DYNAMIC_CLASS(wxMenu
, wxEvtHandler
)
41 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
, wxEvtHandler
)
44 // the (popup) menu title has this special id
45 static const int idMenuTitle
= -2;
46 static MenuItemIndex firstUserHelpMenuItem
= 0 ;
48 const short kwxMacMenuBarResource
= 1 ;
49 const short kwxMacAppleMenuId
= 1 ;
51 // ============================================================================
53 // ============================================================================
54 static void wxMenubarUnsetInvokingWindow( wxMenu
*menu
) ;
55 static void wxMenubarSetInvokingWindow( wxMenu
*menu
, wxWindow
*win
);
59 // Construct a menu with optional title (then use append)
62 short wxMenu::s_macNextMenuId
= 3 ;
64 short wxMenu::s_macNextMenuId
= 2 ;
70 m_startRadioGroup
= -1;
73 m_macMenuId
= s_macNextMenuId
++;
74 m_hMenu
= UMANewMenu(m_macMenuId
, m_title
);
78 wxLogLastError("UMANewMenu failed");
81 // if we have a title, insert it in the beginning of the menu
84 Append(idMenuTitle
, m_title
) ;
91 if (MAC_WXHMENU(m_hMenu
))
92 ::DisposeMenu(MAC_WXHMENU(m_hMenu
));
97 // not available on the mac platform
100 void wxMenu::Attach(wxMenuBarBase
*menubar
)
102 wxMenuBase::Attach(menubar
);
107 // function appends a new item or submenu to the menu
108 // append a new item or submenu to the menu
109 bool wxMenu::DoInsertOrAppend(wxMenuItem
*pItem
, size_t pos
)
111 wxASSERT_MSG( pItem
!= NULL
, "can't append NULL item to the menu" );
113 if ( pItem
->IsSeparator() )
115 if ( pos
== (size_t)-1 )
116 MacAppendMenu(MAC_WXHMENU(m_hMenu
), "\p-");
118 MacInsertMenuItem(MAC_WXHMENU(m_hMenu
), "\p-" , pos
);
122 wxMenu
*pSubMenu
= pItem
->GetSubMenu() ;
123 if ( pSubMenu
!= NULL
)
125 wxASSERT_MSG( pSubMenu
->m_hMenu
!= NULL
, "invalid submenu added");
126 pSubMenu
->m_menuParent
= this ;
128 if (wxMenuBar::MacGetInstalledMenuBar() == m_menuBar
)
129 ::InsertMenu( MAC_WXHMENU( pSubMenu
->m_hMenu
) , -1 ) ;
131 if ( pos
== (size_t)-1 )
132 UMAAppendSubMenuItem(MAC_WXHMENU(m_hMenu
), pItem
->GetText(), pSubMenu
->m_macMenuId
);
134 UMAInsertSubMenuItem(MAC_WXHMENU(m_hMenu
), pItem
->GetText() , pos
, pSubMenu
->m_macMenuId
);
135 pItem
->UpdateItemBitmap() ;
136 pItem
->UpdateItemStatus() ;
140 if ( pos
== (size_t)-1 )
142 UMAAppendMenuItem(MAC_WXHMENU(m_hMenu
), "a" );
143 pos
= CountMenuItems(MAC_WXHMENU(m_hMenu
)) ;
147 UMAInsertMenuItem(MAC_WXHMENU(m_hMenu
), "a" , pos
);
150 SetMenuItemCommandID( MAC_WXHMENU(m_hMenu
) , pos
, pItem
->GetId() ) ;
151 pItem
->UpdateItemText() ;
152 pItem
->UpdateItemBitmap() ;
153 pItem
->UpdateItemStatus() ;
155 if ( pItem
->GetId() == idMenuTitle
)
157 UMAEnableMenuItem(MAC_WXHMENU(m_hMenu
) , pos
, false ) ;
161 // if we're already attached to the menubar, we must update it
164 m_menuBar
->Refresh();
169 void wxMenu::EndRadioGroup()
171 // we're not inside a radio group any longer
172 m_startRadioGroup
= -1;
175 bool wxMenu::DoAppend(wxMenuItem
*item
)
177 wxCHECK_MSG( item
, FALSE
, _T("NULL item in wxMenu::DoAppend") );
181 if ( item
->GetKind() == wxITEM_RADIO
)
183 int count
= GetMenuItemCount();
185 if ( m_startRadioGroup
== -1 )
187 // start a new radio group
188 m_startRadioGroup
= count
;
190 // for now it has just one element
191 item
->SetAsRadioGroupStart();
192 item
->SetRadioGroupEnd(m_startRadioGroup
);
194 // ensure that we have a checked item in the radio group
197 else // extend the current radio group
199 // we need to update its end item
200 item
->SetRadioGroupStart(m_startRadioGroup
);
201 wxMenuItemList::Node
*node
= GetMenuItems().Item(m_startRadioGroup
);
205 node
->GetData()->SetRadioGroupEnd(count
);
209 wxFAIL_MSG( _T("where is the radio group start item?") );
213 else // not a radio item
218 if ( !wxMenuBase::DoAppend(item
) || !DoInsertOrAppend(item
) )
225 // check the item initially
232 bool wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
234 return wxMenuBase::DoInsert(pos
, item
) && DoInsertOrAppend(item
, pos
);
237 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
239 // we need to find the items position in the child list
241 wxMenuItemList::Node
*node
= GetMenuItems().GetFirst();
242 for ( pos
= 0; node
; pos
++ )
244 if ( node
->GetData() == item
)
247 node
= node
->GetNext();
250 // DoRemove() (unlike Remove) can only be called for existing item!
251 wxCHECK_MSG( node
, NULL
, wxT("bug in wxMenu::Remove logic") );
253 ::DeleteMenuItem(MAC_WXHMENU(m_hMenu
) , pos
+ 1);
257 // otherwise, the change won't be visible
258 m_menuBar
->Refresh();
261 // and from internal data structures
262 return wxMenuBase::DoRemove(item
);
265 void wxMenu::SetTitle(const wxString
& label
)
268 UMASetMenuTitle(MAC_WXHMENU(m_hMenu
) , label
) ;
270 bool wxMenu::ProcessCommand(wxCommandEvent
& event
)
272 bool processed
= FALSE
;
274 #if WXWIN_COMPATIBILITY
278 (void)(*(m_callback
))(*this, event
);
281 #endif WXWIN_COMPATIBILITY
283 // Try the menu's event handler
284 if ( !processed
&& GetEventHandler())
286 processed
= GetEventHandler()->ProcessEvent(event
);
289 // Try the window the menu was popped up from (and up through the
291 wxWindow
*win
= GetInvokingWindow();
292 if ( !processed
&& win
)
293 processed
= win
->GetEventHandler()->ProcessEvent(event
);
299 // ---------------------------------------------------------------------------
301 // ---------------------------------------------------------------------------
303 wxWindow
*wxMenu::GetWindow() const
305 if ( m_invokingWindow
!= NULL
)
306 return m_invokingWindow
;
307 else if ( m_menuBar
!= NULL
)
308 return (wxWindow
*) m_menuBar
->GetFrame();
313 // helper functions returning the mac menu position for a certain item, note that this is
314 // mac-wise 1 - based, i.e. the first item has index 1 whereas on MSWin it has pos 0
316 int wxMenu::MacGetIndexFromId( int id
)
319 wxMenuItemList::Node
*node
= GetMenuItems().GetFirst();
320 for ( pos
= 0; node
; pos
++ )
322 if ( node
->GetData()->GetId() == id
)
325 node
= node
->GetNext();
334 int wxMenu::MacGetIndexFromItem( wxMenuItem
*pItem
)
337 wxMenuItemList::Node
*node
= GetMenuItems().GetFirst();
338 for ( pos
= 0; node
; pos
++ )
340 if ( node
->GetData() == pItem
)
343 node
= node
->GetNext();
352 void wxMenu::MacEnableMenu( bool bDoEnable
)
354 UMAEnableMenuItem(MAC_WXHMENU(m_hMenu
) , 0 , bDoEnable
) ;
363 Mac Implementation note :
365 The Mac has only one global menubar, so we attempt to install the currently
366 active menubar from a frame, we currently don't take into account mdi-frames
367 which would ask for menu-merging
369 Secondly there is no mac api for changing a menubar that is not the current
370 menubar, so we have to wait for preparing the actual menubar until the
371 wxMenubar is to be used
373 We can in subsequent versions use MacInstallMenuBar to provide some sort of
374 auto-merge for MDI in case this will be necessary
378 wxMenuBar
* wxMenuBar::s_macInstalledMenuBar
= NULL
;
380 void wxMenuBar::Init()
382 m_eventHandler
= this;
383 m_menuBarFrame
= NULL
;
384 m_invokingWindow
= (wxWindow
*) NULL
;
387 wxMenuBar::wxMenuBar()
392 wxMenuBar::wxMenuBar( long WXUNUSED(style
) )
398 wxMenuBar::wxMenuBar(int count
, wxMenu
*menus
[], const wxString titles
[])
402 m_titles
.Alloc(count
);
404 for ( int i
= 0; i
< count
; i
++ )
406 m_menus
.Append(menus
[i
]);
407 m_titles
.Add(titles
[i
]);
409 menus
[i
]->Attach(this);
413 wxMenuBar::~wxMenuBar()
415 if (s_macInstalledMenuBar
== this)
418 s_macInstalledMenuBar
= NULL
;
423 void wxMenuBar::Refresh(bool WXUNUSED(eraseBackground
), const wxRect
*WXUNUSED(rect
))
425 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
430 void wxMenuBar::MacInstallMenuBar()
432 if ( s_macInstalledMenuBar
== this )
435 wxStAppResource resload
;
437 Handle menubar
= ::GetNewMBar( kwxMacMenuBarResource
) ;
439 wxCHECK_RET( menubar
!= NULL
, "can't read MBAR resource" );
440 ::SetMenuBar( menubar
) ;
441 #if TARGET_API_MAC_CARBON
442 ::DisposeMenuBar( menubar
) ;
444 ::DisposeHandle( menubar
) ;
447 #if TARGET_API_MAC_OS8
448 MenuHandle menu
= ::GetMenuHandle( kwxMacAppleMenuId
) ;
449 if ( CountMenuItems( menu
) == 2 )
451 ::AppendResMenu(menu
, 'DRVR');
455 // clean-up the help menu before adding new items
456 MenuHandle mh
= NULL
;
457 if ( UMAGetHelpMenu( &mh
, &firstUserHelpMenuItem
) == noErr
)
459 for ( int i
= CountMenuItems( mh
) ; i
>= firstUserHelpMenuItem
; --i
)
461 DeleteMenuItem( mh
, i
) ;
469 if ( UMAGetSystemVersion() >= 0x1000 && wxApp::s_macPreferencesMenuItemId
)
471 wxMenuItem
*item
= FindItem( wxApp::s_macPreferencesMenuItemId
, NULL
) ;
472 if ( item
== NULL
|| !(item
->IsEnabled()) )
473 DisableMenuCommand( NULL
, kHICommandPreferences
) ;
475 EnableMenuCommand( NULL
, kHICommandPreferences
) ;
478 for (size_t i
= 0; i
< m_menus
.GetCount(); i
++)
481 wxMenuItemList::Node
*node
;
484 wxMenu
* menu
= m_menus
[i
] , *subMenu
= NULL
;
486 if( m_titles
[i
] == "?" || m_titles
[i
] == "&?" || m_titles
[i
] == wxApp::s_macHelpMenuTitleName
)
493 for (pos
= 0 , node
= menu
->GetMenuItems().GetFirst(); node
; node
= node
->GetNext(), pos
++)
495 item
= (wxMenuItem
*)node
->GetData();
496 subMenu
= item
->GetSubMenu() ;
499 // we don't support hierarchical menus in the help menu yet
503 if ( item
->IsSeparator() )
506 MacAppendMenu(mh
, "\p-" );
510 wxAcceleratorEntry
* entry
= wxGetAccelFromString( item
->GetText() ) ;
512 if ( item
->GetId() == wxApp::s_macAboutMenuItemId
)
514 UMASetMenuItemText( GetMenuHandle( kwxMacAppleMenuId
) , 1 , item
->GetText() );
515 UMAEnableMenuItem( GetMenuHandle( kwxMacAppleMenuId
) , 1 , true );
516 SetMenuItemCommandID( GetMenuHandle( kwxMacAppleMenuId
) , 1 , item
->GetId() ) ;
517 UMASetMenuItemShortcut( GetMenuHandle( kwxMacAppleMenuId
) , 1 , entry
) ;
523 UMAAppendMenuItem(mh
, item
->GetText() , entry
);
524 SetMenuItemCommandID( mh
, CountMenuItems(mh
) , item
->GetId() ) ;
535 UMASetMenuTitle( MAC_WXHMENU(menu
->GetHMenu()) , m_titles
[i
] ) ;
536 wxArrayPtrVoid submenus
;
537 wxMenuItem
* previousItem
= NULL
;
538 for (pos
= 0, node
= menu
->GetMenuItems().GetFirst(); node
; node
= node
->GetNext(), pos
++)
540 item
= (wxMenuItem
*)node
->GetData();
541 subMenu
= item
->GetSubMenu() ;
544 submenus
.Add(subMenu
) ;
549 if ( UMAGetSystemVersion() >= 0x1000 )
551 if ( item
->GetId() == wxApp::s_macPreferencesMenuItemId
|| item
->GetId() == wxApp::s_macExitMenuItemId
)
553 ChangeMenuItemAttributes( MAC_WXHMENU( menu
->GetHMenu() ) , pos
+ 1, kMenuItemAttrHidden
, 0 );
554 if ( menu
->GetMenuItems().GetCount() == pos
+ 1 && previousItem
!= NULL
&& previousItem
->IsSeparator() )
556 ChangeMenuItemAttributes( MAC_WXHMENU( menu
->GetHMenu() ) , pos
, kMenuItemAttrHidden
, 0 );
562 previousItem
= item
;
564 ::InsertMenu(MAC_WXHMENU(m_menus
[i
]->GetHMenu()), 0);
565 for ( size_t i
= 0 ; i
< submenus
.GetCount() ; ++i
)
567 wxMenu
* submenu
= (wxMenu
*) submenus
[i
] ;
568 wxMenuItemList::Node
*subnode
;
571 for ( subpos
= 0 , subnode
= submenu
->GetMenuItems().GetFirst(); subnode
; subnode
= subnode
->GetNext(), subpos
++)
573 subitem
= (wxMenuItem
*)subnode
->GetData();
574 wxMenu
* itsSubMenu
= subitem
->GetSubMenu() ;
577 submenus
.Add(itsSubMenu
) ;
580 ::InsertMenu( MAC_WXHMENU(submenu
->GetHMenu()) , -1 ) ;
585 s_macInstalledMenuBar
= this;
588 void wxMenuBar::EnableTop(size_t pos
, bool enable
)
590 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
591 m_menus
[pos
]->MacEnableMenu( enable
) ;
595 void wxMenuBar::SetLabelTop(size_t pos
, const wxString
& label
)
597 wxCHECK_RET( pos
< GetMenuCount(), wxT("invalid menu index") );
599 m_titles
[pos
] = label
;
606 m_menus
[pos
]->SetTitle( label
) ;
607 if (wxMenuBar::s_macInstalledMenuBar
== this) // are we currently installed ?
609 ::SetMenuBar( GetMenuBar() ) ;
614 wxString
wxMenuBar::GetLabelTop(size_t pos
) const
616 wxCHECK_MSG( pos
< GetMenuCount(), wxEmptyString
,
617 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
619 return m_titles
[pos
];
622 int wxMenuBar::FindMenu(const wxString
& title
)
624 wxString menuTitle
= wxStripMenuCodes(title
);
626 size_t count
= GetMenuCount();
627 for ( size_t i
= 0; i
< count
; i
++ )
629 wxString title
= wxStripMenuCodes(m_titles
[i
]);
630 if ( menuTitle
== title
)
639 // ---------------------------------------------------------------------------
640 // wxMenuBar construction
641 // ---------------------------------------------------------------------------
643 // ---------------------------------------------------------------------------
644 // wxMenuBar construction
645 // ---------------------------------------------------------------------------
647 wxMenu
*wxMenuBar::Replace(size_t pos
, wxMenu
*menu
, const wxString
& title
)
649 wxMenu
*menuOld
= wxMenuBarBase::Replace(pos
, menu
, title
);
652 m_titles
[pos
] = title
;
656 if (s_macInstalledMenuBar
== this)
658 ::DeleteMenu( menuOld
->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
660 UMASetMenuTitle( MAC_WXHMENU(menu
->GetHMenu()) , title
) ;
661 if ( pos
== m_menus
.GetCount() - 1)
663 ::InsertMenu( MAC_WXHMENU(menu
->GetHMenu()) , 0 ) ;
667 ::InsertMenu( MAC_WXHMENU(menu
->GetHMenu()) , m_menus
[pos
+1]->MacGetMenuId() ) ;
678 bool wxMenuBar::Insert(size_t pos
, wxMenu
*menu
, const wxString
& title
)
680 if ( !wxMenuBarBase::Insert(pos
, menu
, title
) )
683 m_titles
.Insert(title
, pos
);
685 UMASetMenuTitle( MAC_WXHMENU(menu
->GetHMenu()) , title
) ;
689 if ( pos
== (size_t) -1 || pos
+ 1 == m_menus
.GetCount() )
691 ::InsertMenu( MAC_WXHMENU(menu
->GetHMenu()) , 0 ) ;
695 ::InsertMenu( MAC_WXHMENU(menu
->GetHMenu()) , m_menus
[pos
+1]->MacGetMenuId() ) ;
703 wxMenu
*wxMenuBar::Remove(size_t pos
)
705 wxMenu
*menu
= wxMenuBarBase::Remove(pos
);
711 if (s_macInstalledMenuBar
== this)
713 ::DeleteMenu( menu
->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
721 m_titles
.Remove(pos
);
726 bool wxMenuBar::Append(wxMenu
*menu
, const wxString
& title
)
728 WXHMENU submenu
= menu
? menu
->GetHMenu() : 0;
729 wxCHECK_MSG( submenu
, FALSE
, wxT("can't append invalid menu to menubar") );
731 if ( !wxMenuBarBase::Append(menu
, title
) )
736 UMASetMenuTitle( MAC_WXHMENU(menu
->GetHMenu()) , title
) ;
740 if (s_macInstalledMenuBar
== this)
742 ::InsertMenu( MAC_WXHMENU(menu
->GetHMenu()) , 0 ) ;
748 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
749 // adding menu later on.
750 if (m_invokingWindow
)
751 wxMenubarSetInvokingWindow( menu
, m_invokingWindow
);
756 static void wxMenubarUnsetInvokingWindow( wxMenu
*menu
)
758 menu
->SetInvokingWindow( (wxWindow
*) NULL
);
760 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
763 wxMenuItem
*menuitem
= node
->GetData();
764 if (menuitem
->IsSubMenu())
765 wxMenubarUnsetInvokingWindow( menuitem
->GetSubMenu() );
766 node
= node
->GetNext();
770 static void wxMenubarSetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
772 menu
->SetInvokingWindow( win
);
774 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
777 wxMenuItem
*menuitem
= node
->GetData();
778 if (menuitem
->IsSubMenu())
779 wxMenubarSetInvokingWindow( menuitem
->GetSubMenu() , win
);
780 node
= node
->GetNext();
784 void wxMenuBar::UnsetInvokingWindow()
786 m_invokingWindow
= (wxWindow
*) NULL
;
787 wxMenuList::Node
*node
= m_menus
.GetFirst();
790 wxMenu
*menu
= node
->GetData();
791 wxMenubarUnsetInvokingWindow( menu
);
792 node
= node
->GetNext();
796 void wxMenuBar::SetInvokingWindow(wxFrame
*frame
)
798 m_invokingWindow
= frame
;
799 wxMenuList::Node
*node
= m_menus
.GetFirst();
802 wxMenu
*menu
= node
->GetData();
803 wxMenubarSetInvokingWindow( menu
, frame
);
804 node
= node
->GetNext();
808 void wxMenuBar::Detach()
810 wxMenuBarBase::Detach() ;
813 void wxMenuBar::Attach(wxFrame
*frame
)
815 wxMenuBarBase::Attach( frame
) ;
817 // ---------------------------------------------------------------------------
818 // wxMenuBar searching for menu items
819 // ---------------------------------------------------------------------------
821 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
822 int wxMenuBar::FindMenuItem(const wxString
& menuString
,
823 const wxString
& itemString
) const
825 wxString menuLabel
= wxStripMenuCodes(menuString
);
826 size_t count
= GetMenuCount();
827 for ( size_t i
= 0; i
< count
; i
++ )
829 wxString title
= wxStripMenuCodes(m_titles
[i
]);
830 if ( menuString
== title
)
831 return m_menus
[i
]->FindItem(itemString
);
837 wxMenuItem
*wxMenuBar::FindItem(int id
, wxMenu
**itemMenu
) const
842 wxMenuItem
*item
= NULL
;
843 size_t count
= GetMenuCount();
844 for ( size_t i
= 0; !item
&& (i
< count
); i
++ )
846 item
= m_menus
[i
]->FindItem(id
, itemMenu
);