Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  ndtbl1.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <hintids.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/brushitem.hxx>
#include <editeng/frmdiritem.hxx>
#include <fesh.hxx>
#include <fmtornt.hxx>
#include <fmtfsize.hxx>
#include <fmtrowsplt.hxx>
#include <tabcol.hxx>
#include <frmatr.hxx>
#include <cellfrm.hxx>
#include <tabfrm.hxx>
#include <cntfrm.hxx>
#include <txtfrm.hxx>
#include <svx/svxids.hrc>
#include <doc.hxx>
#include <IDocumentUndoRedo.hxx>
#include <IDocumentState.hxx>
#include <IDocumentContentOperations.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <pam.hxx>
#include <swcrsr.hxx>
#include <viscrs.hxx>
#include <swtable.hxx>
#include <htmltbl.hxx>
#include <tblsel.hxx>
#include <swtblfmt.hxx>
#include <ndindex.hxx>
#include <undobj.hxx>
#include <calbck.hxx>
#include <UndoTable.hxx>
#include <o3tl/enumrange.hxx>
#include <o3tl/safeint.hxx>
#include <osl/diagnose.h>
#include <redline.hxx>

using ::editeng::SvxBorderLine;
using namespace ::com::sun::star;

// See swtable.cxx too
#define COLFUZZY 20L

static bool IsSame( tools::Long nA, tools::Long nB ) { return  std::abs(nA-nB) <= COLFUZZY; }

namespace {

// SwTableLine::ChgFrameFormat may delete old format which doesn't have writer listeners anymore.
// This may invalidate my pointers, and lead to use-after-free. For this reason, I register myself
// as a writer listener for the old format here, and take care to delete formats without listeners
// in my own dtor.
class SwTableFormatCmp : public SwClient
{
public:
    SwTableFormatCmp( SwFrameFormat *pOld, SwFrameFormat *pNew, sal_Int16 nType );
    ~SwTableFormatCmp() override;

    static SwFrameFormat* FindNewFormat(std::vector<std::unique_ptr<SwTableFormatCmp>>& ;rArr,
                                        SwFrameFormat const* pOld, sal_Int16 nType);

private:
    SwFrameFormat *m_pOld, *m_pNew;
    sal_Int16 m_nType;
};

}

SwTableFormatCmp::SwTableFormatCmp(SwFrameFormat* pO, SwFrameFormat* pN, sal_Int16 nT)
    : m_pOld(pO)
    , m_pNew(pN)
    , m_nType(nT)
{
    if (m_pOld)
        m_pOld->Add(*this);
}

SwTableFormatCmp::~SwTableFormatCmp()
{
    if (m_pOld)
    {
        m_pOld->Remove(*this);
        if (!m_pOld->HasWriterListeners())
            delete m_pOld;
    }
}

// static
SwFrameFormat* SwTableFormatCmp::FindNewFormat(std::vector<std::unique_ptr<SwTableFormatCmp>>& rArr,
                                               SwFrameFormat const* pOld, sal_Int16 nType)
{
    for (const auto& pCmp : rArr)
    {
        if (pCmp->m_pOld == pOld && pCmp->m_nType == nType)
            return pCmp->m_pNew;
    }
    return nullptr;
}

static void lcl_GetStartEndCell( const SwCursor& rCursor,
                        SwLayoutFrame *&prStart, SwLayoutFrame *&prEnd )
{
    OSL_ENSURE( rCursor.GetPointContentNode() && rCursor.GetMarkContentNode(),
            "Tab selection not at ContentNode" );

    Point aPtPos, aMkPos;
    const SwShellCursor* pShCursor = dynamic_cast<const SwShellCursor*>(&rCursor);
    if( pShCursor )
    {
        aPtPos = pShCursor->GetPtPos();
        aMkPos = pShCursor->GetMkPos();
    }

    // Robust:
    SwContentNode* pPointNd = rCursor.GetPointContentNode();
    SwContentNode* pMarkNd  = rCursor.GetMarkContentNode();

    std::pair<Point, bool> tmp(aPtPos, true);
    SwFrame *const pPointFrame = pPointNd ? pPointNd->getLayoutFrame(pPointNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp) : nullptr;
    tmp.first = aMkPos;
    SwFrame *const pMarkFrame = pMarkNd ? pMarkNd->getLayoutFrame(pMarkNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp) : nullptr;

    prStart = pPointFrame ? pPointFrame->GetUpper() : nullptr;
    prEnd   = pMarkFrame  ? pMarkFrame->GetUpper() : nullptr;
}

static bool lcl_GetBoxSel( const SwCursor& rCursor, SwSelBoxes& rBoxes,
                    bool bAllCursor = false )
{
    const SwTableCursor* pTableCursor =
        dynamic_cast<const SwTableCursor*>(&rCursor);
    if( pTableCursor )
        ::GetTableSelCrs( *pTableCursor, rBoxes );
    else
    {
        const SwPaM *pCurPam = &rCursor, *pSttPam = pCurPam;
        do {
            const SwNode* pNd = pCurPam->GetPointNode().FindTableBoxStartNode();
            if( pNd )
            {
                SwTableBox* pBox = const_cast<SwTableBox*>(pNd->FindTableNode()->GetTable().
                                            GetTableBox( pNd->GetIndex() ));
                rBoxes.insert( pBox );
            }
        } while( bAllCursor &&
                pSttPam != ( pCurPam = pCurPam->GetNext()) );
    }
    return !rBoxes.empty();
}

static void InsertLine( std::vector<SwTableLine*>& rLineArr, SwTableLine* pLine )
{
    if( rLineArr.end() == std::find( rLineArr.begin(), rLineArr.end(), pLine ) )
        rLineArr.push_back( pLine );
}

static bool lcl_IsAnLower( const SwTableLine *pLine, const SwTableLine *pAssumed )
{
    const SwTableLine *pTmp = pAssumed->GetUpper() ?
                                    pAssumed->GetUpper()->GetUpper() : nullptr;
    while ( pTmp )
    {
        if ( pTmp == pLine )
            return true;
        pTmp = pTmp->GetUpper() ? pTmp->GetUpper()->GetUpper() : nullptr;
    }
    return false;
}

namespace {

struct LinesAndTable
{
    std::vector<SwTableLine*> &m_rLines;
    const SwTable             &m_rTable;
    bool                      m_bInsertLines;

    LinesAndTable(std::vector<SwTableLine*> &rL, const SwTable &rTable) :
          m_rLines(rL), m_rTable(rTable), m_bInsertLines(true) {}
};

}

static bool FindLine_( FndLine_ & rLine, LinesAndTable* pPara );

static bool FindBox_( FndBox_ & rBox, LinesAndTable* pPara )
{
    if (!rBox.GetLines().empty())
    {
        pPara->m_bInsertLines = true;
        for (auto const& rpFndLine : rBox.GetLines())
        {
            FindLine_(*rpFndLine, pPara);
        }

        if (pPara->m_bInsertLines)
        {
            const SwTableLines &rLines = (rBox.GetBox())
                                    ? rBox.GetBox()->GetTabLines()
                                    : pPara->m_rTable.GetTabLines();
            if (rBox.GetLines().size() == rLines.size())
            {
                for ( auto pLine : rLines )
                    ::InsertLine(pPara->m_rLines, pLine);
            }
            else
                pPara->m_bInsertLines = false;
        }
    }
    else if (rBox.GetBox())
    {
        ::InsertLine(pPara->m_rLines, rBox.GetBox()->GetUpper());
    }
    return true;
}

bool FindLine_( FndLine_& rLine, LinesAndTable* pPara )
{
    for (auto const& it : rLine.GetBoxes())
    {
        FindBox_(*it, pPara);
    }
    return true;
}

static void lcl_CollectLines( std::vector<SwTableLine*> &rArr, const SwCursor& rCursorbool bRemoveLines )
{
    // Collect the selected Boxes first
    SwSelBoxes aBoxes;
    if( !::lcl_GetBoxSel( rCursor, aBoxes ))
        return ;

    // Copy the selected structure
    const SwTable &rTable = aBoxes[0]->GetSttNd()->FindTableNode()->GetTable();
    LinesAndTable aPara( rArr, rTable );
    FndBox_ aFndBox( nullptr, nullptr );
    {
        FndPara aTmpPara( aBoxes, &aFndBox );
        ForEach_FndLineCopyCol( const_cast<SwTableLines&>(rTable.GetTabLines()), &aTmpPara );
    }

    // Collect the Lines which only contain selected Boxes
    ::FindBox_(aFndBox, &aPara);

    // Remove lines, that have a common superordinate row.
    // (Not for row split)
    if ( !bRemoveLines )
        return;

    for ( std::vector<SwTableLine*>::size_type i = 0; i < rArr.size(); ++i )
    {
        SwTableLine *pUpLine = rArr[i];
        for ( std::vector<SwTableLine*>::size_type k = 0; k < rArr.size(); ++k )
        {
            if ( k != i && ::lcl_IsAnLower( pUpLine, rArr[k] ) )
            {
                rArr.erase( rArr.begin() + k );
                if ( k <= i )
                    --i;
                --k;
            }
        }
    }
}

static void lcl_ProcessRowAttr(std::vector<std::unique_ptr<SwTableFormatCmp>>& rFormatCmp,
                               SwTableLine* pLine, const SfxPoolItem& rNew)
{
    SwFrameFormat *pNewFormat = SwTableFormatCmp::FindNewFormat( rFormatCmp, pLine->GetFrameFormat(), 0 );
    if ( nullptr != pNewFormat )
        pLine->ChgFrameFormat( static_cast<SwTableLineFormat*>(pNewFormat) );
    else
    {
        SwFrameFormat *pOld = pLine->GetFrameFormat();
        SwFrameFormat *pNew = pLine->ClaimFrameFormat();
        pNew->SetFormatAttr( rNew );
        rFormatCmp.push_back(std::make_unique<SwTableFormatCmp>(pOld, pNew, 0));
    }
}

static void lcl_ProcessBoxSize(std::vector<std::unique_ptr<SwTableFormatCmp>>& rFormatCmp,
                               SwTableBox* pBox, const SwFormatFrameSize& rNew);

static void lcl_ProcessRowSize(std::vector<std::unique_ptr<SwTableFormatCmp>>& rFormatCmp,
                               SwTableLine* pLine, const SwFormatFrameSize& rNew)
{
    lcl_ProcessRowAttr( rFormatCmp, pLine, rNew );
    SwTableBoxes &rBoxes = pLine->GetTabBoxes();
    for ( auto pBox : rBoxes )
        ::lcl_ProcessBoxSize( rFormatCmp, pBox, rNew );
}

static void lcl_ProcessBoxSize(std::vector<std::unique_ptr<SwTableFormatCmp>>& rFormatCmp,
                               SwTableBox* pBox, const SwFormatFrameSize& rNew)
{
    SwTableLines &rLines = pBox->GetTabLines();
    if ( !rLines.empty() )
    {
        SwFormatFrameSize aSz( rNew );
        aSz.SetHeight( rNew.GetHeight() ? rNew.GetHeight() / rLines.size() : 0 );
        for ( auto pLine : rLines )
            ::lcl_ProcessRowSize( rFormatCmp, pLine, aSz );
    }
}

void SwDoc::SetRowSplit( const SwCursor& rCursor, const SwFormatRowSplit &rNew )
{
    SwTableNode* pTableNd = rCursor.GetPoint()->GetNode().FindTableNode();
    if( !pTableNd )
        return;

    std::vector<SwTableLine*> aRowArr; // For Lines collecting
    ::lcl_CollectLines( aRowArr, rCursor, false );

    if( aRowArr.empty() )
        return;

    if (GetIDocumentUndoRedo().DoesUndo())
    {
        GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAttrTable>(*pTableNd));
    }

    std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
    aFormatCmp.reserve( std::max( 255, static_cast<int>(aRowArr.size()) ) );

    forauto pLn : aRowArr )
        ::lcl_ProcessRowAttr( aFormatCmp, pLn, rNew );

    getIDocumentState().SetModified();
}

std::unique_ptr<SwFormatRowSplit> SwDoc::GetRowSplit( const SwCursor& rCursor )
{
    SwTableNode* pTableNd = rCursor.GetPoint()->GetNode().FindTableNode();
    if( !pTableNd )
        return nullptr;

    std::vector<SwTableLine*> aRowArr; // For Lines collecting
    ::lcl_CollectLines( aRowArr, rCursor, false );

    if( aRowArr.empty() )
        return nullptr;

    SwFormatRowSplit* pSz = &const_cast<SwFormatRowSplit&>(aRowArr[0]->GetFrameFormat()->GetRowSplit());

    for ( auto pLn : aRowArr )
    {
        if ( pSz->GetValue() != pLn->GetFrameFormat()->GetRowSplit().GetValue() )
        {
            return nullptr;
        }
    }
    return std::make_unique<SwFormatRowSplit>( *pSz );
}

/* Class:  SwDoc
 * Methods:  SetRowHeight(), GetRowHeight()
 *
 * The line height is calculated from the Selection.
 * Starting with every Cell within the Selection, all Cells are iterated
 * through in an upwards fashion.
 *
 * The topmost Line gets the requested value, all Lines below it get
 * a respective value that is calculated from the relation of the old and
 * new size of the topmost Line in the lower line's own size.
 *
 * All changed Lines may get an own FrameFormat.
 * Of course we can only touch every Line once.
 */


void SwDoc::SetRowHeight( const SwCursor& rCursor, const SwFormatFrameSize &rNew )
{
    SwTableNode* pTableNd = rCursor.GetPoint()->GetNode().FindTableNode();
    if( !pTableNd )
        return;

    std::vector<SwTableLine*> aRowArr; // For Lines collecting
    ::lcl_CollectLines( aRowArr, rCursor, true );

    if( aRowArr.empty() )
        return;

    if (GetIDocumentUndoRedo().DoesUndo())
    {
        GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAttrTable>(*pTableNd));
    }

    std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
    aFormatCmp.reserve( std::max( 255, static_cast<int>(aRowArr.size()) ) );
    for ( auto pLn : aRowArr )
        ::lcl_ProcessRowSize( aFormatCmp, pLn, rNew );

    getIDocumentState().SetModified();
}

std::unique_ptr<SwFormatFrameSize> SwDoc::GetRowHeight( const SwCursor& rCursor )
{
    SwTableNode* pTableNd = rCursor.GetPoint()->GetNode().FindTableNode();
    if( !pTableNd )
        return nullptr;

    std::vector<SwTableLine*> aRowArr; // For Lines collecting
    ::lcl_CollectLines( aRowArr, rCursor, true );

    if( aRowArr.empty() )
        return nullptr;

    SwFormatFrameSize* pSz = &const_cast<SwFormatFrameSize&>(aRowArr[0]->GetFrameFormat()->GetFrameSize());

    for ( auto pLn : aRowArr )
    {
        if ( *pSz != pLn->GetFrameFormat()->GetFrameSize() )
            return nullptr;
    }
    return std::make_unique<SwFormatFrameSize>( *pSz );
}

bool SwDoc::BalanceRowHeight( const SwCursor& rCursor, bool bTstOnly, const bool bOptimize )
{
    bool bRet = false;
    SwTableNode* pTableNd = rCursor.GetPoint()->GetNode().FindTableNode();
    if( pTableNd )
    {
        std::vector<SwTableLine*> aRowArr; // For Lines collecting
        ::lcl_CollectLines( aRowArr, rCursor, true );

        if( 1 < aRowArr.size() )
        {
            if( !bTstOnly )
            {
                tools::Long nHeight = 0;
                sal_Int32 nTotalHeight = 0;
                for ( auto pLn : aRowArr )
                {
                    if (bOptimize)
                        nHeight = 0;
                    SwIterator<SwFrame,SwFormat> aIter( *pLn->GetFrameFormat() );
                    SwFrame* pFrame = aIter.First();
                    while ( pFrame )
                    {
                        nHeight = std::max( nHeight, pFrame->getFrameArea().Height() );
                        pFrame = aIter.Next();
                    }
                    nTotalHeight += nHeight;
                }

                if ( bOptimize )
                    nHeight = nTotalHeight / aRowArr.size();

                SwFormatFrameSize aNew( SwFrameSize::Minimum, 0, nHeight );

                if (GetIDocumentUndoRedo().DoesUndo())
                {
                    GetIDocumentUndoRedo().AppendUndo(
                            std::make_unique<SwUndoAttrTable>(*pTableNd));
                }

                std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
                aFormatCmp.reserve( std::max( 255, static_cast<int>(aRowArr.size()) ) );
                forauto pLn : aRowArr )
                    ::lcl_ProcessRowSize( aFormatCmp, pLn, aNew );

                getIDocumentState().SetModified();
            }
            bRet = true;
        }
    }
    return bRet;
}

void SwDoc::SetRowBackground( const SwCursor& rCursor, const SvxBrushItem &rNew )
{
    SwTableNode* pTableNd = rCursor.GetPoint()->GetNode().FindTableNode();
    if( !pTableNd )
        return;

    std::vector<SwTableLine*> aRowArr; // For Lines collecting
    ::lcl_CollectLines( aRowArr, rCursor, true );

    if( aRowArr.empty() )
        return;

    if (GetIDocumentUndoRedo().DoesUndo())
    {
        GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAttrTable>(*pTableNd));
    }

    std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
    aFormatCmp.reserve( std::max( 255, static_cast<int>(aRowArr.size()) ) );

    forauto pLn : aRowArr )
        ::lcl_ProcessRowAttr( aFormatCmp, pLn, rNew );

    getIDocumentState().SetModified();
}

bool SwDoc::GetRowBackground( const SwCursor& rCursor, std::unique_ptr<SvxBrushItem>& rToFill )
{
    bool bRet = false;
    SwTableNode* pTableNd = rCursor.GetPoint()->GetNode().FindTableNode();
    if( pTableNd )
    {
        std::vector<SwTableLine*> aRowArr; // For Lines collecting
        ::lcl_CollectLines( aRowArr, rCursor, true );

        if( !aRowArr.empty() )
        {
            rToFill = aRowArr[0]->GetFrameFormat()->makeBackgroundBrushItem();

            bRet = true;
            for ( std::vector<SwTableLine*>::size_type i = 1; i < aRowArr.size(); ++i )
            {
                std::unique_ptr<SvxBrushItem> aAlternative(aRowArr[i]->GetFrameFormat()->makeBackgroundBrushItem());

                if ( *rToFill != *aAlternative )
                {
                    bRet = false;
                    break;
                }
            }
        }
    }
    return bRet;
}

// has a table row, which is not a tracked deletion
bool SwDoc::HasRowNotTracked( const SwCursor& rCursor )
{
    SwTableNode* pTableNd = rCursor.GetPoint()->GetNode().FindTableNode();
    if( !pTableNd )
        return false;

    std::vector<SwTableLine*> aRowArr; // For Lines collecting
    ::lcl_CollectLines( aRowArr, rCursor, true );

    if( aRowArr.empty() )
        return false;

    SwRedlineTable::size_type nRedlinePos = 0;
    SwDoc& rDoc = aRowArr[0]->GetFrameFormat()->GetDoc();
    const IDocumentRedlineAccess& rIDRA = rDoc.getIDocumentRedlineAccess();

    forauto pLn : aRowArr )
    {
        auto pHasTextChangesOnlyProp = pLn->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
        if ( !pHasTextChangesOnlyProp || pHasTextChangesOnlyProp->GetValue() )
            // there is a not tracked row in the table selection
            return true;

        // tdf#150666 examine tracked row: it's possible to delete a tracked insertion
        SwRedlineTable::size_type nPos = pLn->UpdateTextChangesOnly(nRedlinePos);
        if ( nPos != SwRedlineTable::npos )
        {
            const SwRedlineTable& aRedlineTable = rIDRA.GetRedlineTable();
            SwRangeRedline* pTmp = aRedlineTable[ nPos ];
            if ( RedlineType::Insert == pTmp->GetType() )
                return true;
        }
    }
    return false;
}

void SwDoc::SetRowNotTracked( const SwCursor& rCursor,
                                     const SvxPrintItem &rNew, bool bAll, bool bIns )
{
    SwTableNode* pTableNd = rCursor.GetPoint()->GetNode().FindTableNode();
    if( !pTableNd )
        return;

    std::vector<SwTableLine*> aRowArr; // For Lines collecting
    if ( bAll )
    {
        const SwTableLines &rLines = pTableNd->GetTable().GetTabLines();
        aRowArr.insert(aRowArr.end(), rLines.begin(), rLines.end());
    }
    else
        ::lcl_CollectLines( aRowArr, rCursor, true );

    if( aRowArr.empty() )
        return;

    if (GetIDocumentUndoRedo().DoesUndo())
    {
        GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAttrTable>(*pTableNd));
    }

    bool bInsertDummy = !bAll && !bIns &&
            // HasTextChangesOnly == false, i.e. a tracked row change (deletion, if bIns == false)
            !rNew.GetValue();
    std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
    aFormatCmp.reserve( std::max( 255, static_cast<int>(aRowArr.size()) ) );

    SwRedlineTable::size_type nRedlinePos = 0;
    forauto pLn : aRowArr )
    {
        // tdf#150666 deleting row insertion from the same author needs special handling,
        // because removing redlines of the author can result an empty line,
        // which doesn't contain any redline for the tracked row
        bool bDeletionOfOwnRowInsertion = false;
        if ( bInsertDummy )
        {
            SwRedlineTable::size_type nPos = pLn->UpdateTextChangesOnly(nRedlinePos);
            if ( nPos != SwRedlineTable::npos )
            {
                SwDoc& rDoc = pLn->GetFrameFormat()->GetDoc();
                IDocumentRedlineAccess& rIDRA = rDoc.getIDocumentRedlineAccess();
                const SwRedlineTable& aRedlineTable = rIDRA.GetRedlineTable();
                SwRangeRedline* pTmp = aRedlineTable[ nPos ];
                if ( RedlineType::Insert == pTmp->GetType() &&
                         rIDRA.GetRedlineAuthor() == pTmp->GetRedlineData().GetAuthor() &&
                         pTmp->GetText()[0] == CH_TXT_TRACKED_DUMMY_CHAR )
                {
                    bDeletionOfOwnRowInsertion = true;
                }
            }
        }

        ::lcl_ProcessRowAttr( aFormatCmp, pLn, rNew );
        // as a workaround for the rows without text content,
        // add a redline with invisible text CH_TXT_TRACKED_DUMMY_CHAR
        // (unless the table is part of a bigger deletion, where the
        // new redline can cause a problem)
        if ( bInsertDummy && (pLn->IsEmpty() || bDeletionOfOwnRowInsertion ) )
        {
            ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
            SwNodeIndex aInsPos( *(pLn->GetTabBoxes()[0]->GetSttNd()), 1 );
            RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
            getIDocumentRedlineAccess().SetRedlineFlags_intern(RedlineFlags::NONE);
            SwPaM aPaM(aInsPos);
            getIDocumentContentOperations().InsertString( aPaM,
                    OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) );
            aPaM.SetMark();
            getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
            getIDocumentContentOperations().DeleteAndJoin( aPaM );
        }
    }

    getIDocumentState().SetModified();
}

static void InsertCell( std::vector<SwCellFrame*>& rCellArr, SwCellFrame* pCellFrame )
{
    if( rCellArr.end() == std::find( rCellArr.begin(), rCellArr.end(), pCellFrame ) )
        rCellArr.push_back( pCellFrame );
}

static void lcl_CollectCells( std::vector<SwCellFrame*> &rArr, const SwRect &rUnion,
                          SwTabFrame *pTab )
{
    SwLayoutFrame *pCell = pTab->FirstCell();
    do
    {
        // If the Cell contains a CellFrame, we need to use it
        // in order to get to the Cell
        while ( !pCell->IsCellFrame() )
            pCell = pCell->GetUpper();
        assert(pCell && "Frame is not a Cell");
        if ( rUnion.Overlaps( pCell->getFrameArea() ) )
            ::InsertCell( rArr, static_cast<SwCellFrame*>(pCell) );

        // Make sure the Cell is left (Areas)
        SwLayoutFrame *pTmp = pCell;
        do
        {   pTmp = pTmp->GetNextLayoutLeaf();
        } while ( pCell->IsAnLower( pTmp ) );
        pCell = pTmp;
    } while( pCell && pTab->IsAnLower( pCell ) );
}

void SwDoc::SetTabBorders( const SwCursor& rCursor, const SfxItemSet& rSet )
{
    SwContentNode* pCntNd = rCursor.GetPoint()->GetNode().GetContentNode();
    SwTableNode* pTableNd = pCntNd ? pCntNd->FindTableNode() : nullptr;
    if( !pTableNd )
        return ;

    SwLayoutFrame *pStart, *pEnd;
    ::lcl_GetStartEndCell( rCursor, pStart, pEnd );

    SwSelUnions aUnions;
    ::MakeSelUnions( aUnions, pStart, pEnd );

    if( aUnions.empty() )
        return;

    SwTable& rTable = pTableNd->GetTable();
    if (GetIDocumentUndoRedo().DoesUndo())
    {
        GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoAttrTable>(*pTableNd) );
    }

    std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
    aFormatCmp.reserve( 255 );
    const SvxBoxItem* pSetBox;
    const SvxBoxInfoItem *pSetBoxInfo;

    const SvxBorderLine* pLeft = nullptr;
    const SvxBorderLine* pRight = nullptr;
    const SvxBorderLine* pTop = nullptr;
    const SvxBorderLine* pBottom = nullptr;
    const SvxBorderLine* pHori = nullptr;
    const SvxBorderLine* pVert = nullptr;
    bool bHoriValid = true, bVertValid = true,
         bTopValid = true, bBottomValid = true,
         bLeftValid = true, bRightValid = true;

    // The Flags in the BoxInfo Item decide whether a BorderLine is valid!
    pSetBoxInfo = rSet.GetItemIfSet( SID_ATTR_BORDER_INNER, false );
    if( pSetBoxInfo )
    {
        pHori = pSetBoxInfo->GetHori();
        pVert = pSetBoxInfo->GetVert();

        bHoriValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::HORI);
        bVertValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::VERT);

        // Do we want to evaluate these?
        bTopValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::TOP);
        bBottomValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::BOTTOM);
        bLeftValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::LEFT);
        bRightValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::RIGHT);
    }

    pSetBox = rSet.GetItemIfSet( RES_BOX, false );
    if( pSetBox )
    {
        pLeft = pSetBox->GetLeft();
        pRight = pSetBox->GetRight();
        pTop = pSetBox->GetTop();
        pBottom = pSetBox->GetBottom();
    }
    else
    {
        // Not set, thus not valid values
        bTopValid = bBottomValid = bLeftValid = bRightValid = false;
        pSetBox = nullptr;
    }

    bool bFirst = true;
    for ( SwSelUnions::size_type i = 0; i < aUnions.size(); ++i )
    {
        SwSelUnion *pUnion = &aUnions[i];
        SwTabFrame *pTab = pUnion->GetTable();
        const SwRect &rUnion = pUnion->GetUnion();
        const bool bLast  = (i == aUnions.size() - 1);

        std::vector<SwCellFrame*> aCellArr;
        aCellArr.reserve( 255 );
        ::lcl_CollectCells( aCellArr, pUnion->GetUnion(), pTab );

        // All Cell Borders that match the UnionRect or extend it are
        // Outer Borders. All others are Inner Borders.

        // New: The Outer Borders can, depending on whether it's a
        // Start/Middle/Follow Table (for Selection via FollowTabs),
        // also not be Outer Borders.
        // Outer Borders are set on the left, right, at the top and at the bottom.
        // Inner Borders are only set at the top and on the left.
        for ( auto pCell : aCellArr )
        {
            const bool bVert = pTab->IsVertical();
            const bool bRTL = pTab->IsRightToLeft();
            bool bTopOver, bLeftOver, bRightOver, bBottomOver;
            if ( bVert )
            {
                bTopOver = pCell->getFrameArea().Right() >= rUnion.Right();
                bLeftOver = pCell->getFrameArea().Top() <= rUnion.Top();
                bRightOver = pCell->getFrameArea().Bottom() >= rUnion.Bottom();
                bBottomOver = pCell->getFrameArea().Left() <= rUnion.Left();
            }
            else
            {
                bTopOver = pCell->getFrameArea().Top() <= rUnion.Top();
                bLeftOver = pCell->getFrameArea().Left() <= rUnion.Left();
                bRightOver = pCell->getFrameArea().Right() >= rUnion.Right();
                bBottomOver = pCell->getFrameArea().Bottom() >= rUnion.Bottom();
            }

            if ( bRTL )
                std::swap( bLeftOver, bRightOver );

            // Do not set anything by default in HeadlineRepeats
            if ( pTab->IsFollow() &&
                 ( pTab->IsInHeadline( *pCell ) ||
                   // Same holds for follow flow rows
                   pCell->IsInFollowFlowRow() ) )
                continue;

            SvxBoxItem aBox( pCell->GetFormat()->GetBox() );

            sal_Int16 nType = 0;

            // Top Border
            if( bTopValid )
            {
                if ( bFirst && bTopOver )
                {
                    aBox.SetLine( pTop, SvxBoxItemLine::TOP );
                    nType |= 0x0001;
                }
                else if ( bHoriValid )
                {
                    aBox.SetLine( nullptr, SvxBoxItemLine::TOP );
                    nType |= 0x0002;
                }
            }

            // Fix fdo#62470 correct the input for RTL table
            if (bRTL)
            {
                    if( bLeftOver && bRightOver)
                    {
                        if ( bLeftValid )
                        {
                            aBox.SetLine( pLeft, SvxBoxItemLine::RIGHT );
                            nType |= 0x0010;
                        }
                        if ( bRightValid )
                        {
                            aBox.SetLine( pRight, SvxBoxItemLine::LEFT );
                            nType |= 0x0004;
                        }
                    }
                    else
                    {
                        if ( bLeftValid )
                        {
                            aBox.SetLine( bRightOver ? pLeft : nullptr, SvxBoxItemLine::RIGHT );
                            if (bVertValid)
                                nType |= 0x0020;
                            else
                                nType |= 0x0010;
                        }
                        if ( bLeftOver )
                        {
                            if ( bRightValid )
                            {
                                aBox.SetLine( pRight, SvxBoxItemLine::LEFT );
                                nType |= 0x0004;
                            }
                        }
                        else if ( bVertValid )
                        {
                            aBox.SetLine( pVert, SvxBoxItemLine::LEFT );
                            nType |= 0x0008;
                        }
                    }
            }
            else
            {
                // Left Border
                if ( bLeftOver )
                {
                    if( bLeftValid )
                    {
                        aBox.SetLine( pLeft, SvxBoxItemLine::LEFT );
                        nType |= 0x0004;
                    }
                }
                else if( bVertValid )
                {
                    aBox.SetLine( pVert, SvxBoxItemLine::LEFT );
                    nType |= 0x0008;
                }

                // Right Border
                if( bRightValid )
                {
                    if ( bRightOver )
                    {
                        aBox.SetLine( pRight, SvxBoxItemLine::RIGHT );
                        nType |= 0x0010;
                    }
                    else if ( bVertValid )
                    {
                        aBox.SetLine( nullptr, SvxBoxItemLine::RIGHT );
                        nType |= 0x0020;
                    }
                }
            }

            // Bottom Border
            if ( bLast && bBottomOver )
            {
                if( bBottomValid )
                {
                    aBox.SetLine( pBottom, SvxBoxItemLine::BOTTOM );
                    nType |= 0x0040;
                }
            }
            else if( bHoriValid )
            {
                aBox.SetLine( pHori, SvxBoxItemLine::BOTTOM );
                nType |= 0x0080;
            }

            if( pSetBox )
            {
               for( SvxBoxItemLine k : o3tl::enumrange<SvxBoxItemLine>() )
                    aBox.SetDistance( pSetBox->GetDistance( k ), k );
            }

            SwTableBox *pBox = const_cast<SwTableBox*>(pCell->GetTabBox());
            SwFrameFormat *pNewFormat = SwTableFormatCmp::FindNewFormat( aFormatCmp, pBox->GetFrameFormat(), nType );
            if ( nullptr != pNewFormat )
                pBox->ChgFrameFormat( static_cast<SwTableBoxFormat*>(pNewFormat) );
            else
            {
                SwFrameFormat *pOld = pBox->GetFrameFormat();
                SwFrameFormat *pNew = pBox->ClaimFrameFormat();
                pNew->SetFormatAttr( aBox );
                aFormatCmp.push_back(std::make_unique<SwTableFormatCmp>(pOld, pNew, nType));
            }
        }

        bFirst = false;
    }

    SwHTMLTableLayout *pTableLayout = rTable.GetHTMLTableLayout();
    if( pTableLayout )
    {
        SwContentFrame* pFrame = rCursor.GetPointContentNode()->getLayoutFrame( rCursor.GetPointContentNode()->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
        SwTabFrame* pTabFrame = pFrame->ImplFindTabFrame();

        pTableLayout->BordersChanged(
            pTableLayout->GetBrowseWidthByTabFrame( *pTabFrame ) );
    }
    ::ClearFEShellTabCols(*this, nullptr);
    getIDocumentState().SetModified();
}

static void lcl_SetLineStyle( SvxBorderLine *pToSet,
                          const Color *pColor, const SvxBorderLine *pBorderLine)
{
    if ( pBorderLine )
    {
        if ( !pColor )
        {
            Color aTmp( pToSet->GetColor() );
            *pToSet = *pBorderLine;
            pToSet->SetColor( aTmp );
        }
        else
            *pToSet = *pBorderLine;
    }
    if ( pColor )
        pToSet->SetColor( *pColor );
}

void SwDoc::SetTabLineStyle( const SwCursor& rCursor,
                             const Color* pColor, bool bSetLine,
                             const SvxBorderLine* pBorderLine )
{
    SwContentNode* pCntNd = rCursor.GetPoint()->GetNode().GetContentNode();
    SwTableNode* pTableNd = pCntNd ? pCntNd->FindTableNode() : nullptr;
    if( !pTableNd )
        return ;

    SwLayoutFrame *pStart, *pEnd;
    ::lcl_GetStartEndCell( rCursor, pStart, pEnd );

    SwSelUnions aUnions;
    ::MakeSelUnions( aUnions, pStart, pEnd );

    if( aUnions.empty() )
        return;

    SwTable& rTable = pTableNd->GetTable();
    if (GetIDocumentUndoRedo().DoesUndo())
    {
        GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAttrTable>(*pTableNd));
    }

    SvxBorderLine aDefaultBorder(pBorderLine ? *pBorderLine
                                             : SvxBorderLine(pColor, SvxBorderLineWidth::VeryThin));
    if (pColor && pBorderLine)
        aDefaultBorder.SetColor(*pColor);

    forauto &rU : aUnions )
    {
        SwSelUnion *pUnion = &rU;
        SwTabFrame *pTab = pUnion->GetTable();
        std::vector<SwCellFrame*> aCellArr;
        aCellArr.reserve( 255 );
        ::lcl_CollectCells( aCellArr, pUnion->GetUnion(), pTab );

        for ( auto pCell : aCellArr )
        {
            // Do not set anything by default in HeadlineRepeats
            if ( pTab->IsFollow() && pTab->IsInHeadline( *pCell ) )
                continue;

            const_cast<SwTableBox*>(pCell->GetTabBox())->ClaimFrameFormat();
            SwFrameFormat *pFormat = pCell->GetFormat();
            std::unique_ptr<SvxBoxItem> aBox(pFormat->GetBox().Clone());

            SvxBorderLine* pTop = aBox->GetTop();
            SvxBorderLine* pBot = aBox->GetBottom();
            SvxBorderLine* pLeft = aBox->GetLeft();
            SvxBorderLine* pRight = aBox->GetRight();

            if ( !pBorderLine && bSetLine )
            {
                aBox.reset(::GetDfltAttr(RES_BOX)->Clone());
            }
            else if ((pColor || pBorderLine) && !pTop && !pBot && !pLeft && !pRight)
            {
                aBox->SetLine(&aDefaultBorder, SvxBoxItemLine::TOP);
                aBox->SetLine(&aDefaultBorder, SvxBoxItemLine::BOTTOM);
                aBox->SetLine(&aDefaultBorder, SvxBoxItemLine::LEFT);
                aBox->SetLine(&aDefaultBorder, SvxBoxItemLine::RIGHT);
            }
            else
            {
                if (pTop)
                    ::lcl_SetLineStyle(pTop, pColor, pBorderLine);
                if (pBot)
                    ::lcl_SetLineStyle(pBot, pColor, pBorderLine);
                if (pLeft)
                    ::lcl_SetLineStyle(pLeft, pColor, pBorderLine);
                if (pRight)
                    ::lcl_SetLineStyle(pRight, pColor, pBorderLine);
            }
            pFormat->SetFormatAttr( *aBox );
        }
    }

    SwHTMLTableLayout *pTableLayout = rTable.GetHTMLTableLayout();
    if( pTableLayout )
    {
        SwContentFrame* pFrame = rCursor.GetPointContentNode()->getLayoutFrame( rCursor.GetPointContentNode()->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
        SwTabFrame* pTabFrame = pFrame->ImplFindTabFrame();

        pTableLayout->BordersChanged(
            pTableLayout->GetBrowseWidthByTabFrame( *pTabFrame ) );
    }
    ::ClearFEShellTabCols(*this, nullptr);
    getIDocumentState().SetModified();
}

void SwDoc::GetTabBorders( const SwCursor& rCursor, SfxItemSet& rSet )
{
    SwContentNode* pCntNd = rCursor.GetPoint()->GetNode().GetContentNode();
    SwTableNode* pTableNd = pCntNd ? pCntNd->FindTableNode() : nullptr;
    if( !pTableNd )
        return ;

    SwLayoutFrame *pStart, *pEnd;
    ::lcl_GetStartEndCell( rCursor, pStart, pEnd );

    SwSelUnions aUnions;
    ::MakeSelUnions( aUnions, pStart, pEnd );

    if( aUnions.empty() )
        return;

    SvxBoxItem     aSetBox    ( rSet.Get(RES_BOX    ) );
    SvxBoxInfoItem aSetBoxInfo( rSet.Get(SID_ATTR_BORDER_INNER) );

    bool bTopSet      = false,
         bBottomSet   = false,
         bLeftSet     = false,
         bRightSet    = false,
         bHoriSet     = false,
         bVertSet     = false,
         bDistanceSet = false,
         bRTLTab      = false;

    aSetBoxInfo.ResetFlags();

    for ( SwSelUnions::size_type i = 0; i < aUnions.size(); ++i )
    {
        SwSelUnion *pUnion = &aUnions[i];
        const SwTabFrame *pTab = pUnion->GetTable();
        const SwRect &rUnion = pUnion->GetUnion();
        const bool bFirst = i == 0;
        const bool bLast  = (i == aUnions.size() - 1);

        std::vector<SwCellFrame*> aCellArr;
        aCellArr.reserve(255);
        ::lcl_CollectCells( aCellArr, rUnion, const_cast<SwTabFrame*>(pTab) );

        for ( auto pCell : aCellArr )
        {
            const bool bVert = pTab->IsVertical();
            const bool bRTL = bRTLTab = pTab->IsRightToLeft();
            bool bTopOver, bLeftOver, bRightOver, bBottomOver;
            if ( bVert )
            {
                bTopOver = pCell->getFrameArea().Right() >= rUnion.Right();
                bLeftOver = pCell->getFrameArea().Top() <= rUnion.Top();
                bRightOver = pCell->getFrameArea().Bottom() >= rUnion.Bottom();
                bBottomOver = pCell->getFrameArea().Left() <= rUnion.Left();
            }
            else
            {
                bTopOver = pCell->getFrameArea().Top() <= rUnion.Top();
                bLeftOver = pCell->getFrameArea().Left() <= rUnion.Left();
                bRightOver = pCell->getFrameArea().Right() >= rUnion.Right();
                bBottomOver = pCell->getFrameArea().Bottom() >= rUnion.Bottom();
            }

            if ( bRTL )
                std::swap( bLeftOver, bRightOver );

            const SwFrameFormat  *pFormat  = pCell->GetFormat();
            const SvxBoxItem  &rBox  = pFormat->GetBox();

            // Top Border
            if ( bFirst && bTopOver )
            {
                if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::TOP))
                {
                    if ( !bTopSet )
                    {   bTopSet = true;
                        aSetBox.SetLine( rBox.GetTop(), SvxBoxItemLine::TOP );
                    }
                    else if ((aSetBox.GetTop() && rBox.GetTop() &&
                             (*aSetBox.GetTop() != *rBox.GetTop())) ||
                             ((!aSetBox.GetTop()) != (!rBox.GetTop()))) // != expression is true, if one and only one of the two pointers is !0
                    {
                        aSetBoxInfo.SetValid(SvxBoxInfoItemValidFlags::TOP, false );
                        aSetBox.SetLine( nullptr, SvxBoxItemLine::TOP );
                    }
                }
            }

            // Left Border
            if ( bLeftOver )
            {
                if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::LEFT))
                {
                    if ( !bLeftSet )
                    {   bLeftSet = true;
                        aSetBox.SetLine( rBox.GetLeft(), SvxBoxItemLine::LEFT );
                    }
                    else if ((aSetBox.GetLeft() && rBox.GetLeft() &&
                             (*aSetBox.GetLeft() != *rBox.GetLeft())) ||
                             ((!aSetBox.GetLeft()) != (!rBox.GetLeft())))
                    {
                        aSetBoxInfo.SetValid(SvxBoxInfoItemValidFlags::LEFT, false );
                        aSetBox.SetLine( nullptr, SvxBoxItemLine::LEFT );
                    }
                }
            }
            else
            {
                if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::VERT))
                {
                    if ( !bVertSet )
                    {   bVertSet = true;
                        aSetBoxInfo.SetLine( rBox.GetLeft(), SvxBoxInfoItemLine::VERT );
                    }
                    else if ((aSetBoxInfo.GetVert() && rBox.GetLeft() &&
                             (*aSetBoxInfo.GetVert() != *rBox.GetLeft())) ||
                             ((!aSetBoxInfo.GetVert()) != (!rBox.GetLeft())))
                    {   aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::VERT, false );
                        aSetBoxInfo.SetLine( nullptr, SvxBoxInfoItemLine::VERT );
                    }
                }
            }

            // Right Border
            if ( aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::RIGHT) && bRightOver )
            {
                if ( !bRightSet )
                {   bRightSet = true;
                    aSetBox.SetLine( rBox.GetRight(), SvxBoxItemLine::RIGHT );
                }
                else if ((aSetBox.GetRight() && rBox.GetRight() &&
                         (*aSetBox.GetRight() != *rBox.GetRight())) ||
                         (!aSetBox.GetRight() != !rBox.GetRight()))
                {   aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::RIGHT, false );
                    aSetBox.SetLine( nullptr, SvxBoxItemLine::RIGHT );
                }
            }

            // Bottom Border
            if ( bLast && bBottomOver )
            {
                if ( aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::BOTTOM) )
                {
                    if ( !bBottomSet )
                    {   bBottomSet = true;
                        aSetBox.SetLine( rBox.GetBottom(), SvxBoxItemLine::BOTTOM );
                    }
                    else if ((aSetBox.GetBottom() && rBox.GetBottom() &&
                             (*aSetBox.GetBottom() != *rBox.GetBottom())) ||
                             (!aSetBox.GetBottom() != !rBox.GetBottom()))
                    {   aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::BOTTOM, false );
                        aSetBox.SetLine( nullptr, SvxBoxItemLine::BOTTOM );
                    }
                }
            }
            // In all Lines, except for the last one, the horizontal Line
            // is taken from the Bottom Line.
            else
            {
                if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::HORI))
                {
                    if ( !bHoriSet )
                    {   bHoriSet = true;
                        aSetBoxInfo.SetLine( rBox.GetBottom(), SvxBoxInfoItemLine::HORI );
                    }
                    else if ((aSetBoxInfo.GetHori() && rBox.GetBottom() &&
                             (*aSetBoxInfo.GetHori() != *rBox.GetBottom())) ||
                             ((!aSetBoxInfo.GetHori()) != (!rBox.GetBottom())))
                    {
                        aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::HORI, false );
                        aSetBoxInfo.SetLine( nullptr, SvxBoxInfoItemLine::HORI );
                    }
                }
            }

            // Distance to text
            if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::DISTANCE))
            {
                if( !bDistanceSet ) // Set on first iteration
                {
                    bDistanceSet = true;
                    for( SvxBoxItemLine k : o3tl::enumrange<SvxBoxItemLine>() )
                        aSetBox.SetDistance( rBox.GetDistance( k ), k );
                }
                else
                {
                    for( SvxBoxItemLine k : o3tl::enumrange<SvxBoxItemLine>() )
                        if( aSetBox.GetDistance( k ) !=
                            rBox.GetDistance( k ) )
                        {
                            aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::DISTANCE, false );
                            aSetBox.SetAllDistances(0);
                            break;
                        }
                }
            }
        }
    }

    // fdo#62470 fix the reading for table format.
    if ( bRTLTab )
    {
        SvxBoxItem     aTempBox    ( rSet.Get(RES_BOX    ) );
        SvxBoxInfoItem aTempBoxInfo( rSet.Get(SID_ATTR_BORDER_INNER) );

        aTempBox.SetLine( aSetBox.GetRight(), SvxBoxItemLine::RIGHT);
        aSetBox.SetLine( aSetBox.GetLeft(), SvxBoxItemLine::RIGHT);
        aSetBox.SetLine( aTempBox.GetRight(), SvxBoxItemLine::LEFT);

        aTempBoxInfo.SetValid( SvxBoxInfoItemValidFlags::LEFT, aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::LEFT) );
        aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::LEFT, aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::RIGHT) );
        aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::RIGHT, aTempBoxInfo.IsValid(SvxBoxInfoItemValidFlags::LEFT) );
    }

    rSet.Put( aSetBox );
    rSet.Put( aSetBoxInfo );
}

void SwDoc::SetBoxAttr( const SwCursor& rCursor, const SfxPoolItem &rNew )
{
    SwTableNode* pTableNd = rCursor.GetPoint()->GetNode().FindTableNode();
    SwSelBoxes aBoxes;
    if( !(pTableNd && ::lcl_GetBoxSel( rCursor, aBoxes, true )) )
        return;

    SwTable& rTable = pTableNd->GetTable();
    if (GetIDocumentUndoRedo().DoesUndo())
    {
        GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoAttrTable>(*pTableNd) );
    }

    std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
    aFormatCmp.reserve(std::max<size_t>(255, aBoxes.size()));
    for (size_t i = 0; i < aBoxes.size(); ++i)
    {
        SwTableBox *pBox = aBoxes[i];

        SwFrameFormat *pNewFormat = SwTableFormatCmp::FindNewFormat( aFormatCmp, pBox->GetFrameFormat(), 0 );
        if ( nullptr != pNewFormat )
            pBox->ChgFrameFormat( static_cast<SwTableBoxFormat*>(pNewFormat) );
        else
        {
            SwFrameFormat *pOld = pBox->GetFrameFormat();
            SwFrameFormat *pNew = pBox->ClaimFrameFormat();
            pNew->SetFormatAttr( rNew );
            aFormatCmp.push_back(std::make_unique<SwTableFormatCmp>(pOld, pNew, 0));
        }

        pBox->SetDirectFormatting(true);
    }

    SwHTMLTableLayout *pTableLayout = rTable.GetHTMLTableLayout();
    if( pTableLayout )
    {
        SwContentFrame* pFrame = rCursor.GetPointContentNode()->getLayoutFrame( rCursor.GetPointContentNode()->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
        SwTabFrame* pTabFrame = pFrame->ImplFindTabFrame();

        pTableLayout->Resize(
            pTableLayout->GetBrowseWidthByTabFrame( *pTabFrame ), true );
    }
    getIDocumentState().SetModified();
}

bool SwDoc::GetBoxAttr( const SwCursor& rCursor, std::unique_ptr<SfxPoolItem>& rToFill )
{
    // tdf#144843 calling GetBoxAttr *requires* object
    assert(rToFill && "requires object here");
    bool bRet = false;
    SwTableNode* pTableNd = rCursor.GetPoint()->GetNode().FindTableNode();
    SwSelBoxes aBoxes;
    if( pTableNd && lcl_GetBoxSel( rCursor, aBoxes ))
    {
        bRet = true;
        bool bOneFound = false;
        const sal_uInt16 nWhich = rToFill->Which();
        for (size_t i = 0; i < aBoxes.size(); ++i)
        {
            switch ( nWhich )
            {
                case RES_BACKGROUND:
                {
                    std::unique_ptr<SvxBrushItem> xBack =
                        aBoxes[i]->GetFrameFormat()->makeBackgroundBrushItem();
                    if( !bOneFound )
                    {
                        rToFill = std::move(xBack);
                        bOneFound = true;
                    }
                    else if( *rToFill != *xBack )
                        bRet = false;
                }
                break;

                case RES_FRAMEDIR:
                {
                    const SvxFrameDirectionItem& rDir =
                                    aBoxes[i]->GetFrameFormat()->GetFrameDir();
                    if( !bOneFound )
                    {
                        rToFill.reset(rDir.Clone());
                        bOneFound = true;
                    }
                    else if( rToFill && *rToFill != rDir )
                        bRet = false;
                }
                break;
                case RES_VERT_ORIENT:
                {
                    const SwFormatVertOrient& rOrient =
                                    aBoxes[i]->GetFrameFormat()->GetVertOrient();
                    if( !bOneFound )
                    {
                        rToFill.reset(rOrient.Clone());
                        bOneFound = true;
                    }
                    else if( rToFill && *rToFill != rOrient )
                        bRet = false;
                }
                break;
            }

            if ( !bRet )
                break;
        }
    }
    return bRet;
}

void SwDoc::SetBoxAlign( const SwCursor& rCursor, sal_uInt16 nAlign )
{
    OSL_ENSURE( nAlign == text::VertOrientation::NONE   ||
            nAlign == text::VertOrientation::CENTER ||
            nAlign == text::VertOrientation::BOTTOM, "Wrong alignment" );
    SwFormatVertOrient aVertOri( 0, nAlign );
    SetBoxAttr( rCursor, aVertOri );
}

sal_uInt16 SwDoc::GetBoxAlign( const SwCursor& rCursor )
{
    sal_uInt16 nAlign = USHRT_MAX;
    SwTableNode* pTableNd = rCursor.GetPoint()->GetNode().FindTableNode();
    SwSelBoxes aBoxes;
    if( pTableNd && ::lcl_GetBoxSel( rCursor, aBoxes ))
    {
        for (size_t i = 0; i < aBoxes.size(); ++i)
        {
            const SwFormatVertOrient &rOri =
                            aBoxes[i]->GetFrameFormat()->GetVertOrient();
            if( USHRT_MAX == nAlign )
                nAlign = o3tl::narrowing<sal_uInt16>(rOri.GetVertOrient());
            else if( rOri.GetVertOrient() != nAlign )
            {
                nAlign = USHRT_MAX;
                break;
            }
        }
    }
    return nAlign;
}

static sal_uInt16 lcl_CalcCellFit( const SwLayoutFrame *pCell )
{
    SwTwips nRet = 0;
    const SwFrame *pFrame = pCell->Lower(); // The whole Line
    SwRectFnSet aRectFnSet(pCell);
    while ( pFrame )
    {
        const SwTwips nAdd = aRectFnSet.GetWidth(pFrame->getFrameArea()) -
                             aRectFnSet.GetWidth(pFrame->getFramePrintArea());

        // pFrame does not necessarily have to be a SwTextFrame!
        const SwTwips nCalcFitToContent = pFrame->IsTextFrame() ?
                                          const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pFrame))->CalcFitToContent() :
                                          aRectFnSet.GetWidth(pFrame->getFramePrintArea());

        nRet = std::max( nRet, nCalcFitToContent + nAdd );
        pFrame = pFrame->GetNext();
    }
    // Surrounding border as well as left and Right Border also need to be respected
    nRet += aRectFnSet.GetWidth(pCell->getFrameArea()) -
            aRectFnSet.GetWidth(pCell->getFramePrintArea());

    // To compensate for the accuracy of calculation later on in SwTable::SetTabCols
    // we keep adding up a little.
    nRet += COLFUZZY;
    return o3tl::narrowing<sal_uInt16>(std::max( SwTwips(MINLAY), nRet ));
}

/* The Line is within the Selection but not outlined by the TabCols.
 *
 * That means that the Line has been "split" by other Cells due to the
 * two-dimensional representation used. Thus, we have to distribute the cell's
 * default or minimum value amongst the Cell it has been split by.
 *
 * First, we collect the Columns (not the Column separators) which overlap
 * with the Cell. We then distribute the desired value according to the
 * amount of overlapping amongst the Cells.
 *
 * A Cell's default value stays the same if it already has a larger value than
 * the desired one. It's overwritten if it's smaller.
 */

static void lcl_CalcSubColValues( std::vector<sal_uInt16> &rToFill, const SwTabCols &rCols,
                              const SwLayoutFrame *pCell, const SwLayoutFrame *pTab,
                              bool bWishValues )
{
    const sal_uInt16 nWish = bWishValues ?
                    ::lcl_CalcCellFit( pCell ) :
                    MINLAY + sal_uInt16(pCell->getFrameArea().Width() - pCell->getFramePrintArea().Width());

    SwRectFnSet aRectFnSet(pTab);

    for ( size_t i = 0 ; i <= rCols.Count(); ++i )
    {
        tools::Long nColLeft  = i == 0             ? rCols.GetLeft()  : rCols[i-1];
        tools::Long nColRight = i == rCols.Count() ? rCols.GetRight() : rCols[i];
        nColLeft  += rCols.GetLeftMin();
        nColRight += rCols.GetLeftMin();

        // Adapt values to the proportions of the Table (Follows)
        if ( rCols.GetLeftMin() != aRectFnSet.GetLeft(pTab->getFrameArea()) )
        {
            const tools::Long nDiff = aRectFnSet.GetLeft(pTab->getFrameArea()) - rCols.GetLeftMin();
            nColLeft  += nDiff;
            nColRight += nDiff;
        }
        const tools::Long nCellLeft  = aRectFnSet.GetLeft(pCell->getFrameArea());
        const tools::Long nCellRight = aRectFnSet.GetRight(pCell->getFrameArea());

        // Calculate overlapping value
        tools::Long nWidth = 0;
        if ( nColLeft <= nCellLeft && nColRight >= (nCellLeft+COLFUZZY) )
            nWidth = nColRight - nCellLeft;
        else if ( nColLeft <= (nCellRight-COLFUZZY) && nColRight >= nCellRight )
            nWidth = nCellRight - nColLeft;
        else if ( nColLeft >= nCellLeft && nColRight <= nCellRight )
            nWidth = nColRight - nColLeft;
        if ( nWidth && pCell->getFrameArea().Width() )
        {
            tools::Long nTmp = nWidth * nWish / pCell->getFrameArea().Width();
            if ( o3tl::make_unsigned(nTmp) > rToFill[i] )
                rToFill[i] = sal_uInt16(nTmp);
        }
    }
}

/**
 * Retrieves new values to set the TabCols.
 *
 * We do not iterate over the TabCols' entries, but over the gaps that describe Cells.
 * We set TabCol entries for which we did not calculate Cells to 0.
 *
 * @param bWishValues == true:      We calculate the desired value of all affected
 *                                  Cells for the current Selection/current Cell.
 *                                  If more Cells are within a Column, the highest
 *                                  desired value is returned.
 *                                  We set TabCol entries for which we did not calculate
 *                                  Cells to 0.
 *
 * @param bWishValues == false:     The Selection is expanded vertically.
 *                                  We calculate the minimum value for every
 *                                  Column in the TabCols that intersects with the
 *                                  Selection.
 */

static void lcl_CalcColValues( std::vector<sal_uInt16> &rToFill, const SwTabCols &rCols,
                           const SwLayoutFrame *pStart, const SwLayoutFrame *pEnd,
                           bool bWishValues )
{
    SwSelUnions aUnions;
    ::MakeSelUnions( aUnions, pStart, pEnd,
                    bWishValues ? SwTableSearchType::NONE : SwTableSearchType::Col );

    for ( auto &rU : aUnions )
    {
        SwSelUnion *pSelUnion = &rU;
        const SwTabFrame *pTab = pSelUnion->GetTable();
        const SwRect &rUnion = pSelUnion->GetUnion();

        SwRectFnSet aRectFnSet(pTab);
        bool bRTL = pTab->IsRightToLeft();

        const SwLayoutFrame *pCell = pTab->FirstCell();
        if (!pCell)
            continue;
        do
        {
            if ( pCell->IsCellFrame() && pCell->FindTabFrame() == pTab && ::IsFrameInTableSel( rUnion, pCell ) )
            {
                const tools::Long nCLeft  = aRectFnSet.GetLeft(pCell->getFrameArea());
                const tools::Long nCRight = aRectFnSet.GetRight(pCell->getFrameArea());

                bool bNotInCols = true;

                for ( size_t i = 0; i <= rCols.Count(); ++i )
                {
                    sal_uInt16 nFit = rToFill[i];
                    tools::Long nColLeft  = i == 0             ? rCols.GetLeft()  : rCols[i-1];
                    tools::Long nColRight = i == rCols.Count() ? rCols.GetRight() : rCols[i];

                    if ( bRTL )
                    {
                        tools::Long nTmpRight = nColRight;
                        nColRight = rCols.GetRight() - nColLeft;
                        nColLeft = rCols.GetRight() - nTmpRight;
                    }

                    nColLeft  += rCols.GetLeftMin();
                    nColRight += rCols.GetLeftMin();

                    // Adapt values to the proportions of the Table (Follows)
                    tools::Long nLeftA  = nColLeft;
                    tools::Long nRightA = nColRight;
                    if ( rCols.GetLeftMin() !=  sal_uInt16(aRectFnSet.GetLeft(pTab->getFrameArea())) )
                    {
                        const tools::Long nDiff = aRectFnSet.GetLeft(pTab->getFrameArea()) - rCols.GetLeftMin();
                        nLeftA  += nDiff;
                        nRightA += nDiff;
                    }

                    // We don't want to take a too close look
                    if ( ::IsSame(nCLeft, nLeftA) && ::IsSame(nCRight, nRightA))
                    {
                        bNotInCols = false;
                        if ( bWishValues )
                        {
                            const sal_uInt16 nWish = ::lcl_CalcCellFit( pCell );
                            if ( nWish > nFit )
                                nFit = nWish;
                        }
                        else
                        {   const sal_uInt16 nMin = MINLAY + sal_uInt16(pCell->getFrameArea().Width() -
                                                                pCell->getFramePrintArea().Width());
                            if ( !nFit || nMin < nFit )
                                nFit = nMin;
                        }
                        if ( rToFill[i] < nFit )
                            rToFill[i] = nFit;
                    }
                }
                if ( bNotInCols )
                    ::lcl_CalcSubColValues( rToFill, rCols, pCell, pTab, bWishValues );
            }
            do {
                pCell = pCell->GetNextLayoutLeaf();
            } while( pCell && pCell->getFrameArea().Width() == 0 );
        } while ( pCell && pTab->IsAnLower( pCell ) );
    }
}

void SwDoc::AdjustCellWidth( const SwCursor& rCursor,
                             const bool bBalance,
                             const bool bNoShrink )
{
    // Check whether the current Cursor has it's Point/Mark in a Table
    SwContentNode* pCntNd = rCursor.GetPoint()->GetNode().GetContentNode();
    SwTableNode* pTableNd = pCntNd ? pCntNd->FindTableNode() : nullptr;
    if( !pTableNd )
        return ;

    SwLayoutFrame *pStart, *pEnd;
    ::lcl_GetStartEndCell( rCursor, pStart, pEnd );

    // Collect TabCols; we reset the Table with them
    SwFrame* pBoxFrame = pStart;
    while( pBoxFrame && !pBoxFrame->IsCellFrame() )
        pBoxFrame = pBoxFrame->GetUpper();

    if ( !pBoxFrame )
        return// Robust

    SwTabCols aTabCols;
    GetTabCols( aTabCols, static_cast<SwCellFrame*>(pBoxFrame) );

    if ( ! aTabCols.Count() )
        return;

    std::vector<sal_uInt16> aWish(aTabCols.Count() + 1);
    std::vector<sal_uInt16> aMins(aTabCols.Count() + 1);

    ::lcl_CalcColValues( aWish, aTabCols, pStart, pEnd, /*bWishValues=*/true );

    // It's more robust if we calculate the minimum values for the whole Table
    const SwTabFrame *pTab = pStart->ImplFindTabFrame();
    pStart = const_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame const *>(pTab->FirstCell()));
    pEnd   = const_cast<SwLayoutFrame*>(pTab->FindLastContentOrTable()->GetUpper());
    while( !pEnd->IsCellFrame() )
        pEnd = pEnd->GetUpper();
    ::lcl_CalcColValues( aMins, aTabCols, pStart, pEnd, /*bWishValues=*/false );

    sal_uInt16 nSelectedWidth = 0, nCols = 0;
    float fTotalWish = 0;
    if ( bBalance || bNoShrink )
    {
        // Find the combined size of the selected columns
        for ( size_t i = 0; i <= aTabCols.Count(); ++i )
        {
            if ( aWish[i] )
            {
                if ( i == 0 )
                    nSelectedWidth += aTabCols[i] - aTabCols.GetLeft();
                else if ( i == aTabCols.Count() )
                    nSelectedWidth += aTabCols.GetRight() - aTabCols[i-1];
                else
                    nSelectedWidth += aTabCols[i] - aTabCols[i-1];
                ++nCols;
            }
            fTotalWish += aWish[i];
        }
        // bBalance: Distribute the width evenly
        if (bBalance)
        {
            assert(nCols);
            const sal_uInt16 nEqualWidth = nCols ? nSelectedWidth / nCols : 0;
            for (sal_uInt16 & rn : aWish)
                if (rn)
                    rn = nEqualWidth;
        }
    }

    const tools::Long nOldRight = aTabCols.GetRight();

    // In order to make the implementation easier, but still use the available
    // space properly, we do this twice.

    // The problem: The first column is getting wider, the others get slimmer
    // only afterwards.
    // The first column's desired width would be discarded as it would cause
    // the Table's width to exceed the maximum width.
    const tools::Long nMaxRight = std::max(aTabCols.GetRightMax(), nOldRight);
    const sal_uInt16 nEqualWidth = (nMaxRight - aTabCols.GetLeft()) / (aTabCols.Count() + 1);
    const sal_Int16 nTablePadding = nSelectedWidth - fTotalWish;
    for ( int k = 0; k < 2; ++k )
    {
        for ( size_t i = 0; i <= aTabCols.Count(); ++i )
        {
            // bNoShrink: distribute excess space proportionately on pass 2.
            if ( bNoShrink && k && nTablePadding > 0 && fTotalWish > 0 )
                aWish[i] += round( aWish[i] / fTotalWish * nTablePadding );

            // First pass is primarily a shrink pass. Give all columns a chance
            //    to grow by requesting the maximum width as "balanced".
            // Second pass is a first-come, first-served chance to max out.
            int nDiff = k ? aWish[i] : std::min(aWish[i], nEqualWidth);
            if ( nDiff )
            {
                int nMin = aMins[i];
                if ( nMin > nDiff )
                    nDiff = nMin;

                if ( i == 0 )
                {
                    if( aTabCols.Count() )
                        nDiff -= aTabCols[0] - aTabCols.GetLeft();
                    else
                        nDiff -= aTabCols.GetRight() - aTabCols.GetLeft();
                }
                else if ( i == aTabCols.Count() )
                    nDiff -= aTabCols.GetRight() - aTabCols[i-1];
                else
                    nDiff -= aTabCols[i] - aTabCols[i-1];

                tools::Long nTabRight = aTabCols.GetRight() + nDiff;

                // If the Table would become (or is already) too wide,
                // restrict the column growth to the allowed maximum.
                if (!bBalance && nTabRight > nMaxRight)
                {
                    const tools::Long nTmpD = nTabRight - nMaxRight;
                    nDiff     -= nTmpD;
                    nTabRight -= nTmpD;
                }

                // all the remaining columns need to be shifted by the same amount
                for ( size_t i2 = i; i2 < aTabCols.Count(); ++i2 )
                    aTabCols[i2] += nDiff;
                aTabCols.SetRight( nTabRight );
            }
        }
    }

    const tools::Long nNewRight = aTabCols.GetRight();

    SwFrameFormat *pFormat = pTableNd->GetTable().GetFrameFormat();
    const sal_Int16 nOriHori = pFormat->GetHoriOrient().GetHoriOrient();

    // We can leave the "real" work to the SwTable now
    SetTabCols( aTabCols, falsestatic_cast<SwCellFrame*>(pBoxFrame) );

    // Alignment might have been changed in SetTabCols; restore old value
    const SwFormatHoriOrient &rHori = pFormat->GetHoriOrient();
    SwFormatHoriOrient aHori( rHori );
    if ( aHori.GetHoriOrient() != nOriHori )
    {
        aHori.SetHoriOrient( nOriHori );
        pFormat->SetFormatAttr( aHori );
    }

    // We switch to left-adjusted for automatic width
    // We adjust the right border for Border attributes
    if( !bBalance && nNewRight < nOldRight )
    {
        if( aHori.GetHoriOrient() == text::HoriOrientation::FULL )
        {
            aHori.SetHoriOrient( text::HoriOrientation::LEFT );
            pFormat->SetFormatAttr( aHori );
        }
    }

    getIDocumentState().SetModified();
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=95 H=88 G=91

¤ Dauer der Verarbeitung: 0.9 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge