summaryrefslogtreecommitdiffstats
path: root/vcl/osx/salframe.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/osx/salframe.cxx')
-rw-r--r--vcl/osx/salframe.cxx2027
1 files changed, 2027 insertions, 0 deletions
diff --git a/vcl/osx/salframe.cxx b/vcl/osx/salframe.cxx
new file mode 100644
index 0000000000..b02dbc5958
--- /dev/null
+++ b/vcl/osx/salframe.cxx
@@ -0,0 +1,2027 @@
+/* -*- 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 <string>
+
+#include <comphelper/fileurl.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <tools/long.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+#include <osl/file.h>
+
+#include <vcl/event.hxx>
+#include <vcl/inputctx.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <vcl/syswin.hxx>
+#include <vcl/settings.hxx>
+
+#include <osx/saldata.hxx>
+#include <quartz/salgdi.h>
+#include <osx/salframe.h>
+#include <osx/salmenu.h>
+#include <osx/salinst.h>
+#include <osx/salframeview.h>
+#include <osx/a11yfactory.h>
+#include <osx/runinmain.hxx>
+#include <quartz/utils.h>
+
+#include <salwtype.hxx>
+
+#include <premac.h>
+#include <objc/objc-runtime.h>
+// needed for theming
+// FIXME: move theming code to salnativewidgets.cxx
+#include <Carbon/Carbon.h>
+#include <quartz/CGHelpers.hxx>
+#include <postmac.h>
+
+
+const int nMinBlinkCursorDelay = 500;
+
+AquaSalFrame* AquaSalFrame::s_pCaptureFrame = nullptr;
+
+AquaSalFrame::AquaSalFrame( SalFrame* pParent, SalFrameStyleFlags salFrameStyle ) :
+ mpNSWindow(nil),
+ mpNSView(nil),
+ mpDockMenuEntry(nil),
+ mpGraphics(nullptr),
+ mpParent(nullptr),
+ mnMinWidth(0),
+ mnMinHeight(0),
+ mnMaxWidth(0),
+ mnMaxHeight(0),
+ mbGraphics(false),
+ mbFullScreen( false ),
+ mbShown(false),
+ mbInitShow(true),
+ mbPositioned(false),
+ mbSized(false),
+ mbPresentation( false ),
+ mnStyle( salFrameStyle ),
+ mnStyleMask( 0 ),
+ mnLastEventTime( 0 ),
+ mnLastModifierFlags( 0 ),
+ mpMenu( nullptr ),
+ mnExtStyle( 0 ),
+ mePointerStyle( PointerStyle::Arrow ),
+ mrClippingPath( nullptr ),
+ mnICOptions( InputContextFlags::NONE ),
+ mnBlinkCursorDelay( nMinBlinkCursorDelay ),
+ mbForceFlush( false )
+{
+ mpParent = dynamic_cast<AquaSalFrame*>(pParent);
+
+ initWindowAndView();
+
+ SalData* pSalData = GetSalData();
+ pSalData->mpInstance->insertFrame( this );
+ NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
+
+ // tdf#150177 Limit minimum blink cursor rate
+ // This bug occurs when the values for NSTextInsertionPointBlinkPeriodOn or
+ // NSTextInsertionPointBlinkPeriodOff are set to zero or close to zero.
+ // LibreOffice becomes very sluggish opening documents when either is set
+ // at 100 milliseconds or less so set the blink rate to the maximum of
+ // nMinBlinkCursorDelay, NSTextInsertionPointBlinkPeriodOn, and
+ // NSTextInsertionPointBlinkPeriodOff.
+ mnBlinkCursorDelay = nMinBlinkCursorDelay;
+ if (userDefaults != nil)
+ {
+ id setting = [userDefaults objectForKey: @"NSTextInsertionPointBlinkPeriodOn"];
+ if (setting && [setting isKindOfClass:[NSNumber class]])
+ mnBlinkCursorDelay = std::max(mnBlinkCursorDelay, [setting intValue]);
+
+ setting = [userDefaults objectForKey: @"NSTextInsertionPointBlinkPeriodOff"];
+ if (setting && [setting isKindOfClass:[NSNumber class]])
+ mnBlinkCursorDelay = std::max(mnBlinkCursorDelay, [setting intValue]);
+ }
+}
+
+AquaSalFrame::~AquaSalFrame()
+{
+ if (mbFullScreen)
+ doShowFullScreen(false, maGeometry.screen());
+
+ assert( GetSalData()->mpInstance->IsMainThread() );
+
+ // if the frame is destroyed and has the current menubar
+ // set the default menubar
+ if( mpMenu && mpMenu->mbMenuBar && AquaSalMenu::pCurrentMenuBar == mpMenu )
+ AquaSalMenu::setDefaultMenu();
+
+ // cleanup clipping stuff
+ doResetClipRegion();
+
+ [SalFrameView unsetMouseFrame: this];
+
+ SalData* pSalData = GetSalData();
+ pSalData->mpInstance->eraseFrame( this );
+ pSalData->maPresentationFrames.remove( this );
+
+ SAL_WARN_IF( this == s_pCaptureFrame, "vcl", "capture frame destroyed" );
+ if( this == s_pCaptureFrame )
+ s_pCaptureFrame = nullptr;
+
+ delete mpGraphics;
+
+ if( mpDockMenuEntry )
+ {
+ NSMenu* pDock = AquaSalInstance::GetDynamicDockMenu();
+ // life cycle comment: the menu has ownership of the item, so no release
+ [pDock removeItem: mpDockMenuEntry];
+ if ([pDock numberOfItems] != 0
+ && [[pDock itemAtIndex: 0] isSeparatorItem])
+ {
+ [pDock removeItemAtIndex: 0];
+ }
+ }
+ if ( mpNSView ) {
+ if ([mpNSView isKindOfClass:[SalFrameView class]])
+ [static_cast<SalFrameView*>(mpNSView) revokeWrapper];
+ [mpNSView release];
+ }
+ if ( mpNSWindow )
+ [mpNSWindow release];
+}
+
+void AquaSalFrame::initWindowAndView()
+{
+ OSX_SALDATA_RUNINMAIN( initWindowAndView() )
+
+ // initialize mirroring parameters
+ // FIXME: screens changing
+ NSScreen* pNSScreen = [mpNSWindow screen];
+ if( pNSScreen == nil )
+ pNSScreen = [NSScreen mainScreen];
+ maScreenRect = [pNSScreen frame];
+
+ // calculate some default geometry
+ NSRect aVisibleRect = [pNSScreen visibleFrame];
+ CocoaToVCL( aVisibleRect );
+
+ maGeometry.setX(static_cast<sal_Int32>(aVisibleRect.origin.x + aVisibleRect.size.width / 10));
+ maGeometry.setY(static_cast<sal_Int32>(aVisibleRect.origin.y + aVisibleRect.size.height / 10));
+ maGeometry.setWidth(static_cast<sal_uInt32>(aVisibleRect.size.width * 0.8));
+ maGeometry.setHeight(static_cast<sal_uInt32>(aVisibleRect.size.height * 0.8));
+
+ // calculate style mask
+ if( (mnStyle & SalFrameStyleFlags::FLOAT) ||
+ (mnStyle & SalFrameStyleFlags::OWNERDRAWDECORATION) )
+ mnStyleMask = NSWindowStyleMaskBorderless;
+ else if( mnStyle & SalFrameStyleFlags::DEFAULT )
+ {
+ mnStyleMask = NSWindowStyleMaskTitled |
+ NSWindowStyleMaskMiniaturizable |
+ NSWindowStyleMaskResizable |
+ NSWindowStyleMaskClosable;
+ // make default window "maximized"
+ maGeometry.setX(static_cast<sal_Int32>(aVisibleRect.origin.x));
+ maGeometry.setY(static_cast<sal_Int32>(aVisibleRect.origin.y));
+ maGeometry.setWidth(static_cast<sal_uInt32>(aVisibleRect.size.width));
+ maGeometry.setHeight(static_cast<sal_uInt32>(aVisibleRect.size.height));
+ mbPositioned = mbSized = true;
+ }
+ else
+ {
+ if( mnStyle & SalFrameStyleFlags::MOVEABLE )
+ {
+ mnStyleMask |= NSWindowStyleMaskTitled;
+ if( mpParent == nullptr )
+ mnStyleMask |= NSWindowStyleMaskMiniaturizable;
+ }
+ if( mnStyle & SalFrameStyleFlags::SIZEABLE )
+ mnStyleMask |= NSWindowStyleMaskResizable;
+ if( mnStyle & SalFrameStyleFlags::CLOSEABLE )
+ mnStyleMask |= NSWindowStyleMaskClosable;
+ // documentation says anything other than NSWindowStyleMaskBorderless (=0)
+ // should also include NSWindowStyleMaskTitled;
+ if( mnStyleMask != 0 )
+ mnStyleMask |= NSWindowStyleMaskTitled;
+ }
+
+ if (Application::IsBitmapRendering())
+ return;
+
+ // #i91990# support GUI-less (daemon) execution
+ @try
+ {
+ mpNSWindow = [[SalFrameWindow alloc] initWithSalFrame: this];
+ mpNSView = [[SalFrameView alloc] initWithSalFrame: this];
+ }
+ @catch ( id )
+ {
+ std::abort();
+ }
+
+ if( mnStyle & SalFrameStyleFlags::TOOLTIP )
+ [mpNSWindow setIgnoresMouseEvents: YES];
+ else
+ // Related: tdf#155092 mouse events are now handled by tracking areas
+ [mpNSWindow setAcceptsMouseMovedEvents: NO];
+ [mpNSWindow setHasShadow: YES];
+
+ [mpNSWindow setDelegate: static_cast<id<NSWindowDelegate> >(mpNSWindow)];
+
+ [mpNSWindow setRestorable:NO];
+
+ // tdf#155092 use tracking areas instead of tracking rectangles
+ // Apparently, the older, tracking rectangles selectors cause
+ // unexpected window resizing upon the first mouse down after the
+ // window has been manually resized so switch to the newer,
+ // tracking areas selectors. Also, the NSTrackingInVisibleRect
+ // option allows us to create one single tracking area that
+ // resizes itself automatically over the lifetime of the view.
+ // Note: for some unknown reason, both NSTrackingMouseMoved and
+ // NSTrackingAssumeInside are necessary options for this fix
+ // to work.
+ NSTrackingArea *pTrackingArea = [[NSTrackingArea alloc] initWithRect: [mpNSView bounds] options: ( NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways | NSTrackingAssumeInside | NSTrackingInVisibleRect ) owner: mpNSView userInfo: nil];
+ [mpNSView addTrackingArea: pTrackingArea];
+ [pTrackingArea release];
+
+ maSysData.mpNSView = mpNSView;
+
+ UpdateFrameGeometry();
+
+ [mpNSWindow setContentView: mpNSView];
+}
+
+void AquaSalFrame::CocoaToVCL( NSRect& io_rRect, bool bRelativeToScreen )
+{
+ if( bRelativeToScreen )
+ io_rRect.origin.y = maScreenRect.size.height - (io_rRect.origin.y+io_rRect.size.height);
+ else
+ io_rRect.origin.y = maGeometry.height() - (io_rRect.origin.y+io_rRect.size.height);
+}
+
+void AquaSalFrame::VCLToCocoa( NSRect& io_rRect, bool bRelativeToScreen )
+{
+ if( bRelativeToScreen )
+ io_rRect.origin.y = maScreenRect.size.height - (io_rRect.origin.y+io_rRect.size.height);
+ else
+ io_rRect.origin.y = maGeometry.height() - (io_rRect.origin.y+io_rRect.size.height);
+}
+
+void AquaSalFrame::CocoaToVCL( NSPoint& io_rPoint, bool bRelativeToScreen )
+{
+ if( bRelativeToScreen )
+ io_rPoint.y = maScreenRect.size.height - io_rPoint.y;
+ else
+ io_rPoint.y = maGeometry.height() - io_rPoint.y;
+}
+
+void AquaSalFrame::VCLToCocoa( NSPoint& io_rPoint, bool bRelativeToScreen )
+{
+ if( bRelativeToScreen )
+ io_rPoint.y = maScreenRect.size.height - io_rPoint.y;
+ else
+ io_rPoint.y = maGeometry.height() - io_rPoint.y;
+}
+
+void AquaSalFrame::screenParametersChanged()
+{
+ OSX_SALDATA_RUNINMAIN( screenParametersChanged() )
+
+ sal::aqua::resetTotalScreenBounds();
+ sal::aqua::resetWindowScaling();
+
+ UpdateFrameGeometry();
+
+ if( mpGraphics )
+ mpGraphics->updateResolution();
+
+ if (!mbGeometryDidChange)
+ return;
+
+ CallCallback( SalEvent::DisplayChanged, nullptr );
+}
+
+SalGraphics* AquaSalFrame::AcquireGraphics()
+{
+ if ( mbGraphics )
+ return nullptr;
+
+ if ( !mpGraphics )
+ {
+ mpGraphics = new AquaSalGraphics;
+ mpGraphics->SetWindowGraphics( this );
+ }
+
+ mbGraphics = true;
+ return mpGraphics;
+}
+
+void AquaSalFrame::ReleaseGraphics( SalGraphics *pGraphics )
+{
+ SAL_WARN_IF( pGraphics != mpGraphics, "vcl", "graphics released on wrong frame" );
+ mbGraphics = false;
+}
+
+bool AquaSalFrame::PostEvent(std::unique_ptr<ImplSVEvent> pData)
+{
+ GetSalData()->mpInstance->PostEvent( this, pData.release(), SalEvent::UserEvent );
+ return true;
+}
+
+void AquaSalFrame::SetTitle(const OUString& rTitle)
+{
+ if ( !mpNSWindow )
+ return;
+
+ OSX_SALDATA_RUNINMAIN( SetTitle(rTitle) )
+
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ NSString* pTitle = CreateNSString( rTitle );
+ [mpNSWindow setTitle: pTitle];
+
+ // create an entry in the dock menu
+ const SalFrameStyleFlags nAppWindowStyle = SalFrameStyleFlags::CLOSEABLE | SalFrameStyleFlags::MOVEABLE;
+ if( mpParent == nullptr &&
+ (mnStyle & nAppWindowStyle) == nAppWindowStyle )
+ {
+ if( mpDockMenuEntry == nullptr )
+ {
+ NSMenu* pDock = AquaSalInstance::GetDynamicDockMenu();
+
+ if ([pDock numberOfItems] != 0) {
+ NSMenuItem* pTopItem = [pDock itemAtIndex: 0];
+ if ( [pTopItem hasSubmenu] )
+ [pDock insertItem: [NSMenuItem separatorItem] atIndex: 0];
+ }
+
+ mpDockMenuEntry = [pDock insertItemWithTitle: pTitle
+ action: @selector(dockMenuItemTriggered:)
+ keyEquivalent: @""
+ atIndex: 0];
+ [mpDockMenuEntry setTarget: mpNSWindow];
+
+ // TODO: image (either the generic window image or an icon
+ // check mark (for "main" window ?)
+ }
+ else
+ [mpDockMenuEntry setTitle: pTitle];
+ }
+
+ if (pTitle)
+ [pTitle release];
+}
+
+void AquaSalFrame::SetIcon( sal_uInt16 )
+{
+}
+
+void AquaSalFrame::SetRepresentedURL( const OUString& i_rDocURL )
+{
+ OSX_SALDATA_RUNINMAIN( SetRepresentedURL( i_rDocURL ) )
+
+ if( comphelper::isFileUrl(i_rDocURL) )
+ {
+ OUString aSysPath;
+ osl_getSystemPathFromFileURL( i_rDocURL.pData, &aSysPath.pData );
+ NSString* pStr = CreateNSString( aSysPath );
+ if( pStr )
+ {
+ [pStr autorelease];
+ [mpNSWindow setRepresentedFilename: pStr];
+ }
+ }
+}
+
+void AquaSalFrame::initShow()
+{
+ OSX_SALDATA_RUNINMAIN( initShow() )
+
+ mbInitShow = false;
+ if( ! mbPositioned && ! mbFullScreen )
+ {
+ AbsoluteScreenPixelRectangle aScreenRect;
+ GetWorkArea( aScreenRect );
+ if( mpParent ) // center relative to parent
+ {
+ // center on parent
+ tools::Long nNewX = mpParent->maGeometry.x() + (static_cast<tools::Long>(mpParent->maGeometry.width()) - static_cast<tools::Long>(maGeometry.width())) / 2;
+ if( nNewX < aScreenRect.Left() )
+ nNewX = aScreenRect.Left();
+ if (static_cast<tools::Long>(nNewX + maGeometry.width()) > aScreenRect.Right())
+ nNewX = aScreenRect.Right() - maGeometry.width() - 1;
+ tools::Long nNewY = mpParent->maGeometry.y() + (static_cast<tools::Long>(mpParent->maGeometry.height()) - static_cast<tools::Long>(maGeometry.height())) / 2;
+ if( nNewY < aScreenRect.Top() )
+ nNewY = aScreenRect.Top();
+ if( nNewY > aScreenRect.Bottom() )
+ nNewY = aScreenRect.Bottom() - maGeometry.height() - 1;
+ SetPosSize( nNewX - mpParent->maGeometry.x(),
+ nNewY - mpParent->maGeometry.y(),
+ 0, 0, SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y );
+ }
+ else if( ! (mnStyle & SalFrameStyleFlags::SIZEABLE) )
+ {
+ // center on screen
+ tools::Long nNewX = (aScreenRect.GetWidth() - maGeometry.width()) / 2;
+ tools::Long nNewY = (aScreenRect.GetHeight() - maGeometry.height()) / 2;
+ SetPosSize( nNewX, nNewY, 0, 0, SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y );
+ }
+ }
+
+ // make sure the view is present in the wrapper list before any children receive focus
+ if (mpNSView && [mpNSView isKindOfClass:[SalFrameView class]])
+ [static_cast<SalFrameView*>(mpNSView) registerWrapper];
+}
+
+void AquaSalFrame::SendPaintEvent( const tools::Rectangle* pRect )
+{
+ OSX_SALDATA_RUNINMAIN( SendPaintEvent( pRect ) )
+
+ SalPaintEvent aPaintEvt(0, 0, maGeometry.width(), maGeometry.height(), true);
+ if( pRect )
+ {
+ aPaintEvt.mnBoundX = pRect->Left();
+ aPaintEvt.mnBoundY = pRect->Top();
+ aPaintEvt.mnBoundWidth = pRect->GetWidth();
+ aPaintEvt.mnBoundHeight = pRect->GetHeight();
+ }
+
+ CallCallback(SalEvent::Paint, &aPaintEvt);
+}
+
+void AquaSalFrame::Show(bool bVisible, bool bNoActivate)
+{
+ if ( !mpNSWindow )
+ return;
+
+ OSX_SALDATA_RUNINMAIN( Show(bVisible, bNoActivate) )
+
+ // tdf#152173 Don't display tooltip windows when application is inactive
+ // Starting with macOS 13 Ventura, inactive applications receive mouse
+ // move events so when LibreOffice is inactive, a mouse move event causes
+ // a tooltip to be displayed. Since the tooltip window is attached to its
+ // parent window (to ensure that the tooltip is above the parent window),
+ // displaying a tooltip pulls the parent window in front of the windows
+ // of all other inactive applications.
+ // Also, don't display tooltips when mousing over non-key windows even if
+ // the application is active as the tooltip window will pull the non-key
+ // window in front of the key window.
+ if (bVisible && (mnStyle & SalFrameStyleFlags::TOOLTIP))
+ {
+ if (![NSApp isActive])
+ return;
+
+ if (mpParent)
+ {
+ // tdf#157565 show tooltip if any parent window is the key window
+ bool bKeyWindowFound = false;
+ NSWindow *pParent = mpParent->mpNSWindow;
+ while (pParent)
+ {
+ if ([pParent isKeyWindow])
+ {
+ bKeyWindowFound = true;
+ break;
+ }
+ pParent = [pParent parentWindow];
+ }
+ if (!bKeyWindowFound)
+ return;
+ }
+ }
+
+ mbShown = bVisible;
+ if(bVisible)
+ {
+ if( mbInitShow )
+ initShow();
+
+ CallCallback(SalEvent::Resize, nullptr);
+ // trigger filling our backbuffer
+ SendPaintEvent();
+
+ if( bNoActivate || [mpNSWindow canBecomeKeyWindow] == NO )
+ [mpNSWindow orderFront: NSApp];
+ else
+ [mpNSWindow makeKeyAndOrderFront: NSApp];
+
+ if( mpParent )
+ {
+ /* #i92674# #i96433# we do not want an invisible parent to show up (which adding a visible
+ child implicitly does). However we also do not want a parentless toolbar.
+
+ HACK: try to decide when we should not insert a child to its parent
+ floaters and ownerdraw windows have not yet shown up in cases where
+ we don't want the parent to become visible
+ */
+ if( mpParent->mbShown || (mnStyle & (SalFrameStyleFlags::OWNERDRAWDECORATION | SalFrameStyleFlags::FLOAT) ) )
+ {
+ [mpParent->mpNSWindow addChildWindow: mpNSWindow ordered: NSWindowAbove];
+ }
+ }
+
+ if( mbPresentation )
+ [mpNSWindow makeMainWindow];
+ }
+ else
+ {
+ // if the frame holding the current menubar gets hidden
+ // show the default menubar
+ if( mpMenu && mpMenu->mbMenuBar && AquaSalMenu::pCurrentMenuBar == mpMenu )
+ AquaSalMenu::setDefaultMenu();
+
+ // #i90440# #i94443# work around the focus going back to some other window
+ // if a child gets hidden for a parent window
+ if( mpParent && mpParent->mbShown && [mpNSWindow isKeyWindow] )
+ [mpParent->mpNSWindow makeKeyAndOrderFront: NSApp];
+
+ [SalFrameView unsetMouseFrame: this];
+ if( mpParent && [mpNSWindow parentWindow] == mpParent->mpNSWindow )
+ [mpParent->mpNSWindow removeChildWindow: mpNSWindow];
+
+ [mpNSWindow orderOut: NSApp];
+ }
+}
+
+void AquaSalFrame::SetMinClientSize( tools::Long nWidth, tools::Long nHeight )
+{
+ OSX_SALDATA_RUNINMAIN( SetMinClientSize( nWidth, nHeight ) )
+
+ mnMinWidth = nWidth;
+ mnMinHeight = nHeight;
+
+ if( mpNSWindow )
+ {
+ // Always add the decoration as the dimension concerns only
+ // the content rectangle
+ nWidth += maGeometry.leftDecoration() + maGeometry.rightDecoration();
+ nHeight += maGeometry.topDecoration() + maGeometry.bottomDecoration();
+
+ NSSize aSize = { static_cast<CGFloat>(nWidth), static_cast<CGFloat>(nHeight) };
+
+ // Size of full window (content+structure) although we only
+ // have the client size in arguments
+ [mpNSWindow setMinSize: aSize];
+ }
+}
+
+void AquaSalFrame::SetMaxClientSize( tools::Long nWidth, tools::Long nHeight )
+{
+ OSX_SALDATA_RUNINMAIN( SetMaxClientSize( nWidth, nHeight ) )
+
+ mnMaxWidth = nWidth;
+ mnMaxHeight = nHeight;
+
+ if( mpNSWindow )
+ {
+ // Always add the decoration as the dimension concerns only
+ // the content rectangle
+ nWidth += maGeometry.leftDecoration() + maGeometry.rightDecoration();
+ nHeight += maGeometry.topDecoration() + maGeometry.bottomDecoration();
+
+ // Carbon windows can't have a size greater than 32767x32767
+ if (nWidth>32767) nWidth=32767;
+ if (nHeight>32767) nHeight=32767;
+
+ NSSize aSize = { static_cast<CGFloat>(nWidth), static_cast<CGFloat>(nHeight) };
+
+ // Size of full window (content+structure) although we only
+ // have the client size in arguments
+ [mpNSWindow setMaxSize: aSize];
+ }
+}
+
+void AquaSalFrame::GetClientSize( tools::Long& rWidth, tools::Long& rHeight )
+{
+ if (mbShown || mbInitShow || Application::IsBitmapRendering())
+ {
+ rWidth = maGeometry.width();
+ rHeight = maGeometry.height();
+ }
+ else
+ {
+ rWidth = 0;
+ rHeight = 0;
+ }
+}
+
+SalEvent AquaSalFrame::PreparePosSize(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, sal_uInt16 nFlags)
+{
+ SalEvent nEvent = SalEvent::NONE;
+ assert(mpNSWindow || Application::IsBitmapRendering());
+
+ if (nFlags & (SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y))
+ {
+ mbPositioned = true;
+ nEvent = SalEvent::Move;
+ }
+
+ if (nFlags & (SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT))
+ {
+ mbSized = true;
+ nEvent = (nEvent == SalEvent::Move) ? SalEvent::MoveResize : SalEvent::Resize;
+ }
+
+ if (Application::IsBitmapRendering())
+ {
+ if (nFlags & SAL_FRAME_POSSIZE_X)
+ maGeometry.setX(nX);
+ if (nFlags & SAL_FRAME_POSSIZE_Y)
+ maGeometry.setY(nY);
+ if (nFlags & SAL_FRAME_POSSIZE_WIDTH)
+ {
+ maGeometry.setWidth(nWidth);
+ if (mnMaxWidth > 0 && maGeometry.width() > mnMaxWidth)
+ maGeometry.setWidth(mnMaxWidth);
+ if (mnMinWidth > 0 && maGeometry.width() < mnMinWidth)
+ maGeometry.setWidth(mnMinWidth);
+ }
+ if (nFlags & SAL_FRAME_POSSIZE_HEIGHT)
+ {
+ maGeometry.setHeight(nHeight);
+ if (mnMaxHeight > 0 && maGeometry.height() > mnMaxHeight)
+ maGeometry.setHeight(mnMaxHeight);
+ if (mnMinHeight > 0 && maGeometry.height() < mnMinHeight)
+ maGeometry.setHeight(mnMinHeight);
+ }
+ if (nEvent != SalEvent::NONE)
+ CallCallback(nEvent, nullptr);
+ }
+
+ return nEvent;
+}
+
+void AquaSalFrame::SetWindowState(const vcl::WindowData* pState)
+{
+ if (!mpNSWindow && !Application::IsBitmapRendering())
+ return;
+
+ OSX_SALDATA_RUNINMAIN( SetWindowState( pState ) )
+
+ sal_uInt16 nFlags = 0;
+ nFlags |= ((pState->mask() & vcl::WindowDataMask::X) ? SAL_FRAME_POSSIZE_X : 0);
+ nFlags |= ((pState->mask() & vcl::WindowDataMask::Y) ? SAL_FRAME_POSSIZE_Y : 0);
+ nFlags |= ((pState->mask() & vcl::WindowDataMask::Width) ? SAL_FRAME_POSSIZE_WIDTH : 0);
+ nFlags |= ((pState->mask() & vcl::WindowDataMask::Height) ? SAL_FRAME_POSSIZE_HEIGHT : 0);
+
+ SalEvent nEvent = PreparePosSize(pState->x(), pState->y(), pState->width(), pState->height(), nFlags);
+ if (Application::IsBitmapRendering())
+ return;
+
+ // set normal state
+ NSRect aStateRect = [mpNSWindow frame];
+ aStateRect = [NSWindow contentRectForFrameRect: aStateRect styleMask: mnStyleMask];
+ CocoaToVCL(aStateRect);
+ if (pState->mask() & vcl::WindowDataMask::X)
+ aStateRect.origin.x = float(pState->x());
+ if (pState->mask() & vcl::WindowDataMask::Y)
+ aStateRect.origin.y = float(pState->y());
+ if (pState->mask() & vcl::WindowDataMask::Width)
+ aStateRect.size.width = float(pState->width());
+ if (pState->mask() & vcl::WindowDataMask::Height)
+ aStateRect.size.height = float(pState->height());
+ VCLToCocoa(aStateRect);
+ aStateRect = [NSWindow frameRectForContentRect: aStateRect styleMask: mnStyleMask];
+ [mpNSWindow setFrame: aStateRect display: NO];
+
+ if (pState->state() == vcl::WindowState::Minimized)
+ [mpNSWindow miniaturize: NSApp];
+ else if ([mpNSWindow isMiniaturized])
+ [mpNSWindow deminiaturize: NSApp];
+
+ /* ZOOMED is not really maximized (actually it toggles between a user set size and
+ the program specified one), but comes closest since the default behavior is
+ "maximized" if the user did not intervene
+ */
+ if (pState->state() == vcl::WindowState::Maximized)
+ {
+ if (![mpNSWindow isZoomed])
+ [mpNSWindow zoom: NSApp];
+ }
+ else
+ {
+ if ([mpNSWindow isZoomed])
+ [mpNSWindow zoom: NSApp];
+ }
+
+ // get new geometry
+ UpdateFrameGeometry();
+
+ // send event that we were moved/sized
+ if( nEvent != SalEvent::NONE )
+ CallCallback( nEvent, nullptr );
+
+ if (mbShown)
+ {
+ // trigger filling our backbuffer
+ SendPaintEvent();
+
+ // tell the system the views need to be updated
+ [mpNSWindow display];
+ }
+}
+
+bool AquaSalFrame::GetWindowState(vcl::WindowData* pState)
+{
+ if (!mpNSWindow)
+ {
+ if (Application::IsBitmapRendering())
+ {
+ pState->setMask(vcl::WindowDataMask::PosSizeState);
+ pState->setPosSize(maGeometry.posSize());
+ pState->setState(vcl::WindowState::Normal);
+ return true;
+ }
+ return false;
+ }
+
+ OSX_SALDATA_RUNINMAIN_UNION( GetWindowState( pState ), boolean )
+
+ pState->setMask(vcl::WindowDataMask::PosSizeState);
+
+ NSRect aStateRect = [mpNSWindow frame];
+ aStateRect = [NSWindow contentRectForFrameRect: aStateRect styleMask: mnStyleMask];
+ CocoaToVCL( aStateRect );
+ pState->setX(static_cast<sal_Int32>(aStateRect.origin.x));
+ pState->setY(static_cast<sal_Int32>(aStateRect.origin.y));
+ pState->setWidth(static_cast<sal_uInt32>(aStateRect.size.width));
+ pState->setHeight(static_cast<sal_uInt32>(aStateRect.size.height));
+
+ if( [mpNSWindow isMiniaturized] )
+ pState->setState(vcl::WindowState::Minimized);
+ else if( ! [mpNSWindow isZoomed] )
+ pState->setState(vcl::WindowState::Normal);
+ else
+ pState->setState(vcl::WindowState::Maximized);
+
+ return true;
+}
+
+void AquaSalFrame::SetScreenNumber(unsigned int nScreen)
+{
+ if ( !mpNSWindow )
+ return;
+
+ OSX_SALDATA_RUNINMAIN( SetScreenNumber( nScreen ) )
+
+ NSArray* pScreens = [NSScreen screens];
+ NSScreen* pScreen = nil;
+ if( pScreens && nScreen < [pScreens count] )
+ {
+ // get new screen frame
+ pScreen = [pScreens objectAtIndex: nScreen];
+ NSRect aNewScreen = [pScreen frame];
+
+ // get current screen frame
+ pScreen = [mpNSWindow screen];
+ if( pScreen )
+ {
+ NSRect aCurScreen = [pScreen frame];
+ if( aCurScreen.origin.x != aNewScreen.origin.x ||
+ aCurScreen.origin.y != aNewScreen.origin.y )
+ {
+ NSRect aFrameRect = [mpNSWindow frame];
+ aFrameRect.origin.x += aNewScreen.origin.x - aCurScreen.origin.x;
+ aFrameRect.origin.y += aNewScreen.origin.y - aCurScreen.origin.y;
+ [mpNSWindow setFrame: aFrameRect display: NO];
+ UpdateFrameGeometry();
+ }
+ }
+ }
+}
+
+void AquaSalFrame::SetApplicationID( const OUString &/*rApplicationID*/ )
+{
+}
+
+void AquaSalFrame::ShowFullScreen( bool bFullScreen, sal_Int32 nDisplay )
+{
+ doShowFullScreen(bFullScreen, nDisplay);
+}
+
+void AquaSalFrame::doShowFullScreen( bool bFullScreen, sal_Int32 nDisplay )
+{
+ if (!mpNSWindow)
+ {
+ if (Application::IsBitmapRendering() && bFullScreen)
+ SetPosSize(0, 0, 1024, 768, SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT);
+ return;
+ }
+
+ SAL_INFO("vcl.osx", __func__ << ": mbFullScreen=" << mbFullScreen << ", bFullScreen=" << bFullScreen);
+
+ if( mbFullScreen == bFullScreen )
+ return;
+
+ OSX_SALDATA_RUNINMAIN( ShowFullScreen( bFullScreen, nDisplay ) )
+
+ mbFullScreen = bFullScreen;
+
+ if( bFullScreen )
+ {
+ // hide the dock and the menubar if we are on the menu screen
+ // which is always on index 0 according to documentation
+ bool bHideMenu = (nDisplay == 0);
+
+ NSRect aNewContentRect = NSZeroRect;
+ // get correct screen
+ NSScreen* pScreen = nil;
+ NSArray* pScreens = [NSScreen screens];
+ if( pScreens )
+ {
+ if( nDisplay >= 0 && o3tl::make_unsigned(nDisplay) < [pScreens count] )
+ pScreen = [pScreens objectAtIndex: nDisplay];
+ else
+ {
+ // this means span all screens
+ bHideMenu = true;
+ NSEnumerator* pEnum = [pScreens objectEnumerator];
+ while( (pScreen = [pEnum nextObject]) != nil )
+ {
+ NSRect aScreenRect = [pScreen frame];
+ if( aScreenRect.origin.x < aNewContentRect.origin.x )
+ {
+ aNewContentRect.size.width += aNewContentRect.origin.x - aScreenRect.origin.x;
+ aNewContentRect.origin.x = aScreenRect.origin.x;
+ }
+ if( aScreenRect.origin.y < aNewContentRect.origin.y )
+ {
+ aNewContentRect.size.height += aNewContentRect.origin.y - aScreenRect.origin.y;
+ aNewContentRect.origin.y = aScreenRect.origin.y;
+ }
+ if( aScreenRect.origin.x + aScreenRect.size.width > aNewContentRect.origin.x + aNewContentRect.size.width )
+ aNewContentRect.size.width = aScreenRect.origin.x + aScreenRect.size.width - aNewContentRect.origin.x;
+ if( aScreenRect.origin.y + aScreenRect.size.height > aNewContentRect.origin.y + aNewContentRect.size.height )
+ aNewContentRect.size.height = aScreenRect.origin.y + aScreenRect.size.height - aNewContentRect.origin.y;
+ }
+ }
+ }
+ if( aNewContentRect.size.width == 0 && aNewContentRect.size.height == 0 )
+ {
+ if( pScreen == nil )
+ pScreen = [mpNSWindow screen];
+ if( pScreen == nil )
+ pScreen = [NSScreen mainScreen];
+
+ aNewContentRect = [pScreen frame];
+ }
+
+ if( bHideMenu )
+ [NSMenu setMenuBarVisible:NO];
+
+ maFullScreenRect = [mpNSWindow frame];
+
+ [mpNSWindow setFrame: [NSWindow frameRectForContentRect: aNewContentRect styleMask: mnStyleMask] display: mbShown ? YES : NO];
+ }
+ else
+ {
+ [mpNSWindow setFrame: maFullScreenRect display: mbShown ? YES : NO];
+
+ // show the dock and the menubar
+ [NSMenu setMenuBarVisible:YES];
+ }
+
+ UpdateFrameGeometry();
+ if (mbShown)
+ {
+ CallCallback(SalEvent::MoveResize, nullptr);
+
+ // trigger filling our backbuffer
+ SendPaintEvent();
+ }
+}
+
+void AquaSalFrame::StartPresentation( bool bStart )
+{
+ if ( !mpNSWindow )
+ return;
+
+ OSX_SALDATA_RUNINMAIN( StartPresentation( bStart ) )
+
+ if( bStart )
+ {
+ GetSalData()->maPresentationFrames.push_back( this );
+ IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
+ kIOPMAssertionLevelOn,
+ CFSTR("LibreOffice presentation running"),
+ &mnAssertionID);
+ [mpNSWindow setLevel: NSPopUpMenuWindowLevel];
+ if( mbShown )
+ [mpNSWindow makeMainWindow];
+ }
+ else
+ {
+ GetSalData()->maPresentationFrames.remove( this );
+ IOPMAssertionRelease(mnAssertionID);
+ [mpNSWindow setLevel: NSNormalWindowLevel];
+ }
+}
+
+void AquaSalFrame::SetAlwaysOnTop( bool )
+{
+}
+
+void AquaSalFrame::ToTop(SalFrameToTop nFlags)
+{
+ if ( !mpNSWindow )
+ return;
+
+ OSX_SALDATA_RUNINMAIN( ToTop( nFlags ) )
+
+ if( ! (nFlags & SalFrameToTop::RestoreWhenMin) )
+ {
+ if( ! [mpNSWindow isVisible] || [mpNSWindow isMiniaturized] )
+ return;
+ }
+ if( nFlags & SalFrameToTop::GrabFocus )
+ [mpNSWindow makeKeyAndOrderFront: NSApp];
+ else
+ [mpNSWindow orderFront: NSApp];
+}
+
+NSCursor* AquaSalFrame::getCurrentCursor()
+{
+ OSX_SALDATA_RUNINMAIN_POINTER( getCurrentCursor(), NSCursor* )
+
+ NSCursor* pCursor = nil;
+ switch( mePointerStyle )
+ {
+ case PointerStyle::Text: pCursor = [NSCursor IBeamCursor]; break;
+ case PointerStyle::Cross: pCursor = [NSCursor crosshairCursor]; break;
+ case PointerStyle::Hand:
+ case PointerStyle::Move: pCursor = [NSCursor openHandCursor]; break;
+ case PointerStyle::NSize: pCursor = [NSCursor resizeUpCursor]; break;
+ case PointerStyle::SSize: pCursor = [NSCursor resizeDownCursor]; break;
+ case PointerStyle::ESize: pCursor = [NSCursor resizeRightCursor]; break;
+ case PointerStyle::WSize: pCursor = [NSCursor resizeLeftCursor]; break;
+ case PointerStyle::Arrow: pCursor = [NSCursor arrowCursor]; break;
+ case PointerStyle::VSplit:
+ case PointerStyle::VSizeBar:
+ case PointerStyle::WindowNSize:
+ case PointerStyle::WindowSSize:
+ pCursor = [NSCursor resizeUpDownCursor]; break;
+ case PointerStyle::HSplit:
+ case PointerStyle::HSizeBar:
+ case PointerStyle::WindowESize:
+ case PointerStyle::WindowWSize:
+ pCursor = [NSCursor resizeLeftRightCursor]; break;
+ case PointerStyle::RefHand: pCursor = [NSCursor pointingHandCursor]; break;
+
+ default:
+ pCursor = GetSalData()->getCursor( mePointerStyle );
+ if( pCursor == nil )
+ {
+ assert( false && "unmapped cursor" );
+ pCursor = [NSCursor arrowCursor];
+ }
+ break;
+ }
+ return pCursor;
+}
+
+void AquaSalFrame::SetPointer( PointerStyle ePointerStyle )
+{
+ if ( !mpNSWindow )
+ return;
+ if( ePointerStyle == mePointerStyle )
+ return;
+
+ OSX_SALDATA_RUNINMAIN( SetPointer( ePointerStyle ) )
+
+ mePointerStyle = ePointerStyle;
+
+ [mpNSWindow invalidateCursorRectsForView: mpNSView];
+}
+
+void AquaSalFrame::SetPointerPos( tools::Long nX, tools::Long nY )
+{
+ OSX_SALDATA_RUNINMAIN( SetPointerPos( nX, nY ) )
+
+ // FIXME: use Cocoa functions
+ // FIXME: multiscreen support
+ CGPoint aPoint = { static_cast<CGFloat>(nX + maGeometry.x()), static_cast<CGFloat>(nY + maGeometry.y()) };
+ CGDirectDisplayID mainDisplayID = CGMainDisplayID();
+ CGDisplayMoveCursorToPoint( mainDisplayID, aPoint );
+}
+
+void AquaSalFrame::Flush()
+{
+ if( !(mbGraphics && mpGraphics && mpNSView && mbShown) )
+ return;
+
+ OSX_SALDATA_RUNINMAIN( Flush() )
+
+ [mpNSView setNeedsDisplay: YES];
+
+ // outside of the application's event loop (e.g. IntroWindow)
+ // nothing would trigger paint event handling
+ // => fall back to synchronous painting
+ if( mbForceFlush || ImplGetSVData()->maAppData.mnDispatchLevel <= 0 )
+ {
+ mbForceFlush = false;
+ mpGraphics->Flush();
+ // Related: tdf#155266 skip redisplay of the view when forcing flush
+ // It appears that calling -[NSView display] overwhelms some Intel Macs
+ // so only flush the graphics and skip immediate redisplay of the view.
+ if( ImplGetSVData()->maAppData.mnDispatchLevel <= 0 )
+ [mpNSView display];
+ }
+}
+
+void AquaSalFrame::Flush( const tools::Rectangle& rRect )
+{
+ if( !(mbGraphics && mpGraphics && mpNSView && mbShown) )
+ return;
+
+ OSX_SALDATA_RUNINMAIN( Flush( rRect ) )
+
+ NSRect aNSRect = { { static_cast<CGFloat>(rRect.Left()), static_cast<CGFloat>(rRect.Top()) }, { static_cast<CGFloat>(rRect.GetWidth()), static_cast<CGFloat>(rRect.GetHeight()) } };
+ VCLToCocoa( aNSRect, false );
+ [mpNSView setNeedsDisplayInRect: aNSRect];
+
+ // outside of the application's event loop (e.g. IntroWindow)
+ // nothing would trigger paint event handling
+ // => fall back to synchronous painting
+ if( mbForceFlush || ImplGetSVData()->maAppData.mnDispatchLevel <= 0 )
+ {
+ mbForceFlush = false;
+ mpGraphics->Flush( rRect );
+ // Related: tdf#155266 skip redisplay of the view when forcing flush
+ // It appears that calling -[NSView display] overwhelms some Intel Macs
+ // so only flush the graphics and skip immediate redisplay of the view.
+ if( ImplGetSVData()->maAppData.mnDispatchLevel <= 0 )
+ [mpNSView display];
+ }
+}
+
+void AquaSalFrame::SetInputContext( SalInputContext* pContext )
+{
+ if (!pContext)
+ {
+ mnICOptions = InputContextFlags::NONE;
+ return;
+ }
+
+ mnICOptions = pContext->mnOptions;
+
+ if(!(pContext->mnOptions & InputContextFlags::Text))
+ return;
+}
+
+void AquaSalFrame::EndExtTextInput( EndExtTextInputFlags nFlags )
+{
+ // tdf#82115 Commit uncommitted text when a popup menu is opened
+ // The Windows implementation of this method commits or discards the native
+ // input method session. It appears that very few, if any, macOS
+ // applications discard the uncommitted text when cancelling a session so
+ // always commit the uncommitted text.
+ SalFrameWindow *pWindow = static_cast<SalFrameWindow*>(mpNSWindow);
+ if (pWindow && [pWindow isKindOfClass:[SalFrameWindow class]])
+ [pWindow endExtTextInput:nFlags];
+}
+
+OUString AquaSalFrame::GetKeyName( sal_uInt16 nKeyCode )
+{
+ static std::map< sal_uInt16, OUString > aKeyMap;
+ if( aKeyMap.empty() )
+ {
+ sal_uInt16 i;
+ for( i = KEY_A; i <= KEY_Z; i++ )
+ aKeyMap[ i ] = OUString( sal_Unicode( 'A' + (i - KEY_A) ) );
+ for( i = KEY_0; i <= KEY_9; i++ )
+ aKeyMap[ i ] = OUString( sal_Unicode( '0' + (i - KEY_0) ) );
+ for( i = KEY_F1; i <= KEY_F26; i++ )
+ {
+ aKeyMap[ i ] = "F" + OUString::number(i - KEY_F1 + 1);
+ }
+
+ aKeyMap[ KEY_DOWN ] = OUString( u'\x21e3' );
+ aKeyMap[ KEY_UP ] = OUString( u'\x21e1' );
+ aKeyMap[ KEY_LEFT ] = OUString( u'\x21e0' );
+ aKeyMap[ KEY_RIGHT ] = OUString( u'\x21e2' );
+ aKeyMap[ KEY_HOME ] = OUString( u'\x2196' );
+ aKeyMap[ KEY_END ] = OUString( u'\x2198' );
+ aKeyMap[ KEY_PAGEUP ] = OUString( u'\x21de' );
+ aKeyMap[ KEY_PAGEDOWN ] = OUString( u'\x21df' );
+ aKeyMap[ KEY_RETURN ] = OUString( u'\x21a9' );
+ aKeyMap[ KEY_ESCAPE ] = "esc";
+ aKeyMap[ KEY_TAB ] = OUString( u'\x21e5' );
+ aKeyMap[ KEY_BACKSPACE ]= OUString( u'\x232b' );
+ aKeyMap[ KEY_SPACE ] = OUString( u'\x2423' );
+ aKeyMap[ KEY_DELETE ] = OUString( u'\x2326' );
+ aKeyMap[ KEY_ADD ] = "+";
+ aKeyMap[ KEY_SUBTRACT ] = "-";
+ aKeyMap[ KEY_DIVIDE ] = "/";
+ aKeyMap[ KEY_MULTIPLY ] = "*";
+ aKeyMap[ KEY_POINT ] = ".";
+ aKeyMap[ KEY_COMMA ] = ",";
+ aKeyMap[ KEY_LESS ] = "<";
+ aKeyMap[ KEY_GREATER ] = ">";
+ aKeyMap[ KEY_EQUAL ] = "=";
+ aKeyMap[ KEY_OPEN ] = OUString( u'\x23cf' );
+ aKeyMap[ KEY_TILDE ] = "~";
+ aKeyMap[ KEY_BRACKETLEFT ] = "[";
+ aKeyMap[ KEY_BRACKETRIGHT ] = "]";
+ aKeyMap[ KEY_SEMICOLON ] = ";";
+ aKeyMap[ KEY_QUOTERIGHT ] = "'";
+ aKeyMap[ KEY_RIGHTCURLYBRACKET ] = "}";
+ aKeyMap[ KEY_NUMBERSIGN ] = "#";
+ aKeyMap[ KEY_COLON ] = ":";
+
+ /* yet unmapped KEYCODES:
+ aKeyMap[ KEY_INSERT ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_CUT ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_COPY ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_PASTE ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_UNDO ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_REPEAT ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_FIND ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_PROPERTIES ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_FRONT ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_CONTEXTMENU ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_MENU ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_HELP ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_HANGUL_HANJA ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_DECIMAL ] = OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_QUOTELEFT ]= OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_CAPSLOCK ]= OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_NUMLOCK ]= OUString( sal_Unicode( ) );
+ aKeyMap[ KEY_SCROLLLOCK ]= OUString( sal_Unicode( ) );
+ */
+
+ }
+
+ OUStringBuffer aResult( 16 );
+
+ sal_uInt16 nUnmodifiedCode = (nKeyCode & KEY_CODE_MASK);
+ std::map< sal_uInt16, OUString >::const_iterator it = aKeyMap.find( nUnmodifiedCode );
+ if( it != aKeyMap.end() )
+ {
+ if( (nKeyCode & KEY_SHIFT) != 0 )
+ aResult.append( u'\x21e7' ); // shift
+ if( (nKeyCode & KEY_MOD1) != 0 )
+ aResult.append( u'\x2318' ); // command
+ if( (nKeyCode & KEY_MOD2) != 0 )
+ aResult.append( u'\x2325' ); // alternate
+ if( (nKeyCode & KEY_MOD3) != 0 )
+ aResult.append( u'\x2303' ); // control
+
+ aResult.append( it->second );
+ }
+
+ return aResult.makeStringAndClear();
+}
+
+static void getAppleScrollBarVariant(StyleSettings &rSettings)
+{
+ bool bIsScrollbarDoubleMax = true; // default is DoubleMax
+
+ CFStringRef AppleScrollBarType = CFSTR("AppleScrollBarVariant");
+ if( AppleScrollBarType )
+ {
+ CFStringRef ScrollBarVariant = static_cast<CFStringRef>(CFPreferencesCopyAppValue( AppleScrollBarType, kCFPreferencesCurrentApplication ));
+ if( ScrollBarVariant )
+ {
+ if( CFGetTypeID( ScrollBarVariant ) == CFStringGetTypeID() )
+ {
+ // TODO: check for the less important variants "DoubleMin" and "DoubleBoth" too
+ CFStringRef DoubleMax = CFSTR("DoubleMax");
+ if (DoubleMax)
+ {
+ if ( !CFStringCompare(ScrollBarVariant, DoubleMax, kCFCompareCaseInsensitive) )
+ bIsScrollbarDoubleMax = true;
+ else
+ bIsScrollbarDoubleMax = false;
+ CFRelease(DoubleMax);
+ }
+ }
+ CFRelease( ScrollBarVariant );
+ }
+ CFRelease(AppleScrollBarType);
+ }
+
+ GetSalData()->mbIsScrollbarDoubleMax = bIsScrollbarDoubleMax;
+
+ CFStringRef jumpScroll = CFSTR("AppleScrollerPagingBehavior");
+ if( jumpScroll )
+ {
+ CFBooleanRef jumpStr = static_cast<CFBooleanRef>(CFPreferencesCopyAppValue( jumpScroll, kCFPreferencesCurrentApplication ));
+ if( jumpStr )
+ {
+ if( CFGetTypeID( jumpStr ) == CFBooleanGetTypeID() )
+ rSettings.SetPrimaryButtonWarpsSlider(jumpStr == kCFBooleanTrue);
+ CFRelease( jumpStr );
+ }
+ CFRelease( jumpScroll );
+ }
+}
+
+static Color getNSBoxBackgroundColor(NSColor* pSysColor)
+{
+ // Figuring out what a NSBox will draw for windowBackground, etc. seems very difficult.
+ // So just draw to a 1x1 surface and read what actually gets drawn
+ // This is similar to getPixel
+#if defined OSL_BIGENDIAN
+ struct
+ {
+ unsigned char b, g, r, a;
+ } aPixel;
+#else
+ struct
+ {
+ unsigned char a, r, g, b;
+ } aPixel;
+#endif
+
+ // create a one-pixel bitmap context
+ CGContextRef xOnePixelContext = CGBitmapContextCreate(
+ &aPixel, 1, 1, 8, 32, GetSalData()->mxRGBSpace,
+ uint32_t(kCGImageAlphaNoneSkipFirst) | uint32_t(kCGBitmapByteOrder32Big));
+
+ NSGraphicsContext* graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:xOnePixelContext flipped:NO];
+
+ NSRect rect = { NSZeroPoint, NSMakeSize(1, 1) };
+ NSBox* pBox = [[NSBox alloc] initWithFrame: rect];
+
+ [pBox setBoxType: NSBoxCustom];
+ [pBox setFillColor: pSysColor];
+ SAL_WNODEPRECATED_DECLARATIONS_PUSH // setBorderType first deprecated in macOS 10.15
+ [pBox setBorderType: NSNoBorder];
+ SAL_WNODEPRECATED_DECLARATIONS_POP
+
+ [pBox displayRectIgnoringOpacity: rect inContext: graphicsContext];
+
+ [pBox release];
+
+ CGContextRelease(xOnePixelContext);
+
+ return Color(aPixel.r, aPixel.g, aPixel.b);
+}
+
+static Color getColor( NSColor* pSysColor, const Color& rDefault, NSWindow* pWin )
+{
+ Color aRet( rDefault );
+ if( pSysColor )
+ {
+ // transform to RGB
+SAL_WNODEPRECATED_DECLARATIONS_PUSH
+ // "'colorUsingColorSpaceName:device:' is deprecated: first deprecated in macOS 10.14 -
+ // Use -colorUsingType: or -colorUsingColorSpace: instead"
+ NSColor* pRBGColor = [pSysColor colorUsingColorSpaceName: NSDeviceRGBColorSpace device: [pWin deviceDescription]];
+SAL_WNODEPRECATED_DECLARATIONS_POP
+ if( pRBGColor )
+ {
+ CGFloat r = 0, g = 0, b = 0, a = 0;
+ [pRBGColor getRed: &r green: &g blue: &b alpha: &a];
+ aRet = Color( int(r*255.999), int(g*255.999), int(b*255.999) );
+ }
+ }
+ return aRet;
+}
+
+static vcl::Font getFont( NSFont* pFont, sal_Int32 nDPIY, const vcl::Font& rDefault )
+{
+ vcl::Font aResult( rDefault );
+ if( pFont )
+ {
+ aResult.SetFamilyName( GetOUString( [pFont familyName] ) );
+ aResult.SetFontHeight( static_cast<int>(ceil([pFont pointSize] * 72.0 / static_cast<float>(nDPIY))) );
+ aResult.SetItalic( ([pFont italicAngle] != 0.0) ? ITALIC_NORMAL : ITALIC_NONE );
+ // FIMXE: bold ?
+ }
+
+ return aResult;
+}
+
+void AquaSalFrame::getResolution( sal_Int32& o_rDPIX, sal_Int32& o_rDPIY )
+{
+ OSX_SALDATA_RUNINMAIN( getResolution( o_rDPIX, o_rDPIY ) )
+
+ if( ! mpGraphics )
+ {
+ AcquireGraphics();
+ ReleaseGraphics( mpGraphics );
+ }
+ mpGraphics->GetResolution( o_rDPIX, o_rDPIY );
+}
+
+void AquaSalFrame::UpdateDarkMode()
+{
+ if (@available(macOS 10.14, iOS 13, *))
+ {
+ switch (MiscSettings::GetDarkMode())
+ {
+ case 0: // auto
+ default:
+ [NSApp setAppearance: nil];
+ break;
+ case 1: // light
+ [NSApp setAppearance: [NSAppearance appearanceNamed: NSAppearanceNameAqua]];
+ break;
+ case 2: // dark
+ [NSApp setAppearance: [NSAppearance appearanceNamed: NSAppearanceNameDarkAqua]];
+ break;
+ }
+ }
+}
+
+bool AquaSalFrame::GetUseDarkMode() const
+{
+ if (!mpNSView)
+ return false;
+ bool bUseDarkMode(false);
+ if (@available(macOS 10.14, iOS 13, *))
+ {
+ NSAppearanceName match = [mpNSView.effectiveAppearance bestMatchFromAppearancesWithNames: @[
+ NSAppearanceNameAqua, NSAppearanceNameDarkAqua]];
+ bUseDarkMode = [match isEqualToString: NSAppearanceNameDarkAqua];
+ }
+ return bUseDarkMode;
+}
+
+bool AquaSalFrame::GetUseReducedAnimation() const
+{
+ return [[NSWorkspace sharedWorkspace] accessibilityDisplayShouldReduceMotion];
+}
+
+// on OSX-Aqua the style settings are independent of the frame, so it does
+// not really belong here. Since the connection to the Appearance_Manager
+// is currently done in salnativewidgets.cxx this would be a good place.
+// On the other hand VCL's platform independent code currently only asks
+// SalFrames for system settings anyway, so moving the code somewhere else
+// doesn't make the anything cleaner for now
+void AquaSalFrame::UpdateSettings( AllSettings& rSettings )
+{
+ if ( !mpNSWindow )
+ return;
+
+ OSX_SALDATA_RUNINMAIN( UpdateSettings( rSettings ) )
+
+SAL_WNODEPRECATED_DECLARATIONS_PUSH
+ // "'lockFocus' is deprecated: first deprecated in macOS 10.14 - To draw, subclass NSView
+ // and implement -drawRect:; AppKit's automatic deferred display mechanism will call
+ // -drawRect: as necessary to display the view."
+ if (![mpNSView lockFocusIfCanDraw])
+ return;
+SAL_WNODEPRECATED_DECLARATIONS_POP
+
+SAL_WNODEPRECATED_DECLARATIONS_PUSH
+ // "'setCurrentAppearance:' is deprecated: first deprecated in macOS 12.0 - Use
+ // -performAsCurrentDrawingAppearance: to temporarily set the drawing appearance, or
+ // +currentDrawingAppearance to access the currently drawing appearance."
+ [NSAppearance setCurrentAppearance: mpNSView.effectiveAppearance];
+SAL_WNODEPRECATED_DECLARATIONS_POP
+
+ StyleSettings aStyleSettings = rSettings.GetStyleSettings();
+
+ bool bUseDarkMode(GetUseDarkMode());
+ OUString sThemeName(!bUseDarkMode ? u"sukapura" : u"sukapura_dark");
+ aStyleSettings.SetPreferredIconTheme(sThemeName, bUseDarkMode);
+
+ Color aControlBackgroundColor(getNSBoxBackgroundColor([NSColor controlBackgroundColor]));
+ Color aWindowBackgroundColor(getNSBoxBackgroundColor([NSColor windowBackgroundColor]));
+ Color aUnderPageBackgroundColor(getNSBoxBackgroundColor([NSColor underPageBackgroundColor]));
+
+ // Background Color
+ aStyleSettings.BatchSetBackgrounds( aWindowBackgroundColor, false );
+ aStyleSettings.SetLightBorderColor( aWindowBackgroundColor );
+
+ aStyleSettings.SetActiveTabColor(aWindowBackgroundColor);
+ Color aInactiveTabColor( aWindowBackgroundColor );
+ aInactiveTabColor.DecreaseLuminance( 32 );
+ aStyleSettings.SetInactiveTabColor( aInactiveTabColor );
+
+ Color aShadowColor = getColor( [NSColor systemGrayColor ],
+ aStyleSettings.GetShadowColor(), mpNSWindow );
+ aStyleSettings.SetShadowColor( aShadowColor );
+
+ // tdf#152284 for DarkMode brighten it, while darken for BrightMode
+ NSColor* pDarkColor = bUseDarkMode ? [[NSColor systemGrayColor] highlightWithLevel: 0.5]
+ : [[NSColor systemGrayColor] shadowWithLevel: 0.5];
+ Color aDarkShadowColor = getColor( pDarkColor, aStyleSettings.GetDarkShadowColor(), mpNSWindow );
+ aStyleSettings.SetDarkShadowColor(aDarkShadowColor);
+
+ // get the system font settings
+ vcl::Font aAppFont = aStyleSettings.GetAppFont();
+ sal_Int32 nDPIX = 72, nDPIY = 72;
+ getResolution( nDPIX, nDPIY );
+ aAppFont = getFont( [NSFont systemFontOfSize: 0], nDPIY, aAppFont );
+
+ aStyleSettings.SetToolbarIconSize( ToolbarIconSize::Large );
+
+ // TODO: better mapping of macOS<->LibreOffice font settings
+ vcl::Font aLabelFont( getFont( [NSFont labelFontOfSize: 0], nDPIY, aAppFont ) );
+ aStyleSettings.BatchSetFonts( aAppFont, aLabelFont );
+ vcl::Font aMenuFont( getFont( [NSFont menuFontOfSize: 0], nDPIY, aAppFont ) );
+ aStyleSettings.SetMenuFont( aMenuFont );
+
+ vcl::Font aTitleFont( getFont( [NSFont titleBarFontOfSize: 0], nDPIY, aAppFont ) );
+ aStyleSettings.SetTitleFont( aTitleFont );
+ aStyleSettings.SetFloatTitleFont( aTitleFont );
+
+ vcl::Font aTooltipFont(getFont([NSFont toolTipsFontOfSize: 0], nDPIY, aAppFont));
+ aStyleSettings.SetHelpFont(aTooltipFont);
+
+ Color aAccentColor( getColor( [NSColor controlAccentColor],
+ aStyleSettings.GetAccentColor(), mpNSWindow ) );
+ aStyleSettings.SetAccentColor( aAccentColor );
+
+ Color aHighlightColor( getColor( [NSColor selectedTextBackgroundColor],
+ aStyleSettings.GetHighlightColor(), mpNSWindow ) );
+ aStyleSettings.SetHighlightColor( aHighlightColor );
+ Color aHighlightTextColor( getColor( [NSColor selectedTextColor],
+ aStyleSettings.GetHighlightTextColor(), mpNSWindow ) );
+ aStyleSettings.SetHighlightTextColor( aHighlightTextColor );
+
+ aStyleSettings.SetLinkColor(getColor( [NSColor linkColor],
+ aStyleSettings.GetLinkColor(), mpNSWindow ) );
+ aStyleSettings.SetVisitedLinkColor(getColor( [NSColor purpleColor],
+ aStyleSettings.GetVisitedLinkColor(), mpNSWindow ) );
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ Color aMenuHighlightColor( getColor( [NSColor selectedMenuItemColor],
+ aStyleSettings.GetMenuHighlightColor(), mpNSWindow ) );
+#pragma clang diagnostic pop
+ aStyleSettings.SetMenuHighlightColor( aMenuHighlightColor );
+ Color aMenuHighlightTextColor( getColor( [NSColor selectedMenuItemTextColor],
+ aStyleSettings.GetMenuHighlightTextColor(), mpNSWindow ) );
+ aStyleSettings.SetMenuHighlightTextColor( aMenuHighlightTextColor );
+
+ aStyleSettings.SetMenuColor( aWindowBackgroundColor );
+ Color aMenuTextColor( getColor( [NSColor textColor],
+ aStyleSettings.GetMenuTextColor(), mpNSWindow ) );
+ aStyleSettings.SetMenuTextColor( aMenuTextColor );
+ aStyleSettings.SetMenuBarTextColor( aMenuTextColor );
+ aStyleSettings.SetMenuBarRolloverTextColor( aMenuTextColor );
+ aStyleSettings.SetMenuBarHighlightTextColor(aStyleSettings.GetMenuHighlightTextColor());
+
+ aStyleSettings.SetListBoxWindowBackgroundColor( aWindowBackgroundColor );
+ aStyleSettings.SetListBoxWindowTextColor( aMenuTextColor );
+ aStyleSettings.SetListBoxWindowHighlightColor( aMenuHighlightColor );
+ aStyleSettings.SetListBoxWindowHighlightTextColor( aMenuHighlightTextColor );
+
+ // FIXME: Starting with macOS Big Sur, coloring has changed. Currently there is no documentation which system color should be
+ // used for some button states and for selected tab text. As a workaround the current OS version has to be considered. This code
+ // has to be reviewed once issue is covered by documentation.
+
+ // Set text colors for buttons and their different status according to OS settings, typically white for selected buttons,
+ // black otherwise
+
+ NSOperatingSystemVersion aOSVersion = { .majorVersion = 10, .minorVersion = 16, .patchVersion = 0 };
+ Color aControlTextColor(getColor([NSColor controlTextColor], COL_BLACK, mpNSWindow ));
+ Color aSelectedControlTextColor(getColor([NSColor selectedControlTextColor], COL_BLACK, mpNSWindow ));
+ Color aAlternateSelectedControlTextColor(getColor([NSColor alternateSelectedControlTextColor], COL_WHITE, mpNSWindow ));
+ aStyleSettings.SetWindowColor(aWindowBackgroundColor);
+ aStyleSettings.SetListBoxWindowBackgroundColor(aWindowBackgroundColor);
+
+ aStyleSettings.SetDialogTextColor(aControlTextColor);
+ aStyleSettings.SetButtonTextColor(aControlTextColor);
+ aStyleSettings.SetActionButtonTextColor(aControlTextColor);
+ aStyleSettings.SetRadioCheckTextColor(aControlTextColor);
+ aStyleSettings.SetGroupTextColor(aControlTextColor);
+ aStyleSettings.SetLabelTextColor(aControlTextColor);
+ aStyleSettings.SetWindowTextColor(aControlTextColor);
+ aStyleSettings.SetFieldTextColor(aControlTextColor);
+
+ aStyleSettings.SetFieldRolloverTextColor(aControlTextColor);
+ aStyleSettings.SetFieldColor(aControlBackgroundColor);
+ aStyleSettings.SetDefaultActionButtonTextColor(aAlternateSelectedControlTextColor);
+ aStyleSettings.SetFlatButtonTextColor(aControlTextColor);
+ aStyleSettings.SetDefaultButtonRolloverTextColor(aAlternateSelectedControlTextColor);
+ aStyleSettings.SetButtonRolloverTextColor(aControlTextColor);
+ aStyleSettings.SetDefaultActionButtonRolloverTextColor(aAlternateSelectedControlTextColor);
+ aStyleSettings.SetActionButtonRolloverTextColor(aControlTextColor);
+ aStyleSettings.SetFlatButtonRolloverTextColor(aControlTextColor);
+ aStyleSettings.SetDefaultButtonPressedRolloverTextColor(aAlternateSelectedControlTextColor);
+ aStyleSettings.SetDefaultActionButtonPressedRolloverTextColor(aAlternateSelectedControlTextColor);
+ aStyleSettings.SetFlatButtonPressedRolloverTextColor(aControlTextColor);
+ if ([NSProcessInfo.processInfo isOperatingSystemAtLeastVersion: aOSVersion])
+ {
+ aStyleSettings.SetDefaultButtonTextColor(aAlternateSelectedControlTextColor);
+ aStyleSettings.SetButtonPressedRolloverTextColor(aSelectedControlTextColor);
+ aStyleSettings.SetActionButtonPressedRolloverTextColor(aSelectedControlTextColor);
+ }
+ else
+ {
+ aStyleSettings.SetButtonPressedRolloverTextColor(aAlternateSelectedControlTextColor);
+ aStyleSettings.SetActionButtonPressedRolloverTextColor(aAlternateSelectedControlTextColor);
+ aStyleSettings.SetDefaultButtonTextColor(aSelectedControlTextColor);
+ }
+
+ aStyleSettings.SetWorkspaceColor(aUnderPageBackgroundColor);
+
+ aStyleSettings.SetHelpColor(aControlBackgroundColor);
+ aStyleSettings.SetHelpTextColor(aControlTextColor);
+ aStyleSettings.SetToolTextColor(aControlTextColor);
+
+ // Set text colors for tabs according to OS settings
+
+ aStyleSettings.SetTabTextColor(aControlTextColor);
+ aStyleSettings.SetTabRolloverTextColor(aControlTextColor);
+ if ([NSProcessInfo.processInfo isOperatingSystemAtLeastVersion: aOSVersion])
+ aStyleSettings.SetTabHighlightTextColor(aSelectedControlTextColor);
+ else
+ aStyleSettings.SetTabHighlightTextColor(aAlternateSelectedControlTextColor);
+
+ aStyleSettings.SetCursorBlinkTime( mnBlinkCursorDelay );
+ aStyleSettings.SetCursorSize(1);
+
+ // no mnemonics on macOS
+ aStyleSettings.SetOptions( aStyleSettings.GetOptions() | StyleSettingsOptions::NoMnemonics );
+
+ getAppleScrollBarVariant(aStyleSettings);
+
+ // set scrollbar size
+ aStyleSettings.SetScrollBarSize( static_cast<tools::Long>([NSScroller scrollerWidthForControlSize:NSControlSizeRegular scrollerStyle:NSScrollerStyleLegacy]) );
+ // images in menus false for MacOSX
+ aStyleSettings.SetPreferredUseImagesInMenus( false );
+ aStyleSettings.SetHideDisabledMenuItems( true );
+ aStyleSettings.SetPreferredContextMenuShortcuts( false );
+
+ rSettings.SetStyleSettings( aStyleSettings );
+
+ // don't draw frame around each and every toolbar
+ ImplGetSVData()->maNWFData.mbDockingAreaAvoidTBFrames = true;
+
+SAL_WNODEPRECATED_DECLARATIONS_PUSH
+ // "'unlockFocus' is deprecated: first deprecated in macOS 10.14 - To draw, subclass NSView
+ // and implement -drawRect:; AppKit's automatic deferred display mechanism will call
+ // -drawRect: as necessary to display the view."
+ [mpNSView unlockFocus];
+SAL_WNODEPRECATED_DECLARATIONS_POP
+}
+
+const SystemEnvData* AquaSalFrame::GetSystemData() const
+{
+ return &maSysData;
+}
+
+void AquaSalFrame::Beep()
+{
+ OSX_SALDATA_RUNINMAIN( Beep() )
+ NSBeep();
+}
+
+void AquaSalFrame::SetPosSize(
+ tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, sal_uInt16 nFlags)
+{
+ if (!mpNSWindow && !Application::IsBitmapRendering())
+ return;
+
+ OSX_SALDATA_RUNINMAIN( SetPosSize( nX, nY, nWidth, nHeight, nFlags ) )
+
+ SalEvent nEvent = PreparePosSize(nX, nY, nWidth, nHeight, nFlags);
+ if (Application::IsBitmapRendering())
+ return;
+
+ if( [mpNSWindow isMiniaturized] )
+ [mpNSWindow deminiaturize: NSApp]; // expand the window
+
+ NSRect aFrameRect = [mpNSWindow frame];
+ NSRect aContentRect = [NSWindow contentRectForFrameRect: aFrameRect styleMask: mnStyleMask];
+
+ // position is always relative to parent frame
+ NSRect aParentContentRect;
+
+ if( mpParent )
+ {
+ if( AllSettings::GetLayoutRTL() )
+ {
+ if( (nFlags & SAL_FRAME_POSSIZE_WIDTH) != 0 )
+ nX = static_cast<tools::Long>(mpParent->maGeometry.width()) - nWidth - 1 - nX;
+ else
+ nX = static_cast<tools::Long>(mpParent->maGeometry.width()) - aContentRect.size.width - 1 - nX;
+ }
+ NSRect aParentFrameRect = [mpParent->mpNSWindow frame];
+ aParentContentRect = [NSWindow contentRectForFrameRect: aParentFrameRect styleMask: mpParent->mnStyleMask];
+ }
+ else
+ aParentContentRect = maScreenRect; // use screen if no parent
+
+ CocoaToVCL( aContentRect );
+ CocoaToVCL( aParentContentRect );
+
+ bool bPaint = false;
+ if( (nFlags & (SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT)) != 0 )
+ {
+ if( nWidth != aContentRect.size.width || nHeight != aContentRect.size.height )
+ bPaint = true;
+ }
+
+ // use old window pos if no new pos requested
+ if( (nFlags & SAL_FRAME_POSSIZE_X) != 0 )
+ aContentRect.origin.x = nX + aParentContentRect.origin.x;
+ if( (nFlags & SAL_FRAME_POSSIZE_Y) != 0)
+ aContentRect.origin.y = nY + aParentContentRect.origin.y;
+
+ // use old size if no new size requested
+ if( (nFlags & SAL_FRAME_POSSIZE_WIDTH) != 0 )
+ aContentRect.size.width = nWidth;
+ if( (nFlags & SAL_FRAME_POSSIZE_HEIGHT) != 0)
+ aContentRect.size.height = nHeight;
+
+ VCLToCocoa( aContentRect );
+
+ // do not display yet, we need to update our backbuffer
+ {
+ [mpNSWindow setFrame: [NSWindow frameRectForContentRect: aContentRect styleMask: mnStyleMask] display: NO];
+ }
+
+ UpdateFrameGeometry();
+
+ if (nEvent != SalEvent::NONE)
+ CallCallback(nEvent, nullptr);
+
+ if( mbShown && bPaint )
+ {
+ // trigger filling our backbuffer
+ SendPaintEvent();
+
+ // now inform the system that the views need to be drawn
+ [mpNSWindow display];
+ }
+}
+
+void AquaSalFrame::GetWorkArea( AbsoluteScreenPixelRectangle& rRect )
+{
+ if (!mpNSWindow)
+ {
+ if (Application::IsBitmapRendering())
+ rRect = AbsoluteScreenPixelRectangle(AbsoluteScreenPixelPoint(0, 0), AbsoluteScreenPixelSize(1024, 768));
+ return;
+ }
+
+ OSX_SALDATA_RUNINMAIN( GetWorkArea( rRect ) )
+
+ NSScreen* pScreen = [mpNSWindow screen];
+ if( pScreen == nil )
+ pScreen = [NSScreen mainScreen];
+ NSRect aRect = [pScreen visibleFrame];
+ CocoaToVCL( aRect );
+ rRect.SetLeft( static_cast<tools::Long>(aRect.origin.x) );
+ rRect.SetTop( static_cast<tools::Long>(aRect.origin.y) );
+ rRect.SetRight( static_cast<tools::Long>(aRect.origin.x + aRect.size.width - 1) );
+ rRect.SetBottom( static_cast<tools::Long>(aRect.origin.y + aRect.size.height - 1) );
+}
+
+SalFrame::SalPointerState AquaSalFrame::GetPointerState()
+{
+ OSX_SALDATA_RUNINMAIN_UNION( GetPointerState(), state )
+
+ SalPointerState state;
+ state.mnState = 0;
+
+ // get position
+ NSPoint aPt = [mpNSWindow mouseLocationOutsideOfEventStream];
+ CocoaToVCL( aPt, false );
+ state.maPos = Point(static_cast<tools::Long>(aPt.x), static_cast<tools::Long>(aPt.y));
+
+ NSEvent* pCur = [NSApp currentEvent];
+ bool bMouseEvent = false;
+ if( pCur )
+ {
+ bMouseEvent = true;
+ switch( [pCur type] )
+ {
+ case NSEventTypeLeftMouseDown:
+ state.mnState |= MOUSE_LEFT;
+ break;
+ case NSEventTypeLeftMouseUp:
+ break;
+ case NSEventTypeRightMouseDown:
+ state.mnState |= MOUSE_RIGHT;
+ break;
+ case NSEventTypeRightMouseUp:
+ break;
+ case NSEventTypeOtherMouseDown:
+ state.mnState |= ([pCur buttonNumber] == 2) ? MOUSE_MIDDLE : 0;
+ break;
+ case NSEventTypeOtherMouseUp:
+ break;
+ case NSEventTypeMouseMoved:
+ break;
+ case NSEventTypeLeftMouseDragged:
+ state.mnState |= MOUSE_LEFT;
+ break;
+ case NSEventTypeRightMouseDragged:
+ state.mnState |= MOUSE_RIGHT;
+ break;
+ case NSEventTypeOtherMouseDragged:
+ state.mnState |= ([pCur buttonNumber] == 2) ? MOUSE_MIDDLE : 0;
+ break;
+ default:
+ bMouseEvent = false;
+ break;
+ }
+ }
+ if( bMouseEvent )
+ {
+ unsigned int nMask = static_cast<unsigned int>([pCur modifierFlags]);
+ if( (nMask & NSEventModifierFlagShift) != 0 )
+ state.mnState |= KEY_SHIFT;
+ if( (nMask & NSEventModifierFlagControl) != 0 )
+ state.mnState |= KEY_MOD3;
+ if( (nMask & NSEventModifierFlagOption) != 0 )
+ state.mnState |= KEY_MOD2;
+ if( (nMask & NSEventModifierFlagCommand) != 0 )
+ state.mnState |= KEY_MOD1;
+
+ }
+ else
+ {
+ // FIXME: replace Carbon by Cocoa
+ // Cocoa does not have an equivalent for GetCurrentEventButtonState
+ // and GetCurrentEventKeyModifiers.
+ // we could try to get away with tracking all events for modifierKeys
+ // and all mouse events for button state in VCL_NSApplication::sendEvent,
+ // but it is unclear whether this will get us the same result.
+ // leave in GetCurrentEventButtonState and GetCurrentEventKeyModifiers for now
+
+ // fill in button state
+ UInt32 nState = GetCurrentEventButtonState();
+ state.mnState = 0;
+ if( nState & 1 )
+ state.mnState |= MOUSE_LEFT; // primary button
+ if( nState & 2 )
+ state.mnState |= MOUSE_RIGHT; // secondary button
+ if( nState & 4 )
+ state.mnState |= MOUSE_MIDDLE; // tertiary button
+
+ // fill in modifier state
+ nState = GetCurrentEventKeyModifiers();
+ if( nState & shiftKey )
+ state.mnState |= KEY_SHIFT;
+ if( nState & controlKey )
+ state.mnState |= KEY_MOD3;
+ if( nState & optionKey )
+ state.mnState |= KEY_MOD2;
+ if( nState & cmdKey )
+ state.mnState |= KEY_MOD1;
+ }
+
+ return state;
+}
+
+KeyIndicatorState AquaSalFrame::GetIndicatorState()
+{
+ return KeyIndicatorState::NONE;
+}
+
+void AquaSalFrame::SimulateKeyPress( sal_uInt16 /*nKeyCode*/ )
+{
+}
+
+void AquaSalFrame::SetPluginParent( SystemParentData* )
+{
+ // plugin parent may be killed unexpectedly by
+ // plugging process;
+
+ //TODO: implement
+}
+
+bool AquaSalFrame::MapUnicodeToKeyCode( sal_Unicode , LanguageType , vcl::KeyCode& )
+{
+ // not supported yet
+ return false;
+}
+
+LanguageType AquaSalFrame::GetInputLanguage()
+{
+ //TODO: implement
+ return LANGUAGE_DONTKNOW;
+}
+
+void AquaSalFrame::SetMenu( SalMenu* pSalMenu )
+{
+ OSX_SALDATA_RUNINMAIN( SetMenu( pSalMenu ) )
+
+ AquaSalMenu* pMenu = static_cast<AquaSalMenu*>(pSalMenu);
+ SAL_WARN_IF( pMenu && !pMenu->mbMenuBar, "vcl", "setting non menubar on frame" );
+ mpMenu = pMenu;
+ if( mpMenu )
+ mpMenu->setMainMenu();
+}
+
+void AquaSalFrame::SetExtendedFrameStyle( SalExtStyle nStyle )
+{
+ if ( !mpNSWindow )
+ {
+ mnExtStyle = nStyle;
+ return;
+ }
+
+ OSX_SALDATA_RUNINMAIN( SetExtendedFrameStyle( nStyle ) )
+
+ if( (mnExtStyle & SAL_FRAME_EXT_STYLE_DOCMODIFIED) != (nStyle & SAL_FRAME_EXT_STYLE_DOCMODIFIED) )
+ [mpNSWindow setDocumentEdited: (nStyle & SAL_FRAME_EXT_STYLE_DOCMODIFIED) ? YES : NO];
+
+ mnExtStyle = nStyle;
+}
+
+SalFrame* AquaSalFrame::GetParent() const
+{
+ return mpParent;
+}
+
+void AquaSalFrame::SetParent( SalFrame* pNewParent )
+{
+ bool bShown = mbShown;
+ // remove from child list
+ if (bShown)
+ Show(false);
+ mpParent = static_cast<AquaSalFrame*>(pNewParent);
+ // insert to correct parent and paint
+ Show( bShown );
+}
+
+void AquaSalFrame::UpdateFrameGeometry()
+{
+ mbGeometryDidChange = false;
+
+ if ( !mpNSWindow )
+ return;
+
+ OSX_SALDATA_RUNINMAIN( UpdateFrameGeometry() )
+
+ // keep in mind that view and window coordinates are lower left
+ // whereas vcl's are upper left
+
+ // update screen rect
+ NSScreen * pScreen = [mpNSWindow screen];
+ if( pScreen )
+ {
+ NSRect aNewScreenRect = [pScreen frame];
+ if (!NSEqualRects(maScreenRect, aNewScreenRect))
+ {
+ mbGeometryDidChange = true;
+ maScreenRect = aNewScreenRect;
+ }
+ NSArray* pScreens = [NSScreen screens];
+ if( pScreens )
+ {
+ unsigned int nNewDisplayScreenNumber = [pScreens indexOfObject: pScreen];
+ if (maGeometry.screen() != nNewDisplayScreenNumber)
+ {
+ mbGeometryDidChange = true;
+ maGeometry.setScreen(nNewDisplayScreenNumber);
+ }
+ }
+ }
+
+ NSRect aFrameRect = [mpNSWindow frame];
+ NSRect aContentRect = [NSWindow contentRectForFrameRect: aFrameRect styleMask: mnStyleMask];
+
+ NSRect aTrackRect = { NSZeroPoint, aContentRect.size };
+
+ if (!NSEqualRects(maTrackingRect, aTrackRect))
+ {
+ mbGeometryDidChange = true;
+ maTrackingRect = aTrackRect;
+ }
+
+ // convert to vcl convention
+ CocoaToVCL( aFrameRect );
+ CocoaToVCL( aContentRect );
+
+ if (!NSEqualRects(maContentRect, aContentRect) || !NSEqualRects(maFrameRect, aFrameRect))
+ {
+ mbGeometryDidChange = true;
+
+ maContentRect = aContentRect;
+ maFrameRect = aFrameRect;
+
+ maGeometry.setX(static_cast<sal_Int32>(aContentRect.origin.x));
+ maGeometry.setY(static_cast<sal_Int32>(aContentRect.origin.y));
+ maGeometry.setWidth(static_cast<sal_uInt32>(aContentRect.size.width));
+ maGeometry.setHeight(static_cast<sal_uInt32>(aContentRect.size.height));
+
+ maGeometry.setLeftDecoration(static_cast<sal_uInt32>(aContentRect.origin.x - aFrameRect.origin.x));
+ maGeometry.setRightDecoration(static_cast<sal_uInt32>((aFrameRect.origin.x + aFrameRect.size.width) -
+ (aContentRect.origin.x + aContentRect.size.width)));
+ maGeometry.setTopDecoration(static_cast<sal_uInt32>(aContentRect.origin.y - aFrameRect.origin.y));
+ maGeometry.setBottomDecoration(static_cast<sal_uInt32>((aFrameRect.origin.y + aFrameRect.size.height) -
+ (aContentRect.origin.y + aContentRect.size.height)));
+ }
+}
+
+void AquaSalFrame::CaptureMouse( bool bCapture )
+{
+ /* Remark:
+ we'll try to use a pidgin version of capture mouse
+ on MacOSX (neither carbon nor cocoa) there is a
+ CaptureMouse equivalent (in Carbon there is TrackMouseLocation
+ but this is useless to use since it is blocking)
+
+ However on cocoa the active frame seems to get mouse events
+ also outside the window, so we'll try to forward mouse events
+ to the capture frame in the hope that one of our frames
+ gets a mouse event.
+
+ This will break as soon as the user activates another app, but
+ a mouse click will normally lead to a release of the mouse anyway.
+
+ Let's see how far we get this way. Alternatively we could use one
+ large overlay window like we did for the carbon implementation,
+ however that is resource intensive.
+ */
+
+ if( bCapture )
+ s_pCaptureFrame = this;
+ else if( ! bCapture && s_pCaptureFrame == this )
+ s_pCaptureFrame = nullptr;
+}
+
+void AquaSalFrame::ResetClipRegion()
+{
+ doResetClipRegion();
+}
+
+void AquaSalFrame::doResetClipRegion()
+{
+ if ( !mpNSWindow )
+ return;
+
+ OSX_SALDATA_RUNINMAIN( ResetClipRegion() )
+
+ // release old path and indicate no clipping
+ CGPathRelease( mrClippingPath );
+ mrClippingPath = nullptr;
+
+ if( mpNSView && mbShown )
+ [mpNSView setNeedsDisplay: YES];
+ [mpNSWindow setOpaque: YES];
+ [mpNSWindow invalidateShadow];
+}
+
+void AquaSalFrame::BeginSetClipRegion( sal_uInt32 nRects )
+{
+ if ( !mpNSWindow )
+ return;
+
+ OSX_SALDATA_RUNINMAIN( BeginSetClipRegion( nRects ) )
+
+ // release old path
+ if( mrClippingPath )
+ {
+ CGPathRelease( mrClippingPath );
+ mrClippingPath = nullptr;
+ }
+
+ if( maClippingRects.size() > SAL_CLIPRECT_COUNT && nRects < maClippingRects.size() )
+ {
+ std::vector<CGRect>().swap(maClippingRects);
+ }
+ maClippingRects.clear();
+ maClippingRects.reserve( nRects );
+}
+
+void AquaSalFrame::UnionClipRegion(
+ tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight )
+{
+ // #i113170# may not be the main thread if called from UNO API
+ SalData::ensureThreadAutoreleasePool();
+
+ if( nWidth && nHeight )
+ {
+ NSRect aRect = { { static_cast<CGFloat>(nX), static_cast<CGFloat>(nY) }, { static_cast<CGFloat>(nWidth), static_cast<CGFloat>(nHeight) } };
+ VCLToCocoa( aRect, false );
+ maClippingRects.push_back( CGRectMake(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height) );
+ }
+}
+
+void AquaSalFrame::EndSetClipRegion()
+{
+ if ( !mpNSWindow )
+ return;
+
+ OSX_SALDATA_RUNINMAIN( EndSetClipRegion() )
+
+ if( ! maClippingRects.empty() )
+ {
+ mrClippingPath = CGPathCreateMutable();
+ CGPathAddRects( mrClippingPath, nullptr, maClippingRects.data(), maClippingRects.size() );
+ }
+ if( mpNSView && mbShown )
+ [mpNSView setNeedsDisplay: YES];
+ [mpNSWindow setOpaque: (mrClippingPath != nullptr) ? NO : YES];
+ [mpNSWindow setBackgroundColor: [NSColor clearColor]];
+ // shadow is invalidated when view gets drawn again
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */