/* -*- 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 #include #include #include #include #include #include @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; // 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]; SAL_WNODEPRECATED_DECLARATIONS_PUSH // 'NSAlternateKeyMask' is deprecated: first deprecated in macOS 10.12 // 'NSCommandKeyMask' is deprecated: first deprecated in macOS 10.12 // 'NSControlKeyMask' is deprecated: first deprecated in macOS 10.12 // 'NSKeyDown' is deprecated: first deprecated in macOS 10.12 // 'NSShiftKeyMask' is deprecated: first deprecated in macOS 10.12 if( pEvent && [pEvent type] == NSKeyDown ) { unsigned int nModMask = ([pEvent modifierFlags] & (NSShiftKeyMask|NSControlKeyMask|NSAlternateKeyMask|NSCommandKeyMask)); NSString* charactersIgnoringModifiers = [pEvent charactersIgnoringModifiers]; if( nModMask == NSCommandKeyMask && ( [charactersIgnoringModifiers isEqualToString: @"v"] || [charactersIgnoringModifiers isEqualToString: @"c"] || [charactersIgnoringModifiers isEqualToString: @"x"] || [charactersIgnoringModifiers isEqualToString: @"a"] || [charactersIgnoringModifiers isEqualToString: @"z"] ) ) { [[[NSApp keyWindow] contentView] keyDown: pEvent]; return; } } SAL_WNODEPRECATED_DECLARATIONS_POP 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(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]; [SalData::getStatusItem() drawStatusBarBackgroundInRect: aRect withHighlight: NO]; 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 ) SAL_WNODEPRECATED_DECLARATIONS_PUSH // 'NSCompositeSourceOver' is deprecated: first deprecated in macOS 10.12 [rButtons[i].mpNSImage drawInRect: aImgRect fromRect: aFromRect operation: NSCompositeSourceOver fraction: 1.0]; SAL_WNODEPRECATED_DECLARATIONS_POP 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(floor((aSize.height-rButtons[i].maButton.maImage.GetSizePixel().Height())/2)) }, { static_cast(rButtons[i].maButton.maImage.GetSizePixel().Width()), static_cast(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: */