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 <osl/diagnose.h>
23 #include <vcl/window.hxx>
25 #include <osx/salinst.h>
26 #include <osx/saldata.hxx>
27 #include <osx/salframe.h>
28 #include <osx/salframeview.h>
29 #include <osx/salmenu.h>
30 #include <osx/salnsmenu.h>
32 @implementation SalNSMenu
33 -(id)initWithMenu: (AquaSalMenu*)pMenu
36 return [super initWithTitle: [NSString string]];
39 -(void)menuNeedsUpdate: (NSMenu*)pMenu
41 SolarMutexGuard aGuard;
45 const AquaSalFrame* pFrame = mpMenu->getFrame();
46 if( pFrame && AquaSalFrame::isAlive( pFrame ) )
48 SalMenuEvent aMenuEvt;
50 aMenuEvt.mpMenu = mpMenu->mpVCLMenu;
53 pFrame->CallCallback(SalEvent::MenuActivate, &aMenuEvt);
54 pFrame->CallCallback(SalEvent::MenuDeactivate, &aMenuEvt);
57 OSL_FAIL( "unconnected menu" );
59 else if( mpMenu->mpVCLMenu )
61 mpMenu->mpVCLMenu->Activate();
62 mpMenu->mpVCLMenu->Deactivate();
64 // Hide disabled items
65 NSArray* elements = [pMenu itemArray];
66 NSEnumerator* it = [elements objectEnumerator];
68 while ( ( element = [it nextObject] ) != nil )
70 NSMenuItem* item = static_cast< NSMenuItem* >( element );
71 if( ![item isSeparatorItem] )
72 [item setHidden: ![item isEnabled]];
78 -(void)setSalMenu: (AquaSalMenu*)pMenu
84 @implementation SalNSMenuItem
85 -(id)initWithMenuItem: (AquaSalMenuItem*)pMenuItem
87 mpMenuItem = pMenuItem;
88 id ret = [super initWithTitle: [NSString string]
89 action: @selector(menuItemTriggered:)
90 keyEquivalent: [NSString string]];
91 [ret setTarget: self];
94 -(void)menuItemTriggered: (id)aSender
97 SolarMutexGuard aGuard;
99 // Commit uncommitted text before dispatching the selecting menu item. In
100 // certain cases such as selecting the Insert > Comment menu item in a
101 // Writer document while there is uncommitted text will call
102 // AquaSalFrame::EndExtTextInput() which will dispatch a
103 // SalEvent::EndExtTextInput event. Writer's handler for that event will
104 // delete the uncommitted text and then insert the committed text but
105 // LibreOffice will crash when deleting the uncommitted text because
106 // deletion of the text also removes and deletes the newly inserted
108 NSWindow* pKeyWin = [NSApp keyWindow];
109 if( pKeyWin && [pKeyWin isKindOfClass: [SalFrameWindow class]] )
110 [static_cast<SalFrameWindow*>(pKeyWin) endExtTextInput];
112 // tdf#49853 Keyboard shortcuts are also handled by the menu bar, but at least some of them
113 // must still end up in the view. This is necessary to handle common edit actions in docked
114 // windows (e.g. in toolbar fields).
115 NSEvent* pEvent = [NSApp currentEvent];
116 if( pEvent && [pEvent type] == NSEventTypeKeyDown )
118 unsigned int nModMask = ([pEvent modifierFlags] & (NSEventModifierFlagShift|NSEventModifierFlagControl|NSEventModifierFlagOption|NSEventModifierFlagCommand));
119 NSString* charactersIgnoringModifiers = [pEvent charactersIgnoringModifiers];
120 if( nModMask == NSEventModifierFlagCommand &&
121 ( [charactersIgnoringModifiers isEqualToString: @"v"] ||
122 [charactersIgnoringModifiers isEqualToString: @"c"] ||
123 [charactersIgnoringModifiers isEqualToString: @"x"] ||
124 [charactersIgnoringModifiers isEqualToString: @"a"] ||
125 [charactersIgnoringModifiers isEqualToString: @"z"] ) )
127 [[[NSApp keyWindow] contentView] keyDown: pEvent];
132 const AquaSalFrame* pFrame = mpMenuItem->mpParentMenu ? mpMenuItem->mpParentMenu->getFrame() : nullptr;
133 if( pFrame && AquaSalFrame::isAlive( pFrame ) && ! pFrame->GetWindow()->IsInModalMode() )
135 SalMenuEvent aMenuEvt( mpMenuItem->mnId, mpMenuItem->mpVCLMenu );
136 pFrame->CallCallback(SalEvent::MenuCommand, &aMenuEvt);
138 else if( mpMenuItem->mpVCLMenu )
140 // if an item from submenu was selected. the corresponding Window does not exist because
141 // we use native popup menus, so we have to set the selected menuitem directly
142 // incidentally this of course works for top level popup menus, too
143 PopupMenu * pPopupMenu = dynamic_cast<PopupMenu *>(mpMenuItem->mpVCLMenu.get());
146 // FIXME: revise this ugly code
148 // select handlers in vcl are dispatch on the original menu
149 // if not consumed by the select handler of the current menu
150 // however since only the starting menu ever came into Execute
151 // the hierarchy is not build up. Workaround this by getting
152 // the menu it should have been
154 // get started from hierarchy in vcl menus
155 AquaSalMenu* pParentMenu = mpMenuItem->mpParentMenu;
156 Menu* pCurMenu = mpMenuItem->mpVCLMenu;
157 while( pParentMenu && pParentMenu->mpVCLMenu )
159 pCurMenu = pParentMenu->mpVCLMenu;
160 pParentMenu = pParentMenu->mpParentSalMenu;
163 pPopupMenu->SetSelectedEntry( mpMenuItem->mnId );
164 pPopupMenu->ImplSelectWithStart( pCurMenu );
167 OSL_FAIL( "menubar item without frame !" );
172 @implementation OOStatusItemView
173 -(void)drawRect: (NSRect)aRect
175 NSGraphicsContext* pContext = [NSGraphicsContext currentContext];
176 [pContext saveGraphicsState];
177 SAL_WNODEPRECATED_DECLARATIONS_PUSH
178 // "'drawStatusBarBackgroundInRect:withHighlight:' is deprecated: first deprecated in macOS
179 // 10.14 - Use the standard button instead which handles highlight drawing, making this
181 [SalData::getStatusItem() drawStatusBarBackgroundInRect: aRect withHighlight: NO];
182 SAL_WNODEPRECATED_DECLARATIONS_POP
183 if( AquaSalMenu::pCurrentMenuBar )
185 const std::vector< AquaSalMenu::MenuBarButtonEntry >& rButtons( AquaSalMenu::pCurrentMenuBar->getButtons() );
186 NSRect aFrame = [self frame];
187 NSRect aImgRect = { { 2, 0 }, { 0, 0 } };
188 for( size_t i = 0; i < rButtons.size(); ++i )
190 const Size aPixSize = rButtons[i].maButton.maImage.GetSizePixel();
191 const NSRect aFromRect = { NSZeroPoint, NSMakeSize( aPixSize.Width(), aPixSize.Height()) };
192 aImgRect.origin.y = floor((aFrame.size.height - aFromRect.size.height)/2);
193 aImgRect.size = aFromRect.size;
194 if( rButtons[i].mpNSImage )
195 [rButtons[i].mpNSImage drawInRect: aImgRect fromRect: aFromRect operation: NSCompositingOperationSourceOver fraction: 1.0];
196 aImgRect.origin.x += aFromRect.size.width + 2;
199 [pContext restoreGraphicsState];
202 -(void)mouseUp: (NSEvent *)pEvent
204 /* check if button goes up inside one of our status buttons */
205 if( AquaSalMenu::pCurrentMenuBar )
207 const std::vector< AquaSalMenu::MenuBarButtonEntry >& rButtons( AquaSalMenu::pCurrentMenuBar->getButtons() );
208 NSRect aFrame = [self frame];
209 NSRect aImgRect = { { 2, 0 }, { 0, 0 } };
210 NSPoint aMousePt = [pEvent locationInWindow];
211 for( size_t i = 0; i < rButtons.size(); ++i )
213 const Size aPixSize = rButtons[i].maButton.maImage.GetSizePixel();
214 const NSRect aFromRect = { NSZeroPoint, NSMakeSize( aPixSize.Width(), aPixSize.Height()) };
215 aImgRect.origin.y = (aFrame.size.height - aFromRect.size.height)/2;
216 aImgRect.size = aFromRect.size;
217 if( aMousePt.x >= aImgRect.origin.x && aMousePt.x <= (aImgRect.origin.x+aImgRect.size.width) &&
218 aMousePt.y >= aImgRect.origin.y && aMousePt.y <= (aImgRect.origin.y+aImgRect.size.height) )
220 if( AquaSalMenu::pCurrentMenuBar->mpFrame && AquaSalFrame::isAlive( AquaSalMenu::pCurrentMenuBar->mpFrame ) )
222 SalMenuEvent aMenuEvt( rButtons[i].maButton.mnId, AquaSalMenu::pCurrentMenuBar->mpVCLMenu );
223 AquaSalMenu::pCurrentMenuBar->mpFrame->CallCallback(SalEvent::MenuButtonCommand, &aMenuEvt);
228 aImgRect.origin.x += aFromRect.size.width + 2;
235 NSStatusBar* pStatBar = [NSStatusBar systemStatusBar];
236 NSSize aSize = { 0, [pStatBar thickness] };
237 [self removeAllToolTips];
238 if( AquaSalMenu::pCurrentMenuBar )
240 const std::vector< AquaSalMenu::MenuBarButtonEntry >& rButtons( AquaSalMenu::pCurrentMenuBar->getButtons() );
241 if( ! rButtons.empty() )
244 for( size_t i = 0; i < rButtons.size(); ++i )
246 NSRect aImgRect = { { aSize.width,
247 static_cast<CGFloat>(floor((aSize.height-rButtons[i].maButton.maImage.GetSizePixel().Height())/2)) },
248 { static_cast<CGFloat>(rButtons[i].maButton.maImage.GetSizePixel().Width()),
249 static_cast<CGFloat>(rButtons[i].maButton.maImage.GetSizePixel().Height()) } };
250 if( rButtons[i].mpToolTipString )
251 [self addToolTipRect: aImgRect owner: rButtons[i].mpToolTipString userData: nullptr];
252 aSize.width += 2 + aImgRect.size.width;
256 [self setFrameSize: aSize];
261 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */