/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DBG_UTIL #define CHECK_TABLE(t) (t).CheckConsistency(); #else #define CHECK_TABLE(t) #endif using namespace com::sun::star; #define COLFUZZY 20 static void ChgTextToNum( SwTableBox& rBox, const OUString& rText, const Color* pCol, bool bChgAlign, sal_uLong nNdPos ); inline const Color* SwTableBox::GetSaveUserColor() const { return mpUserColor.get(); } inline const Color* SwTableBox::GetSaveNumFormatColor() const { return mpNumFormatColor.get(); } inline void SwTableBox::SetSaveUserColor(const Color* p ) { if (p) mpUserColor.reset(new Color(*p)); else mpUserColor.reset(); } inline void SwTableBox::SetSaveNumFormatColor( const Color* p ) { if (p) mpNumFormatColor.reset(new Color(*p)); else mpNumFormatColor.reset(); } long SwTableBox::getRowSpan() const { return mnRowSpan; } void SwTableBox::setRowSpan( long nNewRowSpan ) { mnRowSpan = nNewRowSpan; } bool SwTableBox::getDummyFlag() const { return mbDummyFlag; } void SwTableBox::setDummyFlag( bool bDummy ) { mbDummyFlag = bDummy; } //JP 15.09.98: Bug 55741 - Keep tabs (front and rear) static OUString& lcl_TabToBlankAtSttEnd( OUString& rText ) { sal_Unicode c; sal_Int32 n; for( n = 0; n < rText.getLength() && ' ' >= ( c = rText[n] ); ++n ) if( '\x9' == c ) rText = rText.replaceAt( n, 1, " " ); for( n = rText.getLength(); n && ' ' >= ( c = rText[--n] ); ) if( '\x9' == c ) rText = rText.replaceAt( n, 1, " " ); return rText; } static OUString& lcl_DelTabsAtSttEnd( OUString& rText ) { sal_Unicode c; sal_Int32 n; OUStringBuffer sBuff(rText); for( n = 0; n < sBuff.getLength() && ' ' >= ( c = sBuff[ n ]); ++n ) { if( '\x9' == c ) sBuff.remove( n--, 1 ); } for( n = sBuff.getLength(); n && ' ' >= ( c = sBuff[ --n ]); ) { if( '\x9' == c ) sBuff.remove( n, 1 ); } rText = sBuff.makeStringAndClear(); return rText; } void InsTableBox( SwDoc* pDoc, SwTableNode* pTableNd, SwTableLine* pLine, SwTableBoxFormat* pBoxFrameFormat, SwTableBox* pBox, sal_uInt16 nInsPos, sal_uInt16 nCnt ) { OSL_ENSURE( pBox->GetSttNd(), "Box with no start node" ); SwNodeIndex aIdx( *pBox->GetSttNd(), +1 ); SwContentNode* pCNd = aIdx.GetNode().GetContentNode(); if( !pCNd ) pCNd = pDoc->GetNodes().GoNext( &aIdx ); OSL_ENSURE( pCNd, "Box with no content node" ); if( pCNd->IsTextNode() ) { if( pBox->GetSaveNumFormatColor() && pCNd->GetpSwAttrSet() ) { SwAttrSet aAttrSet( *pCNd->GetpSwAttrSet() ); if( pBox->GetSaveUserColor() ) aAttrSet.Put( SvxColorItem( *pBox->GetSaveUserColor(), RES_CHRATR_COLOR )); else aAttrSet.ClearItem( RES_CHRATR_COLOR ); pDoc->GetNodes().InsBoxen( pTableNd, pLine, pBoxFrameFormat, static_cast(pCNd)->GetTextColl(), &aAttrSet, nInsPos, nCnt ); } else pDoc->GetNodes().InsBoxen( pTableNd, pLine, pBoxFrameFormat, static_cast(pCNd)->GetTextColl(), pCNd->GetpSwAttrSet(), nInsPos, nCnt ); } else pDoc->GetNodes().InsBoxen( pTableNd, pLine, pBoxFrameFormat, pDoc->GetDfltTextFormatColl(), nullptr, nInsPos, nCnt ); long nRowSpan = pBox->getRowSpan(); if( nRowSpan != 1 ) { SwTableBoxes& rTableBoxes = pLine->GetTabBoxes(); for( sal_uInt16 i = 0; i < nCnt; ++i ) { pBox = rTableBoxes[ i + nInsPos ]; pBox->setRowSpan( nRowSpan ); } } } SwTable::SwTable() : SwClient( nullptr ), m_pTableNode( nullptr ), m_nGraphicsThatResize( 0 ), m_nRowsToRepeat( 1 ), m_bModifyLocked( false ), m_bNewModel( true ) { // default value set in the options m_eTableChgMode = GetTableChgDefaultMode(); } SwTable::SwTable( const SwTable& rTable ) : SwClient( rTable.GetFrameFormat() ), m_pTableNode( nullptr ), m_eTableChgMode( rTable.m_eTableChgMode ), m_nGraphicsThatResize( 0 ), m_nRowsToRepeat( rTable.GetRowsToRepeat() ), maTableStyleName(rTable.maTableStyleName), m_bModifyLocked( false ), m_bNewModel( rTable.m_bNewModel ) { } 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* pDoc = GetFrameFormat()->GetDoc(); if( !pDoc->IsInDtor() ) // then remove from the list pDoc->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(); } namespace { template T lcl_MulDiv64(sal_uInt64 nA, sal_uInt64 nM, sal_uInt64 nD) { return static_cast((nA*nM)/nD); } } static void FormatInArr( std::vector& rFormatArr, SwFormat* pBoxFormat ) { std::vector::const_iterator it = std::find( rFormatArr.begin(), rFormatArr.end(), pBoxFormat ); if ( it == rFormatArr.end() ) rFormatArr.push_back( pBoxFormat ); } static void lcl_ModifyBoxes( SwTableBoxes &rBoxes, const long nOld, const long nNew, std::vector& rFormatArr ); static void lcl_ModifyLines( SwTableLines &rLines, const long nOld, const long nNew, std::vector& rFormatArr, const bool bCheckSum ) { for ( size_t i = 0; i < rLines.size(); ++i ) ::lcl_ModifyBoxes( rLines[i]->GetTabBoxes(), nOld, nNew, rFormatArr ); if( bCheckSum ) { for(SwFormat* pFormat : rFormatArr) { const SwTwips nBox = lcl_MulDiv64(pFormat->GetFrameSize().GetWidth(), nNew, nOld); SwFormatFrameSize aNewBox( SwFrameSize::Variable, nBox, 0 ); pFormat->LockModify(); pFormat->SetFormatAttr( aNewBox ); pFormat->UnlockModify(); } } } static void lcl_ModifyBoxes( SwTableBoxes &rBoxes, const long nOld, const long nNew, std::vector& rFormatArr ) { sal_uInt64 nSum = 0; // To avoid rounding errors we summarize all box widths sal_uInt64 nOriginalSum = 0; // Sum of original widths for ( size_t i = 0; i < rBoxes.size(); ++i ) { SwTableBox &rBox = *rBoxes[i]; if ( !rBox.GetTabLines().empty() ) { // For SubTables the rounding problem will not be solved :-( ::lcl_ModifyLines( rBox.GetTabLines(), nOld, nNew, rFormatArr, false ); } // Adjust the box SwFrameFormat *pFormat = rBox.GetFrameFormat(); sal_uInt64 nBox = pFormat->GetFrameSize().GetWidth(); nOriginalSum += nBox; nBox *= nNew; nBox /= nOld; const sal_uInt64 nWishedSum = lcl_MulDiv64(nOriginalSum, nNew, nOld) - nSum; if( nWishedSum > 0 ) { if( nBox == nWishedSum ) FormatInArr( rFormatArr, pFormat ); else { nBox = nWishedSum; pFormat = rBox.ClaimFrameFormat(); SwFormatFrameSize aNewBox( SwFrameSize::Variable, static_cast< SwTwips >(nBox), 0 ); pFormat->LockModify(); pFormat->SetFormatAttr( aNewBox ); pFormat->UnlockModify(); } } else { OSL_FAIL( "Rounding error" ); } nSum += nBox; } } void SwTable::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew ) { // catch SSize changes, to adjust the lines/boxes const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0 ; const SwFormatFrameSize* pNewSize = nullptr, *pOldSize = nullptr; if( RES_ATTRSET_CHG == nWhich ) { if (pOld && pNew && SfxItemState::SET == static_cast(pNew)->GetChgSet()->GetItemState( RES_FRM_SIZE, false, reinterpret_cast(&pNewSize))) { pOldSize = &static_cast(pOld)->GetChgSet()->GetFrameSize(); } } else if( RES_FRM_SIZE == nWhich ) { pOldSize = static_cast(pOld); pNewSize = static_cast(pNew); } else CheckRegistration( pOld ); if (pOldSize && pNewSize && !m_bModifyLocked) AdjustWidths( pOldSize->GetWidth(), pNewSize->GetWidth() ); } void SwTable::AdjustWidths( const long nOld, const long nNew ) { std::vector aFormatArr; aFormatArr.reserve( m_aLines[0]->GetTabBoxes().size() ); ::lcl_ModifyLines( m_aLines, nOld, nNew, aFormatArr, true ); } static void lcl_RefreshHidden( SwTabCols &rToFill, size_t nPos ) { for ( size_t i = 0; i < rToFill.Count(); ++i ) { if ( std::abs(static_cast(nPos) - rToFill[i]) <= COLFUZZY ) { rToFill.SetHidden( i, false ); break; } } } static void lcl_SortedTabColInsert( SwTabCols &rToFill, const SwTableBox *pBox, const SwFrameFormat *pTabFormat, const bool bHidden, const bool bRefreshHidden ) { const long nWish = pTabFormat->GetFrameSize().GetWidth(); OSL_ENSURE(nWish, "weird <= 0 width frmfrm"); // The value for the left edge of the box is calculated from the // widths of the previous boxes. long nPos = 0; long nLeftMin = 0; long nRightMax = 0; if (nWish != 0) //fdo#33012 0 width frmfmt { SwTwips nSum = 0; const SwTableBox *pCur = pBox; const SwTableLine *pLine = pBox->GetUpper(); const 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 long nTmp = lcl_MulDiv64(nSum, nAct, nWish); if (rBoxes[i] != pCur) { if ( pLine == pBox->GetUpper() || 0 == nLeftMin ) nLeftMin = nTmp - nPos; nPos = nTmp; } else { nSum -= nWidth; if ( 0 == nRightMax ) nRightMax = nTmp - nPos; break; } } pCur = pLine->GetUpper(); pLine = pCur ? pCur->GetUpper() : nullptr; } } bool bInsert = !bRefreshHidden; for ( size_t j = 0; bInsert && (j < rToFill.Count()); ++j ) { long nCmp = rToFill[j]; if ( (nPos >= ((nCmp >= COLFUZZY) ? nCmp - COLFUZZY : nCmp)) && (nPos <= (nCmp + COLFUZZY)) ) { bInsert = false; // Already has it. } else if ( nPos < nCmp ) { bInsert = false; rToFill.Insert( nPos, bHidden, j ); } } if ( bInsert ) rToFill.Insert( nPos, bHidden, rToFill.Count() ); else if ( bRefreshHidden ) ::lcl_RefreshHidden( rToFill, nPos ); if ( bHidden && !bRefreshHidden ) { // calculate minimum/maximum values for the existing entries: nLeftMin = nPos - nLeftMin; nRightMax = nPos + nRightMax; // check if nPos is entry: bool bFoundPos = false; bool bFoundMax = false; for ( size_t j = 0; !(bFoundPos && bFoundMax ) && j < rToFill.Count(); ++j ) { SwTabColsEntry& rEntry = rToFill.GetEntry( j ); long nCmp = rToFill[j]; if ( (nPos >= ((nCmp >= COLFUZZY) ? nCmp - COLFUZZY : nCmp)) && (nPos <= (nCmp + COLFUZZY)) ) { // check if nLeftMin is > old minimum for entry nPos: const long nOldMin = rEntry.nMin; if ( nLeftMin > nOldMin ) rEntry.nMin = nLeftMin; // check if nRightMin is < old maximum for entry nPos: const long nOldMax = rEntry.nMax; if ( nRightMax < nOldMax ) rEntry.nMax = nRightMax; bFoundPos = true; } else if ( (nRightMax >= ((nCmp >= COLFUZZY) ? nCmp - COLFUZZY : nCmp)) && (nRightMax <= (nCmp + COLFUZZY)) ) { // check if nPos is > old minimum for entry nRightMax: const long nOldMin = rEntry.nMin; if ( nPos > nOldMin ) rEntry.nMin = nPos; bFoundMax = true; } } } } static void lcl_ProcessBoxGet( const SwTableBox *pBox, SwTabCols &rToFill, const SwFrameFormat *pTabFormat, bool bRefreshHidden ) { if ( !pBox->GetTabLines().empty() ) { const SwTableLines &rLines = pBox->GetTabLines(); for ( size_t i = 0; i < rLines.size(); ++i ) { const SwTableBoxes &rBoxes = rLines[i]->GetTabBoxes(); for ( size_t j = 0; j < rBoxes.size(); ++j ) ::lcl_ProcessBoxGet( rBoxes[j], rToFill, pTabFormat, bRefreshHidden); } } else ::lcl_SortedTabColInsert( rToFill, pBox, pTabFormat, false, bRefreshHidden ); } static void lcl_ProcessLineGet( const SwTableLine *pLine, SwTabCols &rToFill, const SwFrameFormat *pTabFormat ) { for ( size_t i = 0; i < pLine->GetTabBoxes().size(); ++i ) { const SwTableBox *pBox = pLine->GetTabBoxes()[i]; if ( pBox->GetSttNd() ) ::lcl_SortedTabColInsert( rToFill, pBox, pTabFormat, true, false ); else for ( size_t j = 0; j < pBox->GetTabLines().size(); ++j ) ::lcl_ProcessLineGet( pBox->GetTabLines()[j], rToFill, pTabFormat ); } } 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. const SwFrameFormat *pTabFormat = GetFrameFormat(); // 1. const SwTableBoxes &rBoxes = pStart->GetUpper()->GetTabBoxes(); 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(); } } // Structure for parameter passing struct Parm { const SwTabCols &rNew; const SwTabCols &rOld; long nNewWish, nOldWish; std::deque aBoxArr; SwShareBoxFormats aShareFormats; Parm( const SwTabCols &rN, const SwTabCols &rO ) : rNew( rN ), rOld( rO ), nNewWish(0), nOldWish(0) {} }; static void lcl_ProcessBoxSet( SwTableBox *pBox, Parm &rParm ); static void lcl_ProcessLine( SwTableLine *pLine, Parm &rParm ) { SwTableBoxes &rBoxes = pLine->GetTabBoxes(); for ( size_t i = rBoxes.size(); i > 0; ) { --i; ::lcl_ProcessBoxSet( rBoxes[i], rParm ); } } static void 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. const long nOldAct = rParm.rOld.GetRight() - rParm.rOld.GetLeft(); // +1 why? // The value for the left edge of the box is calculated from the // widths of the previous boxes plus the left edge. 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( rBoxes[i]->GetFrameFormat()->GetFrameSize().GetWidth(), nOldAct, rParm.nOldWish); } pCur = pLine->GetUpper(); pLine = pCur ? pCur->GetUpper() : nullptr; } long nLeftDiff = 0; long nRightDiff = 0; if ( nLeft != rParm.rOld.GetLeft() ) // There are still boxes before this. { // Right edge is left edge plus width. const long nWidth = lcl_MulDiv64( pBox->GetFrameFormat()->GetFrameSize().GetWidth(), nOldAct, rParm.nOldWish); const 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; } else if ( 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 long nWidth = lcl_MulDiv64( pBox->GetFrameFormat()->GetFrameSize().GetWidth(), nOldAct, rParm.nOldWish); const 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( pBox->getRowSpan() == 1 ) { const sal_uInt16 nPos = pBox->GetUpper()->GetBoxPos( pBox ); SwTableBoxes& rTableBoxes = pBox->GetUpper()->GetTabBoxes(); if( nPos && rTableBoxes[ nPos - 1 ]->getRowSpan() != 1 ) nLeftDiff = 0; if( nPos + 1 < static_cast(rTableBoxes.size()) && rTableBoxes[ nPos + 1 ]->getRowSpan() != 1 ) nRightDiff = 0; } else nLeftDiff = nRightDiff = 0; 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. long nTmp = rParm.rNew.GetRight() - rParm.rNew.GetLeft(); // +1 why? nLeftDiff *= rParm.nNewWish; nLeftDiff /= nTmp; nRightDiff *= rParm.nNewWish; nRightDiff /= nTmp; 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; pBox = nDiff ? pBox->GetUpper()->GetUpper() : nullptr; } } } } } static void lcl_ProcessBoxPtr( SwTableBox *pBox, std::deque &rBoxArr, bool bBefore ) { if ( !pBox->GetTabLines().empty() ) { const SwTableLines &rLines = pBox->GetTabLines(); for ( size_t i = 0; i < rLines.size(); ++i ) { const SwTableBoxes &rBoxes = rLines[i]->GetTabBoxes(); for ( size_t j = 0; j < rBoxes.size(); ++j ) ::lcl_ProcessBoxPtr( rBoxes[j], rBoxArr, bBefore ); } } else if ( bBefore ) rBoxArr.push_front( pBox ); else rBoxArr.push_back( pBox ); } static void lcl_AdjustBox( SwTableBox *pBox, const long nDiff, Parm &rParm ); static void lcl_AdjustLines( SwTableLines &rLines, const long nDiff, Parm &rParm ) { for ( size_t i = 0; i < rLines.size(); ++i ) { SwTableBox *pBox = rLines[i]->GetTabBoxes() [rLines[i]->GetTabBoxes().size()-1]; lcl_AdjustBox( pBox, nDiff, rParm ); } } static void lcl_AdjustBox( SwTableBox *pBox, const long nDiff, Parm &rParm ) { if ( !pBox->GetTabLines().empty() ) ::lcl_AdjustLines( pBox->GetTabLines(), nDiff, rParm ); // Adjust the size of the box. SwFormatFrameSize aFormatFrameSize( pBox->GetFrameFormat()->GetFrameSize() ); aFormatFrameSize.SetWidth( aFormatFrameSize.GetWidth() + nDiff ); rParm.aShareFormats.SetSize( *pBox, aFormatFrameSize ); } void SwTable::SetTabCols( const SwTabCols &rNew, const SwTabCols &rOld, const SwTableBox *pStart, bool bCurRowOnly ) { CHECK_TABLE( *this ) SetHTMLTableLayout(std::shared_ptr()); // delete HTML-Layout // 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() ); SwTwips nShRight = aSh.CalcShadowSpace( SvxShadowItemSide::RIGHT ); SwTwips nShLeft = aSh.CalcShadowSpace( SvxShadowItemSide::LEFT ); aLR.SetLeft ( rNew.GetLeft() - nShLeft ); aLR.SetRight( rNew.GetRightMax() - rNew.GetRight() - nShRight ); pFormat->SetFormatAttr( aLR ); // The alignment of the table needs to be adjusted accordingly. // This is done by preserving the exact positions that have been // set by the user. SwFormatHoriOrient aOri( pFormat->GetHoriOrient() ); if(text::HoriOrientation::NONE != aOri.GetHoriOrient()) { const bool bLeftDist = rNew.GetLeft() != nShLeft; const bool bRightDist = rNew.GetRight() + nShRight != rNew.GetRightMax(); if(!bLeftDist && !bRightDist) aOri.SetHoriOrient( text::HoriOrientation::FULL ); else if(!bRightDist && rNew.GetLeft() > nShLeft ) aOri.SetHoriOrient( text::HoriOrientation::RIGHT ); else if(!bLeftDist && rNew.GetRight() + nShRight < rNew.GetRightMax()) aOri.SetHoriOrient( text::HoriOrientation::LEFT ); else aOri.SetHoriOrient( text::HoriOrientation::LEFT_AND_WIDTH ); } pFormat->SetFormatAttr( aOri ); } const long nAct = rOld.GetRight() - rOld.GetLeft(); // +1 why? long nTabDiff = 0; if ( rOld.GetLeft() != rNew.GetLeft() ) { nTabDiff = rOld.GetLeft() - rNew.GetLeft(); nTabDiff *= aParm.nOldWish; nTabDiff /= nAct; } if ( rOld.GetRight() != rNew.GetRight() ) { long nDiff = rNew.GetRight() - rOld.GetRight(); nDiff *= aParm.nOldWish; nDiff /= nAct; nTabDiff += nDiff; if( !IsNewModel() ) ::lcl_AdjustLines( GetTabLines(), nDiff, aParm ); } // 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 ); } } } #ifdef DBG_UTIL { // do some checking for correct table widths SwTwips nSize = GetFrameFormat()->GetFrameSize().GetWidth(); for (size_t n = 0; n < m_aLines.size(); ++n) { CheckBoxWidth( *m_aLines[ n ], nSize ); } } #endif } typedef std::pair ColChange; typedef std::list< ColChange > ChangeList; static void lcl_AdjustWidthsInLine( SwTableLine* pLine, ChangeList& rOldNew, Parm& rParm, sal_uInt16 nColFuzzy ) { ChangeList::iterator pCurr = rOldNew.begin(); if( pCurr == rOldNew.end() ) return; const size_t nCount = pLine->GetTabBoxes().size(); SwTwips nBorder = 0; SwTwips nRest = 0; for( size_t i = 0; i < nCount; ++i ) { SwTableBox* pBox = pLine->GetTabBoxes()[i]; SwTwips nWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth(); SwTwips nNewWidth = nWidth - nRest; nRest = 0; nBorder += nWidth; if( pCurr != rOldNew.end() && nBorder + nColFuzzy >= pCurr->first ) { nBorder -= nColFuzzy; while( pCurr != rOldNew.end() && nBorder > pCurr->first ) ++pCurr; if( pCurr != rOldNew.end() ) { nBorder += nColFuzzy; if( nBorder + nColFuzzy >= pCurr->first ) { if( pCurr->second == pCurr->first ) nRest = 0; else nRest = pCurr->second - nBorder; nNewWidth += nRest; ++pCurr; } } } if( nNewWidth != nWidth ) { if( nNewWidth < 0 ) { nRest += 1 - nNewWidth; nNewWidth = 1; } SwFormatFrameSize aFormatFrameSize( pBox->GetFrameFormat()->GetFrameSize() ); aFormatFrameSize.SetWidth( nNewWidth ); rParm.aShareFormats.SetSize( *pBox, aFormatFrameSize ); } } } static void lcl_CalcNewWidths( std::list &rSpanPos, ChangeList& rChanges, SwTableLine* pLine, long nWish, long nWidth, bool bTop ) { if( rChanges.empty() ) { rSpanPos.clear(); return; } if( rSpanPos.empty() ) { rChanges.clear(); return; } std::list aNewSpanPos; ChangeList aNewChanges; ChangeList::iterator pCurr = rChanges.begin(); aNewChanges.push_back( *pCurr ); // Nullposition std::list::iterator pSpan = rSpanPos.begin(); sal_uInt16 nCurr = 0; SwTwips nOrgSum = 0; bool bRowSpan = false; sal_uInt16 nRowSpanCount = 0; const size_t nCount = pLine->GetTabBoxes().size(); for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox ) { SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox]; SwTwips nCurrWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth(); const long nRowSpan = pBox->getRowSpan(); const bool bCurrRowSpan = bTop ? nRowSpan < 0 : ( nRowSpan > 1 || nRowSpan < -1 ); if( bRowSpan || bCurrRowSpan ) aNewSpanPos.push_back( nRowSpanCount ); bRowSpan = bCurrRowSpan; nOrgSum += nCurrWidth; const sal_uInt16 nPos = lcl_MulDiv64( lcl_MulDiv64(nOrgSum, nWidth, nWish), nWish, nWidth); while( pCurr != rChanges.end() && pCurr->first < nPos ) { ++nCurr; ++pCurr; } bool bNew = true; if( pCurr != rChanges.end() && pCurr->first <= nPos && pCurr->first != pCurr->second ) { pSpan = std::find_if(pSpan, rSpanPos.end(), [nCurr](const sal_uInt16 nSpan) { return nSpan >= nCurr; }); if( pSpan != rSpanPos.end() && *pSpan == nCurr ) { aNewChanges.push_back( *pCurr ); ++nRowSpanCount; bNew = false; } } if( bNew ) { ColChange aTmp( nPos, nPos ); aNewChanges.push_back( aTmp ); ++nRowSpanCount; } } pCurr = aNewChanges.begin(); ChangeList::iterator pLast = pCurr; ChangeList::iterator pLeftMove = pCurr; while( pCurr != aNewChanges.end() ) { if( pLeftMove == pCurr ) { while( ++pLeftMove != aNewChanges.end() && pLeftMove->first <= pLeftMove->second ) ; } if( pCurr->second == pCurr->first ) { if( pLeftMove != aNewChanges.end() && pCurr->second > pLeftMove->second ) { if( pLeftMove->first == pLast->first ) pCurr->second = pLeftMove->second; else { pCurr->second = lcl_MulDiv64( pCurr->first - pLast->first, pLeftMove->second - pLast->second, pLeftMove->first - pLast->first) + pLast->second; } } pLast = pCurr; ++pCurr; } else if( pCurr->second > pCurr->first ) { pLast = pCurr; ++pCurr; ChangeList::iterator pNext = pCurr; while( pNext != pLeftMove && pNext->second == pNext->first && pNext->second < pLast->second ) ++pNext; while( pCurr != pNext ) { if( pNext == aNewChanges.end() || pNext->first == pLast->first ) pCurr->second = pLast->second; else { pCurr->second = lcl_MulDiv64( pCurr->first - pLast->first, pNext->second - pLast->second, pNext->first - pLast->first) + pLast->second; } ++pCurr; } pLast = pCurr; } else { pLast = pCurr; ++pCurr; } } rChanges.swap(aNewChanges); rSpanPos.swap(aNewSpanPos); } void SwTable::NewSetTabCols( Parm &rParm, const SwTabCols &rNew, const SwTabCols &rOld, const SwTableBox *pStart, bool bCurRowOnly ) { #if OSL_DEBUG_LEVEL > 1 static int nCallCount = 0; ++nCallCount; #endif // First step: evaluate which lines have been moved/which widths changed ChangeList aOldNew; const long nNewWidth = rParm.rNew.GetRight() - rParm.rNew.GetLeft(); const long nOldWidth = rParm.rOld.GetRight() - rParm.rOld.GetLeft(); if( nNewWidth < 1 || nOldWidth < 1 ) return; for( size_t i = 0; i <= rOld.Count(); ++i ) { long nNewPos; long nOldPos; if( i == rOld.Count() ) { nOldPos = rParm.rOld.GetRight() - rParm.rOld.GetLeft(); nNewPos = rParm.rNew.GetRight() - rParm.rNew.GetLeft(); } else { nOldPos = rOld[i] - rParm.rOld.GetLeft(); nNewPos = rNew[i] - rParm.rNew.GetLeft(); } nNewPos = lcl_MulDiv64(nNewPos, rParm.nNewWish, nNewWidth); nOldPos = lcl_MulDiv64(nOldPos, rParm.nOldWish, nOldWidth); if( nOldPos != nNewPos && nNewPos > 0 && nOldPos > 0 ) { ColChange aChg( static_cast(nOldPos), static_cast(nNewPos) ); aOldNew.push_back( aChg ); } } // Finished first step int nCount = aOldNew.size(); if( !nCount ) return; // no change, nothing to do SwTableLines &rLines = GetTabLines(); if( bCurRowOnly ) { const SwTableLine* pCurrLine = pStart->GetUpper(); sal_uInt16 nCurr = rLines.GetPos( pCurrLine ); if( nCurr >= USHRT_MAX ) return; ColChange aChg( 0, 0 ); aOldNew.push_front( aChg ); std::list aRowSpanPos; if( nCurr ) { ChangeList aCopy; sal_uInt16 nPos = 0; for( const auto& rCop : aOldNew ) { aCopy.push_back( rCop ); aRowSpanPos.push_back( nPos++ ); } lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[nCurr], rParm.nOldWish, nOldWidth, true ); bool bGoOn = !aRowSpanPos.empty(); sal_uInt16 j = nCurr; while( bGoOn ) { lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[--j], rParm.nOldWish, nOldWidth, true ); lcl_AdjustWidthsInLine( rLines[j], aCopy, rParm, 0 ); bGoOn = !aRowSpanPos.empty() && j > 0; } aRowSpanPos.clear(); } if( nCurr+1 < static_cast(rLines.size()) ) { ChangeList aCopy; sal_uInt16 nPos = 0; for( const auto& rCop : aOldNew ) { aCopy.push_back( rCop ); aRowSpanPos.push_back( nPos++ ); } lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[nCurr], rParm.nOldWish, nOldWidth, false ); bool bGoOn = !aRowSpanPos.empty(); sal_uInt16 j = nCurr; while( bGoOn ) { lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[++j], rParm.nOldWish, nOldWidth, false ); lcl_AdjustWidthsInLine( rLines[j], aCopy, rParm, 0 ); bGoOn = !aRowSpanPos.empty() && j+1 < static_cast(rLines.size()); } } ::lcl_AdjustWidthsInLine( rLines[nCurr], aOldNew, rParm, COLFUZZY ); } else { for( size_t i = 0; i < rLines.size(); ++i ) ::lcl_AdjustWidthsInLine( rLines[i], aOldNew, rParm, COLFUZZY ); } CHECK_TABLE( *this ) } // return the pointer of the box specified. static bool lcl_IsValidRowName( const OUString& rStr ) { bool bIsValid = true; sal_Int32 nLen = rStr.getLength(); for( sal_Int32 i = 0; i < nLen && bIsValid; ++i ) { const sal_Unicode cChar = rStr[i]; if (cChar < '0' || cChar > '9') bIsValid = false; } return bIsValid; } // #i80314# // add 3rd parameter and its handling sal_uInt16 SwTable::GetBoxNum( OUString& rStr, bool bFirstPart, const bool bPerformValidCheck ) { sal_uInt16 nRet = 0; if( bFirstPart ) // true == column; false == row { sal_Int32 nPos = 0; // the first one uses letters for addressing! bool bFirst = true; sal_uInt32 num = 0; bool overflow = false; while (nPos'Z') && (cChar<'a' || cChar>'z')) break; if( (cChar -= 'A') >= 26 ) cChar -= 'a' - '['; if( bFirst ) bFirst = false; else ++num; num = num * 52 + cChar; if (num > SAL_MAX_UINT16) { overflow = true; } ++nPos; } nRet = overflow ? SAL_MAX_UINT16 : num; rStr = rStr.copy( nPos ); // Remove char from String } else { const sal_Int32 nPos = rStr.indexOf( "." ); if ( nPos<0 ) { nRet = 0; if ( !bPerformValidCheck || lcl_IsValidRowName( rStr ) ) { nRet = static_cast(rStr.toInt32()); } rStr.clear(); } else { nRet = 0; const OUString aText( rStr.copy( 0, nPos ) ); if ( !bPerformValidCheck || lcl_IsValidRowName( aText ) ) { nRet = static_cast(aText.toInt32()); } rStr = rStr.copy( nPos+1 ); } } return nRet; } // #i80314# // add 2nd parameter and its handling const SwTableBox* SwTable::GetTableBox( const OUString& rName, const bool bPerformValidCheck ) const { const SwTableBox* pBox = nullptr; const SwTableLine* pLine; const SwTableLines* pLines; sal_uInt16 nLine, nBox; OUString aNm( rName ); while( !aNm.isEmpty() ) { nBox = SwTable::GetBoxNum( aNm, nullptr == pBox, bPerformValidCheck ); // first box ? if( !pBox ) pLines = &GetTabLines(); else { pLines = &pBox->GetTabLines(); if( nBox ) --nBox; } nLine = SwTable::GetBoxNum( aNm, false, bPerformValidCheck ); // determine line if( !nLine || nLine > pLines->size() ) return nullptr; pLine = (*pLines)[ nLine-1 ]; // determine box const SwTableBoxes* pBoxes = &pLine->GetTabBoxes(); if( nBox >= pBoxes->size() ) return nullptr; pBox = (*pBoxes)[ nBox ]; } // 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( sal_uLong 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(); sal_uLong 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 ) { SwModify* pModify = pCNd; // #144862# Better handling of table in table if ( pTableNd && pTableNd->GetTable().GetFrameFormat() ) pModify = pTableNd->GetTable().GetFrameFormat(); SwFrame* pFrame = pModify ? SwIterator(*pModify).First() : nullptr; while ( pFrame && !pFrame->IsCellFrame() ) pFrame = pFrame->GetUpper(); if ( pFrame ) pRet = const_cast(static_cast(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()) { return true; } } return false; } SwTableLine::SwTableLine( SwTableLineFormat *pFormat, sal_uInt16 nBoxes, SwTableBox *pUp ) : SwClient( pFormat ), m_aBoxes(), m_pUpper( pUp ) { m_aBoxes.reserve( nBoxes ); } 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 SwModify* pMod = GetFrameFormat(); pMod->Remove( this ); // remove, if( !pMod->HasWriterListeners() ) delete pMod; // and delete } SwFrameFormat* 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 = static_cast(GetFrameFormat()); SwIterator 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 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; } } return pRet; } void SwTableLine::ChgFrameFormat( SwTableLineFormat *pNewFormat ) { SwFrameFormat *pOld = GetFrameFormat(); SwIterator aIter( *pOld ); // First, re-register the Frames. for( SwRowFrame* pRow = aIter.First(); pRow; pRow = aIter.Next() ) { if( pRow->GetTabLine() == this ) { pRow->RegisterToFormat( *pNewFormat ); pRow->InvalidateSize(); pRow->InvalidatePrt_(); pRow->SetCompletePaint(); pRow->ReinitializeFrameSizeAttrFlags(); // #i35063# // consider 'split row allowed' attribute SwTabFrame* pTab = pRow->FindTabFrame(); bool bInFollowFlowRow = false; const bool bInFirstNonHeadlineRow = pTab->IsFollow() && pRow == pTab->GetFirstNonHeadlineRow(); if ( bInFirstNonHeadlineRow || !pRow->GetNext() || ( bInFollowFlowRow = pRow->IsInFollowFlowRow() ) || nullptr != pRow->IsInSplitTableRow() ) { if ( bInFirstNonHeadlineRow || bInFollowFlowRow ) pTab = pTab->FindMaster(); pTab->SetRemoveFollowFlowLinePending( true ); pTab->InvalidatePos(); } } } // Now, re-register self. pNewFormat->Add( this ); if ( !pOld->HasWriterListeners() ) delete pOld; } SwTwips SwTableLine::GetTableLineHeight( bool& bLayoutAvailable ) const { SwTwips nRet = 0; bLayoutAvailable = false; SwIterator 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 ) { const SwTabFrame* pTab = pLast->FindTabFrame(); bLayoutAvailable = ( pTab && pTab->IsVertical() ) ? ( 0 < pTab->getFrameArea().Height() ) : ( 0 < pTab->getFrameArea().Width() ); // The first one defines the chain, if a chain is defined, only members of the chain // will be added. if (pTab && (!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; } SwTableBox::SwTableBox( SwTableBoxFormat* pFormat, sal_uInt16 nLines, SwTableLine *pUp ) : SwClient(nullptr) , m_aLines() , m_pStartNode(nullptr) , m_pUpper(pUp) , mnRowSpan(1) , mbDummyFlag(false) , mbDirectFormatting(false) { m_aLines.reserve( nLines ); CheckBoxFormat( pFormat )->Add( this ); } SwTableBox::SwTableBox( SwTableBoxFormat* pFormat, const SwNodeIndex &rIdx, SwTableLine *pUp ) : SwClient(nullptr) , m_aLines() , m_pUpper(pUp) , mnRowSpan(1) , mbDummyFlag(false) , mbDirectFormatting(false) { CheckBoxFormat( pFormat )->Add( this ); m_pStartNode = rIdx.GetNode().GetStartNode(); // insert into the table const SwTableNode* pTableNd = m_pStartNode->FindTableNode(); assert(pTableNd && "In which table is that box?"); SwTableSortBoxes& rSrtArr = const_cast(pTableNd->GetTable(). GetTabSortBoxes()); SwTableBox* p = this; // error: &this rSrtArr.insert( p ); // insert } SwTableBox::SwTableBox( SwTableBoxFormat* pFormat, const SwStartNode& rSttNd, SwTableLine *pUp ) : SwClient(nullptr) , m_aLines() , m_pStartNode(&rSttNd) , m_pUpper(pUp) , mnRowSpan(1) , mbDummyFlag(false) , mbDirectFormatting(false) { CheckBoxFormat( pFormat )->Add( this ); // insert into the table const SwTableNode* pTableNd = m_pStartNode->FindTableNode(); OSL_ENSURE( pTableNd, "In which table is the box?" ); SwTableSortBoxes& rSrtArr = const_cast(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(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 SwModify* 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( *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; } SwFrameFormat* 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 = static_cast(GetFrameFormat()); SwIterator 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 aFrameIter( *pRet ); for( SwCellFrame* pCell = aFrameIter.First(); pCell; pCell = aFrameIter.Next() ) if( pCell->GetTabBox() == this ) pCell->RegisterToFormat( *pNewFormat ); // re-register myself pNewFormat->Add( this ); pRet = pNewFormat; break; } } return pRet; } void SwTableBox::ChgFrameFormat( SwTableBoxFormat* pNewFormat, bool bNeedToReregister ) { SwFrameFormat *pOld = GetFrameFormat(); SwIterator aIter( *pOld ); // 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) for( SwCellFrame* pCell = aIter.First(); pCell; pCell = aIter.Next() ) { if( pCell->GetTabBox() == this ) { pCell->RegisterToFormat( *pNewFormat ); pCell->InvalidateSize(); pCell->InvalidatePrt_(); pCell->SetCompletePaint(); pCell->SetDerivedVert( false ); pCell->CheckDirChange(); // #i47489# // make sure that the row will be formatted, in order // to have the correct Get(Top|Bottom)MarginForLowers values // set at the row. const SwTabFrame* pTab = pCell->FindTabFrame(); if ( pTab && pTab->IsCollapsingBorders() ) { SwFrame* pRow = pCell->GetUpper(); pRow->InvalidateSize_(); pRow->InvalidatePrt_(); } } } // 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' do { const sal_uInt16 nCalc = nCol % coDiff; if( nCalc >= 26 ) rNm = OUStringChar( sal_Unicode('a' - 26 + nCalc) ) + rNm; else rNm = OUStringChar( sal_Unicode('A' + nCalc) ) + rNm; if( 0 == (nCol = nCol - nCalc) ) break; nCol /= coDiff; --nCol; } while( true ); } Point SwTableBox::GetCoordinates() const { if( !m_pStartNode ) // box without content? { // search for the next first box? return Point( 0, 0 ); } const SwTable& rTable = m_pStartNode->FindTableNode()->GetTable(); sal_uInt16 nX, nY; const SwTableBox* pBox = this; do { const SwTableLine* pLine = pBox->GetUpper(); // at the first level? const SwTableLines* pLines = pLine->GetUpper() ? &pLine->GetUpper()->GetTabLines() : &rTable.GetTabLines(); nY = pLines->GetPos( pLine ) + 1 ; nX = pBox->GetUpper()->GetBoxPos( pBox ) + 1; pBox = pLine->GetUpper(); } while( pBox ); return Point( nX, nY ); } OUString SwTableBox::GetName() const { if( !m_pStartNode ) // box without content? { // search for the next first box? return OUString(); } const SwTable& rTable = m_pStartNode->FindTableNode()->GetTable(); sal_uInt16 nPos; OUString sNm, sTmp; const SwTableBox* pBox = this; do { const SwTableLine* pLine = pBox->GetUpper(); // at the first level? const SwTableLines* pLines = pLine->GetUpper() ? &pLine->GetUpper()->GetTabLines() : &rTable.GetTabLines(); nPos = pLines->GetPos( pLine ) + 1; sTmp = OUString::number( nPos ); if( !sNm.isEmpty() ) sNm = sTmp + "." + sNm; else sNm = sTmp; nPos = pBox->GetUpper()->GetBoxPos( pBox ); sTmp = OUString::number(nPos + 1); if( nullptr != ( pBox = pLine->GetUpper()) ) sNm = sTmp + "." + sNm; else sw_GetTableBoxColStr( nPos, sNm ); } while( pBox ); return sNm; } bool SwTableBox::IsInHeadline( const SwTable* pTable ) const { if( !GetUpper() ) // should only happen upon merge. return false; if( !pTable ) pTable = &m_pStartNode->FindTableNode()->GetTable(); const SwTableLine* pLine = GetUpper(); while( pLine->GetUpper() ) pLine = pLine->GetUpper()->GetUpper(); // Headerline? return pTable->GetTabLines()[ 0 ] == pLine; } sal_uLong SwTableBox::GetSttIdx() const { return m_pStartNode ? m_pStartNode->GetIndex() : 0; } // retrieve information from the client bool SwTable::GetInfo( SfxPoolItem& rInfo ) const { switch( rInfo.Which() ) { case RES_AUTOFMT_DOCNODE: { const SwTableNode* pNode = GetTableNode(); if (pNode && &pNode->GetNodes() == static_cast(rInfo).pNodes) { if (!m_TabSortContentBoxes.empty()) { SwNodeIndex aIdx( *m_TabSortContentBoxes[0]->GetSttNd() ); GetFrameFormat()->GetDoc()->GetNodes().GoNext( &aIdx ); } return false; } break; } case RES_FINDNEARESTNODE: if( GetFrameFormat() && GetFrameFormat()->GetFormatAttr( RES_PAGEDESC ).GetPageDesc() && !m_TabSortContentBoxes.empty() && m_TabSortContentBoxes[0]->GetSttNd()->GetNodes().IsDocNodes() ) static_cast(rInfo).CheckNode( * m_TabSortContentBoxes[0]->GetSttNd()->FindTableNode() ); break; case RES_CONTENT_VISIBLE: static_cast(rInfo).pObject = SwIterator( *GetFrameFormat() ).First(); return false; } return true; } SwTable * SwTable::FindTable( SwFrameFormat const*const pFormat ) { return pFormat ? SwIterator(*pFormat).First() : nullptr; } SwTableNode* SwTable::GetTableNode() const { return !GetTabSortBoxes().empty() ? const_cast(GetTabSortBoxes()[ 0 ]->GetSttNd()->FindTableNode()) : m_pTableNode; } void SwTable::SetRefObject( SwServerObject* pObj ) { if( m_xRefObj.is() ) m_xRefObj->Closed(); m_xRefObj = pObj; } void SwTable::SetHTMLTableLayout(std::shared_ptr const& r) { m_xHTMLLayout = r; } static void ChgTextToNum( SwTableBox& rBox, const OUString& rText, const Color* pCol, bool bChgAlign ) { sal_uLong nNdPos = rBox.IsValidNumTextNd(); ChgTextToNum( rBox,rText,pCol,bChgAlign,nNdPos); } void ChgTextToNum( SwTableBox& rBox, const OUString& rText, const Color* pCol, bool bChgAlign,sal_uLong nNdPos ) { if( ULONG_MAX == nNdPos ) return; SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc(); SwTextNode* pTNd = pDoc->GetNodes()[ nNdPos ]->GetTextNode(); const SfxPoolItem* pItem; // assign adjustment if( bChgAlign ) { pItem = &pTNd->SwContentNode::GetAttr( RES_PARATR_ADJUST ); SvxAdjust eAdjust = static_cast(pItem)->GetAdjust(); if( SvxAdjust::Left == eAdjust || SvxAdjust::Block == eAdjust ) { SvxAdjustItem aAdjust( *static_cast(pItem) ); aAdjust.SetAdjust( SvxAdjust::Right ); pTNd->SetAttr( aAdjust ); } } // assign color or save "user color" if( !pTNd->GetpSwAttrSet() || SfxItemState::SET != pTNd->GetpSwAttrSet()-> GetItemState( RES_CHRATR_COLOR, false, &pItem )) pItem = nullptr; const Color* pOldNumFormatColor = rBox.GetSaveNumFormatColor(); const Color* pNewUserColor = pItem ? &static_cast(pItem)->GetValue() : nullptr; 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 )); else if( pItem ) { 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 )); } rBox.SetSaveNumFormatColor( pCol ); if( pTNd->GetText() != rText ) { // Exchange text. Bugfix to keep Tabs (front and back!) and annotations (inword comment anchors) const OUString& rOrig = pTNd->GetText(); sal_Int32 n; for( n = 0; n < rOrig.getLength() && ('\x9' == rOrig[n] || CH_TXTATR_INWORD == rOrig[n]); ++n ) ; for( ; n < rOrig.getLength() && '\x01' == rOrig[n]; ++n ) ; SwIndex aIdx( pTNd, n ); for( n = rOrig.getLength(); n && ('\x9' == rOrig[--n] || CH_TXTATR_INWORD == rOrig[n]); ) ; sal_Int32 nEndPos = n; n -= aIdx.GetIndex() - 1; // Reset DontExpand-Flags before exchange, to retrigger expansion { SwIndex aResetIdx( aIdx, n ); pTNd->DontExpandFormat( aResetIdx, false, false ); } if( !pDoc->getIDocumentRedlineAccess().IsIgnoreRedline() && !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() ) { SwPaM aTemp(*pTNd, 0, *pTNd, rOrig.getLength()); pDoc->getIDocumentRedlineAccess().DeleteRedline(aTemp, true, RedlineType::Any); } // preserve comments inside of the number by deleting number portions starting from the back sal_Int32 nCommentPos = pTNd->GetText().lastIndexOf( CH_TXTATR_INWORD, nEndPos ); while( nCommentPos > aIdx.GetIndex() ) { pTNd->EraseText( SwIndex(pTNd, nCommentPos+1), nEndPos - nCommentPos, SwInsertFlags::EMPTYEXPAND ); // find the next non-sequential comment anchor do { nEndPos = nCommentPos; n = nEndPos - aIdx.GetIndex(); nCommentPos = pTNd->GetText().lastIndexOf( CH_TXTATR_INWORD, nEndPos ); --nEndPos; } while( nCommentPos > aIdx.GetIndex() && nCommentPos == nEndPos ); } pTNd->EraseText( aIdx, n, SwInsertFlags::EMPTYEXPAND ); pTNd->InsertText( rText, aIdx, SwInsertFlags::EMPTYEXPAND ); if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) { SwPaM aTemp(*pTNd, 0, *pTNd, rText.getLength()); pDoc->getIDocumentRedlineAccess().AppendRedline(new SwRangeRedline(RedlineType::Insert, aTemp), true); } } // assign vertical orientation if( bChgAlign && ( SfxItemState::SET != rBox.GetFrameFormat()->GetItemState( RES_VERT_ORIENT, true, &pItem ) || text::VertOrientation::TOP == static_cast(pItem)->GetVertOrient() )) { rBox.GetFrameFormat()->SetFormatAttr( SwFormatVertOrient( 0, text::VertOrientation::BOTTOM )); } } static void ChgNumToText( SwTableBox& rBox, sal_uLong nFormat ) { sal_uLong nNdPos = rBox.IsValidNumTextNd( false ); if( ULONG_MAX == nNdPos ) return; SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc(); SwTextNode* pTNd = pDoc->GetNodes()[ nNdPos ]->GetTextNode(); bool bChgAlign = pDoc->IsInsTableAlignNum(); const SfxPoolItem* pItem; Color* pCol = nullptr; if( getSwDefaultTextFormat() != nFormat ) { // special text format: OUString sTmp; const OUString sText( pTNd->GetText() ); pDoc->GetNumberFormatter()->GetOutputString( sText, nFormat, sTmp, &pCol ); if( sText != sTmp ) { // exchange text SwIndex aIdx( pTNd, sText.getLength() ); // Reset DontExpand-Flags before exchange, to retrigger expansion pTNd->DontExpandFormat( aIdx, false, false ); aIdx = 0; pTNd->EraseText( aIdx, SAL_MAX_INT32, SwInsertFlags::EMPTYEXPAND ); pTNd->InsertText( sTmp, aIdx, SwInsertFlags::EMPTYEXPAND ); } } const SfxItemSet* pAttrSet = pTNd->GetpSwAttrSet(); // assign adjustment if( bChgAlign && pAttrSet && SfxItemState::SET == pAttrSet->GetItemState( RES_PARATR_ADJUST, false, &pItem ) && SvxAdjust::Right == static_cast(pItem)->GetAdjust() ) { pTNd->SetAttr( SvxAdjustItem( SvxAdjust::Left, RES_PARATR_ADJUST ) ); } // assign color or save "user color" if( !pAttrSet || SfxItemState::SET != pAttrSet-> GetItemState( RES_CHRATR_COLOR, false, &pItem )) pItem = nullptr; const Color* pOldNumFormatColor = rBox.GetSaveNumFormatColor(); const Color* pNewUserColor = pItem ? &static_cast(pItem)->GetValue() : nullptr; 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 )); else if( pItem ) { 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 )); } rBox.SetSaveNumFormatColor( pCol ); // assign vertical orientation if( bChgAlign && SfxItemState::SET == rBox.GetFrameFormat()->GetItemState( RES_VERT_ORIENT, false, &pItem ) && text::VertOrientation::BOTTOM == static_cast(pItem)->GetVertOrient() ) { rBox.GetFrameFormat()->SetFormatAttr( SwFormatVertOrient( 0, text::VertOrientation::TOP )); } } // for detection of modifications (mainly TableBoxAttribute) void SwTableBoxFormat::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) { if( !IsModifyLocked() && GetDoc() && !GetDoc()->IsInDtor()) { const SwTableBoxNumFormat *pNewFormat = nullptr; const SwTableBoxFormula *pNewFormula = nullptr; const SwTableBoxValue *pNewVal = nullptr; sal_uLong nOldFormat = getSwDefaultTextFormat(); switch( pNew ? pNew->Which() : 0 ) { case RES_ATTRSET_CHG: { const SfxItemSet& rSet = *static_cast(pNew)->GetChgSet(); if( SfxItemState::SET == rSet.GetItemState( RES_BOXATR_FORMAT, false, reinterpret_cast(&pNewFormat) ) ) nOldFormat = static_cast(pOld)-> GetChgSet()->Get( RES_BOXATR_FORMAT ).GetValue(); rSet.GetItemState( RES_BOXATR_FORMULA, false, reinterpret_cast(&pNewFormula) ); rSet.GetItemState( RES_BOXATR_VALUE, false, reinterpret_cast(&pNewVal) ); break; } case RES_BOXATR_FORMAT: pNewFormat = static_cast(pNew); nOldFormat = static_cast(pOld)->GetValue(); break; case RES_BOXATR_FORMULA: pNewFormula = static_cast(pNew); break; case RES_BOXATR_VALUE: pNewVal = static_cast(pNew); break; } // something changed and some BoxAttribut remained in the set! if( pNewFormat || pNewFormula || pNewVal ) { GetDoc()->getIDocumentFieldsAccess().SetFieldsDirty(true, nullptr, 0); if( SfxItemState::SET == GetItemState( RES_BOXATR_FORMAT, false ) || SfxItemState::SET == GetItemState( RES_BOXATR_VALUE, false ) || SfxItemState::SET == GetItemState( RES_BOXATR_FORMULA, false ) ) { // fetch the box SwIterator aIter( *this ); SwTableBox* pBox = aIter.First(); if( pBox ) { OSL_ENSURE( !aIter.Next(), "zero or more than one box at format" ); 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 (void)GetItemState(RES_BOXATR_FORMAT, false, reinterpret_cast(&pNewFormat)); nOldFormat = GetTableBoxNumFormat().GetValue(); nNewFormat = pNewFormat ? pNewFormat->GetValue() : nOldFormat; } // is it newer or has the current been removed? if( pNewVal ) { 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( !pNewVal && SfxItemState::SET != GetItemState( RES_BOXATR_VALUE, false, reinterpret_cast(&pNewVal) )) { // so far, no value has been set, so try to evaluate the content sal_uLong nNdPos = pBox->IsValidNumTextNd(); if( ULONG_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 ); // JP 22.04.98: Bug 49659 - // Special casing for percent if( SvNumFormatType::PERCENT == pNumFormatr->GetType( nNewFormat )) { sal_uInt32 nTmpFormat = 0; if( GetDoc()->IsNumberFormat( aText, nTmpFormat, fVal )) { if( SvNumFormatType::NUMBER == pNumFormatr->GetType( nTmpFormat )) aText += "%"; bIsNumFormat = GetDoc()->IsNumberFormat( aText, nTmpFormatIdx, fVal ); } } else bIsNumFormat = GetDoc()->IsNumberFormat( aText, nTmpFormatIdx, fVal ); if( bIsNumFormat ) { // directly assign value - without Modify bool bIsLockMod = IsModifyLocked(); LockModify(); SetFormatAttr( SwTableBoxValue( fVal )); if( !bIsLockMod ) UnlockModify(); } } } } else { fVal = pNewVal->GetValue(); bIsNumFormat = true; } // format contents with the new value assigned and write to paragraph Color* pCol = nullptr; OUString sNewText; 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 } if( !bChgText ) { sNewText.clear(); } } // across all boxes ChgTextToNum( *pBox, sNewText, pCol, GetDoc()->IsInsTableAlignNum() ); } else if( bNewIsTextFormat && nOldFormat != nNewFormat ) { ChgNumToText( *pBox, nNewFormat ); } } } } } // call base class SwFrameFormat::Modify( pOld, pNew ); } bool SwTableBoxFormat::supportsFullDrawingLayerFillAttributeSet() const { return false; } bool SwTableFormat::supportsFullDrawingLayerFillAttributeSet() const { return false; } bool SwTableLineFormat::supportsFullDrawingLayerFillAttributeSet() const { return false; } bool SwTableBox::HasNumContent( double& rNum, sal_uInt32& rFormatIndex, bool& rIsEmptyTextNd ) const { bool bRet = false; sal_uLong nNdPos = IsValidNumTextNd(); if( ULONG_MAX != nNdPos ) { OUString aText( m_pStartNode->GetNodes()[ nNdPos ]->GetTextNode()->GetRedlineText() ); // Keep Tabs lcl_TabToBlankAtSttEnd( aText ); rIsEmptyTextNd = aText.isEmpty(); SvNumberFormatter* pNumFormatr = GetFrameFormat()->GetDoc()->GetNumberFormatter(); const SfxPoolItem* pItem; if( SfxItemState::SET == GetFrameFormat()->GetItemState( RES_BOXATR_FORMAT, false, &pItem )) { rFormatIndex = static_cast(pItem)->GetValue(); // Special casing for percent if( !rIsEmptyTextNd && SvNumFormatType::PERCENT == pNumFormatr->GetType( rFormatIndex )) { sal_uInt32 nTmpFormat = 0; if( GetFrameFormat()->GetDoc()->IsNumberFormat( aText, nTmpFormat, rNum ) && SvNumFormatType::NUMBER == pNumFormatr->GetType( nTmpFormat )) aText += "%"; } } else rFormatIndex = 0; bRet = GetFrameFormat()->GetDoc()->IsNumberFormat( aText, rFormatIndex, rNum ); } else rIsEmptyTextNd = false; return bRet; } bool SwTableBox::IsNumberChanged() const { bool bRet = true; if( SfxItemState::SET == GetFrameFormat()->GetItemState( RES_BOXATR_FORMULA, false )) { const SwTableBoxNumFormat *pNumFormat; const SwTableBoxValue *pValue; if( SfxItemState::SET != GetFrameFormat()->GetItemState( RES_BOXATR_VALUE, false, reinterpret_cast(&pValue) )) pValue = nullptr; if( SfxItemState::SET != GetFrameFormat()->GetItemState( RES_BOXATR_FORMAT, false, reinterpret_cast(&pNumFormat) )) pNumFormat = nullptr; sal_uLong nNdPos; if( pNumFormat && pValue && ULONG_MAX != ( nNdPos = IsValidNumTextNd() ) ) { OUString sNewText, sOldText( m_pStartNode->GetNodes()[ nNdPos ]-> GetTextNode()->GetRedlineText() ); lcl_DelTabsAtSttEnd( sOldText ); Color* pCol = nullptr; GetFrameFormat()->GetDoc()->GetNumberFormatter()->GetOutputString( pValue->GetValue(), pNumFormat->GetValue(), sNewText, &pCol ); bRet = sNewText != sOldText || !( ( !pCol && !GetSaveNumFormatColor() ) || ( pCol && GetSaveNumFormatColor() && *pCol == *GetSaveNumFormatColor() )); } } return bRet; } sal_uLong SwTableBox::IsValidNumTextNd( bool bCheckAttr ) const { sal_uLong nPos = ULONG_MAX; if( m_pStartNode ) { SwNodeIndex aIdx( *m_pStartNode ); sal_uLong nIndex = aIdx.GetIndex(); const sal_uLong nIndexEnd = m_pStartNode->GetNodes()[ nIndex ]->EndOfSectionIndex(); const SwTextNode *pTextNode = nullptr; while( ++nIndex < nIndexEnd ) { const SwNode* pNode = m_pStartNode->GetNodes()[nIndex]; if( pNode->IsTableNode() ) { pTextNode = nullptr; break; } if( pNode->IsTextNode() ) { if( pTextNode ) { pTextNode = nullptr; break; } else { pTextNode = pNode->GetTextNode(); nPos = nIndex; } } } if( pTextNode ) { if( bCheckAttr ) { const SwpHints* pHts = pTextNode->GetpSwpHints(); // do some tests if there's only text in the node! // Flys/fields/... if( pHts ) { sal_Int32 nNextSetField = 0; for( size_t n = 0; n < pHts->Count(); ++n ) { const SwTextAttr* pAttr = pHts->Get(n); if( RES_TXTATR_NOEND_BEGIN <= pAttr->Which() ) { if ( (pAttr->GetStart() == nNextSetField) && (pAttr->Which() == RES_TXTATR_FIELD)) { // #i104949# hideous hack for report builder: // it inserts hidden variable-set fields at // the beginning of para in cell, but they // should not turn cell into text cell const SwField* pField = pAttr->GetFormatField().GetField(); if (pField && (pField->GetTypeId() == SwFieldTypesEnum::Set) && (0 != (static_cast (pField)->GetSubType() & nsSwExtendedSubType::SUB_INVISIBLE))) { nNextSetField = pAttr->GetStart() + 1; continue; } } else if( RES_TXTATR_ANNOTATION == pAttr->Which() ) { continue; } nPos = ULONG_MAX; break; } } } } } else nPos = ULONG_MAX; } return nPos; } // is this a Formula box or one with numeric content (AutoSum) sal_uInt16 SwTableBox::IsFormulaOrValueBox() const { sal_uInt16 nWhich = 0; const SwTextNode* pTNd; SwFrameFormat* pFormat = GetFrameFormat(); if( SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_FORMULA, false )) nWhich = RES_BOXATR_FORMULA; else if( SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_VALUE, false ) && !pFormat->GetDoc()->GetNumberFormatter()->IsTextFormat( pFormat->GetTableBoxNumFormat().GetValue() )) nWhich = RES_BOXATR_VALUE; else if( m_pStartNode && m_pStartNode->GetIndex() + 2 == m_pStartNode->EndOfSectionIndex() && nullptr != ( pTNd = m_pStartNode->GetNodes()[ m_pStartNode->GetIndex() + 1 ] ->GetTextNode() ) && pTNd->GetText().isEmpty()) nWhich = USHRT_MAX; return nWhich; } void SwTableBox::ActualiseValueBox() { const SfxPoolItem *pFormatItem, *pValItem; SwFrameFormat* pFormat = GetFrameFormat(); if( SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_FORMAT, true, &pFormatItem ) && SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_VALUE, true, &pValItem )) { const sal_uLong nFormatId = static_cast(pFormatItem)->GetValue(); sal_uLong nNdPos = ULONG_MAX; SvNumberFormatter* pNumFormatr = pFormat->GetDoc()->GetNumberFormatter(); if( !pNumFormatr->IsTextFormat( nFormatId ) && ULONG_MAX != (nNdPos = IsValidNumTextNd()) ) { double fVal = static_cast(pValItem)->GetValue(); Color* pCol = nullptr; OUString sNewText; pNumFormatr->GetOutputString( fVal, nFormatId, sNewText, &pCol ); const OUString& rText = m_pStartNode->GetNodes()[ nNdPos ]->GetTextNode()->GetText(); if( rText != sNewText ) ChgTextToNum( *this, sNewText, pCol, false ,nNdPos); } } } struct SwTableCellInfo::Impl { const SwTable * m_pTable; const SwCellFrame * m_pCellFrame; const SwTabFrame * m_pTabFrame; typedef o3tl::sorted_vector TableBoxes_t; TableBoxes_t m_HandledTableBoxes; public: Impl() : m_pTable(nullptr), m_pCellFrame(nullptr), m_pTabFrame(nullptr) { } void setTable(const SwTable * pTable) { m_pTable = pTable; SwFrameFormat * pFrameFormat = m_pTable->GetFrameFormat(); m_pTabFrame = SwIterator(*pFrameFormat).First(); if (m_pTabFrame && m_pTabFrame->IsFollow()) m_pTabFrame = m_pTabFrame->FindMaster(true); } const SwCellFrame * getCellFrame() const { return m_pCellFrame; } const SwFrame * getNextFrameInTable(const SwFrame * pFrame); const SwCellFrame * getNextCellFrame(const SwFrame * pFrame); const SwCellFrame * getNextTableBoxsCellFrame(const SwFrame * pFrame); bool getNext(); }; const SwFrame * SwTableCellInfo::Impl::getNextFrameInTable(const SwFrame * pFrame) { const SwFrame * pResult = nullptr; if (((! pFrame->IsTabFrame()) || pFrame == m_pTabFrame) && pFrame->GetLower()) pResult = pFrame->GetLower(); else if (pFrame->GetNext()) pResult = pFrame->GetNext(); else { while (pFrame->GetUpper() != nullptr) { pFrame = pFrame->GetUpper(); if (pFrame->IsTabFrame()) { m_pTabFrame = static_cast(pFrame)->GetFollow(); pResult = m_pTabFrame; break; } else if (pFrame->GetNext()) { pResult = pFrame->GetNext(); break; } } } return pResult; } const SwCellFrame * SwTableCellInfo::Impl::getNextCellFrame(const SwFrame * pFrame) { const SwCellFrame * pResult = nullptr; while ((pFrame = getNextFrameInTable(pFrame)) != nullptr) { if (pFrame->IsCellFrame()) { pResult = static_cast(pFrame); break; } } return pResult; } const SwCellFrame * SwTableCellInfo::Impl::getNextTableBoxsCellFrame(const SwFrame * pFrame) { const SwCellFrame * pResult = nullptr; while ((pFrame = getNextCellFrame(pFrame)) != nullptr) { const SwCellFrame * pCellFrame = static_cast(pFrame); const SwTableBox * pTabBox = pCellFrame->GetTabBox(); auto aIt = m_HandledTableBoxes.insert(pTabBox); if (aIt.second) { pResult = pCellFrame; break; } } return pResult; } const SwCellFrame * SwTableCellInfo::getCellFrame() const { return m_pImpl->getCellFrame(); } bool SwTableCellInfo::Impl::getNext() { if (m_pCellFrame == nullptr) { if (m_pTabFrame != nullptr) m_pCellFrame = Impl::getNextTableBoxsCellFrame(m_pTabFrame); } else m_pCellFrame = Impl::getNextTableBoxsCellFrame(m_pCellFrame); return m_pCellFrame != nullptr; } SwTableCellInfo::SwTableCellInfo(const SwTable * pTable) : m_pImpl(std::make_unique()) { m_pImpl->setTable(pTable); } SwTableCellInfo::~SwTableCellInfo() { } bool SwTableCellInfo::getNext() { return m_pImpl->getNext(); } SwRect SwTableCellInfo::getRect() const { SwRect aRet; if (getCellFrame() != nullptr) aRet = getCellFrame()->getFrameArea(); return aRet; } const SwTableBox * SwTableCellInfo::getTableBox() const { const SwTableBox * pRet = nullptr; if (getCellFrame() != nullptr) pRet = getCellFrame()->GetTabBox(); return pRet; } void SwTable::RegisterToFormat( SwFormat& rFormat ) { rFormat.Add( this ); } bool SwTable::HasLayout() const { const SwFrameFormat* pFrameFormat = GetFrameFormat(); //a table in a clipboard document doesn't have any layout information return pFrameFormat && SwIterator(*pFrameFormat).First(); } void SwTableLine::RegisterToFormat( SwFormat& rFormat ) { rFormat.Add( this ); } void SwTableBox::RegisterToFormat( SwFormat& rFormat ) { rFormat.Add( this ); } // free's any remaining child objects SwTableLines::~SwTableLines() { for ( const_iterator it = begin(); it != end(); ++it ) delete *it; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */