Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / vcl / osx / salnsmenu.mm
blobfb1f7fb5d4d3de8ef0eb0d61625e4da2c87ba76e
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  */
20 #include <sal/config.h>
22 #include <vcl/window.hxx>
24 #include "osx/salinst.h"
25 #include "osx/saldata.hxx"
26 #include "osx/salframe.h"
27 #include "osx/salmenu.h"
28 #include "osx/salnsmenu.h"
30 @implementation SalNSMenu
31 -(id)initWithMenu: (AquaSalMenu*)pMenu
33     mpMenu = pMenu;
34     return [super initWithTitle: [NSString string]];
37 -(void)menuNeedsUpdate: (NSMenu*)pMenu
39     (void)pMenu;
40     SolarMutexGuard aGuard;
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();
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     }
77 -(void)setSalMenu: (AquaSalMenu*)pMenu
79     mpMenu = pMenu;
81 @end
83 @implementation SalNSMenuItem
84 -(id)initWithMenuItem: (AquaSalMenuItem*)pMenuItem
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;
93 -(void)menuItemTriggered: (id)aSender
95     (void)aSender;
96     SolarMutexGuard aGuard;
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];
102 SAL_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     }
123 SAL_WNODEPRECATED_DECLARATIONS_POP
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
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
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             }
156             pPopupMenu->SetSelectedEntry( mpMenuItem->mnId );
157             pPopupMenu->ImplSelectWithStart( pCurMenu );
158         }
159         else
160             OSL_FAIL( "menubar item without frame !" );
161     }
163 @end
165 @implementation OOStatusItemView
166 -(void)drawRect: (NSRect)aRect
168     NSGraphicsContext* pContext = [NSGraphicsContext currentContext];
169     [pContext saveGraphicsState];
170     [SalData::getStatusItem() drawStatusBarBackgroundInRect: aRect withHighlight: NO];
171     if( AquaSalMenu::pCurrentMenuBar )
172     {
173         const std::vector< AquaSalMenu::MenuBarButtonEntry >& rButtons( AquaSalMenu::pCurrentMenuBar->getButtons() );
174         NSRect aFrame = [self frame];
175         NSRect aImgRect = { { 2, 0 }, { 0, 0 } };
176         for( size_t i = 0; i < rButtons.size(); ++i )
177         {
178             const Size aPixSize = rButtons[i].maButton.maImage.GetSizePixel();
179             const NSRect aFromRect = { NSZeroPoint, NSMakeSize( aPixSize.Width(), aPixSize.Height()) };
180             aImgRect.origin.y = floor((aFrame.size.height - aFromRect.size.height)/2);
181             aImgRect.size = aFromRect.size;
182             if( rButtons[i].mpNSImage )
183 SAL_WNODEPRECATED_DECLARATIONS_PUSH
184     // 'NSCompositeSourceOver' is deprecated: first deprecated in macOS 10.12
185                 [rButtons[i].mpNSImage drawInRect: aImgRect fromRect: aFromRect operation: NSCompositeSourceOver fraction: 1.0];
186 SAL_WNODEPRECATED_DECLARATIONS_POP
187             aImgRect.origin.x += aFromRect.size.width + 2;
188         }
189     }
190     [pContext restoreGraphicsState];
193 -(void)mouseUp: (NSEvent *)pEvent
195     /* check if button goes up inside one of our status buttons */
196     if( AquaSalMenu::pCurrentMenuBar )
197     {
198         const std::vector< AquaSalMenu::MenuBarButtonEntry >& rButtons( AquaSalMenu::pCurrentMenuBar->getButtons() );
199         NSRect aFrame = [self frame];
200         NSRect aImgRect = { { 2, 0 }, { 0, 0 } };
201         NSPoint aMousePt = [pEvent locationInWindow];
202         for( size_t i = 0; i < rButtons.size(); ++i )
203         {
204             const Size aPixSize = rButtons[i].maButton.maImage.GetSizePixel();
205             const NSRect aFromRect = { NSZeroPoint, NSMakeSize( aPixSize.Width(), aPixSize.Height()) };
206             aImgRect.origin.y = (aFrame.size.height - aFromRect.size.height)/2;
207             aImgRect.size = aFromRect.size;
208             if( aMousePt.x >= aImgRect.origin.x && aMousePt.x <= (aImgRect.origin.x+aImgRect.size.width) &&
209                 aMousePt.y >= aImgRect.origin.y && aMousePt.y <= (aImgRect.origin.y+aImgRect.size.height) )
210             {
211                 if( AquaSalMenu::pCurrentMenuBar->mpFrame && AquaSalFrame::isAlive( AquaSalMenu::pCurrentMenuBar->mpFrame ) )
212                 {
213                     SalMenuEvent aMenuEvt( rButtons[i].maButton.mnId, AquaSalMenu::pCurrentMenuBar->mpVCLMenu );
214                     AquaSalMenu::pCurrentMenuBar->mpFrame->CallCallback(SalEvent::MenuButtonCommand, &aMenuEvt);
215                 }
216                 return;
217             }
219             aImgRect.origin.x += aFromRect.size.width + 2;
220         }
221     }
224 -(void)layout
226     NSStatusBar* pStatBar = [NSStatusBar systemStatusBar];
227     NSSize aSize = { 0, [pStatBar thickness] };
228     [self removeAllToolTips];
229     if( AquaSalMenu::pCurrentMenuBar )
230     {
231         const std::vector< AquaSalMenu::MenuBarButtonEntry >& rButtons( AquaSalMenu::pCurrentMenuBar->getButtons() );
232         if( ! rButtons.empty() )
233         {
234             aSize.width = 2;
235             for( size_t i = 0; i < rButtons.size(); ++i )
236             {
237                 NSRect aImgRect = { { aSize.width,
238                                       static_cast<CGFloat>(floor((aSize.height-rButtons[i].maButton.maImage.GetSizePixel().Height())/2)) },
239                                     { static_cast<CGFloat>(rButtons[i].maButton.maImage.GetSizePixel().Width()),
240                                       static_cast<CGFloat>(rButtons[i].maButton.maImage.GetSizePixel().Height()) } };
241                 if( rButtons[i].mpToolTipString )
242                     [self addToolTipRect: aImgRect owner: rButtons[i].mpToolTipString userData: nullptr];
243                 aSize.width += 2 + aImgRect.size.width;
244             }
245         }
246     }
247     [self setFrameSize: aSize];
249 @end
252 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */