/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sw.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const sal_Unicode cRelTrenner = ','; const sal_Unicode cRelKennung = ''; // CTRL-R const sal_uInt16 cMAXSTACKSIZE = 50; const SwFrm* lcl_GetBoxFrm( const SwTableBox& rBox ); long lcl_GetLongBoxNum( String& rStr ); const SwTableBox* lcl_RelToBox( const SwTable&, const SwTableBox*, const String& ); String lcl_BoxNmToRel( const SwTable&, const SwTableNode&, const String& , const String& , sal_Bool ); /************************************************************************* |* |* double SwTableBox::GetValue() const |* gebe den Wert dieser Box zurueck. Der Wert ergibt sich aus dem 1. |* TextNode. Beginnt dieser mit einer Zahl/Formel, so berechne diese; |* oder mit einem Feld, dann hole den Wert. |* Alle anderen Bedingungen returnen einen Fehler (oder 0 ?) |* |* Ersterstellung JP 30. Jun. 93 |* Letzte Aenderung JP 30. Jun. 93 |* |*************************************************************************/ double SwTableBox::GetValue( SwTblCalcPara& rCalcPara ) const { double nRet = 0; if( rCalcPara.rCalc.IsCalcError() ) return nRet; // schon ein Fehler in der Berechnung rCalcPara.rCalc.SetCalcError( CALC_SYNTAX ); // default immer Fehler // keine Content Box ? if( !pSttNd ) return nRet; if( rCalcPara.IncStackCnt() ) return nRet; rCalcPara.SetLastTblBox( this ); // wird eine Rekursion erzeugt ? SwTableBox* pBox = (SwTableBox*)this; if( rCalcPara.pBoxStk->Seek_Entry( pBox )) return nRet; // steht schon auf dem Stack: FEHLER // bei dieser Box nochmal aufsetzen rCalcPara.SetLastTblBox( this ); rCalcPara.pBoxStk->Insert( pBox ); // eintragen do { // Middle-Check-Loop, damit aus dieser gesprungen werden kann // hier aufgespannt, damit am Ende der Box-Pointer aus dem // Stack ausgetragen wird SwDoc* pDoc = GetFrmFmt()->GetDoc(); const SfxPoolItem* pItem; if( SFX_ITEM_SET == GetFrmFmt()->GetItemState( RES_BOXATR_FORMULA, sal_False, &pItem ) ) { rCalcPara.rCalc.SetCalcError( CALC_NOERR ); // wieder zuruecksetzen if( !((SwTblBoxFormula*)pItem)->IsValid() ) { // dann berechnen const SwTable* pTmp = rCalcPara.pTbl; rCalcPara.pTbl = &pBox->GetSttNd()->FindTableNode()->GetTable(); ((SwTblBoxFormula*)pItem)->Calc( rCalcPara, nRet ); if( !rCalcPara.IsStackOverFlow() ) { SwFrmFmt* pFmt = pBox->ClaimFrmFmt(); SfxItemSet aTmp( pDoc->GetAttrPool(), RES_BOXATR_BEGIN,RES_BOXATR_END-1 ); aTmp.Put( SwTblBoxValue( nRet ) ); if( SFX_ITEM_SET != pFmt->GetItemState( RES_BOXATR_FORMAT )) aTmp.Put( SwTblBoxNumFormat( 0 )); pFmt->SetFmtAttr( aTmp ); } rCalcPara.pTbl = pTmp; } else nRet = GetFrmFmt()->GetTblBoxValue().GetValue(); break; } else if( SFX_ITEM_SET == pBox->GetFrmFmt()->GetItemState( RES_BOXATR_VALUE, sal_False, &pItem ) ) { rCalcPara.rCalc.SetCalcError( CALC_NOERR ); // wieder zuruecksetzen nRet = ((SwTblBoxValue*)pItem)->GetValue(); break; } SwTxtNode* pTxtNd = pDoc->GetNodes()[ pSttNd->GetIndex() + 1 ]->GetTxtNode(); if( !pTxtNd ) break; xub_StrLen nSttPos = 0; const String& rTxt = pTxtNd->GetTxt(); while( nSttPos < rTxt.Len() && ( ' ' == rTxt.GetChar( nSttPos ) || '\t' == rTxt.GetChar( nSttPos ) ) ) ++nSttPos; // beginnt an erster Position ein "RechenFeld", dann erfrage den Wert // von diesem sal_Unicode const Char = rTxt.GetChar(nSttPos); if ( nSttPos < rTxt.Len() && ( CH_TXTATR_BREAKWORD == Char || CH_TXTATR_INWORD == Char ) ) { SwIndex aIdx( pTxtNd, nSttPos ); SwTxtFld * const pTxtFld = static_cast( pTxtNd->GetTxtAttrForCharAt(aIdx.GetIndex(), RES_TXTATR_FIELD)); if( !pTxtFld ) break; rCalcPara.rCalc.SetCalcError( CALC_NOERR ); // wieder zuruecksetzen const SwField* pFld = pTxtFld->GetFmtFld().GetField(); switch( pFld->GetTyp()->Which() ) { case RES_SETEXPFLD: nRet = ((SwSetExpField*)pFld)->GetValue(); break; case RES_USERFLD: nRet = ((SwUserFieldType*)pFld)->GetValue(); break; case RES_TABLEFLD: { SwTblField* pTblFld = (SwTblField*)pFld; if( !pTblFld->IsValid() ) // ist der Wert gueltig ?? { // die richtige Tabelle mitgeben! const SwTable* pTmp = rCalcPara.pTbl; rCalcPara.pTbl = &pTxtNd->FindTableNode()->GetTable(); pTblFld->CalcField( rCalcPara ); rCalcPara.pTbl = pTmp; } nRet = pTblFld->GetValue(); } break; case RES_DATETIMEFLD: nRet = ((SwDateTimeField*)pFld)->GetValue(); break; case RES_JUMPEDITFLD: //JP 14.09.98: Bug 56112 - der Platzhalter kann nie einen // gueltigen Inhalt haben! nRet = 0; break; default: String const value(pFld->ExpandField(true)); nRet = rCalcPara.rCalc.Calculate(value).GetDouble(); } } else { // Ergebnis ist 0 und kein Fehler! rCalcPara.rCalc.SetCalcError( CALC_NOERR ); // wieder zuruecksetzen double aNum; String sTxt( rTxt.Copy( nSttPos ) ); sal_uInt32 nFmtIndex = GetFrmFmt()->GetTblBoxNumFmt().GetValue(); SvNumberFormatter* pNumFmtr = pDoc->GetNumberFormatter(); if( NUMBERFORMAT_TEXT == nFmtIndex ) nFmtIndex = 0; // JP 22.04.98: Bug 49659 - Sonderbehandlung fuer Prozent else if( sTxt.Len() && NUMBERFORMAT_PERCENT == pNumFmtr->GetType( nFmtIndex )) { sal_uInt32 nTmpFmt = 0; if( pNumFmtr->IsNumberFormat( sTxt, nTmpFmt, aNum ) && NUMBERFORMAT_NUMBER == pNumFmtr->GetType( nTmpFmt )) sTxt += '%'; } if( pNumFmtr->IsNumberFormat( sTxt, nFmtIndex, aNum )) nRet = aNum; } // ?? sonst ist das ein Fehler } while( sal_False ); if( !rCalcPara.IsStackOverFlow() ) { rCalcPara.pBoxStk->Remove( pBox ); // raus aus dem Stack rCalcPara.DecStackCnt(); } //JP 12.01.99: mit Fehlererkennung, Bug 60794 if( DBL_MAX == nRet ) rCalcPara.rCalc.SetCalcError( CALC_SYNTAX ); // Fehler setzen return nRet; } /* */ // Struktur, die zum TabelleRechnen benoetigt wird SwTblCalcPara::SwTblCalcPara( SwCalc& rCalculator, const SwTable& rTable ) : pLastTblBox( 0 ), nStackCnt( 0 ), nMaxSize( cMAXSTACKSIZE ), rCalc( rCalculator ), pTbl( &rTable ) { pBoxStk = new SwTableSortBoxes; } SwTblCalcPara::~SwTblCalcPara() { delete pBoxStk; } sal_Bool SwTblCalcPara::CalcWithStackOverflow() { // falls ein StackUeberlauf erkannt wurde, sollte mit // der letzten Box noch mal aufgesetzt werden. Irgend // ein Weg sollte dann sal_uInt16 nSaveMaxSize = nMaxSize; nMaxSize = cMAXSTACKSIZE - 5; sal_uInt16 nCnt = 0; SwTableBoxes aStackOverFlows; do { SwTableBox* pBox = (SwTableBox*)pLastTblBox; nStackCnt = 0; rCalc.SetCalcError( CALC_NOERR ); aStackOverFlows.C40_INSERT( SwTableBox, pBox, nCnt++ ); pBoxStk->Remove( pBox ); pBox->GetValue( *this ); } while( IsStackOverFlow() ); nMaxSize = cMAXSTACKSIZE - 3; // es muss mind. 1 Stufe tiefer gehen! // falls Rekursionen erkannt wurden nStackCnt = 0; rCalc.SetCalcError( CALC_NOERR ); pBoxStk->Remove( sal_uInt16(0), pBoxStk->Count() ); while( !rCalc.IsCalcError() && nCnt ) { aStackOverFlows[ --nCnt ]->GetValue( *this ); if( IsStackOverFlow() && !CalcWithStackOverflow() ) break; } nMaxSize = nSaveMaxSize; aStackOverFlows.Remove( 0, aStackOverFlows.Count() ); return !rCalc.IsCalcError(); } /* */ SwTableFormula::SwTableFormula( const String& rFormel ) : sFormel( rFormel ) { eNmType = EXTRNL_NAME; bValidValue = sal_False; } SwTableFormula::~SwTableFormula() { } void SwTableFormula::_MakeFormel( const SwTable& rTbl, String& rNewStr, String& rFirstBox, String* pLastBox, void* pPara ) const { SwTblCalcPara* pCalcPara = (SwTblCalcPara*)pPara; if( pCalcPara->rCalc.IsCalcError() ) // ist schon Fehler gesetzt ? return; SwTableBox* pSttBox, *pEndBox = 0; rFirstBox.Erase(0,1); // Kennung fuer Box loeschen // ein Bereich in dieser Klammer ? if( pLastBox ) { pEndBox = reinterpret_cast(sal::static_int_cast(pLastBox->ToInt64())); // ist das ueberhaupt ein gueltiger Pointer ?? if( !rTbl.GetTabSortBoxes().Seek_Entry( pEndBox )) pEndBox = 0; rFirstBox.Erase( 0, pLastBox->Len()+1 ); } pSttBox = reinterpret_cast(sal::static_int_cast(rFirstBox.ToInt64())); // ist das ueberhaupt ein gueltiger Pointer ?? if( !rTbl.GetTabSortBoxes().Seek_Entry( pSttBox )) pSttBox = 0; rNewStr += ' '; if( pEndBox && pSttBox ) // Bereich ? { // hole ueber das Layout alle "selectierten" Boxen und berechne // deren Werte SwSelBoxes aBoxes; GetBoxes( *pSttBox, *pEndBox, aBoxes ); rNewStr += '('; bool bDelim = false; for( sal_uInt16 n = 0; n < aBoxes.Count() && !pCalcPara->rCalc.IsCalcError(); ++n ) { const SwTableBox* pTblBox = aBoxes[n]; if ( pTblBox->getRowSpan() >= 1 ) { if( bDelim ) rNewStr += cListDelim; bDelim = true; rNewStr += pCalcPara->rCalc.GetStrResult( pTblBox->GetValue( *pCalcPara ), sal_False ); } } rNewStr += ')'; } else if( pSttBox && !pLastBox ) // nur die StartBox ? { //JP 12.01.99: und keine EndBox in der Formel! // Berechne den Wert der Box if ( pSttBox->getRowSpan() >= 1 ) { rNewStr += pCalcPara->rCalc.GetStrResult( pSttBox->GetValue( *pCalcPara ), sal_False ); } } else pCalcPara->rCalc.SetCalcError( CALC_SYNTAX ); // Fehler setzen rNewStr += ' '; } void SwTableFormula::RelNmsToBoxNms( const SwTable& rTbl, String& rNewStr, String& rFirstBox, String* pLastBox, void* pPara ) const { // relativen Namen zu Box-Namen (externe Darstellung) SwNode* pNd = (SwNode*)pPara; ASSERT( pNd, "Feld steht in keinem TextNode" ); const SwTableBox *pRelBox, *pBox = (SwTableBox *)rTbl.GetTblBox( pNd->FindTableBoxStartNode()->GetIndex() ); rNewStr += rFirstBox.Copy(0,1); // Kennung fuer Box erhalten rFirstBox.Erase(0,1); if( pLastBox ) { if( 0 != ( pRelBox = lcl_RelToBox( rTbl, pBox, *pLastBox )) ) rNewStr += pRelBox->GetName(); else rNewStr.AppendAscii("A1"); rNewStr += ':'; rFirstBox.Erase( 0, pLastBox->Len()+1 ); } if( 0 != ( pRelBox = lcl_RelToBox( rTbl, pBox, rFirstBox )) ) rNewStr += pRelBox->GetName(); else rNewStr.AppendAscii("A1"); // Kennung fuer Box erhalten rNewStr += rFirstBox.GetChar( rFirstBox.Len() - 1 ); } void SwTableFormula::RelBoxNmsToPtr( const SwTable& rTbl, String& rNewStr, String& rFirstBox, String* pLastBox, void* pPara ) const { // relativen Namen zu Box-Pointern (interne Darstellung) SwNode* pNd = (SwNode*)pPara; ASSERT( pNd, "Feld steht in keinem Node" ); const SwTableBox *pRelBox, *pBox = (SwTableBox*)rTbl.GetTblBox( pNd->FindTableBoxStartNode()->GetIndex() ); rNewStr += rFirstBox.Copy(0,1); // Kennung fuer Box erhalten rFirstBox.Erase(0,1); if( pLastBox ) { if( 0 != ( pRelBox = lcl_RelToBox( rTbl, pBox, *pLastBox )) ) rNewStr += String::CreateFromInt64( (sal_PtrDiff)pRelBox ); else rNewStr += '0'; rNewStr += ':'; rFirstBox.Erase( 0, pLastBox->Len()+1 ); } if( 0 != ( pRelBox = lcl_RelToBox( rTbl, pBox, rFirstBox )) ) rNewStr += String::CreateFromInt64( (sal_PtrDiff)pRelBox ); else rNewStr += '0'; // Kennung fuer Box erhalten rNewStr += rFirstBox.GetChar( rFirstBox.Len() - 1 ); } void SwTableFormula::BoxNmsToRelNm( const SwTable& rTbl, String& rNewStr, String& rFirstBox, String* pLastBox, void* pPara ) const { // Box-Namen (externe Darstellung) zu relativen Namen SwNode* pNd = (SwNode*)pPara; ASSERT( pNd, "Feld steht in keinem Node" ); const SwTableNode* pTblNd = pNd->FindTableNode(); String sRefBoxNm; if( &pTblNd->GetTable() == &rTbl ) { const SwTableBox *pBox = rTbl.GetTblBox( pNd->FindTableBoxStartNode()->GetIndex() ); ASSERT( pBox, "Feld steht in keiner Tabelle" ); sRefBoxNm = pBox->GetName(); } rNewStr += rFirstBox.Copy(0,1); // Kennung fuer Box erhalten rFirstBox.Erase(0,1); if( pLastBox ) { rNewStr += lcl_BoxNmToRel( rTbl, *pTblNd, sRefBoxNm, *pLastBox, eNmType == EXTRNL_NAME ); rNewStr += ':'; rFirstBox.Erase( 0, pLastBox->Len()+1 ); } rNewStr += lcl_BoxNmToRel( rTbl, *pTblNd, sRefBoxNm, rFirstBox, eNmType == EXTRNL_NAME ); // Kennung fuer Box erhalten rNewStr += rFirstBox.GetChar( rFirstBox.Len() - 1 ); } void SwTableFormula::PtrToBoxNms( const SwTable& rTbl, String& rNewStr, String& rFirstBox, String* pLastBox, void* ) const { // ein Bereich in dieser Klammer ? SwTableBox* pBox; rNewStr += rFirstBox.Copy(0,1); // Kennung fuer Box erhalten rFirstBox.Erase(0,1); if( pLastBox ) { pBox = reinterpret_cast(sal::static_int_cast(pLastBox->ToInt64())); // ist das ueberhaupt ein gueltiger Pointer ?? if( rTbl.GetTabSortBoxes().Seek_Entry( pBox )) rNewStr += pBox->GetName(); else rNewStr += '?'; rNewStr += ':'; rFirstBox.Erase( 0, pLastBox->Len()+1 ); } pBox = reinterpret_cast(sal::static_int_cast(rFirstBox.ToInt64())); // ist das ueberhaupt ein gueltiger Pointer ?? if( rTbl.GetTabSortBoxes().Seek_Entry( pBox )) rNewStr += pBox->GetName(); else rNewStr += '?'; // Kennung fuer Box erhalten rNewStr += rFirstBox.GetChar( rFirstBox.Len() - 1 ); } void SwTableFormula::BoxNmsToPtr( const SwTable& rTbl, String& rNewStr, String& rFirstBox, String* pLastBox, void* ) const { // ein Bereich in dieser Klammer ? const SwTableBox* pBox; rNewStr += rFirstBox.Copy(0,1); // Kennung fuer Box erhalten rFirstBox.Erase(0,1); if( pLastBox ) { pBox = rTbl.GetTblBox( *pLastBox ); rNewStr += String::CreateFromInt64( (sal_PtrDiff)pBox ); rNewStr += ':'; rFirstBox.Erase( 0, pLastBox->Len()+1 ); } pBox = rTbl.GetTblBox( rFirstBox ); rNewStr += String::CreateFromInt64( (sal_PtrDiff)pBox ); // Kennung fuer Box erhalten rNewStr += rFirstBox.GetChar( rFirstBox.Len() - 1 ); } // erzeuge die externe (fuer UI) Formel void SwTableFormula::PtrToBoxNm( const SwTable* pTbl ) { const SwNode* pNd = 0; FnScanFormel fnFormel = 0; switch( eNmType) { case INTRNL_NAME: if( pTbl ) fnFormel = &SwTableFormula::PtrToBoxNms; break; case REL_NAME: if( pTbl ) { fnFormel = &SwTableFormula::RelNmsToBoxNms; pNd = GetNodeOfFormula(); } break; case EXTRNL_NAME: return; } sFormel = ScanString( fnFormel, *pTbl, (void*)pNd ); eNmType = EXTRNL_NAME; } // erzeuge die interne (in CORE) Formel void SwTableFormula::BoxNmToPtr( const SwTable* pTbl ) { const SwNode* pNd = 0; FnScanFormel fnFormel = 0; switch( eNmType) { case EXTRNL_NAME: if( pTbl ) fnFormel = &SwTableFormula::BoxNmsToPtr; break; case REL_NAME: if( pTbl ) { fnFormel = &SwTableFormula::RelBoxNmsToPtr; pNd = GetNodeOfFormula(); } break; case INTRNL_NAME: return; } sFormel = ScanString( fnFormel, *pTbl, (void*)pNd ); eNmType = INTRNL_NAME; } // erzeuge die relative (fuers Kopieren) Formel void SwTableFormula::ToRelBoxNm( const SwTable* pTbl ) { const SwNode* pNd = 0; FnScanFormel fnFormel = 0; switch( eNmType) { case INTRNL_NAME: case EXTRNL_NAME: if( pTbl ) { fnFormel = &SwTableFormula::BoxNmsToRelNm; pNd = GetNodeOfFormula(); } break; case REL_NAME: return; } sFormel = ScanString( fnFormel, *pTbl, (void*)pNd ); eNmType = REL_NAME; } String SwTableFormula::ScanString( FnScanFormel fnFormel, const SwTable& rTbl, void* pPara ) const { String aStr; sal_uInt16 nFml = 0, nStt = 0, nEnd = 0, nTrenner; do { // falls der Formel ein Name vorangestellt ist, diese Tabelle // benutzen !! const SwTable* pTbl = &rTbl; nStt = sFormel.Search( '<', nFml ); if( STRING_NOTFOUND != nStt ) { while( STRING_NOTFOUND != nStt && ( ' ' == sFormel.GetChar( nStt + 1 ) || '=' == sFormel.GetChar( nStt + 1 ) ) ) nStt = sFormel.Search( '<', nStt + 1 ); if( STRING_NOTFOUND != nStt ) nEnd = sFormel.Search( '>', nStt+1 ); } if( STRING_NOTFOUND == nStt || STRING_NOTFOUND == nEnd ) { // den Rest setzen und beenden aStr.Insert( sFormel, nFml, sFormel.Len() - nFml ); break; } aStr.Insert( sFormel, nFml, nStt - nFml ); // Anfang schreiben if( fnFormel != NULL ) { // ist ein TabellenName vorangestellt ?? // JP 16.02.99: SplitMergeBoxNm behandeln den Namen selbst // JP 22.02.99: der CAST muss fuer den Linux-Compiler sein // JP 28.06.99: rel. BoxName have no preceding tablename! if( fnFormel != (FnScanFormel)&SwTableFormula::_SplitMergeBoxNm && 1 < sFormel.Len() && cRelKennung != sFormel.GetChar( 1 ) && STRING_NOTFOUND != ( nTrenner = sFormel.Search( '.', nStt )) && nTrenner < nEnd ) { String sTblNm( sFormel.Copy( nStt, nEnd - nStt )); // falls im Namen schon die Punkte enthalten sind, // treten diese immer paarig auf!!! (A1.1.1 !!) if( (sTblNm.GetTokenCount( '.' ) - 1 ) & 1 ) { sTblNm.Erase( nTrenner - nStt ); // beim Bauen der Formel ist der TabellenName unerwuenscht //JP 22.02.99: der CAST muss fuer den Linux-Compiler sein if( fnFormel != (FnScanFormel)&SwTableFormula::_MakeFormel ) aStr += sTblNm; nStt = nTrenner; sTblNm.Erase( 0, 1 ); // Trenner loeschen if( sTblNm != rTbl.GetFrmFmt()->GetName() ) { // dann suchen wir uns mal unsere Tabelle: const SwTable* pFnd = FindTable( *rTbl.GetFrmFmt()->GetDoc(), sTblNm ); if( pFnd ) pTbl = pFnd; // ?? ASSERT( pFnd, "Tabelle nicht gefunden, was nun?" ); } } } String sBox( sFormel.Copy( nStt, nEnd - nStt + 1 )); // ein Bereich in dieser Klammer ? if( STRING_NOTFOUND != ( nTrenner = sFormel.Search( ':', nStt )) && nTrenner < nEnd ) { // ohne die Anfangsklammer String aFirstBox( sFormel.Copy( nStt+1, nTrenner - nStt - 1 )); (this->*fnFormel)( *pTbl, aStr, sBox, &aFirstBox, pPara ); } else (this->*fnFormel)( *pTbl, aStr, sBox, 0, pPara ); } nFml = nEnd+1; } while( sal_True ); return aStr; } const SwTable* SwTableFormula::FindTable( SwDoc& rDoc, const String& rNm ) const { const SwFrmFmts& rTblFmts = *rDoc.GetTblFrmFmts(); const SwTable* pTmpTbl, *pRet = 0; for( sal_uInt16 nFmtCnt = rTblFmts.Count(); nFmtCnt; ) { SwFrmFmt* pFmt = rTblFmts[ --nFmtCnt ]; // falls wir von Sw3Writer gerufen werden, dann ist dem // FormatNamen eine Nummer anhaengig SwTableBox* pFBox; if( COMPARE_EQUAL == rNm.CompareTo( pFmt->GetName(), pFmt->GetName().Search( 0x0a ) ) && 0 != ( pTmpTbl = SwTable::FindTable( pFmt ) ) && 0 != (pFBox = pTmpTbl->GetTabSortBoxes()[0] ) && pFBox->GetSttNd() && pFBox->GetSttNd()->GetNodes().IsDocNodes() ) { // eine Tabelle im normalen NodesArr pRet = pTmpTbl; break; } } return pRet; } const SwFrm* lcl_GetBoxFrm( const SwTableBox& rBox ) { SwNodeIndex aIdx( *rBox.GetSttNd() ); SwCntntNode* pCNd = aIdx.GetNodes().GoNext( &aIdx ); ASSERT( pCNd, "Box hat keinen TextNode" ); Point aPt; // den im Layout 1. Frame returnen - Tab.Kopfzeile !! return pCNd->getLayoutFrm( pCNd->GetDoc()->GetCurrentLayout(), &aPt, NULL, sal_False ); } long lcl_GetLongBoxNum( String& rStr ) { sal_uInt16 nPos; long nRet; if( STRING_NOTFOUND == ( nPos = rStr.Search( cRelTrenner ) )) { nRet = rStr.ToInt32(); rStr.Erase(); } else { nRet = rStr.Copy( 0, nPos ).ToInt32(); rStr.Erase( 0, nPos+1 ); } return nRet; } const SwTableBox* lcl_RelToBox( const SwTable& rTbl, const SwTableBox* pRefBox, const String& rGetName ) { // hole die Line const SwTableBox* pBox = 0; String sGetName( rGetName ); // ist es denn wirklich eine relative Angabe?? if( cRelKennung == sGetName.GetChar(0) ) // ja, ... { if( !pRefBox ) return 0; sGetName.Erase( 0, 1 ); const SwTableLines* pLines = (SwTableLines*)&rTbl.GetTabLines(); const SwTableBoxes* pBoxes; const SwTableLine* pLine; // bestimme erst mal die Start-Werte der Box: pBox = (SwTableBox*)pRefBox; pLine = pBox->GetUpper(); while( pLine->GetUpper() ) { pBox = pLine->GetUpper(); pLine = pBox->GetUpper(); } sal_uInt16 nSttBox = pLine->GetTabBoxes().GetPos( pBox ); sal_uInt16 nSttLine = rTbl.GetTabLines().GetPos( pLine ); long nBoxOffset = lcl_GetLongBoxNum( sGetName ) + nSttBox; long nLineOffset = lcl_GetLongBoxNum( sGetName ) + nSttLine; if( nBoxOffset < 0 || nBoxOffset >= USHRT_MAX || nLineOffset < 0 || nLineOffset >= USHRT_MAX ) return 0; if( nLineOffset >= long(pLines->Count()) ) return 0; pLine = (*pLines)[ sal_uInt16(nLineOffset) ]; // dann suche die Box pBoxes = &pLine->GetTabBoxes(); if( nBoxOffset >= long(pBoxes->Count()) ) return 0; pBox = (*pBoxes)[ sal_uInt16(nBoxOffset) ]; while( sGetName.Len() ) { nSttBox = SwTable::_GetBoxNum( sGetName ); pLines = &pBox->GetTabLines(); if( nSttBox ) --nSttBox; nSttLine = SwTable::_GetBoxNum( sGetName ); // bestimme die Line if( !nSttLine || nSttLine > pLines->Count() ) break; pLine = (*pLines)[ nSttLine-1 ]; // bestimme die Box pBoxes = &pLine->GetTabBoxes(); if( nSttBox >= pBoxes->Count() ) break; pBox = (*pBoxes)[ nSttBox ]; } if( pBox ) { if( !pBox->GetSttNd() ) // "herunterfallen lassen" bis zur ersten Box while( pBox->GetTabLines().Count() ) pBox = pBox->GetTabLines()[0]->GetTabBoxes()[0]; } } else { // sonst ist es eine absolute externe Darstellung: pBox = rTbl.GetTblBox( sGetName ); } return pBox; } String lcl_BoxNmToRel( const SwTable& rTbl, const SwTableNode& rTblNd, const String& rRefBoxNm, const String& rGetStr, sal_Bool bExtrnlNm ) { String sCpy( rRefBoxNm ); String sTmp( rGetStr ); if( !bExtrnlNm ) { // in die Externe Darstellung umwandeln. SwTableBox* pBox = reinterpret_cast(sal::static_int_cast(sTmp.ToInt64())); if( !rTbl.GetTabSortBoxes().Seek_Entry( pBox )) return '?'; sTmp = pBox->GetName(); } // sollte die es eine Tabellen uebergreifende Formel sein, dann behalte // die externe Darstellung bei: if( &rTbl == &rTblNd.GetTable() ) { long nBox = SwTable::_GetBoxNum( sTmp, sal_True ); nBox -= SwTable::_GetBoxNum( sCpy, sal_True ); long nLine = SwTable::_GetBoxNum( sTmp ); nLine -= SwTable::_GetBoxNum( sCpy ); sCpy = sTmp; //JP 01.11.95: den Rest aus dem BoxNamen anhaengen sTmp = cRelKennung; sTmp += String::CreateFromInt32( nBox ); sTmp += cRelTrenner; sTmp += String::CreateFromInt32( nLine ); if( sCpy.Len() ) { sTmp += cRelTrenner; sTmp += sCpy; } } if( sTmp.Len() && '>' == sTmp.GetChar( sTmp.Len() - 1 )) sTmp.Erase( sTmp.Len()-1 ); return sTmp; } sal_uInt16 SwTableFormula::GetBoxesOfFormula( const SwTable& rTbl, SwSelBoxes& rBoxes ) { if( rBoxes.Count() ) rBoxes.Remove( sal_uInt16(0), rBoxes.Count() ); BoxNmToPtr( &rTbl ); ScanString( &SwTableFormula::_GetFmlBoxes, rTbl, &rBoxes ); return rBoxes.Count(); } void SwTableFormula::_GetFmlBoxes( const SwTable& rTbl, String& , String& rFirstBox, String* pLastBox, void* pPara ) const { SwSelBoxes* pBoxes = (SwSelBoxes*)pPara; SwTableBox* pSttBox, *pEndBox = 0; rFirstBox.Erase(0,1); // Kennung fuer Box loeschen // ein Bereich in dieser Klammer ? if( pLastBox ) { pEndBox = reinterpret_cast(sal::static_int_cast(pLastBox->ToInt64())); // ist das ueberhaupt ein gueltiger Pointer ?? if( !rTbl.GetTabSortBoxes().Seek_Entry( pEndBox )) pEndBox = 0; rFirstBox.Erase( 0, pLastBox->Len()+1 ); } pSttBox = reinterpret_cast(sal::static_int_cast(rFirstBox.ToInt64())); // ist das ueberhaupt ein gueltiger Pointer ?? if( !rTbl.GetTabSortBoxes().Seek_Entry( pSttBox )) pSttBox = 0; if( pEndBox && pSttBox ) // Bereich ? { // ueber das Layout alle "selectierten" Boxen und berechne // deren Werte SwSelBoxes aBoxes; GetBoxes( *pSttBox, *pEndBox, aBoxes ); pBoxes->Insert( &aBoxes ); } else if( pSttBox ) // nur die StartBox ? pBoxes->Insert( pSttBox ); } void SwTableFormula::GetBoxes( const SwTableBox& rSttBox, const SwTableBox& rEndBox, SwSelBoxes& rBoxes ) const { // hole ueber das Layout alle "selektierten" Boxen const SwLayoutFrm *pStt, *pEnd; const SwFrm* pFrm = lcl_GetBoxFrm( rSttBox ); pStt = pFrm ? pFrm->GetUpper() : 0; pEnd = ( 0 != (pFrm = lcl_GetBoxFrm( rEndBox ))) ? pFrm->GetUpper() : 0; if( !pStt || !pEnd ) return ; // no valid selection GetTblSel( pStt, pEnd, rBoxes, 0 ); const SwTable* pTbl = pStt->FindTabFrm()->GetTable(); // filter die Kopfzeilen-Boxen heraus: if( pTbl->GetRowsToRepeat() > 0 ) { do { // middle-check loop const SwTableLine* pLine = rSttBox.GetUpper(); while( pLine->GetUpper() ) pLine = pLine->GetUpper()->GetUpper(); if( pTbl->IsHeadline( *pLine ) ) break; // Headline mit im Bereich ! // vielleicht ist ja Start und Ende vertauscht pLine = rEndBox.GetUpper(); while ( pLine->GetUpper() ) pLine = pLine->GetUpper()->GetUpper(); if( pTbl->IsHeadline( *pLine ) ) break; // Headline mit im Bereich ! const SwTabFrm *pTable = pStt->FindTabFrm(); const SwTabFrm *pEndTable = pEnd->FindTabFrm(); if( pTable == pEndTable ) // keine gespl. Tabelle break; // dann mal die Tabellenkoepfe raus: for( sal_uInt16 n = 0; n < rBoxes.Count(); ++n ) { pLine = rBoxes[n]->GetUpper(); while( pLine->GetUpper() ) pLine = pLine->GetUpper()->GetUpper(); if( pTbl->IsHeadline( *pLine ) ) rBoxes.Remove( n--, 1 ); } } while( sal_False ); } } // sind alle Boxen gueltig, auf die sich die Formel bezieht? void SwTableFormula::_HasValidBoxes( const SwTable& rTbl, String& , String& rFirstBox, String* pLastBox, void* pPara ) const { sal_Bool* pBValid = (sal_Bool*)pPara; if( *pBValid ) // einmal falsch, immer falsch { SwTableBox* pSttBox = 0, *pEndBox = 0; rFirstBox.Erase(0,1); // Kennung fuer Box loeschen // ein Bereich in dieser Klammer ? if( pLastBox ) rFirstBox.Erase( 0, pLastBox->Len()+1 ); switch( eNmType) { case INTRNL_NAME: if( pLastBox ) pEndBox = reinterpret_cast(sal::static_int_cast(pLastBox->ToInt64())); pSttBox = reinterpret_cast(sal::static_int_cast(rFirstBox.ToInt64())); break; case REL_NAME: { const SwNode* pNd = GetNodeOfFormula(); const SwTableBox* pBox = !pNd ? 0 : (SwTableBox *)rTbl.GetTblBox( pNd->FindTableBoxStartNode()->GetIndex() ); if( pLastBox ) pEndBox = (SwTableBox*)lcl_RelToBox( rTbl, pBox, *pLastBox ); pSttBox = (SwTableBox*)lcl_RelToBox( rTbl, pBox, rFirstBox ); } break; case EXTRNL_NAME: if( pLastBox ) pEndBox = (SwTableBox*)rTbl.GetTblBox( *pLastBox ); pSttBox = (SwTableBox*)rTbl.GetTblBox( rFirstBox ); break; } // sind das gueltige Pointer ? if( ( pLastBox && ( !pEndBox || !rTbl.GetTabSortBoxes().Seek_Entry( pEndBox ) ) ) || ( !pSttBox || !rTbl.GetTabSortBoxes().Seek_Entry( pSttBox ) ) ) *pBValid = sal_False; } } sal_Bool SwTableFormula::HasValidBoxes() const { sal_Bool bRet = sal_True; const SwNode* pNd = GetNodeOfFormula(); if( pNd && 0 != ( pNd = pNd->FindTableNode() ) ) ScanString( &SwTableFormula::_HasValidBoxes, ((SwTableNode*)pNd)->GetTable(), &bRet ); return bRet; } sal_uInt16 SwTableFormula::GetLnPosInTbl( const SwTable& rTbl, const SwTableBox* pBox ) { sal_uInt16 nRet = USHRT_MAX; if( pBox ) { const SwTableLine* pLn = pBox->GetUpper(); while( pLn->GetUpper() ) pLn = pLn->GetUpper()->GetUpper(); nRet = rTbl.GetTabLines().GetPos( pLn ); } return nRet; } void SwTableFormula::_SplitMergeBoxNm( const SwTable& rTbl, String& rNewStr, String& rFirstBox, String* pLastBox, void* pPara ) const { SwTableFmlUpdate& rTblUpd = *(SwTableFmlUpdate*)pPara; rNewStr += rFirstBox.Copy(0,1); // Kennung fuer Box erhalten rFirstBox.Erase(0,1); String sTblNm; const SwTable* pTbl = &rTbl; String* pTblNmBox = pLastBox ? pLastBox : &rFirstBox; sal_uInt16 nLastBoxLen = pTblNmBox->Len(); sal_uInt16 nTrenner = pTblNmBox->Search( '.' ); if( STRING_NOTFOUND != nTrenner && // falls im Namen schon die Punkte enthalten sind, // treten diese immer paarig auf!!! (A1.1.1 !!) (pTblNmBox->GetTokenCount( '.' ) - 1 ) & 1 ) { sTblNm = pTblNmBox->Copy( 0, nTrenner ); pTblNmBox->Erase( 0, nTrenner + 1);// den Punkt entfernen const SwTable* pFnd = FindTable( *rTbl.GetFrmFmt()->GetDoc(), sTblNm ); if( pFnd ) pTbl = pFnd; if( TBL_MERGETBL == rTblUpd.eFlags ) { if( pFnd ) { if( pFnd == rTblUpd.DATA.pDelTbl ) { if( rTblUpd.pTbl != &rTbl ) // es ist nicht die akt. (rNewStr += rTblUpd.pTbl->GetFrmFmt()->GetName() ) += '.'; // den neuen Tabellen Namen setzen rTblUpd.bModified = sal_True; } else if( pFnd != rTblUpd.pTbl || ( rTblUpd.pTbl != &rTbl && &rTbl != rTblUpd.DATA.pDelTbl)) (rNewStr += sTblNm ) += '.'; // den Tabellen Namen behalten else rTblUpd.bModified = sal_True; } else (rNewStr += sTblNm ) += '.'; // den Tabellen Namen behalten } } if( pTblNmBox == pLastBox ) rFirstBox.Erase( 0, nLastBoxLen + 1 ); SwTableBox* pSttBox = 0, *pEndBox = 0; switch( eNmType ) { case INTRNL_NAME: if( pLastBox ) pEndBox = reinterpret_cast(sal::static_int_cast(pLastBox->ToInt64())); pSttBox = reinterpret_cast(sal::static_int_cast(rFirstBox.ToInt64())); break; case REL_NAME: { const SwNode* pNd = GetNodeOfFormula(); const SwTableBox* pBox = pNd ? pTbl->GetTblBox( pNd->FindTableBoxStartNode()->GetIndex() ) : 0; if( pLastBox ) pEndBox = (SwTableBox*)lcl_RelToBox( *pTbl, pBox, *pLastBox ); pSttBox = (SwTableBox*)lcl_RelToBox( *pTbl, pBox, rFirstBox ); } break; case EXTRNL_NAME: if( pLastBox ) pEndBox = (SwTableBox*)pTbl->GetTblBox( *pLastBox ); pSttBox = (SwTableBox*)pTbl->GetTblBox( rFirstBox ); break; } if( pLastBox && !pTbl->GetTabSortBoxes().Seek_Entry( pEndBox )) pEndBox = 0; if( !pTbl->GetTabSortBoxes().Seek_Entry( pSttBox )) pSttBox = 0; if( TBL_SPLITTBL == rTblUpd.eFlags ) { // wo liegen die Boxen, in der "alten" oder in der neuen Tabelle? sal_Bool bInNewTbl = sal_False; if( pLastBox ) { // das ist die "erste" Box in der Selektion. Die bestimmt ob die // Formel in der alten oder neuen Tabelle steht. sal_uInt16 nEndLnPos = SwTableFormula::GetLnPosInTbl( *pTbl, pEndBox ), nSttLnPos = SwTableFormula::GetLnPosInTbl( *pTbl, pSttBox ); if( USHRT_MAX != nSttLnPos && USHRT_MAX != nEndLnPos && ((rTblUpd.nSplitLine <= nSttLnPos) == (rTblUpd.nSplitLine <= nEndLnPos)) ) { // bleiben in der gleichen Tabelle bInNewTbl = rTblUpd.nSplitLine <= nEndLnPos && pTbl == rTblUpd.pTbl; } else { // das ist aufjedenfall eine ungueltige Formel, also fuers // Undo auf Modified setzen rTblUpd.bModified = sal_True; if( pEndBox ) bInNewTbl = USHRT_MAX != nEndLnPos && rTblUpd.nSplitLine <= nEndLnPos && pTbl == rTblUpd.pTbl; } } else { sal_uInt16 nSttLnPos = SwTableFormula::GetLnPosInTbl( *pTbl, pSttBox ); // dann landet das Teil in der neuen Tabelle? bInNewTbl = USHRT_MAX != nSttLnPos && rTblUpd.nSplitLine <= nSttLnPos && pTbl == rTblUpd.pTbl; } // wenn die Formel selbst in der neuen Tabellen landet if( rTblUpd.bBehindSplitLine ) { if( !bInNewTbl ) { rTblUpd.bModified = sal_True; ( rNewStr += rTblUpd.pTbl->GetFrmFmt()->GetName() ) += '.'; } else if( sTblNm.Len() ) ( rNewStr += sTblNm ) += '.'; } else if( bInNewTbl ) { rTblUpd.bModified = sal_True; ( rNewStr += *rTblUpd.DATA.pNewTblNm ) += '.'; } else if( sTblNm.Len() ) ( rNewStr += sTblNm ) += '.'; } if( pLastBox ) ( rNewStr += String::CreateFromInt64((sal_PtrDiff)pEndBox)) += ':'; ( rNewStr += String::CreateFromInt64((sal_PtrDiff)pSttBox)) += rFirstBox.GetChar( rFirstBox.Len() - 1 ); } // erzeuge die externe Formel, beachte aber das die Formel // in einer gesplitteten/gemergten Tabelle landet void SwTableFormula::ToSplitMergeBoxNm( SwTableFmlUpdate& rTblUpd ) { const SwTable* pTbl; const SwNode* pNd = GetNodeOfFormula(); if( pNd && 0 != ( pNd = pNd->FindTableNode() )) pTbl = &((SwTableNode*)pNd)->GetTable(); else pTbl = rTblUpd.pTbl; sFormel = ScanString( &SwTableFormula::_SplitMergeBoxNm, *pTbl, (void*)&rTblUpd ); eNmType = INTRNL_NAME; }