1///////////////////////////////////////////////////////////////////////////// 2// Name: src/mac/carbon/menu.cpp 3// Purpose: wxMenu, wxMenuBar, wxMenuItem 4// Author: Stefan Csomor 5// Modified by: 6// Created: 1998-01-01 7// RCS-ID: $Id: menu.cpp 62127 2009-09-25 15:07:53Z JS $ 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 31#include "wx/mac/uma.h" 32 33// other standard headers 34// ---------------------- 35#include <string.h> 36 37IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler) 38IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler) 39 40// the (popup) menu title has this special id 41static const int idMenuTitle = -3; 42 43static const short kwxMacAppleMenuId = 1 ; 44 45 46// Find an item given the Macintosh Menu Reference 47 48WX_DECLARE_HASH_MAP(MenuRef, wxMenu*, wxPointerHash, wxPointerEqual, MacMenuMap); 49 50static MacMenuMap wxWinMacMenuList; 51 52wxMenu *wxFindMenuFromMacMenu(MenuRef inMenuRef) 53{ 54 MacMenuMap::iterator node = wxWinMacMenuList.find(inMenuRef); 55 56 return (node == wxWinMacMenuList.end()) ? NULL : node->second; 57} 58 59void wxAssociateMenuWithMacMenu(MenuRef inMenuRef, wxMenu *menu) ; 60void wxAssociateMenuWithMacMenu(MenuRef inMenuRef, wxMenu *menu) 61{ 62 // adding NULL MenuRef is (first) surely a result of an error and 63 // (secondly) breaks menu command processing 64 wxCHECK_RET( inMenuRef != (MenuRef) NULL, wxT("attempt to add a NULL MenuRef to menu list") ); 65 66 wxWinMacMenuList[inMenuRef] = menu; 67} 68 69void wxRemoveMacMenuAssociation(wxMenu *menu) ; 70void wxRemoveMacMenuAssociation(wxMenu *menu) 71{ 72 // iterate over all the elements in the class 73 MacMenuMap::iterator it; 74 for ( it = wxWinMacMenuList.begin(); it != wxWinMacMenuList.end(); ++it ) 75 { 76 if ( it->second == menu ) 77 { 78 wxWinMacMenuList.erase(it); 79 break; 80 } 81 } 82} 83 84void wxInsertMenuItemsInMenu(wxMenu* menu, MenuRef wm, MenuItemIndex insertAfter) 85{ 86 wxMenuItemList::compatibility_iterator node; 87 wxMenuItem *item; 88 wxMenu *subMenu = NULL ; 89 bool newItems = false; 90 91 for (node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext()) 92 { 93 item = (wxMenuItem *)node->GetData(); 94 subMenu = item->GetSubMenu() ; 95 if (subMenu) 96 { 97 wxInsertMenuItemsInMenu(subMenu, (MenuRef)subMenu->GetHMenu(), 0); 98 } 99 if ( item->IsSeparator() ) 100 { 101 if ( wm && newItems) 102 InsertMenuItemTextWithCFString( wm, 103 CFSTR(""), insertAfter, kMenuItemAttrSeparator, 0); 104 105 newItems = false; 106 } 107 else 108 { 109 wxAcceleratorEntry* 110 entry = wxAcceleratorEntry::Create( item->GetText() ) ; 111 112 MenuItemIndex winListPos = (MenuItemIndex)-1; 113 OSStatus err = GetIndMenuItemWithCommandID(wm, 114 wxIdToMacCommand ( item->GetId() ), 1, NULL, &winListPos); 115 116 if ( wm && err == menuItemNotFoundErr ) 117 { 118 // NB: the only way to determine whether or not we should add 119 // a separator is to know if we've added menu items to the menu 120 // before the separator. 121 newItems = true; 122 UMAInsertMenuItem(wm, wxStripMenuCodes(item->GetText()) , wxFont::GetDefaultEncoding(), insertAfter, entry); 123 SetMenuItemCommandID( wm , insertAfter+1 , wxIdToMacCommand ( item->GetId() ) ) ; 124 SetMenuItemRefCon( wm , insertAfter+1 , (URefCon) item ) ; 125 } 126 127 delete entry ; 128 } 129 } 130} 131 132// ============================================================================ 133// implementation 134// ============================================================================ 135static void wxMenubarUnsetInvokingWindow( wxMenu *menu ) ; 136static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win ); 137 138// Menus 139 140// Construct a menu with optional title (then use append) 141 142#ifdef __DARWIN__ 143short wxMenu::s_macNextMenuId = 3 ; 144#else 145short wxMenu::s_macNextMenuId = 2 ; 146#endif 147 148static 149wxMenu * 150_wxMenuAt(const wxMenuList &menuList, size_t pos) 151{ 152 wxMenuList::compatibility_iterator menuIter = menuList.GetFirst(); 153 154 while (pos-- > 0) 155 menuIter = menuIter->GetNext(); 156 157 return menuIter->GetData() ; 158} 159 160void wxMenu::Init() 161{ 162 m_doBreak = false; 163 m_startRadioGroup = -1; 164 165 // create the menu 166 m_macMenuId = s_macNextMenuId++; 167 m_hMenu = UMANewMenu(m_macMenuId, m_title, wxFont::GetDefaultEncoding() ); 168 169 if ( !m_hMenu ) 170 { 171 wxLogLastError(wxT("UMANewMenu failed")); 172 } 173 174 wxAssociateMenuWithMacMenu( (MenuRef)m_hMenu , this ) ; 175 176 // if we have a title, insert it in the beginning of the menu 177 if ( !m_title.empty() ) 178 { 179 Append(idMenuTitle, m_title) ; 180 AppendSeparator() ; 181 } 182} 183 184wxMenu::~wxMenu() 185{ 186 wxRemoveMacMenuAssociation( this ) ; 187 if (MAC_WXHMENU(m_hMenu)) 188 ::DisposeMenu(MAC_WXHMENU(m_hMenu)); 189} 190 191void wxMenu::Break() 192{ 193 // not available on the mac platform 194} 195 196void wxMenu::Attach(wxMenuBarBase *menubar) 197{ 198 wxMenuBase::Attach(menubar); 199 200 EndRadioGroup(); 201} 202 203// function appends a new item or submenu to the menu 204// append a new item or submenu to the menu 205bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos) 206{ 207 wxASSERT_MSG( pItem != NULL, wxT("can't append NULL item to the menu") ); 208 209 if ( pItem->IsSeparator() ) 210 { 211 if ( pos == (size_t)-1 ) 212 AppendMenuItemTextWithCFString( MAC_WXHMENU(m_hMenu), 213 CFSTR(""), kMenuItemAttrSeparator, 0,NULL); 214 else 215 InsertMenuItemTextWithCFString( MAC_WXHMENU(m_hMenu), 216 CFSTR(""), pos, kMenuItemAttrSeparator, 0); 217 } 218 else 219 { 220 wxMenu *pSubMenu = pItem->GetSubMenu() ; 221 if ( pSubMenu != NULL ) 222 { 223 wxASSERT_MSG( pSubMenu->m_hMenu != NULL , wxT("invalid submenu added")); 224 pSubMenu->m_menuParent = this ; 225 226 // We need the !GetMenuBar() check to make sure we run MacBeforeDisplay() 227 // for popup menus and other menus which may not be part of the main 228 // menu bar. 229 if (!GetMenuBar() || wxMenuBar::MacGetInstalledMenuBar() == GetMenuBar()) 230 pSubMenu->MacBeforeDisplay( true ) ; 231 232 if ( pos == (size_t)-1 ) 233 UMAAppendSubMenuItem(MAC_WXHMENU(m_hMenu), wxStripMenuCodes(pItem->GetText()), wxFont::GetDefaultEncoding(), pSubMenu->m_macMenuId); 234 else 235 UMAInsertSubMenuItem(MAC_WXHMENU(m_hMenu), wxStripMenuCodes(pItem->GetText()), wxFont::GetDefaultEncoding(), pos, pSubMenu->m_macMenuId); 236 237 pItem->UpdateItemBitmap() ; 238 pItem->UpdateItemStatus() ; 239 } 240 else 241 { 242 if ( pos == (size_t)-1 ) 243 { 244 UMAAppendMenuItem(MAC_WXHMENU(m_hMenu), wxT("a") , wxFont::GetDefaultEncoding() ); 245 pos = CountMenuItems(MAC_WXHMENU(m_hMenu)) ; 246 } 247 else 248 { 249 // MacOS counts menu items from 1 and inserts after, therefore having the 250 // same effect as wx 0 based and inserting before, we must correct pos 251 // after however for updates to be correct 252 UMAInsertMenuItem(MAC_WXHMENU(m_hMenu), wxT("a"), wxFont::GetDefaultEncoding(), pos); 253 pos += 1 ; 254 } 255 256 SetMenuItemCommandID( MAC_WXHMENU(m_hMenu) , pos , wxIdToMacCommand ( pItem->GetId() ) ) ; 257 SetMenuItemRefCon( MAC_WXHMENU(m_hMenu) , pos , (URefCon) pItem ) ; 258 pItem->UpdateItemText() ; 259 pItem->UpdateItemBitmap() ; 260 pItem->UpdateItemStatus() ; 261 262 if ( pItem->GetId() == idMenuTitle ) 263 UMAEnableMenuItem(MAC_WXHMENU(m_hMenu) , pos , false ) ; 264 } 265 } 266 267 // if we're already attached to the menubar, we must update it 268 if ( IsAttached() && GetMenuBar()->IsAttached() ) 269 GetMenuBar()->Refresh(); 270 271 return true ; 272} 273 274void wxMenu::EndRadioGroup() 275{ 276 // we're not inside a radio group any longer 277 m_startRadioGroup = -1; 278} 279 280wxMenuItem* wxMenu::DoAppend(wxMenuItem *item) 281{ 282 wxCHECK_MSG( item, NULL, _T("NULL item in wxMenu::DoAppend") ); 283 284 bool check = false; 285 286 if ( item->GetKind() == wxITEM_RADIO ) 287 { 288 int count = GetMenuItemCount(); 289 290 if ( m_startRadioGroup == -1 ) 291 { 292 // start a new radio group 293 m_startRadioGroup = count; 294 295 // for now it has just one element 296 item->SetAsRadioGroupStart(); 297 item->SetRadioGroupEnd(m_startRadioGroup); 298 299 // ensure that we have a checked item in the radio group 300 check = true; 301 } 302 else // extend the current radio group 303 { 304 // we need to update its end item 305 item->SetRadioGroupStart(m_startRadioGroup); 306 wxMenuItemList::compatibility_iterator node = GetMenuItems().Item(m_startRadioGroup); 307 308 if ( node ) 309 { 310 node->GetData()->SetRadioGroupEnd(count); 311 } 312 else 313 { 314 wxFAIL_MSG( _T("where is the radio group start item?") ); 315 } 316 } 317 } 318 else // not a radio item 319 { 320 EndRadioGroup(); 321 } 322 323 if ( !wxMenuBase::DoAppend(item) || !DoInsertOrAppend(item) ) 324 return NULL; 325 326 if ( check ) 327 // check the item initially 328 item->Check(true); 329 330 return item; 331} 332 333wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item) 334{ 335 if (wxMenuBase::DoInsert(pos, item) && DoInsertOrAppend(item, pos)) 336 return item; 337 338 return NULL; 339} 340 341wxMenuItem *wxMenu::DoRemove(wxMenuItem *item) 342{ 343 // we need to find the items position in the child list 344 size_t pos; 345 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst(); 346 347 for ( pos = 0; node; pos++ ) 348 { 349 if ( node->GetData() == item ) 350 break; 351 352 node = node->GetNext(); 353 } 354 355 // DoRemove() (unlike Remove) can only be called for existing item! 356 wxCHECK_MSG( node, NULL, wxT("bug in wxMenu::Remove logic") ); 357 358 ::DeleteMenuItem(MAC_WXHMENU(m_hMenu) , pos + 1); 359 360 if ( IsAttached() && GetMenuBar()->IsAttached() ) 361 // otherwise, the change won't be visible 362 GetMenuBar()->Refresh(); 363 364 // and from internal data structures 365 return wxMenuBase::DoRemove(item); 366} 367 368void wxMenu::SetTitle(const wxString& label) 369{ 370 m_title = label ; 371 UMASetMenuTitle(MAC_WXHMENU(m_hMenu) , label , wxFont::GetDefaultEncoding() ) ; 372} 373 374bool wxMenu::ProcessCommand(wxCommandEvent & event) 375{ 376 bool processed = false; 377 378 // Try the menu's event handler 379 if ( /* !processed && */ GetEventHandler()) 380 processed = GetEventHandler()->ProcessEvent(event); 381 382 // Try the window the menu was popped up from 383 // (and up through the hierarchy) 384 wxWindow *win = GetInvokingWindow(); 385 if ( !processed && win ) 386 processed = win->GetEventHandler()->ProcessEvent(event); 387 388 return processed; 389} 390 391// --------------------------------------------------------------------------- 392// other 393// --------------------------------------------------------------------------- 394 395wxWindow *wxMenu::GetWindow() const 396{ 397 if ( m_invokingWindow != NULL ) 398 return m_invokingWindow; 399 else if ( GetMenuBar() != NULL) 400 return (wxWindow *) GetMenuBar()->GetFrame(); 401 402 return NULL; 403} 404 405// helper functions returning the mac menu position for a certain item, note that this is 406// mac-wise 1 - based, i.e. the first item has index 1 whereas on MSWin it has pos 0 407 408int wxMenu::MacGetIndexFromId( int id ) 409{ 410 size_t pos; 411 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst(); 412 for ( pos = 0; node; pos++ ) 413 { 414 if ( node->GetData()->GetId() == id ) 415 break; 416 417 node = node->GetNext(); 418 } 419 420 if (!node) 421 return 0; 422 423 return pos + 1 ; 424} 425 426int wxMenu::MacGetIndexFromItem( wxMenuItem *pItem ) 427{ 428 size_t pos; 429 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst(); 430 for ( pos = 0; node; pos++ ) 431 { 432 if ( node->GetData() == pItem ) 433 break; 434 435 node = node->GetNext(); 436 } 437 438 if (!node) 439 return 0; 440 441 return pos + 1 ; 442} 443 444void wxMenu::MacEnableMenu( bool bDoEnable ) 445{ 446 UMAEnableMenuItem(MAC_WXHMENU(m_hMenu) , 0 , bDoEnable ) ; 447 448 ::DrawMenuBar() ; 449} 450 451// MacOS needs to know about submenus somewhere within this menu 452// before it can be displayed, also hide special menu items 453// like preferences that are handled by the OS 454void wxMenu::MacBeforeDisplay( bool isSubMenu ) 455{ 456 wxMenuItem* previousItem = NULL ; 457 size_t pos ; 458 wxMenuItemList::compatibility_iterator node; 459 wxMenuItem *item; 460 461 for (pos = 0, node = GetMenuItems().GetFirst(); node; node = node->GetNext(), pos++) 462 { 463 item = (wxMenuItem *)node->GetData(); 464 wxMenu* subMenu = item->GetSubMenu() ; 465 if (subMenu) 466 { 467 subMenu->MacBeforeDisplay( true ) ; 468 } 469 else // normal item 470 { 471#if TARGET_CARBON 472 // what we do here is to hide the special items which are 473 // shown in the application menu anyhow -- it doesn't make 474 // sense to show them in their normal place as well 475 if ( item->GetId() == wxApp::s_macAboutMenuItemId || 476 ( UMAGetSystemVersion() >= 0x1000 && ( 477 item->GetId() == wxApp::s_macPreferencesMenuItemId || 478 item->GetId() == wxApp::s_macExitMenuItemId ) ) ) 479 480 { 481 ChangeMenuItemAttributes( MAC_WXHMENU( GetHMenu() ), 482 pos + 1, kMenuItemAttrHidden, 0 ); 483 484 // also check for a separator which was used just to 485 // separate this item from the others, so don't leave 486 // separator at the menu start or end nor 2 consecutive 487 // separators 488 wxMenuItemList::compatibility_iterator nextNode = node->GetNext(); 489 wxMenuItem *next = nextNode ? nextNode->GetData() : NULL; 490 491 size_t posSeptoHide; 492 if ( !previousItem && next && next->IsSeparator() ) 493 { 494 // next (i.e. second as we must be first) item is 495 // the separator to hide 496 wxASSERT_MSG( pos == 0, _T("should be the menu start") ); 497 posSeptoHide = 2; 498 } 499 else if ( GetMenuItems().GetCount() == pos + 1 && 500 previousItem != NULL && 501 previousItem->IsSeparator() ) 502 { 503 // prev item is a trailing separator we want to hide 504 posSeptoHide = pos; 505 } 506 else if ( previousItem && previousItem->IsSeparator() && 507 next && next->IsSeparator() ) 508 { 509 // two consecutive separators, this is one too many 510 posSeptoHide = pos; 511 } 512 else // no separators to hide 513 { 514 posSeptoHide = 0; 515 } 516 517 if ( posSeptoHide ) 518 { 519 // hide the separator as well 520 ChangeMenuItemAttributes( MAC_WXHMENU( GetHMenu() ), 521 posSeptoHide, 522 kMenuItemAttrHidden, 523 0 ); 524 } 525 } 526#endif // TARGET_CARBON 527 } 528 529 previousItem = item ; 530 } 531 532 if ( isSubMenu ) 533 ::InsertMenu(MAC_WXHMENU( GetHMenu()), -1); 534} 535 536// undo all changes from the MacBeforeDisplay call 537void wxMenu::MacAfterDisplay( bool isSubMenu ) 538{ 539 if ( isSubMenu ) 540 ::DeleteMenu(MacGetMenuId()); 541 542 wxMenuItemList::compatibility_iterator node; 543 wxMenuItem *item; 544 545 for (node = GetMenuItems().GetFirst(); node; node = node->GetNext()) 546 { 547 item = (wxMenuItem *)node->GetData(); 548 wxMenu* subMenu = item->GetSubMenu() ; 549 if (subMenu) 550 { 551 subMenu->MacAfterDisplay( true ) ; 552 } 553 else 554 { 555 // no need to undo hidings 556 } 557 } 558} 559 560wxInt32 wxMenu::MacHandleCommandProcess( wxMenuItem* item, int id, wxWindow* targetWindow ) 561{ 562 OSStatus result = eventNotHandledErr ; 563 if (item->IsCheckable()) 564 item->Check( !item->IsChecked() ) ; 565 566 if ( SendEvent( id , item->IsCheckable() ? item->IsChecked() : -1 ) ) 567 result = noErr ; 568 else 569 { 570 if ( targetWindow != NULL ) 571 { 572 wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED , id); 573 event.SetEventObject(targetWindow); 574 event.SetInt(item->IsCheckable() ? item->IsChecked() : -1); 575 576 if ( targetWindow->GetEventHandler()->ProcessEvent(event) ) 577 result = noErr ; 578 } 579 } 580 return result; 581} 582 583wxInt32 wxMenu::MacHandleCommandUpdateStatus( wxMenuItem* item, int id, wxWindow* targetWindow ) 584{ 585 OSStatus result = eventNotHandledErr ; 586 wxUpdateUIEvent event(id); 587 event.SetEventObject( this ); 588 589 bool processed = false; 590 591 // Try the menu's event handler 592 { 593 wxEvtHandler *handler = GetEventHandler(); 594 if ( handler ) 595 processed = handler->ProcessEvent(event); 596 } 597 598 // Try the window the menu was popped up from 599 // (and up through the hierarchy) 600 if ( !processed ) 601 { 602 const wxMenuBase *menu = this; 603 while ( menu ) 604 { 605 wxWindow *win = menu->GetInvokingWindow(); 606 if ( win ) 607 { 608 processed = win->GetEventHandler()->ProcessEvent(event); 609 break; 610 } 611 612 menu = menu->GetParent(); 613 } 614 } 615 616 if ( !processed && targetWindow != NULL) 617 { 618 processed = targetWindow->GetEventHandler()->ProcessEvent(event); 619 } 620 621 if ( processed ) 622 { 623 // if anything changed, update the changed attribute 624 if (event.GetSetText()) 625 SetLabel(id, event.GetText()); 626 if (event.GetSetChecked()) 627 Check(id, event.GetChecked()); 628 if (event.GetSetEnabled()) 629 Enable(id, event.GetEnabled()); 630 631 result = noErr ; 632 } 633 return result; 634} 635 636// Menu Bar 637 638/* 639 640Mac Implementation note : 641 642The Mac has only one global menubar, so we attempt to install the currently 643active menubar from a frame, we currently don't take into account mdi-frames 644which would ask for menu-merging 645 646Secondly there is no mac api for changing a menubar that is not the current 647menubar, so we have to wait for preparing the actual menubar until the 648wxMenubar is to be used 649 650We can in subsequent versions use MacInstallMenuBar to provide some sort of 651auto-merge for MDI in case this will be necessary 652 653*/ 654 655wxMenuBar* wxMenuBar::s_macInstalledMenuBar = NULL ; 656wxMenuBar* wxMenuBar::s_macCommonMenuBar = NULL ; 657bool wxMenuBar::s_macAutoWindowMenu = true ; 658WXHMENU wxMenuBar::s_macWindowMenuHandle = NULL ; 659 660void wxMenuBar::Init() 661{ 662 m_eventHandler = this; 663 m_menuBarFrame = NULL; 664 m_invokingWindow = (wxWindow*) NULL; 665} 666 667wxMenuBar::wxMenuBar() 668{ 669 Init(); 670} 671 672wxMenuBar::wxMenuBar( long WXUNUSED(style) ) 673{ 674 Init(); 675} 676 677wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], long WXUNUSED(style)) 678{ 679 Init(); 680 681 m_titles.Alloc(count); 682 683 for ( size_t i = 0; i < count; i++ ) 684 { 685 m_menus.Append(menus[i]); 686 m_titles.Add(titles[i]); 687 688 menus[i]->Attach(this); 689 } 690} 691 692wxMenuBar::~wxMenuBar() 693{ 694 if (s_macCommonMenuBar == this) 695 s_macCommonMenuBar = NULL; 696 697 if (s_macInstalledMenuBar == this) 698 { 699 ::ClearMenuBar(); 700 s_macInstalledMenuBar = NULL; 701 } 702} 703 704void wxMenuBar::Refresh(bool WXUNUSED(eraseBackground), const wxRect *WXUNUSED(rect)) 705{ 706 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") ); 707 708 DrawMenuBar(); 709} 710 711void wxMenuBar::MacInstallMenuBar() 712{ 713 if ( s_macInstalledMenuBar == this ) 714 return ; 715 716 MenuBarHandle menubar = NULL ; 717 718#if TARGET_API_MAC_OSX 719 menubar = NewHandleClear( 6 /* sizeof( MenuBarHeader ) */ ) ; 720#else 721 menubar = NewHandleClear( 12 ) ; 722 (*menubar)[3] = 0x0a ; 723#endif 724 725 ::SetMenuBar( menubar ) ; 726 DisposeMenuBar( menubar ) ; 727 MenuHandle appleMenu = NULL ; 728 729 verify_noerr( CreateNewMenu( kwxMacAppleMenuId , 0 , &appleMenu ) ) ; 730 verify_noerr( SetMenuTitleWithCFString( appleMenu , CFSTR( "\x14" ) ) ); 731 732 // Add About/Preferences separator only on OS X 733 // KH/RN: Separator is always present on 10.3 but not on 10.2 734 // However, the change from 10.2 to 10.3 suggests it is preferred 735#if TARGET_API_MAC_OSX 736 InsertMenuItemTextWithCFString( appleMenu, 737 CFSTR(""), 0, kMenuItemAttrSeparator, 0); 738#endif 739 InsertMenuItemTextWithCFString( appleMenu, 740 CFSTR("About..."), 0, 0, 0); 741 MacInsertMenu( appleMenu , 0 ) ; 742 743 // clean-up the help menu before adding new items 744 static MenuHandle mh = NULL ; 745 746 if ( mh != NULL ) 747 { 748 MenuItemIndex firstUserHelpMenuItem ; 749 if ( UMAGetHelpMenu( &mh , &firstUserHelpMenuItem) == noErr ) 750 { 751 for ( int i = CountMenuItems( mh ) ; i >= firstUserHelpMenuItem ; --i ) 752 DeleteMenuItem( mh , i ) ; 753 } 754 else 755 { 756 mh = NULL ; 757 } 758 } 759 760#if TARGET_CARBON 761 if ( UMAGetSystemVersion() >= 0x1000 && wxApp::s_macPreferencesMenuItemId) 762 { 763 wxMenuItem *item = FindItem( wxApp::s_macPreferencesMenuItemId , NULL ) ; 764 if ( item == NULL || !(item->IsEnabled()) ) 765 DisableMenuCommand( NULL , kHICommandPreferences ) ; 766 else 767 EnableMenuCommand( NULL , kHICommandPreferences ) ; 768 } 769 770 // Unlike preferences which may or may not exist, the Quit item should be always 771 // enabled unless it is added by the application and then disabled, otherwise 772 // a program would be required to add an item with wxID_EXIT in order to get the 773 // Quit menu item to be enabled, which seems a bit burdensome. 774 if ( UMAGetSystemVersion() >= 0x1000 && wxApp::s_macExitMenuItemId) 775 { 776 wxMenuItem *item = FindItem( wxApp::s_macExitMenuItemId , NULL ) ; 777 if ( item != NULL && !(item->IsEnabled()) ) 778 DisableMenuCommand( NULL , kHICommandQuit ) ; 779 else 780 EnableMenuCommand( NULL , kHICommandQuit ) ; 781 } 782#endif 783 wxString strippedHelpMenuTitle = wxStripMenuCodes( wxApp::s_macHelpMenuTitleName ) ; 784 wxString strippedTranslatedHelpMenuTitle = wxStripMenuCodes( wxString( _("&Help") ) ) ; 785 786 wxString strippedWindowMenuTitle = wxStripMenuCodes( wxString(wxT("&Window")) /* wxApp::s_macWindowMenuTitleName */ ) ; 787 wxString strippedTranslatedWindowMenuTitle = wxStripMenuCodes( wxString( _("&Window") ) ) ; 788 789 wxMenuList::compatibility_iterator menuIter = m_menus.GetFirst(); 790 for (size_t i = 0; i < m_menus.GetCount(); i++, menuIter = menuIter->GetNext()) 791 { 792 wxMenuItemList::compatibility_iterator node; 793 wxMenuItem *item; 794 wxMenu* menu = menuIter->GetData() , *subMenu = NULL ; 795 wxString strippedMenuTitle = wxStripMenuCodes(m_titles[i]); 796 797 if ( strippedMenuTitle == wxT("?") || strippedMenuTitle == strippedHelpMenuTitle || strippedMenuTitle == strippedTranslatedHelpMenuTitle ) 798 { 799 for (node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext()) 800 { 801 item = (wxMenuItem *)node->GetData(); 802 subMenu = item->GetSubMenu() ; 803 if (subMenu) 804 { 805 UMAAppendMenuItem(mh, wxStripMenuCodes(item->GetText()) , wxFont::GetDefaultEncoding() ); 806 MenuItemIndex position = CountMenuItems(mh); 807 ::SetMenuItemHierarchicalMenu(mh, position, MAC_WXHMENU(subMenu->GetHMenu())); 808 } 809 else 810 { 811 if ( item->GetId() != wxApp::s_macAboutMenuItemId ) 812 { 813 if ( mh == NULL ) 814 { 815 MenuItemIndex firstUserHelpMenuItem ; 816 if ( UMAGetHelpMenu( &mh , &firstUserHelpMenuItem) != noErr ) 817 { 818 mh = NULL ; 819 break ; 820 } 821 } 822 } 823 824 if ( item->IsSeparator() ) 825 { 826 if ( mh ) 827 AppendMenuItemTextWithCFString( mh, 828 CFSTR(""), kMenuItemAttrSeparator, 0,NULL); 829 } 830 else 831 { 832 wxAcceleratorEntry* 833 entry = wxAcceleratorEntry::Create( item->GetText() ) ; 834 835 if ( item->GetId() == wxApp::s_macAboutMenuItemId ) 836 { 837 // this will be taken care of below 838 } 839 else 840 { 841 if ( mh ) 842 { 843 UMAAppendMenuItem(mh, wxStripMenuCodes(item->GetText()) , wxFont::GetDefaultEncoding(), entry); 844 MenuItemIndex position = CountMenuItems(mh); 845 SetMenuItemCommandID( mh , position, wxIdToMacCommand ( item->GetId() ) ); 846 SetMenuItemRefCon( mh , position, (URefCon) item ); 847 item->DoUpdateItemBitmap( mh, position ); 848 } 849 } 850 851 delete entry ; 852 } 853 } 854 } 855 } 856 else if ( (strippedMenuTitle == wxT("Window") || strippedMenuTitle == strippedWindowMenuTitle || strippedMenuTitle == strippedTranslatedWindowMenuTitle ) 857 && GetAutoWindowMenu() ) 858 { 859 if ( MacGetWindowMenuHMenu() == NULL ) 860 { 861 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ; 862 } 863 864 MenuRef wm = (MenuRef)MacGetWindowMenuHMenu(); 865 if ( wm == NULL ) 866 break; 867 868 // get the insertion point in the standard menu 869 MenuItemIndex winListStart; 870 GetIndMenuItemWithCommandID(wm, 871 kHICommandWindowListSeparator, 1, NULL, &winListStart); 872 873 // add a separator so that the standard items and the custom items 874 // aren't mixed together, but only if this is the first run 875 OSStatus err = GetIndMenuItemWithCommandID(wm, 876 'WXWM', 1, NULL, NULL); 877 878 if ( err == menuItemNotFoundErr ) 879 { 880 InsertMenuItemTextWithCFString( wm, 881 CFSTR(""), winListStart-1, kMenuItemAttrSeparator, 'WXWM'); 882 } 883 884 wxInsertMenuItemsInMenu(menu, wm, winListStart); 885 } 886 else 887 { 888 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , m_titles[i], GetFont().GetEncoding() ) ; 889 menu->MacBeforeDisplay(false) ; 890 891 ::InsertMenu(MAC_WXHMENU(_wxMenuAt(m_menus, i)->GetHMenu()), 0); 892 } 893 } 894 895 // take care of the about menu item wherever it is 896 { 897 wxMenu* aboutMenu ; 898 wxMenuItem *aboutMenuItem = FindItem(wxApp::s_macAboutMenuItemId , &aboutMenu) ; 899 if ( aboutMenuItem ) 900 { 901 wxAcceleratorEntry* 902 entry = wxAcceleratorEntry::Create( aboutMenuItem->GetText() ) ; 903 UMASetMenuItemText( GetMenuHandle( kwxMacAppleMenuId ) , 1 , wxStripMenuCodes ( aboutMenuItem->GetText() ) , wxFont::GetDefaultEncoding() ); 904 UMAEnableMenuItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 , true ); 905 SetMenuItemCommandID( GetMenuHandle( kwxMacAppleMenuId ) , 1 , kHICommandAbout ) ; 906 SetMenuItemRefCon(GetMenuHandle( kwxMacAppleMenuId ) , 1 , (URefCon)aboutMenuItem ) ; 907 UMASetMenuItemShortcut( GetMenuHandle( kwxMacAppleMenuId ) , 1 , entry ) ; 908 909 delete entry; 910 } 911 } 912 913 if ( GetAutoWindowMenu() ) 914 { 915 if ( MacGetWindowMenuHMenu() == NULL ) 916 CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ; 917 918 InsertMenu( (MenuHandle) MacGetWindowMenuHMenu() , 0 ) ; 919 } 920 921 ::DrawMenuBar() ; 922 s_macInstalledMenuBar = this; 923} 924 925void wxMenuBar::EnableTop(size_t pos, bool enable) 926{ 927 wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") ); 928 929 _wxMenuAt(m_menus, pos)->MacEnableMenu( enable ) ; 930 Refresh(); 931} 932 933bool wxMenuBar::Enable(bool enable) 934{ 935 wxCHECK_MSG( IsAttached(), false, wxT("doesn't work with unattached menubars") ); 936 937 size_t i; 938 for (i = 0; i < GetMenuCount(); i++) 939 EnableTop(i, enable); 940 941 return true; 942} 943 944void wxMenuBar::SetLabelTop(size_t pos, const wxString& label) 945{ 946 wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") ); 947 948 m_titles[pos] = label; 949 950 if ( !IsAttached() ) 951 return; 952 953 _wxMenuAt(m_menus, pos)->SetTitle( label ) ; 954 955 if (wxMenuBar::s_macInstalledMenuBar == this) // are we currently installed ? 956 { 957 ::SetMenuBar( GetMenuBar() ) ; 958 ::InvalMenuBar() ; 959 } 960} 961 962wxString wxMenuBar::GetLabelTop(size_t pos) const 963{ 964 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString, 965 wxT("invalid menu index in wxMenuBar::GetLabelTop") ); 966 967 return wxStripMenuCodes(m_titles[pos]); 968} 969 970// Gets the original label at the top-level of the menubar 971wxString wxMenuBar::GetMenuLabel(size_t pos) const 972{ 973 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString, 974 wxT("invalid menu index in wxMenuBar::GetMenuLabel") ); 975 976 return m_titles[pos]; 977} 978 979int wxMenuBar::FindMenu(const wxString& title) 980{ 981 wxString menuTitle = wxStripMenuCodes(title); 982 983 size_t count = GetMenuCount(); 984 for ( size_t i = 0; i < count; i++ ) 985 { 986 wxString title = wxStripMenuCodes(m_titles[i]); 987 if ( menuTitle == title ) 988 return i; 989 } 990 991 return wxNOT_FOUND; 992} 993 994// --------------------------------------------------------------------------- 995// wxMenuBar construction 996// --------------------------------------------------------------------------- 997 998wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title) 999{ 1000 wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title); 1001 if ( !menuOld ) 1002 return NULL; 1003 1004 m_titles[pos] = title; 1005 1006 if ( IsAttached() ) 1007 { 1008 if (s_macInstalledMenuBar == this) 1009 { 1010 menuOld->MacAfterDisplay( false ) ; 1011 ::DeleteMenu( menuOld->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ; 1012 1013 menu->MacBeforeDisplay( false ) ; 1014 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , GetFont().GetEncoding() ) ; 1015 if ( pos == m_menus.GetCount() - 1) 1016 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ; 1017 else 1018 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos + 1)->MacGetMenuId() ) ; 1019 } 1020 1021 Refresh(); 1022 } 1023 1024 if (m_invokingWindow) 1025 wxMenubarSetInvokingWindow( menu, m_invokingWindow ); 1026 1027 return menuOld; 1028} 1029 1030bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title) 1031{ 1032 if ( !wxMenuBarBase::Insert(pos, menu, title) ) 1033 return false; 1034 1035 m_titles.Insert(title, pos); 1036 1037 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , GetFont().GetEncoding() ) ; 1038 1039 if ( IsAttached() && s_macInstalledMenuBar == this ) 1040 { 1041 if (s_macInstalledMenuBar == this) 1042 { 1043 menu->MacBeforeDisplay( false ) ; 1044 1045 if ( pos == (size_t) -1 || pos + 1 == m_menus.GetCount() ) 1046 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ; 1047 else 1048 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos+1)->MacGetMenuId() ) ; 1049 } 1050 1051 Refresh(); 1052 } 1053 1054 if (m_invokingWindow) 1055 wxMenubarSetInvokingWindow( menu, m_invokingWindow ); 1056 1057 return true; 1058} 1059 1060wxMenu *wxMenuBar::Remove(size_t pos) 1061{ 1062 wxMenu *menu = wxMenuBarBase::Remove(pos); 1063 if ( !menu ) 1064 return NULL; 1065 1066 if ( IsAttached() ) 1067 { 1068 if (s_macInstalledMenuBar == this) 1069 ::DeleteMenu( menu->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ; 1070 1071 Refresh(); 1072 } 1073 1074 m_titles.RemoveAt(pos); 1075 1076 return menu; 1077} 1078 1079bool wxMenuBar::Append(wxMenu *menu, const wxString& title) 1080{ 1081 WXHMENU submenu = menu ? menu->GetHMenu() : 0; 1082 wxCHECK_MSG( submenu, false, wxT("can't append invalid menu to menubar") ); 1083 1084 if ( !wxMenuBarBase::Append(menu, title) ) 1085 return false; 1086 1087 m_titles.Add(title); 1088 1089 UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , GetFont().GetEncoding() ) ; 1090 1091 if ( IsAttached() ) 1092 { 1093 if (s_macInstalledMenuBar == this) 1094 { 1095 menu->MacBeforeDisplay( false ) ; 1096 ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ; 1097 } 1098 1099 Refresh(); 1100 } 1101 1102 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables 1103 // adding menu later on. 1104 if (m_invokingWindow) 1105 wxMenubarSetInvokingWindow( menu, m_invokingWindow ); 1106 1107 return true; 1108} 1109 1110static void wxMenubarUnsetInvokingWindow( wxMenu *menu ) 1111{ 1112 menu->SetInvokingWindow( (wxWindow*) NULL ); 1113 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst(); 1114 1115 while (node) 1116 { 1117 wxMenuItem *menuitem = node->GetData(); 1118 if (menuitem->IsSubMenu()) 1119 wxMenubarUnsetInvokingWindow( menuitem->GetSubMenu() ); 1120 1121 node = node->GetNext(); 1122 } 1123} 1124 1125static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win ) 1126{ 1127 menu->SetInvokingWindow( win ); 1128 wxMenuItem *menuitem; 1129 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst(); 1130 1131 while (node) 1132 { 1133 menuitem = node->GetData(); 1134 if (menuitem->IsSubMenu()) 1135 wxMenubarSetInvokingWindow( menuitem->GetSubMenu() , win ); 1136 1137 node = node->GetNext(); 1138 } 1139} 1140 1141void wxMenuBar::UnsetInvokingWindow() 1142{ 1143 m_invokingWindow = (wxWindow*) NULL; 1144 wxMenu *menu; 1145 wxMenuList::compatibility_iterator node = m_menus.GetFirst(); 1146 1147 while (node) 1148 { 1149 menu = node->GetData(); 1150 wxMenubarUnsetInvokingWindow( menu ); 1151 1152 node = node->GetNext(); 1153 } 1154} 1155 1156void wxMenuBar::SetInvokingWindow(wxFrame *frame) 1157{ 1158 m_invokingWindow = frame; 1159 wxMenu *menu; 1160 wxMenuList::compatibility_iterator node = m_menus.GetFirst(); 1161 1162 while (node) 1163 { 1164 menu = node->GetData(); 1165 wxMenubarSetInvokingWindow( menu, frame ); 1166 1167 node = node->GetNext(); 1168 } 1169} 1170 1171void wxMenuBar::Detach() 1172{ 1173 wxMenuBarBase::Detach() ; 1174} 1175 1176void wxMenuBar::Attach(wxFrame *frame) 1177{ 1178 wxMenuBarBase::Attach( frame ) ; 1179} 1180 1181// --------------------------------------------------------------------------- 1182// wxMenuBar searching for menu items 1183// --------------------------------------------------------------------------- 1184 1185// Find the itemString in menuString, and return the item id or wxNOT_FOUND 1186int wxMenuBar::FindMenuItem(const wxString& menuString, 1187 const wxString& itemString) const 1188{ 1189 wxString menuLabel = wxStripMenuCodes(menuString); 1190 size_t count = GetMenuCount(); 1191 for ( size_t i = 0; i < count; i++ ) 1192 { 1193 wxString title = wxStripMenuCodes(m_titles[i]); 1194 if ( menuLabel == title ) 1195 return _wxMenuAt(m_menus, i)->FindItem(itemString); 1196 } 1197 1198 return wxNOT_FOUND; 1199} 1200 1201wxMenuItem *wxMenuBar::FindItem(int id, wxMenu **itemMenu) const 1202{ 1203 if ( itemMenu ) 1204 *itemMenu = NULL; 1205 1206 wxMenuItem *item = NULL; 1207 size_t count = GetMenuCount(); 1208 for ( size_t i = 0; !item && (i < count); i++ ) 1209 item = _wxMenuAt(m_menus, i)->FindItem(id, itemMenu); 1210 1211 return item; 1212} 1213