1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <objc/objc-runtime.h>
24 #include <comphelper/string.hxx>
25 #include <rtl/ustrbuf.hxx>
27 #include <vcl/commandevent.hxx>
28 #include <vcl/floatwin.hxx>
29 #include <vcl/window.hxx>
30 #include <vcl/svapp.hxx>
32 #include "osx/saldata.hxx"
33 #include "osx/salinst.h"
34 #include "osx/salmenu.h"
35 #include "osx/salnsmenu.h"
36 #include "osx/salframe.h"
37 #include "osx/a11ywrapper.h"
38 #include "quartz/utils.h"
44 void releaseButtonEntry( AquaSalMenu::MenuBarButtonEntry
& i_rEntry
)
46 if( i_rEntry
.mpNSImage
)
48 [i_rEntry
.mpNSImage release
];
49 i_rEntry
.mpNSImage
= nil
;
51 if( i_rEntry
.mpToolTipString
)
53 [i_rEntry
.mpToolTipString release
];
54 i_rEntry
.mpToolTipString
= nil
;
60 const AquaSalMenu
* AquaSalMenu::pCurrentMenuBar
= nullptr;
62 @interface MainMenuSelector
: NSObject
65 -(void)showDialog
: (ShowDialogId
)nDialog
;
66 -(void)showPreferences
: (id
)sender
;
67 -(void)showAbout
: (id
)sender
;
70 @implementation MainMenuSelector
71 -(void)showDialog
: (ShowDialogId
)nDialog
73 if( AquaSalMenu::pCurrentMenuBar
)
75 const AquaSalFrame
* pFrame
= AquaSalMenu::pCurrentMenuBar
->mpFrame
;
76 if( pFrame
&& AquaSalFrame::isAlive( pFrame
) )
78 pFrame
->CallCallback( SalEvent::ShowDialog
, reinterpret_cast<void*>(nDialog
) );
84 if( nDialog
== ShowDialogId::About
)
86 else if( nDialog
== ShowDialogId::Preferences
)
87 aDialog
= "PREFERENCES";
88 const ApplicationEvent
* pAppEvent
= new ApplicationEvent(
89 ApplicationEvent::Type::ShowDialog
, aDialog
);
90 AquaSalInstance::aAppEventList
.push_back( pAppEvent
);
94 -(void)showPreferences
: (id
) sender
97 SolarMutexGuard aGuard
;
99 [self showDialog
: ShowDialogId::Preferences
];
101 -(void)showAbout
: (id
) sender
104 SolarMutexGuard aGuard
;
106 [self showDialog
: ShowDialogId::About
];
110 // FIXME: currently this is leaked
111 static MainMenuSelector
* pMainMenuSelector
= nil
;
113 static void initAppMenu()
115 static bool bOnce
= true;
120 ResMgr
* pMgr
= ImplGetResMgr();
124 NSMenu
* pMainMenu
= [NSApp mainMenu
];
125 if( pMainMenu
!= nil
)
127 // create the action selector
128 pMainMenuSelector
= [[MainMenuSelector alloc
] init
];
130 // get the proper submenu
131 NSMenu
* pAppMenu
= [[pMainMenu itemAtIndex
: 0] submenu
];
134 // insert about entry
135 OUString
aAbout( ResId( SV_STDTEXT_ABOUT
, *pMgr
) );
136 NSString
* pString
= CreateNSString( aAbout
);
137 NSMenuItem
* pNewItem
= [pAppMenu insertItemWithTitle
: pString
138 action
: @
selector(showAbout
:)
145 [pNewItem setTarget
: pMainMenuSelector
];
146 [pAppMenu insertItem
: [NSMenuItem separatorItem
] atIndex
: 1];
149 // insert preferences entry
150 OUString
aPref( ResId( SV_STDTEXT_PREFERENCES
, *pMgr
) );
151 pString
= CreateNSString( aPref
);
152 pNewItem
= [pAppMenu insertItemWithTitle
: pString
153 action
: @
selector(showPreferences
:)
160 SAL_WNODEPRECATED_DECLARATIONS_PUSH
161 // 'NSCommandKeyMask' is deprecated: first deprecated in macOS 10.12
162 [pNewItem setKeyEquivalentModifierMask
: NSCommandKeyMask
];
163 SAL_WNODEPRECATED_DECLARATIONS_POP
164 [pNewItem setTarget
: pMainMenuSelector
];
165 [pAppMenu insertItem
: [NSMenuItem separatorItem
] atIndex
: 3];
168 // WARNING: ultra ugly code ahead
170 // rename standard entries
172 pNewItem
= [pAppMenu itemAtIndex
: 4];
175 pString
= CreateNSString( OUString( ResId( SV_MENU_MAC_SERVICES
, *pMgr
) ) );
176 [pNewItem setTitle
: pString
];
181 // rename "Hide NewApplication"
182 pNewItem
= [pAppMenu itemAtIndex
: 6];
185 pString
= CreateNSString( OUString( ResId( SV_MENU_MAC_HIDEAPP
, *pMgr
) ) );
186 [pNewItem setTitle
: pString
];
191 // rename "Hide Others"
192 pNewItem
= [pAppMenu itemAtIndex
: 7];
195 pString
= CreateNSString( OUString( ResId( SV_MENU_MAC_HIDEALL
, *pMgr
) ) );
196 [pNewItem setTitle
: pString
];
202 pNewItem
= [pAppMenu itemAtIndex
: 8];
205 pString
= CreateNSString( OUString( ResId( SV_MENU_MAC_SHOWALL
, *pMgr
) ) );
206 [pNewItem setTitle
: pString
];
211 // rename "Quit NewApplication"
212 pNewItem
= [pAppMenu itemAtIndex
: 10];
215 pString
= CreateNSString( OUString( ResId( SV_MENU_MAC_QUITAPP
, *pMgr
) ) );
216 [pNewItem setTitle
: pString
];
226 SalMenu
* AquaSalInstance::CreateMenu( bool bMenuBar
, Menu
* pVCLMenu
)
230 AquaSalMenu
*pAquaSalMenu
= new AquaSalMenu( bMenuBar
);
231 pAquaSalMenu
->mpVCLMenu
= pVCLMenu
;
236 void AquaSalInstance::DestroyMenu( SalMenu
* pSalMenu
)
241 SalMenuItem
* AquaSalInstance::CreateMenuItem( const SalItemParams
* pItemData
)
246 AquaSalMenuItem
*pSalMenuItem
= new AquaSalMenuItem( pItemData
);
251 void AquaSalInstance::DestroyMenuItem( SalMenuItem
* pSalMenuItem
)
260 AquaSalMenu::AquaSalMenu( bool bMenuBar
) :
261 mbMenuBar( bMenuBar
),
264 mpParentSalMenu( nullptr )
268 mpMenu
= [[SalNSMenu alloc
] initWithMenu
: this];
269 [mpMenu setDelegate
: reinterpret_cast< id
<NSMenuDelegate
> >(mpMenu
)];
273 mpMenu
= [NSApp mainMenu
];
275 [mpMenu setAutoenablesItems
: NO
];
278 AquaSalMenu::~AquaSalMenu()
280 // actually someone should have done AquaSalFrame::SetMenu( NULL )
281 // on our frame, alas it is not so
282 if( mpFrame
&& AquaSalFrame::isAlive( mpFrame
) && mpFrame
->mpMenu
== this )
283 const_cast<AquaSalFrame
*>(mpFrame
)->mpMenu
= nullptr;
285 // this should normally be empty already, but be careful...
286 for( size_t i
= 0; i
< maButtons
.size(); i
++ )
287 releaseButtonEntry( maButtons
[i
] );
290 // is this leaking in some cases ? the release often leads to a duplicate release
291 // it seems the parent item gets ownership of the menu
296 if( pCurrentMenuBar
== this )
298 // if the current menubar gets destroyed, set the default menubar
303 // the system may still hold a reference on mpMenu
305 // so set the pointer to this AquaSalMenu to NULL
306 // to protect from calling a dead object
308 // in ! mbMenuBar case our mpMenu is actually a SalNSMenu*
309 // so we can safely cast here
310 [static_cast<SalNSMenu
*>(mpMenu
) setSalMenu
: nullptr];
312 using [autorelease] here (and in AquaSalMenuItem::~AquaSalMenuItem)
313 instead of [release] fixes an occasional crash. That should
314 indicate that we release menus / menu items in the wrong order
315 somewhere, but I could not find that case.
317 [mpMenu autorelease
];
322 bool AquaSalMenu::ShowNativePopupMenu(FloatingWindow
* pWin
, const tools::Rectangle
& rRect
, FloatWinPopupFlags nFlags
)
324 // do not use native popup menu when AQUA_NATIVE_MENUS is set to false
325 if( ! VisibleMenuBar() ) {
329 // set offsets for positioning
330 const float offset
= 9.0;
333 AquaSalFrame
* pParentAquaSalFrame
= static_cast<AquaSalFrame
*>(pWin
->ImplGetWindowImpl()->mpRealParent
->ImplGetFrame());
334 NSWindow
* pParentNSWindow
= pParentAquaSalFrame
->mpNSWindow
;
335 NSView
* pParentNSView
= [pParentNSWindow contentView
];
336 NSView
* pPopupNSView
= static_cast<AquaSalFrame
*>(pWin
->ImplGetWindow()->ImplGetFrame())->mpNSView
;
337 NSRect popupFrame
= [pPopupNSView frame
];
340 NSRect displayPopupFrame
= NSMakeRect( rRect
.Left()+(offset
-1), rRect
.Top()+(offset
+1), popupFrame
.size
.width
, 0 );
341 pParentAquaSalFrame
->VCLToCocoa(displayPopupFrame
, false);
343 // do the same strange semantics as vcl popup windows to arrive at a frame geometry
344 // in mirrored UI case; best done by actually executing the same code
345 sal_uInt16 nArrangeIndex
;
346 pWin
->SetPosPixel( FloatingWindow::ImplCalcPos( pWin
, rRect
, nFlags
, nArrangeIndex
) );
347 displayPopupFrame
.origin
.x
= pWin
->ImplGetFrame()->maGeometry
.nX
- pParentAquaSalFrame
->maGeometry
.nX
+ offset
;
348 displayPopupFrame
.origin
.y
= pWin
->ImplGetFrame()->maGeometry
.nY
- pParentAquaSalFrame
->maGeometry
.nY
+ offset
;
349 pParentAquaSalFrame
->VCLToCocoa(displayPopupFrame
, false);
351 // #i111992# if this menu was opened due to a key event, prevent dispatching that yet again
352 if( [pParentNSView respondsToSelector
: @
selector(clearLastEvent
)] )
353 [pParentNSView performSelector
:@
selector(clearLastEvent
)];
356 NSPopUpButtonCell
* pPopUpButtonCell
= [[NSPopUpButtonCell alloc
] initTextCell
:@
"" pullsDown
:NO
];
357 [pPopUpButtonCell setMenu
: mpMenu
];
358 [pPopUpButtonCell selectItem
:nil
];
359 [AquaA11yWrapper setPopupMenuOpen
: YES
];
360 [pPopUpButtonCell performClickWithFrame
:displayPopupFrame inView
:pParentNSView
];
361 [pPopUpButtonCell release
];
362 [AquaA11yWrapper setPopupMenuOpen
: NO
];
367 int AquaSalMenu::getItemIndexByPos( sal_uInt16 nPos
) const
370 if( nPos
== MENU_APPEND
)
371 nIndex
= [mpMenu numberOfItems
];
373 nIndex
= sal::static_int_cast
<int>( mbMenuBar
? nPos
+1 : nPos
);
377 const AquaSalFrame
* AquaSalMenu::getFrame() const
379 const AquaSalMenu
* pMenu
= this;
380 while( pMenu
&& ! pMenu
->mpFrame
)
381 pMenu
= pMenu
->mpParentSalMenu
;
382 return pMenu
? pMenu
->mpFrame
: nullptr;
385 void AquaSalMenu::unsetMainMenu()
387 pCurrentMenuBar
= nullptr;
389 // remove items from main menu
390 NSMenu
* pMenu
= [NSApp mainMenu
];
391 for( int nItems
= [pMenu numberOfItems
]; nItems
> 1; nItems
-- )
392 [pMenu removeItemAtIndex
: 1];
395 void AquaSalMenu::setMainMenu()
397 SAL_WARN_IF( !mbMenuBar
, "vcl", "setMainMenu on non menubar" );
400 if( pCurrentMenuBar
!= this )
404 for( std::vector
<AquaSalMenuItem
*>::size_type i
= 0; i
< maItems
.size(); i
++ )
406 NSMenuItem
* pItem
= maItems
[i
]->mpMenuItem
;
407 [mpMenu insertItem
: pItem atIndex
: i
+1];
409 pCurrentMenuBar
= this;
411 // change status item
414 enableMainMenu( true );
418 void AquaSalMenu::setDefaultMenu()
420 NSMenu
* pMenu
= [NSApp mainMenu
];
424 // insert default items
425 std::vector
< NSMenuItem
* >& rFallbackMenu( GetSalData()->maFallbackMenu
);
426 for( unsigned int i
= 0, nAddItems
= rFallbackMenu
.size(); i
< nAddItems
; i
++ )
428 NSMenuItem
* pItem
= rFallbackMenu
[i
];
429 if( [pItem menu
] == nil
)
430 [pMenu insertItem
: pItem atIndex
: i
+1];
434 void AquaSalMenu::enableMainMenu( bool bEnable
)
436 NSMenu
* pMainMenu
= [NSApp mainMenu
];
439 // enable/disable items from main menu
440 int nItems
= [pMainMenu numberOfItems
];
441 for( int n
= 1; n
< nItems
; n
++ )
443 NSMenuItem
* pItem
= [pMainMenu itemAtIndex
: n
];
444 [pItem setEnabled
: bEnable
? YES
: NO
];
449 void AquaSalMenu::addFallbackMenuItem( NSMenuItem
* pNewItem
)
453 std::vector
< NSMenuItem
* >& rFallbackMenu( GetSalData()->maFallbackMenu
);
455 // prevent duplicate insertion
456 int nItems
= rFallbackMenu
.size();
457 for( int i
= 0; i
< nItems
; i
++ )
459 if( rFallbackMenu
[i
] == pNewItem
)
463 // push the item to the back and retain it
465 rFallbackMenu
.push_back( pNewItem
);
467 if( pCurrentMenuBar
== nullptr )
471 void AquaSalMenu::removeFallbackMenuItem( NSMenuItem
* pOldItem
)
473 std::vector
< NSMenuItem
* >& rFallbackMenu( GetSalData()->maFallbackMenu
);
476 unsigned int nItems
= rFallbackMenu
.size();
477 for( unsigned int i
= 0; i
< nItems
; i
++ )
479 if( rFallbackMenu
[i
] == pOldItem
)
481 // remove item and release
482 rFallbackMenu
.erase( rFallbackMenu
.begin() + i
);
485 if( pCurrentMenuBar
== nullptr )
493 bool AquaSalMenu::VisibleMenuBar()
495 // Enable/disable experimental native menus code?
497 // To disable native menus, set the environment variable AQUA_NATIVE_MENUS to FALSE
499 static const char *pExperimental
= getenv ("AQUA_NATIVE_MENUS");
501 if ( pExperimental
&& !strcasecmp(pExperimental
, "FALSE") )
504 // End of experimental code enable/disable part
509 void AquaSalMenu::SetFrame( const SalFrame
*pFrame
)
511 mpFrame
= static_cast<const AquaSalFrame
*>(pFrame
);
514 void AquaSalMenu::InsertItem( SalMenuItem
* pSalMenuItem
, unsigned nPos
)
516 AquaSalMenuItem
*pAquaSalMenuItem
= static_cast<AquaSalMenuItem
*>(pSalMenuItem
);
518 pAquaSalMenuItem
->mpParentMenu
= this;
519 DBG_ASSERT( pAquaSalMenuItem
->mpVCLMenu
== nullptr ||
520 pAquaSalMenuItem
->mpVCLMenu
== mpVCLMenu
||
521 mpVCLMenu
== nullptr,
522 "resetting menu ?" );
523 if( pAquaSalMenuItem
->mpVCLMenu
)
524 mpVCLMenu
= pAquaSalMenuItem
->mpVCLMenu
;
526 if( nPos
== MENU_APPEND
|| nPos
== maItems
.size() )
527 maItems
.push_back( pAquaSalMenuItem
);
528 else if( nPos
< maItems
.size() )
529 maItems
.insert( maItems
.begin() + nPos
, pAquaSalMenuItem
);
532 OSL_FAIL( "invalid item index in insert" );
536 if( ! mbMenuBar
|| pCurrentMenuBar
== this )
537 [mpMenu insertItem
: pAquaSalMenuItem
->mpMenuItem atIndex
: getItemIndexByPos(nPos
)];
540 void AquaSalMenu::RemoveItem( unsigned nPos
)
542 AquaSalMenuItem
* pRemoveItem
= nullptr;
543 if( nPos
== MENU_APPEND
|| nPos
== (maItems
.size()-1) )
545 pRemoveItem
= maItems
.back();
548 else if( nPos
< maItems
.size() )
550 pRemoveItem
= maItems
[ nPos
];
551 maItems
.erase( maItems
.begin()+nPos
);
555 OSL_FAIL( "invalid item index in remove" );
559 pRemoveItem
->mpParentMenu
= nullptr;
561 if( ! mbMenuBar
|| pCurrentMenuBar
== this )
562 [mpMenu removeItemAtIndex
: getItemIndexByPos(nPos
)];
565 void AquaSalMenu::SetSubMenu( SalMenuItem
* pSalMenuItem
, SalMenu
* pSubMenu
, unsigned /*nPos*/ )
567 AquaSalMenuItem
*pAquaSalMenuItem
= static_cast<AquaSalMenuItem
*>(pSalMenuItem
);
568 AquaSalMenu
*subAquaSalMenu
= static_cast<AquaSalMenu
*>(pSubMenu
);
572 pAquaSalMenuItem
->mpSubMenu
= subAquaSalMenu
;
573 if( subAquaSalMenu
->mpParentSalMenu
== nullptr )
575 subAquaSalMenu
->mpParentSalMenu
= this;
576 [pAquaSalMenuItem
->mpMenuItem setSubmenu
: subAquaSalMenu
->mpMenu
];
578 // set title of submenu
579 [subAquaSalMenu
->mpMenu setTitle
: [pAquaSalMenuItem
->mpMenuItem title
]];
581 else if( subAquaSalMenu
->mpParentSalMenu
!= this )
583 // cocoa doesn't allow menus to be submenus of multiple
584 // menu items, so place a copy in the menu item instead ?
585 // let's hope that NSMenu copy does the right thing
586 NSMenu
* pCopy
= [subAquaSalMenu
->mpMenu copy
];
587 [pAquaSalMenuItem
->mpMenuItem setSubmenu
: pCopy
];
589 // set title of submenu
590 [pCopy setTitle
: [pAquaSalMenuItem
->mpMenuItem title
]];
595 if( pAquaSalMenuItem
->mpSubMenu
)
597 if( pAquaSalMenuItem
->mpSubMenu
->mpParentSalMenu
== this )
598 pAquaSalMenuItem
->mpSubMenu
->mpParentSalMenu
= nullptr;
600 pAquaSalMenuItem
->mpSubMenu
= nullptr;
601 [pAquaSalMenuItem
->mpMenuItem setSubmenu
: nil
];
605 void AquaSalMenu::CheckItem( unsigned nPos
, bool bCheck
)
607 if( nPos
< maItems
.size() )
609 NSMenuItem
* pItem
= maItems
[nPos
]->mpMenuItem
;
610 [pItem setState
: bCheck
? NSOnState
: NSOffState
];
614 void AquaSalMenu::EnableItem( unsigned nPos
, bool bEnable
)
616 if( nPos
< maItems
.size() )
618 NSMenuItem
* pItem
= maItems
[nPos
]->mpMenuItem
;
619 [pItem setEnabled
: bEnable
? YES
: NO
];
623 void AquaSalMenu::SetItemImage( unsigned /*nPos*/, SalMenuItem
* pSMI
, const Image
& rImage
)
625 AquaSalMenuItem
* pSalMenuItem
= static_cast<AquaSalMenuItem
*>( pSMI
);
626 if( ! pSalMenuItem
|| ! pSalMenuItem
->mpMenuItem
)
629 NSImage
* pImage
= CreateNSImage( rImage
);
631 [pSalMenuItem
->mpMenuItem setImage
: pImage
];
636 void AquaSalMenu::SetItemText( unsigned /*i_nPos*/, SalMenuItem
* i_pSalMenuItem
, const OUString
& i_rText
)
641 AquaSalMenuItem
*pAquaSalMenuItem
= static_cast<AquaSalMenuItem
*>(i_pSalMenuItem
);
644 OUString aText
= i_rText
.replaceAll("~", "");
646 /* #i90015# until there is a correct solution
647 strip out any appended (.*) in menubar entries
651 sal_Int32 nPos
= aText
.lastIndexOf( '(' );
654 sal_Int32 nPos2
= aText
.indexOf( ')' );
656 aText
= aText
.replaceAt( nPos
, nPos2
-nPos
+1, "" );
660 NSString
* pString
= CreateNSString( aText
);
663 [pAquaSalMenuItem
->mpMenuItem setTitle
: pString
];
664 // if the menu item has a submenu, change its title as well
665 if (pAquaSalMenuItem
->mpSubMenu
)
666 [pAquaSalMenuItem
->mpSubMenu
->mpMenu setTitle
: pString
];
671 void AquaSalMenu::SetAccelerator( unsigned /*nPos*/, SalMenuItem
* pSalMenuItem
, const vcl::KeyCode
& rKeyCode
, const OUString
& /*rKeyName*/ )
673 sal_uInt16 nModifier
;
674 sal_Unicode nCommandKey
= 0;
676 sal_uInt16 nKeyCode
=rKeyCode
.GetCode();
679 if ((nKeyCode
>=KEY_A
) && (nKeyCode
<=KEY_Z
)) // letter A..Z
680 nCommandKey
= nKeyCode
-KEY_A
+ 'a';
681 else if ((nKeyCode
>=KEY_0
) && (nKeyCode
<=KEY_9
)) // numbers 0..9
682 nCommandKey
= nKeyCode
-KEY_0
+ '0';
683 else if ((nKeyCode
>=KEY_F1
) && (nKeyCode
<=KEY_F26
)) // function keys F1..F26
684 nCommandKey
= nKeyCode
-KEY_F1
+ NSF1FunctionKey
;
685 else if( nKeyCode
== KEY_REPEAT
)
686 nCommandKey
= NSRedoFunctionKey
;
687 else if( nKeyCode
== KEY_SPACE
)
721 nCommandKey
=u
'\x232b';
724 nCommandKey
=u
'\x21de';
727 nCommandKey
=u
'\x21df';
730 nCommandKey
=u
'\x21e1';
733 nCommandKey
=u
'\x21e3';
736 nCommandKey
=u
'\x21a9';
738 case KEY_BRACKETLEFT
:
741 case KEY_BRACKETRIGHT
:
747 else // not even a code ? nonsense -> ignore
750 SAL_WARN_IF( !nCommandKey
, "vcl", "unmapped accelerator key" );
752 nModifier
=rKeyCode
.GetModifier();
754 // should always use the command key
755 int nItemModifier
= 0;
757 SAL_WNODEPRECATED_DECLARATIONS_PUSH
758 // 'NSAlternateKeyMask' is deprecated: first deprecated in macOS 10.12
759 // 'NSCommandKeyMask' is deprecated: first deprecated in macOS 10.12
760 // 'NSControlKeyMask' is deprecated: first deprecated in macOS 10.12
761 // 'NSShiftKeyMask' is deprecated: first deprecated in macOS 10.12
762 if (nModifier
& KEY_SHIFT
)
764 nItemModifier
|= NSShiftKeyMask
; // actually useful only for function keys
765 if( nKeyCode
>= KEY_A
&& nKeyCode
<= KEY_Z
)
766 nCommandKey
= nKeyCode
- KEY_A
+ 'A';
769 if (nModifier
& KEY_MOD1
)
770 nItemModifier
|= NSCommandKeyMask
;
772 if(nModifier
& KEY_MOD2
)
773 nItemModifier
|= NSAlternateKeyMask
;
775 if(nModifier
& KEY_MOD3
)
776 nItemModifier
|= NSControlKeyMask
;
777 SAL_WNODEPRECATED_DECLARATIONS_POP
779 AquaSalMenuItem
*pAquaSalMenuItem
= static_cast<AquaSalMenuItem
*>(pSalMenuItem
);
780 NSString
* pString
= CreateNSString( OUString( &nCommandKey
, 1 ) );
781 [pAquaSalMenuItem
->mpMenuItem setKeyEquivalent
: pString
];
782 [pAquaSalMenuItem
->mpMenuItem setKeyEquivalentModifierMask
: nItemModifier
];
787 void AquaSalMenu::GetSystemMenuData( SystemMenuData
* )
791 AquaSalMenu::MenuBarButtonEntry
* AquaSalMenu::findButtonItem( sal_uInt16 i_nItemId
)
793 for( size_t i
= 0; i
< maButtons
.size(); ++i
)
795 if( maButtons
[i
].maButton
.mnId
== i_nItemId
)
796 return &maButtons
[i
];
801 void AquaSalMenu::statusLayout()
803 if( GetSalData()->mpStatusItem
)
805 NSView
* pNSView
= [GetSalData()->mpStatusItem view
];
806 if( [pNSView isMemberOfClass
: [OOStatusItemView
class]] ) // well of course it is
807 [(OOStatusItemView
*)pNSView layout
];
809 OSL_FAIL( "someone stole our status view" );
813 bool AquaSalMenu::AddMenuBarButton( const SalMenuButtonItem
& i_rNewItem
)
815 if( ! mbMenuBar
|| ! VisibleMenuBar() )
818 MenuBarButtonEntry
* pEntry
= findButtonItem( i_rNewItem
.mnId
);
821 releaseButtonEntry( *pEntry
);
822 pEntry
->maButton
= i_rNewItem
;
823 pEntry
->mpNSImage
= CreateNSImage( i_rNewItem
.maImage
);
824 if( i_rNewItem
.maToolTipText
.getLength() )
825 pEntry
->mpToolTipString
= CreateNSString( i_rNewItem
.maToolTipText
);
829 maButtons
.push_back( MenuBarButtonEntry( i_rNewItem
) );
830 maButtons
.back().mpNSImage
= CreateNSImage( i_rNewItem
.maImage
);
831 maButtons
.back().mpToolTipString
= CreateNSString( i_rNewItem
.maToolTipText
);
834 // lazy create status item
835 SalData::getStatusItem();
837 if( pCurrentMenuBar
== this )
843 void AquaSalMenu::RemoveMenuBarButton( sal_uInt16 i_nId
)
845 MenuBarButtonEntry
* pEntry
= findButtonItem( i_nId
);
848 releaseButtonEntry( *pEntry
);
849 // note: vector guarantees that its contents are in a plain array
850 maButtons
.erase( maButtons
.begin() + (pEntry
- &maButtons
[0]) );
853 if( pCurrentMenuBar
== this )
857 tools::Rectangle
AquaSalMenu::GetMenuBarButtonRectPixel( sal_uInt16 i_nItemId
, SalFrame
* i_pReferenceFrame
)
859 if( ! i_pReferenceFrame
|| ! AquaSalFrame::isAlive( static_cast<AquaSalFrame
*>(i_pReferenceFrame
) ) )
860 return tools::Rectangle();
862 MenuBarButtonEntry
* pEntry
= findButtonItem( i_nItemId
);
865 return tools::Rectangle();
867 NSStatusItem
* pItem
= SalData::getStatusItem();
869 return tools::Rectangle();
871 NSView
* pNSView
= [pItem view
];
873 return tools::Rectangle();
874 NSWindow
* pNSWin
= [pNSView window
];
876 return tools::Rectangle();
878 NSRect aRect
= [pNSWin convertRectToScreen
:[pNSWin frame
]];
880 // make coordinates relative to reference frame
881 static_cast<AquaSalFrame
*>(i_pReferenceFrame
)->CocoaToVCL( aRect
.origin
);
882 aRect
.origin
.x
-= i_pReferenceFrame
->maGeometry
.nX
;
883 aRect
.origin
.y
-= i_pReferenceFrame
->maGeometry
.nY
+ aRect
.size
.height
;
885 return tools::Rectangle( Point(static_cast<long int>(aRect
.origin
.x
),
886 static_cast<long int>(aRect
.origin
.y
)
888 Size( static_cast<long int>(aRect
.size
.width
),
889 static_cast<long int>(aRect
.size
.height
)
898 AquaSalMenuItem::AquaSalMenuItem( const SalItemParams
* pItemData
) :
899 mnId( pItemData
->nId
),
900 mpVCLMenu( pItemData
->pMenu
),
901 mpParentMenu( nullptr ),
902 mpSubMenu( nullptr ),
905 if (pItemData
->eType
== MenuItemType::SEPARATOR
)
907 mpMenuItem
= [NSMenuItem separatorItem
];
908 // these can go occasionally go in and out of a menu, ensure their lifecycle
909 // also for the release in AquaSalMenuItem destructor
914 mpMenuItem
= [[SalNSMenuItem alloc
] initWithMenuItem
: this];
915 [mpMenuItem setEnabled
: YES
];
917 // peel mnemonics because on mac there are no such things for menu items
918 NSString
* pString
= CreateNSString( pItemData
->aText
.replaceAll( "~", "" ) );
921 [mpMenuItem setTitle
: pString
];
924 // anything but a separator should set a menu to dispatch to
925 SAL_WARN_IF( !mpVCLMenu
, "vcl", "no menu" );
929 AquaSalMenuItem::~AquaSalMenuItem()
932 using [autorelease] here (and in AquaSalMenu:::~AquaSalMenu) instead of
933 [release] fixes an occasional crash. That should indicate that we release
934 menus / menu items in the wrong order somewhere, but I
935 could not find that case.
938 [mpMenuItem autorelease
];
941 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */