update dev300-m58
[ooovba.git] / vcl / aqua / source / window / salmenu.cxx
blob4c65febfcd5ec9102ee94fb77d0ad2e4592b8e23
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: salmenu.cxx,v $
10 * $Revision: 1.14 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 #include "saldata.hxx"
32 #include "salinst.h"
33 #include "salmenu.h"
34 #include "salnsmenu.h"
35 #include "salframe.h"
36 #include "salbmp.h"
37 #include "vcl/svids.hrc"
38 #include "vcl/cmdevt.hxx"
39 #include "vcl/floatwin.hxx"
40 #include "vcl/window.h"
41 #include "vcl/window.hxx"
42 #include "vcl/svapp.hxx"
44 #include "rtl/ustrbuf.hxx"
45 #include "aqua11ywrapper.h"
47 const AquaSalMenu* AquaSalMenu::pCurrentMenuBar = NULL;
49 @interface MainMenuSelector : NSObject
52 -(void)showDialog: (int)nDialog;
53 -(void)showPreferences: (id)sender;
54 -(void)showAbout: (id)sender;
55 @end
57 @implementation MainMenuSelector
58 -(void)showDialog: (int)nDialog
60 if( AquaSalMenu::pCurrentMenuBar )
62 const AquaSalFrame* pFrame = AquaSalMenu::pCurrentMenuBar->mpFrame;
63 if( pFrame && AquaSalFrame::isAlive( pFrame ) )
65 pFrame->CallCallback( SALEVENT_SHOWDIALOG, reinterpret_cast<void*>(nDialog) );
68 else
70 String aDialog;
71 if( nDialog == SHOWDIALOG_ID_ABOUT )
72 aDialog = String( RTL_CONSTASCII_USTRINGPARAM( "ABOUT" ) );
73 else if( nDialog == SHOWDIALOG_ID_PREFERENCES )
74 aDialog = String( RTL_CONSTASCII_USTRINGPARAM( "PREFERENCES" ) );
75 const ApplicationEvent* pAppEvent = new ApplicationEvent( String(),
76 ApplicationAddress(),
77 ByteString( "SHOWDIALOG" ),
78 aDialog );
79 AquaSalInstance::aAppEventList.push_back( pAppEvent );
83 -(void)showPreferences: (id) sender
85 [self showDialog: SHOWDIALOG_ID_PREFERENCES];
87 -(void)showAbout: (id) sender
89 [self showDialog: SHOWDIALOG_ID_ABOUT];
91 @end
94 // FIXME: currently this is leaked
95 static MainMenuSelector* pMainMenuSelector = nil;
97 static void initAppMenu()
99 static bool bOnce = true;
100 if( bOnce )
102 bOnce = false;
104 ResMgr* pMgr = ImplGetResMgr();
105 if( pMgr )
107 // get the main menu
108 NSMenu* pMainMenu = [NSApp mainMenu];
109 if( pMainMenu != nil )
111 // create the action selector
112 pMainMenuSelector = [[MainMenuSelector alloc] init];
114 // get the proper submenu
115 NSMenu* pAppMenu = [[pMainMenu itemAtIndex: 0] submenu];
116 if( pAppMenu )
118 // insert about entry
119 String aAbout( ResId( SV_STDTEXT_ABOUT, *pMgr ) );
120 NSString* pString = CreateNSString( aAbout );
121 NSMenuItem* pNewItem = [pAppMenu insertItemWithTitle: pString
122 action: @selector(showAbout:)
123 keyEquivalent: @""
124 atIndex: 0];
125 if (pString)
126 [pString release];
127 if( pNewItem )
129 [pNewItem setTarget: pMainMenuSelector];
130 [pAppMenu insertItem: [NSMenuItem separatorItem] atIndex: 1];
133 // insert preferences entry
134 String aPref( ResId( SV_STDTEXT_PREFERENCES, *pMgr ) );
135 pString = CreateNSString( aPref );
136 pNewItem = [pAppMenu insertItemWithTitle: pString
137 action: @selector(showPreferences:)
138 keyEquivalent: @","
139 atIndex: 2];
140 if (pString)
141 [pString release];
142 if( pNewItem )
144 [pNewItem setKeyEquivalentModifierMask: NSCommandKeyMask];
145 [pNewItem setTarget: pMainMenuSelector];
146 [pAppMenu insertItem: [NSMenuItem separatorItem] atIndex: 3];
149 // WARNING: ultra ugly code ahead
151 // rename standard entries
152 // rename "Services"
153 pNewItem = [pAppMenu itemAtIndex: 4];
154 if( pNewItem )
156 pString = CreateNSString( String( ResId( SV_MENU_MAC_SERVICES, *pMgr ) ) );
157 [pNewItem setTitle: pString];
158 if( pString )
159 [pString release];
162 // rename "Hide NewApplication"
163 pNewItem = [pAppMenu itemAtIndex: 6];
164 if( pNewItem )
166 pString = CreateNSString( String( ResId( SV_MENU_MAC_HIDEAPP, *pMgr ) ) );
167 [pNewItem setTitle: pString];
168 if( pString )
169 [pString release];
172 // rename "Hide Others"
173 pNewItem = [pAppMenu itemAtIndex: 7];
174 if( pNewItem )
176 pString = CreateNSString( String( ResId( SV_MENU_MAC_HIDEALL, *pMgr ) ) );
177 [pNewItem setTitle: pString];
178 if( pString )
179 [pString release];
182 // rename "Show all"
183 pNewItem = [pAppMenu itemAtIndex: 8];
184 if( pNewItem )
186 pString = CreateNSString( String( ResId( SV_MENU_MAC_SHOWALL, *pMgr ) ) );
187 [pNewItem setTitle: pString];
188 if( pString )
189 [pString release];
192 // rename "Quit NewApplication"
193 pNewItem = [pAppMenu itemAtIndex: 10];
194 if( pNewItem )
196 pString = CreateNSString( String( ResId( SV_MENU_MAC_QUITAPP, *pMgr ) ) );
197 [pNewItem setTitle: pString];
198 if( pString )
199 [pString release];
207 // =======================================================================
209 SalMenu* AquaSalInstance::CreateMenu( BOOL bMenuBar )
211 initAppMenu();
213 AquaSalMenu *pAquaSalMenu = new AquaSalMenu( bMenuBar );
215 return pAquaSalMenu;
218 void AquaSalInstance::DestroyMenu( SalMenu* pSalMenu )
220 delete pSalMenu;
223 SalMenuItem* AquaSalInstance::CreateMenuItem( const SalItemParams* pItemData )
225 if( !pItemData )
226 return NULL;
228 AquaSalMenuItem *pSalMenuItem = new AquaSalMenuItem( pItemData );
230 return pSalMenuItem;
233 void AquaSalInstance::DestroyMenuItem( SalMenuItem* pSalMenuItem )
235 delete pSalMenuItem;
239 // =======================================================================
243 * AquaSalMenu
246 AquaSalMenu::AquaSalMenu( bool bMenuBar ) :
247 mbMenuBar( bMenuBar ),
248 mpMenu( nil ),
249 mpVCLMenu( NULL ),
250 mpFrame( NULL ),
251 mpParentSalMenu( NULL )
253 if( ! mbMenuBar )
255 mpMenu = [[SalNSMenu alloc] initWithMenu: this];
256 [mpMenu setDelegate: mpMenu];
258 else
260 mpMenu = [NSApp mainMenu];
262 [mpMenu setAutoenablesItems: NO];
265 AquaSalMenu::~AquaSalMenu()
267 // actually someone should have done AquaSalFrame::SetMenu( NULL )
268 // on our frame, alas it is not so
269 if( mpFrame && AquaSalFrame::isAlive( mpFrame ) && mpFrame->mpMenu == this )
270 const_cast<AquaSalFrame*>(mpFrame)->mpMenu = NULL;
272 // this should normally be empty already, but be careful...
273 for( size_t i = 0; i < maButtons.size(); i++ )
274 releaseButtonEntry( maButtons[i] );
275 maButtons.clear();
277 // is this leaking in some cases ? the release often leads to a duplicate release
278 // it seems the parent item gets ownership of the menu
279 if( mpMenu )
281 if( mbMenuBar )
283 if( pCurrentMenuBar == this )
285 // if the current menubar gets destroyed, set the default menubar
286 setDefaultMenu();
289 else
290 // the system may still hold a reference on mpMenu
292 // so set the pointer to this AquaSalMenu to NULL
293 // to protect from calling a dead object
295 // in ! mbMenuBar case our mpMenu is actually a SalNSMenu*
296 // so we can safely cast here
297 [static_cast<SalNSMenu*>(mpMenu) setSalMenu: NULL];
298 /* #i89860# FIXME:
299 using [autorelease] here (and in AquaSalMenuItem::~AquaSalMenuItem)
300 instead of [release] fixes an occasional crash. That should
301 indicate that we release menus / menu items in the wrong order
302 somewhere, but I could not find that case.
304 [mpMenu autorelease];
309 sal_Int32 removeUnusedItemsRunner(NSMenu * pMenu)
311 NSArray * elements = [pMenu itemArray];
312 NSEnumerator * it = [elements objectEnumerator];
313 id elem;
314 NSMenuItem * lastDisplayedMenuItem = nil;
315 sal_Int32 drawnItems = 0;
316 bool firstEnabledItemIsNoSeparator = false;
317 while((elem=[it nextObject]) != nil) {
318 NSMenuItem * item = static_cast<NSMenuItem *>(elem);
319 if( (![item isEnabled] && ![item isSeparatorItem]) || ([item isSeparatorItem] && (lastDisplayedMenuItem != nil && [lastDisplayedMenuItem isSeparatorItem])) ) {
320 [[item menu]removeItem:item];
321 } else {
322 if( ! firstEnabledItemIsNoSeparator && [item isSeparatorItem] ) {
323 [[item menu]removeItem:item];
324 } else {
325 firstEnabledItemIsNoSeparator = true;
326 lastDisplayedMenuItem = item;
327 drawnItems++;
328 if( [item hasSubmenu] ) {
329 removeUnusedItemsRunner( [item submenu] );
334 if( lastDisplayedMenuItem != nil && [lastDisplayedMenuItem isSeparatorItem]) {
335 [[lastDisplayedMenuItem menu]removeItem:lastDisplayedMenuItem];
337 return drawnItems;
340 bool AquaSalMenu::ShowNativePopupMenu(FloatingWindow * pWin, const Rectangle& rRect, ULONG nFlags)
342 // do not use native popup menu when AQUA_NATIVE_MENUS is set to FALSE
343 if( ! VisibleMenuBar() ) {
344 return false;
347 // set offsets for positioning
348 const float offset = 9.0;
350 // get the pointers
351 AquaSalFrame * pParentAquaSalFrame = (AquaSalFrame *) pWin->ImplGetWindowImpl()->mpRealParent->ImplGetFrame();
352 NSWindow * pParentNSWindow = pParentAquaSalFrame->mpWindow;
353 NSView * pParentNSView = [pParentNSWindow contentView];
354 NSView * pPopupNSView = ((AquaSalFrame *) pWin->ImplGetWindow()->ImplGetFrame())->mpView;
355 NSRect popupFrame = [pPopupNSView frame];
357 // since we manipulate the menu below (removing entries)
358 // let's rather make a copy here and work with that
359 NSMenu* pCopyMenu = [mpMenu copy];
361 // filter disabled elements
362 removeUnusedItemsRunner( pCopyMenu );
364 // create frame rect
365 NSRect displayPopupFrame = NSMakeRect( rRect.nLeft+(offset-1), rRect.nTop+(offset+1), popupFrame.size.width, 0 );
366 pParentAquaSalFrame->VCLToCocoa(displayPopupFrame, false);
368 // do the same strange semantics as vcl popup windows to arrive at a frame geometry
369 // in mirrored UI case; best done by actually executing the same code
370 USHORT nArrangeIndex;
371 pWin->SetPosPixel( pWin->ImplCalcPos( pWin, rRect, nFlags, nArrangeIndex ) );
372 displayPopupFrame.origin.x = pWin->ImplGetFrame()->maGeometry.nX - pParentAquaSalFrame->maGeometry.nX + offset;
373 displayPopupFrame.origin.y = pWin->ImplGetFrame()->maGeometry.nY - pParentAquaSalFrame->maGeometry.nY + offset;
374 pParentAquaSalFrame->VCLToCocoa(displayPopupFrame, false);
376 // open popup menu
377 NSPopUpButtonCell * pPopUpButtonCell = [[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO];
378 [pPopUpButtonCell setMenu: pCopyMenu];
379 [pPopUpButtonCell selectItem:nil];
380 [AquaA11yWrapper setPopupMenuOpen: YES];
381 [pPopUpButtonCell performClickWithFrame:displayPopupFrame inView:pParentNSView];
382 [pPopUpButtonCell release];
383 [AquaA11yWrapper setPopupMenuOpen: NO];
385 // clean up the copy
386 [pCopyMenu release];
387 return true;
390 int AquaSalMenu::getItemIndexByPos( USHORT nPos ) const
392 int nIndex = 0;
393 if( nPos == MENU_APPEND )
394 nIndex = [mpMenu numberOfItems];
395 else
396 nIndex = sal::static_int_cast<int>( mbMenuBar ? nPos+1 : nPos );
397 return nIndex;
400 const AquaSalFrame* AquaSalMenu::getFrame() const
402 const AquaSalMenu* pMenu = this;
403 while( pMenu && ! pMenu->mpFrame )
404 pMenu = pMenu->mpParentSalMenu;
405 return pMenu ? pMenu->mpFrame : NULL;
408 void AquaSalMenu::unsetMainMenu()
410 pCurrentMenuBar = NULL;
412 // remove items from main menu
413 NSMenu* pMenu = [NSApp mainMenu];
414 for( int nItems = [pMenu numberOfItems]; nItems > 1; nItems-- )
415 [pMenu removeItemAtIndex: 1];
418 void AquaSalMenu::setMainMenu()
420 DBG_ASSERT( mbMenuBar, "setMainMenu on non menubar" );
421 if( mbMenuBar )
423 if( pCurrentMenuBar != this )
425 unsetMainMenu();
426 // insert our items
427 for( unsigned int i = 0; i < maItems.size(); i++ )
429 NSMenuItem* pItem = maItems[i]->mpMenuItem;
430 [mpMenu insertItem: pItem atIndex: i+1];
432 pCurrentMenuBar = this;
434 // change status item
435 statusLayout();
437 enableMainMenu( true );
441 void AquaSalMenu::setDefaultMenu()
443 NSMenu* pMenu = [NSApp mainMenu];
445 unsetMainMenu();
447 // insert default items
448 std::vector< NSMenuItem* >& rFallbackMenu( GetSalData()->maFallbackMenu );
449 for( unsigned int i = 0, nAddItems = rFallbackMenu.size(); i < nAddItems; i++ )
451 NSMenuItem* pItem = rFallbackMenu[i];
452 if( [pItem menu] == nil )
453 [pMenu insertItem: pItem atIndex: i+1];
457 void AquaSalMenu::enableMainMenu( bool bEnable )
459 NSMenu* pMainMenu = [NSApp mainMenu];
460 if( pMainMenu )
462 // enable/disable items from main menu
463 int nItems = [pMainMenu numberOfItems];
464 for( int n = 1; n < nItems; n++ )
466 NSMenuItem* pItem = [pMainMenu itemAtIndex: n];
467 [pItem setEnabled: bEnable ? YES : NO];
472 void AquaSalMenu::addFallbackMenuItem( NSMenuItem* pNewItem )
474 initAppMenu();
476 std::vector< NSMenuItem* >& rFallbackMenu( GetSalData()->maFallbackMenu );
478 // prevent duplicate insertion
479 int nItems = rFallbackMenu.size();
480 for( int i = 0; i < nItems; i++ )
482 if( rFallbackMenu[i] == pNewItem )
483 return;
486 // push the item to the back and retain it
487 [pNewItem retain];
488 rFallbackMenu.push_back( pNewItem );
490 if( pCurrentMenuBar == NULL )
491 setDefaultMenu();
494 void AquaSalMenu::removeFallbackMenuItem( NSMenuItem* pOldItem )
496 std::vector< NSMenuItem* >& rFallbackMenu( GetSalData()->maFallbackMenu );
498 // find item
499 unsigned int nItems = rFallbackMenu.size();
500 for( unsigned int i = 0; i < nItems; i++ )
502 if( rFallbackMenu[i] == pOldItem )
504 // remove item and release
505 rFallbackMenu.erase( rFallbackMenu.begin() + i );
506 [pOldItem release];
508 if( pCurrentMenuBar == NULL )
509 setDefaultMenu();
511 return;
516 BOOL AquaSalMenu::VisibleMenuBar()
518 // Enable/disable experimental native menus code?
520 // To disable native menus, set the environment variable AQUA_NATIVE_MENUS to FALSE
522 static const char *pExperimental = getenv ("AQUA_NATIVE_MENUS");
524 if ( ImplGetSVData()->mbIsTestTool || (pExperimental && !strcasecmp(pExperimental, "FALSE")) )
525 return FALSE;
527 // End of experimental code enable/disable part
529 return TRUE;
532 void AquaSalMenu::SetFrame( const SalFrame *pFrame )
534 mpFrame = static_cast<const AquaSalFrame*>(pFrame);
537 void AquaSalMenu::InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos )
539 AquaSalMenuItem *pAquaSalMenuItem = static_cast<AquaSalMenuItem*>(pSalMenuItem);
541 pAquaSalMenuItem->mpParentMenu = this;
542 DBG_ASSERT( pAquaSalMenuItem->mpVCLMenu == NULL ||
543 pAquaSalMenuItem->mpVCLMenu == mpVCLMenu ||
544 mpVCLMenu == NULL,
545 "resetting menu ?" );
546 if( pAquaSalMenuItem->mpVCLMenu )
547 mpVCLMenu = pAquaSalMenuItem->mpVCLMenu;
549 if( nPos == MENU_APPEND || nPos == maItems.size() )
550 maItems.push_back( pAquaSalMenuItem );
551 else if( nPos < maItems.size() )
552 maItems.insert( maItems.begin() + nPos, pAquaSalMenuItem );
553 else
555 DBG_ERROR( "invalid item index in insert" );
556 return;
559 if( ! mbMenuBar || pCurrentMenuBar == this )
560 [mpMenu insertItem: pAquaSalMenuItem->mpMenuItem atIndex: getItemIndexByPos(nPos)];
563 void AquaSalMenu::RemoveItem( unsigned nPos )
565 AquaSalMenuItem* pRemoveItem = NULL;
566 if( nPos == MENU_APPEND || nPos == (maItems.size()-1) )
568 pRemoveItem = maItems.back();
569 maItems.pop_back();
571 else if( nPos < maItems.size() )
573 pRemoveItem = maItems[ nPos ];
574 maItems.erase( maItems.begin()+nPos );
576 else
578 DBG_ERROR( "invalid item index in remove" );
579 return;
582 pRemoveItem->mpParentMenu = NULL;
584 if( ! mbMenuBar || pCurrentMenuBar == this )
585 [mpMenu removeItemAtIndex: getItemIndexByPos(nPos)];
588 void AquaSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsigned nPos )
590 AquaSalMenuItem *pAquaSalMenuItem = static_cast<AquaSalMenuItem*>(pSalMenuItem);
591 AquaSalMenu *subAquaSalMenu = static_cast<AquaSalMenu*>(pSubMenu);
593 if (subAquaSalMenu)
595 pAquaSalMenuItem->mpSubMenu = subAquaSalMenu;
596 if( subAquaSalMenu->mpParentSalMenu == NULL )
598 subAquaSalMenu->mpParentSalMenu = this;
599 [pAquaSalMenuItem->mpMenuItem setSubmenu: subAquaSalMenu->mpMenu];
601 // set title of submenu
602 [subAquaSalMenu->mpMenu setTitle: [pAquaSalMenuItem->mpMenuItem title]];
604 else if( subAquaSalMenu->mpParentSalMenu != this )
606 // cocoa doesn't allow menus to be submenus of multiple
607 // menu items, so place a copy in the menu item instead ?
608 // let's hope that NSMenu copy does the right thing
609 NSMenu* pCopy = [subAquaSalMenu->mpMenu copy];
610 [pAquaSalMenuItem->mpMenuItem setSubmenu: pCopy];
612 // set title of submenu
613 [pCopy setTitle: [pAquaSalMenuItem->mpMenuItem title]];
616 else
618 if( pAquaSalMenuItem->mpSubMenu )
620 if( pAquaSalMenuItem->mpSubMenu->mpParentSalMenu == this )
621 pAquaSalMenuItem->mpSubMenu->mpParentSalMenu = NULL;
623 pAquaSalMenuItem->mpSubMenu = NULL;
624 [pAquaSalMenuItem->mpMenuItem setSubmenu: nil];
628 void AquaSalMenu::CheckItem( unsigned nPos, BOOL bCheck )
630 if( nPos < maItems.size() )
632 NSMenuItem* pItem = maItems[nPos]->mpMenuItem;
633 [pItem setState: bCheck ? NSOnState : NSOffState];
637 void AquaSalMenu::EnableItem( unsigned nPos, BOOL bEnable )
639 if( nPos < maItems.size() )
641 NSMenuItem* pItem = maItems[nPos]->mpMenuItem;
642 [pItem setEnabled: bEnable ? YES : NO];
646 void AquaSalMenu::SetItemImage( unsigned nPos, SalMenuItem* pSMI, const Image& rImage )
648 AquaSalMenuItem* pSalMenuItem = static_cast<AquaSalMenuItem*>( pSMI );
649 if( ! pSalMenuItem || ! pSalMenuItem->mpMenuItem )
650 return;
652 NSImage* pImage = CreateNSImage( rImage );
654 [pSalMenuItem->mpMenuItem setImage: pImage];
655 if( pImage )
656 [pImage release];
659 void AquaSalMenu::SetItemText( unsigned i_nPos, SalMenuItem* i_pSalMenuItem, const XubString& i_rText )
661 if (!i_pSalMenuItem)
662 return;
664 AquaSalMenuItem *pAquaSalMenuItem = (AquaSalMenuItem *) i_pSalMenuItem;
666 String aText( i_rText );
668 // Delete mnemonics
669 aText.EraseAllChars( '~' );
671 /* #i90015# until there is a correct solution
672 strip out any appended (.*) in menubar entries
674 if( mbMenuBar )
676 xub_StrLen nPos = aText.SearchBackward( sal_Unicode( '(' ) );
677 if( nPos != STRING_NOTFOUND )
679 xub_StrLen nPos2 = aText.Search( sal_Unicode( ')' ) );
680 if( nPos2 != STRING_NOTFOUND )
681 aText.Erase( nPos, nPos2-nPos+1 );
685 NSString* pString = CreateNSString( aText );
686 if (pString)
688 [pAquaSalMenuItem->mpMenuItem setTitle: pString];
689 // if the menu item has a submenu, change its title as well
690 if (pAquaSalMenuItem->mpSubMenu)
691 [pAquaSalMenuItem->mpSubMenu->mpMenu setTitle: pString];
692 [pString release];
696 void AquaSalMenu::SetAccelerator( unsigned nPos, SalMenuItem* pSalMenuItem, const KeyCode& rKeyCode, const XubString& rKeyName )
698 USHORT nModifier;
699 sal_Unicode nCommandKey = 0;
701 USHORT nKeyCode=rKeyCode.GetCode();
702 if( nKeyCode )
704 if ((nKeyCode>=KEY_A) && (nKeyCode<=KEY_Z)) // letter A..Z
705 nCommandKey = nKeyCode-KEY_A + 'a';
706 else if ((nKeyCode>=KEY_0) && (nKeyCode<=KEY_9)) // numbers 0..9
707 nCommandKey = nKeyCode-KEY_0 + '0';
708 else if ((nKeyCode>=KEY_F1) && (nKeyCode<=KEY_F26)) // function keys F1..F26
709 nCommandKey = nKeyCode-KEY_F1 + NSF1FunctionKey;
710 else if( nKeyCode == KEY_REPEAT )
711 nCommandKey = NSRedoFunctionKey;
712 else if( nKeyCode == KEY_SPACE )
713 nCommandKey = ' ';
714 else
716 switch (nKeyCode)
718 case KEY_ADD:
719 nCommandKey='+';
720 break;
721 case KEY_SUBTRACT:
722 nCommandKey='-';
723 break;
724 case KEY_MULTIPLY:
725 nCommandKey='*';
726 break;
727 case KEY_DIVIDE:
728 nCommandKey='/';
729 break;
730 case KEY_POINT:
731 nCommandKey='.';
732 break;
733 case KEY_LESS:
734 nCommandKey='<';
735 break;
736 case KEY_GREATER:
737 nCommandKey='>';
738 break;
739 case KEY_EQUAL:
740 nCommandKey='=';
741 break;
745 else // not even a code ? nonsense -> ignore
746 return;
748 DBG_ASSERT( nCommandKey, "unmapped accelerator key" );
750 nModifier=rKeyCode.GetAllModifier();
752 // should always use the command key
753 int nItemModifier = 0;
755 if (nModifier & KEY_SHIFT)
757 nItemModifier |= NSShiftKeyMask; // actually useful only for function keys
758 if( nKeyCode >= KEY_A && nKeyCode <= KEY_Z )
759 nCommandKey = nKeyCode - KEY_A + 'A';
762 if (nModifier & KEY_MOD1)
763 nItemModifier |= NSCommandKeyMask;
765 if(nModifier & KEY_MOD2)
766 nItemModifier |= NSAlternateKeyMask;
768 if(nModifier & KEY_MOD3)
769 nItemModifier |= NSControlKeyMask;
771 AquaSalMenuItem *pAquaSalMenuItem = (AquaSalMenuItem *) pSalMenuItem;
772 NSString* pString = CreateNSString( rtl::OUString( &nCommandKey, 1 ) );
773 [pAquaSalMenuItem->mpMenuItem setKeyEquivalent: pString];
774 [pAquaSalMenuItem->mpMenuItem setKeyEquivalentModifierMask: nItemModifier];
775 if (pString)
776 [pString release];
779 void AquaSalMenu::GetSystemMenuData( SystemMenuData* pData )
783 AquaSalMenu::MenuBarButtonEntry* AquaSalMenu::findButtonItem( USHORT i_nItemId )
785 for( size_t i = 0; i < maButtons.size(); ++i )
787 if( maButtons[i].maButton.mnId == i_nItemId )
788 return &maButtons[i];
790 return NULL;
793 void AquaSalMenu::statusLayout()
795 if( GetSalData()->mpStatusItem )
797 NSView* pView = [GetSalData()->mpStatusItem view];
798 if( [pView isMemberOfClass: [OOStatusItemView class]] ) // well of course it is
799 [(OOStatusItemView*)pView layout];
800 else
801 DBG_ERROR( "someone stole our status view" );
805 void AquaSalMenu::releaseButtonEntry( MenuBarButtonEntry& i_rEntry )
807 if( i_rEntry.mpNSImage )
809 [i_rEntry.mpNSImage release];
810 i_rEntry.mpNSImage = nil;
812 if( i_rEntry.mpToolTipString )
814 [i_rEntry.mpToolTipString release];
815 i_rEntry.mpToolTipString = nil;
819 bool AquaSalMenu::AddMenuBarButton( const SalMenuButtonItem& i_rNewItem )
821 if( ! mbMenuBar || ! VisibleMenuBar() )
822 return false;
824 MenuBarButtonEntry* pEntry = findButtonItem( i_rNewItem.mnId );
825 if( pEntry )
827 releaseButtonEntry( *pEntry );
828 pEntry->maButton = i_rNewItem;
829 pEntry->mpNSImage = CreateNSImage( i_rNewItem.maImage );
830 if( i_rNewItem.maToolTipText.getLength() )
831 pEntry->mpToolTipString = CreateNSString( i_rNewItem.maToolTipText );
833 else
835 maButtons.push_back( MenuBarButtonEntry( i_rNewItem ) );
836 maButtons.back().mpNSImage = CreateNSImage( i_rNewItem.maImage );
837 maButtons.back().mpToolTipString = CreateNSString( i_rNewItem.maToolTipText );
840 // lazy create status item
841 SalData::getStatusItem();
843 if( pCurrentMenuBar == this )
844 statusLayout();
846 return true;
849 void AquaSalMenu::RemoveMenuBarButton( USHORT i_nId )
851 MenuBarButtonEntry* pEntry = findButtonItem( i_nId );
852 if( pEntry )
854 releaseButtonEntry( *pEntry );
855 // note: vector guarantees that its contents are in a plain array
856 maButtons.erase( maButtons.begin() + (pEntry - &maButtons[0]) );
859 if( pCurrentMenuBar == this )
860 statusLayout();
863 Rectangle AquaSalMenu::GetMenuBarButtonRectPixel( USHORT i_nItemId, SalFrame* i_pReferenceFrame )
865 if( GetSalData()->mnSystemVersion < VER_LEOPARD )
866 return Rectangle( Point( -1, -1 ), Size( 1, 1 ) );
868 if( ! i_pReferenceFrame || ! AquaSalFrame::isAlive( static_cast<AquaSalFrame*>(i_pReferenceFrame) ) )
869 return Rectangle();
871 MenuBarButtonEntry* pEntry = findButtonItem( i_nItemId );
873 if( ! pEntry )
874 return Rectangle();
876 NSStatusItem* pItem = SalData::getStatusItem();
877 if( ! pItem )
878 return Rectangle();
880 NSView* pView = [pItem view];
881 if( ! pView )
882 return Rectangle();
883 NSWindow* pWin = [pView window];
884 if( ! pWin )
885 return Rectangle();
887 NSRect aRect = [pWin frame];
888 aRect.origin = [pWin convertBaseToScreen: NSMakePoint( 0, 0 )];
890 // make coordinates relative to reference frame
891 static_cast<AquaSalFrame*>(i_pReferenceFrame)->CocoaToVCL( aRect.origin );
892 aRect.origin.x -= i_pReferenceFrame->maGeometry.nX;
893 aRect.origin.y -= i_pReferenceFrame->maGeometry.nY + aRect.size.height;
895 return Rectangle( Point(static_cast<long int>(aRect.origin.x),
896 static_cast<long int>(aRect.origin.y)
898 Size( static_cast<long int>(aRect.size.width),
899 static_cast<long int>(aRect.size.height)
904 // =======================================================================
907 * SalMenuItem
910 AquaSalMenuItem::AquaSalMenuItem( const SalItemParams* pItemData ) :
911 mnId( pItemData->nId ),
912 mpVCLMenu( pItemData->pMenu ),
913 mpParentMenu( NULL ),
914 mpSubMenu( NULL ),
915 mpMenuItem( nil )
917 String aText( pItemData->aText );
919 // Delete mnemonics
920 aText.EraseAllChars( '~' );
922 if (pItemData->eType == MENUITEM_SEPARATOR)
924 mpMenuItem = [NSMenuItem separatorItem];
925 // these can go occasionally go in and out of a menu, ensure their lifecycle
926 // also for the release in AquaSalMenuItem destructor
927 [mpMenuItem retain];
929 else
931 mpMenuItem = [[SalNSMenuItem alloc] initWithMenuItem: this];
932 [mpMenuItem setEnabled: YES];
933 NSString* pString = CreateNSString( aText );
934 if (pString)
936 [mpMenuItem setTitle: pString];
937 [pString release];
939 // anything but a separator should set a menu to dispatch to
940 DBG_ASSERT( mpVCLMenu, "no menu" );
944 AquaSalMenuItem::~AquaSalMenuItem()
946 /* #i89860# FIXME:
947 using [autorelease] here (and in AquaSalMenu:::~AquaSalMenu) instead of
948 [release] fixes an occasional crash. That should indicate that we release
949 menus / menu items in the wrong order somewhere, but I
950 could not find that case.
952 if( mpMenuItem )
953 [mpMenuItem autorelease];
956 // -------------------------------------------------------------------