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 assert(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 assert(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 assert(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 assert(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();
601 m_sFormula
= ScanString( fnFormula
, *pTable
, const_cast<void*>(static_cast<void const *>(pNd
)) );
602 m_eNmType
= EXTRNL_NAME
;
605 /// create internal formula (in CORE)
606 void SwTableFormula::BoxNmToPtr( const SwTable
* pTable
)
608 const SwNode
* pNd
= nullptr;
609 FnScanFormula fnFormula
= nullptr;
614 fnFormula
= &SwTableFormula::BoxNmsToPtr
;
619 fnFormula
= &SwTableFormula::RelBoxNmsToPtr
;
620 pNd
= GetNodeOfFormula();
627 m_sFormula
= ScanString( fnFormula
, *pTable
, const_cast<void*>(static_cast<void const *>(pNd
)) );
628 m_eNmType
= INTRNL_NAME
;
631 /// create relative formula (for copy)
632 void SwTableFormula::ToRelBoxNm( const SwTable
* pTable
)
634 const SwNode
* pNd
= nullptr;
635 FnScanFormula fnFormula
= nullptr;
642 fnFormula
= &SwTableFormula::BoxNmsToRelNm
;
643 pNd
= GetNodeOfFormula();
650 m_sFormula
= ScanString( fnFormula
, *pTable
, const_cast<void*>(static_cast<void const *>(pNd
)) );
651 m_eNmType
= REL_NAME
;
654 OUString
SwTableFormula::ScanString( FnScanFormula fnFormula
, const SwTable
& rTable
,
658 sal_Int32 nFormula
= 0;
662 // If the formula is preceded by a name, use this table!
663 const SwTable
* pTable
= &rTable
;
665 sal_Int32 nStt
= m_sFormula
.indexOf( '<', nFormula
);
670 const sal_Int32 nNxt
= nStt
+1;
671 if (nNxt
>=m_sFormula
.getLength())
676 if ( m_sFormula
[nNxt
]!=' ' && m_sFormula
[nNxt
]!='=' )
678 nStt
= m_sFormula
.indexOf( '<', nNxt
);
682 // Start searching from current position, which is valid for sure
683 nEnd
= m_sFormula
.indexOf( '>', nStt
);
685 if (nStt
<0 || nEnd
<0 )
687 // set the rest and finish
688 aStr
.append(m_sFormula
.subView(nFormula
));
693 aStr
.append(m_sFormula
.subView(nFormula
, nStt
- nFormula
));
697 sal_Int32 nSeparator
= 0;
698 // Is a table name preceded?
699 // JP 16.02.99: SplitMergeBoxNm take care of the name themself
700 // JP 22.02.99: Linux compiler needs cast
701 // JP 28.06.99: rel. BoxName has no preceding tablename!
702 if( fnFormula
!= &SwTableFormula::SplitMergeBoxNm_
&&
703 m_sFormula
.getLength()>(nStt
+1) && cRelIdentifier
!= m_sFormula
[nStt
+1] &&
704 (nSeparator
= m_sFormula
.indexOf( '.', nStt
))>=0
705 && nSeparator
< nEnd
)
707 OUString
sTableNm( m_sFormula
.copy( nStt
, nEnd
- nStt
));
709 // If there are dots in the name, then they appear in pairs (e.g. A1.1.1)!
710 if( (comphelper::string::getTokenCount(sTableNm
, '.') - 1) & 1 )
712 sTableNm
= sTableNm
.copy( 0, nSeparator
- nStt
);
714 // when creating a formula the table name is unwanted
715 if( fnFormula
!= &SwTableFormula::MakeFormula_
)
716 aStr
.append(sTableNm
);
719 sTableNm
= sTableNm
.copy( 1 ); // delete separator
720 if( sTableNm
!= rTable
.GetFrameFormat()->GetName() )
722 // then search for table
723 const SwTable
* pFnd
= FindTable(
724 *rTable
.GetFrameFormat()->GetDoc(),
729 OSL_ENSURE( pFnd
, "No table found. What now?" );
734 OUString
sBox( m_sFormula
.copy( nStt
, nEnd
- nStt
+ 1 ));
735 // area in these parentheses?
736 nSeparator
= m_sFormula
.indexOf( ':', nStt
);
737 if ( nSeparator
>=0 && nSeparator
<nEnd
)
739 // without opening parenthesis
740 OUString
aFirstBox( m_sFormula
.copy( nStt
+1, nSeparator
- nStt
- 1 ));
741 (this->*fnFormula
)( *pTable
, aStr
, sBox
, &aFirstBox
, pPara
);
744 (this->*fnFormula
)( *pTable
, aStr
, sBox
, nullptr, pPara
);
749 return aStr
.makeStringAndClear();
752 const SwTable
* SwTableFormula::FindTable( SwDoc
& rDoc
, std::u16string_view rNm
)
754 const sw::TableFrameFormats
& rTableFormats
= *rDoc
.GetTableFrameFormats();
755 const SwTable
* pTmpTable
= nullptr, *pRet
= nullptr;
756 for( auto nFormatCnt
= rTableFormats
.size(); nFormatCnt
; )
758 SwTableFormat
* pFormat
= rTableFormats
[ --nFormatCnt
];
759 // if we are called from Sw3Writer, a number is dependent on the format name
761 if ( rNm
== o3tl::getToken(pFormat
->GetName(), 0, 0x0a) &&
762 nullptr != ( pTmpTable
= SwTable::FindTable( pFormat
) ) &&
763 nullptr != (pFBox
= pTmpTable
->GetTabSortBoxes()[0] ) &&
765 pFBox
->GetSttNd()->GetNodes().IsDocNodes() )
767 // a table in the normal NodesArr
775 static const SwFrame
* lcl_GetBoxFrame( const SwTableBox
& rBox
)
777 SwNodeIndex
aIdx( *rBox
.GetSttNd() );
778 SwContentNode
* pCNd
= SwNodes::GoNext(&aIdx
);
779 assert(pCNd
&& "Box has no TextNode");
780 Point aPt
; // get the first frame of the layout - table headline
781 std::pair
<Point
, bool> const tmp(aPt
, false);
782 return pCNd
->getLayoutFrame(pCNd
->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp
);
785 static sal_Int32
lcl_GetLongBoxNum( OUString
& rStr
)
788 const sal_Int32 nPos
= rStr
.indexOf( cRelSeparator
);
791 nRet
= rStr
.toInt32();
796 nRet
= o3tl::toInt32(rStr
.subView( 0, nPos
));
797 rStr
= rStr
.copy( nPos
+1 );
802 static const SwTableBox
* lcl_RelToBox( const SwTable
& rTable
,
803 const SwTableBox
* pRefBox
,
804 const OUString
& _sGetName
)
807 const SwTableBox
* pBox
= nullptr;
808 OUString sGetName
= _sGetName
;
810 // Is it really a relative value?
811 if ( cRelIdentifier
== sGetName
[0] ) // yes
816 sGetName
= sGetName
.copy( 1 );
818 const SwTableLines
* pLines
= &rTable
.GetTabLines();
819 const SwTableBoxes
* pBoxes
;
820 const SwTableLine
* pLine
;
822 // determine starting values of the box,...
824 pLine
= pBox
->GetUpper();
825 while( pLine
->GetUpper() )
827 pBox
= pLine
->GetUpper();
828 pLine
= pBox
->GetUpper();
830 sal_uInt16 nSttBox
= pLine
->GetBoxPos( pBox
);
831 sal_uInt16 nSttLine
= rTable
.GetTabLines().GetPos( pLine
);
833 const sal_Int32 nBoxOffset
= lcl_GetLongBoxNum( sGetName
) + nSttBox
;
834 const sal_Int32 nLineOffset
= lcl_GetLongBoxNum( sGetName
) + nSttLine
;
836 if( nBoxOffset
< 0 ||
840 if( o3tl::make_unsigned(nLineOffset
) >= pLines
->size() )
843 pLine
= (*pLines
)[ nLineOffset
];
845 // ... then search the box
846 pBoxes
= &pLine
->GetTabBoxes();
847 if( o3tl::make_unsigned(nBoxOffset
) >= pBoxes
->size() )
849 pBox
= (*pBoxes
)[ nBoxOffset
];
851 while (!sGetName
.isEmpty())
853 nSttBox
= SwTable::GetBoxNum( sGetName
);
854 pLines
= &pBox
->GetTabLines();
858 nSttLine
= SwTable::GetBoxNum( sGetName
);
861 if( !nSttLine
|| nSttLine
> pLines
->size() )
863 pLine
= (*pLines
)[ nSttLine
-1 ];
866 pBoxes
= &pLine
->GetTabBoxes();
867 if( nSttBox
>= pBoxes
->size() )
869 pBox
= (*pBoxes
)[ nSttBox
];
874 if( !pBox
->GetSttNd() )
875 // "bubble up" to first box
876 while( !pBox
->GetTabLines().empty() )
877 pBox
= pBox
->GetTabLines().front()->GetTabBoxes().front();
882 // otherwise it is an absolute external presentation
883 pBox
= rTable
.GetTableBox( sGetName
);
888 static OUString
lcl_BoxNmToRel( const SwTable
& rTable
, const SwTableNode
& rTableNd
,
889 const OUString
& _sRefBoxNm
, const OUString
& _sTmp
, bool bExtrnlNm
)
891 OUString sTmp
= _sTmp
;
892 OUString sRefBoxNm
= _sRefBoxNm
;
895 // convert into external presentation
896 SwTableBox
* pBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(sTmp
.toInt64()));
897 if( rTable
.GetTabSortBoxes().find( pBox
) == rTable
.GetTabSortBoxes().end() )
898 return OUString('?');
899 sTmp
= pBox
->GetName();
902 // If the formula is spanning over a table then keep external presentation
903 if( &rTable
== &rTableNd
.GetTable() )
905 tools::Long nBox
= SwTable::GetBoxNum( sTmp
, true );
906 nBox
-= SwTable::GetBoxNum( sRefBoxNm
, true );
907 tools::Long nLine
= SwTable::GetBoxNum( sTmp
);
908 nLine
-= SwTable::GetBoxNum( sRefBoxNm
);
910 const OUString sCpy
= sTmp
; //JP 01.11.95: add rest from box name
912 sTmp
= OUStringChar(cRelIdentifier
) + OUString::number( nBox
)
913 + OUStringChar(cRelSeparator
) + OUString::number( nLine
);
917 sTmp
+= OUStringChar(cRelSeparator
) + sCpy
;
921 if (sTmp
.endsWith(">"))
922 return sTmp
.copy(0, sTmp
.getLength()-1 );
927 void SwTableFormula::GetBoxesOfFormula( const SwTable
& rTable
,
932 BoxNmToPtr( &rTable
);
933 ScanString( &SwTableFormula::GetFormulaBoxes
, rTable
, &rBoxes
);
936 void SwTableFormula::GetFormulaBoxes( const SwTable
& rTable
, OUStringBuffer
& ,
937 OUString
& rFirstBox
, OUString
* pLastBox
, void* pPara
) const
939 SwSelBoxes
* pBoxes
= static_cast<SwSelBoxes
*>(pPara
);
940 SwTableBox
* pEndBox
= nullptr;
942 rFirstBox
= rFirstBox
.copy(1); // delete box label
943 // area in these parentheses?
946 pEndBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(pLastBox
->toInt64()));
948 // Is it actually a valid pointer?
949 if( rTable
.GetTabSortBoxes().find( pEndBox
) == rTable
.GetTabSortBoxes().end() )
951 rFirstBox
= rFirstBox
.copy( pLastBox
->getLength()+1 );
954 SwTableBox
*pSttBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(rFirstBox
.toInt64()));
955 // Is it actually a valid pointer?
956 if( !pSttBox
|| rTable
.GetTabSortBoxes().find( pSttBox
) == rTable
.GetTabSortBoxes().end() )
959 if ( pEndBox
) // area?
961 // get all selected boxes via layout and calculate their values
963 GetBoxes( *pSttBox
, *pEndBox
, aBoxes
);
964 pBoxes
->insert( aBoxes
);
966 else // only the StartBox?
967 pBoxes
->insert( pSttBox
);
970 void SwTableFormula::GetBoxes( const SwTableBox
& rSttBox
,
971 const SwTableBox
& rEndBox
,
974 // get all selected boxes via layout
975 const SwLayoutFrame
*pStart
, *pEnd
;
976 const SwFrame
* pFrame
= lcl_GetBoxFrame( rSttBox
);
977 pStart
= pFrame
? pFrame
->GetUpper() : nullptr;
978 pFrame
= lcl_GetBoxFrame( rEndBox
);
979 pEnd
= pFrame
? pFrame
->GetUpper() : nullptr;
980 if( !pStart
|| !pEnd
)
981 return ; // no valid selection
983 GetTableSel( pStart
, pEnd
, rBoxes
, nullptr );
985 const SwTable
* pTable
= pStart
->FindTabFrame()->GetTable();
987 // filter headline boxes
988 if( pTable
->GetRowsToRepeat() <= 0 )
991 do { // middle-check loop
992 const SwTableLine
* pLine
= rSttBox
.GetUpper();
993 while( pLine
->GetUpper() )
994 pLine
= pLine
->GetUpper()->GetUpper();
996 if( pTable
->IsHeadline( *pLine
) )
997 break; // headline in this area!
999 // maybe start and end are swapped
1000 pLine
= rEndBox
.GetUpper();
1001 while ( pLine
->GetUpper() )
1002 pLine
= pLine
->GetUpper()->GetUpper();
1004 if( pTable
->IsHeadline( *pLine
) )
1005 break; // headline in this area!
1007 const SwTabFrame
*pStartTable
= pStart
->FindTabFrame();
1008 const SwTabFrame
*pEndTable
= pEnd
->FindTabFrame();
1010 if (pStartTable
== pEndTable
) // no split table
1013 // then remove table headers
1014 for (size_t n
= 0; n
< rBoxes
.size(); ++n
)
1016 pLine
= rBoxes
[n
]->GetUpper();
1017 while( pLine
->GetUpper() )
1018 pLine
= pLine
->GetUpper()->GetUpper();
1020 if( pTable
->IsHeadline( *pLine
) )
1021 rBoxes
.erase( rBoxes
.begin() + n
-- );
1026 /// Are all boxes valid that are referenced by the formula?
1027 void SwTableFormula::HasValidBoxes_( const SwTable
& rTable
, OUStringBuffer
& ,
1028 OUString
& rFirstBox
, OUString
* pLastBox
, void* pPara
) const
1030 bool* pBValid
= static_cast<bool*>(pPara
);
1031 if( !(*pBValid
) ) // wrong is wrong
1034 SwTableBox
* pSttBox
= nullptr, *pEndBox
= nullptr;
1035 rFirstBox
= rFirstBox
.copy(1); // delete identifier of box
1037 // area in this parenthesis?
1039 rFirstBox
= rFirstBox
.copy( pLastBox
->getLength()+1 );
1045 pEndBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(pLastBox
->toInt64()));
1046 pSttBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(rFirstBox
.toInt64()));
1051 const SwNode
* pNd
= GetNodeOfFormula();
1052 const SwTableBox
* pBox
= !pNd
? nullptr
1053 : const_cast<SwTableBox
*>(rTable
.GetTableBox(
1054 pNd
->FindTableBoxStartNode()->GetIndex() ));
1056 pEndBox
= const_cast<SwTableBox
*>(lcl_RelToBox( rTable
, pBox
, *pLastBox
));
1057 pSttBox
= const_cast<SwTableBox
*>(lcl_RelToBox( rTable
, pBox
, rFirstBox
));
1063 pEndBox
= const_cast<SwTableBox
*>(rTable
.GetTableBox( *pLastBox
));
1064 pSttBox
= const_cast<SwTableBox
*>(rTable
.GetTableBox( rFirstBox
));
1068 // Are these valid pointers?
1070 ( !pEndBox
|| rTable
.GetTabSortBoxes().find( pEndBox
) == rTable
.GetTabSortBoxes().end() ) ) ||
1071 ( !pSttBox
|| rTable
.GetTabSortBoxes().find( pSttBox
) == rTable
.GetTabSortBoxes().end() ) )
1075 bool SwTableFormula::HasValidBoxes() const
1078 const SwNode
* pNd
= GetNodeOfFormula();
1079 if( pNd
&& nullptr != ( pNd
= pNd
->FindTableNode() ) )
1080 ScanString( &SwTableFormula::HasValidBoxes_
,
1081 static_cast<const SwTableNode
*>(pNd
)->GetTable(), &bRet
);
1085 sal_uInt16
SwTableFormula::GetLnPosInTable( const SwTable
& rTable
, const SwTableBox
* pBox
)
1087 sal_uInt16 nRet
= USHRT_MAX
;
1090 const SwTableLine
* pLn
= pBox
->GetUpper();
1091 while( pLn
->GetUpper() )
1092 pLn
= pLn
->GetUpper()->GetUpper();
1093 nRet
= rTable
.GetTabLines().GetPos( pLn
);
1098 void SwTableFormula::SplitMergeBoxNm_( const SwTable
& rTable
, OUStringBuffer
& rNewStr
,
1099 OUString
& rFirstBox
, OUString
* pLastBox
, void* pPara
) const
1101 SwTableFormulaUpdate
& rTableUpd
= *static_cast<SwTableFormulaUpdate
*>(pPara
);
1103 rNewStr
.append(rFirstBox
[0]); // get label for the box
1104 rFirstBox
= rFirstBox
.copy(1);
1107 const SwTable
* pTable
= &rTable
;
1109 OUString
* pTableNmBox
= pLastBox
? pLastBox
: &rFirstBox
;
1111 const sal_Int32 nLastBoxLen
= pTableNmBox
->getLength();
1112 const sal_Int32 nSeparator
= pTableNmBox
->indexOf('.');
1113 if ( nSeparator
>=0 &&
1114 // If there are dots in the name, then these appear in pairs (e.g. A1.1.1)!
1115 (comphelper::string::getTokenCount(*pTableNmBox
, '.') - 1) & 1 )
1117 sTableNm
= pTableNmBox
->copy( 0, nSeparator
);
1118 *pTableNmBox
= pTableNmBox
->copy( nSeparator
+ 1); // remove dot
1119 const SwTable
* pFnd
= FindTable( *rTable
.GetFrameFormat()->GetDoc(), sTableNm
);
1123 if( TBL_MERGETBL
== rTableUpd
.m_eFlags
)
1127 if( pFnd
== rTableUpd
.m_aData
.pDelTable
)
1129 if( rTableUpd
.m_pTable
!= &rTable
) // not the current one
1130 rNewStr
.append(rTableUpd
.m_pTable
->GetFrameFormat()->GetName() + "."); // set new table name
1131 rTableUpd
.m_bModified
= true;
1133 else if( pFnd
!= rTableUpd
.m_pTable
||
1134 ( rTableUpd
.m_pTable
!= &rTable
&& &rTable
!= rTableUpd
.m_aData
.pDelTable
))
1135 rNewStr
.append(sTableNm
+ "."); // keep table name
1137 rTableUpd
.m_bModified
= true;
1140 rNewStr
.append(sTableNm
+ "."); // keep table name
1143 if( pTableNmBox
== pLastBox
)
1144 rFirstBox
= rFirstBox
.copy( nLastBoxLen
+ 1 );
1146 SwTableBox
* pSttBox
= nullptr, *pEndBox
= nullptr;
1151 pEndBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(pLastBox
->toInt64()));
1152 pSttBox
= reinterpret_cast<SwTableBox
*>(sal::static_int_cast
<sal_IntPtr
>(rFirstBox
.toInt64()));
1157 const SwNode
* pNd
= GetNodeOfFormula();
1158 const SwTableBox
* pBox
= pNd
? pTable
->GetTableBox(
1159 pNd
->FindTableBoxStartNode()->GetIndex() ) : nullptr;
1161 pEndBox
= const_cast<SwTableBox
*>(lcl_RelToBox( *pTable
, pBox
, *pLastBox
));
1162 pSttBox
= const_cast<SwTableBox
*>(lcl_RelToBox( *pTable
, pBox
, rFirstBox
));
1168 pEndBox
= const_cast<SwTableBox
*>(pTable
->GetTableBox( *pLastBox
));
1169 pSttBox
= const_cast<SwTableBox
*>(pTable
->GetTableBox( rFirstBox
));
1173 if( pLastBox
&& pTable
->GetTabSortBoxes().find( pEndBox
) == pTable
->GetTabSortBoxes().end() )
1175 if( pTable
->GetTabSortBoxes().find( pSttBox
) == pTable
->GetTabSortBoxes().end() )
1178 if( TBL_SPLITTBL
== rTableUpd
.m_eFlags
)
1180 // Where are the boxes - in the old or in the new table?
1181 bool bInNewTable
= false;
1184 // It is the "first" box in this selection. It determines if the formula is placed in
1185 // the new or the old table.
1186 sal_uInt16 nEndLnPos
= SwTableFormula::GetLnPosInTable( *pTable
, pEndBox
),
1187 nSttLnPos
= SwTableFormula::GetLnPosInTable( *pTable
, pSttBox
);
1189 if( USHRT_MAX
!= nSttLnPos
&& USHRT_MAX
!= nEndLnPos
&&
1190 ((rTableUpd
.m_nSplitLine
<= nSttLnPos
) ==
1191 (rTableUpd
.m_nSplitLine
<= nEndLnPos
)) )
1193 // stay in same table
1194 bInNewTable
= rTableUpd
.m_nSplitLine
<= nEndLnPos
&&
1195 pTable
== rTableUpd
.m_pTable
;
1199 // this is definitely an invalid formula, also mark as modified for Undo
1200 rTableUpd
.m_bModified
= true;
1202 bInNewTable
= USHRT_MAX
!= nEndLnPos
&&
1203 rTableUpd
.m_nSplitLine
<= nEndLnPos
&&
1204 pTable
== rTableUpd
.m_pTable
;
1209 sal_uInt16 nSttLnPos
= SwTableFormula::GetLnPosInTable( *pTable
, pSttBox
);
1210 // Put it in the new table?
1211 bInNewTable
= USHRT_MAX
!= nSttLnPos
&&
1212 rTableUpd
.m_nSplitLine
<= nSttLnPos
&&
1213 pTable
== rTableUpd
.m_pTable
;
1216 // formula goes into new table
1217 if( rTableUpd
.m_bBehindSplitLine
)
1221 rTableUpd
.m_bModified
= true;
1222 rNewStr
.append(rTableUpd
.m_pTable
->GetFrameFormat()->GetName() + ".");
1224 else if( !sTableNm
.isEmpty() )
1225 rNewStr
.append(sTableNm
+ ".");
1227 else if( bInNewTable
)
1229 rTableUpd
.m_bModified
= true;
1230 rNewStr
.append(*rTableUpd
.m_aData
.pNewTableNm
+ ".");
1232 else if( !sTableNm
.isEmpty() )
1233 rNewStr
.append(sTableNm
+ ".");
1237 rNewStr
.append(OUString::number(reinterpret_cast<sal_IntPtr
>(pEndBox
)) + ":");
1239 rNewStr
.append(OUString::number(reinterpret_cast<sal_IntPtr
>(pSttBox
))
1240 + OUStringChar(rFirstBox
[ rFirstBox
.getLength()-1] ));
1243 /// Create external formula but remember that the formula is placed in a split/merged table
1244 void SwTableFormula::ToSplitMergeBoxNm( SwTableFormulaUpdate
& rTableUpd
)
1246 const SwTable
* pTable
;
1247 const SwNode
* pNd
= GetNodeOfFormula();
1248 if( pNd
&& nullptr != ( pNd
= pNd
->FindTableNode() ))
1249 pTable
= &static_cast<const SwTableNode
*>(pNd
)->GetTable();
1251 pTable
= rTableUpd
.m_pTable
;
1253 m_sFormula
= ScanString( &SwTableFormula::SplitMergeBoxNm_
, *pTable
, static_cast<void*>(&rTableUpd
) );
1254 m_eNmType
= INTRNL_NAME
;
1257 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */