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>
37 #include <swtable.hxx>
39 #include <cellfml.hxx>
44 #include <cellatr.hxx>
45 #include <ndindex.hxx>
46 #include <frameformats.hxx>
47 #include <comphelper/string.hxx>
48 #include <o3tl/safeint.hxx>
53 const sal_Unicode cRelSeparator
= ',';
54 const sal_Unicode cRelIdentifier
= '\x12'; // CTRL-R
63 static const SwFrame
* lcl_GetBoxFrame( const SwTableBox
& rBox
);
64 static sal_Int32
lcl_GetLongBoxNum( OUString
& rStr
);
65 static const SwTableBox
* lcl_RelToBox( const SwTable
& rTable
,
66 const SwTableBox
* pRefBox
,
67 const OUString
& sGetName
);
68 static OUString
lcl_BoxNmToRel( const SwTable
& rTable
,
69 const SwTableNode
& rTableNd
,
70 const OUString
& sRefBoxNm
,
71 const OUString
& sGetStr
,
74 /** Get value of this box.
76 * The value is comes from the first TextNode. If it starts with a number/
77 * formula then calculate it, if it starts with a field then get the value.
78 * All other conditions return 0 (and an error?).
80 double SwTableBox::GetValue( SwTableCalcPara
& rCalcPara
) const
84 if( rCalcPara
.m_rCalc
.IsCalcError() )
85 return nRet
; // stop if there is already an error set
87 rCalcPara
.m_rCalc
.SetCalcError( SwCalcError::Syntax
); // default: error
93 if( rCalcPara
.IncStackCnt() )
96 rCalcPara
.SetLastTableBox( this );
98 // Does it create a recursion?
99 SwTableBox
* pBox
= const_cast<SwTableBox
*>(this);
100 if( rCalcPara
.m_pBoxStack
->find( pBox
) != rCalcPara
.m_pBoxStack
->end() )
101 return nRet
; // already on the stack: error
103 // re-start with this box
104 rCalcPara
.SetLastTableBox( this );
106 rCalcPara
.m_pBoxStack
->insert( pBox
); // add
107 do { // Middle-Check-Loop, so that we can jump from here. Used so that the box pointer
108 // will be removed from stack at the end.
109 SwDoc
* pDoc
= GetFrameFormat()->GetDoc();
111 const SfxPoolItem
* pItem
;
112 if( SfxItemState::SET
== GetFrameFormat()->GetItemState(
113 RES_BOXATR_FORMULA
, false, &pItem
) )
115 rCalcPara
.m_rCalc
.SetCalcError( SwCalcError::NONE
); // reset status
116 if( !static_cast<const SwTableBoxFormula
*>(pItem
)->IsValid() )
119 const SwTable
* pTmp
= rCalcPara
.m_pTable
;
120 rCalcPara
.m_pTable
= &pBox
->GetSttNd()->FindTableNode()->GetTable();
121 const_cast<SwTableBoxFormula
*>(static_cast<const SwTableBoxFormula
*>(pItem
))->Calc( rCalcPara
, nRet
);
123 if( !rCalcPara
.IsStackOverflow() )
125 SwFrameFormat
* pFormat
= pBox
->ClaimFrameFormat();
126 SfxItemSet
aTmp( pDoc
->GetAttrPool(),
127 svl::Items
<RES_BOXATR_BEGIN
,RES_BOXATR_END
-1>{} );
128 aTmp
.Put( SwTableBoxValue( nRet
) );
129 if( SfxItemState::SET
!= pFormat
->GetItemState( RES_BOXATR_FORMAT
))
130 aTmp
.Put( SwTableBoxNumFormat( 0 ));
131 pFormat
->SetFormatAttr( aTmp
);
133 rCalcPara
.m_pTable
= pTmp
;
136 nRet
= GetFrameFormat()->GetTableBoxValue().GetValue();
139 else if( SfxItemState::SET
== pBox
->GetFrameFormat()->GetItemState(
140 RES_BOXATR_VALUE
, false, &pItem
) )
142 rCalcPara
.m_rCalc
.SetCalcError( SwCalcError::NONE
); // reset status
143 nRet
= static_cast<const SwTableBoxValue
*>(pItem
)->GetValue();
147 SwTextNode
* pTextNd
= pDoc
->GetNodes()[ m_pStartNode
->GetIndex() + 1 ]->GetTextNode();
151 sal_Int32 nSttPos
= 0;
152 OUString sText
= pTextNd
->GetText();
153 while ( nSttPos
< sText
.getLength() && ( sText
[nSttPos
]==' ' || sText
[nSttPos
]=='\t' ) )
156 // if there is a calculation field at position 1, get the value of it
157 const bool bOK
= nSttPos
<sText
.getLength();
158 const sal_Unicode Char
= bOK
? sText
[nSttPos
] : 0;
159 SwTextField
* pTextField
= nullptr;
160 if ( bOK
&& (Char
==CH_TXTATR_BREAKWORD
|| Char
==CH_TXTATR_INWORD
) )
162 pTextField
= static_txtattr_cast
<SwTextField
*>(pTextNd
->GetTextAttrForCharAt(nSttPos
, RES_TXTATR_FIELD
));
164 if ( pTextField
!= nullptr )
166 rCalcPara
.m_rCalc
.SetCalcError( SwCalcError::NONE
); // reset status
168 const SwField
* pField
= pTextField
->GetFormatField().GetField();
169 switch ( pField
->GetTyp()->Which() )
171 case SwFieldIds::SetExp
:
172 nRet
= static_cast<const SwSetExpField
*>(pField
)->GetValue(rCalcPara
.m_pLayout
);
174 case SwFieldIds::User
:
175 nRet
= static_cast<const SwUserField
*>(pField
)->GetValue();
177 case SwFieldIds::Table
:
179 SwTableField
* pTableField
= const_cast<SwTableField
*>(static_cast<const SwTableField
*>(pField
));
180 if( !pTableField
->IsValid() )
182 // use the right table!
183 const SwTable
* pTmp
= rCalcPara
.m_pTable
;
184 rCalcPara
.m_pTable
= &pTextNd
->FindTableNode()->GetTable();
185 pTableField
->CalcField( rCalcPara
);
186 rCalcPara
.m_pTable
= pTmp
;
188 nRet
= pTableField
->GetValue();
192 case SwFieldIds::DateTime
:
193 nRet
= static_cast<const SwDateTimeField
*>( pField
)->GetValue();
196 case SwFieldIds::JumpEdit
:
197 //JP 14.09.98: Bug 56112 - placeholder never have the right content!
202 nRet
= rCalcPara
.m_rCalc
.Calculate( pField
->ExpandField(true, nullptr) ).GetDouble();
205 else if ( nSttPos
< sText
.getLength()
206 && Char
== CH_TXT_ATR_INPUTFIELDSTART
)
208 const SwTextInputField
* pTextInputField
=
209 dynamic_cast< const SwTextInputField
* >(
210 pTextNd
->GetTextAttrAt( nSttPos
, RES_TXTATR_INPUTFIELD
) );
211 if ( pTextInputField
== nullptr )
213 nRet
= rCalcPara
.m_rCalc
.Calculate( pTextInputField
->GetFieldContent() ).GetDouble();
215 else if ( Char
!= CH_TXTATR_BREAKWORD
)
217 // result is 0 but no error!
218 rCalcPara
.m_rCalc
.SetCalcError( SwCalcError::NONE
); // reset status
221 sText
= bOK
? sText
.copy( nSttPos
) : OUString();
222 sal_uInt32 nFormatIndex
= GetFrameFormat()->GetTableBoxNumFormat().GetValue();
224 SvNumberFormatter
* pNumFormatr
= pDoc
->GetNumberFormatter();
226 const SvNumFormatType nFormatType
= pNumFormatr
->GetType( nFormatIndex
);
227 if( nFormatType
== SvNumFormatType::TEXT
)
229 // JP 22.04.98: Bug 49659 - special treatment for percentages
230 else if( !sText
.isEmpty() &&
231 SvNumFormatType::PERCENT
== nFormatType
)
233 sal_uInt32 nTmpFormat
= 0;
234 if( pDoc
->IsNumberFormat( sText
, nTmpFormat
, aNum
) &&
235 SvNumFormatType::NUMBER
== pNumFormatr
->GetType( nTmpFormat
))
239 if( pDoc
->IsNumberFormat( sText
, nFormatIndex
, aNum
))
242 rCalcPara
.m_rCalc
.SetCalcError( SwCalcError::NaN
); // set for interoperability functions
244 // ?? otherwise it is an error
247 if( !rCalcPara
.IsStackOverflow() )
249 rCalcPara
.m_pBoxStack
->erase( pBox
); // remove from stack
250 rCalcPara
.DecStackCnt();
253 //JP 12.01.99: error detection, Bug 60794
254 if( DBL_MAX
== nRet
)
255 rCalcPara
.m_rCalc
.SetCalcError( SwCalcError::Syntax
); // set error
260 // structure needed for calculation of tables
262 SwTableCalcPara::SwTableCalcPara(SwCalc
& rCalculator
, const SwTable
& rTable
,
263 SwRootFrame
const*const pLayout
)
264 : m_pLastTableBox(nullptr)
266 , m_nMaxSize( cMAXSTACKSIZE
)
268 , m_pBoxStack( new SwTableSortBoxes
)
269 , m_rCalc( rCalculator
)
270 , m_pTable( &rTable
)
274 SwTableCalcPara::~SwTableCalcPara()
278 bool SwTableCalcPara::CalcWithStackOverflow()
280 // If a stack overflow was detected, redo with last box.
281 sal_uInt16 nSaveMaxSize
= m_nMaxSize
;
283 m_nMaxSize
= cMAXSTACKSIZE
- 5;
285 SwTableBoxes aStackOverflows
;
287 SwTableBox
* pBox
= const_cast<SwTableBox
*>(m_pLastTableBox
);
289 m_rCalc
.SetCalcError( SwCalcError::NONE
);
290 aStackOverflows
.insert( aStackOverflows
.begin() + nCnt
++, pBox
);
292 m_pBoxStack
->erase( pBox
);
293 pBox
->GetValue( *this );
294 } while( IsStackOverflow() );
296 m_nMaxSize
= cMAXSTACKSIZE
- 3; // decrease at least one level
298 // if recursion was detected
300 m_rCalc
.SetCalcError( SwCalcError::NONE
);
301 m_pBoxStack
->clear();
303 while( !m_rCalc
.IsCalcError() && nCnt
)
305 aStackOverflows
[ --nCnt
]->GetValue( *this );
306 if( IsStackOverflow() && !CalcWithStackOverflow() )
310 m_nMaxSize
= nSaveMaxSize
;
311 aStackOverflows
.clear();
312 return !m_rCalc
.IsCalcError();
315 SwTableFormula::SwTableFormula( const OUString
& rFormula
)
316 : m_sFormula( rFormula
)
317 , m_eNmType( EXTRNL_NAME
)
318 , m_bValidValue( false )
322 SwTableFormula::~SwTableFormula()
326 void SwTableFormula::MakeFormula_( const SwTable
& rTable
, OUStringBuffer
& rNewStr
,
327 OUString
& rFirstBox
, OUString
* pLastBox
, void* pPara
) const
329 SwTableCalcPara
* pCalcPara
= static_cast<SwTableCalcPara
*>(pPara
);
330 if( pCalcPara
->m_rCalc
.IsCalcError() ) // stop if there is already an error set
333 SwTableBox
*pEndBox
= nullptr;
335 rFirstBox
= rFirstBox
.copy(1); // erase label of this box
336 // a region in this area?
339 pEndBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(pLastBox
->toInt64()));
341 // Is it actually a valid pointer?
342 if( rTable
.GetTabSortBoxes().find( pEndBox
) == rTable
.GetTabSortBoxes().end() )
344 rFirstBox
= rFirstBox
.copy( pLastBox
->getLength()+1 );
346 SwTableBox
* pSttBox
= reinterpret_cast<SwTableBox
*>(
347 sal::static_int_cast
<sal_IntPtr
>(rFirstBox
.toInt64()));
348 // Is it actually a valid pointer?
349 if( rTable
.GetTabSortBoxes().find( pSttBox
) == rTable
.GetTabSortBoxes().end() )
353 if( pEndBox
&& pSttBox
) // area?
355 // get all selected boxes via layout and calculate their values
357 GetBoxes( *pSttBox
, *pEndBox
, aBoxes
);
359 // don't use empty cells or cells with text content as zeroes in interoperability functions
360 sal_Int16 nUseOnlyNumber
= -1;
364 for (size_t n
= 0; n
< aBoxes
.size() &&
365 !pCalcPara
->m_rCalc
.IsCalcError(); ++n
)
367 const SwTableBox
* pTableBox
= aBoxes
[n
];
368 if ( pTableBox
->getRowSpan() >= 1 )
370 double fVal
= pTableBox
->GetValue( *pCalcPara
);
372 if ( pCalcPara
->m_rCalc
.IsCalcNotANumber() )
374 if ( nUseOnlyNumber
== -1 )
376 OUString sFormula
= rNewStr
.toString().toAsciiUpperCase();
377 nUseOnlyNumber
= sal_Int16(
378 sFormula
.lastIndexOf("AVERAGE") > -1 ||
379 sFormula
.lastIndexOf("COUNT") > -1 ||
380 sFormula
.lastIndexOf("PRODUCT") > -1 );
382 if ( nUseOnlyNumber
> 0 )
387 rNewStr
.append(cListDelim
);
389 rNewStr
.append(pCalcPara
->m_rCalc
.GetStrResult( fVal
));
394 else if( pSttBox
&& !pLastBox
) // only the StartBox?
396 // JP 12.01.99: and no EndBox in the formula!
397 // calculate the value of the box
398 if ( pSttBox
->getRowSpan() >= 1 )
401 double fVal
= pSttBox
->GetValue( *pCalcPara
);
402 // don't use empty cell or a cell with text content as zero in interoperability functions
403 // (except PRODUCT, where the result is correct anyway)
404 if ( !pCalcPara
->m_rCalc
.IsCalcNotANumber() ||
405 ( rNewStr
.toString().toAsciiUpperCase().lastIndexOf("AVERAGE") == -1 &&
406 rNewStr
.toString().toAsciiUpperCase().lastIndexOf("COUNT") == -1 ) )
408 rNewStr
.append(pCalcPara
->m_rCalc
.GetStrResult( fVal
));
414 pCalcPara
->m_rCalc
.SetCalcError( SwCalcError::Syntax
); // set error
418 void SwTableFormula::RelNmsToBoxNms( const SwTable
& rTable
, OUStringBuffer
& rNewStr
,
419 OUString
& rFirstBox
, OUString
* pLastBox
, void* pPara
) const
421 // relative name w.r.t. box name (external presentation)
422 SwNode
* pNd
= static_cast<SwNode
*>(pPara
);
423 OSL_ENSURE( pNd
, "Field isn't in any TextNode" );
424 const SwTableBox
*pBox
= rTable
.GetTableBox(
425 pNd
->FindTableBoxStartNode()->GetIndex() );
427 rNewStr
.append(rFirstBox
[0]); // get label for the box
428 rFirstBox
= rFirstBox
.copy(1);
431 const SwTableBox
*pRelLastBox
= lcl_RelToBox( rTable
, pBox
, *pLastBox
);
433 rNewStr
.append(pRelLastBox
->GetName());
435 rNewStr
.append("A1");
437 rFirstBox
= rFirstBox
.copy( pLastBox
->getLength()+1 );
440 const SwTableBox
*pRelFirstBox
= lcl_RelToBox( rTable
, pBox
, rFirstBox
);
443 rNewStr
.append(pRelFirstBox
->GetName());
445 rNewStr
.append("A1");
447 // get label for the box
448 rNewStr
.append(rFirstBox
[ rFirstBox
.getLength()-1 ]);
451 void SwTableFormula::RelBoxNmsToPtr( const SwTable
& rTable
, OUStringBuffer
& rNewStr
,
452 OUString
& rFirstBox
, OUString
* pLastBox
, void* pPara
) const
454 // relative name w.r.t. box name (internal presentation)
455 SwNode
* pNd
= static_cast<SwNode
*>(pPara
);
456 OSL_ENSURE( pNd
, "Field not placed in any Node" );
457 const SwTableBox
*pBox
= rTable
.GetTableBox(
458 pNd
->FindTableBoxStartNode()->GetIndex() );
460 rNewStr
.append(rFirstBox
[0]); // get label for the box
461 rFirstBox
= rFirstBox
.copy(1);
464 const SwTableBox
*pRelLastBox
= lcl_RelToBox( rTable
, pBox
, *pLastBox
);
466 rNewStr
.append(OUString::number(reinterpret_cast<sal_PtrDiff
>(pRelLastBox
)));
470 rFirstBox
= rFirstBox
.copy( pLastBox
->getLength()+1 );
473 const SwTableBox
*pRelFirstBox
= lcl_RelToBox( rTable
, pBox
, rFirstBox
);
475 rNewStr
.append(OUString::number(reinterpret_cast<sal_PtrDiff
>(pRelFirstBox
)));
479 // get label for the box
480 rNewStr
.append(rFirstBox
[ rFirstBox
.getLength()-1 ]);
483 void SwTableFormula::BoxNmsToRelNm( const SwTable
& rTable
, OUStringBuffer
& rNewStr
,
484 OUString
& rFirstBox
, OUString
* pLastBox
, void* pPara
) const
486 // box name (external presentation) w.r.t. relative name
487 SwNode
* pNd
= static_cast<SwNode
*>(pPara
);
488 OSL_ENSURE( pNd
, "Field not placed in any Node" );
489 const SwTableNode
* pTableNd
= pNd
->FindTableNode();
492 if( &pTableNd
->GetTable() == &rTable
)
494 const SwTableBox
*pBox
= rTable
.GetTableBox(
495 pNd
->FindTableBoxStartNode()->GetIndex() );
496 OSL_ENSURE( pBox
, "Field not placed in any Table" );
497 sRefBoxNm
= pBox
->GetName();
500 rNewStr
.append(rFirstBox
[0]); // get label for the box
501 rFirstBox
= rFirstBox
.copy(1);
504 rNewStr
.append(lcl_BoxNmToRel( rTable
, *pTableNd
, sRefBoxNm
, *pLastBox
,
505 m_eNmType
== EXTRNL_NAME
));
507 rFirstBox
= rFirstBox
.copy( pLastBox
->getLength()+1 );
510 rNewStr
.append(lcl_BoxNmToRel( rTable
, *pTableNd
, sRefBoxNm
, rFirstBox
,
511 m_eNmType
== EXTRNL_NAME
));
513 // get label for the box
514 rNewStr
.append(rFirstBox
[ rFirstBox
.getLength()-1 ]);
517 void SwTableFormula::PtrToBoxNms( const SwTable
& rTable
, OUStringBuffer
& rNewStr
,
518 OUString
& rFirstBox
, OUString
* pLastBox
, void* ) const
520 // area in these parentheses?
523 rNewStr
.append(rFirstBox
[0]); // get label for the box
524 rFirstBox
= rFirstBox
.copy(1);
527 pBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(pLastBox
->toInt64()));
529 // Is it actually a valid pointer?
530 if( rTable
.GetTabSortBoxes().find( pBox
) != rTable
.GetTabSortBoxes().end() )
531 rNewStr
.append(pBox
->GetName());
535 rFirstBox
= rFirstBox
.copy( pLastBox
->getLength()+1 );
538 pBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(rFirstBox
.toInt64()));
539 // Is it actually a valid pointer?
540 if( rTable
.GetTabSortBoxes().find( pBox
) != rTable
.GetTabSortBoxes().end() )
541 rNewStr
.append(pBox
->GetName());
545 // get label for the box
546 rNewStr
.append(rFirstBox
[ rFirstBox
.getLength()-1 ]);
549 void SwTableFormula::BoxNmsToPtr( const SwTable
& rTable
, OUStringBuffer
& rNewStr
,
550 OUString
& rFirstBox
, OUString
* pLastBox
, void* ) const
552 // area in these parentheses?
553 const SwTableBox
* pBox
;
555 rNewStr
.append(rFirstBox
[0]); // get label for the box
556 rFirstBox
= rFirstBox
.copy(1);
559 pBox
= rTable
.GetTableBox( *pLastBox
);
560 rNewStr
.append(OUString::number(reinterpret_cast<sal_PtrDiff
>(pBox
)))
562 rFirstBox
= rFirstBox
.copy( pLastBox
->getLength()+1 );
565 pBox
= rTable
.GetTableBox( rFirstBox
);
566 rNewStr
.append(OUString::number(reinterpret_cast<sal_PtrDiff
>(pBox
)))
567 .append(rFirstBox
[ rFirstBox
.getLength()-1 ]); // get label for the box
570 /// create external formula (for UI)
571 void SwTableFormula::PtrToBoxNm( const SwTable
* pTable
)
573 const SwNode
* pNd
= nullptr;
574 FnScanFormula fnFormula
= nullptr;
579 fnFormula
= &SwTableFormula::PtrToBoxNms
;
584 fnFormula
= &SwTableFormula::RelNmsToBoxNms
;
585 pNd
= GetNodeOfFormula();
591 m_sFormula
= ScanString( fnFormula
, *pTable
, const_cast<void*>(static_cast<void const *>(pNd
)) );
592 m_eNmType
= EXTRNL_NAME
;
595 /// create internal formula (in CORE)
596 void SwTableFormula::BoxNmToPtr( const SwTable
* pTable
)
598 const SwNode
* pNd
= nullptr;
599 FnScanFormula fnFormula
= nullptr;
604 fnFormula
= &SwTableFormula::BoxNmsToPtr
;
609 fnFormula
= &SwTableFormula::RelBoxNmsToPtr
;
610 pNd
= GetNodeOfFormula();
616 m_sFormula
= ScanString( fnFormula
, *pTable
, const_cast<void*>(static_cast<void const *>(pNd
)) );
617 m_eNmType
= INTRNL_NAME
;
620 /// create relative formula (for copy)
621 void SwTableFormula::ToRelBoxNm( const SwTable
* pTable
)
623 const SwNode
* pNd
= nullptr;
624 FnScanFormula fnFormula
= nullptr;
631 fnFormula
= &SwTableFormula::BoxNmsToRelNm
;
632 pNd
= GetNodeOfFormula();
638 m_sFormula
= ScanString( fnFormula
, *pTable
, const_cast<void*>(static_cast<void const *>(pNd
)) );
639 m_eNmType
= REL_NAME
;
642 OUString
SwTableFormula::ScanString( FnScanFormula fnFormula
, const SwTable
& rTable
,
646 sal_Int32 nFormula
= 0;
650 // If the formula is preceded by a name, use this table!
651 const SwTable
* pTable
= &rTable
;
653 sal_Int32 nStt
= m_sFormula
.indexOf( '<', nFormula
);
658 const sal_Int32 nNxt
= nStt
+1;
659 if (nNxt
>=m_sFormula
.getLength())
664 if ( m_sFormula
[nNxt
]!=' ' && m_sFormula
[nNxt
]!='=' )
666 nStt
= m_sFormula
.indexOf( '<', nNxt
);
670 // Start searching from current position, which is valid for sure
671 nEnd
= m_sFormula
.indexOf( '>', nStt
);
673 if (nStt
<0 || nEnd
<0 )
675 // set the rest and finish
676 aStr
.append(std::u16string_view(m_sFormula
).substr(nFormula
));
681 aStr
.append(std::u16string_view(m_sFormula
).substr(nFormula
, nStt
- nFormula
));
685 sal_Int32 nSeparator
= 0;
686 // Is a table name preceded?
687 // JP 16.02.99: SplitMergeBoxNm take care of the name themself
688 // JP 22.02.99: Linux compiler needs cast
689 // JP 28.06.99: rel. BoxName has no preceding tablename!
690 if( fnFormula
!= &SwTableFormula::SplitMergeBoxNm_
&&
691 m_sFormula
.getLength()>(nStt
+1) && cRelIdentifier
!= m_sFormula
[nStt
+1] &&
692 (nSeparator
= m_sFormula
.indexOf( '.', nStt
))>=0
693 && nSeparator
< nEnd
)
695 OUString
sTableNm( m_sFormula
.copy( nStt
, nEnd
- nStt
));
697 // If there are dots in the name, then they appear in pairs (e.g. A1.1.1)!
698 if( (comphelper::string::getTokenCount(sTableNm
, '.') - 1) & 1 )
700 sTableNm
= sTableNm
.copy( 0, nSeparator
- nStt
);
702 // when creating a formula the table name is unwanted
703 if( fnFormula
!= &SwTableFormula::MakeFormula_
)
704 aStr
.append(sTableNm
);
707 sTableNm
= sTableNm
.copy( 1 ); // delete separator
708 if( sTableNm
!= rTable
.GetFrameFormat()->GetName() )
710 // then search for table
711 const SwTable
* pFnd
= FindTable(
712 *rTable
.GetFrameFormat()->GetDoc(),
717 OSL_ENSURE( pFnd
, "No table found. What now?" );
722 OUString
sBox( m_sFormula
.copy( nStt
, nEnd
- nStt
+ 1 ));
723 // area in these parentheses?
724 nSeparator
= m_sFormula
.indexOf( ':', nStt
);
725 if ( nSeparator
>=0 && nSeparator
<nEnd
)
727 // without opening parenthesis
728 OUString
aFirstBox( m_sFormula
.copy( nStt
+1, nSeparator
- nStt
- 1 ));
729 (this->*fnFormula
)( *pTable
, aStr
, sBox
, &aFirstBox
, pPara
);
732 (this->*fnFormula
)( *pTable
, aStr
, sBox
, nullptr, pPara
);
737 return aStr
.makeStringAndClear();
740 const SwTable
* SwTableFormula::FindTable( SwDoc
& rDoc
, const OUString
& rNm
)
742 const SwFrameFormats
& rTableFormats
= *rDoc
.GetTableFrameFormats();
743 const SwTable
* pTmpTable
= nullptr, *pRet
= nullptr;
744 for( auto nFormatCnt
= rTableFormats
.size(); nFormatCnt
; )
746 SwFrameFormat
* pFormat
= rTableFormats
[ --nFormatCnt
];
747 // if we are called from Sw3Writer, a number is dependent on the format name
749 if ( rNm
== pFormat
->GetName().getToken(0, 0x0a) &&
750 nullptr != ( pTmpTable
= SwTable::FindTable( pFormat
) ) &&
751 nullptr != (pFBox
= pTmpTable
->GetTabSortBoxes()[0] ) &&
753 pFBox
->GetSttNd()->GetNodes().IsDocNodes() )
755 // a table in the normal NodesArr
763 static const SwFrame
* lcl_GetBoxFrame( const SwTableBox
& rBox
)
765 SwNodeIndex
aIdx( *rBox
.GetSttNd() );
766 SwContentNode
* pCNd
= aIdx
.GetNodes().GoNext( &aIdx
);
767 OSL_ENSURE( pCNd
, "Box has no TextNode" );
768 Point aPt
; // get the first frame of the layout - table headline
769 std::pair
<Point
, bool> const tmp(aPt
, false);
770 return pCNd
->getLayoutFrame(pCNd
->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp
);
773 static sal_Int32
lcl_GetLongBoxNum( OUString
& rStr
)
776 const sal_Int32 nPos
= rStr
.indexOf( cRelSeparator
);
779 nRet
= rStr
.toInt32();
784 nRet
= rStr
.copy( 0, nPos
).toInt32();
785 rStr
= rStr
.copy( nPos
+1 );
790 static const SwTableBox
* lcl_RelToBox( const SwTable
& rTable
,
791 const SwTableBox
* pRefBox
,
792 const OUString
& _sGetName
)
795 const SwTableBox
* pBox
= nullptr;
796 OUString sGetName
= _sGetName
;
798 // Is it really a relative value?
799 if ( cRelIdentifier
== sGetName
[0] ) // yes
804 sGetName
= sGetName
.copy( 1 );
806 const SwTableLines
* pLines
= &rTable
.GetTabLines();
807 const SwTableBoxes
* pBoxes
;
808 const SwTableLine
* pLine
;
810 // determine starting values of the box,...
812 pLine
= pBox
->GetUpper();
813 while( pLine
->GetUpper() )
815 pBox
= pLine
->GetUpper();
816 pLine
= pBox
->GetUpper();
818 sal_uInt16 nSttBox
= pLine
->GetBoxPos( pBox
);
819 sal_uInt16 nSttLine
= rTable
.GetTabLines().GetPos( pLine
);
821 const sal_Int32 nBoxOffset
= lcl_GetLongBoxNum( sGetName
) + nSttBox
;
822 const sal_Int32 nLineOffset
= lcl_GetLongBoxNum( sGetName
) + nSttLine
;
824 if( nBoxOffset
< 0 ||
828 if( o3tl::make_unsigned(nLineOffset
) >= pLines
->size() )
831 pLine
= (*pLines
)[ nLineOffset
];
833 // ... then search the box
834 pBoxes
= &pLine
->GetTabBoxes();
835 if( o3tl::make_unsigned(nBoxOffset
) >= pBoxes
->size() )
837 pBox
= (*pBoxes
)[ nBoxOffset
];
839 while (!sGetName
.isEmpty())
841 nSttBox
= SwTable::GetBoxNum( sGetName
);
842 pLines
= &pBox
->GetTabLines();
846 nSttLine
= SwTable::GetBoxNum( sGetName
);
849 if( !nSttLine
|| nSttLine
> pLines
->size() )
851 pLine
= (*pLines
)[ nSttLine
-1 ];
854 pBoxes
= &pLine
->GetTabBoxes();
855 if( nSttBox
>= pBoxes
->size() )
857 pBox
= (*pBoxes
)[ nSttBox
];
862 if( !pBox
->GetSttNd() )
863 // "bubble up" to first box
864 while( !pBox
->GetTabLines().empty() )
865 pBox
= pBox
->GetTabLines().front()->GetTabBoxes().front();
870 // otherwise it is an absolute external presentation
871 pBox
= rTable
.GetTableBox( sGetName
);
876 static OUString
lcl_BoxNmToRel( const SwTable
& rTable
, const SwTableNode
& rTableNd
,
877 const OUString
& _sRefBoxNm
, const OUString
& _sTmp
, bool bExtrnlNm
)
879 OUString sTmp
= _sTmp
;
880 OUString sRefBoxNm
= _sRefBoxNm
;
883 // convert into external presentation
884 SwTableBox
* pBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(sTmp
.toInt64()));
885 if( rTable
.GetTabSortBoxes().find( pBox
) == rTable
.GetTabSortBoxes().end() )
886 return OUString('?');
887 sTmp
= pBox
->GetName();
890 // If the formula is spanning over a table then keep external presentation
891 if( &rTable
== &rTableNd
.GetTable() )
893 tools::Long nBox
= SwTable::GetBoxNum( sTmp
, true );
894 nBox
-= SwTable::GetBoxNum( sRefBoxNm
, true );
895 tools::Long nLine
= SwTable::GetBoxNum( sTmp
);
896 nLine
-= SwTable::GetBoxNum( sRefBoxNm
);
898 const OUString sCpy
= sTmp
; //JP 01.11.95: add rest from box name
900 sTmp
= OUStringChar(cRelIdentifier
) + OUString::number( nBox
)
901 + OUStringChar(cRelSeparator
) + OUString::number( nLine
);
905 sTmp
+= OUStringChar(cRelSeparator
) + sCpy
;
909 if (sTmp
.endsWith(">"))
910 return sTmp
.copy(0, sTmp
.getLength()-1 );
915 void SwTableFormula::GetBoxesOfFormula( const SwTable
& rTable
,
920 BoxNmToPtr( &rTable
);
921 ScanString( &SwTableFormula::GetFormulaBoxes
, rTable
, &rBoxes
);
924 void SwTableFormula::GetFormulaBoxes( const SwTable
& rTable
, OUStringBuffer
& ,
925 OUString
& rFirstBox
, OUString
* pLastBox
, void* pPara
) const
927 SwSelBoxes
* pBoxes
= static_cast<SwSelBoxes
*>(pPara
);
928 SwTableBox
* pEndBox
= nullptr;
930 rFirstBox
= rFirstBox
.copy(1); // delete box label
931 // area in these parentheses?
934 pEndBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(pLastBox
->toInt64()));
936 // Is it actually a valid pointer?
937 if( rTable
.GetTabSortBoxes().find( pEndBox
) == rTable
.GetTabSortBoxes().end() )
939 rFirstBox
= rFirstBox
.copy( pLastBox
->getLength()+1 );
942 SwTableBox
*pSttBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(rFirstBox
.toInt64()));
943 // Is it actually a valid pointer?
944 if( !pSttBox
|| rTable
.GetTabSortBoxes().find( pSttBox
) == rTable
.GetTabSortBoxes().end() )
947 if ( pEndBox
) // area?
949 // get all selected boxes via layout and calculate their values
951 GetBoxes( *pSttBox
, *pEndBox
, aBoxes
);
952 pBoxes
->insert( aBoxes
);
954 else // only the StartBox?
955 pBoxes
->insert( pSttBox
);
958 void SwTableFormula::GetBoxes( const SwTableBox
& rSttBox
,
959 const SwTableBox
& rEndBox
,
962 // get all selected boxes via layout
963 const SwLayoutFrame
*pStt
, *pEnd
;
964 const SwFrame
* pFrame
= lcl_GetBoxFrame( rSttBox
);
965 pStt
= pFrame
? pFrame
->GetUpper() : nullptr;
966 pFrame
= lcl_GetBoxFrame( rEndBox
);
967 pEnd
= pFrame
? pFrame
->GetUpper() : nullptr;
969 return ; // no valid selection
971 GetTableSel( pStt
, pEnd
, rBoxes
, nullptr );
973 const SwTable
* pTable
= pStt
->FindTabFrame()->GetTable();
975 // filter headline boxes
976 if( pTable
->GetRowsToRepeat() <= 0 )
979 do { // middle-check loop
980 const SwTableLine
* pLine
= rSttBox
.GetUpper();
981 while( pLine
->GetUpper() )
982 pLine
= pLine
->GetUpper()->GetUpper();
984 if( pTable
->IsHeadline( *pLine
) )
985 break; // headline in this area!
987 // maybe start and end are swapped
988 pLine
= rEndBox
.GetUpper();
989 while ( pLine
->GetUpper() )
990 pLine
= pLine
->GetUpper()->GetUpper();
992 if( pTable
->IsHeadline( *pLine
) )
993 break; // headline in this area!
995 const SwTabFrame
*pStartTable
= pStt
->FindTabFrame();
996 const SwTabFrame
*pEndTable
= pEnd
->FindTabFrame();
998 if (pStartTable
== pEndTable
) // no split table
1001 // then remove table headers
1002 for (size_t n
= 0; n
< rBoxes
.size(); ++n
)
1004 pLine
= rBoxes
[n
]->GetUpper();
1005 while( pLine
->GetUpper() )
1006 pLine
= pLine
->GetUpper()->GetUpper();
1008 if( pTable
->IsHeadline( *pLine
) )
1009 rBoxes
.erase( rBoxes
.begin() + n
-- );
1014 /// Are all boxes valid that are referenced by the formula?
1015 void SwTableFormula::HasValidBoxes_( const SwTable
& rTable
, OUStringBuffer
& ,
1016 OUString
& rFirstBox
, OUString
* pLastBox
, void* pPara
) const
1018 bool* pBValid
= static_cast<bool*>(pPara
);
1019 if( !(*pBValid
) ) // wrong is wrong
1022 SwTableBox
* pSttBox
= nullptr, *pEndBox
= nullptr;
1023 rFirstBox
= rFirstBox
.copy(1); // delete identifier of box
1025 // area in this parenthesis?
1027 rFirstBox
= rFirstBox
.copy( pLastBox
->getLength()+1 );
1033 pEndBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(pLastBox
->toInt64()));
1034 pSttBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(rFirstBox
.toInt64()));
1039 const SwNode
* pNd
= GetNodeOfFormula();
1040 const SwTableBox
* pBox
= !pNd
? nullptr
1041 : const_cast<SwTableBox
*>(rTable
.GetTableBox(
1042 pNd
->FindTableBoxStartNode()->GetIndex() ));
1044 pEndBox
= const_cast<SwTableBox
*>(lcl_RelToBox( rTable
, pBox
, *pLastBox
));
1045 pSttBox
= const_cast<SwTableBox
*>(lcl_RelToBox( rTable
, pBox
, rFirstBox
));
1051 pEndBox
= const_cast<SwTableBox
*>(rTable
.GetTableBox( *pLastBox
));
1052 pSttBox
= const_cast<SwTableBox
*>(rTable
.GetTableBox( rFirstBox
));
1056 // Are these valid pointers?
1058 ( !pEndBox
|| rTable
.GetTabSortBoxes().find( pEndBox
) == rTable
.GetTabSortBoxes().end() ) ) ||
1059 ( !pSttBox
|| rTable
.GetTabSortBoxes().find( pSttBox
) == rTable
.GetTabSortBoxes().end() ) )
1063 bool SwTableFormula::HasValidBoxes() const
1066 const SwNode
* pNd
= GetNodeOfFormula();
1067 if( pNd
&& nullptr != ( pNd
= pNd
->FindTableNode() ) )
1068 ScanString( &SwTableFormula::HasValidBoxes_
,
1069 static_cast<const SwTableNode
*>(pNd
)->GetTable(), &bRet
);
1073 sal_uInt16
SwTableFormula::GetLnPosInTable( const SwTable
& rTable
, const SwTableBox
* pBox
)
1075 sal_uInt16 nRet
= USHRT_MAX
;
1078 const SwTableLine
* pLn
= pBox
->GetUpper();
1079 while( pLn
->GetUpper() )
1080 pLn
= pLn
->GetUpper()->GetUpper();
1081 nRet
= rTable
.GetTabLines().GetPos( pLn
);
1086 void SwTableFormula::SplitMergeBoxNm_( const SwTable
& rTable
, OUStringBuffer
& rNewStr
,
1087 OUString
& rFirstBox
, OUString
* pLastBox
, void* pPara
) const
1089 SwTableFormulaUpdate
& rTableUpd
= *static_cast<SwTableFormulaUpdate
*>(pPara
);
1091 rNewStr
.append(rFirstBox
[0]); // get label for the box
1092 rFirstBox
= rFirstBox
.copy(1);
1095 const SwTable
* pTable
= &rTable
;
1097 OUString
* pTableNmBox
= pLastBox
? pLastBox
: &rFirstBox
;
1099 const sal_Int32 nLastBoxLen
= pTableNmBox
->getLength();
1100 const sal_Int32 nSeparator
= pTableNmBox
->indexOf('.');
1101 if ( nSeparator
>=0 &&
1102 // If there are dots in the name, then these appear in pairs (e.g. A1.1.1)!
1103 (comphelper::string::getTokenCount(*pTableNmBox
, '.') - 1) & 1 )
1105 sTableNm
= pTableNmBox
->copy( 0, nSeparator
);
1106 *pTableNmBox
= pTableNmBox
->copy( nSeparator
+ 1); // remove dot
1107 const SwTable
* pFnd
= FindTable( *rTable
.GetFrameFormat()->GetDoc(), sTableNm
);
1111 if( TBL_MERGETBL
== rTableUpd
.m_eFlags
)
1115 if( pFnd
== rTableUpd
.m_aData
.pDelTable
)
1117 if( rTableUpd
.m_pTable
!= &rTable
) // not the current one
1118 rNewStr
.append(rTableUpd
.m_pTable
->GetFrameFormat()->GetName()).append("."); // set new table name
1119 rTableUpd
.m_bModified
= true;
1121 else if( pFnd
!= rTableUpd
.m_pTable
||
1122 ( rTableUpd
.m_pTable
!= &rTable
&& &rTable
!= rTableUpd
.m_aData
.pDelTable
))
1123 rNewStr
.append(sTableNm
).append("."); // keep table name
1125 rTableUpd
.m_bModified
= true;
1128 rNewStr
.append(sTableNm
).append("."); // keep table name
1131 if( pTableNmBox
== pLastBox
)
1132 rFirstBox
= rFirstBox
.copy( nLastBoxLen
+ 1 );
1134 SwTableBox
* pSttBox
= nullptr, *pEndBox
= nullptr;
1139 pEndBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(pLastBox
->toInt64()));
1140 pSttBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(rFirstBox
.toInt64()));
1145 const SwNode
* pNd
= GetNodeOfFormula();
1146 const SwTableBox
* pBox
= pNd
? pTable
->GetTableBox(
1147 pNd
->FindTableBoxStartNode()->GetIndex() ) : nullptr;
1149 pEndBox
= const_cast<SwTableBox
*>(lcl_RelToBox( *pTable
, pBox
, *pLastBox
));
1150 pSttBox
= const_cast<SwTableBox
*>(lcl_RelToBox( *pTable
, pBox
, rFirstBox
));
1156 pEndBox
= const_cast<SwTableBox
*>(pTable
->GetTableBox( *pLastBox
));
1157 pSttBox
= const_cast<SwTableBox
*>(pTable
->GetTableBox( rFirstBox
));
1161 if( pLastBox
&& pTable
->GetTabSortBoxes().find( pEndBox
) == pTable
->GetTabSortBoxes().end() )
1163 if( pTable
->GetTabSortBoxes().find( pSttBox
) == pTable
->GetTabSortBoxes().end() )
1166 if( TBL_SPLITTBL
== rTableUpd
.m_eFlags
)
1168 // Where are the boxes - in the old or in the new table?
1169 bool bInNewTable
= false;
1172 // It is the "first" box in this selection. It determines if the formula is placed in
1173 // the new or the old table.
1174 sal_uInt16 nEndLnPos
= SwTableFormula::GetLnPosInTable( *pTable
, pEndBox
),
1175 nSttLnPos
= SwTableFormula::GetLnPosInTable( *pTable
, pSttBox
);
1177 if( USHRT_MAX
!= nSttLnPos
&& USHRT_MAX
!= nEndLnPos
&&
1178 ((rTableUpd
.m_nSplitLine
<= nSttLnPos
) ==
1179 (rTableUpd
.m_nSplitLine
<= nEndLnPos
)) )
1181 // stay in same table
1182 bInNewTable
= rTableUpd
.m_nSplitLine
<= nEndLnPos
&&
1183 pTable
== rTableUpd
.m_pTable
;
1187 // this is definitely an invalid formula, also mark as modified for Undo
1188 rTableUpd
.m_bModified
= true;
1190 bInNewTable
= USHRT_MAX
!= nEndLnPos
&&
1191 rTableUpd
.m_nSplitLine
<= nEndLnPos
&&
1192 pTable
== rTableUpd
.m_pTable
;
1197 sal_uInt16 nSttLnPos
= SwTableFormula::GetLnPosInTable( *pTable
, pSttBox
);
1198 // Put it in the new table?
1199 bInNewTable
= USHRT_MAX
!= nSttLnPos
&&
1200 rTableUpd
.m_nSplitLine
<= nSttLnPos
&&
1201 pTable
== rTableUpd
.m_pTable
;
1204 // formula goes into new table
1205 if( rTableUpd
.m_bBehindSplitLine
)
1209 rTableUpd
.m_bModified
= true;
1210 rNewStr
.append(rTableUpd
.m_pTable
->GetFrameFormat()->GetName()).append(".");
1212 else if( !sTableNm
.isEmpty() )
1213 rNewStr
.append(sTableNm
).append(".");
1215 else if( bInNewTable
)
1217 rTableUpd
.m_bModified
= true;
1218 rNewStr
.append(*rTableUpd
.m_aData
.pNewTableNm
).append(".");
1220 else if( !sTableNm
.isEmpty() )
1221 rNewStr
.append(sTableNm
).append(".");
1225 rNewStr
.append(OUString::number(reinterpret_cast<sal_PtrDiff
>(pEndBox
))).append(":");
1227 rNewStr
.append(OUString::number(reinterpret_cast<sal_PtrDiff
>(pSttBox
)))
1228 .append(rFirstBox
[ rFirstBox
.getLength()-1] );
1231 /// Create external formula but remember that the formula is placed in a split/merged table
1232 void SwTableFormula::ToSplitMergeBoxNm( SwTableFormulaUpdate
& rTableUpd
)
1234 const SwTable
* pTable
;
1235 const SwNode
* pNd
= GetNodeOfFormula();
1236 if( pNd
&& nullptr != ( pNd
= pNd
->FindTableNode() ))
1237 pTable
= &static_cast<const SwTableNode
*>(pNd
)->GetTable();
1239 pTable
= rTableUpd
.m_pTable
;
1241 m_sFormula
= ScanString( &SwTableFormula::SplitMergeBoxNm_
, *pTable
, static_cast<void*>(&rTableUpd
) );
1242 m_eNmType
= INTRNL_NAME
;
1245 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */