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