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();
193 virtual void InsertOrAppend(wxMenuItem
*pItem
, size_t pos
)
195 // MacOS counts menu items from 1 and inserts after, therefore having the
196 // same effect as wx 0 based and inserting before, we must correct pos
197 // after however for updates to be correct
199 MenuItemIndex index
= pos
;
200 if ( pos
== (size_t) -1 )
201 index
= CountMenuItems(m_osxMenu
);
203 if ( pItem
->IsSeparator() )
205 InsertMenuItemTextWithCFString( m_osxMenu
, CFSTR(""), index
, kMenuItemAttrSeparator
, 0);
206 // now switch to the Carbon 1 based counting
211 InsertMenuItemTextWithCFString( m_osxMenu
, CFSTR("placeholder"), index
, 0, 0 );
213 // now switch to the Carbon 1 based counting
215 if ( pItem
->IsSubMenu() )
217 MenuRef submenu
= pItem
->GetSubMenu()->GetHMenu();
218 SetMenuItemHierarchicalMenu(m_osxMenu
, index
, submenu
);
219 // carbon is using the title of the submenu, eg in the menubar
220 SetMenuTitleWithCFString(submenu
, wxCFStringRef(wxStripMenuCodes(pItem
->GetLabel())));
224 SetMenuItemCommandID( m_osxMenu
, index
, wxIdToMacCommand(pItem
->GetId()) ) ;
228 wxMenuItemCarbonImpl
* impl
= (wxMenuItemCarbonImpl
*) pItem
->GetPeer();
229 impl
->AttachToParent( m_osxMenu
, index
);
230 // only now can all settings be updated correctly
231 pItem
->UpdateItemText();
232 pItem
->UpdateItemStatus();
233 pItem
->UpdateItemBitmap();
236 virtual void Remove( wxMenuItem
*pItem
)
238 wxMenuItemCarbonImpl
* impl
= (wxMenuItemCarbonImpl
*) pItem
->GetPeer();
241 MenuItemIndex i
= impl
->FindMenuItemIndex();
244 DeleteMenuItem(m_osxMenu
, i
);
245 impl
->AttachToParent( NULL
, 0 );
252 virtual void MakeRoot()
254 SetRootMenu( m_osxMenu
);
257 virtual void SetTitle( const wxString
& text
)
259 SetMenuTitleWithCFString(m_osxMenu
, wxCFStringRef(text
));
262 WXHMENU
GetHMenu() { return m_osxMenu
; }
264 static wxMenuImpl
* Create( wxMenu
* peer
, const wxString
& title
);
265 static wxMenuImpl
* CreateRootMenu( wxMenu
* peer
);
267 wxCFRef
<MenuRef
> m_osxMenu
;
270 // static const short kwxMacAppleMenuId = 1 ;
272 // Find an item given the Macintosh Menu Reference
274 WX_DECLARE_HASH_MAP(WXHMENU
, wxMenu
*, wxPointerHash
, wxPointerEqual
, MacMenuMap
);
276 static MacMenuMap wxWinMacMenuList
;
278 wxMenu
*wxFindMenuFromMacMenu(WXHMENU inMenuRef
)
280 MacMenuMap::iterator node
= wxWinMacMenuList
.find(inMenuRef
);
282 return (node
== wxWinMacMenuList
.end()) ? NULL
: node
->second
;
285 void wxAssociateMenuWithMacMenu(WXHMENU inMenuRef
, wxMenu
*menu
) ;
286 void wxAssociateMenuWithMacMenu(WXHMENU inMenuRef
, wxMenu
*menu
)
288 // adding NULL MenuRef is (first) surely a result of an error and
289 // (secondly) breaks menu command processing
290 wxCHECK_RET( inMenuRef
!= (WXHMENU
) NULL
, wxT("attempt to add a NULL MenuRef to menu list") );
292 wxWinMacMenuList
[inMenuRef
] = menu
;
295 void wxRemoveMacMenuAssociation(wxMenu
*menu
) ;
296 void wxRemoveMacMenuAssociation(wxMenu
*menu
)
298 // iterate over all the elements in the class
299 MacMenuMap::iterator it
;
300 for ( it
= wxWinMacMenuList
.begin(); it
!= wxWinMacMenuList
.end(); ++it
)
302 if ( it
->second
== menu
)
304 wxWinMacMenuList
.erase(it
);
310 wxMenuCarbonImpl::~wxMenuCarbonImpl()
312 wxRemoveMacMenuAssociation( GetWXPeer() );
315 wxMenuImpl
* wxMenuImpl::Create( wxMenu
* peer
, const wxString
& title
)
318 static SInt16 s_macNextMenuId
= 3;
320 CreateNewMenu( s_macNextMenuId
++ , 0 , &menu
) ;
323 wxLogLastError(wxT("CreateNewMenu failed"));
327 wxMenuImpl
* c
= new wxMenuCarbonImpl( peer
, menu
);
329 wxAssociateMenuWithMacMenu( menu
, peer
) ;
337 wxMenuItemCarbonImpl::~wxMenuItemCarbonImpl()
342 wxMenuItemImpl
* wxMenuItemImpl::Create( wxMenuItem
* peer
,
343 wxMenu
* WXUNUSED(pParentMenu
),
345 const wxString
& WXUNUSED(text
),
346 wxAcceleratorEntry
*WXUNUSED(entry
),
347 const wxString
& WXUNUSED(strHelp
),
348 wxItemKind
WXUNUSED(kind
),
349 wxMenu
*WXUNUSED(pSubMenu
) )
351 wxMenuItemImpl
* c
= NULL
;
353 c
= new wxMenuItemCarbonImpl( peer
);
357 void wxInsertMenuItemsInMenu(wxMenu
* menu
, MenuRef wm
, MenuItemIndex insertAfter
)
359 wxMenuItemList::compatibility_iterator node
;
361 wxMenu
*subMenu
= NULL
;
362 bool newItems
= false;
364 for (node
= menu
->GetMenuItems().GetFirst(); node
; node
= node
->GetNext())
366 item
= (wxMenuItem
*)node
->GetData();
367 subMenu
= item
->GetSubMenu() ;
370 wxInsertMenuItemsInMenu(subMenu
, (MenuRef
)subMenu
->GetHMenu(), 0);
372 if ( item
->IsSeparator() )
375 InsertMenuItemTextWithCFString( wm
,
376 CFSTR(""), insertAfter
, kMenuItemAttrSeparator
, 0);
383 entry
= wxAcceleratorEntry::Create( item
->GetItemLabel() ) ;
385 MenuItemIndex winListPos
= (MenuItemIndex
)-1;
386 OSStatus err
= GetIndMenuItemWithCommandID(wm
,
387 wxIdToMacCommand ( item
->GetId() ), 1, NULL
, &winListPos
);
389 if ( wm
&& err
== menuItemNotFoundErr
)
391 // NB: the only way to determine whether or not we should add
392 // a separator is to know if we've added menu items to the menu
393 // before the separator.
395 UMAInsertMenuItem(wm
, wxStripMenuCodes(item
->GetItemLabel()) , wxFont::GetDefaultEncoding(), insertAfter
, entry
);
396 SetMenuItemCommandID( wm
, insertAfter
+1 , wxIdToMacCommand ( item
->GetId() ) ) ;
397 SetMenuItemRefCon( wm
, insertAfter
+1 , (URefCon
) item
) ;