1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/carbon/menu.cpp
3 // Purpose: wxMenu, wxMenuBar, wxMenuItem
4 // Author: Stefan Csomor
8 // Copyright: (c) Stefan Csomor
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
13 // headers & declarations
14 // ============================================================================
19 #include "wx/wxprec.h"
28 #include "wx/menuitem.h"
31 #include "wx/osx/private.h"
32 #include "wx/stockitem.h"
34 // other standard headers
35 // ----------------------
38 // under carbon there's no such thing as a MenuItemRef, everything is done
39 // on the 'parent' menu via index APIs (first line having index 1 !)
40 // so to make things still work, we store the wxMenuItemImpl instance as a
41 // RefCon at the respective menu line
43 class wxMenuItemCarbonImpl
: public wxMenuItemImpl
46 wxMenuItemCarbonImpl( wxMenuItem
* peer
) : wxMenuItemImpl(peer
)
48 // the parent menu ref is only set, once the item has been attached
49 m_parentMenuRef
= NULL
;
52 ~wxMenuItemCarbonImpl();
54 void SetBitmap( const wxBitmap
& bitmap
)
56 MenuItemIndex i
= FindMenuItemIndex() ;
62 ControlButtonContentInfo info
;
63 wxMacCreateBitmapButton( &info
, bitmap
) ;
64 if ( info
.contentType
!= kControlNoContent
)
66 if ( info
.contentType
== kControlContentIconRef
)
67 SetMenuItemIconHandle( m_parentMenuRef
, i
,
68 kMenuIconRefType
, (Handle
) info
.u
.iconRef
) ;
69 else if ( info
.contentType
== kControlContentCGImageRef
)
70 SetMenuItemIconHandle( m_parentMenuRef
, i
,
71 kMenuCGImageRefType
, (Handle
) info
.u
.imageRef
) ;
73 wxMacReleaseBitmapButton( &info
) ;
79 void Enable( bool enable
)
81 MenuItemIndex i
= FindMenuItemIndex() ;
85 if ( GetWXPeer()->GetId() == wxApp::s_macPreferencesMenuItemId
)
88 EnableMenuCommand( NULL
, kHICommandPreferences
) ;
90 DisableMenuCommand( NULL
, kHICommandPreferences
) ;
92 else if ( GetWXPeer()->GetId() == wxApp::s_macExitMenuItemId
)
95 EnableMenuCommand( NULL
, kHICommandQuit
) ;
97 DisableMenuCommand( NULL
, kHICommandQuit
) ;
101 EnableMenuItem(m_parentMenuRef
, i
);
103 DisableMenuItem(m_parentMenuRef
, i
);
105 if ( GetWXPeer()->IsSubMenu() )
107 UMAEnableMenuItem( GetWXPeer()->GetSubMenu()->GetHMenu() , 0 , enable
) ;
112 void Check( bool check
)
114 MenuItemIndex i
= FindMenuItemIndex() ;
118 ::SetItemMark( m_parentMenuRef
, i
, 0x12 ) ; // checkmark
120 ::SetItemMark( m_parentMenuRef
, i
, 0 ) ; // no mark
124 void Hide( bool hide
)
126 MenuItemIndex i
= FindMenuItemIndex() ;
130 ChangeMenuItemAttributes( m_parentMenuRef
, i
, kMenuItemAttrHidden
, 0 );
132 ChangeMenuItemAttributes( m_parentMenuRef
, i
, 0 , kMenuItemAttrHidden
);
136 void SetLabel( const wxString
& text
, wxAcceleratorEntry
*entry
)
138 MenuItemIndex i
= FindMenuItemIndex() ;
141 SetMenuItemTextWithCFString( m_parentMenuRef
, i
, wxCFStringRef(text
));
142 UMASetMenuItemShortcut( m_parentMenuRef
, i
, entry
) ;
146 void * GetHMenuItem() { return NULL
; }
150 void AttachToParent( MenuRef parentMenuRef
, MenuItemIndex index
)
152 m_parentMenuRef
= parentMenuRef
;
153 if ( m_parentMenuRef
&& index
> 0 )
154 SetMenuItemRefCon( m_parentMenuRef
, index
, (URefCon
) this );
157 MenuItemIndex
FindMenuItemIndex()
159 MenuItemIndex hit
= 0 ;
160 if ( m_parentMenuRef
)
162 for ( MenuItemIndex i
= 1 ; i
<= CountMenuItems(m_parentMenuRef
) ; ++i
)
164 URefCon storedRef
= 0;
165 GetMenuItemRefCon(m_parentMenuRef
, i
, &storedRef
);
166 if ( storedRef
== (URefCon
) this )
176 MenuRef m_parentMenuRef
;
183 class wxMenuCarbonImpl
: public wxMenuImpl
186 wxMenuCarbonImpl( wxMenu
* peer
, MenuRef menu
) : wxMenuImpl(peer
), m_osxMenu(menu
)
190 virtual ~wxMenuCarbonImpl();
192 virtual void InsertOrAppend(wxMenuItem
*pItem
, size_t pos
)
194 // MacOS counts menu items from 1 and inserts after, therefore having the
195 // same effect as wx 0 based and inserting before, we must correct pos
196 // after however for updates to be correct
198 MenuItemIndex index
= pos
;
199 if ( pos
== (size_t) -1 )
200 index
= CountMenuItems(m_osxMenu
);
202 if ( pItem
->IsSeparator() )
204 InsertMenuItemTextWithCFString( m_osxMenu
, CFSTR(""), index
, kMenuItemAttrSeparator
, 0);
205 // now switch to the Carbon 1 based counting
210 InsertMenuItemTextWithCFString( m_osxMenu
, CFSTR("placeholder"), index
, 0, 0 );
212 // now switch to the Carbon 1 based counting
214 if ( pItem
->IsSubMenu() )
216 MenuRef submenu
= pItem
->GetSubMenu()->GetHMenu();
217 SetMenuItemHierarchicalMenu(m_osxMenu
, index
, submenu
);
218 // carbon is using the title of the submenu, eg in the menubar
219 SetMenuTitleWithCFString(submenu
, wxCFStringRef(pItem
->GetItemLabelText()));
223 SetMenuItemCommandID( m_osxMenu
, index
, wxIdToMacCommand(pItem
->GetId()) ) ;
227 wxMenuItemCarbonImpl
* impl
= (wxMenuItemCarbonImpl
*) pItem
->GetPeer();
228 impl
->AttachToParent( m_osxMenu
, index
);
229 // only now can all settings be updated correctly
230 pItem
->UpdateItemText();
231 pItem
->UpdateItemStatus();
232 pItem
->UpdateItemBitmap();
235 virtual void Remove( wxMenuItem
*pItem
)
237 wxMenuItemCarbonImpl
* impl
= (wxMenuItemCarbonImpl
*) pItem
->GetPeer();
240 MenuItemIndex i
= impl
->FindMenuItemIndex();
243 DeleteMenuItem(m_osxMenu
, i
);
244 impl
->AttachToParent( NULL
, 0 );
249 virtual void MakeRoot()
251 SetRootMenu( m_osxMenu
);
254 virtual void SetTitle( const wxString
& text
)
256 SetMenuTitleWithCFString(m_osxMenu
, wxCFStringRef(text
));
259 WXHMENU
GetHMenu() { return m_osxMenu
; }
261 virtual void PopUp( wxWindow
*WXUNUSED(win
), int x
, int y
)
263 long menuResult
= ::PopUpMenuSelect(m_osxMenu
, y
, x
, 0) ;
264 if ( HiWord(menuResult
) != 0 )
267 GetMenuItemCommandID( GetMenuHandle(HiWord(menuResult
)) , LoWord(menuResult
) , &macid
);
268 int id
= wxMacCommandToId( macid
);
269 wxMenuItem
* item
= NULL
;
271 item
= m_peer
->FindItem( id
, &realmenu
) ;
274 m_peer
->HandleCommandProcess(item
, NULL
);
279 static wxMenuImpl
* Create( wxMenu
* peer
, const wxString
& title
);
280 static wxMenuImpl
* CreateRootMenu( wxMenu
* peer
);
282 wxCFRef
<MenuRef
> m_osxMenu
;
285 // static const short kwxMacAppleMenuId = 1 ;
287 // Find an item given the Macintosh Menu Reference
289 WX_DECLARE_HASH_MAP(WXHMENU
, wxMenu
*, wxPointerHash
, wxPointerEqual
, MacMenuMap
);
291 static MacMenuMap wxWinMacMenuList
;
293 wxMenu
*wxFindMenuFromMacMenu(WXHMENU inMenuRef
)
295 MacMenuMap::iterator node
= wxWinMacMenuList
.find(inMenuRef
);
297 return (node
== wxWinMacMenuList
.end()) ? NULL
: node
->second
;
300 void wxAssociateMenuWithMacMenu(WXHMENU inMenuRef
, wxMenu
*menu
) ;
301 void wxAssociateMenuWithMacMenu(WXHMENU inMenuRef
, wxMenu
*menu
)
303 // adding NULL MenuRef is (first) surely a result of an error and
304 // (secondly) breaks menu command processing
305 wxCHECK_RET( inMenuRef
!= (WXHMENU
) NULL
, wxT("attempt to add a NULL MenuRef to menu list") );
307 wxWinMacMenuList
[inMenuRef
] = menu
;
310 void wxRemoveMacMenuAssociation(wxMenu
*menu
) ;
311 void wxRemoveMacMenuAssociation(wxMenu
*menu
)
313 // iterate over all the elements in the class
314 MacMenuMap::iterator it
;
315 for ( it
= wxWinMacMenuList
.begin(); it
!= wxWinMacMenuList
.end(); ++it
)
317 if ( it
->second
== menu
)
319 wxWinMacMenuList
.erase(it
);
325 wxMenuCarbonImpl::~wxMenuCarbonImpl()
327 wxRemoveMacMenuAssociation( GetWXPeer() );
330 wxMenuImpl
* wxMenuImpl::Create( wxMenu
* peer
, const wxString
& title
)
333 static SInt16 s_macNextMenuId
= 3;
335 CreateNewMenu( s_macNextMenuId
++ , 0 , &menu
) ;
338 wxLogLastError(wxT("CreateNewMenu failed"));
342 wxMenuImpl
* c
= new wxMenuCarbonImpl( peer
, menu
);
344 wxAssociateMenuWithMacMenu( menu
, peer
) ;
352 wxMenuItemCarbonImpl::~wxMenuItemCarbonImpl()
357 wxMenuItemImpl
* wxMenuItemImpl::Create( wxMenuItem
* peer
,
358 wxMenu
* WXUNUSED(pParentMenu
),
360 const wxString
& WXUNUSED(text
),
361 wxAcceleratorEntry
*WXUNUSED(entry
),
362 const wxString
& WXUNUSED(strHelp
),
363 wxItemKind
WXUNUSED(kind
),
364 wxMenu
*WXUNUSED(pSubMenu
) )
366 wxMenuItemImpl
* c
= NULL
;
368 c
= new wxMenuItemCarbonImpl( peer
);
372 void wxInsertMenuItemsInMenu(wxMenu
* menu
, MenuRef wm
, MenuItemIndex insertAfter
)
374 wxMenuItemList::compatibility_iterator node
;
376 wxMenu
*subMenu
= NULL
;
377 bool newItems
= false;
379 for (node
= menu
->GetMenuItems().GetFirst(); node
; node
= node
->GetNext())
381 item
= (wxMenuItem
*)node
->GetData();
382 subMenu
= item
->GetSubMenu() ;
385 wxInsertMenuItemsInMenu(subMenu
, (MenuRef
)subMenu
->GetHMenu(), 0);
387 if ( item
->IsSeparator() )
390 InsertMenuItemTextWithCFString( wm
,
391 CFSTR(""), insertAfter
, kMenuItemAttrSeparator
, 0);
398 entry
= wxAcceleratorEntry::Create( item
->GetItemLabel() ) ;
400 MenuItemIndex winListPos
= (MenuItemIndex
)-1;
401 OSStatus err
= GetIndMenuItemWithCommandID(wm
,
402 wxIdToMacCommand ( item
->GetId() ), 1, NULL
, &winListPos
);
404 if ( wm
&& err
== menuItemNotFoundErr
)
406 // NB: the only way to determine whether or not we should add
407 // a separator is to know if we've added menu items to the menu
408 // before the separator.
410 UMAInsertMenuItem(wm
, wxStripMenuCodes(item
->GetItemLabel()) , wxFont::GetDefaultEncoding(), insertAfter
, entry
);
411 SetMenuItemCommandID( wm
, insertAfter
+1 , wxIdToMacCommand ( item
->GetId() ) ) ;
412 SetMenuItemRefCon( wm
, insertAfter
+1 , (URefCon
) item
) ;