Update ooo320-m1
[ooovba.git] / sw / source / core / fields / cellfml.cxx
bloba62957faa5d303ff060c6e55e1cac5fc112f59f2
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: cellfml.cxx,v $
10 * $Revision: 1.16 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sw.hxx"
35 #include <float.h>
36 #include <hintids.hxx>
37 #include <hints.hxx>
38 #include <fmtfld.hxx>
39 #include <txtfld.hxx>
40 #include <frmfmt.hxx>
41 #include <layfrm.hxx>
42 #include <cntfrm.hxx>
43 #include <tabfrm.hxx>
44 #include <doc.hxx>
45 #include <docary.hxx>
46 #include <ndtxt.hxx>
47 #include <swtable.hxx>
48 #include <tblsel.hxx>
49 #include <cellfml.hxx>
50 #include <calc.hxx>
51 #include <expfld.hxx>
52 #include <usrfld.hxx>
53 #include <flddat.hxx>
54 #include <cellatr.hxx>
55 #include <ndindex.hxx>
57 const sal_Unicode cRelTrenner = ',';
58 const sal_Unicode cRelKennung = '\x12'; // CTRL-R
60 const USHORT cMAXSTACKSIZE = 50;
62 const SwFrm* lcl_GetBoxFrm( const SwTableBox& rBox );
63 long lcl_GetLongBoxNum( String& rStr );
64 const SwTableBox* lcl_RelToBox( const SwTable&, const SwTableBox*, const String& );
65 String lcl_BoxNmToRel( const SwTable&, const SwTableNode&,
66 const String& , const String& , BOOL );
69 /*************************************************************************
71 |* double SwTableBox::GetValue() const
72 |* gebe den Wert dieser Box zurueck. Der Wert ergibt sich aus dem 1.
73 |* TextNode. Beginnt dieser mit einer Zahl/Formel, so berechne diese;
74 |* oder mit einem Feld, dann hole den Wert.
75 |* Alle anderen Bedingungen returnen einen Fehler (oder 0 ?)
77 |* Ersterstellung JP 30. Jun. 93
78 |* Letzte Aenderung JP 30. Jun. 93
80 |*************************************************************************/
82 double SwTableBox::GetValue( SwTblCalcPara& rCalcPara ) const
84 double nRet = 0;
86 if( rCalcPara.rCalc.IsCalcError() )
87 return nRet; // schon ein Fehler in der Berechnung
89 rCalcPara.rCalc.SetCalcError( CALC_SYNTAX ); // default immer Fehler
91 // keine Content Box ?
92 if( !pSttNd )
93 return nRet;
95 if( rCalcPara.IncStackCnt() )
96 return nRet;
98 rCalcPara.SetLastTblBox( this );
100 // wird eine Rekursion erzeugt ?
101 SwTableBox* pBox = (SwTableBox*)this;
102 if( rCalcPara.pBoxStk->Seek_Entry( pBox ))
103 return nRet; // steht schon auf dem Stack: FEHLER
105 // bei dieser Box nochmal aufsetzen
106 rCalcPara.SetLastTblBox( this );
108 rCalcPara.pBoxStk->Insert( pBox ); // eintragen
109 do { // Middle-Check-Loop, damit aus dieser gesprungen werden kann
110 // hier aufgespannt, damit am Ende der Box-Pointer aus dem
111 // Stack ausgetragen wird
112 SwDoc* pDoc = GetFrmFmt()->GetDoc();
114 const SfxPoolItem* pItem;
115 if( SFX_ITEM_SET == GetFrmFmt()->GetItemState(
116 RES_BOXATR_FORMULA, FALSE, &pItem ) )
118 rCalcPara.rCalc.SetCalcError( CALC_NOERR ); // wieder zuruecksetzen
119 if( !((SwTblBoxFormula*)pItem)->IsValid() )
121 // dann berechnen
122 const SwTable* pTmp = rCalcPara.pTbl;
123 rCalcPara.pTbl = &pBox->GetSttNd()->FindTableNode()->GetTable();
124 ((SwTblBoxFormula*)pItem)->Calc( rCalcPara, nRet );
126 if( !rCalcPara.IsStackOverFlow() )
128 SwFrmFmt* pFmt = pBox->ClaimFrmFmt();
129 SfxItemSet aTmp( pDoc->GetAttrPool(),
130 RES_BOXATR_BEGIN,RES_BOXATR_END-1 );
131 aTmp.Put( SwTblBoxValue( nRet ) );
132 if( SFX_ITEM_SET != pFmt->GetItemState( RES_BOXATR_FORMAT ))
133 aTmp.Put( SwTblBoxNumFormat( 0 ));
134 pFmt->SetFmtAttr( aTmp );
136 rCalcPara.pTbl = pTmp;
138 else
139 nRet = GetFrmFmt()->GetTblBoxValue().GetValue();
140 break;
142 else if( SFX_ITEM_SET == pBox->GetFrmFmt()->GetItemState(
143 RES_BOXATR_VALUE, FALSE, &pItem ) )
145 rCalcPara.rCalc.SetCalcError( CALC_NOERR ); // wieder zuruecksetzen
146 nRet = ((SwTblBoxValue*)pItem)->GetValue();
147 break;
150 SwTxtNode* pTxtNd = pDoc->GetNodes()[ pSttNd->GetIndex() + 1 ]->GetTxtNode();
151 if( !pTxtNd )
152 break;
154 xub_StrLen nSttPos = 0;
155 const String& rTxt = pTxtNd->GetTxt();
156 while( nSttPos < rTxt.Len() &&
157 ( ' ' == rTxt.GetChar( nSttPos ) || '\t' == rTxt.GetChar( nSttPos ) ) )
158 ++nSttPos;
160 // beginnt an erster Position ein "RechenFeld", dann erfrage den Wert
161 // von diesem
162 sal_Unicode const Char = rTxt.GetChar(nSttPos);
163 if ( nSttPos < rTxt.Len() &&
164 ( CH_TXTATR_BREAKWORD == Char || CH_TXTATR_INWORD == Char ) )
166 SwIndex aIdx( pTxtNd, nSttPos );
167 SwTxtFld * const pTxtFld = static_cast<SwTxtFld*>(
168 pTxtNd->GetTxtAttrForCharAt(aIdx.GetIndex(), RES_TXTATR_FIELD));
169 if( !pTxtFld )
170 break;
172 rCalcPara.rCalc.SetCalcError( CALC_NOERR ); // wieder zuruecksetzen
174 const SwField* pFld = pTxtFld->GetFld().GetFld();
175 switch( pFld->GetTyp()->Which() )
177 case RES_SETEXPFLD:
178 nRet = ((SwSetExpField*)pFld)->GetValue();
179 break;
180 case RES_USERFLD:
181 nRet = ((SwUserFieldType*)pFld)->GetValue();
182 break;
183 case RES_TABLEFLD:
185 SwTblField* pTblFld = (SwTblField*)pFld;
186 if( !pTblFld->IsValid() ) // ist der Wert gueltig ??
188 // die richtige Tabelle mitgeben!
189 const SwTable* pTmp = rCalcPara.pTbl;
190 rCalcPara.pTbl = &pTxtNd->FindTableNode()->GetTable();
191 pTblFld->CalcField( rCalcPara );
192 rCalcPara.pTbl = pTmp;
194 nRet = pTblFld->GetValue();
196 break;
198 case RES_DATETIMEFLD:
199 nRet = ((SwDateTimeField*)pFld)->GetValue();
200 break;
202 case RES_JUMPEDITFLD:
203 //JP 14.09.98: Bug 56112 - der Platzhalter kann nie einen
204 // gueltigen Inhalt haben!
205 nRet = 0;
206 break;
208 default:
209 nRet = rCalcPara.rCalc.Calculate( pFld->Expand() ).GetDouble();
212 else
214 // Ergebnis ist 0 und kein Fehler!
215 rCalcPara.rCalc.SetCalcError( CALC_NOERR ); // wieder zuruecksetzen
217 double aNum;
218 String sTxt( rTxt.Copy( nSttPos ) );
219 sal_uInt32 nFmtIndex = GetFrmFmt()->GetTblBoxNumFmt().GetValue();
221 SvNumberFormatter* pNumFmtr = pDoc->GetNumberFormatter();
223 if( NUMBERFORMAT_TEXT == nFmtIndex )
224 nFmtIndex = 0;
225 // JP 22.04.98: Bug 49659 - Sonderbehandlung fuer Prozent
226 else if( sTxt.Len() &&
227 NUMBERFORMAT_PERCENT == pNumFmtr->GetType( nFmtIndex ))
229 sal_uInt32 nTmpFmt = 0;
230 if( pNumFmtr->IsNumberFormat( sTxt, nTmpFmt, aNum ) &&
231 NUMBERFORMAT_NUMBER == pNumFmtr->GetType( nTmpFmt ))
232 sTxt += '%';
235 if( pNumFmtr->IsNumberFormat( sTxt, nFmtIndex, aNum ))
236 nRet = aNum;
239 // ?? sonst ist das ein Fehler
240 } while( FALSE );
242 if( !rCalcPara.IsStackOverFlow() )
244 rCalcPara.pBoxStk->Remove( pBox ); // raus aus dem Stack
245 rCalcPara.DecStackCnt();
248 //JP 12.01.99: mit Fehlererkennung, Bug 60794
249 if( DBL_MAX == nRet )
250 rCalcPara.rCalc.SetCalcError( CALC_SYNTAX ); // Fehler setzen
252 return nRet;
255 /* \f */
257 // Struktur, die zum TabelleRechnen benoetigt wird
259 SwTblCalcPara::SwTblCalcPara( SwCalc& rCalculator, const SwTable& rTable )
260 : pLastTblBox( 0 ), nStackCnt( 0 ), nMaxSize( cMAXSTACKSIZE ),
261 rCalc( rCalculator ), pTbl( &rTable )
263 pBoxStk = new SwTableSortBoxes;
266 SwTblCalcPara::~SwTblCalcPara()
268 delete pBoxStk;
271 BOOL SwTblCalcPara::CalcWithStackOverflow()
273 // falls ein StackUeberlauf erkannt wurde, sollte mit
274 // der letzten Box noch mal aufgesetzt werden. Irgend
275 // ein Weg sollte dann
276 USHORT nSaveMaxSize = nMaxSize;
278 nMaxSize = cMAXSTACKSIZE - 5;
279 USHORT nCnt = 0;
280 SwTableBoxes aStackOverFlows;
281 do {
282 SwTableBox* pBox = (SwTableBox*)pLastTblBox;
283 nStackCnt = 0;
284 rCalc.SetCalcError( CALC_NOERR );
285 aStackOverFlows.C40_INSERT( SwTableBox, pBox, nCnt++ );
287 pBoxStk->Remove( pBox );
288 pBox->GetValue( *this );
289 } while( IsStackOverFlow() );
291 nMaxSize = cMAXSTACKSIZE - 3; // es muss mind. 1 Stufe tiefer gehen!
293 // falls Rekursionen erkannt wurden
294 nStackCnt = 0;
295 rCalc.SetCalcError( CALC_NOERR );
296 pBoxStk->Remove( USHORT(0), pBoxStk->Count() );
298 while( !rCalc.IsCalcError() && nCnt )
300 aStackOverFlows[ --nCnt ]->GetValue( *this );
301 if( IsStackOverFlow() && !CalcWithStackOverflow() )
302 break;
305 nMaxSize = nSaveMaxSize;
306 aStackOverFlows.Remove( 0, aStackOverFlows.Count() );
307 return !rCalc.IsCalcError();
310 /* \f */
312 SwTableFormula::SwTableFormula( const String& rFormel )
313 : sFormel( rFormel )
315 eNmType = EXTRNL_NAME;
316 bValidValue = FALSE;
319 SwTableFormula::~SwTableFormula()
323 void SwTableFormula::_MakeFormel( const SwTable& rTbl, String& rNewStr,
324 String& rFirstBox, String* pLastBox, void* pPara ) const
326 SwTblCalcPara* pCalcPara = (SwTblCalcPara*)pPara;
327 if( pCalcPara->rCalc.IsCalcError() ) // ist schon Fehler gesetzt ?
328 return;
330 SwTableBox* pSttBox, *pEndBox = 0;
332 rFirstBox.Erase(0,1); // Kennung fuer Box loeschen
333 // ein Bereich in dieser Klammer ?
334 if( pLastBox )
336 pEndBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(pLastBox->ToInt64()));
338 // ist das ueberhaupt ein gueltiger Pointer ??
339 if( !rTbl.GetTabSortBoxes().Seek_Entry( pEndBox ))
340 pEndBox = 0;
341 rFirstBox.Erase( 0, pLastBox->Len()+1 );
343 pSttBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(rFirstBox.ToInt64()));
344 // ist das ueberhaupt ein gueltiger Pointer ??
345 if( !rTbl.GetTabSortBoxes().Seek_Entry( pSttBox ))
346 pSttBox = 0;
348 rNewStr += ' ';
349 if( pEndBox && pSttBox ) // Bereich ?
351 // hole ueber das Layout alle "selectierten" Boxen und berechne
352 // deren Werte
353 SwSelBoxes aBoxes;
354 GetBoxes( *pSttBox, *pEndBox, aBoxes );
356 rNewStr += '(';
357 bool bDelim = false;
358 for( USHORT n = 0; n < aBoxes.Count() &&
359 !pCalcPara->rCalc.IsCalcError(); ++n )
361 const SwTableBox* pTblBox = aBoxes[n];
362 if ( pTblBox->getRowSpan() >= 1 )
364 if( bDelim )
365 rNewStr += cListDelim;
366 bDelim = true;
367 rNewStr += pCalcPara->rCalc.GetStrResult(
368 pTblBox->GetValue( *pCalcPara ), FALSE );
371 rNewStr += ')';
373 else if( pSttBox && !pLastBox ) // nur die StartBox ?
375 //JP 12.01.99: und keine EndBox in der Formel!
376 // Berechne den Wert der Box
377 if ( pSttBox->getRowSpan() >= 1 )
379 rNewStr += pCalcPara->rCalc.GetStrResult(
380 pSttBox->GetValue( *pCalcPara ), FALSE );
383 else
384 pCalcPara->rCalc.SetCalcError( CALC_SYNTAX ); // Fehler setzen
385 rNewStr += ' ';
388 void SwTableFormula::RelNmsToBoxNms( const SwTable& rTbl, String& rNewStr,
389 String& rFirstBox, String* pLastBox, void* pPara ) const
391 // relativen Namen zu Box-Namen (externe Darstellung)
392 SwNode* pNd = (SwNode*)pPara;
393 ASSERT( pNd, "Feld steht in keinem TextNode" );
394 const SwTableBox *pRelBox, *pBox = (SwTableBox *)rTbl.GetTblBox(
395 pNd->FindTableBoxStartNode()->GetIndex() );
397 rNewStr += rFirstBox.Copy(0,1); // Kennung fuer Box erhalten
398 rFirstBox.Erase(0,1);
399 if( pLastBox )
401 if( 0 != ( pRelBox = lcl_RelToBox( rTbl, pBox, *pLastBox )) )
402 rNewStr += pRelBox->GetName();
403 else
404 rNewStr.AppendAscii("A1");
405 rNewStr += ':';
406 rFirstBox.Erase( 0, pLastBox->Len()+1 );
409 if( 0 != ( pRelBox = lcl_RelToBox( rTbl, pBox, rFirstBox )) )
410 rNewStr += pRelBox->GetName();
411 else
412 rNewStr.AppendAscii("A1");
414 // Kennung fuer Box erhalten
415 rNewStr += rFirstBox.GetChar( rFirstBox.Len() - 1 );
418 void SwTableFormula::RelBoxNmsToPtr( const SwTable& rTbl, String& rNewStr,
419 String& rFirstBox, String* pLastBox, void* pPara ) const
421 // relativen Namen zu Box-Pointern (interne Darstellung)
422 SwNode* pNd = (SwNode*)pPara;
423 ASSERT( pNd, "Feld steht in keinem Node" );
424 const SwTableBox *pRelBox, *pBox = (SwTableBox*)rTbl.GetTblBox(
425 pNd->FindTableBoxStartNode()->GetIndex() );
427 rNewStr += rFirstBox.Copy(0,1); // Kennung fuer Box erhalten
428 rFirstBox.Erase(0,1);
429 if( pLastBox )
431 if( 0 != ( pRelBox = lcl_RelToBox( rTbl, pBox, *pLastBox )) )
432 rNewStr += String::CreateFromInt64( (sal_PtrDiff)pRelBox );
433 else
434 rNewStr += '0';
435 rNewStr += ':';
436 rFirstBox.Erase( 0, pLastBox->Len()+1 );
439 if( 0 != ( pRelBox = lcl_RelToBox( rTbl, pBox, rFirstBox )) )
440 rNewStr += String::CreateFromInt64( (sal_PtrDiff)pRelBox );
441 else
442 rNewStr += '0';
444 // Kennung fuer Box erhalten
445 rNewStr += rFirstBox.GetChar( rFirstBox.Len() - 1 );
449 void SwTableFormula::BoxNmsToRelNm( const SwTable& rTbl, String& rNewStr,
450 String& rFirstBox, String* pLastBox, void* pPara ) const
452 // Box-Namen (externe Darstellung) zu relativen Namen
453 SwNode* pNd = (SwNode*)pPara;
454 ASSERT( pNd, "Feld steht in keinem Node" );
455 const SwTableNode* pTblNd = pNd->FindTableNode();
457 String sRefBoxNm;
458 if( &pTblNd->GetTable() == &rTbl )
460 const SwTableBox *pBox = rTbl.GetTblBox(
461 pNd->FindTableBoxStartNode()->GetIndex() );
462 ASSERT( pBox, "Feld steht in keiner Tabelle" );
463 sRefBoxNm = pBox->GetName();
466 rNewStr += rFirstBox.Copy(0,1); // Kennung fuer Box erhalten
467 rFirstBox.Erase(0,1);
468 if( pLastBox )
470 rNewStr += lcl_BoxNmToRel( rTbl, *pTblNd, sRefBoxNm, *pLastBox,
471 eNmType == EXTRNL_NAME );
472 rNewStr += ':';
473 rFirstBox.Erase( 0, pLastBox->Len()+1 );
476 rNewStr += lcl_BoxNmToRel( rTbl, *pTblNd, sRefBoxNm, rFirstBox,
477 eNmType == EXTRNL_NAME );
479 // Kennung fuer Box erhalten
480 rNewStr += rFirstBox.GetChar( rFirstBox.Len() - 1 );
484 void SwTableFormula::PtrToBoxNms( const SwTable& rTbl, String& rNewStr,
485 String& rFirstBox, String* pLastBox, void* ) const
487 // ein Bereich in dieser Klammer ?
488 SwTableBox* pBox;
490 rNewStr += rFirstBox.Copy(0,1); // Kennung fuer Box erhalten
491 rFirstBox.Erase(0,1);
492 if( pLastBox )
494 pBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(pLastBox->ToInt64()));
496 // ist das ueberhaupt ein gueltiger Pointer ??
497 if( rTbl.GetTabSortBoxes().Seek_Entry( pBox ))
498 rNewStr += pBox->GetName();
499 else
500 rNewStr += '?';
501 rNewStr += ':';
502 rFirstBox.Erase( 0, pLastBox->Len()+1 );
505 pBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(rFirstBox.ToInt64()));
506 // ist das ueberhaupt ein gueltiger Pointer ??
507 if( rTbl.GetTabSortBoxes().Seek_Entry( pBox ))
508 rNewStr += pBox->GetName();
509 else
510 rNewStr += '?';
512 // Kennung fuer Box erhalten
513 rNewStr += rFirstBox.GetChar( rFirstBox.Len() - 1 );
516 void SwTableFormula::BoxNmsToPtr( const SwTable& rTbl, String& rNewStr,
517 String& rFirstBox, String* pLastBox, void* ) const
519 // ein Bereich in dieser Klammer ?
520 const SwTableBox* pBox;
522 rNewStr += rFirstBox.Copy(0,1); // Kennung fuer Box erhalten
523 rFirstBox.Erase(0,1);
524 if( pLastBox )
526 pBox = rTbl.GetTblBox( *pLastBox );
527 rNewStr += String::CreateFromInt64( (sal_PtrDiff)pBox );
528 rNewStr += ':';
529 rFirstBox.Erase( 0, pLastBox->Len()+1 );
532 pBox = rTbl.GetTblBox( rFirstBox );
533 rNewStr += String::CreateFromInt64( (sal_PtrDiff)pBox );
535 // Kennung fuer Box erhalten
536 rNewStr += rFirstBox.GetChar( rFirstBox.Len() - 1 );
539 // erzeuge die externe (fuer UI) Formel
540 void SwTableFormula::PtrToBoxNm( const SwTable* pTbl )
542 const SwNode* pNd = 0;
543 FnScanFormel fnFormel = 0;
544 switch( eNmType)
546 case INTRNL_NAME:
547 if( pTbl )
548 fnFormel = &SwTableFormula::PtrToBoxNms;
549 break;
550 case REL_NAME:
551 if( pTbl )
553 fnFormel = &SwTableFormula::RelNmsToBoxNms;
554 pNd = GetNodeOfFormula();
556 break;
557 case EXTRNL_NAME:
558 return;
560 sFormel = ScanString( fnFormel, *pTbl, (void*)pNd );
561 eNmType = EXTRNL_NAME;
564 // erzeuge die interne (in CORE) Formel
565 void SwTableFormula::BoxNmToPtr( const SwTable* pTbl )
567 const SwNode* pNd = 0;
568 FnScanFormel fnFormel = 0;
569 switch( eNmType)
571 case EXTRNL_NAME:
572 if( pTbl )
573 fnFormel = &SwTableFormula::BoxNmsToPtr;
574 break;
575 case REL_NAME:
576 if( pTbl )
578 fnFormel = &SwTableFormula::RelBoxNmsToPtr;
579 pNd = GetNodeOfFormula();
581 break;
582 case INTRNL_NAME:
583 return;
585 sFormel = ScanString( fnFormel, *pTbl, (void*)pNd );
586 eNmType = INTRNL_NAME;
589 // erzeuge die relative (fuers Kopieren) Formel
590 void SwTableFormula::ToRelBoxNm( const SwTable* pTbl )
592 const SwNode* pNd = 0;
593 FnScanFormel fnFormel = 0;
594 switch( eNmType)
596 case INTRNL_NAME:
597 case EXTRNL_NAME:
598 if( pTbl )
600 fnFormel = &SwTableFormula::BoxNmsToRelNm;
601 pNd = GetNodeOfFormula();
603 break;
604 case REL_NAME:
605 return;
607 sFormel = ScanString( fnFormel, *pTbl, (void*)pNd );
608 eNmType = REL_NAME;
612 String SwTableFormula::ScanString( FnScanFormel fnFormel, const SwTable& rTbl,
613 void* pPara ) const
615 String aStr;
616 USHORT nFml = 0, nStt = 0, nEnd = 0, nTrenner;
618 do {
619 // falls der Formel ein Name vorangestellt ist, diese Tabelle
620 // benutzen !!
621 const SwTable* pTbl = &rTbl;
623 nStt = sFormel.Search( '<', nFml );
624 if( STRING_NOTFOUND != nStt )
626 while( STRING_NOTFOUND != nStt &&
627 ( ' ' == sFormel.GetChar( nStt + 1 ) ||
628 '=' == sFormel.GetChar( nStt + 1 ) ) )
629 nStt = sFormel.Search( '<', nStt + 1 );
631 if( STRING_NOTFOUND != nStt )
632 nEnd = sFormel.Search( '>', nStt+1 );
634 if( STRING_NOTFOUND == nStt || STRING_NOTFOUND == nEnd )
636 // den Rest setzen und beenden
637 aStr.Insert( sFormel, nFml, sFormel.Len() - nFml );
638 break;
640 aStr.Insert( sFormel, nFml, nStt - nFml ); // Anfang schreiben
642 if( fnFormel != NULL )
644 // ist ein TabellenName vorangestellt ??
645 // JP 16.02.99: SplitMergeBoxNm behandeln den Namen selbst
646 // JP 22.02.99: der CAST muss fuer den Linux-Compiler sein
647 // JP 28.06.99: rel. BoxName have no preceding tablename!
648 if( fnFormel != (FnScanFormel)&SwTableFormula::_SplitMergeBoxNm &&
649 1 < sFormel.Len() && cRelKennung != sFormel.GetChar( 1 ) &&
650 STRING_NOTFOUND != ( nTrenner = sFormel.Search( '.', nStt ))
651 && nTrenner < nEnd )
653 String sTblNm( sFormel.Copy( nStt, nEnd - nStt ));
655 // falls im Namen schon die Punkte enthalten sind,
656 // treten diese immer paarig auf!!! (A1.1.1 !!)
657 if( (sTblNm.GetTokenCount( '.' ) - 1 ) & 1 )
659 sTblNm.Erase( nTrenner - nStt );
661 // beim Bauen der Formel ist der TabellenName unerwuenscht
662 //JP 22.02.99: der CAST muss fuer den Linux-Compiler sein
663 if( fnFormel != (FnScanFormel)&SwTableFormula::_MakeFormel )
664 aStr += sTblNm;
665 nStt = nTrenner;
667 sTblNm.Erase( 0, 1 ); // Trenner loeschen
668 if( sTblNm != rTbl.GetFrmFmt()->GetName() )
670 // dann suchen wir uns mal unsere Tabelle:
671 const SwTable* pFnd = FindTable(
672 *rTbl.GetFrmFmt()->GetDoc(),
673 sTblNm );
674 if( pFnd )
675 pTbl = pFnd;
676 // ??
677 ASSERT( pFnd, "Tabelle nicht gefunden, was nun?" );
682 String sBox( sFormel.Copy( nStt, nEnd - nStt + 1 ));
683 // ein Bereich in dieser Klammer ?
684 if( STRING_NOTFOUND != ( nTrenner = sFormel.Search( ':', nStt ))
685 && nTrenner < nEnd )
687 // ohne die Anfangsklammer
688 String aFirstBox( sFormel.Copy( nStt+1, nTrenner - nStt - 1 ));
689 (this->*fnFormel)( *pTbl, aStr, sBox, &aFirstBox, pPara );
691 else
692 (this->*fnFormel)( *pTbl, aStr, sBox, 0, pPara );
695 nFml = nEnd+1;
696 } while( TRUE );
697 return aStr;
700 const SwTable* SwTableFormula::FindTable( SwDoc& rDoc, const String& rNm ) const
702 const SwFrmFmts& rTblFmts = *rDoc.GetTblFrmFmts();
703 const SwTable* pTmpTbl, *pRet = 0;
704 for( USHORT nFmtCnt = rTblFmts.Count(); nFmtCnt; )
706 SwFrmFmt* pFmt = rTblFmts[ --nFmtCnt ];
707 // falls wir von Sw3Writer gerufen werden, dann ist dem
708 // FormatNamen eine Nummer anhaengig
709 SwTableBox* pFBox;
710 if( COMPARE_EQUAL == rNm.CompareTo( pFmt->GetName(),
711 pFmt->GetName().Search( 0x0a ) ) &&
712 0 != ( pTmpTbl = SwTable::FindTable( pFmt ) ) &&
713 0 != (pFBox = pTmpTbl->GetTabSortBoxes()[0] ) &&
714 pFBox->GetSttNd() &&
715 pFBox->GetSttNd()->GetNodes().IsDocNodes() )
717 // eine Tabelle im normalen NodesArr
718 pRet = pTmpTbl;
719 break;
722 return pRet;
725 /* \f */
727 const SwFrm* lcl_GetBoxFrm( const SwTableBox& rBox )
731 // oder besser ueber die Box den Frame suchen
733 SwClientIter aIter( *pBox->GetFrmFmt() );
734 ULONG nMinPos = ULONG_MAX;
735 const SwFrm* pFnd = 0;
736 for( SwFrm* pF = (SwFrm*)aIter.First( TYPE( SwCellFrm )); pF;
737 pF = (SwFrm*)aIter.Next() )
739 if( pF->Frm().Y() <
743 SwNodeIndex aIdx( *rBox.GetSttNd() );
744 SwCntntNode* pCNd = aIdx.GetNodes().GoNext( &aIdx );
745 ASSERT( pCNd, "Box hat keinen TextNode" );
746 Point aPt; // den im Layout 1. Frame returnen - Tab.Kopfzeile !!
747 return pCNd->GetFrm( &aPt, NULL, FALSE );
750 long lcl_GetLongBoxNum( String& rStr )
752 USHORT nPos;
753 long nRet;
754 if( STRING_NOTFOUND == ( nPos = rStr.Search( cRelTrenner ) ))
756 nRet = rStr.ToInt32();
757 rStr.Erase();
759 else
761 nRet = rStr.Copy( 0, nPos ).ToInt32();
762 rStr.Erase( 0, nPos+1 );
764 return nRet;
767 const SwTableBox* lcl_RelToBox( const SwTable& rTbl,
768 const SwTableBox* pRefBox,
769 const String& rGetName )
771 // hole die Line
772 const SwTableBox* pBox = 0;
773 String sGetName( rGetName );
775 // ist es denn wirklich eine relative Angabe??
776 if( cRelKennung == sGetName.GetChar(0) ) // ja, ...
778 if( !pRefBox )
779 return 0;
781 sGetName.Erase( 0, 1 );
783 const SwTableLines* pLines = (SwTableLines*)&rTbl.GetTabLines();
784 const SwTableBoxes* pBoxes;
785 const SwTableLine* pLine;
787 // bestimme erst mal die Start-Werte der Box:
788 pBox = (SwTableBox*)pRefBox;
789 pLine = pBox->GetUpper();
790 while( pLine->GetUpper() )
792 pBox = pLine->GetUpper();
793 pLine = pBox->GetUpper();
795 USHORT nSttBox = pLine->GetTabBoxes().GetPos( pBox );
796 USHORT nSttLine = rTbl.GetTabLines().GetPos( pLine );
798 long nBoxOffset = lcl_GetLongBoxNum( sGetName ) + nSttBox;
799 long nLineOffset = lcl_GetLongBoxNum( sGetName ) + nSttLine;
801 if( nBoxOffset < 0 || nBoxOffset >= USHRT_MAX ||
802 nLineOffset < 0 || nLineOffset >= USHRT_MAX )
803 return 0;
805 if( nLineOffset >= long(pLines->Count()) )
806 return 0;
808 pLine = (*pLines)[ USHORT(nLineOffset) ];
810 // dann suche die Box
811 pBoxes = &pLine->GetTabBoxes();
812 if( nBoxOffset >= long(pBoxes->Count()) )
813 return 0;
814 pBox = (*pBoxes)[ USHORT(nBoxOffset) ];
816 while( sGetName.Len() )
818 nSttBox = SwTable::_GetBoxNum( sGetName );
819 pLines = &pBox->GetTabLines();
820 if( nSttBox )
821 --nSttBox;
823 nSttLine = SwTable::_GetBoxNum( sGetName );
825 // bestimme die Line
826 if( !nSttLine || nSttLine > pLines->Count() )
827 break;
828 pLine = (*pLines)[ nSttLine-1 ];
830 // bestimme die Box
831 pBoxes = &pLine->GetTabBoxes();
832 if( nSttBox >= pBoxes->Count() )
833 break;
834 pBox = (*pBoxes)[ nSttBox ];
837 if( pBox )
839 if( !pBox->GetSttNd() )
840 // "herunterfallen lassen" bis zur ersten Box
841 while( pBox->GetTabLines().Count() )
842 pBox = pBox->GetTabLines()[0]->GetTabBoxes()[0];
845 else
847 // sonst ist es eine absolute externe Darstellung:
848 pBox = rTbl.GetTblBox( sGetName );
850 return pBox;
853 String lcl_BoxNmToRel( const SwTable& rTbl, const SwTableNode& rTblNd,
854 const String& rRefBoxNm, const String& rGetStr,
855 BOOL bExtrnlNm )
857 String sCpy( rRefBoxNm );
858 String sTmp( rGetStr );
859 if( !bExtrnlNm )
861 // in die Externe Darstellung umwandeln.
862 SwTableBox* pBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(sTmp.ToInt64()));
863 if( !rTbl.GetTabSortBoxes().Seek_Entry( pBox ))
864 return '?';
865 sTmp = pBox->GetName();
868 // sollte die es eine Tabellen uebergreifende Formel sein, dann behalte
869 // die externe Darstellung bei:
870 if( &rTbl == &rTblNd.GetTable() )
872 long nBox = SwTable::_GetBoxNum( sTmp, TRUE );
873 nBox -= SwTable::_GetBoxNum( sCpy, TRUE );
874 long nLine = SwTable::_GetBoxNum( sTmp );
875 nLine -= SwTable::_GetBoxNum( sCpy );
877 sCpy = sTmp; //JP 01.11.95: den Rest aus dem BoxNamen anhaengen
879 sTmp = cRelKennung;
880 sTmp += String::CreateFromInt32( nBox );
881 sTmp += cRelTrenner;
882 sTmp += String::CreateFromInt32( nLine );
884 if( sCpy.Len() )
886 sTmp += cRelTrenner;
887 sTmp += sCpy;
891 if( sTmp.Len() && '>' == sTmp.GetChar( sTmp.Len() - 1 ))
892 sTmp.Erase( sTmp.Len()-1 );
894 return sTmp;
897 USHORT SwTableFormula::GetBoxesOfFormula( const SwTable& rTbl,
898 SwSelBoxes& rBoxes )
900 if( rBoxes.Count() )
901 rBoxes.Remove( USHORT(0), rBoxes.Count() );
903 BoxNmToPtr( &rTbl );
904 ScanString( &SwTableFormula::_GetFmlBoxes, rTbl, &rBoxes );
905 return rBoxes.Count();
908 void SwTableFormula::_GetFmlBoxes( const SwTable& rTbl, String& ,
909 String& rFirstBox, String* pLastBox, void* pPara ) const
911 SwSelBoxes* pBoxes = (SwSelBoxes*)pPara;
912 SwTableBox* pSttBox, *pEndBox = 0;
914 rFirstBox.Erase(0,1); // Kennung fuer Box loeschen
915 // ein Bereich in dieser Klammer ?
916 if( pLastBox )
918 pEndBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(pLastBox->ToInt64()));
920 // ist das ueberhaupt ein gueltiger Pointer ??
921 if( !rTbl.GetTabSortBoxes().Seek_Entry( pEndBox ))
922 pEndBox = 0;
923 rFirstBox.Erase( 0, pLastBox->Len()+1 );
926 pSttBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(rFirstBox.ToInt64()));
927 // ist das ueberhaupt ein gueltiger Pointer ??
928 if( !rTbl.GetTabSortBoxes().Seek_Entry( pSttBox ))
929 pSttBox = 0;
931 if( pEndBox && pSttBox ) // Bereich ?
933 // ueber das Layout alle "selectierten" Boxen und berechne
934 // deren Werte
935 SwSelBoxes aBoxes;
936 GetBoxes( *pSttBox, *pEndBox, aBoxes );
937 pBoxes->Insert( &aBoxes );
939 else if( pSttBox ) // nur die StartBox ?
940 pBoxes->Insert( pSttBox );
943 void SwTableFormula::GetBoxes( const SwTableBox& rSttBox,
944 const SwTableBox& rEndBox,
945 SwSelBoxes& rBoxes ) const
947 // hole ueber das Layout alle "selektierten" Boxen
948 const SwLayoutFrm *pStt, *pEnd;
949 const SwFrm* pFrm = lcl_GetBoxFrm( rSttBox );
950 pStt = pFrm ? pFrm->GetUpper() : 0;
951 pEnd = ( 0 != (pFrm = lcl_GetBoxFrm( rEndBox ))) ? pFrm->GetUpper() : 0;
952 if( !pStt || !pEnd )
953 return ; // no valid selection
955 GetTblSel( pStt, pEnd, rBoxes, 0 );
957 const SwTable* pTbl = pStt->FindTabFrm()->GetTable();
959 // filter die Kopfzeilen-Boxen heraus:
960 if( pTbl->GetRowsToRepeat() > 0 )
962 do { // middle-check loop
963 const SwTableLine* pLine = rSttBox.GetUpper();
964 while( pLine->GetUpper() )
965 pLine = pLine->GetUpper()->GetUpper();
967 if( pTbl->IsHeadline( *pLine ) )
968 break; // Headline mit im Bereich !
970 // vielleicht ist ja Start und Ende vertauscht
971 pLine = rEndBox.GetUpper();
972 while ( pLine->GetUpper() )
973 pLine = pLine->GetUpper()->GetUpper();
975 if( pTbl->IsHeadline( *pLine ) )
976 break; // Headline mit im Bereich !
978 const SwTabFrm *pTable = pStt->FindTabFrm();
979 const SwTabFrm *pEndTable = pEnd->FindTabFrm();
981 if( pTable == pEndTable ) // keine gespl. Tabelle
982 break;
984 // dann mal die Tabellenkoepfe raus:
985 for( USHORT n = 0; n < rBoxes.Count(); ++n )
987 pLine = rBoxes[n]->GetUpper();
988 while( pLine->GetUpper() )
989 pLine = pLine->GetUpper()->GetUpper();
991 if( pTbl->IsHeadline( *pLine ) )
992 rBoxes.Remove( n--, 1 );
994 } while( FALSE );
998 // sind alle Boxen gueltig, auf die sich die Formel bezieht?
999 void SwTableFormula::_HasValidBoxes( const SwTable& rTbl, String& ,
1000 String& rFirstBox, String* pLastBox, void* pPara ) const
1002 BOOL* pBValid = (BOOL*)pPara;
1003 if( *pBValid ) // einmal falsch, immer falsch
1005 SwTableBox* pSttBox = 0, *pEndBox = 0;
1006 rFirstBox.Erase(0,1); // Kennung fuer Box loeschen
1008 // ein Bereich in dieser Klammer ?
1009 if( pLastBox )
1010 rFirstBox.Erase( 0, pLastBox->Len()+1 );
1012 switch( eNmType)
1014 case INTRNL_NAME:
1015 if( pLastBox )
1016 pEndBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(pLastBox->ToInt64()));
1017 pSttBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(rFirstBox.ToInt64()));
1018 break;
1020 case REL_NAME:
1022 const SwNode* pNd = GetNodeOfFormula();
1023 const SwTableBox* pBox = !pNd ? 0
1024 : (SwTableBox *)rTbl.GetTblBox(
1025 pNd->FindTableBoxStartNode()->GetIndex() );
1026 if( pLastBox )
1027 pEndBox = (SwTableBox*)lcl_RelToBox( rTbl, pBox, *pLastBox );
1028 pSttBox = (SwTableBox*)lcl_RelToBox( rTbl, pBox, rFirstBox );
1030 break;
1032 case EXTRNL_NAME:
1033 if( pLastBox )
1034 pEndBox = (SwTableBox*)rTbl.GetTblBox( *pLastBox );
1035 pSttBox = (SwTableBox*)rTbl.GetTblBox( rFirstBox );
1036 break;
1039 // sind das gueltige Pointer ?
1040 if( ( pLastBox &&
1041 ( !pEndBox || !rTbl.GetTabSortBoxes().Seek_Entry( pEndBox ) ) ) ||
1042 ( !pSttBox || !rTbl.GetTabSortBoxes().Seek_Entry( pSttBox ) ) )
1043 *pBValid = FALSE;
1047 BOOL SwTableFormula::HasValidBoxes() const
1049 BOOL bRet = TRUE;
1050 const SwNode* pNd = GetNodeOfFormula();
1051 if( pNd && 0 != ( pNd = pNd->FindTableNode() ) )
1052 ScanString( &SwTableFormula::_HasValidBoxes,
1053 ((SwTableNode*)pNd)->GetTable(), &bRet );
1054 return bRet;
1058 USHORT SwTableFormula::GetLnPosInTbl( const SwTable& rTbl, const SwTableBox* pBox )
1060 USHORT nRet = USHRT_MAX;
1061 if( pBox )
1063 const SwTableLine* pLn = pBox->GetUpper();
1064 while( pLn->GetUpper() )
1065 pLn = pLn->GetUpper()->GetUpper();
1066 nRet = rTbl.GetTabLines().GetPos( pLn );
1068 return nRet;
1071 void SwTableFormula::_SplitMergeBoxNm( const SwTable& rTbl, String& rNewStr,
1072 String& rFirstBox, String* pLastBox, void* pPara ) const
1074 SwTableFmlUpdate& rTblUpd = *(SwTableFmlUpdate*)pPara;
1076 rNewStr += rFirstBox.Copy(0,1); // Kennung fuer Box erhalten
1077 rFirstBox.Erase(0,1);
1079 String sTblNm;
1080 const SwTable* pTbl = &rTbl;
1082 String* pTblNmBox = pLastBox ? pLastBox : &rFirstBox;
1084 USHORT nLastBoxLen = pTblNmBox->Len();
1085 USHORT nTrenner = pTblNmBox->Search( '.' );
1086 if( STRING_NOTFOUND != nTrenner &&
1087 // falls im Namen schon die Punkte enthalten sind,
1088 // treten diese immer paarig auf!!! (A1.1.1 !!)
1089 (pTblNmBox->GetTokenCount( '.' ) - 1 ) & 1 )
1091 sTblNm = pTblNmBox->Copy( 0, nTrenner );
1092 pTblNmBox->Erase( 0, nTrenner + 1);// den Punkt entfernen
1093 const SwTable* pFnd = FindTable( *rTbl.GetFrmFmt()->GetDoc(), sTblNm );
1094 if( pFnd )
1095 pTbl = pFnd;
1097 if( TBL_MERGETBL == rTblUpd.eFlags )
1099 if( pFnd )
1101 if( pFnd == rTblUpd.DATA.pDelTbl )
1103 if( rTblUpd.pTbl != &rTbl ) // es ist nicht die akt.
1104 (rNewStr += rTblUpd.pTbl->GetFrmFmt()->GetName() )
1105 += '.'; // den neuen Tabellen Namen setzen
1106 rTblUpd.bModified = TRUE;
1108 else if( pFnd != rTblUpd.pTbl ||
1109 ( rTblUpd.pTbl != &rTbl && &rTbl != rTblUpd.DATA.pDelTbl))
1110 (rNewStr += sTblNm ) += '.'; // den Tabellen Namen behalten
1111 else
1112 rTblUpd.bModified = TRUE;
1114 else
1115 (rNewStr += sTblNm ) += '.'; // den Tabellen Namen behalten
1119 if( pTblNmBox == pLastBox )
1120 rFirstBox.Erase( 0, nLastBoxLen + 1 );
1122 SwTableBox* pSttBox = 0, *pEndBox = 0;
1123 switch( eNmType )
1125 case INTRNL_NAME:
1126 if( pLastBox )
1127 pEndBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(pLastBox->ToInt64()));
1128 pSttBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(rFirstBox.ToInt64()));
1129 break;
1131 case REL_NAME:
1133 const SwNode* pNd = GetNodeOfFormula();
1134 const SwTableBox* pBox = pNd ? pTbl->GetTblBox(
1135 pNd->FindTableBoxStartNode()->GetIndex() ) : 0;
1136 if( pLastBox )
1137 pEndBox = (SwTableBox*)lcl_RelToBox( *pTbl, pBox, *pLastBox );
1138 pSttBox = (SwTableBox*)lcl_RelToBox( *pTbl, pBox, rFirstBox );
1140 break;
1142 case EXTRNL_NAME:
1143 if( pLastBox )
1144 pEndBox = (SwTableBox*)pTbl->GetTblBox( *pLastBox );
1145 pSttBox = (SwTableBox*)pTbl->GetTblBox( rFirstBox );
1146 break;
1149 if( pLastBox && !pTbl->GetTabSortBoxes().Seek_Entry( pEndBox ))
1150 pEndBox = 0;
1151 if( !pTbl->GetTabSortBoxes().Seek_Entry( pSttBox ))
1152 pSttBox = 0;
1154 if( TBL_SPLITTBL == rTblUpd.eFlags )
1156 // wo liegen die Boxen, in der "alten" oder in der neuen Tabelle?
1157 BOOL bInNewTbl = FALSE;
1158 if( pLastBox )
1160 // das ist die "erste" Box in der Selektion. Die bestimmt ob die
1161 // Formel in der alten oder neuen Tabelle steht.
1162 USHORT nEndLnPos = SwTableFormula::GetLnPosInTbl( *pTbl, pEndBox ),
1163 nSttLnPos = SwTableFormula::GetLnPosInTbl( *pTbl, pSttBox );
1165 if( USHRT_MAX != nSttLnPos && USHRT_MAX != nEndLnPos &&
1166 ((rTblUpd.nSplitLine <= nSttLnPos) ==
1167 (rTblUpd.nSplitLine <= nEndLnPos)) )
1169 // bleiben in der gleichen Tabelle
1170 bInNewTbl = rTblUpd.nSplitLine <= nEndLnPos &&
1171 pTbl == rTblUpd.pTbl;
1173 else
1175 // das ist aufjedenfall eine ungueltige Formel, also fuers
1176 // Undo auf Modified setzen
1177 rTblUpd.bModified = TRUE;
1178 if( pEndBox )
1179 bInNewTbl = USHRT_MAX != nEndLnPos &&
1180 rTblUpd.nSplitLine <= nEndLnPos &&
1181 pTbl == rTblUpd.pTbl;
1184 else
1186 USHORT nSttLnPos = SwTableFormula::GetLnPosInTbl( *pTbl, pSttBox );
1187 // dann landet das Teil in der neuen Tabelle?
1188 bInNewTbl = USHRT_MAX != nSttLnPos &&
1189 rTblUpd.nSplitLine <= nSttLnPos &&
1190 pTbl == rTblUpd.pTbl;
1193 // wenn die Formel selbst in der neuen Tabellen landet
1194 if( rTblUpd.bBehindSplitLine )
1196 if( !bInNewTbl )
1198 rTblUpd.bModified = TRUE;
1199 ( rNewStr += rTblUpd.pTbl->GetFrmFmt()->GetName() ) += '.';
1201 else if( sTblNm.Len() )
1202 ( rNewStr += sTblNm ) += '.';
1204 else if( bInNewTbl )
1206 rTblUpd.bModified = TRUE;
1207 ( rNewStr += *rTblUpd.DATA.pNewTblNm ) += '.';
1209 else if( sTblNm.Len() )
1210 ( rNewStr += sTblNm ) += '.';
1213 if( pLastBox )
1214 ( rNewStr += String::CreateFromInt64((sal_PtrDiff)pEndBox)) += ':';
1215 ( rNewStr += String::CreateFromInt64((sal_PtrDiff)pSttBox))
1216 += rFirstBox.GetChar( rFirstBox.Len() - 1 );
1219 // erzeuge die externe Formel, beachte aber das die Formel
1220 // in einer gesplitteten/gemergten Tabelle landet
1221 void SwTableFormula::ToSplitMergeBoxNm( SwTableFmlUpdate& rTblUpd )
1223 const SwTable* pTbl;
1224 const SwNode* pNd = GetNodeOfFormula();
1225 if( pNd && 0 != ( pNd = pNd->FindTableNode() ))
1226 pTbl = &((SwTableNode*)pNd)->GetTable();
1227 else
1228 pTbl = rTblUpd.pTbl;
1230 sFormel = ScanString( &SwTableFormula::_SplitMergeBoxNm, *pTbl, (void*)&rTblUpd );
1231 eNmType = INTRNL_NAME;