tdf#34449 : ability of deleting borders of a cell from adjacent cell

Change-Id: Ieb13a9ea88faa220d1ee352b0e47268a7fda5f38
Reviewed-on: https://gerrit.libreoffice.org/19715
Reviewed-by: Eike Rathke <erack@redhat.com>
Tested-by: Eike Rathke <erack@redhat.com>
diff --git a/cui/source/inc/border.hxx b/cui/source/inc/border.hxx
index 81fee64..ba780e1 100644
--- a/cui/source/inc/border.hxx
+++ b/cui/source/inc/border.hxx
@@ -98,6 +98,7 @@ private:
    VclPtr<CheckBox>           m_pMergeWithNextCB;
    // #i29550#
    VclPtr<CheckBox>           m_pMergeAdjacentBordersCB;
    VclPtr<CheckBox>           m_pRemoveAdjcentCellBordersCB;

    ImageList           aShadowImgLstH;
    ImageList           aShadowImgLst;
@@ -113,6 +114,8 @@ private:
    bool                mbBLTREnabled;      ///< true = Bottom-left to top-right border enabled.
    bool                mbUseMarginItem;
    bool                mbSync;
    bool                mbRemoveAdjacentCellBorders;
    bool                bIsCalcDoc;

    std::set<sal_Int16> maUsedBorderStyles;

@@ -125,6 +128,7 @@ private:
    DECL_LINK_TYPED( ModifyDistanceHdl_Impl, Edit&, void);
    DECL_LINK_TYPED( ModifyWidthHdl_Impl, Edit&, void);
    DECL_LINK_TYPED( SyncHdl_Impl, Button*, void);
    DECL_LINK_TYPED( RemoveAdjacentCellBorderHdl_Impl, Button*, void);

    sal_uInt16              GetPresetImageId( sal_uInt16 nValueSetIdx ) const;
    sal_uInt16              GetPresetStringId( sal_uInt16 nValueSetIdx ) const;
@@ -142,6 +146,7 @@ private:
                                             bool bValid );

    bool IsBorderLineStyleAllowed( sal_Int16 nStyle ) const;
    void UpdateRemoveAdjCellBorderCB( sal_uInt16 nPreset );
};


diff --git a/cui/source/tabpages/border.cxx b/cui/source/tabpages/border.cxx
index 180fd28..a4853ba 100644
--- a/cui/source/tabpages/border.cxx
+++ b/cui/source/tabpages/border.cxx
@@ -44,10 +44,13 @@
#include <svl/int64item.hxx>
#include <sfx2/itemconnect.hxx>
#include <sal/macros.h>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include "borderconn.hxx"

using namespace ::editeng;

using ::com::sun::star::uno::Reference;
using ::com::sun::star::lang::XServiceInfo;
using ::com::sun::star::uno::UNO_QUERY;


/*
@@ -99,7 +102,9 @@ SvxBorderTabPage::SvxBorderTabPage(vcl::Window* pParent, const SfxItemSet& rCore
        mbTLBREnabled( false ),
        mbBLTREnabled( false ),
        mbUseMarginItem( false ),
        mbSync(true)
        mbSync(true),
        mbRemoveAdjacentCellBorders( false ),
        bIsCalcDoc( false )

{
    get(m_pWndPresets, "presets");
@@ -130,6 +135,7 @@ SvxBorderTabPage::SvxBorderTabPage(vcl::Window* pParent, const SfxItemSet& rCore
    get(m_pPropertiesFrame, "properties");
    get(m_pMergeWithNextCB, "mergewithnext");
    get(m_pMergeAdjacentBordersCB, "mergeadjacent");
    get(m_pRemoveAdjcentCellBordersCB, "rmadjcellborders");

    if ( GetDPIScaleFactor() > 1 )
    {
@@ -331,6 +337,21 @@ SvxBorderTabPage::SvxBorderTabPage(vcl::Window* pParent, const SfxItemSet& rCore
    // checkbox "Merge adjacent line styles" only visible for Writer dialog format.table
    AddItemConnection( new sfx::CheckBoxConnection( SID_SW_COLLAPSING_BORDERS, *m_pMergeAdjacentBordersCB, sfx::ITEMCONN_DEFAULT ) );
    m_pMergeAdjacentBordersCB->Hide();

    if( pDocSh )
    {
        Reference< XServiceInfo > xSI( pDocSh->GetModel(), UNO_QUERY );
        if ( xSI.is() )
            bIsCalcDoc = xSI->supportsService("com.sun.star.sheet.SpreadsheetDocument");
    }
    if( bIsCalcDoc )
    {
        m_pRemoveAdjcentCellBordersCB->SetClickHdl(LINK(this, SvxBorderTabPage, RemoveAdjacentCellBorderHdl_Impl));
        m_pRemoveAdjcentCellBordersCB->Show();
        m_pRemoveAdjcentCellBordersCB->Enable( false );
    }
    else
        m_pRemoveAdjcentCellBordersCB->Hide();
}

SvxBorderTabPage::~SvxBorderTabPage()
@@ -365,6 +386,7 @@ void SvxBorderTabPage::dispose()
    m_pPropertiesFrame.clear();
    m_pMergeWithNextCB.clear();
    m_pMergeAdjacentBordersCB.clear();
    m_pRemoveAdjcentCellBordersCB.clear();
    SfxTabPage::dispose();
}

@@ -596,6 +618,10 @@ void SvxBorderTabPage::Reset( const SfxItemSet* rSet )
    else
        mbSync = false;
    m_pSynchronizeCB->Check(mbSync);

    mbRemoveAdjacentCellBorders = false;
    m_pRemoveAdjcentCellBordersCB->Check( false );
    m_pRemoveAdjcentCellBordersCB->Enable( false );
}

void SvxBorderTabPage::ChangesApplied()
@@ -645,6 +671,7 @@ bool SvxBorderTabPage::FillItemSet( SfxItemSet* rCoreAttrs )
        aBoxItem.SetLine( m_pFrameSel->GetFrameBorderStyle( eTypes1[i].first ), eTypes1[i].second );


    aBoxItem.SetRemoveAdjacentCellBorder( mbRemoveAdjacentCellBorders );
    // border hor/ver and TableFlag

    ::std::pair<svx::FrameBorderType,SvxBoxInfoItemLine> eTypes2[] = {
@@ -843,6 +870,7 @@ IMPL_LINK_NOARG_TYPED(SvxBorderTabPage, SelPreHdl_Impl, ValueSet*, void)
    m_pWndPresets->SetNoSelection();

    LinesChanged_Impl( nullptr );
    UpdateRemoveAdjCellBorderCB( nLine + 1 );
}


@@ -1172,6 +1200,7 @@ IMPL_LINK_NOARG_TYPED(SvxBorderTabPage, LinesChanged_Impl, LinkParamNone*, void)
        m_pSynchronizeCB->Enable( m_pRightMF->IsEnabled() || m_pTopMF->IsEnabled() ||
                               m_pBottomMF->IsEnabled() || m_pLeftMF->IsEnabled() );
    }
    UpdateRemoveAdjCellBorderCB( -1 );
}


@@ -1197,6 +1226,59 @@ IMPL_LINK_TYPED( SvxBorderTabPage, SyncHdl_Impl, Button*, pBox, void)
    mbSync = static_cast<CheckBox*>(pBox)->IsChecked();
}

IMPL_LINK_TYPED( SvxBorderTabPage, RemoveAdjacentCellBorderHdl_Impl, Button*, pBox, void)
{
    mbRemoveAdjacentCellBorders = static_cast<CheckBox*>(pBox)->IsChecked();
}

void SvxBorderTabPage::UpdateRemoveAdjCellBorderCB( sal_uInt16 nPreset )
{
    if( !bIsCalcDoc )
        return;
    const SfxItemSet&     rOldSet         = GetItemSet();
    const SvxBoxInfoItem* pOldBoxInfoItem = static_cast<const SvxBoxInfoItem*>(GetOldItem( rOldSet, SID_ATTR_BORDER_INNER ));
    const SvxBoxItem*     pOldBoxItem     = static_cast<const SvxBoxItem*>(GetOldItem( rOldSet, SID_ATTR_BORDER_OUTER ));
    if( !pOldBoxInfoItem || !pOldBoxItem )
        return;
    ::std::pair<svx::FrameBorderType, SvxBoxInfoItemValidFlags> eTypes1[] = {
        { svx::FRAMEBORDER_TOP,SvxBoxInfoItemValidFlags::TOP },
        { svx::FRAMEBORDER_BOTTOM,SvxBoxInfoItemValidFlags::BOTTOM },
        { svx::FRAMEBORDER_LEFT,SvxBoxInfoItemValidFlags::LEFT },
        { svx::FRAMEBORDER_RIGHT,SvxBoxInfoItemValidFlags::RIGHT },
    };
    SvxBoxItemLine eTypes2[] = {
        SvxBoxItemLine::TOP,
        SvxBoxItemLine::BOTTOM,
        SvxBoxItemLine::LEFT,
        SvxBoxItemLine::RIGHT,
    };

    // Check if current selection involves deletion of at least one border
    bool bBorderDeletionReq = false;
    for ( sal_uInt32 i=0; i < SAL_N_ELEMENTS( eTypes1 ); ++i )
    {
        if( pOldBoxItem->GetLine( eTypes2[i] ) || !( pOldBoxInfoItem->IsValid( eTypes1[i].second ) ) )
        {
            if( m_pFrameSel->GetFrameBorderState( eTypes1[i].first ) == svx::FRAMESTATE_HIDE )
            {
                bBorderDeletionReq = true;
                break;
            }
        }
    }

    if( !bBorderDeletionReq && ( nPreset == IID_PRE_CELL_NONE || nPreset == IID_PRE_TABLE_NONE ) )
        bBorderDeletionReq = true;

    m_pRemoveAdjcentCellBordersCB->Enable( bBorderDeletionReq );

    if( !bBorderDeletionReq )
    {
        mbRemoveAdjacentCellBorders = false;
        m_pRemoveAdjcentCellBordersCB->Check( false );
    }
}

void SvxBorderTabPage::DataChanged( const DataChangedEvent& rDCEvt )
{
    if( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
diff --git a/cui/uiconfig/ui/borderpage.ui b/cui/uiconfig/ui/borderpage.ui
index 4e1943e..d25dbed 100644
--- a/cui/uiconfig/ui/borderpage.ui
+++ b/cui/uiconfig/ui/borderpage.ui
@@ -102,6 +102,20 @@
                        <property name="top_attach">1</property>
                      </packing>
                    </child>
                    <child>
                      <object class="GtkCheckButton" id="rmadjcellborders">
                        <property name="label" translatable="yes">Remove border from adjacent cells as well</property>
                        <property name="visible">True</property>
                        <property name="can_focus">True</property>
                        <property name="receives_default">False</property>
                        <property name="xalign">0</property>
                        <property name="draw_indicator">True</property>
                      </object>
                      <packing>
                        <property name="left_attach">0</property>
                        <property name="top_attach">4</property>
                      </packing>
                    </child>
                  </object>
                </child>
              </object>
diff --git a/editeng/source/items/frmitems.cxx b/editeng/source/items/frmitems.cxx
index 10211e4..69c18f3 100644
--- a/editeng/source/items/frmitems.cxx
+++ b/editeng/source/items/frmitems.cxx
@@ -1611,7 +1611,8 @@ SvxBoxItem::SvxBoxItem( const SvxBoxItem& rCpy ) :
    nTopDist    ( rCpy.nTopDist ),
    nBottomDist ( rCpy.nBottomDist ),
    nLeftDist   ( rCpy.nLeftDist ),
    nRightDist  ( rCpy.nRightDist )
    nRightDist  ( rCpy.nRightDist ),
    bRemoveAdjCellBorder ( rCpy.bRemoveAdjCellBorder )

{
    pTop    = rCpy.GetTop()     ? new SvxBorderLine( *rCpy.GetTop() )    : nullptr;
@@ -1632,8 +1633,8 @@ SvxBoxItem::SvxBoxItem( const sal_uInt16 nId ) :
    nTopDist    ( 0 ),
    nBottomDist ( 0 ),
    nLeftDist   ( 0 ),
    nRightDist  ( 0 )

    nRightDist  ( 0 ),
    bRemoveAdjCellBorder ( false )
{
}

@@ -1655,6 +1656,7 @@ SvxBoxItem& SvxBoxItem::operator=( const SvxBoxItem& rBox )
    nBottomDist = rBox.nBottomDist;
    nLeftDist = rBox.nLeftDist;
    nRightDist = rBox.nRightDist;
    bRemoveAdjCellBorder = rBox.bRemoveAdjCellBorder;
    SetLine( rBox.GetTop(), SvxBoxItemLine::TOP );
    SetLine( rBox.GetBottom(), SvxBoxItemLine::BOTTOM );
    SetLine( rBox.GetLeft(), SvxBoxItemLine::LEFT );
@@ -1685,6 +1687,7 @@ bool SvxBoxItem::operator==( const SfxPoolItem& rAttr ) const
        ( nBottomDist == rBoxItem.nBottomDist )   &&
        ( nLeftDist == rBoxItem.nLeftDist )   &&
        ( nRightDist == rBoxItem.nRightDist ) &&
        ( bRemoveAdjCellBorder == rBoxItem.bRemoveAdjCellBorder ) &&
        CmpBrdLn( pTop, rBoxItem.GetTop() )           &&
        CmpBrdLn( pBottom, rBoxItem.GetBottom() )     &&
        CmpBrdLn( pLeft, rBoxItem.GetLeft() )         &&
diff --git a/include/editeng/boxitem.hxx b/include/editeng/boxitem.hxx
index 7b1efd8..563a1b5 100644
--- a/include/editeng/boxitem.hxx
+++ b/include/editeng/boxitem.hxx
@@ -60,6 +60,7 @@ class EDITENG_DLLPUBLIC SvxBoxItem : public SfxPoolItem
                    nBottomDist,
                    nLeftDist,
                    nRightDist;
    bool            bRemoveAdjCellBorder;

public:
    static SfxPoolItem* CreateDefault();
@@ -100,9 +101,13 @@ public:
    sal_uInt16  GetDistance( SvxBoxItemLine nLine ) const;
    sal_uInt16  GetDistance() const;

    bool IsRemoveAdjacentCellBorder() const { return bRemoveAdjCellBorder; }

    void    SetDistance( sal_uInt16 nNew, SvxBoxItemLine nLine );
    inline void SetDistance( sal_uInt16 nNew );

    void SetRemoveAdjacentCellBorder( bool bSet = true ) { bRemoveAdjCellBorder = bSet; }

    // Line width plus Space plus inward distance
    // bIgnoreLine = TRUE -> Also return distance, when no Line is set
    sal_uInt16  CalcLineSpace( SvxBoxItemLine nLine, bool bIgnoreLine = false ) const;
diff --git a/sc/inc/markdata.hxx b/sc/inc/markdata.hxx
index 20777a6..6e3eafc 100644
--- a/sc/inc/markdata.hxx
+++ b/sc/inc/markdata.hxx
@@ -21,6 +21,7 @@
#define INCLUDED_SC_INC_MARKDATA_HXX

#include "address.hxx"
#include "rangelst.hxx"
#include "scdllapi.h"

#include <set>
@@ -55,6 +56,11 @@ private:

    bool            bMarking:1;               // area is being marked -> no MarkToMulti
    bool            bMarkIsNeg:1;             // cancel if multi selection
    ScRangeList     aTopEnvelope;             // list of ranges in the top envelope of the multi selection
    ScRangeList     aBottomEnvelope;          // list of ranges in the bottom envelope of the multi selection
    ScRangeList     aLeftEnvelope;            // list of ranges in the left envelope of the multi selection
    ScRangeList     aRightEnvelope;           // list of ranges in the right envelope of the multi selection


public:
                ScMarkData();
@@ -122,6 +128,15 @@ public:
    void        InsertTab( SCTAB nTab );
    void        DeleteTab( SCTAB nTab );

    // Generate envelopes if mutimarked and fills the passed ScRange object with
    // the smallest range that includes the marked area plus its envelopes.
    void        GetSelectionCover( ScRange& rRange );
    // Get top, bottom, left and right envelopes
    const ScRangeList& GetTopEnvelope() const    { return aTopEnvelope;    }
    const ScRangeList& GetBottomEnvelope() const { return aBottomEnvelope; }
    const ScRangeList& GetLeftEnvelope() const   { return aLeftEnvelope;   }
    const ScRangeList& GetRightEnvelope() const  { return aRightEnvelope;  }

    // iterators for table access
    typedef std::set<SCTAB>::iterator iterator;
    typedef std::set<SCTAB>::const_iterator const_iterator;
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index 5225443..129695a 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -5591,6 +5591,86 @@ void ScDocument::ApplySelectionFrame( const ScMarkData& rMark,
            }
        }
    }
    if( pLineOuter && pLineOuter->IsRemoveAdjacentCellBorder() )
    {
        SvxBoxItem aTmp0( *pLineOuter );
        aTmp0.SetLine( NULL, SvxBoxItemLine::TOP );
        aTmp0.SetLine( NULL, SvxBoxItemLine::BOTTOM );
        aTmp0.SetLine( NULL, SvxBoxItemLine::LEFT );
        aTmp0.SetLine( NULL, SvxBoxItemLine::RIGHT );
        SvxBoxItem aLeft( aTmp0 );
        SvxBoxItem aRight( aTmp0 );
        SvxBoxItem aTop( aTmp0 );
        SvxBoxItem aBottom( aTmp0 );

        SvxBoxInfoItem aTmp1( *pLineInner );
        aTmp1.SetTable( false );
        aTmp1.SetLine( NULL, SvxBoxInfoItemLine::HORI );
        aTmp1.SetLine( NULL, SvxBoxInfoItemLine::VERT );
        aTmp1.SetValid( SvxBoxInfoItemValidFlags::ALL, false );
        aTmp1.SetValid( SvxBoxInfoItemValidFlags::DISTANCE, true );
        SvxBoxInfoItem aLeftInfo( aTmp1 );
        SvxBoxInfoItem aRightInfo( aTmp1 );
        SvxBoxInfoItem aTopInfo( aTmp1 );
        SvxBoxInfoItem aBottomInfo( aTmp1 );

        if( pLineInner->IsValid( SvxBoxInfoItemValidFlags::TOP ) && !pLineOuter->GetTop() )
            aTopInfo.SetValid( SvxBoxInfoItemValidFlags::BOTTOM, true );

        if( pLineInner->IsValid( SvxBoxInfoItemValidFlags::BOTTOM ) && !pLineOuter->GetBottom() )
            aBottomInfo.SetValid( SvxBoxInfoItemValidFlags::TOP, true );

        if( pLineInner->IsValid( SvxBoxInfoItemValidFlags::LEFT ) && !pLineOuter->GetLeft() )
            aLeftInfo.SetValid( SvxBoxInfoItemValidFlags::RIGHT, true );

        if( pLineInner->IsValid( SvxBoxInfoItemValidFlags::RIGHT ) &&  !pLineOuter->GetRight() )
            aRightInfo.SetValid( SvxBoxInfoItemValidFlags::LEFT, true );

        const ScRangeList& rRangeListTopEnvelope = rMark.GetTopEnvelope();
        const ScRangeList& rRangeListBottomEnvelope = rMark.GetBottomEnvelope();
        const ScRangeList& rRangeListLeftEnvelope = rMark.GetLeftEnvelope();
        const ScRangeList& rRangeListRightEnvelope = rMark.GetRightEnvelope();

        ScMarkData::const_iterator itr1 = rMark.begin(), itrEnd1 = rMark.end();
        for ( ; itr1 != itrEnd1 && *itr1 < nMax; ++itr1 )
        {
            if ( maTabs[*itr1] )
            {
                size_t nEnvelopeRangeCount = rRangeListTopEnvelope.size();
                for ( size_t j=0; j < nEnvelopeRangeCount; j++ )
                {
                    const ScRange* pRange = rRangeListTopEnvelope[ j ];
                    maTabs[*itr1]->ApplyBlockFrame( &aTop, &aTopInfo,
                                                    pRange->aStart.Col(), pRange->aStart.Row(),
                                                    pRange->aEnd.Col(),   pRange->aEnd.Row() );
                }
                nEnvelopeRangeCount = rRangeListBottomEnvelope.size();
                for ( size_t j=0; j < nEnvelopeRangeCount; j++ )
                {
                    const ScRange* pRange = rRangeListBottomEnvelope[ j ];
                    maTabs[*itr1]->ApplyBlockFrame( &aBottom, &aBottomInfo,
                                                    pRange->aStart.Col(), pRange->aStart.Row(),
                                                    pRange->aEnd.Col(),   pRange->aEnd.Row() );
                }
                nEnvelopeRangeCount = rRangeListLeftEnvelope.size();
                for ( size_t j=0; j < nEnvelopeRangeCount; j++ )
                {
                    const ScRange* pRange = rRangeListLeftEnvelope[ j ];
                    maTabs[*itr1]->ApplyBlockFrame( &aLeft, &aLeftInfo,
                                                    pRange->aStart.Col(), pRange->aStart.Row(),
                                                    pRange->aEnd.Col(),   pRange->aEnd.Row() );
                }
                nEnvelopeRangeCount = rRangeListRightEnvelope.size();
                for ( size_t j=0; j < nEnvelopeRangeCount; j++ )
                {
                    const ScRange* pRange = rRangeListRightEnvelope[ j ];
                    maTabs[*itr1]->ApplyBlockFrame( &aRight, &aRightInfo,
                                                    pRange->aStart.Col(), pRange->aStart.Row(),
                                                    pRange->aEnd.Col(),   pRange->aEnd.Row() );
                }
            }
        }
    }
}

void ScDocument::ApplyFrameAreaTab( const ScRange& rRange,
diff --git a/sc/source/core/data/markdata.cxx b/sc/source/core/data/markdata.cxx
index 137a2e4..b8eaac7 100644
--- a/sc/source/core/data/markdata.cxx
+++ b/sc/source/core/data/markdata.cxx
@@ -20,8 +20,10 @@
#include "markdata.hxx"
#include "markarr.hxx"
#include "rangelst.hxx"
#include "segmenttree.hxx"
#include <columnspanset.hxx>
#include <fstalgorithm.hxx>
#include <unordered_map>

#include <osl/diagnose.h>

@@ -40,7 +42,11 @@ ScMarkData::ScMarkData(const ScMarkData& rData) :
    maTabMarked( rData.maTabMarked ),
    aMarkRange( rData.aMarkRange ),
    aMultiRange( rData.aMultiRange ),
    pMultiSel( nullptr )
    pMultiSel( nullptr ),
    aTopEnvelope( rData.aTopEnvelope ),
    aBottomEnvelope( rData.aBottomEnvelope ),
    aLeftEnvelope( rData.aLeftEnvelope ),
    aRightEnvelope( rData.aRightEnvelope )
{
    bMarked      = rData.bMarked;
    bMultiMarked = rData.bMultiMarked;
@@ -69,6 +75,10 @@ ScMarkData& ScMarkData::operator=(const ScMarkData& rData)
    bMultiMarked = rData.bMultiMarked;
    bMarking     = rData.bMarking;
    bMarkIsNeg   = rData.bMarkIsNeg;
    aTopEnvelope = rData.aTopEnvelope;
    aBottomEnvelope = rData.aBottomEnvelope;
    aLeftEnvelope   = rData.aLeftEnvelope;
    aRightEnvelope  = rData.aRightEnvelope;

    maTabMarked = rData.maTabMarked;

@@ -94,6 +104,10 @@ void ScMarkData::ResetMark()

    bMarked = bMultiMarked = false;
    bMarking = bMarkIsNeg = false;
    aTopEnvelope.RemoveAll();
    aBottomEnvelope.RemoveAll();
    aLeftEnvelope.RemoveAll();
    aRightEnvelope.RemoveAll();
}

void ScMarkData::SetMarkArea( const ScRange& rRange )
@@ -553,6 +567,257 @@ void ScMarkData::DeleteTab( SCTAB nTab )
    maTabMarked.swap(tabMarked);
}

static void lcl_AddRanges(ScRange& rRangeDest, const ScRange& rNewRange )
{
    SCCOL nStartCol = rNewRange.aStart.Col();
    SCROW nStartRow = rNewRange.aStart.Row();
    SCCOL nEndCol = rNewRange.aEnd.Col();
    SCROW nEndRow = rNewRange.aEnd.Row();
    PutInOrder( nStartRow, nEndRow );
    PutInOrder( nStartCol, nEndCol );
    if ( nStartCol < rRangeDest.aStart.Col() )
        rRangeDest.aStart.SetCol( nStartCol );
    if ( nStartRow < rRangeDest.aStart.Row() )
        rRangeDest.aStart.SetRow( nStartRow );
    if ( nEndCol > rRangeDest.aEnd.Col() )
        rRangeDest.aEnd.SetCol( nEndCol );
    if ( nEndRow > rRangeDest.aEnd.Row() )
        rRangeDest.aEnd.SetRow( nEndRow );
}

void ScMarkData::GetSelectionCover( ScRange& rRange )
{
    if( bMultiMarked )
    {
        rRange = aMultiRange;
        SCCOL nStartCol = aMultiRange.aStart.Col(), nEndCol = aMultiRange.aEnd.Col();
        PutInOrder( nStartCol, nEndCol );
        nStartCol = ( nStartCol == 0 ) ? nStartCol : nStartCol - 1;
        nEndCol = ( nEndCol == MAXCOL ) ? nEndCol : nEndCol + 1;
        std::unique_ptr<ScFlatBoolRowSegments> pPrevColMarkedRows;
        std::unique_ptr<ScFlatBoolRowSegments> pCurColMarkedRows;
        std::unordered_map<SCROW,ScFlatBoolColSegments> aRowToColSegmentsInTopEnvelope;
        std::unordered_map<SCROW,ScFlatBoolColSegments> aRowToColSegmentsInBottomEnvelope;
        ScFlatBoolRowSegments aNoRowsMarked;
        aNoRowsMarked.setFalse( 0, MAXROW );

        const ScMarkArray* pArray = pMultiSel;
        bool bPrevColUnMarked = false;

        for ( SCCOL nCol=nStartCol; nCol <= nEndCol; nCol++ )
        {
            SCROW nTop, nBottom;
            bool bCurColUnMarked = !pArray[nCol].HasMarks();
            if ( !bCurColUnMarked )
            {
                pCurColMarkedRows.reset( new ScFlatBoolRowSegments() );
                pCurColMarkedRows->setFalse( 0, MAXROW );
                ScMarkArrayIter aMarkIter( pArray + nCol );
                ScFlatBoolRowSegments::ForwardIterator aPrevItr ( pPrevColMarkedRows.get() ? *pPrevColMarkedRows : aNoRowsMarked ); // For finding left envelope
                ScFlatBoolRowSegments::ForwardIterator aPrevItr1( pPrevColMarkedRows.get() ? *pPrevColMarkedRows : aNoRowsMarked ); // For finding right envelope
                SCROW nTopPrev = 0, nBottomPrev = 0; // For right envelope
                while ( aMarkIter.Next( nTop, nBottom ) )
                {
                    pCurColMarkedRows->setTrue( nTop, nBottom );
                    if( bPrevColUnMarked && ( nCol > nStartCol ))
                    {
                        ScRange aAddRange(nCol - 1, nTop, aMultiRange.aStart.Tab(),
                                          nCol - 1, nBottom, aMultiRange.aStart.Tab());
                        lcl_AddRanges( rRange, aAddRange ); // Left envelope
                        aLeftEnvelope.Append( aAddRange );
                    }
                    else if( nCol > nStartCol )
                    {
                        SCROW nTop1 = nTop, nBottom1 = nTop;
                        while( nTop1 <= nBottom && nBottom1 <= nBottom )
                        {
                            bool bRangeMarked = false;
                            aPrevItr.getValue( nTop1, bRangeMarked );
                            if( bRangeMarked )
                            {
                                nTop1 = aPrevItr.getLastPos() + 1;
                                nBottom1 = nTop1;
                            }
                            else
                            {
                                nBottom1 = aPrevItr.getLastPos();
                                if( nBottom1 > nBottom )
                                    nBottom1 = nBottom;
                                ScRange aAddRange( nCol - 1, nTop1, aMultiRange.aStart.Tab(),
                                                   nCol - 1, nBottom1, aMultiRange.aStart.Tab() );
                                lcl_AddRanges( rRange, aAddRange ); // Left envelope
                                aLeftEnvelope.Append( aAddRange );
                                nTop1 = ++nBottom1;
                            }
                        }
                        while( nTopPrev <= nBottom && nBottomPrev <= nBottom )
                        {
                            bool bRangeMarked;
                            aPrevItr1.getValue( nTopPrev, bRangeMarked );
                            if( bRangeMarked )
                            {
                                nBottomPrev = aPrevItr1.getLastPos();
                                if( nTopPrev < nTop )
                                {
                                    if( nBottomPrev >= nTop )
                                    {
                                        nBottomPrev = nTop - 1;
                                        ScRange aAddRange( nCol, nTopPrev, aMultiRange.aStart.Tab(),
                                                           nCol, nBottomPrev, aMultiRange.aStart.Tab());
                                        lcl_AddRanges( rRange, aAddRange ); // Right envelope
                                        aRightEnvelope.Append( aAddRange );
                                        nTopPrev = nBottomPrev = (nBottom + 1);
                                    }
                                    else
                                    {
                                        ScRange aAddRange( nCol, nTopPrev, aMultiRange.aStart.Tab(),
                                                           nCol, nBottomPrev, aMultiRange.aStart.Tab());
                                        lcl_AddRanges( rRange, aAddRange ); // Right envelope
                                        aRightEnvelope.Append( aAddRange );
                                        nTopPrev = ++nBottomPrev;
                                    }
                                }
                                else
                                    nTopPrev = nBottomPrev = ( nBottom + 1 );
                            }
                            else
                            {
                                nBottomPrev = aPrevItr1.getLastPos();
                                nTopPrev = ++nBottomPrev;
                            }
                        }
                    }
                    if( nTop )
                    {
                        ScRange aAddRange( nCol, nTop - 1, aMultiRange.aStart.Tab(),
                                           nCol, nTop - 1, aMultiRange.aStart.Tab());
                        lcl_AddRanges( rRange, aAddRange ); // Top envelope
                        aRowToColSegmentsInTopEnvelope[nTop - 1].setTrue( nCol, nCol );
                    }
                    if( nBottom < MAXROW )
                    {
                        ScRange aAddRange(nCol, nBottom + 1, aMultiRange.aStart.Tab(),
                                          nCol, nBottom + 1, aMultiRange.aStart.Tab());
                        lcl_AddRanges( rRange, aAddRange ); // Bottom envelope
                        aRowToColSegmentsInBottomEnvelope[nBottom + 1].setTrue( nCol, nCol );
                    }
                }

                while( nTopPrev <= MAXROW && nBottomPrev <= MAXROW && ( nCol > nStartCol ) )
                {
                    bool bRangeMarked;
                    aPrevItr1.getValue( nTopPrev, bRangeMarked );
                    if( bRangeMarked )
                    {
                        nBottomPrev = aPrevItr1.getLastPos();
                        ScRange aAddRange(nCol, nTopPrev, aMultiRange.aStart.Tab(),
                                          nCol, nBottomPrev, aMultiRange.aStart.Tab());
                        lcl_AddRanges( rRange, aAddRange ); // Right envelope
                        aRightEnvelope.Append( aAddRange );
                        nTopPrev = ++nBottomPrev;
                    }
                    else
                    {
                        nBottomPrev = aPrevItr1.getLastPos();
                        nTopPrev = ++nBottomPrev;
                    }
                }
            }
            else if( nCol > nStartCol )
            {
                bPrevColUnMarked = true;
                SCROW nTopPrev = 0, nBottomPrev = 0;
                bool bRangeMarked = false;
                ScFlatBoolRowSegments::ForwardIterator aPrevItr( pPrevColMarkedRows.get() ? *pPrevColMarkedRows : aNoRowsMarked );
                while( nTopPrev <= MAXROW && nBottomPrev <= MAXROW )
                {
                    aPrevItr.getValue(nTopPrev, bRangeMarked);
                    if( bRangeMarked )
                    {
                        nBottomPrev = aPrevItr.getLastPos();
                        ScRange aAddRange(nCol, nTopPrev, aMultiRange.aStart.Tab(),
                                          nCol, nBottomPrev, aMultiRange.aStart.Tab());
                        lcl_AddRanges( rRange, aAddRange ); // Right envelope
                        aRightEnvelope.Append( aAddRange );
                        nTopPrev = ++nBottomPrev;
                    }
                    else
                    {
                        nBottomPrev = aPrevItr.getLastPos();
                        nTopPrev = ++nBottomPrev;
                    }
                }
            }
            if ( bCurColUnMarked )
                pPrevColMarkedRows.reset( nullptr );
            else
                pPrevColMarkedRows.reset( pCurColMarkedRows.release() );
        }
        for( auto& rKV : aRowToColSegmentsInTopEnvelope )
        {
            SCCOL nStart = nStartCol;
            ScFlatBoolColSegments::RangeData aRange;
            while( nStart <= nEndCol )
            {
                if( !rKV.second.getRangeData( nStart, aRange ) )
                    break;
                if( aRange.mbValue ) // is marked
                    aTopEnvelope.Append( ScRange( aRange.mnCol1, rKV.first, aMultiRange.aStart.Tab(),
                                                  aRange.mnCol2, rKV.first, aMultiRange.aStart.Tab() ) );
                nStart = aRange.mnCol2 + 1;
            }
        }
        for( auto& rKV : aRowToColSegmentsInBottomEnvelope )
        {
            SCCOL nStart = nStartCol;
            ScFlatBoolColSegments::RangeData aRange;
            while( nStart <= nEndCol )
            {
                if( !rKV.second.getRangeData( nStart, aRange ) )
                    break;
                if( aRange.mbValue ) // is marked
                    aBottomEnvelope.Append( ScRange( aRange.mnCol1, rKV.first, aMultiRange.aStart.Tab(),
                                                     aRange.mnCol2, rKV.first, aMultiRange.aStart.Tab() ) );
                nStart = aRange.mnCol2 + 1;
            }
        }
    }
    else if( bMarked )
    {
        aMarkRange.PutInOrder();
        SCROW nRow1, nRow2, nRow1New, nRow2New;
        SCCOL nCol1, nCol2, nCol1New, nCol2New;
        SCTAB nTab1, nTab2;
        aMarkRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
        nCol1New = nCol1;
        nCol2New = nCol2;
        nRow1New = nRow1;
        nRow2New = nRow2;
        // Each envelope will have zero or more ranges for single rectangle selection.
        if( nCol1 > 0 )
        {
            aLeftEnvelope.Append( ScRange( nCol1 - 1, nRow1, nTab1, nCol1 - 1, nRow2, nTab2 ) );
            --nCol1New;
        }
        if( nRow1 > 0 )
        {
            aTopEnvelope.Append( ScRange( nCol1, nRow1 - 1, nTab1, nCol2, nRow1 - 1, nTab2 ) );
            --nRow1New;
        }
        if( nCol2 < MAXCOL )
        {
            aRightEnvelope.Append( ScRange( nCol2 + 1, nRow1, nTab1, nCol2 + 1, nRow2, nTab2 ) );
            ++nCol2New;
        }
        if( nRow2 < MAXROW )
        {
            aBottomEnvelope.Append( ScRange( nCol1, nRow2 + 1, nTab1, nCol2, nRow2 + 1, nTab2 ) );
            ++nRow2New;
        }
        rRange = ScRange( nCol1New, nRow1New, nTab1, nCol2New, nRow2New, nTab2 );
    }
}

//iterators
ScMarkData::iterator ScMarkData::begin()
{
diff --git a/sc/source/ui/inc/undoblk.hxx b/sc/source/ui/inc/undoblk.hxx
index 2bafcdd..3187163 100644
--- a/sc/source/ui/inc/undoblk.hxx
+++ b/sc/source/ui/inc/undoblk.hxx
@@ -322,7 +322,8 @@ public:
                                         ScDocument* pNewUndoDoc, bool bNewMulti,
                                         const ScPatternAttr* pNewApply,
                                         const SvxBoxItem* pNewOuter = nullptr,
                                         const SvxBoxInfoItem* pNewInner = nullptr );
                                         const SvxBoxInfoItem* pNewInner = nullptr,
                                         const ScRange* pRangeCover = nullptr );
    virtual         ~ScUndoSelectionAttr();

    virtual void    Undo() override;
@@ -336,6 +337,7 @@ public:
private:
    ScMarkData      aMarkData;
    ScRange         aRange;
    ScRange         aRangeCover;
    std::unique_ptr<ScEditDataArray> mpDataArray;
    ScDocument*     pUndoDoc;
    bool            bMulti;
diff --git a/sc/source/ui/undo/undoblk3.cxx b/sc/source/ui/undo/undoblk3.cxx
index f7e0e2846..aa04576 100644
--- a/sc/source/ui/undo/undoblk3.cxx
+++ b/sc/source/ui/undo/undoblk3.cxx
@@ -349,7 +349,8 @@ ScUndoSelectionAttr::ScUndoSelectionAttr( ScDocShell* pNewDocShell,
                SCCOL nEndX, SCROW nEndY, SCTAB nEndZ,
                ScDocument* pNewUndoDoc, bool bNewMulti,
                const ScPatternAttr* pNewApply,
                const SvxBoxItem* pNewOuter, const SvxBoxInfoItem* pNewInner )
                const SvxBoxItem* pNewOuter, const SvxBoxInfoItem* pNewInner,
                const ScRange* pRangeCover )
    :   ScSimpleUndo( pNewDocShell ),
        aMarkData   ( rMark ),
        aRange      ( nStartX, nStartY, nStartZ, nEndX, nEndY, nEndZ ),
@@ -361,6 +362,7 @@ ScUndoSelectionAttr::ScUndoSelectionAttr( ScDocShell* pNewDocShell,
    pApplyPattern = const_cast<ScPatternAttr*>(static_cast<const ScPatternAttr*>( &pPool->Put( *pNewApply ) ));
    pLineOuter = pNewOuter ? const_cast<SvxBoxItem*>(static_cast<const SvxBoxItem*>( &pPool->Put( *pNewOuter ) )) : nullptr;
    pLineInner = pNewInner ? const_cast<SvxBoxInfoItem*>(static_cast<const SvxBoxInfoItem*>( &pPool->Put( *pNewInner ) )) : nullptr;
    aRangeCover = pRangeCover ? *pRangeCover : aRange;
}

ScUndoSelectionAttr::~ScUndoSelectionAttr()
@@ -392,7 +394,7 @@ void ScUndoSelectionAttr::DoChange( const bool bUndo )

    SetViewMarkData( aMarkData );

    ScRange aEffRange( aRange );
    ScRange aEffRange( aRangeCover );
    if ( rDoc.HasAttrib( aEffRange, HASATTR_MERGED ) )         // merged cells?
        rDoc.ExtendMerge( aEffRange );

@@ -403,7 +405,7 @@ void ScUndoSelectionAttr::DoChange( const bool bUndo )

    if (bUndo)  // only for Undo
    {
        ScRange aCopyRange = aRange;
        ScRange aCopyRange = aRangeCover;
        SCTAB nTabCount = rDoc.GetTableCount();
        aCopyRange.aStart.SetTab(0);
        aCopyRange.aEnd.SetTab(nTabCount-1);
diff --git a/sc/source/ui/view/viewfunc.cxx b/sc/source/ui/view/viewfunc.cxx
index ebcee5d..cb140c8 100644
--- a/sc/source/ui/view/viewfunc.cxx
+++ b/sc/source/ui/view/viewfunc.cxx
@@ -1019,7 +1019,10 @@ void ScViewFunc::ApplyPatternLines( const ScPatternAttr& rAttr, const SvxBoxItem
    if (bRecord && !pDoc->IsUndoEnabled())
        bRecord = false;

    ScRange aMarkRange;
    bool bRemoveAdjCellBorder = false;
    if( pNewOuter )
        bRemoveAdjCellBorder = pNewOuter->IsRemoveAdjacentCellBorder();
    ScRange aMarkRange, aMarkRangeWithEnvelope;
    aFuncMark.MarkToSimple();
    bool bMulti = aFuncMark.IsMultiMarked();
    if (bMulti)
@@ -1035,6 +1038,10 @@ void ScViewFunc::ApplyPatternLines( const ScPatternAttr& rAttr, const SvxBoxItem
        aFuncMark.SetMarkArea(aMarkRange);
        MarkDataChanged();
    }
    if( bRemoveAdjCellBorder )
        aFuncMark.GetSelectionCover( aMarkRangeWithEnvelope );
    else
        aMarkRangeWithEnvelope = aMarkRange;

    ScDocShell* pDocSh = GetViewData().GetDocShell();

@@ -1045,31 +1052,34 @@ void ScViewFunc::ApplyPatternLines( const ScPatternAttr& rAttr, const SvxBoxItem
        ScDocument* pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
        SCTAB nStartTab = aMarkRange.aStart.Tab();
        SCTAB nTabCount = pDoc->GetTableCount();
        bool bCopyOnlyMarked = false;
        if( !bRemoveAdjCellBorder )
            bCopyOnlyMarked = bMulti;
        pUndoDoc->InitUndo( pDoc, nStartTab, nStartTab );
        ScMarkData::iterator itr = aFuncMark.begin(), itrEnd = aFuncMark.end();
        for (; itr != itrEnd; ++itr)
            if (*itr != nStartTab)
                pUndoDoc->AddUndoTab( *itr, *itr );

        ScRange aCopyRange = aMarkRange;
        ScRange aCopyRange = aMarkRangeWithEnvelope;
        aCopyRange.aStart.SetTab(0);
        aCopyRange.aEnd.SetTab(nTabCount-1);
        pDoc->CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, bMulti, pUndoDoc, &aFuncMark );
        pDoc->CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, bCopyOnlyMarked, pUndoDoc, &aFuncMark );

        pDocSh->GetUndoManager()->AddUndoAction(
            new ScUndoSelectionAttr(
            pDocSh, aFuncMark,
            aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), aMarkRange.aStart.Tab(),
            aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(), aMarkRange.aEnd.Tab(),
            pUndoDoc, bMulti, &rAttr, pNewOuter, pNewInner ) );
            pUndoDoc, bCopyOnlyMarked, &rAttr, pNewOuter, pNewInner, &aMarkRangeWithEnvelope ) );
    }

    sal_uInt16 nExt = SC_PF_TESTMERGE;
    pDocSh->UpdatePaintExt( nExt, aMarkRange ); // content before the change
    pDocSh->UpdatePaintExt( nExt, aMarkRangeWithEnvelope ); // content before the change

    pDoc->ApplySelectionFrame( aFuncMark, pNewOuter, pNewInner );

    pDocSh->UpdatePaintExt( nExt, aMarkRange ); // content after the change
    pDocSh->UpdatePaintExt( nExt, aMarkRangeWithEnvelope ); // content after the change

    aFuncMark.MarkToMulti();
    pDoc->ApplySelectionPattern( rAttr, aFuncMark );