/* -*- 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 .
*/
// 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;
// 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;
}
}
}
}
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.
*/
// has a table row, which is not a tracked deletion bool SwDoc::HasRowNotTracked( const SwCursor& rCursor )
{
SwTableNode* pTableNd = rCursor.GetPoint()->GetNode().FindTableNode(); if( !pTableNd ) returnfalse;
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 returntrue;
// 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() ) returntrue;
}
} returnfalse;
}
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& 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 );
}
}
staticvoid 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 ) );
}
// 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 )
{ constbool bVert = pTab->IsVertical(); constbool 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;
// Top Border if ( bFirst && bTopOver )
{ if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::TOP))
{ if ( !bTopSet )
{ bTopSet = true;
aSetBox.SetLine( rBox.GetTop(), SvxBoxItemLine::TOP );
} elseif ((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 );
}
}
}
// 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.
*/ staticvoid 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());
/** * 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.
*/ staticvoid 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 );
// 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 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 );
}
}
}
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.