1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/carbon/menu.cpp
3 // Purpose: wxMenu, wxMenuBar, wxMenuItem
4 // Author: Stefan Csomor
7 // Copyright: (c) Stefan Csomor
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
12 // headers & declarations
13 // ============================================================================
18 #include "wx/wxprec.h"
27 #include "wx/menuitem.h"
30 #include "wx/osx/private.h"
31 #include "wx/stockitem.h"
33 // other standard headers
34 // ----------------------
37 // under carbon there's no such thing as a MenuItemRef, everything is done
38 // on the 'parent' menu via index APIs (first line having index 1 !)
39 // so to make things still work, we store the wxMenuItemImpl instance as a
40 // RefCon at the respective menu line
42 class wxMenuItemCarbonImpl
: public wxMenuItemImpl
45 wxMenuItemCarbonImpl( wxMenuItem
* peer
) : wxMenuItemImpl(peer
)
47 // the parent menu ref is only set, once the item has been attached
48 m_parentMenuRef
= NULL
;
51 ~wxMenuItemCarbonImpl();
53 void SetBitmap( const wxBitmap
& bitmap
)
55 MenuItemIndex i
= FindMenuItemIndex() ;
61 ControlButtonContentInfo info
;
62 wxMacCreateBitmapButton( &info
, bitmap
) ;
63 if ( info
.contentType
!= kControlNoContent
)
65 if ( info
.contentType
== kControlContentIconRef
)
66 SetMenuItemIconHandle( m_parentMenuRef
, i
,
67 kMenuIconRefType
, (Handle
) info
.u
.iconRef
) ;
68 else if ( info
.contentType
== kControlContentCGImageRef
)
69 SetMenuItemIconHandle( m_parentMenuRef
, i
,
70 kMenuCGImageRefType
, (Handle
) info
.u
.imageRef
) ;
72 wxMacReleaseBitmapButton( &info
) ;
78 void Enable( bool enable
)
80 MenuItemIndex i
= FindMenuItemIndex() ;
84 if ( GetWXPeer()->GetId() == wxApp::s_macPreferencesMenuItemId
)
87 EnableMenuCommand( NULL
, kHICommandPreferences
) ;
89 DisableMenuCommand( NULL
, kHICommandPreferences
) ;
91 else if ( GetWXPeer()->GetId() == wxApp::s_macExitMenuItemId
)
94 EnableMenuCommand( NULL
, kHICommandQuit
) ;
96 DisableMenuCommand( NULL
, kHICommandQuit
) ;
100 EnableMenuItem(m_parentMenuRef
, i
);
102 DisableMenuItem(m_parentMenuRef
, i
);
104 if ( GetWXPeer()->IsSubMenu() )
106 UMAEnableMenuItem( GetWXPeer()->GetSubMenu()->GetHMenu() , 0 , enable
) ;
111 void Check( bool check
)
113 MenuItemIndex i
= FindMenuItemIndex() ;
117 ::SetItemMark( m_parentMenuRef
, i
, 0x12 ) ; // checkmark
119 ::SetItemMark( m_parentMenuRef
, i
, 0 ) ; // no mark
123 void Hide( bool hide
)
125 MenuItemIndex i
= FindMenuItemIndex() ;
129 ChangeMenuItemAttributes( m_parentMenuRef
, i
, kMenuItemAttrHidden
, 0 );
131 ChangeMenuItemAttributes( m_parentMenuRef
, i
, 0 , kMenuItemAttrHidden
);
135 void SetLabel( const wxString
& text
, wxAcceleratorEntry
*entry
)
137 MenuItemIndex i
= FindMenuItemIndex() ;
140 SetMenuItemTextWithCFString( m_parentMenuRef
, i
, wxCFStringRef(text
));
141 UMASetMenuItemShortcut( m_parentMenuRef
, i
, entry
) ;
145 void * GetHMenuItem() { return NULL
; }
149 void AttachToParent( MenuRef parentMenuRef
, MenuItemIndex index
)
151 m_parentMenuRef
= parentMenuRef
;
152 if ( m_parentMenuRef
&& index
> 0 )
153 SetMenuItemRefCon( m_parentMenuRef
, index
, (URefCon
) m_peer
);
156 MenuItemIndex
FindMenuItemIndex()
158 MenuItemIndex hit
= 0 ;
159 if ( m_parentMenuRef
)
161 for ( MenuItemIndex i
= 1 ; i
<= CountMenuItems(m_parentMenuRef
) ; ++i
)
163 URefCon storedRef
= 0;
164 GetMenuItemRefCon(m_parentMenuRef
, i
, &storedRef
);
165 if ( storedRef
== (URefCon
) m_peer
)
175 MenuRef m_parentMenuRef
;
182 class wxMenuCarbonImpl
: public wxMenuImpl
185 wxMenuCarbonImpl( wxMenu
* peer
, MenuRef menu
, MenuRef oldMenu
, SInt16 menuId
)
186 : wxMenuImpl(peer
), m_osxMenu(menu
), m_oldMenuRef(oldMenu
), m_menuId(menuId
)
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
;
283 MenuRef m_oldMenuRef
;
287 // static const short kwxMacAppleMenuId = 1 ;
289 // Find an item given the Macintosh Menu Reference
291 WX_DECLARE_HASH_MAP(WXHMENU
, wxMenu
*, wxPointerHash
, wxPointerEqual
, MacMenuMap
);
293 static MacMenuMap wxWinMacMenuList
;
295 wxMenu
*wxFindMenuFromMacMenu(WXHMENU inMenuRef
)
297 MacMenuMap::iterator node
= wxWinMacMenuList
.find(inMenuRef
);
299 return (node
== wxWinMacMenuList
.end()) ? NULL
: node
->second
;
302 void wxAssociateMenuWithMacMenu(WXHMENU inMenuRef
, wxMenu
*menu
) ;
303 void wxAssociateMenuWithMacMenu(WXHMENU inMenuRef
, wxMenu
*menu
)
305 // adding NULL MenuRef is (first) surely a result of an error and
306 // (secondly) breaks menu command processing
307 wxCHECK_RET( inMenuRef
!= (WXHMENU
) NULL
, wxT("attempt to add a NULL MenuRef to menu list") );
309 wxWinMacMenuList
[inMenuRef
] = menu
;
312 void wxRemoveMacMenuAssociation(wxMenu
*menu
) ;
313 void wxRemoveMacMenuAssociation(wxMenu
*menu
)
315 // iterate over all the elements in the class
316 MacMenuMap::iterator it
;
317 for ( it
= wxWinMacMenuList
.begin(); it
!= wxWinMacMenuList
.end(); ++it
)
319 if ( it
->second
== menu
)
321 wxWinMacMenuList
.erase(it
);
327 wxMenuCarbonImpl::~wxMenuCarbonImpl()
329 wxRemoveMacMenuAssociation( GetWXPeer() );
330 // restore previous menu
333 MacDeleteMenu(m_menuId
);
335 MacInsertMenu(m_oldMenuRef
, -1);
338 wxMenuImpl
* wxMenuImpl::Create( wxMenu
* peer
, const wxString
& title
)
341 static SInt16 s_macNextMenuId
= 3;
342 SInt16 menuId
= s_macNextMenuId
++;
343 // save existing menu in case we're embedding into an application
344 // or sharing outside UI elements.
345 WXHMENU oldMenu
= GetMenuHandle(menuId
);
347 MacDeleteMenu(menuId
);
349 CreateNewMenu( menuId
, 0 , &menu
) ;
352 wxLogLastError(wxT("CreateNewMenu failed"));
354 MacInsertMenu(oldMenu
, -1);
358 wxMenuImpl
* c
= new wxMenuCarbonImpl( peer
, menu
, oldMenu
, menuId
);
360 wxAssociateMenuWithMacMenu( menu
, peer
) ;
368 wxMenuItemCarbonImpl::~wxMenuItemCarbonImpl()
373 wxMenuItemImpl
* wxMenuItemImpl::Create( wxMenuItem
* peer
,
374 wxMenu
* WXUNUSED(pParentMenu
),
376 const wxString
& WXUNUSED(text
),
377 wxAcceleratorEntry
*WXUNUSED(entry
),
378 const wxString
& WXUNUSED(strHelp
),
379 wxItemKind
WXUNUSED(kind
),
380 wxMenu
*WXUNUSED(pSubMenu
) )
382 wxMenuItemImpl
* c
= NULL
;
384 c
= new wxMenuItemCarbonImpl( peer
);
388 void wxInsertMenuItemsInMenu(wxMenu
* menu
, MenuRef wm
, MenuItemIndex insertAfter
)
390 wxMenuItemList::compatibility_iterator node
;
392 wxMenu
*subMenu
= NULL
;
393 bool newItems
= false;
395 for (node
= menu
->GetMenuItems().GetFirst(); node
; node
= node
->GetNext())
397 item
= (wxMenuItem
*)node
->GetData();
398 subMenu
= item
->GetSubMenu() ;
401 wxInsertMenuItemsInMenu(subMenu
, (MenuRef
)subMenu
->GetHMenu(), 0);
403 if ( item
->IsSeparator() )
406 InsertMenuItemTextWithCFString( wm
,
407 CFSTR(""), insertAfter
, kMenuItemAttrSeparator
, 0);
414 entry
= wxAcceleratorEntry::Create( item
->GetItemLabel() ) ;
416 MenuItemIndex winListPos
= (MenuItemIndex
)-1;
417 OSStatus err
= GetIndMenuItemWithCommandID(wm
,
418 wxIdToMacCommand ( item
->GetId() ), 1, NULL
, &winListPos
);
420 if ( wm
&& err
== menuItemNotFoundErr
)
422 // NB: the only way to determine whether or not we should add
423 // a separator is to know if we've added menu items to the menu
424 // before the separator.
426 UMAInsertMenuItem(wm
, wxStripMenuCodes(item
->GetItemLabel()) , wxFont::GetDefaultEncoding(), insertAfter
, entry
);
427 SetMenuItemCommandID( wm
, insertAfter
+1 , wxIdToMacCommand ( item
->GetId() ) ) ;
428 SetMenuItemRefCon( wm
, insertAfter
+1 , (URefCon
) item
) ;