]> git.saurik.com Git - wxWidgets.git/blame - src/osx/carbon/menu.cpp
wxMessageBox off the main thread lost result code.
[wxWidgets.git] / src / osx / carbon / menu.cpp
CommitLineData
489468fe 1/////////////////////////////////////////////////////////////////////////////
524c47aa 2// Name: src/osx/carbon/menu.cpp
489468fe
SC
3// Purpose: wxMenu, wxMenuBar, wxMenuItem
4// Author: Stefan Csomor
5// Modified by:
6// Created: 1998-01-01
489468fe
SC
7// Copyright: (c) Stefan Csomor
8// Licence: wxWindows licence
9/////////////////////////////////////////////////////////////////////////////
10
11// ============================================================================
12// headers & declarations
13// ============================================================================
14
15// wxWidgets headers
16// -----------------
17
18#include "wx/wxprec.h"
19
20#include "wx/menu.h"
21
22#ifndef WX_PRECOMP
23 #include "wx/log.h"
24 #include "wx/app.h"
25 #include "wx/utils.h"
26 #include "wx/frame.h"
27 #include "wx/menuitem.h"
28#endif
29
524c47aa
SC
30#include "wx/osx/private.h"
31#include "wx/stockitem.h"
489468fe
SC
32
33// other standard headers
34// ----------------------
35#include <string.h>
36
524c47aa
SC
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 !)
03647350 39// so to make things still work, we store the wxMenuItemImpl instance as a
524c47aa 40// RefCon at the respective menu line
489468fe 41
03647350 42class wxMenuItemCarbonImpl : public wxMenuItemImpl
489468fe 43{
524c47aa
SC
44public :
45 wxMenuItemCarbonImpl( wxMenuItem* peer ) : wxMenuItemImpl(peer)
489468fe 46 {
524c47aa
SC
47 // the parent menu ref is only set, once the item has been attached
48 m_parentMenuRef = NULL;
489468fe 49 }
03647350 50
524c47aa 51 ~wxMenuItemCarbonImpl();
03647350 52
524c47aa 53 void SetBitmap( const wxBitmap& bitmap )
489468fe 54 {
524c47aa
SC
55 MenuItemIndex i = FindMenuItemIndex() ;
56 if ( i > 0 )
489468fe 57 {
a1b806b9 58 if ( bitmap.IsOk() )
489468fe 59 {
524c47aa
SC
60#if wxUSE_BMPBUTTON
61 ControlButtonContentInfo info ;
62 wxMacCreateBitmapButton( &info , bitmap ) ;
63 if ( info.contentType != kControlNoContent )
64 {
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 ) ;
71 }
72 wxMacReleaseBitmapButton( &info ) ;
489468fe 73#endif
489468fe 74 }
489468fe 75 }
03647350
VZ
76 }
77
78 void Enable( bool enable )
489468fe 79 {
524c47aa
SC
80 MenuItemIndex i = FindMenuItemIndex() ;
81 if ( i > 0 )
489468fe 82 {
03647350 83
524c47aa
SC
84 if ( GetWXPeer()->GetId() == wxApp::s_macPreferencesMenuItemId)
85 {
86 if ( enable )
87 EnableMenuCommand( NULL , kHICommandPreferences ) ;
88 else
89 DisableMenuCommand( NULL , kHICommandPreferences ) ;
03647350 90 }
524c47aa 91 else if ( GetWXPeer()->GetId() == wxApp::s_macExitMenuItemId)
489468fe 92 {
524c47aa
SC
93 if ( enable )
94 EnableMenuCommand( NULL , kHICommandQuit ) ;
95 else
96 DisableMenuCommand( NULL , kHICommandQuit ) ;
489468fe 97 }
03647350 98
524c47aa
SC
99 if ( enable )
100 EnableMenuItem(m_parentMenuRef , i);
489468fe 101 else
524c47aa 102 DisableMenuItem(m_parentMenuRef , i);
03647350 103
524c47aa 104 if ( GetWXPeer()->IsSubMenu() )
489468fe 105 {
524c47aa 106 UMAEnableMenuItem( GetWXPeer()->GetSubMenu()->GetHMenu() , 0 , enable ) ;
489468fe
SC
107 }
108 }
03647350
VZ
109 }
110
111 void Check( bool check )
489468fe 112 {
524c47aa
SC
113 MenuItemIndex i = FindMenuItemIndex() ;
114 if ( i > 0 )
489468fe 115 {
524c47aa
SC
116 if ( check )
117 ::SetItemMark( m_parentMenuRef, i, 0x12 ) ; // checkmark
118 else
119 ::SetItemMark( m_parentMenuRef, i, 0 ) ; // no mark
489468fe 120 }
03647350 121 }
489468fe 122
03647350 123 void Hide( bool hide )
489468fe 124 {
524c47aa
SC
125 MenuItemIndex i = FindMenuItemIndex() ;
126 if ( i > 0 )
489468fe 127 {
524c47aa
SC
128 if ( hide )
129 ChangeMenuItemAttributes( m_parentMenuRef, i, kMenuItemAttrHidden, 0 );
130 else
131 ChangeMenuItemAttributes( m_parentMenuRef, i, 0 , kMenuItemAttrHidden );
489468fe 132 }
03647350
VZ
133 }
134
135 void SetLabel( const wxString& text, wxAcceleratorEntry *entry )
489468fe 136 {
524c47aa
SC
137 MenuItemIndex i = FindMenuItemIndex() ;
138 if ( i > 0 )
489468fe 139 {
524c47aa
SC
140 SetMenuItemTextWithCFString( m_parentMenuRef, i, wxCFStringRef(text));
141 UMASetMenuItemShortcut( m_parentMenuRef, i , entry ) ;
142 }
489468fe 143 }
03647350 144
524c47aa 145 void * GetHMenuItem() { return NULL; }
03647350 146
524c47aa 147 // Carbon Only
03647350
VZ
148
149 void AttachToParent( MenuRef parentMenuRef, MenuItemIndex index )
489468fe 150 {
524c47aa
SC
151 m_parentMenuRef = parentMenuRef;
152 if ( m_parentMenuRef && index > 0 )
0ee6e449 153 SetMenuItemRefCon( m_parentMenuRef, index, (URefCon) m_peer );
489468fe
SC
154 }
155
524c47aa 156 MenuItemIndex FindMenuItemIndex()
489468fe 157 {
524c47aa
SC
158 MenuItemIndex hit = 0 ;
159 if ( m_parentMenuRef )
489468fe 160 {
524c47aa 161 for ( MenuItemIndex i = 1 ; i <= CountMenuItems(m_parentMenuRef) ; ++i )
489468fe 162 {
524c47aa
SC
163 URefCon storedRef = 0;
164 GetMenuItemRefCon(m_parentMenuRef, i, &storedRef );
0ee6e449 165 if ( storedRef == (URefCon) m_peer )
524c47aa
SC
166 {
167 hit = i;
168 break;
169 }
489468fe 170 }
489468fe 171 }
524c47aa 172 return hit;
489468fe 173 }
524c47aa
SC
174protected :
175 MenuRef m_parentMenuRef;
176} ;
489468fe 177
524c47aa
SC
178//
179// wxMenuImpl
180//
489468fe 181
03647350 182class wxMenuCarbonImpl : public wxMenuImpl
489468fe 183{
524c47aa 184public :
1076e695
VZ
185 wxMenuCarbonImpl( wxMenu* peer , MenuRef menu , MenuRef oldMenu , SInt16 menuId)
186 : wxMenuImpl(peer), m_osxMenu(menu), m_oldMenuRef(oldMenu), m_menuId(menuId)
489468fe 187 {
489468fe 188 }
489468fe 189
524c47aa 190 virtual ~wxMenuCarbonImpl();
03647350
VZ
191
192 virtual void InsertOrAppend(wxMenuItem *pItem, size_t pos)
489468fe 193 {
524c47aa
SC
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
489468fe 197
524c47aa
SC
198 MenuItemIndex index = pos;
199 if ( pos == (size_t) -1 )
200 index = CountMenuItems(m_osxMenu);
03647350 201
524c47aa 202 if ( pItem->IsSeparator() )
489468fe 203 {
524c47aa
SC
204 InsertMenuItemTextWithCFString( m_osxMenu, CFSTR(""), index, kMenuItemAttrSeparator, 0);
205 // now switch to the Carbon 1 based counting
206 index += 1 ;
489468fe 207 }
03647350 208 else
489468fe 209 {
524c47aa
SC
210 InsertMenuItemTextWithCFString( m_osxMenu, CFSTR("placeholder"), index, 0, 0 );
211
212 // now switch to the Carbon 1 based counting
213 index += 1 ;
214 if ( pItem->IsSubMenu() )
489468fe 215 {
524c47aa
SC
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
41b93cd7 219 SetMenuTitleWithCFString(submenu, wxCFStringRef(pItem->GetItemLabelText()));
489468fe 220 }
524c47aa 221 else
489468fe 222 {
524c47aa 223 SetMenuItemCommandID( m_osxMenu, index , wxIdToMacCommand(pItem->GetId()) ) ;
489468fe 224 }
489468fe 225 }
03647350 226
524c47aa
SC
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();
233 }
03647350
VZ
234
235 virtual void Remove( wxMenuItem *pItem )
524c47aa
SC
236 {
237 wxMenuItemCarbonImpl* impl = (wxMenuItemCarbonImpl*) pItem->GetPeer();
238 if ( impl )
489468fe 239 {
524c47aa
SC
240 MenuItemIndex i = impl->FindMenuItemIndex();
241 if ( i > 0 )
242 {
243 DeleteMenuItem(m_osxMenu , i);
244 impl->AttachToParent( NULL, 0 );
245 }
489468fe
SC
246 }
247 }
03647350 248
524c47aa 249 virtual void MakeRoot()
489468fe 250 {
524c47aa 251 SetRootMenu( m_osxMenu );
489468fe
SC
252 }
253
524c47aa
SC
254 virtual void SetTitle( const wxString& text )
255 {
256 SetMenuTitleWithCFString(m_osxMenu, wxCFStringRef(text));
257 }
489468fe 258
524c47aa 259 WXHMENU GetHMenu() { return m_osxMenu; }
489468fe 260
2cb5d2d2
SC
261 virtual void PopUp( wxWindow *WXUNUSED(win), int x, int y )
262 {
263 long menuResult = ::PopUpMenuSelect(m_osxMenu, y, x, 0) ;
264 if ( HiWord(menuResult) != 0 )
265 {
266 MenuCommand macid;
267 GetMenuItemCommandID( GetMenuHandle(HiWord(menuResult)) , LoWord(menuResult) , &macid );
268 int id = wxMacCommandToId( macid );
269 wxMenuItem* item = NULL ;
270 wxMenu* realmenu ;
271 item = m_peer->FindItem( id, &realmenu ) ;
272 if ( item )
273 {
274 m_peer->HandleCommandProcess(item, NULL );
275 }
276 }
277 }
278
524c47aa
SC
279 static wxMenuImpl* Create( wxMenu* peer, const wxString& title );
280 static wxMenuImpl* CreateRootMenu( wxMenu* peer );
281protected :
282 wxCFRef<MenuRef> m_osxMenu;
1076e695
VZ
283 MenuRef m_oldMenuRef;
284 SInt16 m_menuId;
524c47aa 285} ;
489468fe 286
524c47aa 287// static const short kwxMacAppleMenuId = 1 ;
489468fe 288
524c47aa 289// Find an item given the Macintosh Menu Reference
489468fe 290
524c47aa 291WX_DECLARE_HASH_MAP(WXHMENU, wxMenu*, wxPointerHash, wxPointerEqual, MacMenuMap);
489468fe 292
524c47aa 293static MacMenuMap wxWinMacMenuList;
489468fe 294
524c47aa 295wxMenu *wxFindMenuFromMacMenu(WXHMENU inMenuRef)
489468fe 296{
524c47aa 297 MacMenuMap::iterator node = wxWinMacMenuList.find(inMenuRef);
489468fe 298
524c47aa 299 return (node == wxWinMacMenuList.end()) ? NULL : node->second;
489468fe
SC
300}
301
524c47aa
SC
302void wxAssociateMenuWithMacMenu(WXHMENU inMenuRef, wxMenu *menu) ;
303void wxAssociateMenuWithMacMenu(WXHMENU inMenuRef, wxMenu *menu)
489468fe 304{
524c47aa
SC
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") );
489468fe 308
524c47aa 309 wxWinMacMenuList[inMenuRef] = menu;
489468fe
SC
310}
311
524c47aa
SC
312void wxRemoveMacMenuAssociation(wxMenu *menu) ;
313void wxRemoveMacMenuAssociation(wxMenu *menu)
489468fe 314{
524c47aa
SC
315 // iterate over all the elements in the class
316 MacMenuMap::iterator it;
317 for ( it = wxWinMacMenuList.begin(); it != wxWinMacMenuList.end(); ++it )
489468fe 318 {
524c47aa 319 if ( it->second == menu )
489468fe 320 {
524c47aa
SC
321 wxWinMacMenuList.erase(it);
322 break;
489468fe 323 }
489468fe 324 }
489468fe
SC
325}
326
524c47aa 327wxMenuCarbonImpl::~wxMenuCarbonImpl()
489468fe 328{
524c47aa 329 wxRemoveMacMenuAssociation( GetWXPeer() );
1076e695
VZ
330 // restore previous menu
331 m_osxMenu.reset();
332 if ( m_menuId > 0 )
333 MacDeleteMenu(m_menuId);
334 if ( m_oldMenuRef )
335 MacInsertMenu(m_oldMenuRef, -1);
489468fe
SC
336}
337
524c47aa 338wxMenuImpl* wxMenuImpl::Create( wxMenu* peer, const wxString& title )
489468fe 339{
524c47aa
SC
340 // create the menu
341 static SInt16 s_macNextMenuId = 3;
1076e695
VZ
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);
346 if ( oldMenu )
347 MacDeleteMenu(menuId);
524c47aa 348 WXHMENU menu = NULL;
1076e695 349 CreateNewMenu( menuId , 0 , &menu ) ;
489468fe 350 if ( !menu )
489468fe 351 {
524c47aa 352 wxLogLastError(wxT("CreateNewMenu failed"));
1076e695
VZ
353 if ( oldMenu )
354 MacInsertMenu(oldMenu, -1);
524c47aa 355 return NULL;
489468fe
SC
356 }
357
1076e695 358 wxMenuImpl* c = new wxMenuCarbonImpl( peer, menu, oldMenu, menuId );
524c47aa
SC
359 c->SetTitle(title);
360 wxAssociateMenuWithMacMenu( menu , peer ) ;
361 return c;
489468fe
SC
362}
363
524c47aa
SC
364//
365//
366//
489468fe 367
524c47aa 368wxMenuItemCarbonImpl::~wxMenuItemCarbonImpl()
489468fe 369{
489468fe
SC
370}
371
489468fe 372
03647350 373wxMenuItemImpl* wxMenuItemImpl::Create( wxMenuItem* peer,
524c47aa
SC
374 wxMenu * WXUNUSED(pParentMenu),
375 int WXUNUSED(id),
376 const wxString& WXUNUSED(text),
377 wxAcceleratorEntry *WXUNUSED(entry),
378 const wxString& WXUNUSED(strHelp),
379 wxItemKind WXUNUSED(kind),
380 wxMenu *WXUNUSED(pSubMenu) )
489468fe 381{
524c47aa 382 wxMenuItemImpl* c = NULL;
489468fe 383
524c47aa
SC
384 c = new wxMenuItemCarbonImpl( peer );
385 return c;
489468fe
SC
386}
387
524c47aa 388void wxInsertMenuItemsInMenu(wxMenu* menu, MenuRef wm, MenuItemIndex insertAfter)
489468fe 389{
524c47aa
SC
390 wxMenuItemList::compatibility_iterator node;
391 wxMenuItem *item;
392 wxMenu *subMenu = NULL ;
393 bool newItems = false;
489468fe 394
524c47aa 395 for (node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext())
489468fe 396 {
524c47aa
SC
397 item = (wxMenuItem *)node->GetData();
398 subMenu = item->GetSubMenu() ;
399 if (subMenu)
400 {
401 wxInsertMenuItemsInMenu(subMenu, (MenuRef)subMenu->GetHMenu(), 0);
402 }
403 if ( item->IsSeparator() )
404 {
405 if ( wm && newItems)
406 InsertMenuItemTextWithCFString( wm,
407 CFSTR(""), insertAfter, kMenuItemAttrSeparator, 0);
489468fe 408
524c47aa
SC
409 newItems = false;
410 }
411 else
412 {
413 wxAcceleratorEntry*
414 entry = wxAcceleratorEntry::Create( item->GetItemLabel() ) ;
489468fe 415
524c47aa
SC
416 MenuItemIndex winListPos = (MenuItemIndex)-1;
417 OSStatus err = GetIndMenuItemWithCommandID(wm,
418 wxIdToMacCommand ( item->GetId() ), 1, NULL, &winListPos);
489468fe 419
524c47aa
SC
420 if ( wm && err == menuItemNotFoundErr )
421 {
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.
425 newItems = true;
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 ) ;
429 }
489468fe 430
524c47aa
SC
431 delete entry ;
432 }
489468fe 433 }
489468fe
SC
434}
435
489468fe 436