1768 lines
64 KiB
C++
1768 lines
64 KiB
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& rCursor, bool 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()) ) );
|
|
|
|
for( auto 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()) ) );
|
|
for( auto 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()) ) );
|
|
|
|
for( auto 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* pDoc = aRowArr[0]->GetFrameFormat()->GetDoc();
|
|
const IDocumentRedlineAccess& rIDRA = pDoc->getIDocumentRedlineAccess();
|
|
|
|
for( auto 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;
|
|
for( auto 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* pDoc = pLn->GetFrameFormat()->GetDoc();
|
|
IDocumentRedlineAccess& rIDRA = pDoc->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);
|
|
|
|
for( auto &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, false, static_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: */
|