borderline: Preparing further to use CellCoordinateSystem

Multiple cleanups, made svx::frame::Style a std::shared_ptr class,
preparing basing all border stuff on transformations, so it will
need a CellCoordinateSystem. Added stuff to get this Coordinate
System from the svx::frame::Cell using the Frame and knowledge
about ownerships

Change-Id: Ic2cb59cc92e648ac2fef72f22f8913479769d3e2
diff --git a/basegfx/source/matrix/b2dhommatrixtools.cxx b/basegfx/source/matrix/b2dhommatrixtools.cxx
index 645d4c7..89ab914 100644
--- a/basegfx/source/matrix/b2dhommatrixtools.cxx
+++ b/basegfx/source/matrix/b2dhommatrixtools.cxx
@@ -398,6 +398,20 @@ namespace basegfx
            return aRetval;
        }

        B2DHomMatrix createCoordinateSystemTransform(
            const B2DPoint& rOrigin,
            const B2DVector& rX,
            const B2DVector& rY)
        {
            return basegfx::B2DHomMatrix(
                rX.getX(), rY.getX(), rOrigin.getX(),
                rX.getY(), rY.getY(), rOrigin.getY());
        }

        B2DTuple getColumn(const B2DHomMatrix& rMatrix, sal_uInt16 nCol)
        {
            return B2DTuple(rMatrix.get(0, nCol), rMatrix.get(1, nCol));
        }
    } // end of namespace tools
} // end of namespace basegfx

diff --git a/include/basegfx/matrix/b2dhommatrixtools.hxx b/include/basegfx/matrix/b2dhommatrixtools.hxx
index 5dcd1b4..aa3c047 100644
--- a/include/basegfx/matrix/b2dhommatrixtools.hxx
+++ b/include/basegfx/matrix/b2dhommatrixtools.hxx
@@ -131,6 +131,15 @@ namespace basegfx
            const B2DRange& rSourceRange,
            const B2DRange& rTargetRange);

        /// create based on given CoordinateSystem which is defined by origin and x/yaxis
        BASEGFX_DLLPUBLIC B2DHomMatrix createCoordinateSystemTransform(
            const B2DPoint& rOrigin,
            const B2DVector& rX,
            const B2DVector& rY);

        /// get column vector from B2dHomMatrix, e.g. to extract coordinate system origin and x/yaxis
        BASEGFX_DLLPUBLIC B2DTuple getColumn(const B2DHomMatrix& rMatrix, sal_uInt16 nCol);

    } // end of namespace tools
} // end of namespace basegfx

diff --git a/include/svx/framelink.hxx b/include/svx/framelink.hxx
index 973c401..d0fb165 100644
--- a/include/svx/framelink.hxx
+++ b/include/svx/framelink.hxx
@@ -107,6 +107,43 @@ enum class RefMode
 */
class SAL_WARN_UNUSED SVX_DLLPUBLIC Style
{
private:
    class implStyle
    {
    private:
        friend class Style;

        Color               maColorPrim;
        Color               maColorSecn;
        Color               maColorGap;
        bool                mbUseGapColor;
        RefMode             meRefMode;  /// Reference point handling for this frame border.
        double              mfPrim;     /// Width of primary (single, left, or top) line.
        double              mfDist;     /// Distance between primary and secondary line.
        double              mfSecn;     /// Width of secondary (right or bottom) line.
        double              mfPatternScale; /// Scale used for line pattern spacing.
        SvxBorderLineStyle  mnType;
        const Cell*         mpUsingCell;

    public:
        /** Constructs an invisible frame style. */
        explicit implStyle() :
            maColorPrim(),
            maColorSecn(),
            maColorGap(),
            mbUseGapColor(false),
            meRefMode(RefMode::Centered),
            mfPrim(0.0),
            mfDist(0.0),
            mfSecn(0.0),
            mfPatternScale(1.0),
            mnType(SvxBorderLineStyle::SOLID),
            mpUsingCell(nullptr)
        {}
    };

    std::shared_ptr< implStyle >        maImplStyle;

public:
    /** Constructs an invisible frame style. */
    explicit Style();
@@ -118,20 +155,20 @@ public:
    /** Constructs a frame style from the passed SvxBorderLine struct. Clears the style, if pBorder is 0. */
    explicit Style( const editeng::SvxBorderLine* pBorder, double fScale = 1.0 );

    RefMode      GetRefMode() const { return meRefMode; }
    const Color& GetColorPrim() const { return maColorPrim; }
    const Color& GetColorSecn() const { return maColorSecn; }
    const Color& GetColorGap() const { return maColorGap; }
    bool         UseGapColor() const { return mbUseGapColor; }
    double       Prim() const { return mfPrim; }
    double       Dist() const { return mfDist; }
    double       Secn() const { return mfSecn; }
    double PatternScale() const { return mfPatternScale;}
    void SetPatternScale( double fScale );
    SvxBorderLineStyle Type() const { return mnType; }
    RefMode      GetRefMode() const { return maImplStyle->meRefMode; }
    const Color& GetColorPrim() const { return maImplStyle->maColorPrim; }
    const Color& GetColorSecn() const { return maImplStyle->maColorSecn; }
    const Color& GetColorGap() const { return maImplStyle->maColorGap; }
    bool         UseGapColor() const { return maImplStyle->mbUseGapColor; }
    double       Prim() const { return maImplStyle->mfPrim; }
    double       Dist() const { return maImplStyle->mfDist; }
    double       Secn() const { return maImplStyle->mfSecn; }
    double PatternScale() const { return maImplStyle->mfPatternScale;}
    void SetPatternScale( double fScale ) { maImplStyle->mfPatternScale = fScale; }
    SvxBorderLineStyle Type() const { return maImplStyle->mnType; }

    /** Returns the total width of this frame style. */
    double       GetWidth() const { return mfPrim + mfDist + mfSecn; }
    double       GetWidth() const;

    /** Sets the frame style to invisible state. */
    void                Clear();
@@ -146,37 +183,24 @@ public:
    void                Set( const editeng::SvxBorderLine* pBorder, double fScale, sal_uInt16 nMaxWidth = SAL_MAX_UINT16 );

    /** Sets a new reference point handling mode, does not modify other settings. */
    void         SetRefMode( RefMode eRefMode ) { meRefMode = eRefMode; }
    void         SetRefMode( RefMode eRefMode ) { maImplStyle->meRefMode = eRefMode; }
    /** Sets a new color, does not modify other settings. */
    void         SetColorPrim( const Color& rColor ) { maColorPrim = rColor; }
    void         SetColorSecn( const Color& rColor ) { maColorSecn = rColor; }
    void         SetColorPrim( const Color& rColor ) { maImplStyle->maColorPrim = rColor; }
    void         SetColorSecn( const Color& rColor ) { maImplStyle->maColorSecn = rColor; }
    /** Sets whether to use dotted style for single hair lines. */
    void         SetType( SvxBorderLineStyle nType ) { mnType = nType; }
    void         SetType( SvxBorderLineStyle nType ) { maImplStyle->mnType = nType; }

    /** Mirrors this style (exchanges primary and secondary), if it is a double frame style. */
    Style&              MirrorSelf();

    /** return the Cell using this style (if set) */
    const Cell* GetUsingCell() const { return mpUsingCell; }
    const Cell* GetUsingCell() const;

private:
    Color               maColorPrim;
    Color               maColorSecn;
    Color               maColorGap;
    bool                mbUseGapColor;
    RefMode             meRefMode;  /// Reference point handling for this frame border.
    double              mfPrim;     /// Width of primary (single, left, or top) line.
    double              mfDist;     /// Distance between primary and secondary line.
    double              mfSecn;     /// Width of secondary (right or bottom) line.
    double              mfPatternScale; /// Scale used for line pattern spacing.
    SvxBorderLineStyle  mnType;

    /// need information which cell this style info comes from due to needed
    /// rotation info (which is in the cell). Rotation depends on the cell.
    /// Encapsulated using a single static friend method that is the single
    /// allowed instance to set/modify this value
    friend void exclusiveSetUsigCellAtStyle(Style& rStyle, const Cell* pCell);
    const Cell*         mpUsingCell;
    friend class Cell;
    void SetUsingCell(const Cell* pCell);
};

bool operator==( const Style& rL, const Style& rR );
diff --git a/include/svx/framelinkarray.hxx b/include/svx/framelinkarray.hxx
index 4801db2..78b95c8 100644
--- a/include/svx/framelinkarray.hxx
+++ b/include/svx/framelinkarray.hxx
@@ -292,6 +292,7 @@ public:
    /** Returns the output range of the cell (nCol,nRow).
        Returns total output range of merged ranges. */
    basegfx::B2DRange GetCellRange( size_t nCol, size_t nRow ) const;
    basegfx::B2DRange GetCellRange( size_t nCellIndex ) const;

    // mirroring --------------------------------------------------------------

@@ -311,6 +312,8 @@ public:
    /** Draws the part of the array, that is inside the clipping range. */
    void                DrawArray(drawinglayer::processor2d::BaseProcessor2D& rProcessor) const;

    // fill the Cell::maCellIndex entries to allow referencing back from Cell to Array Col/Row coordinates
    void AddCellIndices() const;

private:
    std::unique_ptr<ArrayImpl>        mxImpl;
diff --git a/svx/source/dialog/framelink.cxx b/svx/source/dialog/framelink.cxx
index e5523fe..cbabbcb 100644
--- a/svx/source/dialog/framelink.cxx
+++ b/svx/source/dialog/framelink.cxx
@@ -41,64 +41,36 @@ using namespace editeng;
namespace svx {
namespace frame {


namespace {

/** Converts a width in twips to a width in another map unit (specified by fScale). */
double lclScaleValue( double nValue, double fScale, sal_uInt16 nMaxWidth )
{
    return std::min<double>(nValue * fScale, nMaxWidth);
}

} // namespace


// Classes


#define SCALEVALUE( value ) lclScaleValue( value, fScale, nMaxWidth )

Style::Style() :
    meRefMode(RefMode::Centered),
    mfPatternScale(1.0),
    mnType(SvxBorderLineStyle::SOLID),
    mpUsingCell(nullptr)
Style::Style() : maImplStyle(new implStyle())
{
    Clear();
}

Style::Style( double nP, double nD, double nS, SvxBorderLineStyle nType ) :
    meRefMode(RefMode::Centered),
    mfPatternScale(1.0),
    mnType(nType),
    mpUsingCell(nullptr)
Style::Style( double nP, double nD, double nS, SvxBorderLineStyle nType ) : maImplStyle(new implStyle())
{
    maImplStyle->mnType = nType;
    Clear();
    Set( nP, nD, nS );
}

Style::Style( const Color& rColorPrim, const Color& rColorSecn, const Color& rColorGap, bool bUseGapColor,
              double nP, double nD, double nS, SvxBorderLineStyle nType ) :
    meRefMode(RefMode::Centered),
    mfPatternScale(1.0),
    mnType(nType),
    mpUsingCell(nullptr)
Style::Style( const Color& rColorPrim, const Color& rColorSecn, const Color& rColorGap, bool bUseGapColor, double nP, double nD, double nS, SvxBorderLineStyle nType ) : maImplStyle(new implStyle())
{
    maImplStyle->mnType = nType;
    Set( rColorPrim, rColorSecn, rColorGap, bUseGapColor, nP, nD, nS );
}

Style::Style( const editeng::SvxBorderLine* pBorder, double fScale ) :
    meRefMode(RefMode::Centered),
    mfPatternScale(fScale),
    mpUsingCell(nullptr)
Style::Style( const editeng::SvxBorderLine* pBorder, double fScale ) : maImplStyle(new implStyle())
{
    maImplStyle->mfPatternScale = fScale;
    Set( pBorder, fScale );
}


void Style::SetPatternScale( double fScale )
double Style::GetWidth() const
{
    mfPatternScale = fScale;
    implStyle* pTarget = maImplStyle.get();

    return pTarget->mfPrim + pTarget->mfDist + pTarget->mfSecn;
}

void Style::Clear()
@@ -115,66 +87,78 @@ void Style::Set( double nP, double nD, double nS )
        >0  0   >0      nP      0       0
        >0  >0  >0      nP      nD      nS
     */
    mfPrim = rtl::math::round(nP ? nP : nS, 2);
    mfDist = rtl::math::round((nP && nS) ? nD : 0, 2);
    mfSecn = rtl::math::round((nP && nD) ? nS : 0, 2);
    implStyle* pTarget = maImplStyle.get();
    pTarget->mfPrim = rtl::math::round(nP ? nP : nS, 2);
    pTarget->mfDist = rtl::math::round((nP && nS) ? nD : 0, 2);
    pTarget->mfSecn = rtl::math::round((nP && nD) ? nS : 0, 2);
}

void Style::Set( const Color& rColorPrim, const Color& rColorSecn, const Color& rColorGap, bool bUseGapColor, double nP, double nD, double nS )
{
    maColorPrim = rColorPrim;
    maColorSecn = rColorSecn;
    maColorGap = rColorGap;
    mbUseGapColor = bUseGapColor;
    implStyle* pTarget = maImplStyle.get();
    pTarget->maColorPrim = rColorPrim;
    pTarget->maColorSecn = rColorSecn;
    pTarget->maColorGap = rColorGap;
    pTarget->mbUseGapColor = bUseGapColor;
    Set( nP, nD, nS );
}

void Style::Set( const SvxBorderLine& rBorder, double fScale, sal_uInt16 nMaxWidth )
{
    maColorPrim = rBorder.GetColorOut();
    maColorSecn = rBorder.GetColorIn();
    maColorGap = rBorder.GetColorGap();
    mbUseGapColor = rBorder.HasGapColor();
    implStyle* pTarget = maImplStyle.get();
    pTarget->maColorPrim = rBorder.GetColorOut();
    pTarget->maColorSecn = rBorder.GetColorIn();
    pTarget->maColorGap = rBorder.GetColorGap();
    pTarget->mbUseGapColor = rBorder.HasGapColor();

    sal_uInt16 nPrim = rBorder.GetOutWidth();
    sal_uInt16 nDist = rBorder.GetDistance();
    sal_uInt16 nSecn = rBorder.GetInWidth();

    mnType = rBorder.GetBorderLineStyle();
    pTarget->mnType = rBorder.GetBorderLineStyle();
    if( !nSecn )    // no or single frame border
    {
        Set( SCALEVALUE( nPrim ), 0, 0 );
        Set( std::min<double>(nPrim * fScale, nMaxWidth), 0, 0 );
    }
    else
    {
        Set( SCALEVALUE( nPrim ), SCALEVALUE( nDist ), SCALEVALUE( nSecn ) );
        Set(std::min<double>(nPrim * fScale, nMaxWidth), std::min<double>(nDist * fScale, nMaxWidth), std::min<double>(nSecn * fScale, nMaxWidth));
        // Enlarge the style if distance is too small due to rounding losses.
        double nPixWidth = SCALEVALUE( nPrim + nDist + nSecn );
        double nPixWidth = std::min<double>((nPrim + nDist + nSecn) * fScale, nMaxWidth);

        if( nPixWidth > GetWidth() )
            mfDist = nPixWidth - mfPrim - mfSecn;
        {
            pTarget->mfDist = nPixWidth - pTarget->mfPrim - pTarget->mfSecn;
        }

        // Shrink the style if it is too thick for the control.
        while( GetWidth() > nMaxWidth )
        {
            // First decrease space between lines.
            if (mfDist)
                --mfDist;
            // Still too thick? Decrease the line widths.
            if( GetWidth() > nMaxWidth )
            if (pTarget->mfDist)
            {
                if (mfPrim != 0.0 && rtl::math::approxEqual(mfPrim, mfSecn))
                {
                    // Both lines equal - decrease both to keep symmetry.
                    --mfPrim;
                    --mfSecn;
                }
                else
                {
                    // Decrease each line for itself
                    if (mfPrim)
                        --mfPrim;
                    if ((GetWidth() > nMaxWidth) && mfSecn != 0.0)
                        --mfSecn;
                }
                --(pTarget->mfDist);
                continue;
            }

            // Still too thick? Decrease the line widths.
            if (pTarget->mfPrim != 0.0 && rtl::math::approxEqual(pTarget->mfPrim, pTarget->mfSecn))
            {
                // Both lines equal - decrease both to keep symmetry.
                --(pTarget->mfPrim);
                --(pTarget->mfSecn);
                continue;
            }

            // Decrease each line for itself
            if (pTarget->mfPrim)
            {
                --(pTarget->mfPrim);
            }

            if ((GetWidth() > nMaxWidth) && pTarget->mfSecn != 0.0)
            {
                --(pTarget->mfSecn);
            }
        }
    }
@@ -183,23 +167,36 @@ void Style::Set( const SvxBorderLine& rBorder, double fScale, sal_uInt16 nMaxWid
void Style::Set( const SvxBorderLine* pBorder, double fScale, sal_uInt16 nMaxWidth )
{
    if( pBorder )
    {
        Set( *pBorder, fScale, nMaxWidth );
    }
    else
    {
        Clear();
        mnType = SvxBorderLineStyle::SOLID;
        maImplStyle->mnType = SvxBorderLineStyle::SOLID;
    }
}

Style& Style::MirrorSelf()
{
    if (mfSecn)
        std::swap( mfPrim, mfSecn );
    if( meRefMode != RefMode::Centered )
        meRefMode = (meRefMode == RefMode::Begin) ? RefMode::End : RefMode::Begin;
    implStyle* pTarget = maImplStyle.get();

    if (pTarget->mfSecn)
    {
        std::swap( pTarget->mfPrim, pTarget->mfSecn );
    }

    if( pTarget->meRefMode != RefMode::Centered )
    {
        pTarget->meRefMode = (pTarget->meRefMode == RefMode::Begin) ? RefMode::End : RefMode::Begin;
    }

    return *this;
}

const Cell* Style::GetUsingCell() const { return maImplStyle->mpUsingCell; }
void Style::SetUsingCell(const Cell* pCell) { maImplStyle->mpUsingCell = pCell; }

bool operator==( const Style& rL, const Style& rR )
{
    return (rL.Prim() == rR.Prim()) && (rL.Dist() == rR.Dist()) && (rL.Secn() == rR.Secn()) &&
@@ -228,8 +225,6 @@ bool operator<( const Style& rL, const Style& rR )
    return false;
}

#undef SCALEVALUE

bool CheckFrameBorderConnectable( const Style& rLBorder, const Style& rRBorder,
        const Style& rTFromTL, const Style& rTFromT, const Style& rTFromTR,
        const Style& rBFromBL, const Style& rBFromB, const Style& rBFromBR )
diff --git a/svx/source/dialog/framelinkarray.cxx b/svx/source/dialog/framelinkarray.cxx
index ef6c410..70a7175 100644
--- a/svx/source/dialog/framelinkarray.cxx
+++ b/svx/source/dialog/framelinkarray.cxx
@@ -24,14 +24,11 @@
#include <algorithm>
#include <vcl/outdev.hxx>
#include <drawinglayer/primitive2d/borderlineprimitive2d.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>

namespace svx {
namespace frame {

/// single exclusive friend method to change mpUsingCell at style when style
/// is set at Cell, see friend definition for more info
void exclusiveSetUsigCellAtStyle(Style& rStyle, const Cell* pCell) { rStyle.mpUsingCell = pCell; }

class Cell
{
private:
@@ -48,8 +45,10 @@ public:
    long                mnAddTop;
    long                mnAddBottom;

    SvxRotateMode       meRotMode;
    double              mfOrientation;
    SvxRotateMode           meRotMode;
    double                  mfOrientation;
    basegfx::B2DHomMatrix   maCoordinateSystem;
    size_t                  maCellIndex;

    bool                mbMergeOrig;
    bool                mbOverlapX;
@@ -58,12 +57,12 @@ public:
public:
    explicit            Cell();

    void SetStyleLeft(const Style& rStyle) { maLeft = rStyle; exclusiveSetUsigCellAtStyle(maLeft, this); }
    void SetStyleRight(const Style& rStyle) { maRight = rStyle; exclusiveSetUsigCellAtStyle(maRight, this); }
    void SetStyleTop(const Style& rStyle) { maTop = rStyle; exclusiveSetUsigCellAtStyle(maTop, this); }
    void SetStyleBottom(const Style& rStyle) { maBottom = rStyle; exclusiveSetUsigCellAtStyle(maBottom, this); }
    void SetStyleTLBR(const Style& rStyle) { maTLBR = rStyle; exclusiveSetUsigCellAtStyle(maTLBR, this); }
    void SetStyleBLTR(const Style& rStyle) { maBLTR = rStyle; exclusiveSetUsigCellAtStyle(maBLTR, this); }
    void SetStyleLeft(const Style& rStyle) { maLeft = rStyle; maLeft.SetUsingCell(this); }
    void SetStyleRight(const Style& rStyle) { maRight = rStyle; maRight.SetUsingCell(this); }
    void SetStyleTop(const Style& rStyle) { maTop = rStyle; maTop.SetUsingCell(this); }
    void SetStyleBottom(const Style& rStyle) { maBottom = rStyle; maBottom.SetUsingCell(this); }
    void SetStyleTLBR(const Style& rStyle) { maTLBR = rStyle; maTLBR.SetUsingCell(this); }
    void SetStyleBLTR(const Style& rStyle) { maBLTR = rStyle; maBLTR.SetUsingCell(this); }

    const Style& GetStyleLeft() const { return maLeft; }
    const Style& GetStyleRight() const { return maRight; }
@@ -76,11 +75,65 @@ public:
    bool                IsRotated() const { return mfOrientation != 0.0; }

    void                MirrorSelfX();

    basegfx::B2DHomMatrix CreateCoordinateSystem(const Array& rArray) const;
};

typedef std::vector< long >     LongVec;
typedef std::vector< Cell >     CellVec;

basegfx::B2DHomMatrix Cell::CreateCoordinateSystem(const Array& rArray) const
{
    if(!maCoordinateSystem.isIdentity())
    {
        return maCoordinateSystem;
    }

    if(-1 == maCellIndex)
    {
        rArray.AddCellIndices();
    }

    if(-1 != maCellIndex)
    {
        const basegfx::B2DRange aRange(rArray.GetCellRange(maCellIndex));
        basegfx::B2DPoint aOrigin(aRange.getMinimum());
        basegfx::B2DVector aX(aRange.getWidth(), 0.0);
        basegfx::B2DVector aY(0.0, aRange.getHeight());

        if (IsRotated() && SvxRotateMode::SVX_ROTATE_MODE_STANDARD != meRotMode)
        {
            // when rotated, adapt values. Get Skew (cos/sin == 1/tan)
            const double fSkew(aY.getY() * (cos(mfOrientation) / sin(mfOrientation)));

            switch (meRotMode)
            {
            case SvxRotateMode::SVX_ROTATE_MODE_TOP:
                // shear Y-Axis
                aY.setX(-fSkew);
                break;
            case SvxRotateMode::SVX_ROTATE_MODE_CENTER:
                // shear origin half, Y full
                aOrigin.setX(aOrigin.getX() + (fSkew * 0.5));
                aY.setX(-fSkew);
                break;
            case SvxRotateMode::SVX_ROTATE_MODE_BOTTOM:
                // shear origin full, Y full
                aOrigin.setX(aOrigin.getX() + fSkew);
                aY.setX(-fSkew);
                break;
            default: // SvxRotateMode::SVX_ROTATE_MODE_STANDARD, already excluded above
                break;
            }
        }

        // use column vectors as coordinate axes, homogen column for translation
        const_cast<Cell*>(this)->maCoordinateSystem = basegfx::tools::createCoordinateSystemTransform(aOrigin, aX, aY);
    }

    return maCoordinateSystem;
}

Cell::Cell() :
    mnAddLeft( 0 ),
    mnAddRight( 0 ),
@@ -88,6 +141,8 @@ Cell::Cell() :
    mnAddBottom( 0 ),
    meRotMode(SvxRotateMode::SVX_ROTATE_MODE_STANDARD ),
    mfOrientation( 0.0 ),
    maCoordinateSystem(),
    maCellIndex(static_cast<size_t>(-1)),
    mbMergeOrig( false ),
    mbOverlapX( false ),
    mbOverlapY( false )
@@ -101,6 +156,8 @@ void Cell::MirrorSelfX()
    maLeft.MirrorSelf();
    maRight.MirrorSelf();
    mfOrientation = -mfOrientation;
    maCoordinateSystem.identity();
    maCellIndex = static_cast<size_t>(-1);
}


@@ -808,6 +865,11 @@ long Array::GetHeight() const
    return GetRowPosition( mxImpl->mnHeight ) - GetRowPosition( 0 );
}

basegfx::B2DRange Array::GetCellRange( size_t nCellIndex ) const
{
    return GetCellRange(nCellIndex % GetColCount(), nCellIndex / GetColCount());
}

basegfx::B2DRange Array::GetCellRange( size_t nCol, size_t nRow ) const
{
    size_t nFirstCol = mxImpl->GetMergedFirstCol( nCol, nRow );
@@ -866,46 +928,6 @@ void Array::MirrorSelfX()
}

// drawing

void CreateCoordinateSystemForCell(
    const basegfx::B2DRange& rRange,
    const Cell& rCell,
    basegfx::B2DPoint& rOrigin,
    basegfx::B2DVector& rX,
    basegfx::B2DVector& rY)
{
    // fill in defaults
    rOrigin = rRange.getMinimum();
    rX = basegfx::B2DVector(rRange.getWidth(), 0.0);
    rY = basegfx::B2DVector(0.0, rRange.getHeight());

    if (rCell.IsRotated() && SvxRotateMode::SVX_ROTATE_MODE_STANDARD != rCell.meRotMode)
    {
        // when rotated, adapt values. Get Skew (cos/sin == 1/tan)
        const double fSkew(rRange.getHeight() * (cos(rCell.mfOrientation) / sin(rCell.mfOrientation)));

        switch (rCell.meRotMode)
        {
        case SvxRotateMode::SVX_ROTATE_MODE_TOP:
            // shear Y-Axis
            rY.setX(-fSkew);
            break;
        case SvxRotateMode::SVX_ROTATE_MODE_CENTER:
            // shear origin half, Y full
            rOrigin.setX(rOrigin.getX() + (fSkew * 0.5));
            rY.setX(-fSkew);
            break;
        case SvxRotateMode::SVX_ROTATE_MODE_BOTTOM:
            // shear origin full, Y full
            rOrigin.setX(rOrigin.getX() + fSkew);
            rY.setX(-fSkew);
            break;
        default: // SvxRotateMode::SVX_ROTATE_MODE_STANDARD, already excluded above
            break;
        }
    }
}

void Array::DrawRange( drawinglayer::processor2d::BaseProcessor2D& rProcessor,
        size_t nFirstCol, size_t nFirstRow, size_t nLastCol, size_t nLastRow,
        const Color* pForceColor ) const
@@ -942,17 +964,13 @@ void Array::DrawRange( drawinglayer::processor2d::BaseProcessor2D& rProcessor,
                    if (rTLBR.GetWidth() || rBLTR.GetWidth())
                    {
                        drawinglayer::primitive2d::Primitive2DContainer aSequence;
                        basegfx::B2DPoint aOrigin;
                        basegfx::B2DVector aX;
                        basegfx::B2DVector aY;

                        CreateCoordinateSystemForCell(aRange, rCell, aOrigin, aX, aY);
                        basegfx::B2DHomMatrix aCoordinateSystem(rCell.CreateCoordinateSystem(*this));

                        CreateDiagFrameBorderPrimitives(
                            aSequence,
                            aOrigin,
                            aX,
                            aY,
                            basegfx::tools::getColumn(aCoordinateSystem, 2),
                            basegfx::tools::getColumn(aCoordinateSystem, 0),
                            basegfx::tools::getColumn(aCoordinateSystem, 1),
                            rTLBR,
                            rBLTR,
                            GetCellStyleLeft(_nFirstCol, _nFirstRow),
@@ -1016,54 +1034,40 @@ void Array::DrawRange( drawinglayer::processor2d::BaseProcessor2D& rProcessor,
                if ((pStart->Prim() || pStart->Secn()) && (aStartPos.getX() <= aEndPos.getX()))
                {
                    // prepare defaults for borderline coordinate system
                    basegfx::B2DPoint aOrigin(aStartPos);
                    basegfx::B2DVector aX(aEndPos - aStartPos);
                    basegfx::B2DVector aY(0.0, 1.0);
                    const Cell* pCell = pStart->GetUsingCell();

                    if (pCell && pCell->IsRotated())
                    if(pCell)
                    {
                        // To apply the shear, we need to get the cell range. We have the defining cell,
                        // but there is no call at it to directly get it's range. To get the correct one,
                        // we need to take care if the borderline is at top or bottom, so use pointer
                        // compare here to find out
                        basegfx::B2DHomMatrix aCoordinateSystem(pCell->CreateCoordinateSystem(*this));
                        const bool bUpper(&pCell->GetStyleTop() == pStart);
                        const basegfx::B2DRange aRange(GetCellRange(nCol - 1, bUpper ? nRow : nRow - 1));

                        // adapt to cell coordinate system, including shear
                        CreateCoordinateSystemForCell(aRange, *pCell, aOrigin, aX, aY);

                        if (!bUpper)
                        if(!bUpper)
                        {
                            // for the lower edge we need to translate to get to the
                            // borderline coordinate system. For the upper one, all is
                            // okay already
                            aOrigin  += aY;
                            // borderline coordinate system
                            aCoordinateSystem.set(0, 2, aCoordinateSystem.get(0, 2) + aCoordinateSystem.get(0, 1));
                            aCoordinateSystem.set(1, 2, aCoordinateSystem.get(1, 2) + aCoordinateSystem.get(1, 1));
                        }

                        // borderline coordinate system uses normalized 2nd axis
                        aY.normalize();
                        drawinglayer::primitive2d::Primitive2DContainer aSequence;
                        CreateBorderPrimitives(
                            aSequence,
                            basegfx::tools::getColumn(aCoordinateSystem, 2),
                            basegfx::tools::getColumn(aCoordinateSystem, 0),
                            basegfx::B2DVector(basegfx::tools::getColumn(aCoordinateSystem, 1)).normalize(),
                            *pStart,
                            aStartLFromTR,
                            *pStartLFromT,
                            *pStartLFromL,
                            *pStartLFromB,
                            aStartLFromBR,
                            aEndRFromTL,
                            *pEndRFromT,
                            *pEndRFromR,
                            *pEndRFromB,
                            aEndRFromBL,
                            pForceColor);
                        rProcessor.process(aSequence);
                    }

                    drawinglayer::primitive2d::Primitive2DContainer aSequence;
                    CreateBorderPrimitives(
                        aSequence,
                        aOrigin,
                        aX,
                        aY,
                        *pStart,
                        aStartLFromTR,
                        *pStartLFromT,
                        *pStartLFromL,
                        *pStartLFromB,
                        aStartLFromBR,
                        aEndRFromTL,
                        *pEndRFromT,
                        *pEndRFromR,
                        *pEndRFromB,
                        aEndRFromBL,
                        pForceColor);
                    rProcessor.process(aSequence);
                }

                // re-init "*Start***" variables
@@ -1090,45 +1094,41 @@ void Array::DrawRange( drawinglayer::processor2d::BaseProcessor2D& rProcessor,
        {
            // for description of involved coordinate systems have a look at
            // the first CreateBorderPrimitives call above
            basegfx::B2DPoint aOrigin(aStartPos);
            basegfx::B2DVector aX(aEndPos - aStartPos);
            basegfx::B2DVector aY(0.0, 1.0);
            const Cell* pCell = pStart->GetUsingCell();

            if (pCell && pCell->IsRotated())
            if(pCell)
            {
                basegfx::B2DHomMatrix aCoordinateSystem(pCell->CreateCoordinateSystem(*this));
                const bool bUpper(&pCell->GetStyleTop() == pStart);
                const basegfx::B2DRange aRange(GetCellRange(nCol - 1, bUpper ? nRow : nRow - 1));

                CreateCoordinateSystemForCell(aRange, *pCell, aOrigin, aX, aY);

                if (!bUpper)
                if(!bUpper)
                {
                    aOrigin += aY;
                    // for the lower edge we need to translate to get to the
                    // borderline coordinate system
                    aCoordinateSystem.set(0, 2, aCoordinateSystem.get(0, 2) + aCoordinateSystem.get(0, 1));
                    aCoordinateSystem.set(1, 2, aCoordinateSystem.get(1, 2) + aCoordinateSystem.get(1, 1));
                }

                aY.normalize();
                drawinglayer::primitive2d::Primitive2DContainer aSequence;
                CreateBorderPrimitives(
                    aSequence,
                    basegfx::tools::getColumn(aCoordinateSystem, 2),
                    basegfx::tools::getColumn(aCoordinateSystem, 0),
                    basegfx::B2DVector(basegfx::tools::getColumn(aCoordinateSystem, 1)).normalize(),
                    *pStart,
                    aStartLFromTR,
                    *pStartLFromT,
                    *pStartLFromL,
                    *pStartLFromB,
                    aStartLFromBR,
                    aEndRFromTL,
                    *pEndRFromT,
                    *pEndRFromR,
                    *pEndRFromB,
                    aEndRFromBL,
                    pForceColor);
                rProcessor.process(aSequence);
            }

            drawinglayer::primitive2d::Primitive2DContainer aSequence;
            CreateBorderPrimitives(
                aSequence,
                aOrigin,
                aX,
                aY,
                *pStart,
                aStartLFromTR,
                *pStartLFromT,
                *pStartLFromL,
                *pStartLFromB,
                aStartLFromBR,
                aEndRFromTL,
                *pEndRFromT,
                *pEndRFromR,
                *pEndRFromB,
                aEndRFromBL,
                pForceColor);
            rProcessor.process(aSequence);
        }
    }

@@ -1177,59 +1177,57 @@ void Array::DrawRange( drawinglayer::processor2d::BaseProcessor2D& rProcessor,
                {
                    // for description of involved coordinate systems have a look at
                    // the first CreateBorderPrimitives call above. Additionally adapt to vertical
                    basegfx::B2DPoint aOrigin(aStartPos);
                    basegfx::B2DVector aX(aEndPos - aStartPos);
                    basegfx::B2DVector aY(-1.0, 0.0);
                    const Cell* pCell = pStart->GetUsingCell();

                    if (pCell && pCell->IsRotated())
                    if(pCell)
                    {
                         basegfx::B2DHomMatrix aCoordinateSystem(pCell->CreateCoordinateSystem(*this));
                        const bool bLeft(&pCell->GetStyleLeft() == pStart);
                        const  basegfx::B2DRange aRange(GetCellRange(bLeft ? nCol : nCol - 1, nRow - 1));

                        CreateCoordinateSystemForCell(aRange, *pCell, aOrigin, aX, aY);

                        if (!bLeft)
                        if(!bLeft)
                        {
                            aOrigin += aX;
                            // for the Right edge we need to translate to get to the
                            // borderline coordinate system
                            aCoordinateSystem.set(0, 2, aCoordinateSystem.get(0, 2) + aCoordinateSystem.get(0, 0));
                            aCoordinateSystem.set(1, 2, aCoordinateSystem.get(1, 2) + aCoordinateSystem.get(1, 0));
                        }

                        // The *coordinate system* of the edge has to be given, which for vertical
                        // lines uses the Y-Vector as X-Axis and the X-Vector as Y-Axis, so swap both
                        // and mirror aX to keep the same orientation (should be (-1.0, 0.0) for
                        // horizontal lines anyways, this could be used as test here, checked in debug mode)
                        std::swap(aX, aY);
                        aY.normalize();
                        aY = -aY;
                    }
                        const basegfx::B2DTuple aX(basegfx::tools::getColumn(aCoordinateSystem, 0));
                        const basegfx::B2DTuple aY(basegfx::tools::getColumn(aCoordinateSystem, 1));
                        aCoordinateSystem = basegfx::tools::createCoordinateSystemTransform(basegfx::tools::getColumn(aCoordinateSystem, 2), aY, -aX);

                    drawinglayer::primitive2d::Primitive2DContainer aSequence;
                    CreateBorderPrimitives(
                        // This replaces DrawVerFrameBorder which went from top to bottom. To be able to use
                        // the same method as for horizontal (CreateBorderPrimitives), the given borders
                        // have to be rearranged. Best is to look at the explanations of parameters in
                        // framelink.hxx and the former calls to DrawVerFrameBorder and it's parameters.
                        // In principle, the order of the five TFrom and BFrom has to be
                        // inverted to get the same orientation. Before, EndPos and StartPos were changed
                        // which avoids the reordering, but also leads to inverted line patters for vertical
                        // lines.
                        aSequence,
                        aOrigin,
                        aX,
                        aY,
                        *pStart,
                        aStartTFromBR,
                        *pStartTFromR,
                        *pStartTFromT,
                        *pStartTFromL,
                        aStartTFromBL,
                        aEndBFromTR,
                        *pEndBFromR,
                        *pEndBFromB,
                        *pEndBFromL,
                        aEndBFromTL,
                        pForceColor);
                    rProcessor.process(aSequence);
                        drawinglayer::primitive2d::Primitive2DContainer aSequence;
                        CreateBorderPrimitives(
                            // This replaces DrawVerFrameBorder which went from top to bottom. To be able to use
                            // the same method as for horizontal (CreateBorderPrimitives), the given borders
                            // have to be rearranged. Best is to look at the explanations of parameters in
                            // framelink.hxx and the former calls to DrawVerFrameBorder and it's parameters.
                            // In principle, the order of the five TFrom and BFrom has to be
                            // inverted to get the same orientation. Before, EndPos and StartPos were changed
                            // which avoids the reordering, but also leads to inverted line patters for vertical
                            // lines.
                            aSequence,
                            basegfx::tools::getColumn(aCoordinateSystem, 2),
                            basegfx::tools::getColumn(aCoordinateSystem, 0),
                            basegfx::B2DVector(basegfx::tools::getColumn(aCoordinateSystem, 1)).normalize(),
                            *pStart,
                            aStartTFromBR,
                            *pStartTFromR,
                            *pStartTFromT,
                            *pStartTFromL,
                            aStartTFromBL,
                            aEndBFromTR,
                            *pEndBFromR,
                            *pEndBFromB,
                            *pEndBFromL,
                            aEndBFromTL,
                            pForceColor);
                        rProcessor.process(aSequence);
                    }
                }

                // re-init "*Start***" variables
@@ -1256,51 +1254,50 @@ void Array::DrawRange( drawinglayer::processor2d::BaseProcessor2D& rProcessor,
        {
            // for description of involved coordinate systems have a look at
            // the first CreateBorderPrimitives call above, adapt to vertical
            basegfx::B2DPoint aOrigin(aStartPos);
            basegfx::B2DVector aX(aEndPos - aStartPos);
            basegfx::B2DVector aY(-1.0, 0.0);
            const Cell* pCell = pStart->GetUsingCell();

            if (pCell && pCell->IsRotated())
            if(pCell)
            {
                basegfx::B2DHomMatrix aCoordinateSystem(pCell->CreateCoordinateSystem(*this));
                const bool bLeft(&pCell->GetStyleLeft() == pStart);
                const basegfx::B2DRange aRange(GetCellRange(bLeft ? nCol : nCol - 1, nRow - 1));

                CreateCoordinateSystemForCell(aRange, *pCell, aOrigin, aX, aY);

                if (!bLeft)
                if(!bLeft)
                {
                    aOrigin += aX;
                    // for the Right edge we need to translate to get to the
                    // borderline coordinate system
                    aCoordinateSystem.set(0, 2, aCoordinateSystem.get(0, 2) + aCoordinateSystem.get(0, 0));
                    aCoordinateSystem.set(1, 2, aCoordinateSystem.get(1, 2) + aCoordinateSystem.get(1, 0));
                }

                // The *coordinate system* of the edge has to be given, which for vertical
                // lines uses the Y-Vector as X-Axis and the X-Vector as Y-Axis, so swap both
                // and mirror aX to keep the same orientation (should be (-1.0, 0.0) for horizontal lines anyways)
                std::swap(aX, aY);
                aY.normalize();
                aY = -aY;
            }
                // and mirror aX to keep the same orientation (should be (-1.0, 0.0) for
                // horizontal lines anyways, this could be used as test here, checked in debug mode)
                const basegfx::B2DTuple aX(basegfx::tools::getColumn(aCoordinateSystem, 0));
                const basegfx::B2DTuple aY(basegfx::tools::getColumn(aCoordinateSystem, 1));
                aCoordinateSystem = basegfx::tools::createCoordinateSystemTransform(basegfx::tools::getColumn(aCoordinateSystem, 2), aY, -aX);

            drawinglayer::primitive2d::Primitive2DContainer aSequence;
            CreateBorderPrimitives(
                // also reordered, see call to CreateBorderPrimitives above
                aSequence,
                aOrigin,
                aX,
                aY,
                *pStart,
                aStartTFromBR,
                *pStartTFromR,
                *pStartTFromT,
                *pStartTFromL,
                aStartTFromBL,
                aEndBFromTR,
                *pEndBFromR,
                *pEndBFromB,
                *pEndBFromL,
                aEndBFromTL,
                pForceColor);
            rProcessor.process(aSequence);
                drawinglayer::primitive2d::Primitive2DContainer aSequence;
                CreateBorderPrimitives(
                    // also reordered, see call to CreateBorderPrimitives above
                    aSequence,
                    basegfx::tools::getColumn(aCoordinateSystem, 2),
                    basegfx::tools::getColumn(aCoordinateSystem, 0),
                    basegfx::B2DVector(basegfx::tools::getColumn(aCoordinateSystem, 1)).normalize(),
                    *pStart,
                    aStartTFromBR,
                    *pStartTFromR,
                    *pStartTFromT,
                    *pStartTFromL,
                    aStartTFromBL,
                    aEndBFromTR,
                    *pEndBFromR,
                    *pEndBFromB,
                    *pEndBFromL,
                    aEndBFromTL,
                    pForceColor);
                rProcessor.process(aSequence);
            }
        }
    }
}
@@ -1311,6 +1308,14 @@ void Array::DrawArray(drawinglayer::processor2d::BaseProcessor2D& rProcessor) co
        DrawRange(rProcessor, 0, 0, mxImpl->mnWidth - 1, mxImpl->mnHeight - 1, nullptr);
}

void Array::AddCellIndices() const
{
    for (size_t a(0); a < mxImpl->maCells.size(); a++)
    {
        const_cast<Array*>(this)->mxImpl->maCells[a].maCellIndex = a;
    }
}

#undef ORIGCELL
#undef CELLACC
#undef CELL