1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2/* 3 * This file is part of the LibreOffice project. 4 * 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/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 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 . 18 */ 19 20#include <sal/config.h> 21#include <osl/diagnose.h> 22 23#include <vcl/window.hxx> 24 25#include <osx/salinst.h> 26#include <osx/saldata.hxx> 27#include <osx/salframe.h> 28#include <osx/salmenu.h> 29#include <osx/salnsmenu.h> 30 31@implementation SalNSMenu 32-(id)initWithMenu: (AquaSalMenu*)pMenu 33{ 34 mpMenu = pMenu; 35 return [super initWithTitle: [NSString string]]; 36} 37 38-(void)menuNeedsUpdate: (NSMenu*)pMenu 39{ 40 SolarMutexGuard aGuard; 41 42 if( mpMenu ) 43 { 44 const AquaSalFrame* pFrame = mpMenu->getFrame(); 45 if( pFrame && AquaSalFrame::isAlive( pFrame ) ) 46 { 47 SalMenuEvent aMenuEvt; 48 aMenuEvt.mnId = 0; 49 aMenuEvt.mpMenu = mpMenu->mpVCLMenu; 50 if( aMenuEvt.mpMenu ) 51 { 52 pFrame->CallCallback(SalEvent::MenuActivate, &aMenuEvt); 53 pFrame->CallCallback(SalEvent::MenuDeactivate, &aMenuEvt); 54 } 55 else 56 OSL_FAIL( "unconnected menu" ); 57 } 58 else if( mpMenu->mpVCLMenu ) 59 { 60 mpMenu->mpVCLMenu->Activate(); 61 mpMenu->mpVCLMenu->Deactivate(); 62 63 // Hide disabled items 64 NSArray* elements = [pMenu itemArray]; 65 NSEnumerator* it = [elements objectEnumerator]; 66 id element; 67 while ( ( element = [it nextObject] ) != nil ) 68 { 69 NSMenuItem* item = static_cast< NSMenuItem* >( element ); 70 if( ![item isSeparatorItem] ) 71 [item setHidden: ![item isEnabled]]; 72 } 73 } 74 } 75} 76 77-(void)setSalMenu: (AquaSalMenu*)pMenu 78{ 79 mpMenu = pMenu; 80} 81@end 82 83@implementation SalNSMenuItem 84-(id)initWithMenuItem: (AquaSalMenuItem*)pMenuItem 85{ 86 mpMenuItem = pMenuItem; 87 id ret = [super initWithTitle: [NSString string] 88 action: @selector(menuItemTriggered:) 89 keyEquivalent: [NSString string]]; 90 [ret setTarget: self]; 91 return ret; 92} 93-(void)menuItemTriggered: (id)aSender 94{ 95 (void)aSender; 96 SolarMutexGuard aGuard; 97 98 // tdf#49853 Keyboard shortcuts are also handled by the menu bar, but at least some of them 99 // must still end up in the view. This is necessary to handle common edit actions in docked 100 // windows (e.g. in toolbar fields). 101 NSEvent* pEvent = [NSApp currentEvent]; 102SAL_WNODEPRECATED_DECLARATIONS_PUSH 103 // 'NSAlternateKeyMask' is deprecated: first deprecated in macOS 10.12 104 // 'NSCommandKeyMask' is deprecated: first deprecated in macOS 10.12 105 // 'NSControlKeyMask' is deprecated: first deprecated in macOS 10.12 106 // 'NSKeyDown' is deprecated: first deprecated in macOS 10.12 107 // 'NSShiftKeyMask' is deprecated: first deprecated in macOS 10.12 108 if( pEvent && [pEvent type] == NSKeyDown ) 109 { 110 unsigned int nModMask = ([pEvent modifierFlags] & (NSShiftKeyMask|NSControlKeyMask|NSAlternateKeyMask|NSCommandKeyMask)); 111 NSString* charactersIgnoringModifiers = [pEvent charactersIgnoringModifiers]; 112 if( nModMask == NSCommandKeyMask && 113 ( [charactersIgnoringModifiers isEqualToString: @"v"] || 114 [charactersIgnoringModifiers isEqualToString: @"c"] || 115 [charactersIgnoringModifiers isEqualToString: @"x"] || 116 [charactersIgnoringModifiers isEqualToString: @"a"] || 117 [charactersIgnoringModifiers isEqualToString: @"z"] ) ) 118 { 119 [[[NSApp keyWindow] contentView] keyDown: pEvent]; 120 return; 121 } 122 } 123SAL_WNODEPRECATED_DECLARATIONS_POP 124 125 const AquaSalFrame* pFrame = mpMenuItem->mpParentMenu ? mpMenuItem->mpParentMenu->getFrame() : nullptr; 126 if( pFrame && AquaSalFrame::isAlive( pFrame ) && ! pFrame->GetWindow()->IsInModalMode() ) 127 { 128 SalMenuEvent aMenuEvt( mpMenuItem->mnId, mpMenuItem->mpVCLMenu ); 129 pFrame->CallCallback(SalEvent::MenuCommand, &aMenuEvt); 130 } 131 else if( mpMenuItem->mpVCLMenu ) 132 { 133 // if an item from submenu was selected. the corresponding Window does not exist because 134 // we use native popup menus, so we have to set the selected menuitem directly 135 // incidentally this of course works for top level popup menus, too 136 PopupMenu * pPopupMenu = dynamic_cast<PopupMenu *>(mpMenuItem->mpVCLMenu.get()); 137 if( pPopupMenu ) 138 { 139 // FIXME: revise this ugly code 140 141 // select handlers in vcl are dispatch on the original menu 142 // if not consumed by the select handler of the current menu 143 // however since only the starting menu ever came into Execute 144 // the hierarchy is not build up. Workaround this by getting 145 // the menu it should have been 146 147 // get started from hierarchy in vcl menus 148 AquaSalMenu* pParentMenu = mpMenuItem->mpParentMenu; 149 Menu* pCurMenu = mpMenuItem->mpVCLMenu; 150 while( pParentMenu && pParentMenu->mpVCLMenu ) 151 { 152 pCurMenu = pParentMenu->mpVCLMenu; 153 pParentMenu = pParentMenu->mpParentSalMenu; 154 } 155 156 pPopupMenu->SetSelectedEntry( mpMenuItem->mnId ); 157 pPopupMenu->ImplSelectWithStart( pCurMenu ); 158 } 159 else 160 OSL_FAIL( "menubar item without frame !" ); 161 } 162} 163@end 164 165@implementation OOStatusItemView 166-(void)drawRect: (NSRect)aRect 167{ 168 NSGraphicsContext* pContext = [NSGraphicsContext currentContext]; 169 [pContext saveGraphicsState]; 170SAL_WNODEPRECATED_DECLARATIONS_PUSH 171 // "'drawStatusBarBackgroundInRect:withHighlight:' is deprecated: first deprecated in macOS 172 // 10.14 - Use the standard button instead which handles highlight drawing, making this 173 // method obsolete" 174 [SalData::getStatusItem() drawStatusBarBackgroundInRect: aRect withHighlight: NO]; 175SAL_WNODEPRECATED_DECLARATIONS_POP 176 if( AquaSalMenu::pCurrentMenuBar ) 177 { 178 const std::vector< AquaSalMenu::MenuBarButtonEntry >& rButtons( AquaSalMenu::pCurrentMenuBar->getButtons() ); 179 NSRect aFrame = [self frame]; 180 NSRect aImgRect = { { 2, 0 }, { 0, 0 } }; 181 for( size_t i = 0; i < rButtons.size(); ++i ) 182 { 183 const Size aPixSize = rButtons[i].maButton.maImage.GetSizePixel(); 184 const NSRect aFromRect = { NSZeroPoint, NSMakeSize( aPixSize.Width(), aPixSize.Height()) }; 185 aImgRect.origin.y = floor((aFrame.size.height - aFromRect.size.height)/2); 186 aImgRect.size = aFromRect.size; 187 if( rButtons[i].mpNSImage ) 188SAL_WNODEPRECATED_DECLARATIONS_PUSH 189 // 'NSCompositeSourceOver' is deprecated: first deprecated in macOS 10.12 190 [rButtons[i].mpNSImage drawInRect: aImgRect fromRect: aFromRect operation: NSCompositeSourceOver fraction: 1.0]; 191SAL_WNODEPRECATED_DECLARATIONS_POP 192 aImgRect.origin.x += aFromRect.size.width + 2; 193 } 194 } 195 [pContext restoreGraphicsState]; 196} 197 198-(void)mouseUp: (NSEvent *)pEvent 199{ 200 /* check if button goes up inside one of our status buttons */ 201 if( AquaSalMenu::pCurrentMenuBar ) 202 { 203 const std::vector< AquaSalMenu::MenuBarButtonEntry >& rButtons( AquaSalMenu::pCurrentMenuBar->getButtons() ); 204 NSRect aFrame = [self frame]; 205 NSRect aImgRect = { { 2, 0 }, { 0, 0 } }; 206 NSPoint aMousePt = [pEvent locationInWindow]; 207 for( size_t i = 0; i < rButtons.size(); ++i ) 208 { 209 const Size aPixSize = rButtons[i].maButton.maImage.GetSizePixel(); 210 const NSRect aFromRect = { NSZeroPoint, NSMakeSize( aPixSize.Width(), aPixSize.Height()) }; 211 aImgRect.origin.y = (aFrame.size.height - aFromRect.size.height)/2; 212 aImgRect.size = aFromRect.size; 213 if( aMousePt.x >= aImgRect.origin.x && aMousePt.x <= (aImgRect.origin.x+aImgRect.size.width) && 214 aMousePt.y >= aImgRect.origin.y && aMousePt.y <= (aImgRect.origin.y+aImgRect.size.height) ) 215 { 216 if( AquaSalMenu::pCurrentMenuBar->mpFrame && AquaSalFrame::isAlive( AquaSalMenu::pCurrentMenuBar->mpFrame ) ) 217 { 218 SalMenuEvent aMenuEvt( rButtons[i].maButton.mnId, AquaSalMenu::pCurrentMenuBar->mpVCLMenu ); 219 AquaSalMenu::pCurrentMenuBar->mpFrame->CallCallback(SalEvent::MenuButtonCommand, &aMenuEvt); 220 } 221 return; 222 } 223 224 aImgRect.origin.x += aFromRect.size.width + 2; 225 } 226 } 227} 228 229-(void)layout 230{ 231 NSStatusBar* pStatBar = [NSStatusBar systemStatusBar]; 232 NSSize aSize = { 0, [pStatBar thickness] }; 233 [self removeAllToolTips]; 234 if( AquaSalMenu::pCurrentMenuBar ) 235 { 236 const std::vector< AquaSalMenu::MenuBarButtonEntry >& rButtons( AquaSalMenu::pCurrentMenuBar->getButtons() ); 237 if( ! rButtons.empty() ) 238 { 239 aSize.width = 2; 240 for( size_t i = 0; i < rButtons.size(); ++i ) 241 { 242 NSRect aImgRect = { { aSize.width, 243 static_cast<CGFloat>(floor((aSize.height-rButtons[i].maButton.maImage.GetSizePixel().Height())/2)) }, 244 { static_cast<CGFloat>(rButtons[i].maButton.maImage.GetSizePixel().Width()), 245 static_cast<CGFloat>(rButtons[i].maButton.maImage.GetSizePixel().Height()) } }; 246 if( rButtons[i].mpToolTipString ) 247 [self addToolTipRect: aImgRect owner: rButtons[i].mpToolTipString userData: nullptr]; 248 aSize.width += 2 + aImgRect.size.width; 249 } 250 } 251 } 252 [self setFrameSize: aSize]; 253} 254@end 255 256 257/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 258