1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: salmenu.cxx,v $
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"
34 #include "salnsmenu.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
;
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
) );
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(),
77 ByteString( "SHOWDIALOG" ),
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
];
94 // FIXME: currently this is leaked
95 static MainMenuSelector
* pMainMenuSelector
= nil
;
97 static void initAppMenu()
99 static bool bOnce
= true;
104 ResMgr
* pMgr
= ImplGetResMgr();
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
];
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
:)
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
:)
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
153 pNewItem
= [pAppMenu itemAtIndex
: 4];
156 pString
= CreateNSString( String( ResId( SV_MENU_MAC_SERVICES
, *pMgr
) ) );
157 [pNewItem setTitle
: pString
];
162 // rename "Hide NewApplication"
163 pNewItem
= [pAppMenu itemAtIndex
: 6];
166 pString
= CreateNSString( String( ResId( SV_MENU_MAC_HIDEAPP
, *pMgr
) ) );
167 [pNewItem setTitle
: pString
];
172 // rename "Hide Others"
173 pNewItem
= [pAppMenu itemAtIndex
: 7];
176 pString
= CreateNSString( String( ResId( SV_MENU_MAC_HIDEALL
, *pMgr
) ) );
177 [pNewItem setTitle
: pString
];
183 pNewItem
= [pAppMenu itemAtIndex
: 8];
186 pString
= CreateNSString( String( ResId( SV_MENU_MAC_SHOWALL
, *pMgr
) ) );
187 [pNewItem setTitle
: pString
];
192 // rename "Quit NewApplication"
193 pNewItem
= [pAppMenu itemAtIndex
: 10];
196 pString
= CreateNSString( String( ResId( SV_MENU_MAC_QUITAPP
, *pMgr
) ) );
197 [pNewItem setTitle
: pString
];
207 // =======================================================================
209 SalMenu
* AquaSalInstance::CreateMenu( BOOL bMenuBar
)
213 AquaSalMenu
*pAquaSalMenu
= new AquaSalMenu( bMenuBar
);
218 void AquaSalInstance::DestroyMenu( SalMenu
* pSalMenu
)
223 SalMenuItem
* AquaSalInstance::CreateMenuItem( const SalItemParams
* pItemData
)
228 AquaSalMenuItem
*pSalMenuItem
= new AquaSalMenuItem( pItemData
);
233 void AquaSalInstance::DestroyMenuItem( SalMenuItem
* pSalMenuItem
)
239 // =======================================================================
246 AquaSalMenu::AquaSalMenu( bool bMenuBar
) :
247 mbMenuBar( bMenuBar
),
251 mpParentSalMenu( NULL
)
255 mpMenu
= [[SalNSMenu alloc
] initWithMenu
: this];
256 [mpMenu setDelegate
: mpMenu
];
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
] );
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
283 if( pCurrentMenuBar
== this )
285 // if the current menubar gets destroyed, set the default menubar
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
];
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
];
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
];
322 if( ! firstEnabledItemIsNoSeparator
&& [item isSeparatorItem
] ) {
323 [[item menu
]removeItem
:item
];
325 firstEnabledItemIsNoSeparator
= true;
326 lastDisplayedMenuItem
= item
;
328 if( [item hasSubmenu
] ) {
329 removeUnusedItemsRunner( [item submenu
] );
334 if( lastDisplayedMenuItem
!= nil
&& [lastDisplayedMenuItem isSeparatorItem
]) {
335 [[lastDisplayedMenuItem menu
]removeItem
:lastDisplayedMenuItem
];
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() ) {
347 // set offsets for positioning
348 const float offset
= 9.0;
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
);
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);
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
];
390 int AquaSalMenu::getItemIndexByPos( USHORT nPos
) const
393 if( nPos
== MENU_APPEND
)
394 nIndex
= [mpMenu numberOfItems
];
396 nIndex
= sal::static_int_cast
<int>( mbMenuBar
? nPos
+1 : nPos
);
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" );
423 if( pCurrentMenuBar
!= this )
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
437 enableMainMenu( true );
441 void AquaSalMenu::setDefaultMenu()
443 NSMenu
* pMenu
= [NSApp mainMenu
];
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
];
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
)
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
)
486 // push the item to the back and retain it
488 rFallbackMenu
.push_back( pNewItem
);
490 if( pCurrentMenuBar
== NULL
)
494 void AquaSalMenu::removeFallbackMenuItem( NSMenuItem
* pOldItem
)
496 std::vector
< NSMenuItem
* >& rFallbackMenu( GetSalData()->maFallbackMenu
);
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
);
508 if( pCurrentMenuBar
== NULL
)
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")) )
527 // End of experimental code enable/disable part
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
||
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
);
555 DBG_ERROR( "invalid item index in insert" );
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();
571 else if( nPos
< maItems
.size() )
573 pRemoveItem
= maItems
[ nPos
];
574 maItems
.erase( maItems
.begin()+nPos
);
578 DBG_ERROR( "invalid item index in remove" );
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
);
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
]];
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
)
652 NSImage
* pImage
= CreateNSImage( rImage
);
654 [pSalMenuItem
->mpMenuItem setImage
: pImage
];
659 void AquaSalMenu::SetItemText( unsigned i_nPos
, SalMenuItem
* i_pSalMenuItem
, const XubString
& i_rText
)
664 AquaSalMenuItem
*pAquaSalMenuItem
= (AquaSalMenuItem
*) i_pSalMenuItem
;
666 String
aText( i_rText
);
669 aText
.EraseAllChars( '~' );
671 /* #i90015# until there is a correct solution
672 strip out any appended (.*) in menubar entries
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
);
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
];
696 void AquaSalMenu::SetAccelerator( unsigned nPos
, SalMenuItem
* pSalMenuItem
, const KeyCode
& rKeyCode
, const XubString
& rKeyName
)
699 sal_Unicode nCommandKey
= 0;
701 USHORT nKeyCode
=rKeyCode
.GetCode();
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
)
745 else // not even a code ? nonsense -> ignore
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
];
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
];
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
];
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() )
824 MenuBarButtonEntry
* pEntry
= findButtonItem( i_rNewItem
.mnId
);
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
);
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 )
849 void AquaSalMenu::RemoveMenuBarButton( USHORT i_nId
)
851 MenuBarButtonEntry
* pEntry
= findButtonItem( i_nId
);
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 )
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
) ) )
871 MenuBarButtonEntry
* pEntry
= findButtonItem( i_nItemId
);
876 NSStatusItem
* pItem
= SalData::getStatusItem();
880 NSView
* pView
= [pItem view
];
883 NSWindow
* pWin
= [pView window
];
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 // =======================================================================
910 AquaSalMenuItem::AquaSalMenuItem( const SalItemParams
* pItemData
) :
911 mnId( pItemData
->nId
),
912 mpVCLMenu( pItemData
->pMenu
),
913 mpParentMenu( NULL
),
917 String
aText( pItemData
->aText
);
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
931 mpMenuItem
= [[SalNSMenuItem alloc
] initWithMenuItem
: this];
932 [mpMenuItem setEnabled
: YES
];
933 NSString
* pString
= CreateNSString( aText
);
936 [mpMenuItem setTitle
: pString
];
939 // anything but a separator should set a menu to dispatch to
940 DBG_ASSERT( mpVCLMenu
, "no menu" );
944 AquaSalMenuItem::~AquaSalMenuItem()
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.
953 [mpMenuItem autorelease
];
956 // -------------------------------------------------------------------