tdf#151341 Use lzfse compression instead of bzip2
[LibreOffice.git] / vcl / osx / salnsmenu.mm
blobb2df2da7e5f52edad3986907ee204c18fc6c59ef
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>
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
35     mpMenu = pMenu;
36     return [super initWithTitle: [NSString string]];
39 -(void)menuNeedsUpdate: (NSMenu*)pMenu
41     SolarMutexGuard aGuard;
43     if( mpMenu )
44     {
45         const AquaSalFrame* pFrame = mpMenu->getFrame();
46         if( pFrame && AquaSalFrame::isAlive( pFrame ) )
47         {
48             SalMenuEvent aMenuEvt;
49             aMenuEvt.mnId   = 0;
50             aMenuEvt.mpMenu = mpMenu->mpVCLMenu;
51             if( aMenuEvt.mpMenu )
52             {
53                 pFrame->CallCallback(SalEvent::MenuActivate, &aMenuEvt);
54                 pFrame->CallCallback(SalEvent::MenuDeactivate, &aMenuEvt);
55             }
56             else
57                 OSL_FAIL( "unconnected menu" );
58         }
59         else if( mpMenu->mpVCLMenu )
60         {
61             mpMenu->mpVCLMenu->Activate();
62             mpMenu->mpVCLMenu->Deactivate();
64             // Hide disabled items
65             NSArray* elements = [pMenu itemArray];
66             NSEnumerator* it = [elements objectEnumerator];
67             id element;
68             while ( ( element = [it nextObject] ) != nil )
69             {
70                 NSMenuItem* item = static_cast< NSMenuItem* >( element );
71                 if( ![item isSeparatorItem] )
72                     [item setHidden: ![item isEnabled]];
73             }
74         }
75     }
78 -(void)setSalMenu: (AquaSalMenu*)pMenu
80     mpMenu = pMenu;
82 @end
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];
92     return ret;
94 -(void)menuItemTriggered: (id)aSender
96     (void)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
107     // comment.
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 )
117     {
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"] ) )
126         {
127             [[[NSApp keyWindow] contentView] keyDown: pEvent];
128             return;
129         }
130     }
132     const AquaSalFrame* pFrame = mpMenuItem->mpParentMenu ? mpMenuItem->mpParentMenu->getFrame() : nullptr;
133     if( pFrame && AquaSalFrame::isAlive( pFrame ) && ! pFrame->GetWindow()->IsInModalMode() )
134     {
135         SalMenuEvent aMenuEvt( mpMenuItem->mnId, mpMenuItem->mpVCLMenu );
136         pFrame->CallCallback(SalEvent::MenuCommand, &aMenuEvt);
137     }
138     else if( mpMenuItem->mpVCLMenu )
139     {
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());
144         if( pPopupMenu )
145         {
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 )
158             {
159                 pCurMenu = pParentMenu->mpVCLMenu;
160                 pParentMenu = pParentMenu->mpParentSalMenu;
161             }
163             pPopupMenu->SetSelectedEntry( mpMenuItem->mnId );
164             pPopupMenu->ImplSelectWithStart( pCurMenu );
165         }
166         else
167             OSL_FAIL( "menubar item without frame !" );
168     }
170 @end
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
180         // method obsolete"
181     [SalData::getStatusItem() drawStatusBarBackgroundInRect: aRect withHighlight: NO];
182 SAL_WNODEPRECATED_DECLARATIONS_POP
183     if( AquaSalMenu::pCurrentMenuBar )
184     {
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 )
189         {
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;
197         }
198     }
199     [pContext restoreGraphicsState];
202 -(void)mouseUp: (NSEvent *)pEvent
204     /* check if button goes up inside one of our status buttons */
205     if( AquaSalMenu::pCurrentMenuBar )
206     {
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 )
212         {
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) )
219             {
220                 if( AquaSalMenu::pCurrentMenuBar->mpFrame && AquaSalFrame::isAlive( AquaSalMenu::pCurrentMenuBar->mpFrame ) )
221                 {
222                     SalMenuEvent aMenuEvt( rButtons[i].maButton.mnId, AquaSalMenu::pCurrentMenuBar->mpVCLMenu );
223                     AquaSalMenu::pCurrentMenuBar->mpFrame->CallCallback(SalEvent::MenuButtonCommand, &aMenuEvt);
224                 }
225                 return;
226             }
228             aImgRect.origin.x += aFromRect.size.width + 2;
229         }
230     }
233 -(void)layout
235     NSStatusBar* pStatBar = [NSStatusBar systemStatusBar];
236     NSSize aSize = { 0, [pStatBar thickness] };
237     [self removeAllToolTips];
238     if( AquaSalMenu::pCurrentMenuBar )
239     {
240         const std::vector< AquaSalMenu::MenuBarButtonEntry >& rButtons( AquaSalMenu::pCurrentMenuBar->getButtons() );
241         if( ! rButtons.empty() )
242         {
243             aSize.width = 2;
244             for( size_t i = 0; i < rButtons.size(); ++i )
245             {
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;
253             }
254         }
255     }
256     [self setFrameSize: aSize];
258 @end
261 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */