[Libreoffice-commits] core.git: Branch 'private/tml/lov-7.0.4' - 5 commits - desktop/Executable_soffice_bin.mk include/sal sot/source vcl/inc vcl/osx vcl/quartz

Thorsten Wagner (via logerrit) logerrit at kemper.freedesktop.org
Mon Jan 18 15:45:20 UTC 2021


 desktop/Executable_soffice_bin.mk |   12 -
 include/sal/log-areas.dox         |    1 
 sot/source/base/exchange.cxx      |    8 
 vcl/inc/quartz/CGHelpers.hxx      |    2 
 vcl/inc/quartz/salgdi.h           |   11 +
 vcl/osx/DataFlavorMapping.cxx     |   79 +++++----
 vcl/osx/salframe.cxx              |   32 ++-
 vcl/osx/salinst.cxx               |   15 +
 vcl/quartz/salbmp.cxx             |   22 +-
 vcl/quartz/salgdi.cxx             |    8 
 vcl/quartz/salgdicommon.cxx       |  314 +++++++++++++++-----------------------
 vcl/quartz/salgdiutils.cxx        |   41 ++++
 vcl/quartz/salvd.cxx              |  113 +++++--------
 13 files changed, 330 insertions(+), 328 deletions(-)

New commits:
commit 2ca3dad5f5604890fa83ed8220cf7deeab26250e
Author:     Thorsten Wagner <thorsten.wagner.4 at gmail.com>
AuthorDate: Sun Jan 17 20:24:26 2021 +0100
Commit:     Tor Lillqvist <tml at iki.fi>
CommitDate: Mon Jan 18 15:53:50 2021 +0200

    tdf#138314 Change selected tab text color on macOS Big Sur
    
    Starting with macOS Big Sur, coloring has changed. Currently there is
    no documentation which system color should be used for selected tab
    text. As a workaround text color is changed for macOS Big Sur and
    newer only.
    
    Change-Id: I2e8c83527775c17f95e5e4afe70577442e14715e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/109479
    Tested-by: Tor Lillqvist <tml at collabora.com>
    Reviewed-by: Tor Lillqvist <tml at collabora.com>

diff --git a/vcl/osx/salframe.cxx b/vcl/osx/salframe.cxx
index 71cc5a4f1982..f4c7379cf42b 100644
--- a/vcl/osx/salframe.cxx
+++ b/vcl/osx/salframe.cxx
@@ -1301,27 +1301,37 @@ SAL_WNODEPRECATED_DECLARATIONS_POP
     // black otherwise
 
     Color aControlTextColor(getColor([NSColor controlTextColor], COL_BLACK, mpNSWindow));
-    Color aSelectedControlTextColor(getColor([NSColor alternateSelectedControlTextColor], COL_WHITE, mpNSWindow));
-    aStyleSettings.SetDefaultButtonTextColor(aSelectedControlTextColor);
+    Color aSelectedControlTextColor(getColor([NSColor selectedControlTextColor], COL_BLACK, mpNSWindow));
+    Color aAlternateSelectedControlTextColor(getColor([NSColor alternateSelectedControlTextColor], COL_WHITE, mpNSWindow));
+    aStyleSettings.SetDefaultButtonTextColor(aAlternateSelectedControlTextColor);
     aStyleSettings.SetButtonTextColor(aControlTextColor);
-    aStyleSettings.SetDefaultActionButtonTextColor(aSelectedControlTextColor);
+    aStyleSettings.SetDefaultActionButtonTextColor(aAlternateSelectedControlTextColor);
     aStyleSettings.SetActionButtonTextColor(aControlTextColor);
     aStyleSettings.SetFlatButtonTextColor(aControlTextColor);
-    aStyleSettings.SetDefaultButtonRolloverTextColor(aSelectedControlTextColor);
+    aStyleSettings.SetDefaultButtonRolloverTextColor(aAlternateSelectedControlTextColor);
     aStyleSettings.SetButtonRolloverTextColor(aControlTextColor);
-    aStyleSettings.SetDefaultActionButtonRolloverTextColor(aSelectedControlTextColor);
+    aStyleSettings.SetDefaultActionButtonRolloverTextColor(aAlternateSelectedControlTextColor);
     aStyleSettings.SetActionButtonRolloverTextColor(aControlTextColor);
     aStyleSettings.SetFlatButtonRolloverTextColor(aControlTextColor);
-    aStyleSettings.SetDefaultButtonPressedRolloverTextColor(aSelectedControlTextColor);
-    aStyleSettings.SetButtonPressedRolloverTextColor(aSelectedControlTextColor);
-    aStyleSettings.SetDefaultActionButtonPressedRolloverTextColor(aSelectedControlTextColor);
-    aStyleSettings.SetActionButtonPressedRolloverTextColor(aSelectedControlTextColor);
+    aStyleSettings.SetDefaultButtonPressedRolloverTextColor(aAlternateSelectedControlTextColor);
+    aStyleSettings.SetButtonPressedRolloverTextColor(aAlternateSelectedControlTextColor);
+    aStyleSettings.SetDefaultActionButtonPressedRolloverTextColor(aAlternateSelectedControlTextColor);
+    aStyleSettings.SetActionButtonPressedRolloverTextColor(aAlternateSelectedControlTextColor);
     aStyleSettings.SetFlatButtonPressedRolloverTextColor(aControlTextColor);
 
-    // Set text colors for tabs according to OS settings, typically white for selected buttons, black otherwise
+    // Set text colors for tabs according to OS settings
 
     aStyleSettings.SetTabTextColor(aControlTextColor);
-    aStyleSettings.SetTabHighlightTextColor(aSelectedControlTextColor);
+
+    // FIXME: Starting with macOS Big Sur, coloring has changed. Currently there is no documentation which system color should be
+    // used 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.
+
+    NSOperatingSystemVersion aOSVersion = { .majorVersion = 11, .minorVersion = 0, .patchVersion = 0 };
+    if ([NSProcessInfo.processInfo isOperatingSystemAtLeastVersion: aOSVersion])
+        aStyleSettings.SetTabHighlightTextColor(aSelectedControlTextColor);
+    else
+        aStyleSettings.SetTabHighlightTextColor(aAlternateSelectedControlTextColor);
 
     aStyleSettings.SetCursorBlinkTime( mnBlinkCursorDelay );
 
commit 539e1e9f60923ec75ed68992f93e3717f9251dcd
Author:     Thorsten Wagner <thorsten.wagner.4 at gmail.com>
AuthorDate: Sun Jan 10 23:49:25 2021 +0100
Commit:     Tor Lillqvist <tml at iki.fi>
CommitDate: Mon Jan 18 15:49:19 2021 +0200

    tdf#138122 Add window scaling for retina displays on macOS
    
    (1) Remove hack to make application look as if being linked against SDK 10.13
    
    (2) Use quad storage size on virtual devices for displaying on retina displays thereafter
    
    (3) Apply workaround to downsample bitmaps from scaled layers (to be implemented)
    
    (4) Disable dark mode (to be implemented)
    
    (5) Provide new environment variables:
    
    VCL_MACOS_FORCE_WINDOW_SCALING:
    window scaling on non retina displays
    
    VCL_MACOS_FORCE_DARK_MODE:
    enable dark mode (macOS 10.14, iOS 13 and newer)
    
    VCL_MACOS_USE_SYSTEM_APPEARANCE:
    use light mode or dark mode (macOS 10.14, iOS 13 and newer) as configured by system preferences
    
    Change-Id: I99877cd62a98cb91bcbf27af62b043c31c5f5fc9
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/109072
    Tested-by: Jenkins
    Reviewed-by: Tor Lillqvist <tml at collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/109495
    Reviewed-by: Adolfo Jayme Barrientos <fitojb at ubuntu.com>

diff --git a/desktop/Executable_soffice_bin.mk b/desktop/Executable_soffice_bin.mk
index 5c895ed0e7cb..9c08a28361df 100644
--- a/desktop/Executable_soffice_bin.mk
+++ b/desktop/Executable_soffice_bin.mk
@@ -23,18 +23,6 @@ $(eval $(call gb_Executable_add_cobjects,soffice_bin,\
     desktop/source/app/main \
 ))
 
-ifeq ($(OS),MACOSX)
-# At least when building against SDK 10.15, changing the LC_VERSION_MIN_MACOSX load command's sdk
-# value from 10.15 to "n/a" (i.e., 0.0.0) is necessary to avoid blurry text in the LO UI (see
-# <https://github.com/llvm/llvm-project/commit/25ce33a6e4f3b13732c0f851e68390dc2acb9123>
-# "[driver][darwin] Pass -platform_version flag to the linker instead of the
-# -<platform>_version_min flag", clang/test/Driver/darwin-ld-platform-version-macos.c in particular,
-# for the -platform_version that Clang passes by default to new-enough ld):
-$(eval $(call gb_Executable_add_ldflags,soffice_bin, \
-    -Xlinker -platform_version -Xlinker macos -Xlinker $(MACOSX_DEPLOYMENT_TARGET) -Xlinker 0.0.0 \
-))
-endif
-
 ifeq ($(OS),WNT)
 
 $(eval $(call gb_Executable_set_targettype_gui,soffice_bin,NO))
diff --git a/vcl/inc/quartz/salgdi.h b/vcl/inc/quartz/salgdi.h
index 2ac33fdbdcf7..88024b7e4879 100644
--- a/vcl/inc/quartz/salgdi.h
+++ b/vcl/inc/quartz/salgdi.h
@@ -174,6 +174,7 @@ class AquaSalGraphics : public SalGraphics
 #ifdef MACOSX
     /// is this a window graphics
     bool                                    mbWindow;
+    bool                                    mbWindowScaling;
 
 #else // IOS
 
@@ -189,6 +190,7 @@ public:
     bool                    IsPenVisible() const    { return maLineColor.IsVisible(); }
     bool                    IsBrushVisible() const  { return maFillColor.IsVisible(); }
 
+    float                   GetWindowScaling();
     void                    SetWindowGraphics( AquaSalFrame* pFrame );
     void                    SetPrinterGraphics( CGContextRef, long nRealDPIX, long nRealDPIY );
     void                    SetVirDevGraphics(CGLayerHolder const & rLayer, CGContextRef, int nBitDepth = 0);
@@ -296,9 +298,14 @@ public:
     virtual bool            drawAlphaRect( long nX, long nY, long nWidth,
                                            long nHeight, sal_uInt8 nTransparency ) override;
 
+protected:
+    virtual void            copyScaledArea( long nDestX, long nDestY, long nSrcX, long nSrcY,
+                                            long nSrcWidth, long nSrcHeight, SalGraphics* pSrcGraphics );
+
     // native widget rendering methods that require mirroring
+
 #ifdef MACOSX
-protected:
+
     virtual bool            isNativeControlSupported( ControlType nType, ControlPart nPart ) override;
 
     virtual bool            hitTestNativeControl( ControlType nType, ControlPart nPart, const tools::Rectangle& rControlRegion,
@@ -310,9 +317,9 @@ protected:
                                                     const ImplControlValue& aValue, const OUString& aCaption,
                                                     tools::Rectangle &rNativeBoundingRegion, tools::Rectangle &rNativeContentRegion ) override;
 
-public:
 #endif
 
+public:
     // get device resolution
     virtual void            GetResolution( sal_Int32& rDPIX, sal_Int32& rDPIY ) override;
     // get the depth of the device
diff --git a/vcl/osx/salinst.cxx b/vcl/osx/salinst.cxx
index c1b11ebfaa58..e7f1a4304a31 100644
--- a/vcl/osx/salinst.cxx
+++ b/vcl/osx/salinst.cxx
@@ -314,6 +314,21 @@ VCLPLUG_OSX_PUBLIC SalInstance* create_SalInstance()
     // put cocoa into multithreaded mode
     [NSThread detachNewThreadSelector:@selector(enableCocoaThreads:) toTarget:[[CocoaThreadEnabler alloc] init] withObject:nil];
 
+    // Dark mode is disabled as long as it is not implemented completely. For development purposes, it may be controlled by
+    // environment variables: VCL_MACOS_FORCE_DARK_MODE enable dark mode independent of system settings,
+    // VCL_MACOS_USE_SYSTEM_APPEARANCE to use system settings (light mode oder dark mode as configured within system preferences).
+
+    // TODO: After implementation of dark mode, this code has to be removed.
+
+    if (getenv("VCL_MACOS_FORCE_DARK_MODE"))
+    {
+        if (@available(macOS 10.14, iOS 13, *))
+            [NSApp setAppearance: [NSAppearance appearanceNamed: NSAppearanceNameDarkAqua]];
+    }
+    else
+        if (!getenv("VCL_MACOS_USE_SYSTEM_APPEARANCE"))
+           [NSApp setAppearance: [NSAppearance appearanceNamed: NSAppearanceNameAqua]];
+
     // activate our delegate methods
     [NSApp setDelegate: NSApp];
 
diff --git a/vcl/quartz/salbmp.cxx b/vcl/quartz/salbmp.cxx
index 8dd09d1765a3..bbf368a9ae53 100644
--- a/vcl/quartz/salbmp.cxx
+++ b/vcl/quartz/salbmp.cxx
@@ -71,6 +71,10 @@ QuartzSalBitmap::~QuartzSalBitmap()
 
 bool QuartzSalBitmap::Create(CGLayerHolder const & rLayerHolder, int nBitmapBits, int nX, int nY, int nWidth, int nHeight, bool bFlipped)
 {
+
+    // TODO: Bitmaps from scaled layers are reverted to single precision. This is a workaround only unless bitmaps with precision of
+    // source layer are implemented.
+
     SAL_WARN_IF(!rLayerHolder.isSet(), "vcl", "QuartzSalBitmap::Create() from non-layered context");
 
     // sanitize input parameters
@@ -84,7 +88,10 @@ bool QuartzSalBitmap::Create(CGLayerHolder const & rLayerHolder, int nBitmapBits
         nY = 0;
     }
 
-    const CGSize aLayerSize = CGLayerGetSize(rLayerHolder.get());
+    CGSize aLayerSize = CGLayerGetSize(rLayerHolder.get());
+    const float fScale = rLayerHolder.getScale();
+    aLayerSize.width /= fScale;
+    aLayerSize.height /= fScale;
 
     if( nWidth >= static_cast<int>(aLayerSize.width) - nX )
         nWidth = static_cast<int>(aLayerSize.width) - nX;
@@ -104,17 +111,18 @@ bool QuartzSalBitmap::Create(CGLayerHolder const & rLayerHolder, int nBitmapBits
     CreateContext();
 
     // copy layer content into the bitmap buffer
-    const CGPoint aSrcPoint = { static_cast<CGFloat>(-nX), static_cast<CGFloat>(-nY) };
-    if (maGraphicContext.isSet()) // remove warning
+    const CGPoint aSrcPoint = { static_cast<CGFloat>(-nX * fScale), static_cast<CGFloat>(-nY * fScale) };
+    if (maGraphicContext.isSet())
     {
         if( bFlipped )
         {
-            CGContextTranslateCTM( maGraphicContext.get(), 0, +mnHeight );
-
-            CGContextScaleCTM( maGraphicContext.get(), +1, -1 );
+            CGContextTranslateCTM(maGraphicContext.get(), 0, +mnHeight);
+            CGContextScaleCTM(maGraphicContext.get(), +1, -1);
         }
-
+        maGraphicContext.saveState();
+        CGContextScaleCTM(maGraphicContext.get(), 1 / fScale, 1 / fScale);
         CGContextDrawLayerAtPoint(maGraphicContext.get(), aSrcPoint, rLayerHolder.get());
+        maGraphicContext.restoreState();
     }
     return true;
 }
diff --git a/vcl/quartz/salgdi.cxx b/vcl/quartz/salgdi.cxx
index 83aebe2abc82..6909ac4ae4ae 100644
--- a/vcl/quartz/salgdi.cxx
+++ b/vcl/quartz/salgdi.cxx
@@ -204,6 +204,14 @@ AquaSalGraphics::AquaSalGraphics()
     , mbVirDev( false )
 #ifdef MACOSX
     , mbWindow( false )
+
+    // Window scaling independent from main display may be forced by setting VCL_MACOS_FORCE_WINDOW_SCALING environment variable. If
+    // unset window scaling from main display will be used. After implementation of full support of scaled displays window scaling
+    // will be set to 2.0f for macOS as default.
+
+    // TODO: After implementation of full support of scaled displays VCL_FORCE_WINDOW_SCALING control has to be removed.
+
+    , mbWindowScaling( getenv("VCL_MACOS_FORCE_WINDOW_SCALING") )
 #else
     , mbForeignContext( false )
 #endif
diff --git a/vcl/quartz/salgdicommon.cxx b/vcl/quartz/salgdicommon.cxx
index 8b51e91c6d54..e528a0f6c5f5 100644
--- a/vcl/quartz/salgdicommon.cxx
+++ b/vcl/quartz/salgdicommon.cxx
@@ -294,109 +294,6 @@ static void alignLinePoint( const SalPoint* i_pIn, float& o_fX, float& o_fY )
     o_fY = static_cast<float>(i_pIn->mnY ) + 0.5;
 }
 
-void AquaSalGraphics::copyBits( const SalTwoRect& rPosAry, SalGraphics *pSrcGraphics )
-{
-
-    if( !pSrcGraphics )
-    {
-        pSrcGraphics = this;
-    }
-    //from unix salgdi2.cxx
-    //[FIXME] find a better way to prevent calc from crashing when width and height are negative
-    if( rPosAry.mnSrcWidth <= 0 ||
-        rPosAry.mnSrcHeight <= 0 ||
-        rPosAry.mnDestWidth <= 0 ||
-        rPosAry.mnDestHeight <= 0 )
-    {
-        return;
-    }
-
-#ifdef IOS
-    // If called from idle layout, maContextHolder.get() is NULL, no idea what to do
-    if (!maContextHolder.isSet())
-        return;
-#endif
-
-    // accelerate trivial operations
-    /*const*/ AquaSalGraphics* pSrc = static_cast<AquaSalGraphics*>(pSrcGraphics);
-    const bool bSameGraphics = (this == pSrc)
-#ifdef MACOSX
-        || (mbWindow && mpFrame && pSrc->mbWindow && (mpFrame == pSrc->mpFrame))
-#endif
-        ;
-
-    if( bSameGraphics &&
-        (rPosAry.mnSrcWidth == rPosAry.mnDestWidth) &&
-        (rPosAry.mnSrcHeight == rPosAry.mnDestHeight))
-    {
-        // short circuit if there is nothing to do
-        if( (rPosAry.mnSrcX == rPosAry.mnDestX) &&
-            (rPosAry.mnSrcY == rPosAry.mnDestY))
-        {
-            return;
-        }
-        // use copyArea() if source and destination context are identical
-        copyArea( rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnSrcX, rPosAry.mnSrcY,
-                  rPosAry.mnSrcWidth, rPosAry.mnSrcHeight, false/*bWindowInvalidate*/ );
-        return;
-    }
-
-    ApplyXorContext();
-    pSrc->ApplyXorContext();
-
-    SAL_WARN_IF (!pSrc->maLayer.isSet(), "vcl.quartz",
-                 "AquaSalGraphics::copyBits() from non-layered graphics this=" << this);
-
-    const CGPoint aDstPoint = CGPointMake(+rPosAry.mnDestX - rPosAry.mnSrcX, rPosAry.mnDestY - rPosAry.mnSrcY);
-    if ((rPosAry.mnSrcWidth == rPosAry.mnDestWidth &&
-         rPosAry.mnSrcHeight == rPosAry.mnDestHeight) &&
-        (!mnBitmapDepth || (aDstPoint.x + pSrc->mnWidth) <= mnWidth)
-        && pSrc->maLayer.isSet()) // workaround for a Quartz crash
-    {
-        // in XOR mode the drawing context is redirected to the XOR mask
-        // if source and target are identical then copyBits() paints onto the target context though
-        CGContextHolder aCopyContext = maContextHolder;
-        if( mpXorEmulation && mpXorEmulation->IsEnabled() )
-        {
-            if( pSrcGraphics == this )
-            {
-                aCopyContext.set(mpXorEmulation->GetTargetContext());
-            }
-        }
-        aCopyContext.saveState();
-
-        const CGRect aDstRect = CGRectMake(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnDestWidth, rPosAry.mnDestHeight);
-        CGContextClipToRect(aCopyContext.get(), aDstRect);
-
-        // draw at new destination
-        // NOTE: flipped drawing gets disabled for this, else the subimage would be drawn upside down
-        if( pSrc->IsFlipped() )
-        {
-            CGContextTranslateCTM( aCopyContext.get(), 0, +mnHeight );
-            CGContextScaleCTM( aCopyContext.get(), +1, -1 );
-        }
-
-        // TODO: pSrc->size() != this->size()
-        CGContextDrawLayerAtPoint(aCopyContext.get(), aDstPoint, pSrc->maLayer.get());
-
-        aCopyContext.restoreState();
-        // mark the destination rectangle as updated
-        RefreshRect( aDstRect );
-    }
-    else
-    {
-        std::shared_ptr<SalBitmap> pBitmap = pSrc->getBitmap( rPosAry.mnSrcX, rPosAry.mnSrcY,
-                                              rPosAry.mnSrcWidth, rPosAry.mnSrcHeight );
-        if( pBitmap )
-        {
-            SalTwoRect aPosAry( rPosAry );
-            aPosAry.mnSrcX = 0;
-            aPosAry.mnSrcY = 0;
-            drawBitmap( aPosAry, *pBitmap );
-        }
-    }
-}
-
 static void DrawPattern50( void*, CGContextRef rContext )
 {
     static const CGRect aRects[2] = { { {0,0}, { 2, 2 } }, { { 2, 2 }, { 2, 2 } } };
@@ -464,82 +361,115 @@ void AquaSalGraphics::ApplyXorContext()
     }
 }
 
-void AquaSalGraphics::copyArea( long nDstX, long nDstY,long nSrcX, long nSrcY,
-                                long nSrcWidth, long nSrcHeight, bool /*bWindowInvalidate*/ )
+void AquaSalGraphics::copyBits(const SalTwoRect &rPosAry, SalGraphics *pSrcGraphics)
 {
-    SAL_WARN_IF (!maLayer.isSet(), "vcl.quartz",
-                 "AquaSalGraphics::copyArea() for non-layered graphics this=" << this);
+    if (!pSrcGraphics)
+        pSrcGraphics = this;
+    AquaSalGraphics *pSrc = static_cast<AquaSalGraphics*>(pSrcGraphics);
+    if (rPosAry.mnSrcWidth <= 0 || rPosAry.mnSrcHeight <= 0 || rPosAry.mnDestWidth <= 0 || rPosAry.mnDestHeight <= 0)
+        return;
+    if (!maContextHolder.isSet())
+        return;
 
-#ifdef IOS
-    if (!maLayer.isSet())
+    SAL_WARN_IF (!pSrc->maLayer.isSet(), "vcl.quartz", "AquaSalGraphics::copyBits() from non-layered graphics this=" << this);
+
+    // Layered graphics are copied by AquaSalGraphics::copyScaledArea() which is able to consider the layer's scaling.
+
+    if (pSrc->maLayer.isSet())
+        copyScaledArea(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnSrcX, rPosAry.mnSrcY,
+                       rPosAry.mnSrcWidth, rPosAry.mnSrcHeight, pSrcGraphics);
+    else
+    {
+        ApplyXorContext();
+        pSrc->ApplyXorContext();
+        std::shared_ptr<SalBitmap> pBitmap = pSrc->getBitmap(rPosAry.mnSrcX, rPosAry.mnSrcY,
+                                                             rPosAry.mnSrcWidth, rPosAry.mnSrcHeight);
+        if (pBitmap)
+        {
+            SalTwoRect aPosAry(rPosAry);
+            aPosAry.mnSrcX = 0;
+            aPosAry.mnSrcY = 0;
+            drawBitmap(aPosAry, *pBitmap);
+        }
+    }
+}
+
+void AquaSalGraphics::copyArea(long nDstX, long nDstY,long nSrcX, long nSrcY,
+                               long nSrcWidth, long nSrcHeight, bool)
+{
+    if (!maContextHolder.isSet())
         return;
-#endif
-    float fScale = maLayer.getScale();
 
-    long nScaledSourceX = nSrcX * fScale;
-    long nScaledSourceY = nSrcY * fScale;
+    // Functionality is implemented in protected member function AquaSalGraphics::copyScaledArea() which requires an additional
+    // parameter of type SalGraphics to be used in AquaSalGraphics::copyBits() too.
 
-    long nScaledTargetX = nDstX * fScale;
-    long nScaledTargetY = nDstY * fScale;
+    copyScaledArea(nDstX, nDstY, nSrcX, nSrcY, nSrcWidth, nSrcHeight, this);
+}
 
-    long nScaledSourceWidth = nSrcWidth * fScale;
-    long nScaledSourceHeight = nSrcHeight * fScale;
+void AquaSalGraphics::copyScaledArea(long nDstX, long nDstY,long nSrcX, long nSrcY,
+                                     long nSrcWidth, long nSrcHeight, SalGraphics *pSrcGraphics)
+{
+    if (!pSrcGraphics)
+        pSrcGraphics = this;
+    AquaSalGraphics *pSrc = static_cast<AquaSalGraphics*>(pSrcGraphics);
 
-    ApplyXorContext();
+    SAL_WARN_IF(!maLayer.isSet(), "vcl.quartz",
+                "AquaSalGraphics::copyScaledArea() without graphics context or for non-layered graphics this=" << this);
 
-    maContextHolder.saveState();
+    if (!maContextHolder.isSet() || !maLayer.isSet())
+        return;
 
-    // in XOR mode the drawing context is redirected to the XOR mask
-    // copyArea() always works on the target context though
-    CGContextRef xCopyContext = maContextHolder.get();
+    // Determine scaled geometry of source and target area assuming source and target area have the same scale
 
-    if( mpXorEmulation && mpXorEmulation->IsEnabled() )
-    {
-        xCopyContext = mpXorEmulation->GetTargetContext();
-    }
+    float fScale = maLayer.getScale();
+    CGFloat nScaledSourceX = nSrcX * fScale;
+    CGFloat nScaledSourceY = nSrcY * fScale;
+    CGFloat nScaledTargetX = nDstX * fScale;
+    CGFloat nScaledTargetY = nDstY * fScale;
+    CGFloat nScaledSourceWidth = nSrcWidth * fScale;
+    CGFloat nScaledSourceHeight = nSrcHeight * fScale;
 
-    // If we have a scaled layer, we need to revert the scaling or else
-    // it will interfere with the coordinate calculation
-    CGContextScaleCTM(xCopyContext, 1.0 / fScale, 1.0 / fScale);
+    // Apply XOR context and get copy context from current graphics context or XOR context
 
-    // drawing a layer onto its own context causes trouble on OSX => copy it first
-    // TODO: is it possible to get rid of this unneeded copy more often?
-    //       e.g. on OSX>=10.5 only this situation causes problems:
-    //          mnBitmapDepth && (aDstPoint.x + pSrc->mnWidth) > mnWidth
+    ApplyXorContext();
+    maContextHolder.saveState();
+    CGContextRef xCopyContext = maContextHolder.get();
+    if (mpXorEmulation && mpXorEmulation->IsEnabled())
+        xCopyContext = mpXorEmulation->GetTargetContext();
 
-    CGLayerHolder sSourceLayerHolder(maLayer);
-    {
-        const CGSize aSrcSize = CGSizeMake(nScaledSourceWidth, nScaledSourceHeight);
-        sSourceLayerHolder.set(CGLayerCreateWithContext(xCopyContext, aSrcSize, nullptr));
+    // Set scale matrix of copy context to consider layer scaling
 
-        const CGContextRef xSrcContext = CGLayerGetContext(sSourceLayerHolder.get());
+    CGContextScaleCTM(xCopyContext, 1 / fScale, 1 / fScale);
 
-        CGPoint aSrcPoint = CGPointMake(-nScaledSourceX, -nScaledSourceY);
-        if( IsFlipped() )
-        {
-            CGContextTranslateCTM( xSrcContext, 0, +nScaledSourceHeight );
-            CGContextScaleCTM( xSrcContext, +1, -1 );
-            aSrcPoint.y = (nScaledSourceY + nScaledSourceHeight) - (mnHeight * fScale);
-        }
-        CGContextSetBlendMode(xSrcContext, kCGBlendModeCopy);
+    // Creating an additional layer is required for drawing with the required scale and extent at the drawing destination
+    // thereafter.
 
-        CGContextDrawLayerAtPoint(xSrcContext, aSrcPoint, maLayer.get());
+    CGLayerHolder aSourceLayerHolder(pSrc->maLayer);
+    const CGSize aSourceSize = CGSizeMake(nScaledSourceWidth, nScaledSourceHeight);
+    aSourceLayerHolder.set(CGLayerCreateWithContext(xCopyContext, aSourceSize, nullptr));
+    const CGContextRef xSourceContext = CGLayerGetContext(aSourceLayerHolder.get());
+    CGPoint aSrcPoint = CGPointMake(-nScaledSourceX, -nScaledSourceY);
+    if (pSrc->IsFlipped())
+    {
+        CGContextTranslateCTM(xSourceContext, 0, nScaledSourceHeight);
+        CGContextScaleCTM(xSourceContext, 1, -1);
+        aSrcPoint.y = nScaledSourceY + nScaledSourceHeight - mnHeight * fScale;
     }
+    CGContextSetBlendMode(xSourceContext, kCGBlendModeCopy);
+    CGContextDrawLayerAtPoint(xSourceContext, aSrcPoint, pSrc->maLayer.get());
+
+    // Copy source area from additional layer to traget area
 
-    // draw at new destination
     const CGRect aTargetRect = CGRectMake(nScaledTargetX, nScaledTargetY, nScaledSourceWidth, nScaledSourceHeight);
     CGContextSetBlendMode(xCopyContext, kCGBlendModeCopy);
-    CGContextDrawLayerInRect(xCopyContext, aTargetRect, sSourceLayerHolder.get());
+    CGContextDrawLayerInRect(xCopyContext, aTargetRect, aSourceLayerHolder.get());
 
-    maContextHolder.restoreState();
+    // Housekeeping on exit
 
-    // cleanup
-    if (sSourceLayerHolder.get() != maLayer.get())
-    {
-        CGLayerRelease(sSourceLayerHolder.get());
-    }
-    // mark the destination rectangle as updated
-    RefreshRect( nDstX, nDstY, nSrcWidth, nSrcHeight );
+    maContextHolder.restoreState();
+    if (aSourceLayerHolder.get() != maLayer.get())
+        CGLayerRelease(aSourceLayerHolder.get());
+    RefreshRect(nDstX, nDstY, nSrcWidth, nSrcHeight);
 }
 
 #ifndef IOS
@@ -1986,62 +1916,74 @@ bool XorEmulation::UpdateTarget()
     return true;
 }
 
-void AquaSalGraphics::SetVirDevGraphics(CGLayerHolder const & rLayer, CGContextRef xContext,
-                                        int nBitmapDepth)
+void AquaSalGraphics::SetVirDevGraphics(CGLayerHolder const &rLayer, CGContextRef xContext, int nBitmapDepth)
 {
-    SAL_INFO( "vcl.quartz", "SetVirDevGraphics() this=" << this << " layer=" << rLayer.get() << " context=" << xContext );
+    SAL_INFO("vcl.quartz", "SetVirDevGraphics() this=" << this << " layer=" << rLayer.get() << " context=" << xContext);
 
-#ifndef IOS
+    // Set member variables
+
+    InvalidateContext();
     mbWindow = false;
-#endif
     mbPrinter = false;
     mbVirDev = true;
-
-    // set graphics properties
     maLayer = rLayer;
-    maContextHolder.set(xContext);
-
     mnBitmapDepth = nBitmapDepth;
 
 #ifdef IOS
+
     mbForeignContext = xContext != NULL;
+
 #endif
 
-    // return early if the virdev is being destroyed
-    if( !xContext )
-        return;
+    // Get size and scale from layer if set else from bitmap and AquaSalGraphics::GetWindowScaling(), which is used to determine
+    // scaling for direct graphics output too
 
-    // get new graphics properties
-    if (!maLayer.isSet())
+    CGSize aSize;
+    float fScale;
+    if (maLayer.isSet())
     {
-        mnWidth = CGBitmapContextGetWidth( maContextHolder.get() );
-        mnHeight = CGBitmapContextGetHeight( maContextHolder.get() );
+        maContextHolder.set(CGLayerGetContext(maLayer.get()));
+        aSize = CGLayerGetSize(maLayer.get());
+        fScale = maLayer.getScale();
     }
     else
     {
-        const CGSize aSize = CGLayerGetSize(maLayer.get());
-        mnWidth = static_cast<int>(aSize.width);
-        mnHeight = static_cast<int>(aSize.height);
+        maContextHolder.set(xContext);
+        if (!xContext)
+            return;
+        aSize.width = CGBitmapContextGetWidth(xContext);
+        aSize.height = CGBitmapContextGetHeight(xContext);
+        fScale = GetWindowScaling();
     }
+    mnWidth = aSize.width / fScale;
+    mnHeight = aSize.height / fScale;
+
+    // Set color space for fill and stroke
+
+    CGColorSpaceRef aColorSpace = GetSalData()->mxRGBSpace;
+    CGContextSetFillColorSpace(maContextHolder.get(), aColorSpace);
+    CGContextSetStrokeColorSpace(maContextHolder.get(), aColorSpace);
 
-    // prepare graphics for drawing
-    const CGColorSpaceRef aCGColorSpace = GetSalData()->mxRGBSpace;
-    CGContextSetFillColorSpace( maContextHolder.get(), aCGColorSpace );
-    CGContextSetStrokeColorSpace( maContextHolder.get(), aCGColorSpace );
+    // Apply scale matrix to virtual device graphics
 
-    // re-enable XorEmulation for the new context
-    if( mpXorEmulation )
+    CGContextScaleCTM(maContextHolder.get(), fScale, fScale);
+
+    // Apply XOR emulation if required
+
+    if (mpXorEmulation)
     {
         mpXorEmulation->SetTarget(mnWidth, mnHeight, mnBitmapDepth, maContextHolder.get(), maLayer.get());
-        if( mpXorEmulation->IsEnabled() )
-        {
+        if (mpXorEmulation->IsEnabled())
             maContextHolder.set(mpXorEmulation->GetMaskContext());
-        }
     }
 
-    // initialize stack of CGContext states
+    // Housekeeping on exit
+
     maContextHolder.saveState();
     SetState();
+
+    SAL_INFO("vcl.quartz", "SetVirDevGraphics() this=" << this <<
+             " (" << mnWidth << "x" << mnHeight << ") fScale=" << fScale << " mnBitmapDepth=" << mnBitmapDepth);
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/quartz/salgdiutils.cxx b/vcl/quartz/salgdiutils.cxx
index 57953e536796..e693c3853bd3 100644
--- a/vcl/quartz/salgdiutils.cxx
+++ b/vcl/quartz/salgdiutils.cxx
@@ -32,6 +32,39 @@
 #include <osx/salframe.h>
 #include <osx/saldata.hxx>
 
+float AquaSalGraphics::GetWindowScaling()
+{
+    float fScale = 1.0f;
+
+#ifdef MACOSX
+
+    // Window scaling independent from main display may be forced by setting VCL_MACOS_FORCE_WINDOW_SCALING environment variable
+    // whose setting is stored in mbWindowScaling. After implementation of full support of scaled displays window scaling will be
+    // set to 2.0f for macOS as default. This will allow moving of windows between non retina and retina displays without blurry
+    // text and graphics.
+
+    // TODO: After implementation of full support of scaled displays code has to be modified to set a scaling of 2.0f as default.
+
+    if (mbWindowScaling)
+    {
+        fScale = 2.0f;
+        return fScale;
+    }
+
+#endif
+
+    AquaSalFrame *pSalFrame = mpFrame;
+    if (!pSalFrame)
+        pSalFrame = static_cast<AquaSalFrame *>(GetSalData()->mpInstance->anyFrame());
+    if (pSalFrame)
+    {
+        NSWindow *pNSWindow = pSalFrame->getNSWindow();
+        if (pNSWindow)
+            fScale = [pNSWindow backingScaleFactor];
+    }
+    return fScale;
+}
+
 void AquaSalGraphics::SetWindowGraphics( AquaSalFrame* pFrame )
 {
     mpFrame = pFrame;
@@ -113,13 +146,7 @@ bool AquaSalGraphics::CheckContext()
     {
         const unsigned int nWidth = mpFrame->maGeometry.nWidth;
         const unsigned int nHeight = mpFrame->maGeometry.nHeight;
-
-        // Let's get the window scaling factor if possible, or use 1.0
-        // as the scaling factor.
-        float fScale = 1.0f;
-        if (mpFrame->getNSWindow())
-            fScale = [mpFrame->getNSWindow() backingScaleFactor];
-
+        const float fScale = GetWindowScaling();
         CGLayerRef rReleaseLayer = nullptr;
 
         // check if a new drawing context is needed (e.g. after a resize)
diff --git a/vcl/quartz/salvd.cxx b/vcl/quartz/salvd.cxx
index c06ba33c5b6f..e567da97123d 100644
--- a/vcl/quartz/salvd.cxx
+++ b/vcl/quartz/salvd.cxx
@@ -103,6 +103,10 @@ AquaSalVirtualDevice::AquaSalVirtualDevice( AquaSalGraphics* pGraphic, long &nDX
         }
 
         mpGraphics->SetVirDevGraphics(maLayer, pData->rCGContext);
+
+        SAL_INFO("vcl.virdev", "AquaSalVirtualDevice::AquaSalVirtualDevice() this=" << this <<
+                 " (" << nDX << "x" << nDY << ") mbForeignContext=" << (mbForeignContext ? "YES" : "NO"));
+
     }
     else
     {
@@ -179,8 +183,6 @@ void AquaSalVirtualDevice::Destroy()
 
     if (maBitmapContext.isSet())
     {
-        void* pRawData = CGBitmapContextGetData(maBitmapContext.get());
-        std::free(pRawData);
         CGContextRelease(maBitmapContext.get());
         maBitmapContext.set(nullptr);
     }
@@ -206,96 +208,73 @@ bool AquaSalVirtualDevice::SetSize( long nDX, long nDY )
     SAL_INFO( "vcl.virdev", "AquaSalVirtualDevice::SetSize() this=" << this <<
               " (" << nDX << "x" << nDY << ") mbForeignContext=" << (mbForeignContext ? "YES" : "NO"));
 
-    if( mbForeignContext )
-    {
-        // Do not delete/resize mxContext that we have received from outside VCL
+    // Do not delete/resize graphics context if it has been received from outside VCL
+
+    if (mbForeignContext)
         return true;
-    }
 
+    // Do not delete/resize graphics context if no change of geometry has been requested
+
+    float fScale;
     if (maLayer.isSet())
     {
+        fScale = maLayer.getScale();
         const CGSize aSize = CGLayerGetSize(maLayer.get());
-        if( (nDX == aSize.width) && (nDY == aSize.height) )
-        {
-            // Yay, we do not have to do anything :)
+        if ((nDX == aSize.width / fScale) && (nDY  == aSize.height / fScale))
             return true;
-        }
     }
 
+    // Destroy graphics context if change of geometry has been requested
+
     Destroy();
 
+    // Prepare new graphics context for initialization, use scaling independent of prior graphics context calculated by
+    // AquaSalGraphics::GetWindowScaling(), which is used to determine scaling for direct graphics output too
+
     mnWidth = nDX;
     mnHeight = nDY;
-
-    // create a Quartz layer matching to the intended virdev usage
-    CGContextHolder xCGContextHolder;
-    if( mnBitmapDepth && (mnBitmapDepth < 16) )
+    fScale = mpGraphics->GetWindowScaling();
+    CGColorSpaceRef aColorSpace;
+    uint32_t nFlags;
+    if (mnBitmapDepth && (mnBitmapDepth < 16))
     {
-        mnBitmapDepth = 8;  // TODO: are 1bit vdevs worth it?
-        const int nBytesPerRow = (mnBitmapDepth * nDX + 7) / 8;
-
-        void* pRawData = std::malloc( nBytesPerRow * nDY );
-        maBitmapContext.set(CGBitmapContextCreate( pRawData, nDX, nDY,
-                                                 mnBitmapDepth, nBytesPerRow,
-                                                 GetSalData()->mxGraySpace, kCGImageAlphaNone));
-        xCGContextHolder = maBitmapContext;
+        mnBitmapDepth = 8;
+        aColorSpace = GetSalData()->mxGraySpace;
+        nFlags = kCGImageAlphaNone;
     }
     else
     {
+        mnBitmapDepth = 32;
+        aColorSpace = GetSalData()->mxRGBSpace;
+
 #ifdef MACOSX
-        // default to a NSView target context
-        AquaSalFrame* pSalFrame = mpGraphics->getGraphicsFrame();
-        if( !pSalFrame || !AquaSalFrame::isAlive( pSalFrame ))
-        {
-            pSalFrame = static_cast<AquaSalFrame*>( GetSalData()->mpInstance->anyFrame() );
-            if ( pSalFrame )
-                // update the frame reference
-                mpGraphics->setGraphicsFrame( pSalFrame );
-        }
-        if( pSalFrame )
-        {
-            // #i91990#
-            NSWindow* pNSWindow = pSalFrame->getNSWindow();
-            if ( pNSWindow )
-            {
-                NSGraphicsContext* pNSContext = [NSGraphicsContext graphicsContextWithWindow: pNSWindow];
-                if( pNSContext )
-                {
-                    xCGContextHolder.set([pNSContext CGContext]);
-                }
-            }
-        }
-#endif
 
-        if (!xCGContextHolder.isSet())
-        {
-            // assert(Application::IsBitmapRendering());
-            mnBitmapDepth = 32;
+        nFlags = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
 
-            const int nBytesPerRow = (mnBitmapDepth * nDX) / 8;
-            void* pRawData = std::malloc( nBytesPerRow * nDY );
-#ifdef MACOSX
-            const int nFlags = kCGImageAlphaNoneSkipFirst;
 #else
-            const int nFlags = kCGImageAlphaNoneSkipFirst | kCGImageByteOrder32Little;
+
+        nFlags = kCGImageAlphaNoneSkipFirst | kCGImageByteOrder32Little;
+
 #endif
-            maBitmapContext.set(CGBitmapContextCreate(pRawData, nDX, nDY, 8, nBytesPerRow,
-                                                      GetSalData()->mxRGBSpace, nFlags));
-            xCGContextHolder = maBitmapContext;
-        }
+
     }
 
-    SAL_WARN_IF(!xCGContextHolder.isSet(), "vcl.quartz", "No context");
+    // Allocate buffer for virtual device graphics as bitmap context to store graphics with highest required (scaled) resolution
 
-    const CGSize aNewSize = { static_cast<CGFloat>(nDX), static_cast<CGFloat>(nDY) };
-    maLayer.set(CGLayerCreateWithContext(xCGContextHolder.get(), aNewSize, nullptr));
+    size_t nScaledWidth = mnWidth * fScale;
+    size_t nScaledHeight = mnHeight * fScale;
+    size_t nBytesPerRow = mnBitmapDepth * nScaledWidth / 8;
+    maBitmapContext.set(CGBitmapContextCreate(nullptr, nScaledWidth, nScaledHeight, 8, nBytesPerRow, aColorSpace, nFlags));
 
-    if (maLayer.isSet() && mpGraphics)
-    {
-        // get the matching Quartz context
-        CGContextRef xDrawContext = CGLayerGetContext( maLayer.get() );
-        mpGraphics->SetVirDevGraphics(maLayer.get(), xDrawContext, mnBitmapDepth);
-    }
+    SAL_INFO("vcl.virdev", "AquaSalVirtualDevice::SetSize() this=" << this <<
+             " fScale=" << fScale << " mnBitmapDepth=" << mnBitmapDepth);
+
+    CGSize aLayerSize = { static_cast<CGFloat>(nScaledWidth), static_cast<CGFloat>(nScaledHeight) };
+    maLayer.set(CGLayerCreateWithContext(maBitmapContext.get(), aLayerSize, nullptr));
+    maLayer.setScale(fScale);
+    mpGraphics->SetVirDevGraphics(maLayer, CGLayerGetContext(maLayer.get()), mnBitmapDepth);
+
+    SAL_WARN_IF(!maBitmapContext.isSet(), "vcl.quartz", "No context");
 
     return maLayer.isSet();
 }
commit 7af82a5cdcce31e1dc9800437fa2780536265322
Author:     Tor Lillqvist <tml at collabora.com>
AuthorDate: Mon Jan 4 13:12:11 2021 +0200
Commit:     Tor Lillqvist <tml at iki.fi>
CommitDate: Mon Jan 18 15:49:10 2021 +0200

    CGLayerHolder::getScale() can be const
    
    (Will be required by upcoming bugfix.)
    
    Change-Id: I727b2b5f1035ae70d62d3c5339a814161e3ec92a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/108663
    Tested-by: Jenkins
    Reviewed-by: Tor Lillqvist <tml at collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/109494
    Tested-by: Adolfo Jayme Barrientos <fitojb at ubuntu.com>
    Reviewed-by: Adolfo Jayme Barrientos <fitojb at ubuntu.com>

diff --git a/vcl/inc/quartz/CGHelpers.hxx b/vcl/inc/quartz/CGHelpers.hxx
index cbd9a5e785f4..c83477ead4fe 100644
--- a/vcl/inc/quartz/CGHelpers.hxx
+++ b/vcl/inc/quartz/CGHelpers.hxx
@@ -68,7 +68,7 @@ public:
 
     void set(CGLayerRef const& pLayer) { mpLayer = pLayer; }
 
-    float getScale() { return mfScale; }
+    float getScale() const { return mfScale; }
 
     void setScale(float fScale) { mfScale = fScale; }
 };
commit bcb670f43e66511c0171e42de59516e50e4a7910
Author:     Tor Lillqvist <tml at collabora.com>
AuthorDate: Thu Jan 14 16:03:33 2021 +0200
Commit:     Tor Lillqvist <tml at iki.fi>
CommitDate: Mon Jan 18 15:13:52 2021 +0200

    Fix handling of the OBJECTDESCRIPTOR clipboard (pasteboard) type on macOS
    
    This fixes a demonstrable problem, and affects only macOS.
    
    This is also on the path towards fixing
    https://github.com/CollaboraOnline/online/issues/849 , even if the
    code touched here is for macOS only. The iOS pasteboard handling code
    is based on the macOS one.
    
    We need the pasteboard to have the OBJECTDESCRIPTOR type as a MIME
    type that includes the typename attribute, because the code in sc
    checks for that when it decides whether it is a proper
    OBJECTDESCRIPTOR.
    
    Simplify the data in the flavorMap array. No need to duplicate the
    same MIME type string as both the pasteboard type and MIME type, for
    those cases where the MIME type is used diretly as pasteboard type. We
    also know that for those types, the MIME type might have additional
    parameters, so be more lenient in checking.
    
    With this change, and my recent change to sot, this now works:
    
    Start LibreOffice. Open a spreadsheet. Select a cell range. (It can
    include formulas.) Edit > Copy. Quit LibreOffice.
    
    Start LibreOffice again. Open a spreadsheet. Edit > Paste. You get the
    very same cells that you pasted as such (with relative cell addresses
    in formulas properly adjusted, as expected).
    
    Previously, it would paste an image of the copied cell range, which is
    fairly pointless.
    
    There is still lots of opportunity for cleanup in the clipboard code
    for macOS and iOS. It is presumably rather pointless to put images on
    the pasteboard in Windows bitmap format, for instance. Just PNG should
    be enough. No idea why the code provides the TIFF pasteboard type for
    images. Ditto for Windows metafiles (WMF and EMF). It is also unclear
    how useful the EMBED_SOURCE or LINK_SOURCE types are on macOS, for
    instance.
    
    Change-Id: I573648480b03972b506203b4f470d513bcb81212
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/109297
    Tested-by: Jenkins
    Reviewed-by: Tor Lillqvist <tml at collabora.com>

diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox
index 7a740191ffdf..79dd91c9697e 100644
--- a/include/sal/log-areas.dox
+++ b/include/sal/log-areas.dox
@@ -486,6 +486,7 @@ certain functionality.
 @li @c vcl.opengl
 @li @c vcl.opengl.qt5 - QT5 OpenGL
 @li @c vcl.osx
+ at li @c vcl.osx.clipboard
 @li @c vcl.osx.print
 @li @c vcl.pdfwriter
 @li @c vcl.plugadapt - the Unix/X11 backend plugin mechanism
diff --git a/vcl/osx/DataFlavorMapping.cxx b/vcl/osx/DataFlavorMapping.cxx
index dfba27d20e31..5f86143352cd 100644
--- a/vcl/osx/DataFlavorMapping.cxx
+++ b/vcl/osx/DataFlavorMapping.cxx
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
 /*
  * This file is part of the LibreOffice project.
  *
@@ -73,19 +73,6 @@ namespace
     return [NSString stringWithCString: utf8Str.getStr() encoding: NSUTF8StringEncoding];
   }
 
-  NSString* PBTYPE_SODX = @"application/x-openoffice-objectdescriptor-xml;windows_formatname=\"Star Object Descriptor (XML)\"";
-  NSString* PBTYPE_SESX = @"application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\"";
-  NSString* PBTYPE_SLSDX = @"application/x-openoffice-linksrcdescriptor-xml;windows_formatname=\"Star Link Source Descriptor (XML)\"";
-  NSString* PBTYPE_ESX = @"application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\"";
-  NSString* PBTYPE_LSX = @"application/x-openoffice-link-source-xml;windows_formatname=\"Star Link Source (XML)\"";
-  NSString* PBTYPE_EOX = @"application/x-openoffice-embedded-obj-xml;windows_formatname=\"Star Embedded Object (XML)\"";
-  NSString* PBTYPE_SVXB = @"application/x-openoffice-svbx;windows_formatname=\"SVXB (StarView Bitmap/Animation)\"";
-  NSString* PBTYPE_GDIMF = @"application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"";
-  NSString* PBTYPE_WMF = @"application/x-openoffice-wmf;windows_formatname=\"Image WMF\"";
-  NSString* PBTYPE_EMF = @"application/x-openoffice-emf;windows_formatname=\"Image EMF\"";
-
-  NSString* PBTYPE_DUMMY_INTERNAL = @"application/x-openoffice-internal";
-
   const char* FLAVOR_SODX = "application/x-openoffice-objectdescriptor-xml;windows_formatname=\"Star Object Descriptor (XML)\"";
   const char* FLAVOR_SESX = "application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\"";
   const char* FLAVOR_SLSDX = "application/x-openoffice-linksrcdescriptor-xml;windows_formatname=\"Star Link Source Descriptor (XML)\"";
@@ -107,8 +94,17 @@ namespace
     bool DataTypeOUString; // sequence<byte> otherwise
   };
 
-  /* At the moment it appears as if only MS Office pastes "public.html" to the clipboard.
-   */
+  // The SystemFlavor member is nil for the cases where there is no predefined pasteboard type UTI
+  // and we use the internal MIME type (media type) also on the pasteboard. That is OK in macOS,
+  // there is no requirement that the types are well-formed UTIs. It is different on iOS, I think,
+  // though. For an introduction to UTIs, see for instance
+  // https://alastairs-place.net/blog/2012/06/06/utis-are-better-than-you-think-and-heres-why/
+  //
+  // In those cases the MIME type might actually have parameters appended, separated by semicolons.
+  // At least the FLAVOR_SODX one must have at least a typename="%PRODUCTNAME %PRODUCTVERSION
+  // Spreadsheet" parameter (with macros expanded and translated) for LO to recognise it. See
+  // lcl_TestFormat() in sc/source/ui/view/cellsh.cxx.
+
   static const FlavorMap flavorMap[] =
     {
       { NSPasteboardTypeString, "text/plain;charset=utf-16", "Unicode Text (UTF-16)", true },
@@ -121,17 +117,17 @@ SAL_WNODEPRECATED_DECLARATIONS_PUSH
           // multiple pasteboard items with NSPasteboardTypeFileURL or kUTTypeFileURL instead"
       { NSFilenamesPboardType, "application/x-openoffice-filelist;windows_formatname=\"FileList\"", "FileList", false },
 SAL_WNODEPRECATED_DECLARATIONS_POP
-      { PBTYPE_SESX, FLAVOR_SESX, "Star Embed Source (XML)", false },
-      { PBTYPE_SLSDX, FLAVOR_SLSDX, "Star Link Source Descriptor (XML)", false },
-      { PBTYPE_ESX, FLAVOR_ESX, "Star Embed Source (XML)", false },
-      { PBTYPE_LSX, FLAVOR_LSX, "Star Link Source (XML)", false },
-      { PBTYPE_EOX, FLAVOR_EOX, "Star Embedded Object (XML)", false },
-      { PBTYPE_SVXB, FLAVOR_SVXB, "SVXB (StarView Bitmap/Animation", false },
-      { PBTYPE_GDIMF, FLAVOR_GDIMF, "GDIMetaFile", false },
-      { PBTYPE_WMF, FLAVOR_WMF, "Windows MetaFile", false },
-      { PBTYPE_EMF, FLAVOR_EMF, "Windows Enhanced MetaFile", false },
-      { PBTYPE_SODX, FLAVOR_SODX, "Star Object Descriptor (XML)", false },
-      { PBTYPE_DUMMY_INTERNAL, FLAVOR_DUMMY_INTERNAL, "internal data",false }
+      { nil, FLAVOR_SESX, "Star Embed Source (XML)", false },
+      { nil, FLAVOR_SLSDX, "Star Link Source Descriptor (XML)", false },
+      { nil, FLAVOR_ESX, "Star Embed Source (XML)", false },
+      { nil, FLAVOR_LSX, "Star Link Source (XML)", false },
+      { nil, FLAVOR_EOX, "Star Embedded Object (XML)", false },
+      { nil, FLAVOR_SVXB, "SVXB (StarView Bitmap/Animation", false },
+      { nil, FLAVOR_GDIMF, "GDIMetaFile", false },
+      { nil, FLAVOR_WMF, "Windows MetaFile", false },
+      { nil, FLAVOR_EMF, "Windows Enhanced MetaFile", false },
+      { nil, FLAVOR_SODX, "Star Object Descriptor (XML)", false },
+      { nil, FLAVOR_DUMMY_INTERNAL, "internal data",false }
     };
 
   #define SIZE_FLAVOR_MAP (sizeof(flavorMap)/sizeof(FlavorMap))
@@ -509,9 +505,16 @@ DataFlavor DataFlavorMapper::systemToOpenOfficeFlavor( const NSString* systemDat
 
     for (size_t i = 0; i < SIZE_FLAVOR_MAP; i++)
     {
-      if ([systemDataFlavor caseInsensitiveCompare:const_cast<NSString*>(flavorMap[i].SystemFlavor)] == NSOrderedSame)
+        if ((flavorMap[i].SystemFlavor == nil && ([systemDataFlavor isEqualToString:[NSString stringWithUTF8String:flavorMap[i].OOoFlavor]]
+                                                  ||
+                                                  [systemDataFlavor hasPrefix:[[NSString stringWithUTF8String:flavorMap[i].OOoFlavor] stringByAppendingString:@";"]]))
+            ||
+            (flavorMap[i].SystemFlavor != nil && [systemDataFlavor isEqualToString:const_cast<NSString*>(flavorMap[i].SystemFlavor)]))
         {
-          oOOFlavor.MimeType = OUString::createFromAscii(flavorMap[i].OOoFlavor);
+          if (flavorMap[i].SystemFlavor == nil)
+              oOOFlavor.MimeType = NSStringToOUString(systemDataFlavor);
+          else
+              oOOFlavor.MimeType = OUString::createFromAscii(flavorMap[i].OOoFlavor);
           oOOFlavor.HumanPresentableName = OUString::createFromAscii(flavorMap[i].HumanPresentableName);
           oOOFlavor.DataType = flavorMap[i].DataTypeOUString ? cppu::UnoType<OUString>::get() : cppu::UnoType<Sequence<sal_Int8>>::get();
           return oOOFlavor;
@@ -540,7 +543,10 @@ const NSString* DataFlavorMapper::openOfficeToSystemFlavor( const DataFlavor& oO
     {
        if (oOOFlavor.MimeType.startsWith(OUString::createFromAscii(flavorMap[i].OOoFlavor)))
         {
-            sysFlavor = flavorMap[i].SystemFlavor;
+            if (flavorMap[i].SystemFlavor != nil)
+                sysFlavor = flavorMap[i].SystemFlavor;
+            else
+                sysFlavor = OUStringToNSString(oOOFlavor.MimeType);
         }
     }
 
@@ -614,8 +620,9 @@ SAL_WNODEPRECATED_DECLARATIONS_POP
           dp = DataProviderPtr_t(new UniDataProvider(data));
         }
     }
-  catch(UnsupportedFlavorException&)
+  catch( const UnsupportedFlavorException& e )
     {
+     SAL_WARN( "vcl.osx.clipboard", "DataFlavorMapper::getDataProvider(): Exception: " << e.Message );
       // Somebody violates the contract of the clipboard
       // interface @see XTransferable
     }
@@ -668,8 +675,9 @@ bool DataFlavorMapper::isValidMimeContentType(const OUString& contentType) const
     {
       Reference<XMimeContentType> xCntType(mrXMimeCntFactory->createMimeContentType(contentType));
     }
-  catch( IllegalArgumentException& )
+  catch( const IllegalArgumentException& e )
     {
+      SAL_WARN("vcl.osx.clipboard", "DataFlavorMapper::isValidMimeContentType(): Exception: " << e.Message);
       result = false;
     }
 
@@ -706,7 +714,7 @@ NSArray* DataFlavorMapper::flavorSequenceToTypesArray(const css::uno::Sequence<c
    // report at least one so D&D between OOo targets works
   if( [array count] == 0 || bNeedDummyInternalFlavor)
   {
-      [array addObject: PBTYPE_DUMMY_INTERNAL];
+      [array addObject: [NSString stringWithUTF8String: FLAVOR_DUMMY_INTERNAL]];
   }
 
   return [array autorelease];
@@ -738,7 +746,10 @@ NSArray* DataFlavorMapper::getAllSupportedPboardTypes()
 
   for (sal_uInt32 i = 0; i < SIZE_FLAVOR_MAP; i++)
     {
-      [array addObject: flavorMap[i].SystemFlavor];
+      if (flavorMap[i].SystemFlavor != nil)
+          [array addObject: flavorMap[i].SystemFlavor];
+      else
+          [array addObject: [NSString stringWithUTF8String: flavorMap[i].OOoFlavor]];
     }
 
   return [array autorelease];
commit 84fb78eef2944b2544d3e6b6af7e3f8494d8f090
Author:     Tor Lillqvist <tml at collabora.com>
AuthorDate: Thu Jan 14 02:57:35 2021 +0200
Commit:     Tor Lillqvist <tml at iki.fi>
CommitDate: Mon Jan 18 15:13:15 2021 +0200

    Make SotExchange::GetFormat() accept a MIME type with additional parameters
    
    For instance, if SotExchange::GetFormat() is passed a flavor with MIME
    type
    "application/x-openoffice-objectdescriptor-xml;windows_formatname=\"Star
    Object Descriptor (XML)\";typename=\"LibreOffice 7.2 Spreadsheet\"" we
    do want to recognize it as SotClipboardFormatId::OBJECTDESCRIPTOR,
    even if the MIME type in the entry for it in the array in
    ImplFormatArray_Impl only has the windows_formatname parameter.
    
    This is part of fixing
    https://github.com/CollaboraOnline/online/issues/849, but it will help
    for corresponding problems with LibreOffice on macOS, too.
    
    Note that on Linux, SotExchange::GetFormat() gets called with a flavor
    with a MIME type that does *not* have all the extra parameters
    (classname, typename, displayname, viewaspect, width, height, posx,
    posy) (See the GitHub issue mentioned above.) This change does not
    remove any checks for classname, typename, etc. There are/were no such
    checks in SotExchange::GetFormat().
    
    But, in the (much different) code path for macOS (and iOS), with my
    work in process in vcl, SotExchange::GetFormat() gets called with a
    flavor with a MIME type that *does* contain those extra parameters. I
    don't see the point in introducing checks of the "sanity" of those
    into SotExchange::GetFormat().
    
    (In this branch, already contains the add-on fix to use the existing
    OUString::matchAsciiL() instead of requiring a new
    OUString::startsWithAsciiL().)
    
    Change-Id: Ie65ed1ab922cdaa6557eb65d980b9e886d3c6971
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/109254
    Tested-by: Jenkins
    Reviewed-by: Tor Lillqvist <tml at collabora.com>

diff --git a/sot/source/base/exchange.cxx b/sot/source/base/exchange.cxx
index 4888a366ec71..cca47c4bcad4 100644
--- a/sot/source/base/exchange.cxx
+++ b/sot/source/base/exchange.cxx
@@ -447,10 +447,16 @@ SotClipboardFormatId SotExchange::GetFormat( const DataFlavor& rFlavor )
     // only into 5.1 chart documents - in 5.0 and 5.2 it was 42 ("StarChart 5.0")
     // The registry only contains the entry for the 42 format id.
     for( SotClipboardFormatId i = SotClipboardFormatId::RTF; i <= SotClipboardFormatId::USER_END;  ++i )
-        if( rMimeType.equalsAscii( pFormatArray_Impl[ static_cast<int>(i) ].pMimeType ) )
+    {
+        const char* const pFormatMimeType = pFormatArray_Impl[ static_cast<int>(i) ].pMimeType;
+        const sal_Int32 nFormatMimeTypeLen = rtl_str_getLength( pFormatMimeType );
+        if( rMimeType.matchAsciiL( pFormatMimeType, nFormatMimeTypeLen ) &&
+            ( rMimeType.getLength() == nFormatMimeTypeLen ||
+              rMimeType[ nFormatMimeTypeLen ] == ';' ) )
             return ( (i == SotClipboardFormatId::STARCHARTDOCUMENT_50)
                      ? SotClipboardFormatId::STARCHART_50
                      : i );
+    }
 
     // then in the dynamic list
     tDataFlavorList& rL = InitFormats_Impl();


More information about the Libreoffice-commits mailing list