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>
21 #include <sal/log.hxx>
22 #include <osl/diagnose.h>
24 #include <objc/objc-runtime.h>
26 #include <rtl/ustrbuf.hxx>
27 #include <tools/debug.hxx>
28 #include <tools/long.hxx>
29 #include <vcl/commandevent.hxx>
30 #include <vcl/toolkit/floatwin.hxx>
31 #include <vcl/window.hxx>
32 #include <vcl/svapp.hxx>
34 #include <osx/runinmain.hxx>
35 #include <osx/saldata.hxx>
36 #include <osx/salinst.h>
37 #include <osx/salmenu.h>
38 #include <osx/salnsmenu.h>
39 #include <osx/salframe.h>
40 #include <osx/a11ywrapper.h>
41 #include <quartz/utils.h>
42 #include <strings.hrc>
44 #include <vcl/mnemonic.hxx>
48 void releaseButtonEntry( AquaSalMenu::MenuBarButtonEntry
& i_rEntry
)
50 if( i_rEntry
.mpNSImage
)
52 [i_rEntry
.mpNSImage release
];
53 i_rEntry
.mpNSImage
= nil
;
55 if( i_rEntry
.mpToolTipString
)
57 [i_rEntry
.mpToolTipString release
];
58 i_rEntry
.mpToolTipString
= nil
;
64 const AquaSalMenu
* AquaSalMenu::pCurrentMenuBar
= nullptr;
66 @interface MainMenuSelector
: NSObject
69 -(void)showDialog
: (ShowDialogId
)nDialog
;
70 -(void)showPreferences
: (id
)sender
;
71 -(void)showAbout
: (id
)sender
;
74 @implementation MainMenuSelector
75 -(void)showDialog
: (ShowDialogId
)nDialog
77 if( AquaSalMenu::pCurrentMenuBar
)
79 const AquaSalFrame
* pFrame
= AquaSalMenu::pCurrentMenuBar
->mpFrame
;
80 if( pFrame
&& AquaSalFrame::isAlive( pFrame
) )
82 pFrame
->CallCallback( SalEvent::ShowDialog
, reinterpret_cast<void*>(nDialog
) );
88 if( nDialog
== ShowDialogId::About
)
90 else if( nDialog
== ShowDialogId::Preferences
)
91 aDialog
= "PREFERENCES";
92 const ApplicationEvent
* pAppEvent
= new ApplicationEvent(
93 ApplicationEvent::Type::ShowDialog
, aDialog
);
94 AquaSalInstance::aAppEventList
.push_back( pAppEvent
);
98 -(void)showPreferences
: (id
) sender
101 SolarMutexGuard aGuard
;
103 [self showDialog
: ShowDialogId::Preferences
];
105 -(void)showAbout
: (id
) sender
108 SolarMutexGuard aGuard
;
110 [self showDialog
: ShowDialogId::About
];
114 // FIXME: currently this is leaked
115 static MainMenuSelector
* pMainMenuSelector
= nil
;
117 static void initAppMenu()
119 static bool bInitialized
= false;
122 OSX_SALDATA_RUNINMAIN(initAppMenu())
125 NSMenu
* pAppMenu
= nil
;
126 NSMenuItem
* pNewItem
= nil
;
128 // Related: tdf#126638 use NSMenu subclass to catch and redirect key
129 // shortcuts when a modal window is displayed
130 SalNSMainMenu
* pMainMenu
= [[[SalNSMainMenu alloc
] initWithTitle
: @
"Main Menu"] autorelease
];
131 pNewItem
= [pMainMenu addItemWithTitle
: @
"Application"
134 pAppMenu
= [[[NSMenu alloc
] initWithTitle
: @
"Application"] autorelease
];
135 [pNewItem setSubmenu
: pAppMenu
];
136 [NSApp setMainMenu
: pMainMenu
];
138 pMainMenuSelector
= [[MainMenuSelector alloc
] init
];
141 NSString
* pString
= CreateNSString(VclResId(SV_STDTEXT_ABOUT
));
142 pNewItem
= [pAppMenu addItemWithTitle
: pString
143 action
: @
selector(showAbout
:)
146 [pNewItem setTarget
: pMainMenuSelector
];
148 [pAppMenu addItem
:[NSMenuItem separatorItem
]];
151 pString
= CreateNSString(VclResId(SV_STDTEXT_PREFERENCES
));
152 pNewItem
= [pAppMenu addItemWithTitle
: pString
153 action
: @
selector(showPreferences
:)
154 keyEquivalent
: @
","];
156 [pNewItem setKeyEquivalentModifierMask
: NSEventModifierFlagCommand
];
157 [pNewItem setTarget
: pMainMenuSelector
];
159 [pAppMenu addItem
:[NSMenuItem separatorItem
]];
161 // Services item and menu
162 pString
= CreateNSString(VclResId(SV_MENU_MAC_SERVICES
));
163 pNewItem
= [pAppMenu addItemWithTitle
: pString
167 NSMenu
*servicesMenu
= [[[NSMenu alloc
] initWithTitle
:@
"Services"] autorelease
];
168 [pNewItem setSubmenu
: servicesMenu
];
169 [NSApp setServicesMenu
: servicesMenu
];
171 [pAppMenu addItem
:[NSMenuItem separatorItem
]];
174 pString
= CreateNSString(VclResId(SV_MENU_MAC_HIDEAPP
));
175 [pAppMenu addItemWithTitle
: pString
176 action
:@
selector(hide
:)
181 pString
= CreateNSString(VclResId(SV_MENU_MAC_HIDEALL
));
182 [pAppMenu addItemWithTitle
: pString
183 action
:@
selector(hideOtherApplications
:)
186 [pNewItem setKeyEquivalentModifierMask
: NSEventModifierFlagCommand
| NSEventModifierFlagOption
];
189 pString
= CreateNSString(VclResId(SV_MENU_MAC_SHOWALL
));
190 [pAppMenu addItemWithTitle
: pString
191 action
:@
selector(unhideAllApplications
:)
195 [pAppMenu addItem
:[NSMenuItem separatorItem
]];
198 pString
= CreateNSString(VclResId(SV_MENU_MAC_QUITAPP
));
199 [pAppMenu addItemWithTitle
: pString
200 action
:@
selector(terminate
:)
205 std::unique_ptr
<SalMenu
> AquaSalInstance::CreateMenu( bool bMenuBar
, Menu
* pVCLMenu
)
209 AquaSalMenu
*pAquaSalMenu
= new AquaSalMenu( bMenuBar
);
210 pAquaSalMenu
->mpVCLMenu
= pVCLMenu
;
212 return std::unique_ptr
<SalMenu
>(pAquaSalMenu
);
215 std::unique_ptr
<SalMenuItem
> AquaSalInstance::CreateMenuItem( const SalItemParams
& rItemData
)
217 AquaSalMenuItem
*pSalMenuItem
= new AquaSalMenuItem( &rItemData
);
219 return std::unique_ptr
<SalMenuItem
>(pSalMenuItem
);
226 AquaSalMenu::AquaSalMenu( bool bMenuBar
) :
227 mbMenuBar( bMenuBar
),
230 mpParentSalMenu( nullptr )
234 mpMenu
= [[SalNSMenu alloc
] initWithMenu
: this];
235 [mpMenu setDelegate
: reinterpret_cast< id
<NSMenuDelegate
> >(mpMenu
)];
237 // Related: tdf#126638 enable the menu's "autoenabledItems" property
238 // Enable the menu's "autoenabledItems" property so that
239 // -[SalNSMenuItem validateMenuItem:] will be called before handling
240 // a key shortcut and the menu item can be temporarily disabled if a
241 // modal window is displayed.
242 [mpMenu setAutoenablesItems
: YES
];
246 mpMenu
= [NSApp mainMenu
];
247 [mpMenu setAutoenablesItems
: NO
];
251 AquaSalMenu::~AquaSalMenu()
253 // actually someone should have done AquaSalFrame::SetMenu( NULL )
254 // on our frame, alas it is not so
255 if( mpFrame
&& AquaSalFrame::isAlive( mpFrame
) && mpFrame
->mpMenu
== this )
256 const_cast<AquaSalFrame
*>(mpFrame
)->mpMenu
= nullptr;
258 // this should normally be empty already, but be careful...
259 for( size_t i
= 0; i
< maButtons
.size(); i
++ )
260 releaseButtonEntry( maButtons
[i
] );
263 // is this leaking in some cases ? the release often leads to a duplicate release
264 // it seems the parent item gets ownership of the menu
269 if( pCurrentMenuBar
== this )
271 // if the current menubar gets destroyed, set the default menubar
276 // the system may still hold a reference on mpMenu
278 // so set the pointer to this AquaSalMenu to NULL
279 // to protect from calling a dead object
281 // in ! mbMenuBar case our mpMenu is actually a SalNSMenu*
282 // so we can safely cast here
283 [static_cast<SalNSMenu
*>(mpMenu
) setSalMenu
: nullptr];
285 using [autorelease] here (and in AquaSalMenuItem::~AquaSalMenuItem)
286 instead of [release] fixes an occasional crash. That should
287 indicate that we release menus / menu items in the wrong order
288 somewhere, but I could not find that case.
290 [mpMenu autorelease
];
295 bool AquaSalMenu::ShowNativePopupMenu(FloatingWindow
* pWin
, const tools::Rectangle
& rRect
, FloatWinPopupFlags nFlags
)
297 // set offsets for positioning
298 const float offset
= 9.0;
301 AquaSalFrame
* pParentAquaSalFrame
= static_cast<AquaSalFrame
*>(pWin
->ImplGetWindowImpl()->mpRealParent
->ImplGetFrame());
302 NSWindow
* pParentNSWindow
= pParentAquaSalFrame
->mpNSWindow
;
303 NSView
* pParentNSView
= [pParentNSWindow contentView
];
304 NSView
* pPopupNSView
= static_cast<AquaSalFrame
*>(pWin
->ImplGetWindow()->ImplGetFrame())->mpNSView
;
305 NSRect popupFrame
= [pPopupNSView frame
];
308 NSRect displayPopupFrame
= NSMakeRect( rRect
.Left()+(offset
-1), rRect
.Top()+(offset
+1), popupFrame
.size
.width
, 0 );
309 pParentAquaSalFrame
->VCLToCocoa(displayPopupFrame
, false);
311 // do the same strange semantics as vcl popup windows to arrive at a frame geometry
312 // in mirrored UI case; best done by actually executing the same code
313 sal_uInt16 nArrangeIndex
;
314 pWin
->SetPosPixel( FloatingWindow::ImplCalcPos( pWin
, rRect
, nFlags
, nArrangeIndex
) );
315 displayPopupFrame
.origin
.x
= pWin
->ImplGetFrame()->GetUnmirroredGeometry().x() - pParentAquaSalFrame
->GetUnmirroredGeometry().x() + offset
;
316 displayPopupFrame
.origin
.y
= pWin
->ImplGetFrame()->GetUnmirroredGeometry().y() - pParentAquaSalFrame
->GetUnmirroredGeometry().y() + offset
;
317 pParentAquaSalFrame
->VCLToCocoa(displayPopupFrame
, false);
319 // #i111992# if this menu was opened due to a key event, prevent dispatching that yet again
320 if( [pParentNSView respondsToSelector
: @
selector(clearLastEvent
)] )
321 [pParentNSView performSelector
:@
selector(clearLastEvent
)];
324 NSPopUpButtonCell
* pPopUpButtonCell
= [[NSPopUpButtonCell alloc
] initTextCell
:@
"" pullsDown
:NO
];
325 [pPopUpButtonCell setMenu
: mpMenu
];
326 [pPopUpButtonCell selectItem
:nil
];
327 [AquaA11yWrapper setPopupMenuOpen
: YES
];
328 [pPopUpButtonCell performClickWithFrame
:displayPopupFrame inView
:pParentNSView
];
329 [pPopUpButtonCell release
];
330 [AquaA11yWrapper setPopupMenuOpen
: NO
];
335 int AquaSalMenu::getItemIndexByPos( sal_uInt16 nPos
) const
338 if( nPos
== MENU_APPEND
)
339 nIndex
= [mpMenu numberOfItems
];
341 nIndex
= sal::static_int_cast
<int>( mbMenuBar
? nPos
+1 : nPos
);
345 const AquaSalFrame
* AquaSalMenu::getFrame() const
347 const AquaSalMenu
* pMenu
= this;
348 while( pMenu
&& ! pMenu
->mpFrame
)
349 pMenu
= pMenu
->mpParentSalMenu
;
350 return pMenu
? pMenu
->mpFrame
: nullptr;
353 void AquaSalMenu::unsetMainMenu()
355 pCurrentMenuBar
= nullptr;
357 // remove items from main menu
358 NSMenu
* pMenu
= [NSApp mainMenu
];
359 for( int nItems
= [pMenu numberOfItems
]; nItems
> 1; nItems
-- )
360 [pMenu removeItemAtIndex
: 1];
363 void AquaSalMenu::setMainMenu()
365 SAL_WARN_IF( !mbMenuBar
, "vcl", "setMainMenu on non menubar" );
368 if( pCurrentMenuBar
!= this )
372 for( std::vector
<AquaSalMenuItem
*>::size_type i
= 0; i
< maItems
.size(); i
++ )
374 NSMenuItem
* pItem
= maItems
[i
]->mpMenuItem
;
375 [mpMenu insertItem
: pItem atIndex
: i
+1];
377 pCurrentMenuBar
= this;
379 // change status item
382 enableMainMenu( true );
386 void AquaSalMenu::setDefaultMenu()
388 // tdf#160427 native menu changes can only be done on the main thread
389 OSX_SALDATA_RUNINMAIN(AquaSalMenu::setDefaultMenu())
391 NSMenu
* pMenu
= [NSApp mainMenu
];
395 // insert default items
396 std::vector
< NSMenuItem
* >& rFallbackMenu( GetSalData()->maFallbackMenu
);
397 for( unsigned int i
= 0, nAddItems
= rFallbackMenu
.size(); i
< nAddItems
; i
++ )
399 NSMenuItem
* pItem
= rFallbackMenu
[i
];
400 if( [pItem menu
] == nil
)
401 [pMenu insertItem
: pItem atIndex
: i
+1];
405 void AquaSalMenu::enableMainMenu( bool bEnable
)
407 NSMenu
* pMainMenu
= [NSApp mainMenu
];
410 // enable/disable items from main menu
411 int nItems
= [pMainMenu numberOfItems
];
412 for( int n
= 1; n
< nItems
; n
++ )
414 NSMenuItem
* pItem
= [pMainMenu itemAtIndex
: n
];
415 if( [pItem isKindOfClass
: [SalNSMenuItem
class]])
416 [static_cast<SalNSMenuItem
*>(pItem
) setReallyEnabled
: bEnable
];
418 [pItem setEnabled
: bEnable
];
423 void AquaSalMenu::addFallbackMenuItem( NSMenuItem
* pNewItem
)
427 std::vector
< NSMenuItem
* >& rFallbackMenu( GetSalData()->maFallbackMenu
);
429 // prevent duplicate insertion
430 int nItems
= rFallbackMenu
.size();
431 for( int i
= 0; i
< nItems
; i
++ )
433 if( rFallbackMenu
[i
] == pNewItem
)
437 // push the item to the back and retain it
439 rFallbackMenu
.push_back( pNewItem
);
441 if( pCurrentMenuBar
== nullptr )
445 void AquaSalMenu::removeFallbackMenuItem( NSMenuItem
* pOldItem
)
447 std::vector
< NSMenuItem
* >& rFallbackMenu( GetSalData()->maFallbackMenu
);
450 unsigned int nItems
= rFallbackMenu
.size();
451 for( unsigned int i
= 0; i
< nItems
; i
++ )
453 if( rFallbackMenu
[i
] == pOldItem
)
455 // remove item and release
456 rFallbackMenu
.erase( rFallbackMenu
.begin() + i
);
459 if( pCurrentMenuBar
== nullptr )
467 bool AquaSalMenu::VisibleMenuBar()
472 void AquaSalMenu::SetFrame( const SalFrame
*pFrame
)
474 mpFrame
= static_cast<const AquaSalFrame
*>(pFrame
);
477 void AquaSalMenu::InsertItem( SalMenuItem
* pSalMenuItem
, unsigned nPos
)
479 OSX_SALDATA_RUNINMAIN(InsertItem(pSalMenuItem
, nPos
))
481 AquaSalMenuItem
*pAquaSalMenuItem
= static_cast<AquaSalMenuItem
*>(pSalMenuItem
);
483 pAquaSalMenuItem
->mpParentMenu
= this;
484 DBG_ASSERT( pAquaSalMenuItem
->mpVCLMenu
== nullptr ||
485 pAquaSalMenuItem
->mpVCLMenu
== mpVCLMenu
||
486 mpVCLMenu
== nullptr,
487 "resetting menu ?" );
488 if( pAquaSalMenuItem
->mpVCLMenu
)
489 mpVCLMenu
= pAquaSalMenuItem
->mpVCLMenu
;
491 if( nPos
== MENU_APPEND
|| nPos
== maItems
.size() )
492 maItems
.push_back( pAquaSalMenuItem
);
493 else if( nPos
< maItems
.size() )
494 maItems
.insert( maItems
.begin() + nPos
, pAquaSalMenuItem
);
497 OSL_FAIL( "invalid item index in insert" );
501 if( ! mbMenuBar
|| pCurrentMenuBar
== this )
502 [mpMenu insertItem
: pAquaSalMenuItem
->mpMenuItem atIndex
: getItemIndexByPos(nPos
)];
505 void AquaSalMenu::RemoveItem( unsigned nPos
)
507 // tdf#160427 native menu changes can only be done on the main thread
508 OSX_SALDATA_RUNINMAIN(RemoveItem(nPos
))
510 AquaSalMenuItem
* pRemoveItem
= nullptr;
511 if( nPos
== MENU_APPEND
|| nPos
== (maItems
.size()-1) )
513 pRemoveItem
= maItems
.back();
516 else if( nPos
< maItems
.size() )
518 pRemoveItem
= maItems
[ nPos
];
519 maItems
.erase( maItems
.begin()+nPos
);
523 OSL_FAIL( "invalid item index in remove" );
527 pRemoveItem
->mpParentMenu
= nullptr;
529 if( ! mbMenuBar
|| pCurrentMenuBar
== this )
530 [mpMenu removeItemAtIndex
: getItemIndexByPos(nPos
)];
533 void AquaSalMenu::SetSubMenu( SalMenuItem
* pSalMenuItem
, SalMenu
* pSubMenu
, unsigned /*nPos*/ )
535 AquaSalMenuItem
*pAquaSalMenuItem
= static_cast<AquaSalMenuItem
*>(pSalMenuItem
);
536 AquaSalMenu
*subAquaSalMenu
= static_cast<AquaSalMenu
*>(pSubMenu
);
540 pAquaSalMenuItem
->mpSubMenu
= subAquaSalMenu
;
541 if( subAquaSalMenu
->mpParentSalMenu
== nullptr )
543 subAquaSalMenu
->mpParentSalMenu
= this;
544 [pAquaSalMenuItem
->mpMenuItem setSubmenu
: subAquaSalMenu
->mpMenu
];
546 // set title of submenu
547 [subAquaSalMenu
->mpMenu setTitle
: [pAquaSalMenuItem
->mpMenuItem title
]];
549 else if( subAquaSalMenu
->mpParentSalMenu
!= this )
551 // cocoa doesn't allow menus to be submenus of multiple
552 // menu items, so place a copy in the menu item instead ?
553 // let's hope that NSMenu copy does the right thing
554 NSMenu
* pCopy
= [subAquaSalMenu
->mpMenu copy
];
555 [pAquaSalMenuItem
->mpMenuItem setSubmenu
: pCopy
];
557 // set title of submenu
558 [pCopy setTitle
: [pAquaSalMenuItem
->mpMenuItem title
]];
563 if( pAquaSalMenuItem
->mpSubMenu
)
565 if( pAquaSalMenuItem
->mpSubMenu
->mpParentSalMenu
== this )
566 pAquaSalMenuItem
->mpSubMenu
->mpParentSalMenu
= nullptr;
568 pAquaSalMenuItem
->mpSubMenu
= nullptr;
569 [pAquaSalMenuItem
->mpMenuItem setSubmenu
: nil
];
573 void AquaSalMenu::CheckItem( unsigned nPos
, bool bCheck
)
575 if( nPos
< maItems
.size() )
577 NSMenuItem
* pItem
= maItems
[nPos
]->mpMenuItem
;
578 [pItem setState
: bCheck
? NSControlStateValueOn
: NSControlStateValueOff
];
582 void AquaSalMenu::EnableItem( unsigned nPos
, bool bEnable
)
584 if( nPos
< maItems
.size() )
586 NSMenuItem
* pItem
= maItems
[nPos
]->mpMenuItem
;
587 if( [pItem isKindOfClass
: [SalNSMenuItem
class]])
588 [static_cast<SalNSMenuItem
*>(pItem
) setReallyEnabled
: bEnable
];
590 [pItem setEnabled
: bEnable
];
594 void AquaSalMenu::SetItemImage( unsigned /*nPos*/, SalMenuItem
* pSMI
, const Image
& rImage
)
596 AquaSalMenuItem
* pSalMenuItem
= static_cast<AquaSalMenuItem
*>( pSMI
);
597 if( ! pSalMenuItem
|| ! pSalMenuItem
->mpMenuItem
)
600 NSImage
* pImage
= CreateNSImage( rImage
);
602 [pSalMenuItem
->mpMenuItem setImage
: pImage
];
607 void AquaSalMenu::SetItemText( unsigned /*i_nPos*/, SalMenuItem
* i_pSalMenuItem
, const OUString
& i_rText
)
612 AquaSalMenuItem
*pAquaSalMenuItem
= static_cast<AquaSalMenuItem
*>(i_pSalMenuItem
);
614 // Delete all mnemonics of mbMenuBar and CJK-style mnemonic
615 OUString aText
= MnemonicGenerator::EraseAllMnemonicChars(i_rText
);
617 if (aText
.endsWith("...", &aText
))
620 NSString
* pString
= CreateNSString( aText
);
623 [pAquaSalMenuItem
->mpMenuItem setTitle
: pString
];
624 // if the menu item has a submenu, change its title as well
625 if (pAquaSalMenuItem
->mpSubMenu
)
626 [pAquaSalMenuItem
->mpSubMenu
->mpMenu setTitle
: pString
];
631 void AquaSalMenu::SetAccelerator( unsigned /*nPos*/, SalMenuItem
* pSalMenuItem
, const vcl::KeyCode
& rKeyCode
, const OUString
& /*rKeyName*/ )
633 sal_uInt16 nModifier
;
634 sal_Unicode nCommandKey
= 0;
636 sal_uInt16 nKeyCode
=rKeyCode
.GetCode();
639 if ((nKeyCode
>=KEY_A
) && (nKeyCode
<=KEY_Z
)) // letter A..Z
640 nCommandKey
= nKeyCode
-KEY_A
+ 'a';
641 else if ((nKeyCode
>=KEY_0
) && (nKeyCode
<=KEY_9
)) // numbers 0..9
642 nCommandKey
= nKeyCode
-KEY_0
+ '0';
643 else if ((nKeyCode
>=KEY_F1
) && (nKeyCode
<=KEY_F26
)) // function keys F1..F26
644 nCommandKey
= nKeyCode
-KEY_F1
+ NSF1FunctionKey
;
645 else if( nKeyCode
== KEY_REPEAT
)
646 nCommandKey
= NSRedoFunctionKey
;
647 else if( nKeyCode
== KEY_SPACE
)
687 nCommandKey
=u
'\x232b';
690 nCommandKey
=u
'\x21de';
693 nCommandKey
=u
'\x21df';
696 nCommandKey
=u
'\x21e1';
699 nCommandKey
=u
'\x21e3';
702 nCommandKey
=u
'\x21a9';
704 case KEY_BRACKETLEFT
:
707 case KEY_BRACKETRIGHT
:
713 else // not even a code ? nonsense -> ignore
716 SAL_WARN_IF( !nCommandKey
, "vcl", "unmapped accelerator key" );
718 nModifier
=rKeyCode
.GetModifier();
720 // should always use the command key
721 int nItemModifier
= 0;
723 if (nModifier
& KEY_SHIFT
)
725 nItemModifier
|= NSEventModifierFlagShift
; // actually useful only for function keys
726 if( nKeyCode
>= KEY_A
&& nKeyCode
<= KEY_Z
)
727 nCommandKey
= nKeyCode
- KEY_A
+ 'A';
730 if (nModifier
& KEY_MOD1
)
731 nItemModifier
|= NSEventModifierFlagCommand
;
733 if(nModifier
& KEY_MOD2
)
734 nItemModifier
|= NSEventModifierFlagOption
;
736 if(nModifier
& KEY_MOD3
)
737 nItemModifier
|= NSEventModifierFlagControl
;
739 AquaSalMenuItem
*pAquaSalMenuItem
= static_cast<AquaSalMenuItem
*>(pSalMenuItem
);
740 NSString
* pString
= CreateNSString( OUString( &nCommandKey
, 1 ) );
741 [pAquaSalMenuItem
->mpMenuItem setKeyEquivalent
: pString
];
742 [pAquaSalMenuItem
->mpMenuItem setKeyEquivalentModifierMask
: nItemModifier
];
747 AquaSalMenu::MenuBarButtonEntry
* AquaSalMenu::findButtonItem( sal_uInt16 i_nItemId
)
749 for( size_t i
= 0; i
< maButtons
.size(); ++i
)
751 if( maButtons
[i
].maButton
.mnId
== i_nItemId
)
752 return &maButtons
[i
];
757 void AquaSalMenu::statusLayout()
759 if( GetSalData()->mpStatusItem
)
761 SAL_WNODEPRECATED_DECLARATIONS_PUSH
762 // "'view' is deprecated: first deprecated in macOS 10.14 - Use the standard button
764 NSView
* pNSView
= [GetSalData()->mpStatusItem view
];
765 SAL_WNODEPRECATED_DECLARATIONS_POP
766 if( [pNSView isMemberOfClass
: [OOStatusItemView
class]] ) // well of course it is
767 [static_cast<OOStatusItemView
*>(pNSView
) layout
];
769 OSL_FAIL( "someone stole our status view" );
773 bool AquaSalMenu::AddMenuBarButton( const SalMenuButtonItem
& i_rNewItem
)
778 MenuBarButtonEntry
* pEntry
= findButtonItem( i_rNewItem
.mnId
);
781 releaseButtonEntry( *pEntry
);
782 pEntry
->maButton
= i_rNewItem
;
783 pEntry
->mpNSImage
= CreateNSImage( i_rNewItem
.maImage
);
784 if( i_rNewItem
.maToolTipText
.getLength() )
785 pEntry
->mpToolTipString
= CreateNSString( i_rNewItem
.maToolTipText
);
789 maButtons
.push_back( MenuBarButtonEntry( i_rNewItem
) );
790 maButtons
.back().mpNSImage
= CreateNSImage( i_rNewItem
.maImage
);
791 maButtons
.back().mpToolTipString
= CreateNSString( i_rNewItem
.maToolTipText
);
794 // lazy create status item
795 SalData::getStatusItem();
797 if( pCurrentMenuBar
== this )
803 void AquaSalMenu::RemoveMenuBarButton( sal_uInt16 i_nId
)
805 MenuBarButtonEntry
* pEntry
= findButtonItem( i_nId
);
808 releaseButtonEntry( *pEntry
);
809 // note: vector guarantees that its contents are in a plain array
810 maButtons
.erase( maButtons
.begin() + (pEntry
- maButtons
.data()) );
813 if( pCurrentMenuBar
== this )
817 tools::Rectangle
AquaSalMenu::GetMenuBarButtonRectPixel( sal_uInt16 i_nItemId
, SalFrame
* i_pReferenceFrame
)
819 if( ! i_pReferenceFrame
|| ! AquaSalFrame::isAlive( static_cast<AquaSalFrame
*>(i_pReferenceFrame
) ) )
820 return tools::Rectangle();
822 MenuBarButtonEntry
* pEntry
= findButtonItem( i_nItemId
);
825 return tools::Rectangle();
827 NSStatusItem
* pItem
= SalData::getStatusItem();
829 return tools::Rectangle();
831 SAL_WNODEPRECATED_DECLARATIONS_PUSH
832 // "'view' is deprecated: first deprecated in macOS 10.14 - Use the standard button property
834 NSView
* pNSView
= [pItem view
];
835 SAL_WNODEPRECATED_DECLARATIONS_POP
837 return tools::Rectangle();
838 NSWindow
* pNSWin
= [pNSView window
];
840 return tools::Rectangle();
842 NSRect aRect
= [pNSWin convertRectToScreen
:[pNSWin frame
]];
844 // make coordinates relative to reference frame
845 static_cast<AquaSalFrame
*>(i_pReferenceFrame
)->CocoaToVCL( aRect
.origin
);
846 aRect
.origin
.x
-= i_pReferenceFrame
->GetUnmirroredGeometry().x();
847 aRect
.origin
.y
-= i_pReferenceFrame
->GetUnmirroredGeometry().y() + aRect
.size
.height
;
849 return tools::Rectangle( Point(static_cast<tools::Long
>(aRect
.origin
.x
),
850 static_cast<tools::Long
>(aRect
.origin
.y
)
852 Size( static_cast<tools::Long
>(aRect
.size
.width
),
853 static_cast<tools::Long
>(aRect
.size
.height
)
862 AquaSalMenuItem::AquaSalMenuItem( const SalItemParams
* pItemData
) :
863 mnId( pItemData
->nId
),
864 mpVCLMenu( pItemData
->pMenu
),
865 mpParentMenu( nullptr ),
866 mpSubMenu( nullptr ),
869 if (pItemData
->eType
== MenuItemType::SEPARATOR
)
871 mpMenuItem
= [NSMenuItem separatorItem
];
872 // these can go occasionally go in and out of a menu, ensure their lifecycle
873 // also for the release in AquaSalMenuItem destructor
878 mpMenuItem
= [[SalNSMenuItem alloc
] initWithMenuItem
: this];
879 [static_cast<SalNSMenuItem
*>(mpMenuItem
) setReallyEnabled
: YES
];
881 // peel mnemonics because on mac there are no such things for menu items
882 // Delete CJK-style mnemonics for the dropdown menu of the 'New button' and lower menu of 'File > New'
883 NSString
* pString
= CreateNSString(MnemonicGenerator::EraseAllMnemonicChars(pItemData
->aText
));
886 [mpMenuItem setTitle
: pString
];
889 // anything but a separator should set a menu to dispatch to
890 SAL_WARN_IF( !mpVCLMenu
, "vcl", "no menu" );
894 AquaSalMenuItem::~AquaSalMenuItem()
897 using [autorelease] here (and in AquaSalMenu:::~AquaSalMenu) instead of
898 [release] fixes an occasional crash. That should indicate that we release
899 menus / menu items in the wrong order somewhere, but I
900 could not find that case.
903 [mpMenuItem autorelease
];
906 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */