]>
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 | |
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 !) | |
40 | // so to make things still work, we store the wxMenuItemImpl instance as a | |
41 | // RefCon at the respective menu line | |
489468fe | 42 | |
524c47aa | 43 | class wxMenuItemCarbonImpl : public wxMenuItemImpl |
489468fe | 44 | { |
524c47aa SC |
45 | public : |
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 | } |
524c47aa SC |
51 | |
52 | ~wxMenuItemCarbonImpl(); | |
53 | ||
54 | void SetBitmap( const wxBitmap& bitmap ) | |
489468fe | 55 | { |
524c47aa SC |
56 | MenuItemIndex i = FindMenuItemIndex() ; |
57 | if ( i > 0 ) | |
489468fe | 58 | { |
524c47aa | 59 | if ( bitmap.Ok() ) |
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 | } |
524c47aa SC |
77 | } |
78 | ||
79 | void Enable( bool enable ) | |
489468fe | 80 | { |
524c47aa SC |
81 | MenuItemIndex i = FindMenuItemIndex() ; |
82 | if ( i > 0 ) | |
489468fe | 83 | { |
524c47aa SC |
84 | |
85 | if ( GetWXPeer()->GetId() == wxApp::s_macPreferencesMenuItemId) | |
86 | { | |
87 | if ( enable ) | |
88 | EnableMenuCommand( NULL , kHICommandPreferences ) ; | |
89 | else | |
90 | DisableMenuCommand( NULL , kHICommandPreferences ) ; | |
91 | } | |
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 | } |
524c47aa SC |
99 | |
100 | if ( enable ) | |
101 | EnableMenuItem(m_parentMenuRef , i); | |
489468fe | 102 | else |
524c47aa SC |
103 | DisableMenuItem(m_parentMenuRef , i); |
104 | ||
105 | if ( GetWXPeer()->IsSubMenu() ) | |
489468fe | 106 | { |
524c47aa | 107 | UMAEnableMenuItem( GetWXPeer()->GetSubMenu()->GetHMenu() , 0 , enable ) ; |
489468fe SC |
108 | } |
109 | } | |
524c47aa SC |
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 | } |
524c47aa | 122 | } |
489468fe | 123 | |
524c47aa | 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 | } |
524c47aa SC |
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 | } |
524c47aa SC |
145 | |
146 | void * GetHMenuItem() { return NULL; } | |
147 | ||
148 | // Carbon Only | |
149 | ||
150 | void AttachToParent( MenuRef parentMenuRef, MenuItemIndex index ) | |
489468fe | 151 | { |
524c47aa SC |
152 | m_parentMenuRef = parentMenuRef; |
153 | if ( m_parentMenuRef && index > 0 ) | |
154 | SetMenuItemRefCon( m_parentMenuRef, index, (URefCon) this ); | |
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 ); | |
166 | if ( storedRef == (URefCon) this ) | |
167 | { | |
168 | hit = i; | |
169 | break; | |
170 | } | |
489468fe | 171 | } |
489468fe | 172 | } |
524c47aa | 173 | return hit; |
489468fe | 174 | } |
524c47aa SC |
175 | protected : |
176 | MenuRef m_parentMenuRef; | |
177 | } ; | |
489468fe | 178 | |
524c47aa SC |
179 | // |
180 | // wxMenuImpl | |
181 | // | |
489468fe | 182 | |
524c47aa | 183 | class wxMenuCarbonImpl : public wxMenuImpl |
489468fe | 184 | { |
524c47aa SC |
185 | public : |
186 | wxMenuCarbonImpl( wxMenu* peer , MenuRef menu) : wxMenuImpl(peer), m_osxMenu(menu) | |
489468fe | 187 | { |
489468fe | 188 | } |
489468fe | 189 | |
524c47aa SC |
190 | virtual ~wxMenuCarbonImpl(); |
191 | ||
489468fe | 192 | |
524c47aa | 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); | |
202 | ||
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 | } |
524c47aa | 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 | } |
524c47aa SC |
227 | |
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 | } | |
235 | ||
236 | virtual void Remove( wxMenuItem *pItem ) | |
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 | } | |
524c47aa SC |
249 | |
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 | |
524c47aa SC |
262 | static wxMenuImpl* Create( wxMenu* peer, const wxString& title ); |
263 | static wxMenuImpl* CreateRootMenu( wxMenu* peer ); | |
264 | protected : | |
265 | wxCFRef<MenuRef> m_osxMenu; | |
266 | } ; | |
489468fe | 267 | |
524c47aa | 268 | // static const short kwxMacAppleMenuId = 1 ; |
489468fe | 269 | |
524c47aa | 270 | // Find an item given the Macintosh Menu Reference |
489468fe | 271 | |
524c47aa | 272 | WX_DECLARE_HASH_MAP(WXHMENU, wxMenu*, wxPointerHash, wxPointerEqual, MacMenuMap); |
489468fe | 273 | |
524c47aa | 274 | static MacMenuMap wxWinMacMenuList; |
489468fe | 275 | |
524c47aa | 276 | wxMenu *wxFindMenuFromMacMenu(WXHMENU inMenuRef) |
489468fe | 277 | { |
524c47aa | 278 | MacMenuMap::iterator node = wxWinMacMenuList.find(inMenuRef); |
489468fe | 279 | |
524c47aa | 280 | return (node == wxWinMacMenuList.end()) ? NULL : node->second; |
489468fe SC |
281 | } |
282 | ||
524c47aa SC |
283 | void wxAssociateMenuWithMacMenu(WXHMENU inMenuRef, wxMenu *menu) ; |
284 | void wxAssociateMenuWithMacMenu(WXHMENU inMenuRef, wxMenu *menu) | |
489468fe | 285 | { |
524c47aa SC |
286 | // adding NULL MenuRef is (first) surely a result of an error and |
287 | // (secondly) breaks menu command processing | |
288 | wxCHECK_RET( inMenuRef != (WXHMENU) NULL, wxT("attempt to add a NULL MenuRef to menu list") ); | |
489468fe | 289 | |
524c47aa | 290 | wxWinMacMenuList[inMenuRef] = menu; |
489468fe SC |
291 | } |
292 | ||
524c47aa SC |
293 | void wxRemoveMacMenuAssociation(wxMenu *menu) ; |
294 | void wxRemoveMacMenuAssociation(wxMenu *menu) | |
489468fe | 295 | { |
524c47aa SC |
296 | // iterate over all the elements in the class |
297 | MacMenuMap::iterator it; | |
298 | for ( it = wxWinMacMenuList.begin(); it != wxWinMacMenuList.end(); ++it ) | |
489468fe | 299 | { |
524c47aa | 300 | if ( it->second == menu ) |
489468fe | 301 | { |
524c47aa SC |
302 | wxWinMacMenuList.erase(it); |
303 | break; | |
489468fe | 304 | } |
489468fe | 305 | } |
489468fe SC |
306 | } |
307 | ||
524c47aa | 308 | wxMenuCarbonImpl::~wxMenuCarbonImpl() |
489468fe | 309 | { |
524c47aa | 310 | wxRemoveMacMenuAssociation( GetWXPeer() ); |
489468fe SC |
311 | } |
312 | ||
524c47aa | 313 | wxMenuImpl* wxMenuImpl::Create( wxMenu* peer, const wxString& title ) |
489468fe | 314 | { |
524c47aa SC |
315 | // create the menu |
316 | static SInt16 s_macNextMenuId = 3; | |
317 | WXHMENU menu = NULL; | |
318 | CreateNewMenu( s_macNextMenuId++ , 0 , &menu ) ; | |
489468fe | 319 | if ( !menu ) |
489468fe | 320 | { |
524c47aa SC |
321 | wxLogLastError(wxT("CreateNewMenu failed")); |
322 | return NULL; | |
489468fe SC |
323 | } |
324 | ||
524c47aa SC |
325 | wxMenuImpl* c = new wxMenuCarbonImpl( peer, menu ); |
326 | c->SetTitle(title); | |
327 | wxAssociateMenuWithMacMenu( menu , peer ) ; | |
328 | return c; | |
489468fe SC |
329 | } |
330 | ||
524c47aa SC |
331 | // |
332 | // | |
333 | // | |
489468fe | 334 | |
524c47aa | 335 | wxMenuItemCarbonImpl::~wxMenuItemCarbonImpl() |
489468fe | 336 | { |
489468fe SC |
337 | } |
338 | ||
489468fe | 339 | |
524c47aa SC |
340 | wxMenuItemImpl* wxMenuItemImpl::Create( wxMenuItem* peer, |
341 | wxMenu * WXUNUSED(pParentMenu), | |
342 | int WXUNUSED(id), | |
343 | const wxString& WXUNUSED(text), | |
344 | wxAcceleratorEntry *WXUNUSED(entry), | |
345 | const wxString& WXUNUSED(strHelp), | |
346 | wxItemKind WXUNUSED(kind), | |
347 | wxMenu *WXUNUSED(pSubMenu) ) | |
489468fe | 348 | { |
524c47aa | 349 | wxMenuItemImpl* c = NULL; |
489468fe | 350 | |
524c47aa SC |
351 | c = new wxMenuItemCarbonImpl( peer ); |
352 | return c; | |
489468fe SC |
353 | } |
354 | ||
524c47aa | 355 | void wxInsertMenuItemsInMenu(wxMenu* menu, MenuRef wm, MenuItemIndex insertAfter) |
489468fe | 356 | { |
524c47aa SC |
357 | wxMenuItemList::compatibility_iterator node; |
358 | wxMenuItem *item; | |
359 | wxMenu *subMenu = NULL ; | |
360 | bool newItems = false; | |
489468fe | 361 | |
524c47aa | 362 | for (node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext()) |
489468fe | 363 | { |
524c47aa SC |
364 | item = (wxMenuItem *)node->GetData(); |
365 | subMenu = item->GetSubMenu() ; | |
366 | if (subMenu) | |
367 | { | |
368 | wxInsertMenuItemsInMenu(subMenu, (MenuRef)subMenu->GetHMenu(), 0); | |
369 | } | |
370 | if ( item->IsSeparator() ) | |
371 | { | |
372 | if ( wm && newItems) | |
373 | InsertMenuItemTextWithCFString( wm, | |
374 | CFSTR(""), insertAfter, kMenuItemAttrSeparator, 0); | |
489468fe | 375 | |
524c47aa SC |
376 | newItems = false; |
377 | } | |
378 | else | |
379 | { | |
380 | wxAcceleratorEntry* | |
381 | entry = wxAcceleratorEntry::Create( item->GetItemLabel() ) ; | |
489468fe | 382 | |
524c47aa SC |
383 | MenuItemIndex winListPos = (MenuItemIndex)-1; |
384 | OSStatus err = GetIndMenuItemWithCommandID(wm, | |
385 | wxIdToMacCommand ( item->GetId() ), 1, NULL, &winListPos); | |
489468fe | 386 | |
524c47aa SC |
387 | if ( wm && err == menuItemNotFoundErr ) |
388 | { | |
389 | // NB: the only way to determine whether or not we should add | |
390 | // a separator is to know if we've added menu items to the menu | |
391 | // before the separator. | |
392 | newItems = true; | |
393 | UMAInsertMenuItem(wm, wxStripMenuCodes(item->GetItemLabel()) , wxFont::GetDefaultEncoding(), insertAfter, entry); | |
394 | SetMenuItemCommandID( wm , insertAfter+1 , wxIdToMacCommand ( item->GetId() ) ) ; | |
395 | SetMenuItemRefCon( wm , insertAfter+1 , (URefCon) item ) ; | |
396 | } | |
489468fe | 397 | |
524c47aa SC |
398 | delete entry ; |
399 | } | |
489468fe | 400 | } |
489468fe SC |
401 | } |
402 | ||
489468fe | 403 |