1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxMenu, wxMenuBar, wxMenuItem
4 // Author: Stefan Csomor
8 // Copyright: (c) Stefan Csomor
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
13 #pragma implementation "menu.h"
14 #pragma implementation "menuitem.h"
17 // ============================================================================
18 // headers & declarations
19 // ============================================================================
26 #include "wx/menuitem.h"
27 #include "wx/window.h"
32 #include "wx/mac/uma.h"
34 // other standard headers
35 // ----------------------
38 #if !USE_SHARED_LIBRARY
39 IMPLEMENT_DYNAMIC_CLASS(wxMenu
, wxEvtHandler
)
40 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
, wxEvtHandler
)
43 // the (popup) menu title has this special id
44 static const int idMenuTitle
= -2;
45 static MenuItemIndex firstUserHelpMenuItem
= 0 ;
47 const short kwxMacMenuBarResource
= 1 ;
48 const short kwxMacAppleMenuId
= 1 ;
50 // ============================================================================
52 // ============================================================================
53 static void wxMenubarUnsetInvokingWindow( wxMenu
*menu
) ;
54 static void wxMenubarSetInvokingWindow( wxMenu
*menu
, wxWindow
*win
);
58 // Construct a menu with optional title (then use append)
61 short wxMenu::s_macNextMenuId
= 3 ;
63 short wxMenu::s_macNextMenuId
= 2 ;
69 m_startRadioGroup
= -1;
72 m_macMenuId
= s_macNextMenuId
++;
73 m_hMenu
= UMANewMenu(m_macMenuId
, m_title
);
77 wxLogLastError("UMANewMenu failed");
80 // if we have a title, insert it in the beginning of the menu
83 Append(idMenuTitle
, m_title
) ;
90 if (MAC_WXHMENU(m_hMenu
))
91 ::DisposeMenu(MAC_WXHMENU(m_hMenu
));
96 // not available on the mac platform
99 void wxMenu::Attach(wxMenuBarBase
*menubar
)
101 wxMenuBase::Attach(menubar
);
106 // function appends a new item or submenu to the menu
107 // append a new item or submenu to the menu
108 bool wxMenu::DoInsertOrAppend(wxMenuItem
*pItem
, size_t pos
)
110 wxASSERT_MSG( pItem
!= NULL
, "can't append NULL item to the menu" );
112 if ( pItem
->IsSeparator() )
114 if ( pos
== (size_t)-1 )
115 MacAppendMenu(MAC_WXHMENU(m_hMenu
), "\p-");
117 MacInsertMenuItem(MAC_WXHMENU(m_hMenu
), "\p-" , pos
);
121 wxMenu
*pSubMenu
= pItem
->GetSubMenu() ;
122 if ( pSubMenu
!= NULL
)
124 wxASSERT_MSG( pSubMenu
->m_hMenu
!= NULL
, "invalid submenu added");
125 pSubMenu
->m_menuParent
= this ;
127 if (wxMenuBar::MacGetInstalledMenuBar() == m_menuBar
)
128 ::InsertMenu( MAC_WXHMENU( pSubMenu
->m_hMenu
) , -1 ) ;
130 if ( pos
== (size_t)-1 )
131 UMAAppendSubMenuItem(MAC_WXHMENU(m_hMenu
), pItem
->GetText(), pSubMenu
->m_macMenuId
);
133 UMAInsertSubMenuItem(MAC_WXHMENU(m_hMenu
), pItem
->GetText() , pos
, pSubMenu
->m_macMenuId
);
134 pItem
->UpdateItemBitmap() ;
135 pItem
->UpdateItemStatus() ;
139 if ( pos
== (size_t)-1 )
141 UMAAppendMenuItem(MAC_WXHMENU(m_hMenu
), "a" );
142 pos
= CountMenuItems(MAC_WXHMENU(m_hMenu
)) ;
146 UMAInsertMenuItem(MAC_WXHMENU(m_hMenu
), "a" , pos
);
149 SetMenuItemCommandID( MAC_WXHMENU(m_hMenu
) , pos
, pItem
->GetId() ) ;
150 pItem
->UpdateItemText() ;
151 pItem
->UpdateItemBitmap() ;
152 pItem
->UpdateItemStatus() ;
154 if ( pItem
->GetId() == idMenuTitle
)
156 UMAEnableMenuItem(MAC_WXHMENU(m_hMenu
) , pos
, false ) ;
160 // if we're already attached to the menubar, we must update it
163 m_menuBar
->Refresh();
168 void wxMenu::EndRadioGroup()
170 // we're not inside a radio group any longer
171 m_startRadioGroup
= -1;
174 bool wxMenu::DoAppend(wxMenuItem
*item
)
176 wxCHECK_MSG( item
, FALSE
, _T("NULL item in wxMenu::DoAppend") );
180 if ( item
->GetKind() == wxITEM_RADIO
)
182 int count
= GetMenuItemCount();
184 if ( m_startRadioGroup
== -1 )
186 // start a new radio group
187 m_startRadioGroup
= count
;
189 // for now it has just one element
190 item
->SetAsRadioGroupStart();
191 item
->SetRadioGroupEnd(m_startRadioGroup
);
193 // ensure that we have a checked item in the radio group
196 else // extend the current radio group
198 // we need to update its end item
199 item
->SetRadioGroupStart(m_startRadioGroup
);
200 wxMenuItemList::Node
*node
= GetMenuItems().Item(m_startRadioGroup
);
204 node
->GetData()->SetRadioGroupEnd(count
);
208 wxFAIL_MSG( _T("where is the radio group start item?") );
212 else // not a radio item
217 if ( !wxMenuBase::DoAppend(item
) || !DoInsertOrAppend(item
) )
224 // check the item initially
231 bool wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
233 return wxMenuBase::DoInsert(pos
, item
) && DoInsertOrAppend(item
, pos
);
236 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
238 // we need to find the items position in the child list
240 wxMenuItemList::Node
*node
= GetMenuItems().GetFirst();
241 for ( pos
= 0; node
; pos
++ )
243 if ( node
->GetData() == item
)
246 node
= node
->GetNext();
249 // DoRemove() (unlike Remove) can only be called for existing item!
250 wxCHECK_MSG( node
, NULL
, wxT("bug in wxMenu::Remove logic") );
252 ::DeleteMenuItem(MAC_WXHMENU(m_hMenu
) , pos
+ 1);
256 // otherwise, the change won't be visible
257 m_menuBar
->Refresh();
260 // and from internal data structures
261 return wxMenuBase::DoRemove(item
);
264 void wxMenu::SetTitle(const wxString
& label
)
267 UMASetMenuTitle(MAC_WXHMENU(m_hMenu
) , label
) ;
269 bool wxMenu::ProcessCommand(wxCommandEvent
& event
)
271 bool processed
= FALSE
;
273 #if WXWIN_COMPATIBILITY
277 (void)(*(m_callback
))(*this, event
);
280 #endif WXWIN_COMPATIBILITY
282 // Try the menu's event handler
283 if ( !processed
&& GetEventHandler())
285 processed
= GetEventHandler()->ProcessEvent(event
);
288 // Try the window the menu was popped up from (and up through the
290 wxWindow
*win
= GetInvokingWindow();
291 if ( !processed
&& win
)
292 processed
= win
->GetEventHandler()->ProcessEvent(event
);
298 // ---------------------------------------------------------------------------
300 // ---------------------------------------------------------------------------
302 wxWindow
*wxMenu::GetWindow() const
304 if ( m_invokingWindow
!= NULL
)
305 return m_invokingWindow
;
306 else if ( m_menuBar
!= NULL
)
307 return (wxWindow
*) m_menuBar
->GetFrame();
312 // helper functions returning the mac menu position for a certain item, note that this is
313 // mac-wise 1 - based, i.e. the first item has index 1 whereas on MSWin it has pos 0
315 int wxMenu::MacGetIndexFromId( int id
)
318 wxMenuItemList::Node
*node
= GetMenuItems().GetFirst();
319 for ( pos
= 0; node
; pos
++ )
321 if ( node
->GetData()->GetId() == id
)
324 node
= node
->GetNext();
333 int wxMenu::MacGetIndexFromItem( wxMenuItem
*pItem
)
336 wxMenuItemList::Node
*node
= GetMenuItems().GetFirst();
337 for ( pos
= 0; node
; pos
++ )
339 if ( node
->GetData() == pItem
)
342 node
= node
->GetNext();
351 void wxMenu::MacEnableMenu( bool bDoEnable
)
353 UMAEnableMenuItem(MAC_WXHMENU(m_hMenu
) , 0 , bDoEnable
) ;
362 Mac Implementation note :
364 The Mac has only one global menubar, so we attempt to install the currently
365 active menubar from a frame, we currently don't take into account mdi-frames
366 which would ask for menu-merging
368 Secondly there is no mac api for changing a menubar that is not the current
369 menubar, so we have to wait for preparing the actual menubar until the
370 wxMenubar is to be used
372 We can in subsequent versions use MacInstallMenuBar to provide some sort of
373 auto-merge for MDI in case this will be necessary
377 wxMenuBar
* wxMenuBar::s_macInstalledMenuBar
= NULL
;
379 void wxMenuBar::Init()
381 m_eventHandler
= this;
382 m_menuBarFrame
= NULL
;
383 m_invokingWindow
= (wxWindow
*) NULL
;
386 wxMenuBar::wxMenuBar()
391 wxMenuBar::wxMenuBar( long WXUNUSED(style
) )
397 wxMenuBar::wxMenuBar(int count
, wxMenu
*menus
[], const wxString titles
[])
401 m_titles
.Alloc(count
);
403 for ( int i
= 0; i
< count
; i
++ )
405 m_menus
.Append(menus
[i
]);
406 m_titles
.Add(titles
[i
]);
408 menus
[i
]->Attach(this);
412 wxMenuBar::~wxMenuBar()
414 if (s_macInstalledMenuBar
== this)
417 s_macInstalledMenuBar
= NULL
;
422 void wxMenuBar::Refresh(bool WXUNUSED(eraseBackground
), const wxRect
*WXUNUSED(rect
))
424 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
429 void wxMenuBar::MacInstallMenuBar()
431 if ( s_macInstalledMenuBar
== this )
434 wxStAppResource resload
;
436 Handle menubar
= ::GetNewMBar( kwxMacMenuBarResource
) ;
438 wxCHECK_RET( menubar
!= NULL
, "can't read MBAR resource" );
439 ::SetMenuBar( menubar
) ;
440 #if TARGET_API_MAC_CARBON
441 ::DisposeMenuBar( menubar
) ;
443 ::DisposeHandle( menubar
) ;
446 #if TARGET_API_MAC_OS8
447 MenuHandle menu
= ::GetMenuHandle( kwxMacAppleMenuId
) ;
448 if ( CountMenuItems( menu
) == 2 )
450 ::AppendResMenu(menu
, 'DRVR');
454 // clean-up the help menu before adding new items
455 MenuHandle mh
= NULL
;
456 if ( UMAGetHelpMenu( &mh
, &firstUserHelpMenuItem
) == noErr
)
458 for ( int i
= CountMenuItems( mh
) ; i
>= firstUserHelpMenuItem
; --i
)
460 DeleteMenuItem( mh
, i
) ;
468 if ( UMAGetSystemVersion() >= 0x1000 && wxApp::s_macPreferencesMenuItemId
)
470 wxMenuItem
*item
= FindItem( wxApp::s_macPreferencesMenuItemId
, NULL
) ;
471 if ( item
== NULL
|| !(item
->IsEnabled()) )
472 DisableMenuCommand( NULL
, kHICommandPreferences
) ;
474 EnableMenuCommand( NULL
, kHICommandPreferences
) ;
477 for (size_t i
= 0; i
< m_menus
.GetCount(); i
++)
480 wxMenuItemList::Node
*node
;
483 wxMenu
* menu
= m_menus
[i
] , *subMenu
= NULL
;
485 if( m_titles
[i
] == "?" || m_titles
[i
] == "&?" || m_titles
[i
] == wxApp::s_macHelpMenuTitleName
)
492 for (pos
= 0 , node
= menu
->GetMenuItems().GetFirst(); node
; node
= node
->GetNext(), pos
++)
494 item
= (wxMenuItem
*)node
->GetData();
495 subMenu
= item
->GetSubMenu() ;
498 // we don't support hierarchical menus in the help menu yet
502 if ( item
->IsSeparator() )
505 MacAppendMenu(mh
, "\p-" );
509 wxAcceleratorEntry
* entry
= wxGetAccelFromString( item
->GetText() ) ;
511 if ( item
->GetId() == wxApp::s_macAboutMenuItemId
)
513 UMASetMenuItemText( GetMenuHandle( kwxMacAppleMenuId
) , 1 , item
->GetText() );
514 UMAEnableMenuItem( GetMenuHandle( kwxMacAppleMenuId
) , 1 , true );
515 SetMenuItemCommandID( GetMenuHandle( kwxMacAppleMenuId
) , 1 , item
->GetId() ) ;
516 UMASetMenuItemShortcut( GetMenuHandle( kwxMacAppleMenuId
) , 1 , entry
) ;
522 UMAAppendMenuItem(mh
, item
->GetText() , entry
);
523 SetMenuItemCommandID( mh
, CountMenuItems(mh
) , item
->GetId() ) ;
534 UMASetMenuTitle( MAC_WXHMENU(menu
->GetHMenu()) , m_titles
[i
] ) ;
535 wxArrayPtrVoid submenus
;
536 wxMenuItem
* previousItem
= NULL
;
537 for (pos
= 0, node
= menu
->GetMenuItems().GetFirst(); node
; node
= node
->GetNext(), pos
++)
539 item
= (wxMenuItem
*)node
->GetData();
540 subMenu
= item
->GetSubMenu() ;
543 submenus
.Add(subMenu
) ;
548 if ( UMAGetSystemVersion() >= 0x1000 )
550 if ( item
->GetId() == wxApp::s_macPreferencesMenuItemId
|| item
->GetId() == wxApp::s_macExitMenuItemId
)
552 ChangeMenuItemAttributes( MAC_WXHMENU( menu
->GetHMenu() ) , pos
+ 1, kMenuItemAttrHidden
, 0 );
553 if ( menu
->GetMenuItems().GetCount() == pos
+ 1 && previousItem
!= NULL
&& previousItem
->IsSeparator() )
555 ChangeMenuItemAttributes( MAC_WXHMENU( menu
->GetHMenu() ) , pos
, kMenuItemAttrHidden
, 0 );
561 previousItem
= item
;
563 ::InsertMenu(MAC_WXHMENU(m_menus
[i
]->GetHMenu()), 0);
564 for ( size_t i
= 0 ; i
< submenus
.GetCount() ; ++i
)
566 wxMenu
* submenu
= (wxMenu
*) submenus
[i
] ;
567 wxMenuItemList::Node
*subnode
;
570 for ( subpos
= 0 , subnode
= submenu
->GetMenuItems().GetFirst(); subnode
; subnode
= subnode
->GetNext(), subpos
++)
572 subitem
= (wxMenuItem
*)subnode
->GetData();
573 wxMenu
* itsSubMenu
= subitem
->GetSubMenu() ;
576 submenus
.Add(itsSubMenu
) ;
579 ::InsertMenu( MAC_WXHMENU(submenu
->GetHMenu()) , -1 ) ;
584 s_macInstalledMenuBar
= this;
587 void wxMenuBar::EnableTop(size_t pos
, bool enable
)
589 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
590 m_menus
[pos
]->MacEnableMenu( enable
) ;
594 void wxMenuBar::SetLabelTop(size_t pos
, const wxString
& label
)
596 wxCHECK_RET( pos
< GetMenuCount(), wxT("invalid menu index") );
598 m_titles
[pos
] = label
;
605 m_menus
[pos
]->SetTitle( label
) ;
606 if (wxMenuBar::s_macInstalledMenuBar
== this) // are we currently installed ?
608 ::SetMenuBar( GetMenuBar() ) ;
613 wxString
wxMenuBar::GetLabelTop(size_t pos
) const
615 wxCHECK_MSG( pos
< GetMenuCount(), wxEmptyString
,
616 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
618 return m_titles
[pos
];
621 int wxMenuBar::FindMenu(const wxString
& title
)
623 wxString menuTitle
= wxStripMenuCodes(title
);
625 size_t count
= GetMenuCount();
626 for ( size_t i
= 0; i
< count
; i
++ )
628 wxString title
= wxStripMenuCodes(m_titles
[i
]);
629 if ( menuTitle
== title
)
638 // ---------------------------------------------------------------------------
639 // wxMenuBar construction
640 // ---------------------------------------------------------------------------
642 // ---------------------------------------------------------------------------
643 // wxMenuBar construction
644 // ---------------------------------------------------------------------------
646 wxMenu
*wxMenuBar::Replace(size_t pos
, wxMenu
*menu
, const wxString
& title
)
648 wxMenu
*menuOld
= wxMenuBarBase::Replace(pos
, menu
, title
);
651 m_titles
[pos
] = title
;
655 if (s_macInstalledMenuBar
== this)
657 ::DeleteMenu( menuOld
->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
659 UMASetMenuTitle( MAC_WXHMENU(menu
->GetHMenu()) , title
) ;
660 if ( pos
== m_menus
.GetCount() - 1)
662 ::InsertMenu( MAC_WXHMENU(menu
->GetHMenu()) , 0 ) ;
666 ::InsertMenu( MAC_WXHMENU(menu
->GetHMenu()) , m_menus
[pos
+1]->MacGetMenuId() ) ;
677 bool wxMenuBar::Insert(size_t pos
, wxMenu
*menu
, const wxString
& title
)
679 if ( !wxMenuBarBase::Insert(pos
, menu
, title
) )
682 m_titles
.Insert(title
, pos
);
684 UMASetMenuTitle( MAC_WXHMENU(menu
->GetHMenu()) , title
) ;
688 if ( pos
== (size_t) -1 || pos
+ 1 == m_menus
.GetCount() )
690 ::InsertMenu( MAC_WXHMENU(menu
->GetHMenu()) , 0 ) ;
694 ::InsertMenu( MAC_WXHMENU(menu
->GetHMenu()) , m_menus
[pos
+1]->MacGetMenuId() ) ;
702 wxMenu
*wxMenuBar::Remove(size_t pos
)
704 wxMenu
*menu
= wxMenuBarBase::Remove(pos
);
710 if (s_macInstalledMenuBar
== this)
712 ::DeleteMenu( menu
->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
720 m_titles
.Remove(pos
);
725 bool wxMenuBar::Append(wxMenu
*menu
, const wxString
& title
)
727 WXHMENU submenu
= menu
? menu
->GetHMenu() : 0;
728 wxCHECK_MSG( submenu
, FALSE
, wxT("can't append invalid menu to menubar") );
730 if ( !wxMenuBarBase::Append(menu
, title
) )
735 UMASetMenuTitle( MAC_WXHMENU(menu
->GetHMenu()) , title
) ;
739 if (s_macInstalledMenuBar
== this)
741 ::InsertMenu( MAC_WXHMENU(menu
->GetHMenu()) , 0 ) ;
747 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
748 // adding menu later on.
749 if (m_invokingWindow
)
750 wxMenubarSetInvokingWindow( menu
, m_invokingWindow
);
755 static void wxMenubarUnsetInvokingWindow( wxMenu
*menu
)
757 menu
->SetInvokingWindow( (wxWindow
*) NULL
);
759 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
762 wxMenuItem
*menuitem
= node
->GetData();
763 if (menuitem
->IsSubMenu())
764 wxMenubarUnsetInvokingWindow( menuitem
->GetSubMenu() );
765 node
= node
->GetNext();
769 static void wxMenubarSetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
771 menu
->SetInvokingWindow( win
);
773 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
776 wxMenuItem
*menuitem
= node
->GetData();
777 if (menuitem
->IsSubMenu())
778 wxMenubarSetInvokingWindow( menuitem
->GetSubMenu() , win
);
779 node
= node
->GetNext();
783 void wxMenuBar::UnsetInvokingWindow()
785 m_invokingWindow
= (wxWindow
*) NULL
;
786 wxMenuList::Node
*node
= m_menus
.GetFirst();
789 wxMenu
*menu
= node
->GetData();
790 wxMenubarUnsetInvokingWindow( menu
);
791 node
= node
->GetNext();
795 void wxMenuBar::SetInvokingWindow(wxFrame
*frame
)
797 m_invokingWindow
= frame
;
798 wxMenuList::Node
*node
= m_menus
.GetFirst();
801 wxMenu
*menu
= node
->GetData();
802 wxMenubarSetInvokingWindow( menu
, frame
);
803 node
= node
->GetNext();
807 void wxMenuBar::Detach()
809 wxMenuBarBase::Detach() ;
812 void wxMenuBar::Attach(wxFrame
*frame
)
814 wxMenuBarBase::Attach( frame
) ;
816 // ---------------------------------------------------------------------------
817 // wxMenuBar searching for menu items
818 // ---------------------------------------------------------------------------
820 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
821 int wxMenuBar::FindMenuItem(const wxString
& menuString
,
822 const wxString
& itemString
) const
824 wxString menuLabel
= wxStripMenuCodes(menuString
);
825 size_t count
= GetMenuCount();
826 for ( size_t i
= 0; i
< count
; i
++ )
828 wxString title
= wxStripMenuCodes(m_titles
[i
]);
829 if ( menuString
== title
)
830 return m_menus
[i
]->FindItem(itemString
);
836 wxMenuItem
*wxMenuBar::FindItem(int id
, wxMenu
**itemMenu
) const
841 wxMenuItem
*item
= NULL
;
842 size_t count
= GetMenuCount();
843 for ( size_t i
= 0; !item
&& (i
< count
); i
++ )
845 item
= m_menus
[i
]->FindItem(id
, itemMenu
);