summaryrefslogtreecommitdiffstats
path: root/vcl/osx/salnsmenu.mm
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--vcl/osx/salnsmenu.mm261
1 files changed, 261 insertions, 0 deletions
diff --git a/vcl/osx/salnsmenu.mm b/vcl/osx/salnsmenu.mm
new file mode 100644
index 000000000..b2df2da7e
--- /dev/null
+++ b/vcl/osx/salnsmenu.mm
@@ -0,0 +1,261 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <osl/diagnose.h>
+
+#include <vcl/window.hxx>
+
+#include <osx/salinst.h>
+#include <osx/saldata.hxx>
+#include <osx/salframe.h>
+#include <osx/salframeview.h>
+#include <osx/salmenu.h>
+#include <osx/salnsmenu.h>
+
+@implementation SalNSMenu
+-(id)initWithMenu: (AquaSalMenu*)pMenu
+{
+ mpMenu = pMenu;
+ return [super initWithTitle: [NSString string]];
+}
+
+-(void)menuNeedsUpdate: (NSMenu*)pMenu
+{
+ SolarMutexGuard aGuard;
+
+ if( mpMenu )
+ {
+ const AquaSalFrame* pFrame = mpMenu->getFrame();
+ if( pFrame && AquaSalFrame::isAlive( pFrame ) )
+ {
+ SalMenuEvent aMenuEvt;
+ aMenuEvt.mnId = 0;
+ aMenuEvt.mpMenu = mpMenu->mpVCLMenu;
+ if( aMenuEvt.mpMenu )
+ {
+ pFrame->CallCallback(SalEvent::MenuActivate, &aMenuEvt);
+ pFrame->CallCallback(SalEvent::MenuDeactivate, &aMenuEvt);
+ }
+ else
+ OSL_FAIL( "unconnected menu" );
+ }
+ else if( mpMenu->mpVCLMenu )
+ {
+ mpMenu->mpVCLMenu->Activate();
+ mpMenu->mpVCLMenu->Deactivate();
+
+ // Hide disabled items
+ NSArray* elements = [pMenu itemArray];
+ NSEnumerator* it = [elements objectEnumerator];
+ id element;
+ while ( ( element = [it nextObject] ) != nil )
+ {
+ NSMenuItem* item = static_cast< NSMenuItem* >( element );
+ if( ![item isSeparatorItem] )
+ [item setHidden: ![item isEnabled]];
+ }
+ }
+ }
+}
+
+-(void)setSalMenu: (AquaSalMenu*)pMenu
+{
+ mpMenu = pMenu;
+}
+@end
+
+@implementation SalNSMenuItem
+-(id)initWithMenuItem: (AquaSalMenuItem*)pMenuItem
+{
+ mpMenuItem = pMenuItem;
+ id ret = [super initWithTitle: [NSString string]
+ action: @selector(menuItemTriggered:)
+ keyEquivalent: [NSString string]];
+ [ret setTarget: self];
+ return ret;
+}
+-(void)menuItemTriggered: (id)aSender
+{
+ (void)aSender;
+ SolarMutexGuard aGuard;
+
+ // Commit uncommitted text before dispatching the selecting menu item. In
+ // certain cases such as selecting the Insert > Comment menu item in a
+ // Writer document while there is uncommitted text will call
+ // AquaSalFrame::EndExtTextInput() which will dispatch a
+ // SalEvent::EndExtTextInput event. Writer's handler for that event will
+ // delete the uncommitted text and then insert the committed text but
+ // LibreOffice will crash when deleting the uncommitted text because
+ // deletion of the text also removes and deletes the newly inserted
+ // comment.
+ NSWindow* pKeyWin = [NSApp keyWindow];
+ if( pKeyWin && [pKeyWin isKindOfClass: [SalFrameWindow class]] )
+ [static_cast<SalFrameWindow*>(pKeyWin) endExtTextInput];
+
+ // tdf#49853 Keyboard shortcuts are also handled by the menu bar, but at least some of them
+ // must still end up in the view. This is necessary to handle common edit actions in docked
+ // windows (e.g. in toolbar fields).
+ NSEvent* pEvent = [NSApp currentEvent];
+ if( pEvent && [pEvent type] == NSEventTypeKeyDown )
+ {
+ unsigned int nModMask = ([pEvent modifierFlags] & (NSEventModifierFlagShift|NSEventModifierFlagControl|NSEventModifierFlagOption|NSEventModifierFlagCommand));
+ NSString* charactersIgnoringModifiers = [pEvent charactersIgnoringModifiers];
+ if( nModMask == NSEventModifierFlagCommand &&
+ ( [charactersIgnoringModifiers isEqualToString: @"v"] ||
+ [charactersIgnoringModifiers isEqualToString: @"c"] ||
+ [charactersIgnoringModifiers isEqualToString: @"x"] ||
+ [charactersIgnoringModifiers isEqualToString: @"a"] ||
+ [charactersIgnoringModifiers isEqualToString: @"z"] ) )
+ {
+ [[[NSApp keyWindow] contentView] keyDown: pEvent];
+ return;
+ }
+ }
+
+ const AquaSalFrame* pFrame = mpMenuItem->mpParentMenu ? mpMenuItem->mpParentMenu->getFrame() : nullptr;
+ if( pFrame && AquaSalFrame::isAlive( pFrame ) && ! pFrame->GetWindow()->IsInModalMode() )
+ {
+ SalMenuEvent aMenuEvt( mpMenuItem->mnId, mpMenuItem->mpVCLMenu );
+ pFrame->CallCallback(SalEvent::MenuCommand, &aMenuEvt);
+ }
+ else if( mpMenuItem->mpVCLMenu )
+ {
+ // if an item from submenu was selected. the corresponding Window does not exist because
+ // we use native popup menus, so we have to set the selected menuitem directly
+ // incidentally this of course works for top level popup menus, too
+ PopupMenu * pPopupMenu = dynamic_cast<PopupMenu *>(mpMenuItem->mpVCLMenu.get());
+ if( pPopupMenu )
+ {
+ // FIXME: revise this ugly code
+
+ // select handlers in vcl are dispatch on the original menu
+ // if not consumed by the select handler of the current menu
+ // however since only the starting menu ever came into Execute
+ // the hierarchy is not build up. Workaround this by getting
+ // the menu it should have been
+
+ // get started from hierarchy in vcl menus
+ AquaSalMenu* pParentMenu = mpMenuItem->mpParentMenu;
+ Menu* pCurMenu = mpMenuItem->mpVCLMenu;
+ while( pParentMenu && pParentMenu->mpVCLMenu )
+ {
+ pCurMenu = pParentMenu->mpVCLMenu;
+ pParentMenu = pParentMenu->mpParentSalMenu;
+ }
+
+ pPopupMenu->SetSelectedEntry( mpMenuItem->mnId );
+ pPopupMenu->ImplSelectWithStart( pCurMenu );
+ }
+ else
+ OSL_FAIL( "menubar item without frame !" );
+ }
+}
+@end
+
+@implementation OOStatusItemView
+-(void)drawRect: (NSRect)aRect
+{
+ NSGraphicsContext* pContext = [NSGraphicsContext currentContext];
+ [pContext saveGraphicsState];
+SAL_WNODEPRECATED_DECLARATIONS_PUSH
+ // "'drawStatusBarBackgroundInRect:withHighlight:' is deprecated: first deprecated in macOS
+ // 10.14 - Use the standard button instead which handles highlight drawing, making this
+ // method obsolete"
+ [SalData::getStatusItem() drawStatusBarBackgroundInRect: aRect withHighlight: NO];
+SAL_WNODEPRECATED_DECLARATIONS_POP
+ if( AquaSalMenu::pCurrentMenuBar )
+ {
+ const std::vector< AquaSalMenu::MenuBarButtonEntry >& rButtons( AquaSalMenu::pCurrentMenuBar->getButtons() );
+ NSRect aFrame = [self frame];
+ NSRect aImgRect = { { 2, 0 }, { 0, 0 } };
+ for( size_t i = 0; i < rButtons.size(); ++i )
+ {
+ const Size aPixSize = rButtons[i].maButton.maImage.GetSizePixel();
+ const NSRect aFromRect = { NSZeroPoint, NSMakeSize( aPixSize.Width(), aPixSize.Height()) };
+ aImgRect.origin.y = floor((aFrame.size.height - aFromRect.size.height)/2);
+ aImgRect.size = aFromRect.size;
+ if( rButtons[i].mpNSImage )
+ [rButtons[i].mpNSImage drawInRect: aImgRect fromRect: aFromRect operation: NSCompositingOperationSourceOver fraction: 1.0];
+ aImgRect.origin.x += aFromRect.size.width + 2;
+ }
+ }
+ [pContext restoreGraphicsState];
+}
+
+-(void)mouseUp: (NSEvent *)pEvent
+{
+ /* check if button goes up inside one of our status buttons */
+ if( AquaSalMenu::pCurrentMenuBar )
+ {
+ const std::vector< AquaSalMenu::MenuBarButtonEntry >& rButtons( AquaSalMenu::pCurrentMenuBar->getButtons() );
+ NSRect aFrame = [self frame];
+ NSRect aImgRect = { { 2, 0 }, { 0, 0 } };
+ NSPoint aMousePt = [pEvent locationInWindow];
+ for( size_t i = 0; i < rButtons.size(); ++i )
+ {
+ const Size aPixSize = rButtons[i].maButton.maImage.GetSizePixel();
+ const NSRect aFromRect = { NSZeroPoint, NSMakeSize( aPixSize.Width(), aPixSize.Height()) };
+ aImgRect.origin.y = (aFrame.size.height - aFromRect.size.height)/2;
+ aImgRect.size = aFromRect.size;
+ if( aMousePt.x >= aImgRect.origin.x && aMousePt.x <= (aImgRect.origin.x+aImgRect.size.width) &&
+ aMousePt.y >= aImgRect.origin.y && aMousePt.y <= (aImgRect.origin.y+aImgRect.size.height) )
+ {
+ if( AquaSalMenu::pCurrentMenuBar->mpFrame && AquaSalFrame::isAlive( AquaSalMenu::pCurrentMenuBar->mpFrame ) )
+ {
+ SalMenuEvent aMenuEvt( rButtons[i].maButton.mnId, AquaSalMenu::pCurrentMenuBar->mpVCLMenu );
+ AquaSalMenu::pCurrentMenuBar->mpFrame->CallCallback(SalEvent::MenuButtonCommand, &aMenuEvt);
+ }
+ return;
+ }
+
+ aImgRect.origin.x += aFromRect.size.width + 2;
+ }
+ }
+}
+
+-(void)layout
+{
+ NSStatusBar* pStatBar = [NSStatusBar systemStatusBar];
+ NSSize aSize = { 0, [pStatBar thickness] };
+ [self removeAllToolTips];
+ if( AquaSalMenu::pCurrentMenuBar )
+ {
+ const std::vector< AquaSalMenu::MenuBarButtonEntry >& rButtons( AquaSalMenu::pCurrentMenuBar->getButtons() );
+ if( ! rButtons.empty() )
+ {
+ aSize.width = 2;
+ for( size_t i = 0; i < rButtons.size(); ++i )
+ {
+ NSRect aImgRect = { { aSize.width,
+ static_cast<CGFloat>(floor((aSize.height-rButtons[i].maButton.maImage.GetSizePixel().Height())/2)) },
+ { static_cast<CGFloat>(rButtons[i].maButton.maImage.GetSizePixel().Width()),
+ static_cast<CGFloat>(rButtons[i].maButton.maImage.GetSizePixel().Height()) } };
+ if( rButtons[i].mpToolTipString )
+ [self addToolTipRect: aImgRect owner: rButtons[i].mpToolTipString userData: nullptr];
+ aSize.width += 2 + aImgRect.size.width;
+ }
+ }
+ }
+ [self setFrameSize: aSize];
+}
+@end
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */