]>
Commit | Line | Data |
---|---|---|
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 | 42 | class wxMenuItemCarbonImpl : public wxMenuItemImpl |
489468fe | 43 | { |
524c47aa SC |
44 | public : |
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 |
174 | protected : |
175 | MenuRef m_parentMenuRef; | |
176 | } ; | |
489468fe | 177 | |
524c47aa SC |
178 | // |
179 | // wxMenuImpl | |
180 | // | |
489468fe | 181 | |
03647350 | 182 | class wxMenuCarbonImpl : public wxMenuImpl |
489468fe | 183 | { |
524c47aa | 184 | public : |
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 ); | |
281 | protected : | |
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 | 291 | WX_DECLARE_HASH_MAP(WXHMENU, wxMenu*, wxPointerHash, wxPointerEqual, MacMenuMap); |
489468fe | 292 | |
524c47aa | 293 | static MacMenuMap wxWinMacMenuList; |
489468fe | 294 | |
524c47aa | 295 | wxMenu *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 |
302 | void wxAssociateMenuWithMacMenu(WXHMENU inMenuRef, wxMenu *menu) ; |
303 | void 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 |
312 | void wxRemoveMacMenuAssociation(wxMenu *menu) ; |
313 | void 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 | 327 | wxMenuCarbonImpl::~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 | 338 | wxMenuImpl* 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 | 368 | wxMenuItemCarbonImpl::~wxMenuItemCarbonImpl() |
489468fe | 369 | { |
489468fe SC |
370 | } |
371 | ||
489468fe | 372 | |
03647350 | 373 | wxMenuItemImpl* 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 | 388 | void 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 |