svx/source/customshapes/EnhancedCustomShape2d.cxx |  324 ++++++++++++++++++++--
 1 file changed, 307 insertions(+), 17 deletions(-)

New commits:
commit 89ce0ef3744572127a75fbe82c2fc194ce63b702
Author:     Regina Henschel <rb.hensc...@t-online.de>
AuthorDate: Wed Apr 24 22:08:37 2019 +0200
Commit:     Regina Henschel <rb.hensc...@t-online.de>
CommitDate: Tue Apr 30 17:10:35 2019 +0200

    tdf#115813 Get correct adjust value from XY handle of ooxml-shape
    
    If an adjust value is not directly bind to a handle parameter but
    via formulas, the calculation of the adjust value depends on the
    individual shape. The error was, that one calculation method was
    applied to any OOXML-shape. User noticed, that handles did not
    stick to the position, where they are released but jumped to
    unexpected positions. The patch calculates guide formulas backwards
    to get an adjust value. The patch solves the problem for preset
    OOXML-shapes until some day a general method for any shape is
    implemented.
    
    Change-Id: I1a47082d5110a63530a273665d80348c119dc08b
    Reviewed-on: https://gerrit.libreoffice.org/71258
    Tested-by: Jenkins
    Reviewed-by: Regina Henschel <rb.hensc...@t-online.de>

diff --git a/svx/source/customshapes/EnhancedCustomShape2d.cxx 
b/svx/source/customshapes/EnhancedCustomShape2d.cxx
index 0e38216c1cd1..d34e514a69a4 100644
--- a/svx/source/customshapes/EnhancedCustomShape2d.cxx
+++ b/svx/source/customshapes/EnhancedCustomShape2d.cxx
@@ -1192,8 +1192,210 @@ bool EnhancedCustomShape2d::GetHandlePosition( const 
sal_uInt32 nIndex, Point& r
     return bRetValue;
 }
 
+static double lcl_getXAdjustmentValue(OUString& rShapeType, const sal_uInt32 
nHandleIndex,
+                                      const double fX, const double fW, const 
double fH)
+{
+    // degenerated shapes are not worth to calculate special case for each 
shape type
+    if (fW <= 0.0 || fH <= 0.0)
+        return 50000;
+
+    // pattern (w - x) / ss * 100000 or (r - x) / ss * 100000
+    if ((rShapeType == "ooxml-bentArrow" && nHandleIndex == 2) || (rShapeType 
== "ooxml-chevron")
+        || (rShapeType == "ooxml-curvedRightArrow") || (rShapeType == 
"ooxml-foldedCorner")
+        || (rShapeType == "ooxml-homePlate") || (rShapeType == 
"ooxml-notchedRightArrow")
+        || (rShapeType == "ooxml-rightArrow")
+        || (rShapeType == "ooxml-rightArrowCallout" && nHandleIndex == 2)
+        || (rShapeType == "ooxml-round1Rect")
+        || (rShapeType == "ooxml-round2DiagRect" && nHandleIndex == 1)
+        || (rShapeType == "ooxml-round2SameRect" && nHandleIndex == 0)
+        || (rShapeType == "ooxml-snip1Rect")
+        || (rShapeType == "ooxml-snip2DiagRect" && nHandleIndex == 1)
+        || (rShapeType == "ooxml-snip2SameRect" && nHandleIndex == 0)
+        || (rShapeType == "ooxml-snipRoundRect" && nHandleIndex == 1)
+        || (rShapeType == "ooxml-stripedRightArrow"))
+        return (fW - fX) / std::min(fW, fH) * 100000.0;
+
+    // pattern  x / ss * 100000 or (x - l) / ss * 100000
+    if ((rShapeType == "ooxml-bentArrow" && nHandleIndex == 0)
+        || (rShapeType == "ooxml-bentArrow" && nHandleIndex == 3) || 
(rShapeType == "ooxml-corner")
+        || (rShapeType == "ooxml-curvedDownArrow") || (rShapeType == 
"ooxml-curvedLeftArrow")
+        || (rShapeType == "ooxml-curvedUpArrow") || (rShapeType == 
"ooxml-leftArrow")
+        || (rShapeType == "ooxml-leftArrowCallout" && nHandleIndex == 2)
+        || (rShapeType == "ooxml-leftRightArrow")
+        || (rShapeType == "ooxml-leftRightArrowCallout" && nHandleIndex == 2)
+        || (rShapeType == "ooxml-round2DiagRect" && nHandleIndex == 0)
+        || (rShapeType == "ooxml-round2SameRect" && nHandleIndex == 1)
+        || (rShapeType == "ooxml-roundRect")
+        || (rShapeType == "ooxml-snip2DiagRect" && nHandleIndex == 0)
+        || (rShapeType == "ooxml-snip2SameRect" && nHandleIndex == 1)
+        || (rShapeType == "ooxml-snipRoundRect" && nHandleIndex == 0)
+        || (rShapeType == "ooxml-uturnArrow" && nHandleIndex == 0)
+        || (rShapeType == "ooxml-uturnArrow" && nHandleIndex == 3))
+        return fX / std::min(fW, fH) * 100000.0;
+
+    // pattern (hc - x) / ss * 200000
+    if ((rShapeType == "ooxml-downArrowCallout" && nHandleIndex == 0)
+        || (rShapeType == "ooxml-leftRightUpArrow" && nHandleIndex == 0)
+        || (rShapeType == "ooxml-quadArrow" && nHandleIndex == 0)
+        || (rShapeType == "ooxml-quadArrowCallout" && nHandleIndex == 0)
+        || (rShapeType == "ooxml-upArrowCallout" && nHandleIndex == 0))
+        return (fW / 2.0 - fX) / std::min(fW, fH) * 200000.0;
+
+    // pattern (hc - x) / ss * 100000
+    if ((rShapeType == "ooxml-downArrowCallout" && nHandleIndex == 1)
+        || (rShapeType == "ooxml-leftRightUpArrow" && nHandleIndex == 1)
+        || (rShapeType == "ooxml-quadArrow" && nHandleIndex == 1)
+        || (rShapeType == "ooxml-quadArrowCallout" && nHandleIndex == 1)
+        || (rShapeType == "ooxml-upArrowCallout" && nHandleIndex == 1))
+        return (fW / 2.0 - fX) / std::min(fW, fH) * 100000.0;
+
+    // pattern (w - x) / ss * 50000 or (r - x) / ss * 50000
+    if ((rShapeType == "ooxml-bentUpArrow") || (rShapeType == 
"ooxml-leftUpArrow")
+        || (rShapeType == "ooxml-uturnArrow" && nHandleIndex == 1))
+        return (fW - fX) / std::min(fW, fH) * 50000.0;
+
+    // pattern (hc - x) / w * 200000
+    if ((rShapeType == "ooxml-downArrow" && nHandleIndex == 0)
+        || (rShapeType == "ooxml-ellipseRibbon") || (rShapeType == 
"ooxml-ellipseRibbon2")
+        || (rShapeType == "ooxml-leftRightArrowCallout" && nHandleIndex == 3)
+        || (rShapeType == "ooxml-ribbon") || (rShapeType == "ooxml-ribbon2")
+        || (rShapeType == "ooxml-upArrow" && nHandleIndex == 0)
+        || (rShapeType == "ooxml-upDownArrow" && nHandleIndex == 0))
+        return (fW / 2.0 - fX) / fW * 200000.0;
+
+    // pattern (x - hc) / w * 100000
+    if ((rShapeType == "ooxml-cloudCallout") || (rShapeType == 
"ooxml-doubleWave")
+        || (rShapeType == "ooxml-wave") || (rShapeType == 
"ooxml-wedgeEllipseCallout")
+        || (rShapeType == "ooxml-wedgeRectCallout")
+        || (rShapeType == "ooxml-wedgeRoundRectCallout"))
+        return (fX - fW / 2.0) / fW * 100000.0;
+
+    // pattern (x - hc) / w * 200000
+    if (rShapeType == "ooxml-teardrop")
+        return (fX - fW / 2.0) / fW * 200000.0;
+
+    // pattern (w - x) / w * 100000 or (r - x) / w * 100000
+    if (rShapeType == "ooxml-leftArrowCallout" && nHandleIndex == 3)
+        return (fW - fX) / fW * 100000.0;
+
+    // pattern (hc - x) / h * 100000
+    if (rShapeType == "ooxml-mathDivide")
+        return (fW / 2.0 - fX) / fH * 100000.0;
+
+    // pattern x / w * 100000, simple scaling
+    if (rShapeType.startsWith("ooxml-"))
+        return fX / fW * 100000.0;
+
+    return fX; // method is unknown
+}
+
+static double lcl_getYAdjustmentValue(OUString& rShapeType, const sal_uInt32 
nHandleIndex,
+                                      const double fY, const double fW, const 
double fH)
+{
+    // degenerated shapes are not worth to calculate a special case for each 
shape type
+    if (fW <= 0.0 || fH <= 0.0)
+        return 50000;
+
+    // pattern (vc - y) / ss * 100000
+    if ((rShapeType == "ooxml-leftArrowCallout" && nHandleIndex == 1)
+        || (rShapeType == "ooxml-leftRightArrowCallout" && nHandleIndex == 1)
+        || (rShapeType == "ooxml-rightArrowCallout" && nHandleIndex == 1))
+        return (fH / 2.0 - fY) / std::min(fW, fH) * 100000.0;
+
+    // pattern (vc - y) / ss * 200000
+    if ((rShapeType == "ooxml-curvedLeftArrow") || (rShapeType == 
"ooxml-curvedRightArrow")
+        || (rShapeType == "ooxml-leftArrowCallout" && nHandleIndex == 0)
+        || (rShapeType == "ooxml-leftRightArrowCallout" && nHandleIndex == 0)
+        || (rShapeType == "ooxml-mathPlus")
+        || (rShapeType == "ooxml-rightArrowCallout" && nHandleIndex == 0))
+        return (fH / 2.0 - fY) / std::min(fW, fH) * 200000.0;
+
+    // pattern (h - y) / ss * 100000 or (b - y) / ss * 100000
+    if ((rShapeType == "ooxml-bentUpArrow" && nHandleIndex == 0) || 
(rShapeType == "ooxml-corner")
+        || (rShapeType == "ooxml-curvedDownArrow") || (rShapeType == 
"ooxml-downArrow")
+        || (rShapeType == "ooxml-downArrowCallout" && nHandleIndex == 2)
+        || (rShapeType == "ooxml-uturnArrow" && nHandleIndex == 2))
+        return (fH - fY) / std::min(fW, fH) * 100000.0;
+
+    // pattern (h - y) / ss * 200000 or (b - y) / ss * 200000
+    if (rShapeType == "ooxml-leftUpArrow" && nHandleIndex == 0) // - adj2 * 2 
outside
+        return (fH - fY) / std::min(fW, fH) * 200000.0;
+
+    // pattern  y / ss * 100000 or (y - t) / ss * 100000
+    if ((rShapeType == "ooxml-bentUpArrow" && nHandleIndex == 2)
+        || (rShapeType == "ooxml-curvedUpArrow") || (rShapeType == 
"ooxml-leftRightUpArrow")
+        || (rShapeType == "ooxml-leftUpArrow" && nHandleIndex == 2)
+        || (rShapeType == "ooxml-mathMultiply") || (rShapeType == 
"ooxml-quadArrow")
+        || (rShapeType == "ooxml-quadArrowCallout" && nHandleIndex == 2)
+        || (rShapeType == "ooxml-upArrow")
+        || (rShapeType == "ooxml-upArrowCallout" && nHandleIndex == 2)
+        || (rShapeType == "ooxml-upDownArrow"))
+        return fY / std::min(fW, fH) * 100000.0;
+
+    // pattern y / ss * 50000
+    if (rShapeType == "ooxml-bentArrow")
+        return fY / std::min(fW, fH) * 50000.0;
+
+    // pattern (vc - y) / h * 100000
+    if ((rShapeType == "ooxml-mathDivide" && nHandleIndex == 1) // -adj1 / 2 - 
adj3 outside
+        || (rShapeType == "ooxml-mathEqual" && nHandleIndex == 0) // -adj2 / 2 
outside
+        || (rShapeType == "ooxml-mathNotEqual" && nHandleIndex == 0) // -adj3 
/ 2 outside
+        || (rShapeType == "ooxml-star4") || (rShapeType == "ooxml-star6")
+        || (rShapeType == "ooxml-star8") || (rShapeType == "ooxml-star10")
+        || (rShapeType == "ooxml-star12") || (rShapeType == "ooxml-star16")
+        || (rShapeType == "ooxml-star24") || (rShapeType == "ooxml-star32"))
+        return (fH / 2.0 - fY) / fH * 100000.0;
+
+    // pattern (vc - y) / h * 200000
+    if ((rShapeType == "ooxml-leftArrow") || (rShapeType == 
"ooxml-leftRightArrow")
+        || (rShapeType == "ooxml-mathDivide" && nHandleIndex == 0)
+        || (rShapeType == "ooxml-mathEqual" && nHandleIndex == 1)
+        || (rShapeType == "ooxml-mathMinus") || (rShapeType == 
"ooxml-notchedRightArrow")
+        || (rShapeType == "ooxml-mathNotEqual" && nHandleIndex == 2)
+        || (rShapeType == "ooxml-quadArrowCallout" && nHandleIndex == 3)
+        || (rShapeType == "ooxml-rightArrow") || (rShapeType == 
"ooxml-stripedRightArrow"))
+        return (fH / 2.0 - fY) / fH * 200000.0;
+
+    // pattern (y - vc) / h * 100000
+    if ((rShapeType == "ooxml-cloudCallout") || (rShapeType == 
"ooxml-wedgeEllipseCallout")
+        || (rShapeType == "ooxml-wedgeRectCallout")
+        || (rShapeType == "ooxml-wedgeRoundRectCallout"))
+        return (fY - fH / 2.0) / fH * 100000.0;
+
+    // pattern (h - y) / h * 100000 or (b - y) / h * 100000
+    if ((rShapeType == "ooxml-ellipseRibbon" && nHandleIndex == 2)
+        || (rShapeType == "ooxml-ellipseRibbon2" && nHandleIndex == 0)
+        || (rShapeType == "ooxml-ribbon2")
+        || (rShapeType == "ooxml-upArrowCallout" && nHandleIndex == 3))
+        return (fH - fY) / fH * 100000.0;
+
+    // special pattern smiley
+    if (rShapeType == "ooxml-smileyFace")
+        return (fY - fH * 16515.0 / 21600.0) / fY * 100000.0;
+
+    // special pattern for star with odd number of tips, because center of 
star not center of shape
+    if (rShapeType == "ooxml-star5")
+        return (fH / 2.0 - fY * 100000.0 / 110557.0) / fH * 100000.0;
+    if (rShapeType == "ooxml-star7")
+        return (fH / 2.0 - fY * 100000.0 / 105210.0) / fH * 100000.0;
+
+    // pattern y / h * 100000, simple scaling
+    if (rShapeType.startsWith("ooxml-"))
+        return fY / fH * 100000;
+
+    return fY; // method is unknown
+}
+
 bool EnhancedCustomShape2d::SetHandleControllerPosition( const sal_uInt32 
nIndex, const css::awt::Point& rPosition )
 {
+    // For ooxml-foo shapes, the way to calculate the adjustment value from 
the handle position depends on
+    // the type of the shape.
+    OUString sShapeType("non-primitive"); // default for ODF
+    const SdrCustomShapeGeometryItem& 
rGeometryItem(mrSdrObjCustomShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY 
));
+    const Any* pAny = rGeometryItem.GetPropertyValueByName("Type");
+    if (pAny)
+        *pAny >>= sShapeType;
+
     bool bRetValue = false;
     if ( nIndex < GetHdlCount() )
     {
@@ -1261,12 +1463,15 @@ bool 
EnhancedCustomShape2d::SetHandleControllerPosition( const sal_uInt32 nIndex
 
             sal_Int32 nFirstAdjustmentValue = -1, nSecondAdjustmentValue = -1;
 
+            // ODF shapes are expected to use a direct binding beween position 
and adjustment
+            // values. OOXML preset shapes use known formulas. These are 
calculated backward to
+            // get the adjustment values. So far we do not have a general 
method to calculate
+            // the adjustment values for any shape from the handle position.
             if ( aHandle.aPosition.First.Type == 
EnhancedCustomShapeParameterType::ADJUSTMENT )
                 aHandle.aPosition.First.Value >>= nFirstAdjustmentValue;
             if ( aHandle.aPosition.Second.Type == 
EnhancedCustomShapeParameterType::ADJUSTMENT )
                 aHandle.aPosition.Second.Value>>= nSecondAdjustmentValue;
 
-
             // DrawingML polar handles set REFR or REFANGLE instead of POLAR
             if ( aHandle.nFlags & ( HandleFlags::POLAR | HandleFlags::REFR | 
HandleFlags::REFANGLE ) )
             {
@@ -1323,35 +1528,120 @@ bool 
EnhancedCustomShape2d::SetHandleControllerPosition( const sal_uInt32 nIndex
             }
             else
             {
-                if ( aHandle.nFlags & HandleFlags::REFX )
+                // Calculating the adjustment values follows in most cases 
some patterns, which only
+                // need width and height of the shape and handle position. 
These patterns are calculated
+                // in the static, local methods. More complex calculations or 
additional steps are
+                // done here.
+                // Values for corner cases 'root(negative)' or 'div zero' are 
meaningless dummies.
+                double fAdjustX = fPos1;
+                double fAdjustY = fPos2;
+                if (aHandle.nFlags & HandleFlags::REFX)
                 {
                     nFirstAdjustmentValue = aHandle.nRefX;
-                    fPos1 *= 100000.0;
-                    fPos1 /= fWidth;
+                    fAdjustX = lcl_getXAdjustmentValue(sShapeType, nIndex, 
fPos1, fWidth, fHeight);
+                    if ((sShapeType == "ooxml-curvedDownArrow")
+                        || (sShapeType == "ooxml-curvedUpArrow"))
+                    {
+                        double fss(std::min(fWidth, fHeight));
+                        if (fss != 0.0)
+                        {
+                            double fadj3(GetAdjustValueAsDouble(2));
+                            double fHScaled(100000.0 * fHeight / fss);
+                            double fRadicand(fHScaled * fHScaled - fadj3 * 
fadj3);
+                            double fSqrt = fRadicand >= 0.0 ? sqrt(fRadicand) 
: 0.0;
+                            double fPart(200000.0 * fWidth / fss * (fSqrt + 
fHScaled));
+                            fAdjustX = fPart - 4.0 * fHScaled * fAdjustX;
+                            if (nIndex == 0)
+                            {
+                                // calculate adj1
+                                double fadj2(GetAdjustValueAsDouble(1));
+                                fAdjustX = fAdjustX - fadj2 * (fSqrt + 
fHScaled);
+                                double fDenominator(fSqrt - 3.0 * fHScaled);
+                                fAdjustX /= fDenominator != 0.0 ? fDenominator 
: 1.0;
+                            }
+                            else
+                            {
+                                // nIndex == 1, calculate adj2
+                                double fadj1(GetAdjustValueAsDouble(0));
+                                fAdjustX = fAdjustX - fadj1 * (fSqrt - 
fHScaled);
+                                double fDenominator(fSqrt + 3.0 * fHScaled);
+                                fAdjustX /= fDenominator != 0.0 ? fDenominator 
: 1.0;
+                            }
+                        }
+                    }
                 }
-                if ( aHandle.nFlags & HandleFlags::REFY )
+                if (aHandle.nFlags & HandleFlags::REFY)
                 {
                     nSecondAdjustmentValue = aHandle.nRefY;
-                    fPos2 *= 100000.0;
-                    fPos2 /= fHeight;
+                    fAdjustY = lcl_getYAdjustmentValue(sShapeType, nIndex, 
fPos2, fWidth, fHeight);
+
+                    if (sShapeType == "ooxml-mathDivide" && nIndex == 1)
+                        fAdjustY = fAdjustY - GetAdjustValueAsDouble(0) / 2.0
+                                   - GetAdjustValueAsDouble(2);
+                    else if (sShapeType == "ooxml-mathEqual" && nIndex == 0)
+                        fAdjustY -= GetAdjustValueAsDouble(1) / 2.0;
+                    else if (sShapeType == "ooxml-mathNotEqual" && nIndex == 0)
+                        fAdjustY -= GetAdjustValueAsDouble(2) / 2.0;
+                    else if (sShapeType == "ooxml-leftUpArrow" && nIndex == 0)
+                        fAdjustY -= GetAdjustValueAsDouble(1) * 2.0;
+                    else if ((sShapeType == "ooxml-curvedRightArrow")
+                             || (sShapeType == "ooxml-curvedLeftArrow"))
+                    {
+                        double fss(std::min(fWidth, fHeight));
+                        if (fss != 0.0)
+                        {
+                            double fadj3(GetAdjustValueAsDouble(2));
+                            double fWScaled(100000.0 * fWidth / fss);
+                            double fRadicand(fWScaled * fWScaled - fadj3 * 
fadj3);
+                            double fSqrt = fRadicand >= 0.0 ? sqrt(fRadicand) 
: 0.0;
+                            if (nIndex == 0)
+                            {
+                                // calculate adj1
+                                double fadj2(GetAdjustValueAsDouble(1));
+                                fAdjustY = fWScaled * (2.0 * fAdjustY - fadj2);
+                                fAdjustY += (200000.0 / fss * fHeight - fadj2) 
* fSqrt;
+                                double fDenominator(fSqrt + fWScaled);
+                                fAdjustY /= fDenominator != 0.0 ? fDenominator 
: 1.0;
+                            }
+                            else
+                            {
+                                // nIndex == 1, calculate adj2
+                                double fadj1(GetAdjustValueAsDouble(0));
+                                fAdjustY = fWScaled * (2.0 * fAdjustY + fadj1);
+                                fAdjustY += (200000.0 / fss * fHeight - fadj1) 
* fSqrt;
+                                double fDenominator(fSqrt + 3.0 * fWScaled);
+                                fAdjustY /= fDenominator != 0.0 ? fDenominator 
: 1.0;
+                            }
+                        }
+                    }
+                    else if (sShapeType == "ooxml-uturnArrow" && nIndex == 2)
+                    {
+                        double fss(std::min(fWidth, fHeight));
+                        if (fss != 0.0)
+                        {
+                            double fadj5(GetAdjustValueAsDouble(4));
+                            fAdjustY += fHeight / fss * (fadj5 - 100000.0);
+                        }
+                    }
                 }
+
                 if ( nFirstAdjustmentValue >= 0 )
                 {
                     if ( aHandle.nFlags & HandleFlags::RANGE_X_MINIMUM )       
 // check if horizontal handle needs to be within a range
                     {
                         double fXMin;
                         GetParameter( fXMin, aHandle.aXRangeMinimum, false, 
false );
-                        if ( fPos1 < fXMin )
-                            fPos1 = fXMin;
+                        if (fAdjustX < fXMin)
+                            fAdjustX = fXMin;
                     }
                     if ( aHandle.nFlags & HandleFlags::RANGE_X_MAXIMUM )       
 // check if horizontal handle needs to be within a range
                     {
                         double fXMax;
                         GetParameter( fXMax, aHandle.aXRangeMaximum, false, 
false );
-                        if ( fPos1 > fXMax )
-                            fPos1 = fXMax;
+                        if (fAdjustX > fXMax)
+                            fAdjustX = fXMax;
                     }
-                    SetAdjustValueAsDouble( fPos1, nFirstAdjustmentValue );
+                    SetAdjustValueAsDouble(fAdjustX, nFirstAdjustmentValue);
                 }
                 if ( nSecondAdjustmentValue >= 0 )
                 {
@@ -1359,17 +1649,17 @@ bool 
EnhancedCustomShape2d::SetHandleControllerPosition( const sal_uInt32 nIndex
                     {
                         double fYMin;
                         GetParameter( fYMin, aHandle.aYRangeMinimum, false, 
false );
-                        if ( fPos2 < fYMin )
-                            fPos2 = fYMin;
+                        if (fAdjustY < fYMin)
+                            fAdjustY = fYMin;
                     }
                     if ( aHandle.nFlags & HandleFlags::RANGE_Y_MAXIMUM )       
 // check if vertical handle needs to be within a range
                     {
                         double fYMax;
                         GetParameter( fYMax, aHandle.aYRangeMaximum, false, 
false );
-                        if ( fPos2 > fYMax )
-                            fPos2 = fYMax;
+                        if (fAdjustY > fYMax)
+                            fAdjustY = fYMax;
                     }
-                    SetAdjustValueAsDouble( fPos2, nSecondAdjustmentValue );
+                    SetAdjustValueAsDouble(fAdjustY, nSecondAdjustmentValue);
                 }
             }
             // and writing them back into the GeometryItem
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to