1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <string_view>
25 #include <hintids.hxx>
34 #include <IDocumentLayoutAccess.hxx>
36 #include <swtable.hxx>
38 #include <cellfml.hxx>
43 #include <cellatr.hxx>
44 #include <ndindex.hxx>
45 #include <frameformats.hxx>
46 #include <comphelper/string.hxx>
47 #include <o3tl/string_view.hxx>
48 #include <o3tl/safeint.hxx>
49 #include <osl/diagnose.h>
50 #include <svl/numformat.hxx>
56 const sal_Unicode cRelSeparator
= ',';
57 const sal_Unicode cRelIdentifier
= '\x12'; // CTRL-R
66 static const SwFrame
* lcl_GetBoxFrame( const SwTableBox
& rBox
);
67 static sal_Int32
lcl_GetLongBoxNum( OUString
& rStr
);
68 static const SwTableBox
* lcl_RelToBox( const SwTable
& rTable
,
69 const SwTableBox
* pRefBox
,
70 const OUString
& sGetName
);
71 static OUString
lcl_BoxNmToRel( const SwTable
& rTable
,
72 const SwTableNode
& rTableNd
,
73 const OUString
& sRefBoxNm
,
74 const OUString
& sGetStr
,
77 /** Get value of this box.
79 * The value is comes from the first TextNode. If it starts with a number/
80 * formula then calculate it, if it starts with a field then get the value.
81 * All other conditions return 0 (and an error?).
83 double SwTableBox::GetValue( SwTableCalcPara
& rCalcPara
) const
87 if( rCalcPara
.m_rCalc
.IsCalcError() )
88 return nRet
; // stop if there is already an error set
90 rCalcPara
.m_rCalc
.SetCalcError( SwCalcError::Syntax
); // default: error
96 if( rCalcPara
.IncStackCnt() )
99 rCalcPara
.SetLastTableBox( this );
101 // Does it create a recursion?
102 SwTableBox
* pBox
= const_cast<SwTableBox
*>(this);
103 if( rCalcPara
.m_pBoxStack
->find( pBox
) != rCalcPara
.m_pBoxStack
->end() )
104 return nRet
; // already on the stack: error
106 // re-start with this box
107 rCalcPara
.SetLastTableBox( this );
109 rCalcPara
.m_pBoxStack
->insert( pBox
); // add
110 do { // Middle-Check-Loop, so that we can jump from here. Used so that the box pointer
111 // will be removed from stack at the end.
112 SwDoc
* pDoc
= GetFrameFormat()->GetDoc();
114 if( const SwTableBoxFormula
* pFormulaItem
= GetFrameFormat()->GetItemIfSet(
115 RES_BOXATR_FORMULA
, false ) )
117 rCalcPara
.m_rCalc
.SetCalcError( SwCalcError::NONE
); // reset status
118 if( !pFormulaItem
->IsValid() )
121 const SwTable
* pTmp
= rCalcPara
.m_pTable
;
122 rCalcPara
.m_pTable
= &pBox
->GetSttNd()->FindTableNode()->GetTable();
123 const_cast<SwTableBoxFormula
*>(pFormulaItem
)->Calc( rCalcPara
, nRet
);
125 if( !rCalcPara
.IsStackOverflow() )
127 SwFrameFormat
* pFormat
= pBox
->ClaimFrameFormat();
128 SfxItemSetFixed
<RES_BOXATR_BEGIN
,RES_BOXATR_END
-1> aTmp( pDoc
->GetAttrPool() );
129 aTmp
.Put( SwTableBoxValue( nRet
) );
130 if( SfxItemState::SET
!= pFormat
->GetItemState( RES_BOXATR_FORMAT
))
131 aTmp
.Put( SwTableBoxNumFormat( 0 ));
132 pFormat
->SetFormatAttr( aTmp
);
134 rCalcPara
.m_pTable
= pTmp
;
137 nRet
= GetFrameFormat()->GetTableBoxValue().GetValue();
140 else if( const SwTableBoxValue
* pBoxValueItem
= pBox
->GetFrameFormat()->GetItemIfSet(
141 RES_BOXATR_VALUE
, false ) )
143 rCalcPara
.m_rCalc
.SetCalcError( SwCalcError::NONE
); // reset status
144 nRet
= pBoxValueItem
->GetValue();
148 SwTextNode
* pTextNd
= pDoc
->GetNodes()[ m_pStartNode
->GetIndex() + 1 ]->GetTextNode();
152 sal_Int32 nSttPos
= 0;
153 OUString sText
= pTextNd
->GetText();
155 // use text of the tracked changes
156 if ( sText
.getLength() > 0 &&
157 sText
[0] != CH_TXTATR_BREAKWORD
&& sText
[0] != CH_TXTATR_INWORD
)
159 sText
= pTextNd
->GetRedlineText();
162 while ( nSttPos
< sText
.getLength() && ( sText
[nSttPos
]==' ' || sText
[nSttPos
]=='\t' ) )
165 // if there is a calculation field at position 1, get the value of it
166 const bool bOK
= nSttPos
<sText
.getLength();
167 const sal_Unicode Char
= bOK
? sText
[nSttPos
] : 0;
168 SwTextField
* pTextField
= nullptr;
169 if ( bOK
&& (Char
==CH_TXTATR_BREAKWORD
|| Char
==CH_TXTATR_INWORD
) )
171 pTextField
= static_txtattr_cast
<SwTextField
*>(pTextNd
->GetTextAttrForCharAt(nSttPos
, RES_TXTATR_FIELD
));
173 if ( pTextField
!= nullptr )
175 rCalcPara
.m_rCalc
.SetCalcError( SwCalcError::NONE
); // reset status
177 const SwField
* pField
= pTextField
->GetFormatField().GetField();
178 switch ( pField
->GetTyp()->Which() )
180 case SwFieldIds::SetExp
:
181 nRet
= static_cast<const SwSetExpField
*>(pField
)->GetValue(rCalcPara
.m_pLayout
);
183 case SwFieldIds::User
:
184 nRet
= static_cast<const SwUserField
*>(pField
)->GetValue();
186 case SwFieldIds::Table
:
188 SwTableField
* pTableField
= const_cast<SwTableField
*>(static_cast<const SwTableField
*>(pField
));
189 if( !pTableField
->IsValid() )
191 // use the right table!
192 const SwTable
* pTmp
= rCalcPara
.m_pTable
;
193 rCalcPara
.m_pTable
= &pTextNd
->FindTableNode()->GetTable();
194 pTableField
->CalcField( rCalcPara
);
195 rCalcPara
.m_pTable
= pTmp
;
197 nRet
= pTableField
->GetValue();
201 case SwFieldIds::DateTime
:
202 nRet
= static_cast<const SwDateTimeField
*>( pField
)->GetValue();
205 case SwFieldIds::JumpEdit
:
206 //JP 14.09.98: Bug 56112 - placeholder never have the right content!
211 nRet
= rCalcPara
.m_rCalc
.Calculate( pField
->ExpandField(true, nullptr) ).GetDouble();
214 else if ( nSttPos
< sText
.getLength()
215 && Char
== CH_TXT_ATR_INPUTFIELDSTART
)
217 const SwTextInputField
* pTextInputField
=
218 dynamic_cast< const SwTextInputField
* >(
219 pTextNd
->GetTextAttrAt( nSttPos
, RES_TXTATR_INPUTFIELD
) );
220 if ( pTextInputField
== nullptr )
222 nRet
= rCalcPara
.m_rCalc
.Calculate( pTextInputField
->GetFieldContent() ).GetDouble();
224 else if ( Char
!= CH_TXTATR_BREAKWORD
)
226 // result is 0 but no error!
227 rCalcPara
.m_rCalc
.SetCalcError( SwCalcError::NONE
); // reset status
230 sText
= bOK
? sText
.copy( nSttPos
) : OUString();
231 sal_uInt32 nFormatIndex
= GetFrameFormat()->GetTableBoxNumFormat().GetValue();
233 SvNumberFormatter
* pNumFormatr
= pDoc
->GetNumberFormatter();
235 const SvNumFormatType nFormatType
= pNumFormatr
->GetType( nFormatIndex
);
236 if( nFormatType
== SvNumFormatType::TEXT
)
238 // JP 22.04.98: Bug 49659 - special treatment for percentages
239 else if( !sText
.isEmpty() &&
240 SvNumFormatType::PERCENT
== nFormatType
)
242 sal_uInt32 nTmpFormat
= 0;
243 if( pDoc
->IsNumberFormat( sText
, nTmpFormat
, aNum
) &&
244 SvNumFormatType::NUMBER
== pNumFormatr
->GetType( nTmpFormat
))
248 if( pDoc
->IsNumberFormat( sText
, nFormatIndex
, aNum
))
251 rCalcPara
.m_rCalc
.SetCalcError( SwCalcError::NaN
); // set for interoperability functions
253 // ?? otherwise it is an error
256 if( !rCalcPara
.IsStackOverflow() )
258 rCalcPara
.m_pBoxStack
->erase( pBox
); // remove from stack
259 rCalcPara
.DecStackCnt();
262 //JP 12.01.99: error detection, Bug 60794
263 if( DBL_MAX
== nRet
)
264 rCalcPara
.m_rCalc
.SetCalcError( SwCalcError::Syntax
); // set error
269 // structure needed for calculation of tables
271 SwTableCalcPara::SwTableCalcPara(SwCalc
& rCalculator
, const SwTable
& rTable
,
272 SwRootFrame
const*const pLayout
)
273 : m_pLastTableBox(nullptr)
275 , m_nMaxSize( cMAXSTACKSIZE
)
277 , m_pBoxStack( new SwTableSortBoxes
)
278 , m_rCalc( rCalculator
)
279 , m_pTable( &rTable
)
283 SwTableCalcPara::~SwTableCalcPara()
287 bool SwTableCalcPara::CalcWithStackOverflow()
289 // If a stack overflow was detected, redo with last box.
290 sal_uInt16 nSaveMaxSize
= m_nMaxSize
;
292 m_nMaxSize
= cMAXSTACKSIZE
- 5;
294 SwTableBoxes aStackOverflows
;
296 SwTableBox
* pBox
= const_cast<SwTableBox
*>(m_pLastTableBox
);
298 m_rCalc
.SetCalcError( SwCalcError::NONE
);
299 aStackOverflows
.insert( aStackOverflows
.begin() + nCnt
++, pBox
);
301 m_pBoxStack
->erase( pBox
);
302 pBox
->GetValue( *this );
303 } while( IsStackOverflow() );
305 m_nMaxSize
= cMAXSTACKSIZE
- 3; // decrease at least one level
307 // if recursion was detected
309 m_rCalc
.SetCalcError( SwCalcError::NONE
);
310 m_pBoxStack
->clear();
312 while( !m_rCalc
.IsCalcError() && nCnt
)
314 aStackOverflows
[ --nCnt
]->GetValue( *this );
315 if( IsStackOverflow() && !CalcWithStackOverflow() )
319 m_nMaxSize
= nSaveMaxSize
;
320 aStackOverflows
.clear();
321 return !m_rCalc
.IsCalcError();
324 SwTableFormula::SwTableFormula( OUString aFormula
)
325 : m_sFormula( std::move(aFormula
) )
326 , m_eNmType( EXTRNL_NAME
)
327 , m_bValidValue( false )
331 SwTableFormula::~SwTableFormula()
335 void SwTableFormula::MakeFormula_( const SwTable
& rTable
, OUStringBuffer
& rNewStr
,
336 OUString
& rFirstBox
, OUString
* pLastBox
, void* pPara
) const
338 SwTableCalcPara
* pCalcPara
= static_cast<SwTableCalcPara
*>(pPara
);
339 if( pCalcPara
->m_rCalc
.IsCalcError() ) // stop if there is already an error set
342 SwTableBox
*pEndBox
= nullptr;
344 rFirstBox
= rFirstBox
.copy(1); // erase label of this box
345 // a region in this area?
348 pEndBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(pLastBox
->toInt64()));
350 // Is it actually a valid pointer?
351 if( rTable
.GetTabSortBoxes().find( pEndBox
) == rTable
.GetTabSortBoxes().end() )
353 rFirstBox
= rFirstBox
.copy( pLastBox
->getLength()+1 );
355 SwTableBox
* pSttBox
= reinterpret_cast<SwTableBox
*>(
356 sal::static_int_cast
<sal_IntPtr
>(rFirstBox
.toInt64()));
357 // Is it actually a valid pointer?
358 if( rTable
.GetTabSortBoxes().find( pSttBox
) == rTable
.GetTabSortBoxes().end() )
362 if( pEndBox
&& pSttBox
) // area?
364 // get all selected boxes via layout and calculate their values
366 GetBoxes( *pSttBox
, *pEndBox
, aBoxes
);
368 // don't use empty cells or cells with text content as zeroes in interoperability functions
369 sal_Int16 nUseOnlyNumber
= -1;
373 for (size_t n
= 0; n
< aBoxes
.size() &&
374 !pCalcPara
->m_rCalc
.IsCalcError(); ++n
)
376 const SwTableBox
* pTableBox
= aBoxes
[n
];
377 if ( pTableBox
->getRowSpan() >= 1 )
379 double fVal
= pTableBox
->GetValue( *pCalcPara
);
381 if ( pCalcPara
->m_rCalc
.IsCalcNotANumber() )
383 if ( nUseOnlyNumber
== -1 )
385 OUString sFormula
= rNewStr
.toString().toAsciiUpperCase();
386 nUseOnlyNumber
= sal_Int16(
387 sFormula
.lastIndexOf("AVERAGE") > -1 ||
388 sFormula
.lastIndexOf("COUNT") > -1 ||
389 sFormula
.lastIndexOf("PRODUCT") > -1 );
391 if ( nUseOnlyNumber
> 0 )
396 rNewStr
.append(cListDelim
);
398 rNewStr
.append(pCalcPara
->m_rCalc
.GetStrResult( fVal
));
403 else if( pSttBox
&& !pLastBox
) // only the StartBox?
405 // JP 12.01.99: and no EndBox in the formula!
406 // calculate the value of the box
407 if ( pSttBox
->getRowSpan() >= 1 )
410 double fVal
= pSttBox
->GetValue( *pCalcPara
);
411 // don't use empty cell or a cell with text content as zero in interoperability functions
412 // (except PRODUCT, where the result is correct anyway)
413 if ( !pCalcPara
->m_rCalc
.IsCalcNotANumber() ||
414 ( rNewStr
.toString().toAsciiUpperCase().lastIndexOf("AVERAGE") == -1 &&
415 rNewStr
.toString().toAsciiUpperCase().lastIndexOf("COUNT") == -1 ) )
417 rNewStr
.append(pCalcPara
->m_rCalc
.GetStrResult( fVal
));
423 pCalcPara
->m_rCalc
.SetCalcError( SwCalcError::Syntax
); // set error
427 void SwTableFormula::RelNmsToBoxNms( const SwTable
& rTable
, OUStringBuffer
& rNewStr
,
428 OUString
& rFirstBox
, OUString
* pLastBox
, void* pPara
) const
430 // relative name w.r.t. box name (external presentation)
431 SwNode
* pNd
= static_cast<SwNode
*>(pPara
);
432 OSL_ENSURE( pNd
, "Field isn't in any TextNode" );
433 const SwTableBox
*pBox
= rTable
.GetTableBox(
434 pNd
->FindTableBoxStartNode()->GetIndex() );
436 rNewStr
.append(rFirstBox
[0]); // get label for the box
437 rFirstBox
= rFirstBox
.copy(1);
440 const SwTableBox
*pRelLastBox
= lcl_RelToBox( rTable
, pBox
, *pLastBox
);
442 rNewStr
.append(pRelLastBox
->GetName());
444 rNewStr
.append("A1");
446 rFirstBox
= rFirstBox
.copy( pLastBox
->getLength()+1 );
449 const SwTableBox
*pRelFirstBox
= lcl_RelToBox( rTable
, pBox
, rFirstBox
);
452 rNewStr
.append(pRelFirstBox
->GetName());
454 rNewStr
.append("A1");
456 // get label for the box
457 rNewStr
.append(rFirstBox
[ rFirstBox
.getLength()-1 ]);
460 void SwTableFormula::RelBoxNmsToPtr( const SwTable
& rTable
, OUStringBuffer
& rNewStr
,
461 OUString
& rFirstBox
, OUString
* pLastBox
, void* pPara
) const
463 // relative name w.r.t. box name (internal presentation)
464 SwNode
* pNd
= static_cast<SwNode
*>(pPara
);
465 OSL_ENSURE( pNd
, "Field not placed in any Node" );
466 const SwTableBox
*pBox
= rTable
.GetTableBox(
467 pNd
->FindTableBoxStartNode()->GetIndex() );
469 rNewStr
.append(rFirstBox
[0]); // get label for the box
470 rFirstBox
= rFirstBox
.copy(1);
473 const SwTableBox
*pRelLastBox
= lcl_RelToBox( rTable
, pBox
, *pLastBox
);
475 rNewStr
.append(reinterpret_cast<sal_IntPtr
>(pRelLastBox
));
479 rFirstBox
= rFirstBox
.copy( pLastBox
->getLength()+1 );
482 const SwTableBox
*pRelFirstBox
= lcl_RelToBox( rTable
, pBox
, rFirstBox
);
484 rNewStr
.append(reinterpret_cast<sal_IntPtr
>(pRelFirstBox
));
488 // get label for the box
489 rNewStr
.append(rFirstBox
[ rFirstBox
.getLength()-1 ]);
492 void SwTableFormula::BoxNmsToRelNm( const SwTable
& rTable
, OUStringBuffer
& rNewStr
,
493 OUString
& rFirstBox
, OUString
* pLastBox
, void* pPara
) const
495 // box name (external presentation) w.r.t. relative name
496 SwNode
* pNd
= static_cast<SwNode
*>(pPara
);
497 OSL_ENSURE( pNd
, "Field not placed in any Node" );
498 const SwTableNode
* pTableNd
= pNd
->FindTableNode();
501 if( &pTableNd
->GetTable() == &rTable
)
503 const SwTableBox
*pBox
= rTable
.GetTableBox(
504 pNd
->FindTableBoxStartNode()->GetIndex() );
505 OSL_ENSURE( pBox
, "Field not placed in any Table" );
506 sRefBoxNm
= pBox
->GetName();
509 rNewStr
.append(rFirstBox
[0]); // get label for the box
510 rFirstBox
= rFirstBox
.copy(1);
513 rNewStr
.append(lcl_BoxNmToRel( rTable
, *pTableNd
, sRefBoxNm
, *pLastBox
,
514 m_eNmType
== EXTRNL_NAME
));
516 rFirstBox
= rFirstBox
.copy( pLastBox
->getLength()+1 );
519 rNewStr
.append(lcl_BoxNmToRel( rTable
, *pTableNd
, sRefBoxNm
, rFirstBox
,
520 m_eNmType
== EXTRNL_NAME
));
522 // get label for the box
523 rNewStr
.append(rFirstBox
[ rFirstBox
.getLength()-1 ]);
526 void SwTableFormula::PtrToBoxNms( const SwTable
& rTable
, OUStringBuffer
& rNewStr
,
527 OUString
& rFirstBox
, OUString
* pLastBox
, void* ) const
529 // area in these parentheses?
532 rNewStr
.append(rFirstBox
[0]); // get label for the box
533 rFirstBox
= rFirstBox
.copy(1);
536 pBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(pLastBox
->toInt64()));
538 // Is it actually a valid pointer?
539 if( rTable
.GetTabSortBoxes().find( pBox
) != rTable
.GetTabSortBoxes().end() )
540 rNewStr
.append(pBox
->GetName());
544 rFirstBox
= rFirstBox
.copy( pLastBox
->getLength()+1 );
547 pBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(rFirstBox
.toInt64()));
548 // Is it actually a valid pointer?
549 if( rTable
.GetTabSortBoxes().find( pBox
) != rTable
.GetTabSortBoxes().end() )
550 rNewStr
.append(pBox
->GetName());
554 // get label for the box
555 rNewStr
.append(rFirstBox
[ rFirstBox
.getLength()-1 ]);
558 void SwTableFormula::BoxNmsToPtr( const SwTable
& rTable
, OUStringBuffer
& rNewStr
,
559 OUString
& rFirstBox
, OUString
* pLastBox
, void* ) const
561 // area in these parentheses?
562 const SwTableBox
* pBox
;
564 rNewStr
.append(rFirstBox
[0]); // get label for the box
565 rFirstBox
= rFirstBox
.copy(1);
568 pBox
= rTable
.GetTableBox( *pLastBox
);
569 rNewStr
.append(OUString::number(reinterpret_cast<sal_IntPtr
>(pBox
)) +
571 rFirstBox
= rFirstBox
.copy( pLastBox
->getLength()+1 );
574 pBox
= rTable
.GetTableBox( rFirstBox
);
575 rNewStr
.append(OUString::number(reinterpret_cast<sal_IntPtr
>(pBox
))
576 + OUStringChar(rFirstBox
[ rFirstBox
.getLength()-1 ])); // get label for the box
579 /// create external formula (for UI)
580 void SwTableFormula::PtrToBoxNm( const SwTable
* pTable
)
582 const SwNode
* pNd
= nullptr;
583 FnScanFormula fnFormula
= nullptr;
588 fnFormula
= &SwTableFormula::PtrToBoxNms
;
593 fnFormula
= &SwTableFormula::RelNmsToBoxNms
;
594 pNd
= GetNodeOfFormula();
600 m_sFormula
= ScanString( fnFormula
, *pTable
, const_cast<void*>(static_cast<void const *>(pNd
)) );
601 m_eNmType
= EXTRNL_NAME
;
604 /// create internal formula (in CORE)
605 void SwTableFormula::BoxNmToPtr( const SwTable
* pTable
)
607 const SwNode
* pNd
= nullptr;
608 FnScanFormula fnFormula
= nullptr;
613 fnFormula
= &SwTableFormula::BoxNmsToPtr
;
618 fnFormula
= &SwTableFormula::RelBoxNmsToPtr
;
619 pNd
= GetNodeOfFormula();
625 m_sFormula
= ScanString( fnFormula
, *pTable
, const_cast<void*>(static_cast<void const *>(pNd
)) );
626 m_eNmType
= INTRNL_NAME
;
629 /// create relative formula (for copy)
630 void SwTableFormula::ToRelBoxNm( const SwTable
* pTable
)
632 const SwNode
* pNd
= nullptr;
633 FnScanFormula fnFormula
= nullptr;
640 fnFormula
= &SwTableFormula::BoxNmsToRelNm
;
641 pNd
= GetNodeOfFormula();
647 m_sFormula
= ScanString( fnFormula
, *pTable
, const_cast<void*>(static_cast<void const *>(pNd
)) );
648 m_eNmType
= REL_NAME
;
651 OUString
SwTableFormula::ScanString( FnScanFormula fnFormula
, const SwTable
& rTable
,
655 sal_Int32 nFormula
= 0;
659 // If the formula is preceded by a name, use this table!
660 const SwTable
* pTable
= &rTable
;
662 sal_Int32 nStt
= m_sFormula
.indexOf( '<', nFormula
);
667 const sal_Int32 nNxt
= nStt
+1;
668 if (nNxt
>=m_sFormula
.getLength())
673 if ( m_sFormula
[nNxt
]!=' ' && m_sFormula
[nNxt
]!='=' )
675 nStt
= m_sFormula
.indexOf( '<', nNxt
);
679 // Start searching from current position, which is valid for sure
680 nEnd
= m_sFormula
.indexOf( '>', nStt
);
682 if (nStt
<0 || nEnd
<0 )
684 // set the rest and finish
685 aStr
.append(m_sFormula
.subView(nFormula
));
690 aStr
.append(m_sFormula
.subView(nFormula
, nStt
- nFormula
));
694 sal_Int32 nSeparator
= 0;
695 // Is a table name preceded?
696 // JP 16.02.99: SplitMergeBoxNm take care of the name themself
697 // JP 22.02.99: Linux compiler needs cast
698 // JP 28.06.99: rel. BoxName has no preceding tablename!
699 if( fnFormula
!= &SwTableFormula::SplitMergeBoxNm_
&&
700 m_sFormula
.getLength()>(nStt
+1) && cRelIdentifier
!= m_sFormula
[nStt
+1] &&
701 (nSeparator
= m_sFormula
.indexOf( '.', nStt
))>=0
702 && nSeparator
< nEnd
)
704 OUString
sTableNm( m_sFormula
.copy( nStt
, nEnd
- nStt
));
706 // If there are dots in the name, then they appear in pairs (e.g. A1.1.1)!
707 if( (comphelper::string::getTokenCount(sTableNm
, '.') - 1) & 1 )
709 sTableNm
= sTableNm
.copy( 0, nSeparator
- nStt
);
711 // when creating a formula the table name is unwanted
712 if( fnFormula
!= &SwTableFormula::MakeFormula_
)
713 aStr
.append(sTableNm
);
716 sTableNm
= sTableNm
.copy( 1 ); // delete separator
717 if( sTableNm
!= rTable
.GetFrameFormat()->GetName() )
719 // then search for table
720 const SwTable
* pFnd
= FindTable(
721 *rTable
.GetFrameFormat()->GetDoc(),
726 OSL_ENSURE( pFnd
, "No table found. What now?" );
731 OUString
sBox( m_sFormula
.copy( nStt
, nEnd
- nStt
+ 1 ));
732 // area in these parentheses?
733 nSeparator
= m_sFormula
.indexOf( ':', nStt
);
734 if ( nSeparator
>=0 && nSeparator
<nEnd
)
736 // without opening parenthesis
737 OUString
aFirstBox( m_sFormula
.copy( nStt
+1, nSeparator
- nStt
- 1 ));
738 (this->*fnFormula
)( *pTable
, aStr
, sBox
, &aFirstBox
, pPara
);
741 (this->*fnFormula
)( *pTable
, aStr
, sBox
, nullptr, pPara
);
746 return aStr
.makeStringAndClear();
749 const SwTable
* SwTableFormula::FindTable( SwDoc
& rDoc
, std::u16string_view rNm
)
751 const sw::TableFrameFormats
& rTableFormats
= *rDoc
.GetTableFrameFormats();
752 const SwTable
* pTmpTable
= nullptr, *pRet
= nullptr;
753 for( auto nFormatCnt
= rTableFormats
.size(); nFormatCnt
; )
755 SwTableFormat
* pFormat
= rTableFormats
[ --nFormatCnt
];
756 // if we are called from Sw3Writer, a number is dependent on the format name
758 if ( rNm
== o3tl::getToken(pFormat
->GetName(), 0, 0x0a) &&
759 nullptr != ( pTmpTable
= SwTable::FindTable( pFormat
) ) &&
760 nullptr != (pFBox
= pTmpTable
->GetTabSortBoxes()[0] ) &&
762 pFBox
->GetSttNd()->GetNodes().IsDocNodes() )
764 // a table in the normal NodesArr
772 static const SwFrame
* lcl_GetBoxFrame( const SwTableBox
& rBox
)
774 SwNodeIndex
aIdx( *rBox
.GetSttNd() );
775 SwContentNode
* pCNd
= aIdx
.GetNodes().GoNext( &aIdx
);
776 OSL_ENSURE( pCNd
, "Box has no TextNode" );
777 Point aPt
; // get the first frame of the layout - table headline
778 std::pair
<Point
, bool> const tmp(aPt
, false);
779 return pCNd
->getLayoutFrame(pCNd
->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp
);
782 static sal_Int32
lcl_GetLongBoxNum( OUString
& rStr
)
785 const sal_Int32 nPos
= rStr
.indexOf( cRelSeparator
);
788 nRet
= rStr
.toInt32();
793 nRet
= o3tl::toInt32(rStr
.subView( 0, nPos
));
794 rStr
= rStr
.copy( nPos
+1 );
799 static const SwTableBox
* lcl_RelToBox( const SwTable
& rTable
,
800 const SwTableBox
* pRefBox
,
801 const OUString
& _sGetName
)
804 const SwTableBox
* pBox
= nullptr;
805 OUString sGetName
= _sGetName
;
807 // Is it really a relative value?
808 if ( cRelIdentifier
== sGetName
[0] ) // yes
813 sGetName
= sGetName
.copy( 1 );
815 const SwTableLines
* pLines
= &rTable
.GetTabLines();
816 const SwTableBoxes
* pBoxes
;
817 const SwTableLine
* pLine
;
819 // determine starting values of the box,...
821 pLine
= pBox
->GetUpper();
822 while( pLine
->GetUpper() )
824 pBox
= pLine
->GetUpper();
825 pLine
= pBox
->GetUpper();
827 sal_uInt16 nSttBox
= pLine
->GetBoxPos( pBox
);
828 sal_uInt16 nSttLine
= rTable
.GetTabLines().GetPos( pLine
);
830 const sal_Int32 nBoxOffset
= lcl_GetLongBoxNum( sGetName
) + nSttBox
;
831 const sal_Int32 nLineOffset
= lcl_GetLongBoxNum( sGetName
) + nSttLine
;
833 if( nBoxOffset
< 0 ||
837 if( o3tl::make_unsigned(nLineOffset
) >= pLines
->size() )
840 pLine
= (*pLines
)[ nLineOffset
];
842 // ... then search the box
843 pBoxes
= &pLine
->GetTabBoxes();
844 if( o3tl::make_unsigned(nBoxOffset
) >= pBoxes
->size() )
846 pBox
= (*pBoxes
)[ nBoxOffset
];
848 while (!sGetName
.isEmpty())
850 nSttBox
= SwTable::GetBoxNum( sGetName
);
851 pLines
= &pBox
->GetTabLines();
855 nSttLine
= SwTable::GetBoxNum( sGetName
);
858 if( !nSttLine
|| nSttLine
> pLines
->size() )
860 pLine
= (*pLines
)[ nSttLine
-1 ];
863 pBoxes
= &pLine
->GetTabBoxes();
864 if( nSttBox
>= pBoxes
->size() )
866 pBox
= (*pBoxes
)[ nSttBox
];
871 if( !pBox
->GetSttNd() )
872 // "bubble up" to first box
873 while( !pBox
->GetTabLines().empty() )
874 pBox
= pBox
->GetTabLines().front()->GetTabBoxes().front();
879 // otherwise it is an absolute external presentation
880 pBox
= rTable
.GetTableBox( sGetName
);
885 static OUString
lcl_BoxNmToRel( const SwTable
& rTable
, const SwTableNode
& rTableNd
,
886 const OUString
& _sRefBoxNm
, const OUString
& _sTmp
, bool bExtrnlNm
)
888 OUString sTmp
= _sTmp
;
889 OUString sRefBoxNm
= _sRefBoxNm
;
892 // convert into external presentation
893 SwTableBox
* pBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(sTmp
.toInt64()));
894 if( rTable
.GetTabSortBoxes().find( pBox
) == rTable
.GetTabSortBoxes().end() )
895 return OUString('?');
896 sTmp
= pBox
->GetName();
899 // If the formula is spanning over a table then keep external presentation
900 if( &rTable
== &rTableNd
.GetTable() )
902 tools::Long nBox
= SwTable::GetBoxNum( sTmp
, true );
903 nBox
-= SwTable::GetBoxNum( sRefBoxNm
, true );
904 tools::Long nLine
= SwTable::GetBoxNum( sTmp
);
905 nLine
-= SwTable::GetBoxNum( sRefBoxNm
);
907 const OUString sCpy
= sTmp
; //JP 01.11.95: add rest from box name
909 sTmp
= OUStringChar(cRelIdentifier
) + OUString::number( nBox
)
910 + OUStringChar(cRelSeparator
) + OUString::number( nLine
);
914 sTmp
+= OUStringChar(cRelSeparator
) + sCpy
;
918 if (sTmp
.endsWith(">"))
919 return sTmp
.copy(0, sTmp
.getLength()-1 );
924 void SwTableFormula::GetBoxesOfFormula( const SwTable
& rTable
,
929 BoxNmToPtr( &rTable
);
930 ScanString( &SwTableFormula::GetFormulaBoxes
, rTable
, &rBoxes
);
933 void SwTableFormula::GetFormulaBoxes( const SwTable
& rTable
, OUStringBuffer
& ,
934 OUString
& rFirstBox
, OUString
* pLastBox
, void* pPara
) const
936 SwSelBoxes
* pBoxes
= static_cast<SwSelBoxes
*>(pPara
);
937 SwTableBox
* pEndBox
= nullptr;
939 rFirstBox
= rFirstBox
.copy(1); // delete box label
940 // area in these parentheses?
943 pEndBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(pLastBox
->toInt64()));
945 // Is it actually a valid pointer?
946 if( rTable
.GetTabSortBoxes().find( pEndBox
) == rTable
.GetTabSortBoxes().end() )
948 rFirstBox
= rFirstBox
.copy( pLastBox
->getLength()+1 );
951 SwTableBox
*pSttBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(rFirstBox
.toInt64()));
952 // Is it actually a valid pointer?
953 if( !pSttBox
|| rTable
.GetTabSortBoxes().find( pSttBox
) == rTable
.GetTabSortBoxes().end() )
956 if ( pEndBox
) // area?
958 // get all selected boxes via layout and calculate their values
960 GetBoxes( *pSttBox
, *pEndBox
, aBoxes
);
961 pBoxes
->insert( aBoxes
);
963 else // only the StartBox?
964 pBoxes
->insert( pSttBox
);
967 void SwTableFormula::GetBoxes( const SwTableBox
& rSttBox
,
968 const SwTableBox
& rEndBox
,
971 // get all selected boxes via layout
972 const SwLayoutFrame
*pStt
, *pEnd
;
973 const SwFrame
* pFrame
= lcl_GetBoxFrame( rSttBox
);
974 pStt
= pFrame
? pFrame
->GetUpper() : nullptr;
975 pFrame
= lcl_GetBoxFrame( rEndBox
);
976 pEnd
= pFrame
? pFrame
->GetUpper() : nullptr;
978 return ; // no valid selection
980 GetTableSel( pStt
, pEnd
, rBoxes
, nullptr );
982 const SwTable
* pTable
= pStt
->FindTabFrame()->GetTable();
984 // filter headline boxes
985 if( pTable
->GetRowsToRepeat() <= 0 )
988 do { // middle-check loop
989 const SwTableLine
* pLine
= rSttBox
.GetUpper();
990 while( pLine
->GetUpper() )
991 pLine
= pLine
->GetUpper()->GetUpper();
993 if( pTable
->IsHeadline( *pLine
) )
994 break; // headline in this area!
996 // maybe start and end are swapped
997 pLine
= rEndBox
.GetUpper();
998 while ( pLine
->GetUpper() )
999 pLine
= pLine
->GetUpper()->GetUpper();
1001 if( pTable
->IsHeadline( *pLine
) )
1002 break; // headline in this area!
1004 const SwTabFrame
*pStartTable
= pStt
->FindTabFrame();
1005 const SwTabFrame
*pEndTable
= pEnd
->FindTabFrame();
1007 if (pStartTable
== pEndTable
) // no split table
1010 // then remove table headers
1011 for (size_t n
= 0; n
< rBoxes
.size(); ++n
)
1013 pLine
= rBoxes
[n
]->GetUpper();
1014 while( pLine
->GetUpper() )
1015 pLine
= pLine
->GetUpper()->GetUpper();
1017 if( pTable
->IsHeadline( *pLine
) )
1018 rBoxes
.erase( rBoxes
.begin() + n
-- );
1023 /// Are all boxes valid that are referenced by the formula?
1024 void SwTableFormula::HasValidBoxes_( const SwTable
& rTable
, OUStringBuffer
& ,
1025 OUString
& rFirstBox
, OUString
* pLastBox
, void* pPara
) const
1027 bool* pBValid
= static_cast<bool*>(pPara
);
1028 if( !(*pBValid
) ) // wrong is wrong
1031 SwTableBox
* pSttBox
= nullptr, *pEndBox
= nullptr;
1032 rFirstBox
= rFirstBox
.copy(1); // delete identifier of box
1034 // area in this parenthesis?
1036 rFirstBox
= rFirstBox
.copy( pLastBox
->getLength()+1 );
1042 pEndBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(pLastBox
->toInt64()));
1043 pSttBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(rFirstBox
.toInt64()));
1048 const SwNode
* pNd
= GetNodeOfFormula();
1049 const SwTableBox
* pBox
= !pNd
? nullptr
1050 : const_cast<SwTableBox
*>(rTable
.GetTableBox(
1051 pNd
->FindTableBoxStartNode()->GetIndex() ));
1053 pEndBox
= const_cast<SwTableBox
*>(lcl_RelToBox( rTable
, pBox
, *pLastBox
));
1054 pSttBox
= const_cast<SwTableBox
*>(lcl_RelToBox( rTable
, pBox
, rFirstBox
));
1060 pEndBox
= const_cast<SwTableBox
*>(rTable
.GetTableBox( *pLastBox
));
1061 pSttBox
= const_cast<SwTableBox
*>(rTable
.GetTableBox( rFirstBox
));
1065 // Are these valid pointers?
1067 ( !pEndBox
|| rTable
.GetTabSortBoxes().find( pEndBox
) == rTable
.GetTabSortBoxes().end() ) ) ||
1068 ( !pSttBox
|| rTable
.GetTabSortBoxes().find( pSttBox
) == rTable
.GetTabSortBoxes().end() ) )
1072 bool SwTableFormula::HasValidBoxes() const
1075 const SwNode
* pNd
= GetNodeOfFormula();
1076 if( pNd
&& nullptr != ( pNd
= pNd
->FindTableNode() ) )
1077 ScanString( &SwTableFormula::HasValidBoxes_
,
1078 static_cast<const SwTableNode
*>(pNd
)->GetTable(), &bRet
);
1082 sal_uInt16
SwTableFormula::GetLnPosInTable( const SwTable
& rTable
, const SwTableBox
* pBox
)
1084 sal_uInt16 nRet
= USHRT_MAX
;
1087 const SwTableLine
* pLn
= pBox
->GetUpper();
1088 while( pLn
->GetUpper() )
1089 pLn
= pLn
->GetUpper()->GetUpper();
1090 nRet
= rTable
.GetTabLines().GetPos( pLn
);
1095 void SwTableFormula::SplitMergeBoxNm_( const SwTable
& rTable
, OUStringBuffer
& rNewStr
,
1096 OUString
& rFirstBox
, OUString
* pLastBox
, void* pPara
) const
1098 SwTableFormulaUpdate
& rTableUpd
= *static_cast<SwTableFormulaUpdate
*>(pPara
);
1100 rNewStr
.append(rFirstBox
[0]); // get label for the box
1101 rFirstBox
= rFirstBox
.copy(1);
1104 const SwTable
* pTable
= &rTable
;
1106 OUString
* pTableNmBox
= pLastBox
? pLastBox
: &rFirstBox
;
1108 const sal_Int32 nLastBoxLen
= pTableNmBox
->getLength();
1109 const sal_Int32 nSeparator
= pTableNmBox
->indexOf('.');
1110 if ( nSeparator
>=0 &&
1111 // If there are dots in the name, then these appear in pairs (e.g. A1.1.1)!
1112 (comphelper::string::getTokenCount(*pTableNmBox
, '.') - 1) & 1 )
1114 sTableNm
= pTableNmBox
->copy( 0, nSeparator
);
1115 *pTableNmBox
= pTableNmBox
->copy( nSeparator
+ 1); // remove dot
1116 const SwTable
* pFnd
= FindTable( *rTable
.GetFrameFormat()->GetDoc(), sTableNm
);
1120 if( TBL_MERGETBL
== rTableUpd
.m_eFlags
)
1124 if( pFnd
== rTableUpd
.m_aData
.pDelTable
)
1126 if( rTableUpd
.m_pTable
!= &rTable
) // not the current one
1127 rNewStr
.append(rTableUpd
.m_pTable
->GetFrameFormat()->GetName() + "."); // set new table name
1128 rTableUpd
.m_bModified
= true;
1130 else if( pFnd
!= rTableUpd
.m_pTable
||
1131 ( rTableUpd
.m_pTable
!= &rTable
&& &rTable
!= rTableUpd
.m_aData
.pDelTable
))
1132 rNewStr
.append(sTableNm
+ "."); // keep table name
1134 rTableUpd
.m_bModified
= true;
1137 rNewStr
.append(sTableNm
+ "."); // keep table name
1140 if( pTableNmBox
== pLastBox
)
1141 rFirstBox
= rFirstBox
.copy( nLastBoxLen
+ 1 );
1143 SwTableBox
* pSttBox
= nullptr, *pEndBox
= nullptr;
1148 pEndBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(pLastBox
->toInt64()));
1149 pSttBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(rFirstBox
.toInt64()));
1154 const SwNode
* pNd
= GetNodeOfFormula();
1155 const SwTableBox
* pBox
= pNd
? pTable
->GetTableBox(
1156 pNd
->FindTableBoxStartNode()->GetIndex() ) : nullptr;
1158 pEndBox
= const_cast<SwTableBox
*>(lcl_RelToBox( *pTable
, pBox
, *pLastBox
));
1159 pSttBox
= const_cast<SwTableBox
*>(lcl_RelToBox( *pTable
, pBox
, rFirstBox
));
1165 pEndBox
= const_cast<SwTableBox
*>(pTable
->GetTableBox( *pLastBox
));
1166 pSttBox
= const_cast<SwTableBox
*>(pTable
->GetTableBox( rFirstBox
));
1170 if( pLastBox
&& pTable
->GetTabSortBoxes().find( pEndBox
) == pTable
->GetTabSortBoxes().end() )
1172 if( pTable
->GetTabSortBoxes().find( pSttBox
) == pTable
->GetTabSortBoxes().end() )
1175 if( TBL_SPLITTBL
== rTableUpd
.m_eFlags
)
1177 // Where are the boxes - in the old or in the new table?
1178 bool bInNewTable
= false;
1181 // It is the "first" box in this selection. It determines if the formula is placed in
1182 // the new or the old table.
1183 sal_uInt16 nEndLnPos
= SwTableFormula::GetLnPosInTable( *pTable
, pEndBox
),
1184 nSttLnPos
= SwTableFormula::GetLnPosInTable( *pTable
, pSttBox
);
1186 if( USHRT_MAX
!= nSttLnPos
&& USHRT_MAX
!= nEndLnPos
&&
1187 ((rTableUpd
.m_nSplitLine
<= nSttLnPos
) ==
1188 (rTableUpd
.m_nSplitLine
<= nEndLnPos
)) )
1190 // stay in same table
1191 bInNewTable
= rTableUpd
.m_nSplitLine
<= nEndLnPos
&&
1192 pTable
== rTableUpd
.m_pTable
;
1196 // this is definitely an invalid formula, also mark as modified for Undo
1197 rTableUpd
.m_bModified
= true;
1199 bInNewTable
= USHRT_MAX
!= nEndLnPos
&&
1200 rTableUpd
.m_nSplitLine
<= nEndLnPos
&&
1201 pTable
== rTableUpd
.m_pTable
;
1206 sal_uInt16 nSttLnPos
= SwTableFormula::GetLnPosInTable( *pTable
, pSttBox
);
1207 // Put it in the new table?
1208 bInNewTable
= USHRT_MAX
!= nSttLnPos
&&
1209 rTableUpd
.m_nSplitLine
<= nSttLnPos
&&
1210 pTable
== rTableUpd
.m_pTable
;
1213 // formula goes into new table
1214 if( rTableUpd
.m_bBehindSplitLine
)
1218 rTableUpd
.m_bModified
= true;
1219 rNewStr
.append(rTableUpd
.m_pTable
->GetFrameFormat()->GetName() + ".");
1221 else if( !sTableNm
.isEmpty() )
1222 rNewStr
.append(sTableNm
+ ".");
1224 else if( bInNewTable
)
1226 rTableUpd
.m_bModified
= true;
1227 rNewStr
.append(*rTableUpd
.m_aData
.pNewTableNm
+ ".");
1229 else if( !sTableNm
.isEmpty() )
1230 rNewStr
.append(sTableNm
+ ".");
1234 rNewStr
.append(OUString::number(reinterpret_cast<sal_IntPtr
>(pEndBox
)) + ":");
1236 rNewStr
.append(OUString::number(reinterpret_cast<sal_IntPtr
>(pSttBox
))
1237 + OUStringChar(rFirstBox
[ rFirstBox
.getLength()-1] ));
1240 /// Create external formula but remember that the formula is placed in a split/merged table
1241 void SwTableFormula::ToSplitMergeBoxNm( SwTableFormulaUpdate
& rTableUpd
)
1243 const SwTable
* pTable
;
1244 const SwNode
* pNd
= GetNodeOfFormula();
1245 if( pNd
&& nullptr != ( pNd
= pNd
->FindTableNode() ))
1246 pTable
= &static_cast<const SwTableNode
*>(pNd
)->GetTable();
1248 pTable
= rTableUpd
.m_pTable
;
1250 m_sFormula
= ScanString( &SwTableFormula::SplitMergeBoxNm_
, *pTable
, static_cast<void*>(&rTableUpd
) );
1251 m_eNmType
= INTRNL_NAME
;
1254 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */