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
) m_peer
);
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
) m_peer
)
176 MenuRef m_parentMenuRef
;
183 class wxMenuCarbonImpl
: public wxMenuImpl
186 wxMenuCarbonImpl( wxMenu
* peer
, MenuRef menu
, MenuRef oldMenu
, SInt16 menuId
)
187 : wxMenuImpl(peer
), m_osxMenu(menu
), m_oldMenuRef(oldMenu
), m_menuId(menuId
)
191 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(pItem
->GetItemLabelText()));
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 );
250 virtual void MakeRoot()
252 SetRootMenu( m_osxMenu
);
255 virtual void SetTitle( const wxString
& text
)
257 SetMenuTitleWithCFString(m_osxMenu
, wxCFStringRef(text
));
260 WXHMENU
GetHMenu() { return m_osxMenu
; }
262 virtual void PopUp( wxWindow
*WXUNUSED(win
), int x
, int y
)
264 long menuResult
= ::PopUpMenuSelect(m_osxMenu
, y
, x
, 0) ;
265 if ( HiWord(menuResult
) != 0 )
268 GetMenuItemCommandID( GetMenuHandle(HiWord(menuResult
)) , LoWord(menuResult
) , &macid
);
269 int id
= wxMacCommandToId( macid
);
270 wxMenuItem
* item
= NULL
;
272 item
= m_peer
->FindItem( id
, &realmenu
) ;
275 m_peer
->HandleCommandProcess(item
, NULL
);
280 static wxMenuImpl
* Create( wxMenu
* peer
, const wxString
& title
);
281 static wxMenuImpl
* CreateRootMenu( wxMenu
* peer
);
283 wxCFRef
<MenuRef
> m_osxMenu
;
284 MenuRef m_oldMenuRef
;
288 // static const short kwxMacAppleMenuId = 1 ;
290 // Find an item given the Macintosh Menu Reference
292 WX_DECLARE_HASH_MAP(WXHMENU
, wxMenu
*, wxPointerHash
, wxPointerEqual
, MacMenuMap
);
294 static MacMenuMap wxWinMacMenuList
;
296 wxMenu
*wxFindMenuFromMacMenu(WXHMENU inMenuRef
)
298 MacMenuMap::iterator node
= wxWinMacMenuList
.find(inMenuRef
);
300 return (node
== wxWinMacMenuList
.end()) ? NULL
: node
->second
;
303 void wxAssociateMenuWithMacMenu(WXHMENU inMenuRef
, wxMenu
*menu
) ;
304 void wxAssociateMenuWithMacMenu(WXHMENU inMenuRef
, wxMenu
*menu
)
306 // adding NULL MenuRef is (first) surely a result of an error and
307 // (secondly) breaks menu command processing
308 wxCHECK_RET( inMenuRef
!= (WXHMENU
) NULL
, wxT("attempt to add a NULL MenuRef to menu list") );
310 wxWinMacMenuList
[inMenuRef
] = menu
;
313 void wxRemoveMacMenuAssociation(wxMenu
*menu
) ;
314 void wxRemoveMacMenuAssociation(wxMenu
*menu
)
316 // iterate over all the elements in the class
317 MacMenuMap::iterator it
;
318 for ( it
= wxWinMacMenuList
.begin(); it
!= wxWinMacMenuList
.end(); ++it
)
320 if ( it
->second
== menu
)
322 wxWinMacMenuList
.erase(it
);
328 wxMenuCarbonImpl::~wxMenuCarbonImpl()
330 wxRemoveMacMenuAssociation( GetWXPeer() );
331 // restore previous menu
334 MacDeleteMenu(m_menuId
);
336 MacInsertMenu(m_oldMenuRef
, -1);
339 wxMenuImpl
* wxMenuImpl::Create( wxMenu
* peer
, const wxString
& title
)
342 static SInt16 s_macNextMenuId
= 3;
343 SInt16 menuId
= s_macNextMenuId
++;
344 // save existing menu in case we're embedding into an application
345 // or sharing outside UI elements.
346 WXHMENU oldMenu
= GetMenuHandle(menuId
);
348 MacDeleteMenu(menuId
);
350 CreateNewMenu( menuId
, 0 , &menu
) ;
353 wxLogLastError(wxT("CreateNewMenu failed"));
355 MacInsertMenu(oldMenu
, -1);
359 wxMenuImpl
* c
= new wxMenuCarbonImpl( peer
, menu
, oldMenu
, menuId
);
361 wxAssociateMenuWithMacMenu( menu
, peer
) ;
369 wxMenuItemCarbonImpl::~wxMenuItemCarbonImpl()
374 wxMenuItemImpl
* wxMenuItemImpl::Create( wxMenuItem
* peer
,
375 wxMenu
* WXUNUSED(pParentMenu
),
377 const wxString
& WXUNUSED(text
),
378 wxAcceleratorEntry
*WXUNUSED(entry
),
379 const wxString
& WXUNUSED(strHelp
),
380 wxItemKind
WXUNUSED(kind
),
381 wxMenu
*WXUNUSED(pSubMenu
) )
383 wxMenuItemImpl
* c
= NULL
;
385 c
= new wxMenuItemCarbonImpl( peer
);
389 void wxInsertMenuItemsInMenu(wxMenu
* menu
, MenuRef wm
, MenuItemIndex insertAfter
)
391 wxMenuItemList::compatibility_iterator node
;
393 wxMenu
*subMenu
= NULL
;
394 bool newItems
= false;
396 for (node
= menu
->GetMenuItems().GetFirst(); node
; node
= node
->GetNext())
398 item
= (wxMenuItem
*)node
->GetData();
399 subMenu
= item
->GetSubMenu() ;
402 wxInsertMenuItemsInMenu(subMenu
, (MenuRef
)subMenu
->GetHMenu(), 0);
404 if ( item
->IsSeparator() )
407 InsertMenuItemTextWithCFString( wm
,
408 CFSTR(""), insertAfter
, kMenuItemAttrSeparator
, 0);
415 entry
= wxAcceleratorEntry::Create( item
->GetItemLabel() ) ;
417 MenuItemIndex winListPos
= (MenuItemIndex
)-1;
418 OSStatus err
= GetIndMenuItemWithCommandID(wm
,
419 wxIdToMacCommand ( item
->GetId() ), 1, NULL
, &winListPos
);
421 if ( wm
&& err
== menuItemNotFoundErr
)
423 // NB: the only way to determine whether or not we should add
424 // a separator is to know if we've added menu items to the menu
425 // before the separator.
427 UMAInsertMenuItem(wm
, wxStripMenuCodes(item
->GetItemLabel()) , wxFont::GetDefaultEncoding(), insertAfter
, entry
);
428 SetMenuItemCommandID( wm
, insertAfter
+1 , wxIdToMacCommand ( item
->GetId() ) ) ;
429 SetMenuItemRefCon( wm
, insertAfter
+1 , (URefCon
) item
) ;