1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxMenu, wxMenuBar, wxMenuItem
8 // Copyright: (c) AUTHOR
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
13 // ============================================================================
14 // headers & declarations
15 // ============================================================================
21 #pragma implementation "menu.h"
22 #pragma implementation "menuitem.h"
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 // ============================================================================
58 // Construct a menu with optional title (then use append)
61 short wxMenu::s_macNextMenuId
= 3 ;
63 short wxMenu::s_macNextMenuId
= 2 ;
72 wxMenuItem::MacBuildMenuString( label
, NULL
, NULL
, m_title
, false );
73 m_macMenuId
= s_macNextMenuId
++;
74 m_hMenu
= ::NewMenu(m_macMenuId
, label
);
78 wxLogLastError("CreatePopupMenu");
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
));
96 WX_CLEAR_ARRAY(m_accels
);
102 // not available on the mac platform
107 int wxMenu::FindAccel(int id
) const
109 size_t n
, count
= m_accels
.GetCount();
110 for ( n
= 0; n
< count
; n
++ )
112 if ( m_accels
[n
]->m_command
== id
)
119 void wxMenu::UpdateAccel(wxMenuItem
*item
)
121 // find the (new) accel for this item
122 wxAcceleratorEntry
*accel
= wxGetAccelFromString(item
->GetText());
124 accel
->m_command
= item
->GetId();
127 int n
= FindAccel(item
->GetId());
128 if ( n
== wxNOT_FOUND
)
130 // no old, add new if any
134 return; // skipping RebuildAccelTable() below
138 // replace old with new or just remove the old one if no new
143 m_accels
.RemoveAt(n
);
148 m_menuBar
->RebuildAccelTable();
152 #endif // wxUSE_ACCEL
154 // function appends a new item or submenu to the menu
155 // append a new item or submenu to the menu
156 bool wxMenu::DoInsertOrAppend(wxMenuItem
*pItem
, size_t pos
)
158 wxASSERT_MSG( pItem
!= NULL
, "can't append NULL item to the menu" );
161 #endif // wxUSE_ACCEL
163 if ( pItem
->IsSeparator() )
165 if ( pos
== (size_t)-1 )
167 MacAppendMenu(MAC_WXHMENU(m_hMenu
), "\p-");
171 MacInsertMenuItem(MAC_WXHMENU(m_hMenu
), "\p-" , pos
);
176 wxMenu
*pSubMenu
= pItem
->GetSubMenu() ;
177 if ( pSubMenu
!= NULL
)
180 wxASSERT_MSG( pSubMenu
->m_hMenu
!= NULL
, "invalid submenu added");
181 pSubMenu
->m_menuParent
= this ;
182 wxMenuItem::MacBuildMenuString( label
, NULL
, NULL
, pItem
->GetText() ,false);
184 if (wxMenuBar::MacGetInstalledMenuBar() == m_menuBar
)
186 ::InsertMenu( MAC_WXHMENU( pSubMenu
->m_hMenu
) , -1 ) ;
189 if ( pos
== (size_t)-1 )
191 UMAAppendSubMenuItem(MAC_WXHMENU(m_hMenu
), label
, pSubMenu
->m_macMenuId
);
195 UMAInsertSubMenuItem(MAC_WXHMENU(m_hMenu
), label
, pos
, pSubMenu
->m_macMenuId
);
203 wxMenuItem::MacBuildMenuString( label
, &key
, &modifiers
, pItem
->GetText(), pItem
->GetId() == wxApp::s_macAboutMenuItemId
);
206 // we cannot add empty menus on mac
210 if ( pos
== (size_t)-1 )
212 UMAAppendMenuItem(MAC_WXHMENU(m_hMenu
), label
,key
,modifiers
);
213 SetMenuItemCommandID( MAC_WXHMENU(m_hMenu
) , CountMenuItems(MAC_WXHMENU(m_hMenu
)) , pItem
->GetId() ) ;
214 if ( pItem
->GetBitmap().Ok() )
216 ControlButtonContentInfo info
;
217 wxMacCreateBitmapButton( &info
, pItem
->GetBitmap() , kControlContentCIconHandle
) ;
218 if ( info
.contentType
!= kControlNoContent
)
220 if ( info
.contentType
== kControlContentCIconHandle
)
221 SetMenuItemIconHandle( MAC_WXHMENU(m_hMenu
) , CountMenuItems(MAC_WXHMENU(m_hMenu
) ) ,
222 kMenuColorIconType
, (Handle
) info
.u
.cIconHandle
) ;
229 UMAInsertMenuItem(MAC_WXHMENU(m_hMenu
), label
, pos
,key
,modifiers
);
230 SetMenuItemCommandID( MAC_WXHMENU(m_hMenu
) , pos
, pItem
->GetId() ) ;
231 if ( pItem
->GetBitmap().Ok() )
233 ControlButtonContentInfo info
;
234 wxMacCreateBitmapButton( &info
, pItem
->GetBitmap() , kControlContentCIconHandle
) ;
235 if ( info
.contentType
!= kControlNoContent
)
237 if ( info
.contentType
== kControlContentCIconHandle
)
238 SetMenuItemIconHandle( MAC_WXHMENU(m_hMenu
) , pos
,
239 kMenuColorIconType
, (Handle
) info
.u
.cIconHandle
) ;
244 if ( pItem
->GetId() == idMenuTitle
)
246 if ( pos
== (size_t)-1 )
248 UMADisableMenuItem(MAC_WXHMENU(m_hMenu
) , CountMenuItems(MAC_WXHMENU(m_hMenu
) ) ) ;
252 UMADisableMenuItem(MAC_WXHMENU(m_hMenu
) , pos
+ 1 ) ;
257 // if we're already attached to the menubar, we must update it
260 m_menuBar
->Refresh();
265 bool wxMenu::DoAppend(wxMenuItem
*item
)
267 return wxMenuBase::DoAppend(item
) && DoInsertOrAppend(item
);
270 bool wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
272 return wxMenuBase::DoInsert(pos
, item
) && DoInsertOrAppend(item
, pos
);
275 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
277 // we need to find the items position in the child list
279 wxMenuItemList::Node
*node
= GetMenuItems().GetFirst();
280 for ( pos
= 0; node
; pos
++ )
282 if ( node
->GetData() == item
)
285 node
= node
->GetNext();
288 // DoRemove() (unlike Remove) can only be called for existing item!
289 wxCHECK_MSG( node
, NULL
, wxT("bug in wxMenu::Remove logic") );
292 // remove the corresponding accel from the accel table
293 int n
= FindAccel(item
->GetId());
294 if ( n
!= wxNOT_FOUND
)
298 m_accels
.RemoveAt(n
);
300 //else: this item doesn't have an accel, nothing to do
301 #endif // wxUSE_ACCEL
303 ::DeleteMenuItem(MAC_WXHMENU(m_hMenu
) , pos
+ 1);
307 // otherwise, the chane won't be visible
308 m_menuBar
->Refresh();
311 // and from internal data structures
312 return wxMenuBase::DoRemove(item
);
315 // ---------------------------------------------------------------------------
316 // accelerator helpers
317 // ---------------------------------------------------------------------------
321 // create the wxAcceleratorEntries for our accels and put them into provided
322 // array - return the number of accels we have
323 size_t wxMenu::CopyAccels(wxAcceleratorEntry
*accels
) const
325 size_t count
= GetAccelCount();
326 for ( size_t n
= 0; n
< count
; n
++ )
328 *accels
++ = *m_accels
[n
];
334 #endif // wxUSE_ACCEL
336 void wxMenu::SetTitle(const wxString
& label
)
340 wxMenuItem::MacBuildMenuString( title
, NULL
, NULL
, label
, false );
341 UMASetMenuTitle(MAC_WXHMENU(m_hMenu
) , title
) ;
343 bool wxMenu::ProcessCommand(wxCommandEvent
& event
)
345 bool processed
= FALSE
;
347 #if WXWIN_COMPATIBILITY
351 (void)(*(m_callback
))(*this, event
);
354 #endif WXWIN_COMPATIBILITY
356 // Try the menu's event handler
357 if ( !processed
&& GetEventHandler())
359 processed
= GetEventHandler()->ProcessEvent(event
);
362 // Try the window the menu was popped up from (and up through the
364 wxWindow
*win
= GetInvokingWindow();
365 if ( !processed
&& win
)
366 processed
= win
->GetEventHandler()->ProcessEvent(event
);
372 // ---------------------------------------------------------------------------
374 // ---------------------------------------------------------------------------
376 wxWindow
*wxMenu::GetWindow() const
378 if ( m_invokingWindow
!= NULL
)
379 return m_invokingWindow
;
380 else if ( m_menuBar
!= NULL
)
381 return (wxWindow
*) m_menuBar
->GetFrame();
386 // helper functions returning the mac menu position for a certain item, note that this is
387 // mac-wise 1 - based, i.e. the first item has index 1 whereas on MSWin it has pos 0
389 int wxMenu::MacGetIndexFromId( int id
)
392 wxMenuItemList::Node
*node
= GetMenuItems().GetFirst();
393 for ( pos
= 0; node
; pos
++ )
395 if ( node
->GetData()->GetId() == id
)
398 node
= node
->GetNext();
407 int wxMenu::MacGetIndexFromItem( wxMenuItem
*pItem
)
410 wxMenuItemList::Node
*node
= GetMenuItems().GetFirst();
411 for ( pos
= 0; node
; pos
++ )
413 if ( node
->GetData() == pItem
)
416 node
= node
->GetNext();
425 void wxMenu::MacEnableMenu( bool bDoEnable
)
428 UMAEnableMenuItem(MAC_WXHMENU(m_hMenu
) , 0 ) ;
430 UMADisableMenuItem(MAC_WXHMENU(m_hMenu
) , 0 ) ;
439 Mac Implementation note :
441 The Mac has only one global menubar, so we attempt to install the currently
442 active menubar from a frame, we currently don't take into account mdi-frames
443 which would ask for menu-merging
445 Secondly there is no mac api for changing a menubar that is not the current
446 menubar, so we have to wait for preparing the actual menubar until the
447 wxMenubar is to be used
449 We can in subsequent versions use MacInstallMenuBar to provide some sort of
450 auto-merge for MDI in case this will be necessary
454 wxMenuBar
* wxMenuBar::s_macInstalledMenuBar
= NULL
;
456 void wxMenuBar::Init()
458 m_eventHandler
= this;
459 m_menuBarFrame
= NULL
;
462 wxMenuBar::wxMenuBar()
467 wxMenuBar::wxMenuBar( long WXUNUSED(style
) )
473 wxMenuBar::wxMenuBar(int count
, wxMenu
*menus
[], const wxString titles
[])
477 m_titles
.Alloc(count
);
479 for ( int i
= 0; i
< count
; i
++ )
481 m_menus
.Append(menus
[i
]);
482 m_titles
.Add(titles
[i
]);
484 menus
[i
]->Attach(this);
488 wxMenuBar::~wxMenuBar()
490 if (s_macInstalledMenuBar
== this)
493 s_macInstalledMenuBar
= NULL
;
498 void wxMenuBar::Refresh(bool WXUNUSED(eraseBackground
), const wxRect
*WXUNUSED(rect
))
500 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
507 void wxMenuBar::RebuildAccelTable()
509 // merge the accelerators of all menus into one accel table
510 size_t nAccelCount
= 0;
511 size_t i
, count
= GetMenuCount();
512 for ( i
= 0; i
< count
; i
++ )
514 nAccelCount
+= m_menus
[i
]->GetAccelCount();
519 wxAcceleratorEntry
*accelEntries
= new wxAcceleratorEntry
[nAccelCount
];
522 for ( i
= 0; i
< count
; i
++ )
524 nAccelCount
+= m_menus
[i
]->CopyAccels(&accelEntries
[nAccelCount
]);
527 m_accelTable
= wxAcceleratorTable(nAccelCount
, accelEntries
);
529 delete [] accelEntries
;
533 #endif // wxUSE_ACCEL
535 void wxMenuBar::MacInstallMenuBar()
537 if ( s_macInstalledMenuBar
== this )
540 wxStAppResource resload
;
542 Handle menubar
= ::GetNewMBar( kwxMacMenuBarResource
) ;
544 wxCHECK_RET( menubar
!= NULL
, "can't read MBAR resource" );
545 ::SetMenuBar( menubar
) ;
546 #if TARGET_API_MAC_CARBON
547 ::DisposeMenuBar( menubar
) ;
549 ::DisposeHandle( menubar
) ;
552 #if TARGET_API_MAC_OS8
553 MenuHandle menu
= ::GetMenuHandle( kwxMacAppleMenuId
) ;
554 if ( CountMenuItems( menu
) == 2 )
556 ::AppendResMenu(menu
, 'DRVR');
560 for (size_t i
= 0; i
< m_menus
.GetCount(); i
++)
566 wxMenu
* menu
= m_menus
[i
] , *subMenu
= NULL
;
568 if( m_titles
[i
] == "?" || m_titles
[i
] == "&?" || m_titles
[i
] == wxApp::s_macHelpMenuTitleName
)
570 MenuHandle mh
= NULL
;
571 if ( UMAGetHelpMenu( &mh
, &firstUserHelpMenuItem
) != noErr
)
576 for ( int i
= CountMenuItems( mh
) ; i
>= firstUserHelpMenuItem
; --i
)
578 DeleteMenuItem( mh
, i
) ;
581 for (pos
= 0 , node
= menu
->GetMenuItems().First(); node
; node
= node
->Next(), pos
++)
583 item
= (wxMenuItem
*)node
->Data();
584 subMenu
= item
->GetSubMenu() ;
587 // we don't support hierarchical menus in the help menu yet
591 if ( item
->IsSeparator() )
594 MacAppendMenu(mh
, "\p-" );
601 wxMenuItem::MacBuildMenuString( label
, &key
, &modifiers
, item
->GetText(), item
->GetId() != wxApp::s_macAboutMenuItemId
); // no shortcut in about menu
604 // we cannot add empty menus on mac
608 if ( item
->GetId() == wxApp::s_macAboutMenuItemId
)
610 ::SetMenuItemText( GetMenuHandle( kwxMacAppleMenuId
) , 1 , label
);
611 UMAEnableMenuItem( GetMenuHandle( kwxMacAppleMenuId
) , 1 );
612 SetMenuItemCommandID( GetMenuHandle( kwxMacAppleMenuId
) , 1 , item
->GetId() ) ;
618 UMAAppendMenuItem(mh
, label
, key
, modifiers
);
619 SetMenuItemCommandID( mh
, CountMenuItems(mh
) , item
->GetId() ) ;
628 wxMenuItem::MacBuildMenuString( label
, NULL
, NULL
, m_titles
[i
] , false );
629 UMASetMenuTitle( MAC_WXHMENU(menu
->GetHMenu()) , label
) ;
630 wxArrayPtrVoid submenus
;
632 for (pos
= 0, node
= menu
->GetMenuItems().First(); node
; node
= node
->Next(), pos
++)
634 item
= (wxMenuItem
*)node
->Data();
635 subMenu
= item
->GetSubMenu() ;
638 submenus
.Add(subMenu
) ;
641 ::InsertMenu(MAC_WXHMENU(m_menus
[i
]->GetHMenu()), 0);
642 for ( size_t i
= 0 ; i
< submenus
.GetCount() ; ++i
)
644 wxMenu
* submenu
= (wxMenu
*) submenus
[i
] ;
648 for ( subpos
= 0 , subnode
= submenu
->GetMenuItems().First(); subnode
; subnode
= subnode
->Next(), subpos
++)
650 subitem
= (wxMenuItem
*)subnode
->Data();
651 wxMenu
* itsSubMenu
= subitem
->GetSubMenu() ;
654 submenus
.Add(itsSubMenu
) ;
657 ::InsertMenu( MAC_WXHMENU(submenu
->GetHMenu()) , -1 ) ;
662 s_macInstalledMenuBar
= this;
665 void wxMenuBar::EnableTop(size_t pos
, bool enable
)
667 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") );
668 m_menus
[pos
]->MacEnableMenu( enable
) ;
672 void wxMenuBar::SetLabelTop(size_t pos
, const wxString
& label
)
674 wxCHECK_RET( pos
< GetMenuCount(), wxT("invalid menu index") );
676 m_titles
[pos
] = label
;
683 m_menus
[pos
]->SetTitle( label
) ;
684 if (wxMenuBar::s_macInstalledMenuBar
== this) // are we currently installed ?
686 ::SetMenuBar( GetMenuBar() ) ;
691 wxString
wxMenuBar::GetLabelTop(size_t pos
) const
693 wxCHECK_MSG( pos
< GetMenuCount(), wxEmptyString
,
694 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
696 return m_titles
[pos
];
699 int wxMenuBar::FindMenu(const wxString
& title
)
701 wxString menuTitle
= wxStripMenuCodes(title
);
703 size_t count
= GetMenuCount();
704 for ( size_t i
= 0; i
< count
; i
++ )
706 wxString title
= wxStripMenuCodes(m_titles
[i
]);
707 if ( menuTitle
== title
)
716 // ---------------------------------------------------------------------------
717 // wxMenuBar construction
718 // ---------------------------------------------------------------------------
720 // ---------------------------------------------------------------------------
721 // wxMenuBar construction
722 // ---------------------------------------------------------------------------
724 wxMenu
*wxMenuBar::Replace(size_t pos
, wxMenu
*menu
, const wxString
& title
)
726 wxMenu
*menuOld
= wxMenuBarBase::Replace(pos
, menu
, title
);
729 m_titles
[pos
] = title
;
733 if (s_macInstalledMenuBar
== this)
735 ::DeleteMenu( menuOld
->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
738 wxMenuItem::MacBuildMenuString( label
, NULL
, NULL
, title
, false );
739 UMASetMenuTitle( MAC_WXHMENU(menu
->GetHMenu()) , label
) ;
740 if ( pos
== m_menus
.GetCount() - 1)
742 ::InsertMenu( MAC_WXHMENU(menu
->GetHMenu()) , 0 ) ;
746 ::InsertMenu( MAC_WXHMENU(menu
->GetHMenu()) , m_menus
[pos
+1]->MacGetMenuId() ) ;
753 if ( menuOld
->HasAccels() || menu
->HasAccels() )
755 // need to rebuild accell table
758 #endif // wxUSE_ACCEL
766 bool wxMenuBar::Insert(size_t pos
, wxMenu
*menu
, const wxString
& title
)
768 if ( !wxMenuBarBase::Insert(pos
, menu
, title
) )
771 m_titles
.Insert(title
, pos
);
774 wxMenuItem::MacBuildMenuString( label
, NULL
, NULL
, title
, false );
775 UMASetMenuTitle( MAC_WXHMENU(menu
->GetHMenu()) , label
) ;
779 if ( pos
== (size_t) -1 || pos
+ 1 == m_menus
.GetCount() )
781 ::InsertMenu( MAC_WXHMENU(menu
->GetHMenu()) , 0 ) ;
785 ::InsertMenu( MAC_WXHMENU(menu
->GetHMenu()) , m_menus
[pos
+1]->MacGetMenuId() ) ;
789 if ( menu
->HasAccels() )
791 // need to rebuild accell table
794 #endif // wxUSE_ACCEL
802 wxMenu
*wxMenuBar::Remove(size_t pos
)
804 wxMenu
*menu
= wxMenuBarBase::Remove(pos
);
810 if (s_macInstalledMenuBar
== this)
812 ::DeleteMenu( menu
->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ;
818 if ( menu
->HasAccels() )
820 // need to rebuild accell table
823 #endif // wxUSE_ACCEL
828 m_titles
.Remove(pos
);
833 bool wxMenuBar::Append(wxMenu
*menu
, const wxString
& title
)
835 WXHMENU submenu
= menu
? menu
->GetHMenu() : 0;
836 wxCHECK_MSG( submenu
, FALSE
, wxT("can't append invalid menu to menubar") );
838 if ( !wxMenuBarBase::Append(menu
, title
) )
844 wxMenuItem::MacBuildMenuString( label
, NULL
, NULL
, title
, false );
845 UMASetMenuTitle( MAC_WXHMENU(menu
->GetHMenu()) , label
) ;
849 if (s_macInstalledMenuBar
== this)
851 ::InsertMenu( MAC_WXHMENU(menu
->GetHMenu()) , 0 ) ;
855 if ( menu
->HasAccels() )
857 // need to rebuild accell table
860 #endif // wxUSE_ACCEL
868 static void wxMenubarUnsetInvokingWindow( wxMenu
*menu
)
870 menu
->SetInvokingWindow( (wxWindow
*) NULL
);
872 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
875 wxMenuItem
*menuitem
= node
->GetData();
876 if (menuitem
->IsSubMenu())
877 wxMenubarUnsetInvokingWindow( menuitem
->GetSubMenu() );
878 node
= node
->GetNext();
882 static void wxMenubarSetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
884 menu
->SetInvokingWindow( win
);
886 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
889 wxMenuItem
*menuitem
= node
->GetData();
890 if (menuitem
->IsSubMenu())
891 wxMenubarSetInvokingWindow( menuitem
->GetSubMenu() , win
);
892 node
= node
->GetNext();
896 void wxMenuBar::UnsetInvokingWindow()
898 wxMenuList::Node
*node
= m_menus
.GetFirst();
901 wxMenu
*menu
= node
->GetData();
902 wxMenubarUnsetInvokingWindow( menu
);
903 node
= node
->GetNext();
907 void wxMenuBar::SetInvokingWindow(wxFrame
*frame
)
909 wxMenuList::Node
*node
= m_menus
.GetFirst();
912 wxMenu
*menu
= node
->GetData();
913 wxMenubarSetInvokingWindow( menu
, frame
);
914 node
= node
->GetNext();
918 void wxMenuBar::Detach()
920 wxMenuBarBase::Detach() ;
923 void wxMenuBar::Attach(wxFrame
*frame
)
925 wxMenuBarBase::Attach( frame
) ;
928 #endif // wxUSE_ACCEL
930 // ---------------------------------------------------------------------------
931 // wxMenuBar searching for menu items
932 // ---------------------------------------------------------------------------
934 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
935 int wxMenuBar::FindMenuItem(const wxString
& menuString
,
936 const wxString
& itemString
) const
938 wxString menuLabel
= wxStripMenuCodes(menuString
);
939 size_t count
= GetMenuCount();
940 for ( size_t i
= 0; i
< count
; i
++ )
942 wxString title
= wxStripMenuCodes(m_titles
[i
]);
943 if ( menuString
== title
)
944 return m_menus
[i
]->FindItem(itemString
);
950 wxMenuItem
*wxMenuBar::FindItem(int id
, wxMenu
**itemMenu
) const
955 wxMenuItem
*item
= NULL
;
956 size_t count
= GetMenuCount();
957 for ( size_t i
= 0; !item
&& (i
< count
); i
++ )
959 item
= m_menus
[i
]->FindItem(id
, itemMenu
);