/* -*- 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 .
*/
void DelBoxNode( SwTableSortBoxes const & rSortCntBoxes )
{ for (size_t n = 0; n < rSortCntBoxes.size(); ++n)
{
rSortCntBoxes[ n ]->m_pStartNode = nullptr;
}
}
SwTable::~SwTable()
{ if( m_xRefObj.is() )
{
SwDoc& rDoc = GetFrameFormat()->GetDoc(); if( !rDoc.IsInDtor() ) // then remove from the list
rDoc.getIDocumentLinksAdministration().GetLinkManager().RemoveServer( m_xRefObj.get() );
m_xRefObj->Closed();
}
// the table can be deleted if it's the last client of the FrameFormat
SwTableFormat* pFormat = GetFrameFormat();
pFormat->Remove(*this); // remove
if( !pFormat->HasWriterListeners() )
pFormat->GetDoc().DelTableFrameFormat( pFormat ); // and delete
// Delete the pointers from the SortArray of the boxes. The objects // are preserved and are deleted by the lines/boxes arrays dtor. // Note: unfortunately not enough, pointers to the StartNode of the // section need deletion.
DelBoxNode(m_TabSortContentBoxes);
m_TabSortContentBoxes.clear();
}
// The value for the left edge of the box is calculated from the // widths of the previous boxes.
tools::Long nPos = 0;
tools::Long nLeftMin = 0;
tools::Long nRightMax = 0; if (nWish != 0) //fdo#33012 0 width frmfmt
{
SwTwips nSum = 0; const SwTableBox *pCur = pBox; const SwTableLine *pLine = pBox->GetUpper(); const tools::Long nAct = rToFill.GetRight() - rToFill.GetLeft(); // +1 why?
while ( pLine )
{ const SwTableBoxes &rBoxes = pLine->GetTabBoxes(); for ( size_t i = 0; i < rBoxes.size(); ++i )
{ const SwTwips nWidth = rBoxes[i]->GetFrameFormat()->GetFrameSize().GetWidth();
nSum += nWidth; const tools::Long nTmp = lcl_MulDiv64<tools::Long>(nSum, nAct, nWish);
void SwTable::GetTabCols( SwTabCols &rToFill, const SwTableBox *pStart, bool bRefreshHidden, bool bCurRowOnly ) const
{ // Optimization: if bHidden is set, we only update the Hidden Array. if ( bRefreshHidden )
{ // remove corrections for ( size_t i = 0; i < rToFill.Count(); ++i )
{
SwTabColsEntry& rEntry = rToFill.GetEntry( i );
rEntry.nPos -= rToFill.GetLeft();
rEntry.nMin -= rToFill.GetLeft();
rEntry.nMax -= rToFill.GetLeft();
}
// All are hidden, so add the visible ones. for ( size_t i = 0; i < rToFill.Count(); ++i )
rToFill.SetHidden( i, true );
} else
{
rToFill.Remove( 0, rToFill.Count() );
}
// Insertion cases: // 1. All boxes which are inferior to Line which is superior to the Start, // as well as their inferior boxes if present. // 2. Starting from the Line, the superior box plus its neighbours; but no inferiors. // 3. Apply 2. to the Line superior to the chain of boxes, // until the Line's superior is not a box but the table. // Only those boxes are inserted that don't contain further rows. The insertion // function takes care to avoid duplicates. In order to achieve this, we work // with some degree of fuzzyness (to avoid rounding errors). // Only the left edge of the boxes are inserted. // Finally, the first entry is removed again, because it's already // covered by the border. // 4. Scan the table again and insert _all_ boxes, this time as hidden.
for ( size_t i = 0; i < rBoxes.size(); ++i )
::lcl_ProcessBoxGet( rBoxes[i], rToFill, pTabFormat, bRefreshHidden );
// 2. and 3. const SwTableLine *pLine = pStart->GetUpper()->GetUpper() ?
pStart->GetUpper()->GetUpper()->GetUpper() : nullptr; while ( pLine )
{ const SwTableBoxes &rBoxes2 = pLine->GetTabBoxes(); for ( size_t k = 0; k < rBoxes2.size(); ++k )
::lcl_SortedTabColInsert( rToFill, rBoxes2[k],
pTabFormat, false, bRefreshHidden );
pLine = pLine->GetUpper() ? pLine->GetUpper()->GetUpper() : nullptr;
}
if ( !bRefreshHidden )
{ // 4. if ( !bCurRowOnly )
{ for ( size_t i = 0; i < m_aLines.size(); ++i )
::lcl_ProcessLineGet( m_aLines[i], rToFill, pTabFormat );
}
rToFill.Remove( 0 );
}
// Now the coordinates are relative to the left table border - i.e. // relative to SwTabCols.nLeft. However, they are expected // relative to the left document border, i.e. SwTabCols.nLeftMin. // So all values need to be extended by nLeft. for ( size_t i = 0; i < rToFill.Count(); ++i )
{
SwTabColsEntry& rEntry = rToFill.GetEntry( i );
rEntry.nPos += rToFill.GetLeft();
rEntry.nMin += rToFill.GetLeft();
rEntry.nMax += rToFill.GetLeft();
}
}
staticvoid lcl_ProcessLine( SwTableLine *pLine, Parm &rParm )
{
SwTableBoxes &rBoxes = pLine->GetTabBoxes(); for ( size_t i = rBoxes.size(); i > 0; )
{
--i;
::lcl_ProcessBoxSet( rBoxes[i], rParm );
}
}
staticvoid lcl_ProcessBoxSet( SwTableBox *pBox, Parm &rParm )
{ if ( !pBox->GetTabLines().empty() )
{
SwTableLines &rLines = pBox->GetTabLines(); for ( size_t i = rLines.size(); i > 0; )
{
--i;
lcl_ProcessLine( rLines[i], rParm );
}
} else
{ // Search the old TabCols for the current position (calculate from // left and right edge). Adjust the box if the values differ from // the new TabCols. If the adjusted edge has no neighbour we also // adjust all superior boxes.
// The value for the left edge of the box is calculated from the // widths of the previous boxes plus the left edge.
tools::Long nLeft = rParm.rOld.GetLeft(); const SwTableBox *pCur = pBox; const SwTableLine *pLine = pBox->GetUpper();
while ( pLine )
{ const SwTableBoxes &rBoxes = pLine->GetTabBoxes(); for ( size_t i = 0; (i < rBoxes.size()) && (rBoxes[i] != pCur); ++i)
{
nLeft += lcl_MulDiv64<tools::Long>(
rBoxes[i]->GetFrameFormat()->GetFrameSize().GetWidth(),
nOldAct, rParm.nOldWish);
}
pCur = pLine->GetUpper();
pLine = pCur ? pCur->GetUpper() : nullptr;
}
tools::Long nLeftDiff = 0;
tools::Long nRightDiff = 0; if ( nLeft != rParm.rOld.GetLeft() ) // There are still boxes before this.
{ // Right edge is left edge plus width. const tools::Long nWidth = lcl_MulDiv64<tools::Long>(
pBox->GetFrameFormat()->GetFrameSize().GetWidth(),
nOldAct, rParm.nOldWish); const tools::Long nRight = nLeft + nWidth;
size_t nLeftPos = 0;
size_t nRightPos = 0; bool bFoundLeftPos = false; bool bFoundRightPos = false; for ( size_t i = 0; i < rParm.rOld.Count(); ++i )
{ if ( nLeft >= (rParm.rOld[i] - COLFUZZY) &&
nLeft <= (rParm.rOld[i] + COLFUZZY) )
{
nLeftPos = i;
bFoundLeftPos = true;
} elseif ( nRight >= (rParm.rOld[i] - COLFUZZY) &&
nRight <= (rParm.rOld[i] + COLFUZZY) )
{
nRightPos = i;
bFoundRightPos = true;
}
}
nLeftDiff = bFoundLeftPos ?
rParm.rOld[nLeftPos] - rParm.rNew[nLeftPos] : 0;
nRightDiff= bFoundRightPos ?
rParm.rNew[nRightPos] - rParm.rOld[nRightPos] : 0;
} else// The first box.
{
nLeftDiff = rParm.rOld.GetLeft() - rParm.rNew.GetLeft(); if ( rParm.rOld.Count() )
{ // Calculate the difference to the edge touching the first box. const tools::Long nWidth = lcl_MulDiv64<tools::Long>(
pBox->GetFrameFormat()->GetFrameSize().GetWidth(),
nOldAct, rParm.nOldWish); const tools::Long nTmp = nWidth + rParm.rOld.GetLeft(); for ( size_t i = 0; i < rParm.rOld.Count(); ++i )
{ if ( nTmp >= (rParm.rOld[i] - COLFUZZY) &&
nTmp <= (rParm.rOld[i] + COLFUZZY) )
{
nRightDiff = rParm.rNew[i] - rParm.rOld[i]; break;
}
}
}
}
if ( nLeftDiff || nRightDiff )
{ // The difference is the actual difference amount. For stretched // tables, it does not make sense to adjust the attributes of the // boxes by this amount. The difference amount needs to be converted // accordingly.
tools::Long nTmp = rParm.rNew.GetRight() - rParm.rNew.GetLeft(); // +1 why?
nLeftDiff *= rParm.nNewWish;
nLeftDiff /= nTmp;
nRightDiff *= rParm.nNewWish;
nRightDiff /= nTmp;
tools::Long nDiff = nLeftDiff + nRightDiff;
// Adjust the box and all superiors by the difference amount. while ( pBox )
{
SwFormatFrameSize aFormatFrameSize( pBox->GetFrameFormat()->GetFrameSize() );
aFormatFrameSize.SetWidth( aFormatFrameSize.GetWidth() + nDiff ); if ( aFormatFrameSize.GetWidth() < 0 )
aFormatFrameSize.SetWidth( -aFormatFrameSize.GetWidth() );
rParm.aShareFormats.SetSize( *pBox, aFormatFrameSize );
// The outer cells of the last row are responsible to adjust a surrounding cell. // Last line check: if ( pBox->GetUpper()->GetUpper() &&
pBox->GetUpper() != pBox->GetUpper()->GetUpper()->GetTabLines().back())
{
pBox = nullptr;
} else
{ // Middle cell check: if ( pBox != pBox->GetUpper()->GetTabBoxes().front() )
nDiff = nRightDiff;
if ( pBox != pBox->GetUpper()->GetTabBoxes().back() )
nDiff -= nRightDiff;
// FME: Made rOld const. The caller is responsible for passing correct // values of rOld. Therefore we do not have to call GetTabCols anymore: //GetTabCols( rOld, pStart );
Parm aParm( rNew, rOld );
OSL_ENSURE( rOld.Count() == rNew.Count(), "Number of columns changed.");
// Convert the edges. We need to adjust the size of the table and some boxes. // For the size adjustment, we must not make use of the Modify, since that'd // adjust all boxes, which we really don't want.
SwFrameFormat *pFormat = GetFrameFormat();
aParm.nOldWish = aParm.nNewWish = pFormat->GetFrameSize().GetWidth(); if ( (rOld.GetLeft() != rNew.GetLeft()) ||
(rOld.GetRight()!= rNew.GetRight()) )
{
LockModify();
{
SvxLRSpaceItem aLR( pFormat->GetLRSpace() );
SvxShadowItem aSh( pFormat->GetShadow() );
// Adjust the size of the table, watch out for stretched tables. if ( nTabDiff )
{
aParm.nNewWish += nTabDiff; if ( aParm.nNewWish < 0 )
aParm.nNewWish = USHRT_MAX; // Oops! Have to roll back.
SwFormatFrameSize aSz( pFormat->GetFrameSize() ); if ( aSz.GetWidth() != aParm.nNewWish )
{
aSz.SetWidth( aParm.nNewWish );
aSz.SetWidthPercent( 0 );
pFormat->SetFormatAttr( aSz );
}
}
UnlockModify();
}
if( IsNewModel() )
NewSetTabCols( aParm, rNew, rOld, pStart, bCurRowOnly ); else
{ if ( bCurRowOnly )
{ // To adjust the current row, we need to process all its boxes, // similar to the filling of the TabCols (see GetTabCols()). // Unfortunately we again have to take care to adjust the boxes // from back to front, respectively from outer to inner. // The best way to achieve this is probably to track the boxes // in a PtrArray. const SwTableBoxes &rBoxes = pStart->GetUpper()->GetTabBoxes(); for ( size_t i = 0; i < rBoxes.size(); ++i )
::lcl_ProcessBoxPtr( rBoxes[i], aParm.aBoxArr, false );
const SwTableLine *pLine = pStart->GetUpper()->GetUpper() ?
pStart->GetUpper()->GetUpper()->GetUpper() : nullptr; const SwTableBox *pExcl = pStart->GetUpper()->GetUpper(); while ( pLine )
{ const SwTableBoxes &rBoxes2 = pLine->GetTabBoxes(); bool bBefore = true; for ( size_t i = 0; i < rBoxes2.size(); ++i )
{ if ( rBoxes2[i] != pExcl )
::lcl_ProcessBoxPtr( rBoxes2[i], aParm.aBoxArr, bBefore ); else
bBefore = false;
}
pExcl = pLine->GetUpper();
pLine = pLine->GetUpper() ? pLine->GetUpper()->GetUpper() : nullptr;
} // After we've inserted a bunch of boxes (hopefully all and in // correct order), we just need to process them in reverse order. for ( int j = aParm.aBoxArr.size()-1; j >= 0; --j )
{
SwTableBox *pBox = aParm.aBoxArr[j];
::lcl_ProcessBoxSet( pBox, aParm );
}
} else
{ // Adjusting the entire table is 'easy'. All boxes without lines are // adjusted, as are their superiors. Of course we need to process // in reverse order to prevent fooling ourselves!
SwTableLines &rLines = GetTabLines(); for ( size_t i = rLines.size(); i > 0; )
{
--i;
::lcl_ProcessLine( rLines[i], aParm );
}
}
}
// check if the box found has any contents if( pBox && !pBox->GetSttNd() )
{
OSL_FAIL( "Box without content, looking for the next one!" ); // "drop this" until the first box while( !pBox->GetTabLines().empty() )
pBox = pBox->GetTabLines().front()->GetTabBoxes().front();
} return pBox;
}
SwTableBox* SwTable::GetTableBox( SwNodeOffset nSttIdx )
{ // For optimizations, don't always process the entire SortArray. // Converting text to table, tries certain conditions // to ask for a table box of a table that is not yet having a format if(!GetFrameFormat()) return nullptr;
SwTableBox* pRet = nullptr;
SwNodes& rNds = GetFrameFormat()->GetDoc().GetNodes();
SwNodeOffset nIndex = nSttIdx + 1;
SwContentNode* pCNd = nullptr;
SwTableNode* pTableNd = nullptr;
while ( nIndex < rNds.Count() )
{
pTableNd = rNds[ nIndex ]->GetTableNode(); if ( pTableNd ) break;
pCNd = rNds[ nIndex ]->GetContentNode(); if ( pCNd ) break;
++nIndex;
}
if ( pCNd || pTableNd )
{
sw::BroadcastingModify* pModify = pCNd; // #144862# Better handling of table in table if ( pTableNd && pTableNd->GetTable().GetFrameFormat() )
pModify = pTableNd->GetTable().GetFrameFormat();
SwFrame* pFrame = pModify ? SwIterator<SwFrame,sw::BroadcastingModify>(*pModify).First() : nullptr; while ( pFrame && !pFrame->IsCellFrame() )
pFrame = pFrame->GetUpper(); if ( pFrame )
pRet = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox());
}
// In case the layout doesn't exist yet or anything else goes wrong. if ( !pRet )
{ for (size_t n = m_TabSortContentBoxes.size(); n; )
{ if (m_TabSortContentBoxes[ --n ]->GetSttIdx() == nSttIdx)
{ return m_TabSortContentBoxes[ n ];
}
}
} return pRet;
}
bool SwTable::IsTableComplex() const
{ // Returns true for complex tables, i.e. tables that contain nestings, // like containing boxes not part of the first line, e.g. results of // splits/merges which lead to more complex structures. for (size_t n = 0; n < m_TabSortContentBoxes.size(); ++n)
{ if (m_TabSortContentBoxes[ n ]->GetUpper()->GetUpper())
{ returntrue;
}
} returnfalse;
}
SwTableLine::~SwTableLine()
{ for (size_t i = 0; i < m_aBoxes.size(); ++i)
{ delete m_aBoxes[i];
} // the TabelleLine can be deleted if it's the last client of the FrameFormat
sw::BroadcastingModify* pMod = GetFrameFormat();
pMod->Remove(*this); // remove, if( !pMod->HasWriterListeners() ) delete pMod; // and delete
}
SwTableLineFormat* SwTableLine::ClaimFrameFormat()
{ // This method makes sure that this object is an exclusive SwTableLine client // of an SwTableLineFormat object // If other SwTableLine objects currently listen to the same SwTableLineFormat as // this one, something needs to be done
SwTableLineFormat *pRet = GetFrameFormat();
SwIterator<SwTableLine,SwFormat> aIter( *pRet ); for( SwTableLine* pLast = aIter.First(); pLast; pLast = aIter.Next() )
{ if ( pLast != this )
{ // found another SwTableLine that is a client of the current Format // create a new Format as a copy and use it for this object
SwTableLineFormat *pNewFormat = pRet->GetDoc().MakeTableLineFormat();
*pNewFormat = *pRet;
// register SwRowFrames that know me as clients at the new Format
SwIterator<SwRowFrame,SwFormat> aFrameIter( *pRet ); for( SwRowFrame* pFrame = aFrameIter.First(); pFrame; pFrame = aFrameIter.Next() ) if( pFrame->GetTabLine() == this )
pFrame->RegisterToFormat( *pNewFormat );
// register myself
pNewFormat->Add(*this);
pRet = pNewFormat; break;
}
}
SwTwips SwTableLine::GetTableLineHeight( bool& bLayoutAvailable ) const
{
SwTwips nRet = 0;
bLayoutAvailable = false;
SwIterator<SwRowFrame,SwFormat> aIter( *GetFrameFormat() ); // A row could appear several times in headers/footers so only one chain of master/follow tables // will be accepted... const SwTabFrame* pChain = nullptr; // My chain for( SwRowFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() )
{ if (pLast->GetTabLine() != this) continue;
const SwTabFrame* pTab = pLast->FindTabFrame(); if (!pTab) continue;
// The first one defines the chain, if a chain is defined, only members of the chain // will be added. if (!pChain || pChain->IsAnFollow( pTab ) || pTab->IsAnFollow(pChain))
{
pChain = pTab; // defines my chain (even it is already) if( pTab->IsVertical() )
nRet += pLast->getFrameArea().Width(); else
nRet += pLast->getFrameArea().Height(); // Optimization, if there are no master/follows in my chain, nothing more to add if( !pTab->HasFollow() && !pTab->IsFollow() ) break; // This is not an optimization, this is necessary to avoid double additions of // repeating rows if( pTab->IsInHeadline(*pLast) ) break;
}
} return nRet;
}
bool SwTableLine::IsEmpty() const
{ for (size_t i = 0; i < m_aBoxes.size(); ++i)
{ if ( !m_aBoxes[i]->IsEmpty() ) returnfalse;
} returntrue;
}
bool SwTable::IsEmpty() const
{ for (size_t i = 0; i < m_aLines.size(); ++i)
{ if ( !m_aLines[i]->IsEmpty() ) returnfalse;
} returntrue;
}
SwRedlineTable::size_type nRedlinePos = 0; for (size_t i = 0; i < m_aLines.size(); ++i)
{ // has a deleted row if ( m_aLines[i]->IsDeleted(nRedlinePos) ) returntrue;
// has a deleted cell in the not deleted row
SwTableBoxes& rBoxes = m_aLines[i]->GetTabBoxes(); for( size_t j = 0; j < rBoxes.size(); ++j )
{ if ( RedlineType::Delete == rBoxes[j]->GetRedlineType() ) returntrue;
}
} returnfalse;
}
void SwTable::UpdateFields(TableFormulaUpdateFlags eFlags)
{
SwDoc& rDoc = GetFrameFormat()->GetDoc(); auto pFieldType = rDoc.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Table, OUString(), false); if(!pFieldType) return;
std::vector<SwFormatField*> vFields;
pFieldType->GatherFields(vFields); for(auto pFormatField : vFields)
{
SwTableField* pField = static_cast<SwTableField*>(pFormatField->GetField()); // table where this field is located const SwTableNode* pTableNd; const SwTextNode& rTextNd = pFormatField->GetTextField()->GetTextNode();
pTableNd = rTextNd.FindTableNode(); if(pTableNd == nullptr || &pTableNd->GetTable() != this) continue;
switch(eFlags)
{ case TBL_BOXNAME: // to the external representation
pField->PtrToBoxNm(this); break; case TBL_RELBOXNAME: // to the relative representation
pField->ToRelBoxNm(this); break; case TBL_BOXPTR: // to the internal representation // JP 17.06.96: internal representation on all formulas // (reference to other table!!!)
pField->BoxNmToPtr( &pTableNd->GetTable() ); break; default:
assert(false); // Only TBL_BOXNAME, TBL_RELBOXNAME and TBL_BOXPTR are supported break;
}
}
// process all table box formulas
SwTableLines& rTableLines = GetTabLines(); for (SwTableLine* pTableLine : rTableLines)
{
SwTableBoxes& rTableBoxes = pTableLine->GetTabBoxes(); for (SwTableBox* pTableBox : rTableBoxes)
{
SwTableBoxFormat* pTableBoxFormat = pTableBox->GetFrameFormat(); if (const SwTableBoxFormula* pItem = pTableBoxFormat->GetItemIfSet( RES_BOXATR_FORMULA, false ))
{ // SwTableBoxFormula is non-shareable, so const_cast is somewhat OK auto & rBoxFormula = const_cast<SwTableBoxFormula&>(*pItem); if(eFlags == TBL_BOXPTR)
rBoxFormula.TryBoxNmToPtr(); elseif(eFlags == TBL_RELBOXNAME)
rBoxFormula.TryRelBoxNm(); else
rBoxFormula.ChangeState();
}
}
}
}
// TODO Set HasTextChangesOnly=true, if needed based on the redlines in the cells. // At tracked row deletion, return with the newest deletion of the row or // at tracked row insertion, return with the oldest insertion in the row, which // contain the change data of the row change. // If the return value is SwRedlineTable::npos, there is no tracked row change.
SwRedlineTable::size_type SwTableLine::UpdateTextChangesOnly(
SwRedlineTable::size_type& rRedlinePos, bool bUpdateProperty ) const
{
SwRedlineTable::size_type nRet = SwRedlineTable::npos; const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc().getIDocumentRedlineAccess().GetRedlineTable();
// check table row property "HasTextChangesOnly", if it's defined and its // value is false, and all text content is in delete redlines, the row is deleted const SvxPrintItem *pHasTextChangesOnlyProp =
GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT); if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
{ const SwTableBoxes & rBoxes = GetTabBoxes();
size_t nBoxes = rBoxes.size(); bool bInsertion = false; bool bPlainTextInLine = false;
SwRedlineTable::size_type nOldestRedline = SwRedlineTable::npos;
SwRedlineTable::size_type nNewestRedline = SwRedlineTable::npos; for (size_t nBoxIndex = 0; nBoxIndex < nBoxes && rRedlinePos < aRedlineTable.size(); ++nBoxIndex)
{ auto pBox = rBoxes[nBoxIndex]; if ( pBox->IsEmpty( /*bWithRemainingNestedTable =*/ false ) )
{ // no text content, check the next cells continue;
}
if ( pRedline->Start()->GetNodeIndex() > pEndNodeIndex.GetIndex() )
{ // no more redlines in the actual cell, // check the next ones break;
}
// redline in the cell if ( aCellStart <= *pRedline->Start() )
{ if ( !bHasRedlineInBox )
{
bHasRedlineInBox = true; // plain text before the first redline in the text if ( pRedline->Start()->GetContentIndex() > 0 )
bPlainTextInLine = true;
}
RedlineType nType = pRedline->GetType();
// first insert redline if ( !bInsertion )
{ if ( RedlineType::Insert == nType )
{
bInsertion = true;
} else
{ // plain text between the delete redlines if ( pPreviousDeleteRedline &&
*pPreviousDeleteRedline->End() < *pRedline->Start() && // in the same section, i.e. not in a nested table
pPreviousDeleteRedline->End()->nNode.GetNode().StartOfSectionNode() ==
pRedline->Start()->nNode.GetNode().StartOfSectionNode() )
{
bPlainTextInLine = true;
}
pPreviousDeleteRedline = const_cast<SwRangeRedline*>(pRedline);
}
}
// there is text content outside of redlines: not a deletion if ( !bInsertion && ( !bHasRedlineInBox || ( pPreviousDeleteRedline && // in the same cell, i.e. not in a nested table
pPreviousDeleteRedline->End()->nNode.GetNode().StartOfSectionNode() ==
aCellEnd.GetNode().StartOfSectionNode() &&
( pPreviousDeleteRedline->End()->GetNode() < aCellEnd.GetNode() ||
pPreviousDeleteRedline->End()->GetContentIndex() <
aCellEnd.GetNode().GetContentNode()->Len() ) ) ) )
{
bPlainTextInLine = true; // not deleted cell content: the row is not empty // maybe insertion of a row, try to search it
bInsertion = true;
}
}
// choose return redline, if it exists or remove changed row attribute if ( bInsertion && SwRedlineTable::npos != nOldestRedline &&
RedlineType::Insert == aRedlineTable[ nOldestRedline ]->GetType() )
{ // there is an insert redline, which is the oldest redline in the row
nRet = nOldestRedline;
} elseif ( !bInsertion && !bPlainTextInLine && SwRedlineTable::npos != nNewestRedline &&
RedlineType::Delete == aRedlineTable[ nNewestRedline ]->GetType() )
{ // there is a delete redline, which is the newest redline in the row, // and no text outside of redlines, and no insert redline in the row, // i.e. whole text content is deleted
nRet = nNewestRedline;
} else
{ // no longer tracked row insertion or deletion
nRet = SwRedlineTable::npos; // set TextChangesOnly = true to remove the tracked deletion // FIXME Undo is not supported here (this is only a fallback, // because using SetRowNotTracked() is not recommended here) if ( bUpdateProperty )
{
SvxPrintItem aUnsetTracking(RES_PRINT, true);
SwFrameFormat *pFormat = const_cast<SwTableLine*>(this)->ClaimFrameFormat();
pFormat->LockModify();
pFormat->SetFormatAttr( aUnsetTracking );
pFormat->UnlockModify();
}
}
}
// cache the result const_cast<SwTableLine*>(this)->SetRedlineType( SwRedlineTable::npos == nRet
? RedlineType::None
: aRedlineTable[ nRet ]->GetType());
bool SwTableLine::IsDeleted(SwRedlineTable::size_type& rRedlinePos) const
{ // if not a deleted row, check the deleted columns if ( !IsTracked(rRedlinePos, /*bOnlyDeleted=*/true) )
{ const SwTableBoxes& rBoxes = GetTabBoxes(); for( size_t i = 0; i < rBoxes.size(); ++i )
{ // there is a not deleted column if ( rBoxes[i]->GetRedlineType() != RedlineType::Delete ) returnfalse;
}
}
// check table row property "HasTextChangesOnly", if it's defined and its value is // false, return with the cached redline type, if it exists, otherwise calculate it const SvxPrintItem *pHasTextChangesOnlyProp =
GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT); if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
{ if ( RedlineType::None != m_eRedlineType ) return m_eRedlineType;
// is the whole table part of a changed text
SwRedlineTable::size_type nTableRedline = GetTableRedline(); if ( nTableRedline != SwRedlineTable::npos ) return aRedlineTable[nTableRedline]->GetType();
// insert into the table const SwTableNode* pTableNd = m_pStartNode->FindTableNode();
assert(pTableNd && "In which table is that box?");
SwTableSortBoxes& rSrtArr = const_cast<SwTableSortBoxes&>(pTableNd->GetTable().
GetTabSortBoxes());
SwTableBox* p = this; // error: &this
rSrtArr.insert( p ); // insert
}
// insert into the table const SwTableNode* pTableNd = m_pStartNode->FindTableNode();
assert(pTableNd && "In which table is the box?");
SwTableSortBoxes& rSrtArr = const_cast<SwTableSortBoxes&>(pTableNd->GetTable().
GetTabSortBoxes());
SwTableBox* p = this; // error: &this
rSrtArr.insert( p ); // insert
}
void SwTableBox::RemoveFromTable()
{ if (m_pStartNode) // box containing contents?
{ // remove from table const SwTableNode* pTableNd = m_pStartNode->FindTableNode();
assert(pTableNd && "In which table is that box?");
SwTableSortBoxes& rSrtArr = const_cast<SwTableSortBoxes&>(pTableNd->GetTable().
GetTabSortBoxes());
SwTableBox *p = this; // error: &this
rSrtArr.erase( p ); // remove
m_pStartNode = nullptr; // clear it so this is only run once
}
}
SwTableBox::~SwTableBox()
{ if (!GetFrameFormat()->GetDoc().IsInDtor())
{
RemoveFromTable();
}
// the TabelleBox can be deleted if it's the last client of the FrameFormat
sw::BroadcastingModify* pMod = GetFrameFormat();
pMod->Remove(*this); // remove, if( !pMod->HasWriterListeners() ) delete pMod; // and delete
}
SwTableBoxFormat* SwTableBox::CheckBoxFormat( SwTableBoxFormat* pFormat )
{ // We might need to create a new format here, because the box must be // added to the format solely if pFormat has a value or form. if( SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_VALUE, false ) ||
SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_FORMULA, false ) )
{
SwTableBox* pOther = SwIterator<SwTableBox,SwFormat>( *pFormat ).First(); if( pOther )
{
SwTableBoxFormat* pNewFormat = pFormat->GetDoc().MakeTableBoxFormat();
pNewFormat->LockModify();
*pNewFormat = *pFormat;
// Remove values and formulas
pNewFormat->ResetFormatAttr( RES_BOXATR_FORMULA, RES_BOXATR_VALUE );
pNewFormat->UnlockModify();
pFormat = pNewFormat;
}
} return pFormat;
}
SwTableBoxFormat* SwTableBox::ClaimFrameFormat()
{ // This method makes sure that this object is an exclusive SwTableBox client // of an SwTableBoxFormat object // If other SwTableBox objects currently listen to the same SwTableBoxFormat as // this one, something needs to be done
SwTableBoxFormat *pRet = GetFrameFormat();
SwIterator<SwTableBox,SwFormat> aIter( *pRet ); for( SwTableBox* pLast = aIter.First(); pLast; pLast = aIter.Next() )
{ if ( pLast != this )
{ // Found another SwTableBox object // create a new Format as a copy and assign me to it // don't copy values and formulas
SwTableBoxFormat* pNewFormat = pRet->GetDoc().MakeTableBoxFormat();
pNewFormat->LockModify();
*pNewFormat = *pRet;
pNewFormat->ResetFormatAttr( RES_BOXATR_FORMULA, RES_BOXATR_VALUE );
pNewFormat->UnlockModify();
// re-register SwCellFrame objects that know me
SwIterator<SwCellFrame,SwFormat> aFrameIter( *pRet ); for( SwCellFrame* pCell = aFrameIter.First(); pCell; pCell = aFrameIter.Next() ) if( pCell->GetTabBox() == this )
pCell->RegisterToFormat( *pNewFormat );
void SwTableBox::ChgFrameFormat(SwTableBoxFormat* pNewFormat, bool bNeedToReregister)
{
SwFrameFormat* pOld = GetFrameFormat(); // tdf#84635 We set bNeedToReregister=false to avoid a quadratic slowdown on loading large tables, // and since we are creating the table for the first time, no re-registration is necessary. // First, re-register the Frames. if(bNeedToReregister)
pOld->CallSwClientNotify(sw::TableBoxFormatChanged(*pNewFormat, *this)); // Now, re-register self.
pNewFormat->Add(*this); if(!pOld->HasWriterListeners()) delete pOld;
}
// Return the name of this box. This is determined dynamically // resulting from the position in the lines/boxes/tables. void sw_GetTableBoxColStr( sal_uInt16 nCol, OUString& rNm )
{ const sal_uInt16 coDiff = 52; // 'A'-'Z' 'a' - 'z'
if( ( pNewUserColor && pOldNumFormatColor &&
*pNewUserColor == *pOldNumFormatColor ) ||
( !pNewUserColor && !pOldNumFormatColor ))
{ // Keep the user color, set updated values, delete old NumFormatColor if needed if( pCol ) // if needed, set the color
pTNd->SetAttr( SvxColorItem( *pCol, RES_CHRATR_COLOR )); elseif( pColorItem )
{
pNewUserColor = rBox.GetSaveUserColor(); if( pNewUserColor )
pTNd->SetAttr( SvxColorItem( *pNewUserColor, RES_CHRATR_COLOR )); else
pTNd->ResetAttr( RES_CHRATR_COLOR );
}
} else
{ // Save user color, set NumFormat color if needed, but never reset the color
rBox.SetSaveUserColor( pNewUserColor ? *pNewUserColor : std::optional<Color>() );
if( pCol ) // if needed, set the color
pTNd->SetAttr( SvxColorItem( *pCol, RES_CHRATR_COLOR ));
if( ( pNewUserColor && pOldNumFormatColor &&
*pNewUserColor == *pOldNumFormatColor ) ||
( !pNewUserColor && !pOldNumFormatColor ))
{ // Keep the user color, set updated values, delete old NumFormatColor if needed if( pCol ) // if needed, set the color
pTNd->SetAttr( SvxColorItem( *pCol, RES_CHRATR_COLOR )); elseif( pColorItem )
{
pNewUserColor = rBox.GetSaveUserColor(); if( pNewUserColor )
pTNd->SetAttr( SvxColorItem( *pNewUserColor, RES_CHRATR_COLOR )); else
pTNd->ResetAttr( RES_CHRATR_COLOR );
}
} else
{ // Save user color, set NumFormat color if needed, but never reset the color
rBox.SetSaveUserColor( pNewUserColor );
if( pCol ) // if needed, set the color
pTNd->SetAttr( SvxColorItem( *pCol, RES_CHRATR_COLOR ));
} void SwTableBoxFormat::BoxAttributeChanged(SwTableBox& rBox, const SwTableBoxNumFormat* pNewFormat, const SwTableBoxFormula* pNewFormula, const SwTableBoxValue* pNewValue, sal_uLong nOldFormat)
{
sal_uLong nNewFormat; if(pNewFormat)
{
nNewFormat = pNewFormat->GetValue(); // new formatting // is it newer or has the current been removed? if( SfxItemState::SET != GetItemState(RES_BOXATR_VALUE, false))
pNewFormat = nullptr;
} else
{ // fetch the current Item
pNewFormat = GetItemIfSet(RES_BOXATR_FORMAT, false);
nOldFormat = GetTableBoxNumFormat().GetValue();
nNewFormat = pNewFormat ? pNewFormat->GetValue() : nOldFormat;
}
// is it newer or has the current been removed? if(pNewValue)
{ if(GetDoc().GetNumberFormatter()->IsTextFormat(nNewFormat))
nOldFormat = 0; else
{ if(SfxItemState::SET == GetItemState(RES_BOXATR_VALUE, false))
nOldFormat = getSwDefaultTextFormat(); else
nNewFormat = getSwDefaultTextFormat();
}
}
// Logic: // Value change: -> "simulate" a format change! // Format change: // Text -> !Text or format change: // - align right for horizontal alignment, if LEFT or JUSTIFIED // - align bottom for vertical alignment, if TOP is set, or default // - replace text (color? negative numbers RED?) // !Text -> Text: // - align left for horizontal alignment, if RIGHT // - align top for vertical alignment, if BOTTOM is set
SvNumberFormatter* pNumFormatr = GetDoc().GetNumberFormatter(); bool bNewIsTextFormat = pNumFormatr->IsTextFormat(nNewFormat);
if((!bNewIsTextFormat && nOldFormat != nNewFormat) || pNewFormula)
{ bool bIsNumFormat = false;
OUString aOrigText; bool bChgText = true; double fVal = 0; if(!pNewValue)
pNewValue = GetItemIfSet(RES_BOXATR_VALUE, false); if(!pNewValue)
{ // so far, no value has been set, so try to evaluate the content
SwNodeOffset nNdPos = rBox.IsValidNumTextNd(); if(NODE_OFFSET_MAX != nNdPos)
{
sal_uInt32 nTmpFormatIdx = nNewFormat;
OUString aText(GetDoc().GetNodes()[nNdPos] ->GetTextNode()->GetRedlineText());
aOrigText = aText; if(aText.isEmpty())
bChgText = false; else
{ // Keep Tabs
lcl_TabToBlankAtSttEnd(aText);
// format contents with the new value assigned and write to paragraph const Color* pCol = nullptr;
OUString sNewText; bool bChangeFormat = true; if(DBL_MAX == fVal)
{
sNewText = SwViewShell::GetShellRes()->aCalc_Error;
} else
{ if(bIsNumFormat)
pNumFormatr->GetOutputString(fVal, nNewFormat, sNewText, &pCol); else
{ // Original text could not be parsed as // number/date/time/..., so keep the text. #if 0 // Actually the text should be formatted // according to the format, which may include // additional text from the format, for example // in {0;-0;"BAD: "@}. But other places when // entering a new value or changing text or // changing to a different format of type Text // don't do this (yet?).
pNumFormatr->GetOutputString(aOrigText, nNewFormat, sNewText, &pCol); #else
sNewText = aOrigText; #endif // Remove the newly assigned numbering format as well if text actually exists. // Exception: assume user-defined formats are always intentional. if (bChgText && pNumFormatr->IsTextFormat(nOldFormat)
&& !pNumFormatr->IsUserDefined(nNewFormat))
{
rBox.GetFrameFormat()->ResetFormatAttr(RES_BOXATR_FORMAT);
bChangeFormat = false;
}
}
if(!bChgText)
sNewText.clear();
}
// across all boxes if (bChangeFormat)
ChgTextToNum(rBox, sNewText, pCol, GetDoc().IsInsTableAlignNum());
SwTableBox* SwTableBoxFormat::SwTableBoxFormat::GetTableBox()
{
SwIterator<SwTableBox,SwFormat> aIter(*this); auto pBox = aIter.First();
SAL_INFO_IF(!pBox, "sw.core", "no box found at format");
SAL_WARN_IF(pBox && aIter.Next(), "sw.core", "more than one box found at format"); return pBox;
}
// something changed and some BoxAttribute remained in the set! if( pNewFormat || pNewFormula || pNewVal )
{
GetDoc().getIDocumentFieldsAccess().SetFieldsDirty(true, nullptr, SwNodeOffset(0));
if ( aRedlineTable.empty() || !pSttNd ) return SwRedlineTable::npos;
// check table row property "HasTextChangesOnly", if it's defined and its value is // false, return with the first redline of the cell const SvxPrintItem *pHasTextChangesOnlyProp =
GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT); if ( !pHasTextChangesOnlyProp || pHasTextChangesOnlyProp->GetValue() ) return SwRedlineTable::npos;
bool SwTable::HasLayout() const
{ const SwFrameFormat* pFrameFormat = GetFrameFormat(); //a table in a clipboard document doesn't have any layout information return pFrameFormat && SwIterator<SwTabFrame,SwFormat>(*pFrameFormat).First();
}
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.