xref: /core/sw/source/core/fields/cellfml.cxx (revision 94306083)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <string_view>
23 
24 #include <float.h>
25 #include <hintids.hxx>
26 #include <hints.hxx>
27 #include <fmtfld.hxx>
28 #include <txtfld.hxx>
29 #include <frmfmt.hxx>
30 #include <layfrm.hxx>
31 #include <cntfrm.hxx>
32 #include <tabfrm.hxx>
33 #include <doc.hxx>
34 #include <IDocumentLayoutAccess.hxx>
35 #include <ndtxt.hxx>
36 #include <swtable.hxx>
37 #include <tblsel.hxx>
38 #include <cellfml.hxx>
39 #include <calc.hxx>
40 #include <expfld.hxx>
41 #include <usrfld.hxx>
42 #include <flddat.hxx>
43 #include <cellatr.hxx>
44 #include <ndindex.hxx>
45 #include <frameformats.hxx>
46 #include <comphelper/string.hxx>
47 #include <o3tl/safeint.hxx>
48 #include <osl/diagnose.h>
49 
50 namespace
51 {
52 
53 const sal_Unicode cRelSeparator = ',';
54 const sal_Unicode cRelIdentifier = '\x12'; // CTRL-R
55 
56 enum
57 {
58     cMAXSTACKSIZE = 50
59 };
60 
61 }
62 
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,
72                                 bool bExtrnlNm);
73 
74 /** Get value of this box.
75  *
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?).
79  */
80 double SwTableBox::GetValue( SwTableCalcPara& rCalcPara ) const
81 {
82     double nRet = 0;
83 
84     if( rCalcPara.m_rCalc.IsCalcError() )
85         return nRet;            // stop if there is already an error set
86 
87     rCalcPara.m_rCalc.SetCalcError( SwCalcError::Syntax );    // default: error
88 
89     // no content box?
90     if( !m_pStartNode  )
91         return nRet;
92 
93     if( rCalcPara.IncStackCnt() )
94         return nRet;
95 
96     rCalcPara.SetLastTableBox( this );
97 
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
102 
103     // re-start with this box
104     rCalcPara.SetLastTableBox( this );
105 
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();
110 
111         const SfxPoolItem* pItem;
112         if( SfxItemState::SET == GetFrameFormat()->GetItemState(
113                                 RES_BOXATR_FORMULA, false, &pItem ) )
114         {
115             rCalcPara.m_rCalc.SetCalcError( SwCalcError::NONE ); // reset status
116             if( !static_cast<const SwTableBoxFormula*>(pItem)->IsValid() )
117             {
118                 // calculate
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 );
122 
123                 if( !rCalcPara.IsStackOverflow() )
124                 {
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 );
132                 }
133                 rCalcPara.m_pTable = pTmp;
134             }
135             else
136                 nRet = GetFrameFormat()->GetTableBoxValue().GetValue();
137             break;
138         }
139         else if( SfxItemState::SET == pBox->GetFrameFormat()->GetItemState(
140                                 RES_BOXATR_VALUE, false, &pItem ) )
141         {
142             rCalcPara.m_rCalc.SetCalcError( SwCalcError::NONE ); // reset status
143             nRet = static_cast<const SwTableBoxValue*>(pItem)->GetValue();
144             break;
145         }
146 
147         SwTextNode* pTextNd = pDoc->GetNodes()[ m_pStartNode->GetIndex() + 1 ]->GetTextNode();
148         if( !pTextNd )
149             break;
150 
151         sal_Int32 nSttPos = 0;
152         OUString sText = pTextNd->GetText();
153         while ( nSttPos < sText.getLength() && ( sText[nSttPos]==' ' || sText[nSttPos]=='\t' ) )
154             ++nSttPos;
155 
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) )
161         {
162             pTextField = static_txtattr_cast<SwTextField*>(pTextNd->GetTextAttrForCharAt(nSttPos, RES_TXTATR_FIELD));
163         }
164         if ( pTextField != nullptr )
165         {
166             rCalcPara.m_rCalc.SetCalcError( SwCalcError::NONE ); // reset status
167 
168             const SwField* pField = pTextField->GetFormatField().GetField();
169             switch ( pField->GetTyp()->Which() )
170             {
171             case SwFieldIds::SetExp:
172                 nRet = static_cast<const SwSetExpField*>(pField)->GetValue(rCalcPara.m_pLayout);
173                 break;
174             case SwFieldIds::User:
175                 nRet = static_cast<const SwUserField*>(pField)->GetValue();
176                 break;
177             case SwFieldIds::Table:
178                 {
179                     SwTableField* pTableField = const_cast<SwTableField*>(static_cast<const SwTableField*>(pField));
180                     if( !pTableField->IsValid() )
181                     {
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;
187                     }
188                     nRet = pTableField->GetValue();
189                 }
190                 break;
191 
192             case SwFieldIds::DateTime:
193                 nRet = static_cast<const SwDateTimeField*>( pField )->GetValue();
194                 break;
195 
196             case SwFieldIds::JumpEdit:
197                 //JP 14.09.98: Bug 56112 - placeholder never have the right content!
198                 nRet = 0;
199                 break;
200 
201             default:
202                 nRet = rCalcPara.m_rCalc.Calculate( pField->ExpandField(true, nullptr) ).GetDouble();
203             }
204         }
205         else if ( nSttPos < sText.getLength()
206                   && Char == CH_TXT_ATR_INPUTFIELDSTART )
207         {
208             const SwTextInputField * pTextInputField =
209                 dynamic_cast< const SwTextInputField* >(
210                     pTextNd->GetTextAttrAt( nSttPos, RES_TXTATR_INPUTFIELD ) );
211             if ( pTextInputField == nullptr )
212                 break;
213             nRet = rCalcPara.m_rCalc.Calculate( pTextInputField->GetFieldContent() ).GetDouble();
214         }
215         else if ( Char != CH_TXTATR_BREAKWORD )
216         {
217             // result is 0 but no error!
218             rCalcPara.m_rCalc.SetCalcError( SwCalcError::NONE ); // reset status
219 
220             double aNum = 0.0;
221             sText = bOK ? sText.copy( nSttPos ) : OUString();
222             sal_uInt32 nFormatIndex = GetFrameFormat()->GetTableBoxNumFormat().GetValue();
223 
224             SvNumberFormatter* pNumFormatr = pDoc->GetNumberFormatter();
225 
226             const SvNumFormatType nFormatType = pNumFormatr->GetType( nFormatIndex );
227             if( nFormatType == SvNumFormatType::TEXT )
228                 nFormatIndex = 0;
229             // JP 22.04.98: Bug 49659 - special treatment for percentages
230             else if( !sText.isEmpty() &&
231                     SvNumFormatType::PERCENT == nFormatType)
232             {
233                 sal_uInt32 nTmpFormat = 0;
234                 if( pDoc->IsNumberFormat( sText, nTmpFormat, aNum ) &&
235                     SvNumFormatType::NUMBER == pNumFormatr->GetType( nTmpFormat ))
236                     sText += "%";
237             }
238 
239             if( pDoc->IsNumberFormat( sText, nFormatIndex, aNum ))
240                 nRet = aNum;
241             else
242                 rCalcPara.m_rCalc.SetCalcError( SwCalcError::NaN ); // set for interoperability functions
243         }
244         // ?? otherwise it is an error
245     } while( false );
246 
247     if( !rCalcPara.IsStackOverflow() )
248     {
249         rCalcPara.m_pBoxStack->erase( pBox );      // remove from stack
250         rCalcPara.DecStackCnt();
251     }
252 
253     //JP 12.01.99: error detection, Bug 60794
254     if( DBL_MAX == nRet )
255         rCalcPara.m_rCalc.SetCalcError( SwCalcError::Syntax ); // set error
256 
257     return nRet;
258 }
259 
260 // structure needed for calculation of tables
261 
262 SwTableCalcPara::SwTableCalcPara(SwCalc& rCalculator, const SwTable& rTable,
263         SwRootFrame const*const pLayout)
264     : m_pLastTableBox(nullptr)
265     , m_nStackCount( 0 )
266     , m_nMaxSize( cMAXSTACKSIZE )
267     , m_pLayout(pLayout)
268     , m_pBoxStack( new SwTableSortBoxes )
269     , m_rCalc( rCalculator )
270     , m_pTable( &rTable )
271 {
272 }
273 
274 SwTableCalcPara::~SwTableCalcPara()
275 {
276 }
277 
278 bool SwTableCalcPara::CalcWithStackOverflow()
279 {
280     // If a stack overflow was detected, redo with last box.
281     sal_uInt16 nSaveMaxSize = m_nMaxSize;
282 
283     m_nMaxSize = cMAXSTACKSIZE - 5;
284     sal_uInt16 nCnt = 0;
285     SwTableBoxes aStackOverflows;
286     do {
287         SwTableBox* pBox = const_cast<SwTableBox*>(m_pLastTableBox);
288         m_nStackCount = 0;
289         m_rCalc.SetCalcError( SwCalcError::NONE );
290         aStackOverflows.insert( aStackOverflows.begin() + nCnt++, pBox );
291 
292         m_pBoxStack->erase( pBox );
293         pBox->GetValue( *this );
294     } while( IsStackOverflow() );
295 
296     m_nMaxSize = cMAXSTACKSIZE - 3; // decrease at least one level
297 
298     // if recursion was detected
299     m_nStackCount = 0;
300     m_rCalc.SetCalcError( SwCalcError::NONE );
301     m_pBoxStack->clear();
302 
303     while( !m_rCalc.IsCalcError() && nCnt )
304     {
305         aStackOverflows[ --nCnt ]->GetValue( *this );
306         if( IsStackOverflow() && !CalcWithStackOverflow() )
307             break;
308     }
309 
310     m_nMaxSize = nSaveMaxSize;
311     aStackOverflows.clear();
312     return !m_rCalc.IsCalcError();
313 }
314 
315 SwTableFormula::SwTableFormula( const OUString& rFormula )
316 : m_sFormula( rFormula )
317 , m_eNmType( EXTRNL_NAME )
318 , m_bValidValue( false )
319 {
320 }
321 
322 SwTableFormula::~SwTableFormula()
323 {
324 }
325 
326 void SwTableFormula::MakeFormula_( const SwTable& rTable, OUStringBuffer& rNewStr,
327                     OUString& rFirstBox, OUString* pLastBox, void* pPara ) const
328 {
329     SwTableCalcPara* pCalcPara = static_cast<SwTableCalcPara*>(pPara);
330     if( pCalcPara->m_rCalc.IsCalcError() )        // stop if there is already an error set
331         return;
332 
333     SwTableBox *pEndBox = nullptr;
334 
335     rFirstBox = rFirstBox.copy(1); // erase label of this box
336     // a region in this area?
337     if( pLastBox )
338     {
339         pEndBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(pLastBox->toInt64()));
340 
341         // Is it actually a valid pointer?
342         if( rTable.GetTabSortBoxes().find( pEndBox ) == rTable.GetTabSortBoxes().end() )
343             pEndBox = nullptr;
344         rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 );
345     }
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() )
350         pSttBox = nullptr;
351 
352     rNewStr.append(" ");
353     if( pEndBox && pSttBox )    // area?
354     {
355         // get all selected boxes via layout and calculate their values
356         SwSelBoxes aBoxes;
357         GetBoxes( *pSttBox, *pEndBox, aBoxes );
358 
359         // don't use empty cells or cells with text content as zeroes in interoperability functions
360         sal_Int16 nUseOnlyNumber = -1;
361 
362         rNewStr.append("(");
363         bool bDelim = false;
364         for (size_t n = 0; n < aBoxes.size() &&
365                            !pCalcPara->m_rCalc.IsCalcError(); ++n)
366         {
367             const SwTableBox* pTableBox = aBoxes[n];
368             if ( pTableBox->getRowSpan() >= 1 )
369             {
370                 double fVal = pTableBox->GetValue( *pCalcPara );
371 
372                 if ( pCalcPara->m_rCalc.IsCalcNotANumber() )
373                 {
374                     if ( nUseOnlyNumber == -1 )
375                     {
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 );
381                     }
382                     if ( nUseOnlyNumber > 0 )
383                         continue;
384                 }
385 
386                 if( bDelim )
387                     rNewStr.append(cListDelim);
388                 bDelim = true;
389                 rNewStr.append(pCalcPara->m_rCalc.GetStrResult( fVal ));
390             }
391         }
392         rNewStr.append(")");
393     }
394     else if( pSttBox && !pLastBox ) // only the StartBox?
395     {
396         // JP 12.01.99: and no EndBox in the formula!
397         // calculate the value of the box
398         if ( pSttBox->getRowSpan() >= 1 )
399         {
400             rNewStr.append("(");
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 ) )
407             {
408                 rNewStr.append(pCalcPara->m_rCalc.GetStrResult( fVal ));
409             }
410             rNewStr.append(")");
411         }
412     }
413     else
414         pCalcPara->m_rCalc.SetCalcError( SwCalcError::Syntax );   // set error
415     rNewStr.append(" ");
416 }
417 
418 void SwTableFormula::RelNmsToBoxNms( const SwTable& rTable, OUStringBuffer& rNewStr,
419             OUString& rFirstBox, OUString* pLastBox, void* pPara ) const
420 {
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() );
426 
427     rNewStr.append(rFirstBox[0]); // get label for the box
428     rFirstBox = rFirstBox.copy(1);
429     if( pLastBox )
430     {
431         const SwTableBox *pRelLastBox = lcl_RelToBox( rTable, pBox, *pLastBox );
432         if ( pRelLastBox )
433             rNewStr.append(pRelLastBox->GetName());
434         else
435             rNewStr.append("A1");
436         rNewStr.append(":");
437         rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 );
438     }
439 
440     const SwTableBox *pRelFirstBox = lcl_RelToBox( rTable, pBox, rFirstBox );
441 
442     if (pRelFirstBox)
443         rNewStr.append(pRelFirstBox->GetName());
444     else
445         rNewStr.append("A1");
446 
447     // get label for the box
448     rNewStr.append(rFirstBox[ rFirstBox.getLength()-1 ]);
449 }
450 
451 void SwTableFormula::RelBoxNmsToPtr( const SwTable& rTable, OUStringBuffer& rNewStr,
452             OUString& rFirstBox, OUString* pLastBox, void* pPara ) const
453 {
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() );
459 
460     rNewStr.append(rFirstBox[0]); // get label for the box
461     rFirstBox = rFirstBox.copy(1);
462     if( pLastBox )
463     {
464         const SwTableBox *pRelLastBox = lcl_RelToBox( rTable, pBox, *pLastBox );
465         if ( pRelLastBox )
466             rNewStr.append(OUString::number(reinterpret_cast<sal_PtrDiff>(pRelLastBox)));
467         else
468             rNewStr.append("0");
469         rNewStr.append(":");
470         rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 );
471     }
472 
473     const SwTableBox *pRelFirstBox = lcl_RelToBox( rTable, pBox, rFirstBox );
474     if ( pRelFirstBox )
475         rNewStr.append(OUString::number(reinterpret_cast<sal_PtrDiff>(pRelFirstBox)));
476     else
477         rNewStr.append("0");
478 
479     // get label for the box
480     rNewStr.append(rFirstBox[ rFirstBox.getLength()-1 ]);
481 }
482 
483 void SwTableFormula::BoxNmsToRelNm( const SwTable& rTable, OUStringBuffer& rNewStr,
484                     OUString& rFirstBox, OUString* pLastBox, void* pPara ) const
485 {
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();
490 
491     OUString sRefBoxNm;
492     if( &pTableNd->GetTable() == &rTable )
493     {
494         const SwTableBox *pBox = rTable.GetTableBox(
495                 pNd->FindTableBoxStartNode()->GetIndex() );
496         OSL_ENSURE( pBox, "Field not placed in any Table" );
497         sRefBoxNm = pBox->GetName();
498     }
499 
500     rNewStr.append(rFirstBox[0]); // get label for the box
501     rFirstBox = rFirstBox.copy(1);
502     if( pLastBox )
503     {
504         rNewStr.append(lcl_BoxNmToRel( rTable, *pTableNd, sRefBoxNm, *pLastBox,
505                                 m_eNmType == EXTRNL_NAME ));
506         rNewStr.append(":");
507         rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 );
508     }
509 
510     rNewStr.append(lcl_BoxNmToRel( rTable, *pTableNd, sRefBoxNm, rFirstBox,
511                             m_eNmType == EXTRNL_NAME ));
512 
513     // get label for the box
514     rNewStr.append(rFirstBox[ rFirstBox.getLength()-1 ]);
515 }
516 
517 void SwTableFormula::PtrToBoxNms( const SwTable& rTable, OUStringBuffer& rNewStr,
518                         OUString& rFirstBox, OUString* pLastBox, void* ) const
519 {
520     // area in these parentheses?
521     SwTableBox* pBox;
522 
523     rNewStr.append(rFirstBox[0]); // get label for the box
524     rFirstBox = rFirstBox.copy(1);
525     if( pLastBox )
526     {
527         pBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(pLastBox->toInt64()));
528 
529         // Is it actually a valid pointer?
530         if( rTable.GetTabSortBoxes().find( pBox ) != rTable.GetTabSortBoxes().end() )
531             rNewStr.append(pBox->GetName());
532         else
533             rNewStr.append("?");
534         rNewStr.append(":");
535         rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 );
536     }
537 
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());
542     else
543         rNewStr.append("?");
544 
545     // get label for the box
546     rNewStr.append(rFirstBox[ rFirstBox.getLength()-1 ]);
547 }
548 
549 void SwTableFormula::BoxNmsToPtr( const SwTable& rTable, OUStringBuffer& rNewStr,
550                         OUString& rFirstBox, OUString* pLastBox, void* ) const
551 {
552     // area in these parentheses?
553     const SwTableBox* pBox;
554 
555     rNewStr.append(rFirstBox[0]); // get label for the box
556     rFirstBox = rFirstBox.copy(1);
557     if( pLastBox )
558     {
559         pBox = rTable.GetTableBox( *pLastBox );
560         rNewStr.append(OUString::number(reinterpret_cast<sal_PtrDiff>(pBox)))
561                 .append(":");
562         rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 );
563     }
564 
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
568 }
569 
570 /// create external formula (for UI)
571 void SwTableFormula::PtrToBoxNm( const SwTable* pTable )
572 {
573     const SwNode* pNd = nullptr;
574     FnScanFormula fnFormula = nullptr;
575     switch (m_eNmType)
576     {
577     case INTRNL_NAME:
578         if( pTable )
579             fnFormula = &SwTableFormula::PtrToBoxNms;
580         break;
581     case REL_NAME:
582         if( pTable )
583         {
584             fnFormula = &SwTableFormula::RelNmsToBoxNms;
585             pNd = GetNodeOfFormula();
586         }
587         break;
588     case EXTRNL_NAME:
589         return;
590     }
591     m_sFormula = ScanString( fnFormula, *pTable, const_cast<void*>(static_cast<void const *>(pNd)) );
592     m_eNmType = EXTRNL_NAME;
593 }
594 
595 /// create internal formula (in CORE)
596 void SwTableFormula::BoxNmToPtr( const SwTable* pTable )
597 {
598     const SwNode* pNd = nullptr;
599     FnScanFormula fnFormula = nullptr;
600     switch (m_eNmType)
601     {
602     case EXTRNL_NAME:
603         if( pTable )
604             fnFormula = &SwTableFormula::BoxNmsToPtr;
605         break;
606     case REL_NAME:
607         if( pTable )
608         {
609             fnFormula = &SwTableFormula::RelBoxNmsToPtr;
610             pNd = GetNodeOfFormula();
611         }
612         break;
613     case INTRNL_NAME:
614         return;
615     }
616     m_sFormula = ScanString( fnFormula, *pTable, const_cast<void*>(static_cast<void const *>(pNd)) );
617     m_eNmType = INTRNL_NAME;
618 }
619 
620 /// create relative formula (for copy)
621 void SwTableFormula::ToRelBoxNm( const SwTable* pTable )
622 {
623     const SwNode* pNd = nullptr;
624     FnScanFormula fnFormula = nullptr;
625     switch (m_eNmType)
626     {
627     case INTRNL_NAME:
628     case EXTRNL_NAME:
629         if( pTable )
630         {
631             fnFormula = &SwTableFormula::BoxNmsToRelNm;
632             pNd = GetNodeOfFormula();
633         }
634         break;
635     case REL_NAME:
636         return;
637     }
638     m_sFormula = ScanString( fnFormula, *pTable, const_cast<void*>(static_cast<void const *>(pNd)) );
639     m_eNmType = REL_NAME;
640 }
641 
642 OUString SwTableFormula::ScanString( FnScanFormula fnFormula, const SwTable& rTable,
643                                      void* pPara ) const
644 {
645     OUStringBuffer aStr;
646     sal_Int32 nFormula = 0;
647     sal_Int32 nEnd = 0;
648 
649     do {
650         // If the formula is preceded by a name, use this table!
651         const SwTable* pTable = &rTable;
652 
653         sal_Int32 nStt = m_sFormula.indexOf( '<', nFormula );
654         if ( nStt>=0 )
655         {
656             while ( nStt>=0 )
657             {
658                 const sal_Int32 nNxt = nStt+1;
659                 if (nNxt>=m_sFormula.getLength())
660                 {
661                     nStt = -1;
662                     break;
663                 }
664                 if ( m_sFormula[nNxt]!=' ' && m_sFormula[nNxt]!='=' )
665                     break;
666                 nStt = m_sFormula.indexOf( '<', nNxt );
667             }
668 
669             if ( nStt>=0 )
670                 // Start searching from current position, which is valid for sure
671                 nEnd = m_sFormula.indexOf( '>', nStt );
672         }
673         if (nStt<0 || nEnd<0 )
674         {
675             // set the rest and finish
676             aStr.append(m_sFormula.subView(nFormula));
677             break;
678         }
679 
680         // write beginning
681         aStr.append(m_sFormula.subView(nFormula, nStt - nFormula));
682 
683         if (fnFormula)
684         {
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 )
694             {
695                 OUString sTableNm( m_sFormula.copy( nStt, nEnd - nStt ));
696 
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 )
699                 {
700                     sTableNm = sTableNm.copy( 0, nSeparator - nStt );
701 
702                     // when creating a formula the table name is unwanted
703                     if( fnFormula != &SwTableFormula::MakeFormula_ )
704                         aStr.append(sTableNm);
705                     nStt = nSeparator;
706 
707                     sTableNm = sTableNm.copy( 1 );   // delete separator
708                     if( sTableNm != rTable.GetFrameFormat()->GetName() )
709                     {
710                         // then search for table
711                         const SwTable* pFnd = FindTable(
712                                                 *rTable.GetFrameFormat()->GetDoc(),
713                                                 sTableNm );
714                         if( pFnd )
715                             pTable = pFnd;
716                         // ??
717                         OSL_ENSURE( pFnd, "No table found. What now?" );
718                     }
719                 }
720             }
721 
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 )
726             {
727                 // without opening parenthesis
728                 OUString aFirstBox( m_sFormula.copy( nStt+1, nSeparator - nStt - 1 ));
729                 (this->*fnFormula)( *pTable, aStr, sBox, &aFirstBox, pPara );
730             }
731             else
732                 (this->*fnFormula)( *pTable, aStr, sBox, nullptr, pPara );
733         }
734 
735         nFormula = nEnd+1;
736     } while( true );
737     return aStr.makeStringAndClear();
738 }
739 
740 const SwTable* SwTableFormula::FindTable( SwDoc& rDoc, std::u16string_view rNm )
741 {
742     const SwFrameFormats& rTableFormats = *rDoc.GetTableFrameFormats();
743     const SwTable* pTmpTable = nullptr, *pRet = nullptr;
744     for( auto nFormatCnt = rTableFormats.size(); nFormatCnt; )
745     {
746         SwFrameFormat* pFormat = rTableFormats[ --nFormatCnt ];
747         // if we are called from Sw3Writer, a number is dependent on the format name
748         SwTableBox* pFBox;
749         if ( rNm == pFormat->GetName().getToken(0, 0x0a) &&
750             nullptr != ( pTmpTable = SwTable::FindTable( pFormat ) ) &&
751             nullptr != (pFBox = pTmpTable->GetTabSortBoxes()[0] ) &&
752             pFBox->GetSttNd() &&
753             pFBox->GetSttNd()->GetNodes().IsDocNodes() )
754         {
755             // a table in the normal NodesArr
756             pRet = pTmpTable;
757             break;
758         }
759     }
760     return pRet;
761 }
762 
763 static const SwFrame* lcl_GetBoxFrame( const SwTableBox& rBox )
764 {
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);
771 }
772 
773 static sal_Int32 lcl_GetLongBoxNum( OUString& rStr )
774 {
775     sal_Int32 nRet;
776     const sal_Int32 nPos = rStr.indexOf( cRelSeparator );
777     if ( nPos<0 )
778     {
779         nRet = rStr.toInt32();
780         rStr.clear();
781     }
782     else
783     {
784         nRet = rStr.copy( 0, nPos ).toInt32();
785         rStr = rStr.copy( nPos+1 );
786     }
787     return nRet;
788 }
789 
790 static const SwTableBox* lcl_RelToBox( const SwTable& rTable,
791                                     const SwTableBox* pRefBox,
792                                     const OUString& _sGetName )
793 {
794     // get line
795     const SwTableBox* pBox = nullptr;
796     OUString sGetName = _sGetName;
797 
798     // Is it really a relative value?
799     if ( cRelIdentifier == sGetName[0] ) // yes
800     {
801         if( !pRefBox )
802             return nullptr;
803 
804         sGetName = sGetName.copy( 1 );
805 
806         const SwTableLines* pLines = &rTable.GetTabLines();
807         const SwTableBoxes* pBoxes;
808         const SwTableLine* pLine;
809 
810         // determine starting values of the box,...
811         pBox = pRefBox;
812         pLine = pBox->GetUpper();
813         while( pLine->GetUpper() )
814         {
815             pBox = pLine->GetUpper();
816             pLine = pBox->GetUpper();
817         }
818         sal_uInt16 nSttBox = pLine->GetBoxPos( pBox );
819         sal_uInt16 nSttLine = rTable.GetTabLines().GetPos( pLine );
820 
821         const sal_Int32 nBoxOffset = lcl_GetLongBoxNum( sGetName ) + nSttBox;
822         const sal_Int32 nLineOffset = lcl_GetLongBoxNum( sGetName ) + nSttLine;
823 
824         if( nBoxOffset < 0 ||
825             nLineOffset < 0 )
826             return nullptr;
827 
828         if( o3tl::make_unsigned(nLineOffset) >= pLines->size() )
829             return nullptr;
830 
831         pLine = (*pLines)[ nLineOffset ];
832 
833         // ... then search the box
834         pBoxes = &pLine->GetTabBoxes();
835         if( o3tl::make_unsigned(nBoxOffset) >= pBoxes->size() )
836             return nullptr;
837         pBox = (*pBoxes)[ nBoxOffset ];
838 
839         while (!sGetName.isEmpty())
840         {
841             nSttBox = SwTable::GetBoxNum( sGetName );
842             pLines = &pBox->GetTabLines();
843             if( nSttBox )
844                 --nSttBox;
845 
846             nSttLine = SwTable::GetBoxNum( sGetName );
847 
848             // determine line
849             if( !nSttLine || nSttLine > pLines->size() )
850                 break;
851             pLine = (*pLines)[ nSttLine-1 ];
852 
853             // determine box
854             pBoxes = &pLine->GetTabBoxes();
855             if( nSttBox >= pBoxes->size() )
856                 break;
857             pBox = (*pBoxes)[ nSttBox ];
858         }
859 
860         if( pBox )
861         {
862             if( !pBox->GetSttNd() )
863                 // "bubble up" to first box
864                 while( !pBox->GetTabLines().empty() )
865                     pBox = pBox->GetTabLines().front()->GetTabBoxes().front();
866         }
867     }
868     else
869     {
870         // otherwise it is an absolute external presentation
871         pBox = rTable.GetTableBox( sGetName );
872     }
873     return pBox;
874 }
875 
876 static OUString lcl_BoxNmToRel( const SwTable& rTable, const SwTableNode& rTableNd,
877                                 const OUString& _sRefBoxNm, const OUString& _sTmp, bool bExtrnlNm )
878 {
879     OUString sTmp = _sTmp;
880     OUString sRefBoxNm = _sRefBoxNm;
881     if( !bExtrnlNm )
882     {
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();
888     }
889 
890     // If the formula is spanning over a table then keep external presentation
891     if( &rTable == &rTableNd.GetTable() )
892     {
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 );
897 
898         const OUString sCpy = sTmp;        //JP 01.11.95: add rest from box name
899 
900         sTmp = OUStringChar(cRelIdentifier) + OUString::number( nBox )
901              + OUStringChar(cRelSeparator) + OUString::number( nLine );
902 
903         if (!sCpy.isEmpty())
904         {
905             sTmp += OUStringChar(cRelSeparator) + sCpy;
906         }
907     }
908 
909     if (sTmp.endsWith(">"))
910         return sTmp.copy(0, sTmp.getLength()-1 );
911 
912     return sTmp;
913 }
914 
915 void SwTableFormula::GetBoxesOfFormula( const SwTable& rTable,
916                                         SwSelBoxes& rBoxes )
917 {
918     rBoxes.clear();
919 
920     BoxNmToPtr( &rTable );
921     ScanString( &SwTableFormula::GetFormulaBoxes, rTable, &rBoxes );
922 }
923 
924 void SwTableFormula::GetFormulaBoxes( const SwTable& rTable, OUStringBuffer& ,
925                     OUString& rFirstBox, OUString* pLastBox, void* pPara ) const
926 {
927     SwSelBoxes* pBoxes = static_cast<SwSelBoxes*>(pPara);
928     SwTableBox* pEndBox = nullptr;
929 
930     rFirstBox = rFirstBox.copy(1); // delete box label
931     // area in these parentheses?
932     if( pLastBox )
933     {
934         pEndBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(pLastBox->toInt64()));
935 
936         // Is it actually a valid pointer?
937         if( rTable.GetTabSortBoxes().find( pEndBox ) == rTable.GetTabSortBoxes().end() )
938             pEndBox = nullptr;
939         rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 );
940     }
941 
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() )
945         return;
946 
947     if ( pEndBox ) // area?
948     {
949         // get all selected boxes via layout and calculate their values
950         SwSelBoxes aBoxes;
951         GetBoxes( *pSttBox, *pEndBox, aBoxes );
952         pBoxes->insert( aBoxes );
953     }
954     else          // only the StartBox?
955         pBoxes->insert( pSttBox );
956 }
957 
958 void SwTableFormula::GetBoxes( const SwTableBox& rSttBox,
959                                 const SwTableBox& rEndBox,
960                                 SwSelBoxes& rBoxes )
961 {
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;
968     if( !pStt || !pEnd )
969         return ;                        // no valid selection
970 
971     GetTableSel( pStt, pEnd, rBoxes, nullptr );
972 
973     const SwTable* pTable = pStt->FindTabFrame()->GetTable();
974 
975     // filter headline boxes
976     if( pTable->GetRowsToRepeat() <= 0 )
977         return;
978 
979     do {    // middle-check loop
980         const SwTableLine* pLine = rSttBox.GetUpper();
981         while( pLine->GetUpper() )
982             pLine = pLine->GetUpper()->GetUpper();
983 
984         if( pTable->IsHeadline( *pLine ) )
985             break;      // headline in this area!
986 
987         // maybe start and end are swapped
988         pLine = rEndBox.GetUpper();
989         while ( pLine->GetUpper() )
990             pLine = pLine->GetUpper()->GetUpper();
991 
992         if( pTable->IsHeadline( *pLine ) )
993             break;      // headline in this area!
994 
995         const SwTabFrame *pStartTable = pStt->FindTabFrame();
996         const SwTabFrame *pEndTable = pEnd->FindTabFrame();
997 
998         if (pStartTable == pEndTable) // no split table
999             break;
1000 
1001         // then remove table headers
1002         for (size_t n = 0; n < rBoxes.size(); ++n)
1003         {
1004             pLine = rBoxes[n]->GetUpper();
1005             while( pLine->GetUpper() )
1006                 pLine = pLine->GetUpper()->GetUpper();
1007 
1008             if( pTable->IsHeadline( *pLine ) )
1009                 rBoxes.erase( rBoxes.begin() + n-- );
1010         }
1011     } while( false );
1012 }
1013 
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
1017 {
1018     bool* pBValid = static_cast<bool*>(pPara);
1019     if( !(*pBValid) )      // wrong is wrong
1020         return;
1021 
1022     SwTableBox* pSttBox = nullptr, *pEndBox = nullptr;
1023     rFirstBox = rFirstBox.copy(1);       // delete identifier of box
1024 
1025     // area in this parenthesis?
1026     if( pLastBox )
1027         rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 );
1028 
1029     switch (m_eNmType)
1030     {
1031     case INTRNL_NAME:
1032         if( pLastBox )
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()));
1035         break;
1036 
1037     case REL_NAME:
1038         {
1039             const SwNode* pNd = GetNodeOfFormula();
1040             const SwTableBox* pBox = !pNd ? nullptr
1041                                            : const_cast<SwTableBox *>(rTable.GetTableBox(
1042                                 pNd->FindTableBoxStartNode()->GetIndex() ));
1043             if( pLastBox )
1044                 pEndBox = const_cast<SwTableBox*>(lcl_RelToBox( rTable, pBox, *pLastBox ));
1045             pSttBox = const_cast<SwTableBox*>(lcl_RelToBox( rTable, pBox, rFirstBox ));
1046         }
1047         break;
1048 
1049     case EXTRNL_NAME:
1050         if( pLastBox )
1051             pEndBox = const_cast<SwTableBox*>(rTable.GetTableBox( *pLastBox ));
1052         pSttBox = const_cast<SwTableBox*>(rTable.GetTableBox( rFirstBox ));
1053         break;
1054     }
1055 
1056     // Are these valid pointers?
1057     if( ( pLastBox &&
1058           ( !pEndBox || rTable.GetTabSortBoxes().find( pEndBox ) == rTable.GetTabSortBoxes().end() ) ) ||
1059         ( !pSttBox || rTable.GetTabSortBoxes().find( pSttBox ) == rTable.GetTabSortBoxes().end() ) )
1060             *pBValid = false;
1061 }
1062 
1063 bool SwTableFormula::HasValidBoxes() const
1064 {
1065     bool bRet = true;
1066     const SwNode* pNd = GetNodeOfFormula();
1067     if( pNd && nullptr != ( pNd = pNd->FindTableNode() ) )
1068         ScanString( &SwTableFormula::HasValidBoxes_,
1069                         static_cast<const SwTableNode*>(pNd)->GetTable(), &bRet );
1070     return bRet;
1071 }
1072 
1073 sal_uInt16 SwTableFormula::GetLnPosInTable( const SwTable& rTable, const SwTableBox* pBox )
1074 {
1075     sal_uInt16 nRet = USHRT_MAX;
1076     if( pBox )
1077     {
1078         const SwTableLine* pLn = pBox->GetUpper();
1079         while( pLn->GetUpper() )
1080             pLn = pLn->GetUpper()->GetUpper();
1081         nRet = rTable.GetTabLines().GetPos( pLn );
1082     }
1083     return nRet;
1084 }
1085 
1086 void SwTableFormula::SplitMergeBoxNm_( const SwTable& rTable, OUStringBuffer& rNewStr,
1087                     OUString& rFirstBox, OUString* pLastBox, void* pPara ) const
1088 {
1089     SwTableFormulaUpdate& rTableUpd = *static_cast<SwTableFormulaUpdate*>(pPara);
1090 
1091     rNewStr.append(rFirstBox[0]);     // get label for the box
1092     rFirstBox = rFirstBox.copy(1);
1093 
1094     OUString sTableNm;
1095     const SwTable* pTable = &rTable;
1096 
1097     OUString* pTableNmBox = pLastBox ? pLastBox : &rFirstBox;
1098 
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 )
1104     {
1105         sTableNm = pTableNmBox->copy( 0, nSeparator );
1106         *pTableNmBox = pTableNmBox->copy( nSeparator + 1); // remove dot
1107         const SwTable* pFnd = FindTable( *rTable.GetFrameFormat()->GetDoc(), sTableNm );
1108         if( pFnd )
1109             pTable = pFnd;
1110 
1111         if( TBL_MERGETBL == rTableUpd.m_eFlags )
1112         {
1113             if( pFnd )
1114             {
1115                 if( pFnd == rTableUpd.m_aData.pDelTable )
1116                 {
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;
1120                 }
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
1124                 else
1125                     rTableUpd.m_bModified = true;
1126             }
1127             else
1128                 rNewStr.append(sTableNm).append(".");     // keep table name
1129         }
1130     }
1131     if( pTableNmBox == pLastBox )
1132         rFirstBox = rFirstBox.copy( nLastBoxLen + 1 );
1133 
1134     SwTableBox* pSttBox = nullptr, *pEndBox = nullptr;
1135     switch (m_eNmType)
1136     {
1137     case INTRNL_NAME:
1138         if( pLastBox )
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()));
1141         break;
1142 
1143     case REL_NAME:
1144         {
1145             const SwNode* pNd = GetNodeOfFormula();
1146             const SwTableBox* pBox = pNd ? pTable->GetTableBox(
1147                             pNd->FindTableBoxStartNode()->GetIndex() ) : nullptr;
1148             if( pLastBox )
1149                 pEndBox = const_cast<SwTableBox*>(lcl_RelToBox( *pTable, pBox, *pLastBox ));
1150             pSttBox = const_cast<SwTableBox*>(lcl_RelToBox( *pTable, pBox, rFirstBox ));
1151         }
1152         break;
1153 
1154     case EXTRNL_NAME:
1155         if( pLastBox )
1156             pEndBox = const_cast<SwTableBox*>(pTable->GetTableBox( *pLastBox ));
1157         pSttBox = const_cast<SwTableBox*>(pTable->GetTableBox( rFirstBox ));
1158         break;
1159     }
1160 
1161     if( pLastBox && pTable->GetTabSortBoxes().find( pEndBox ) == pTable->GetTabSortBoxes().end() )
1162         pEndBox = nullptr;
1163     if( pTable->GetTabSortBoxes().find( pSttBox ) == pTable->GetTabSortBoxes().end() )
1164         pSttBox = nullptr;
1165 
1166     if( TBL_SPLITTBL == rTableUpd.m_eFlags )
1167     {
1168         // Where are the boxes - in the old or in the new table?
1169         bool bInNewTable = false;
1170         if( pLastBox )
1171         {
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 );
1176 
1177             if( USHRT_MAX != nSttLnPos && USHRT_MAX != nEndLnPos &&
1178                 ((rTableUpd.m_nSplitLine <= nSttLnPos) ==
1179                 (rTableUpd.m_nSplitLine <= nEndLnPos)) )
1180             {
1181                 // stay in same table
1182                 bInNewTable = rTableUpd.m_nSplitLine <= nEndLnPos &&
1183                                     pTable == rTableUpd.m_pTable;
1184             }
1185             else
1186             {
1187                 // this is definitely an invalid formula, also mark as modified for Undo
1188                 rTableUpd.m_bModified = true;
1189                 if( pEndBox )
1190                     bInNewTable = USHRT_MAX != nEndLnPos &&
1191                                     rTableUpd.m_nSplitLine <= nEndLnPos &&
1192                                     pTable == rTableUpd.m_pTable;
1193             }
1194         }
1195         else
1196         {
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;
1202         }
1203 
1204         // formula goes into new table
1205         if( rTableUpd.m_bBehindSplitLine )
1206         {
1207             if( !bInNewTable )
1208             {
1209                 rTableUpd.m_bModified = true;
1210                 rNewStr.append(rTableUpd.m_pTable->GetFrameFormat()->GetName()).append(".");
1211             }
1212             else if( !sTableNm.isEmpty() )
1213                 rNewStr.append(sTableNm).append(".");
1214         }
1215         else if( bInNewTable )
1216         {
1217             rTableUpd.m_bModified = true;
1218             rNewStr.append(*rTableUpd.m_aData.pNewTableNm).append(".");
1219         }
1220         else if( !sTableNm.isEmpty() )
1221             rNewStr.append(sTableNm).append(".");
1222     }
1223 
1224     if( pLastBox )
1225         rNewStr.append(OUString::number(reinterpret_cast<sal_PtrDiff>(pEndBox))).append(":");
1226 
1227     rNewStr.append(OUString::number(reinterpret_cast<sal_PtrDiff>(pSttBox)))
1228             .append(rFirstBox[ rFirstBox.getLength()-1] );
1229 }
1230 
1231 /// Create external formula but remember that the formula is placed in a split/merged table
1232 void SwTableFormula::ToSplitMergeBoxNm( SwTableFormulaUpdate& rTableUpd )
1233 {
1234     const SwTable* pTable;
1235     const SwNode* pNd = GetNodeOfFormula();
1236     if( pNd && nullptr != ( pNd = pNd->FindTableNode() ))
1237         pTable = &static_cast<const SwTableNode*>(pNd)->GetTable();
1238     else
1239         pTable = rTableUpd.m_pTable;
1240 
1241     m_sFormula = ScanString( &SwTableFormula::SplitMergeBoxNm_, *pTable, static_cast<void*>(&rTableUpd) );
1242     m_eNmType = INTRNL_NAME;
1243 }
1244 
1245 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1246