1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_sc.hxx" 26 27 28 29 // INCLUDE --------------------------------------------------------------- 30 31 #include <svl/intitem.hxx> 32 #include <svl/zforlist.hxx> 33 #include <vcl/sound.hxx> 34 #include <formula/token.hxx> 35 36 #include "document.hxx" 37 #include "table.hxx" 38 #include "globstr.hrc" 39 #include "subtotal.hxx" 40 #include "docoptio.hxx" 41 #include "interpre.hxx" 42 #include "markdata.hxx" 43 #include "validat.hxx" 44 #include "scitems.hxx" 45 #include "stlpool.hxx" 46 #include "poolhelp.hxx" 47 #include "detdata.hxx" 48 #include "patattr.hxx" 49 #include "chgtrack.hxx" 50 #include "progress.hxx" 51 #include "paramisc.hxx" 52 #include "compiler.hxx" 53 #include "externalrefmgr.hxx" 54 55 using namespace formula; 56 57 // ----------------------------------------------------------------------- 58 59 // Nach der Regula Falsi Methode 60 sal_Bool ScDocument::Solver(SCCOL nFCol, SCROW nFRow, SCTAB nFTab, 61 SCCOL nVCol, SCROW nVRow, SCTAB nVTab, 62 const String& sValStr, double& nX) 63 { 64 sal_Bool bRet = sal_False; 65 nX = 0.0; 66 if (ValidColRow(nFCol, nFRow) && ValidColRow(nVCol, nVRow) && 67 VALIDTAB(nFTab) && VALIDTAB(nVTab) && pTab[nFTab] && pTab[nVTab]) 68 { 69 CellType eFType, eVType; 70 GetCellType(nFCol, nFRow, nFTab, eFType); 71 GetCellType(nVCol, nVRow, nVTab, eVType); 72 // CELLTYPE_NOTE: no value, but referenced by formula 73 // #i108005# convert target value to number using default format, 74 // as previously done in ScInterpreter::GetDouble 75 double nTargetVal = 0.0; 76 sal_uInt32 nFIndex = 0; 77 if (eFType == CELLTYPE_FORMULA && (eVType == CELLTYPE_VALUE || eVType == CELLTYPE_NOTE) && 78 GetFormatTable()->IsNumberFormat(sValStr, nFIndex, nTargetVal)) 79 { 80 ScSingleRefData aRefData; 81 aRefData.InitFlags(); 82 aRefData.nCol = nVCol; 83 aRefData.nRow = nVRow; 84 aRefData.nTab = nVTab; 85 86 ScTokenArray aArr; 87 aArr.AddOpCode( ocBackSolver ); 88 aArr.AddOpCode( ocOpen ); 89 aArr.AddSingleReference( aRefData ); 90 aArr.AddOpCode( ocSep ); 91 92 aRefData.nCol = nFCol; 93 aRefData.nRow = nFRow; 94 aRefData.nTab = nFTab; 95 96 aArr.AddSingleReference( aRefData ); 97 aArr.AddOpCode( ocSep ); 98 aArr.AddDouble( nTargetVal ); 99 aArr.AddOpCode( ocClose ); 100 aArr.AddOpCode( ocStop ); 101 102 ScFormulaCell* pCell = new ScFormulaCell( this, ScAddress(), &aArr ); 103 104 if (pCell) 105 { 106 // FIXME FIXME FIXME this might need to be reworked now that we have formula::FormulaErrorToken and ScFormulaResult, double check !!! 107 DBG_ERRORFILE("ScDocument::Solver: -> ScFormulaCell::GetValueAlways might need reimplementation"); 108 pCell->Interpret(); 109 sal_uInt16 nErrCode = pCell->GetErrCode(); 110 nX = pCell->GetValueAlways(); 111 if (nErrCode == 0) // kein fehler beim Rechnen 112 bRet = sal_True; 113 delete pCell; 114 } 115 } 116 } 117 return bRet; 118 } 119 120 void ScDocument::InsertMatrixFormula(SCCOL nCol1, SCROW nRow1, 121 SCCOL nCol2, SCROW nRow2, 122 const ScMarkData& rMark, 123 const String& rFormula, 124 const ScTokenArray* pArr, 125 const formula::FormulaGrammar::Grammar eGram ) 126 { 127 PutInOrder(nCol1, nCol2); 128 PutInOrder(nRow1, nRow2); 129 SCTAB i, nTab1; 130 SCCOL j; 131 SCROW k; 132 i = 0; 133 sal_Bool bStop = sal_False; 134 while (i <= MAXTAB && !bStop) // erste markierte Tabelle finden 135 { 136 if (pTab[i] && rMark.GetTableSelect(i)) 137 bStop = sal_True; 138 else 139 i++; 140 } 141 nTab1 = i; 142 if (i == MAXTAB + 1) 143 { 144 Sound::Beep(); 145 DBG_ERROR("ScDocument::InsertMatrixFormula Keine Tabelle markiert"); 146 return; 147 } 148 149 ScFormulaCell* pCell; 150 ScAddress aPos( nCol1, nRow1, nTab1 ); 151 if (pArr) 152 pCell = new ScFormulaCell( this, aPos, pArr, eGram, MM_FORMULA ); 153 else 154 pCell = new ScFormulaCell( this, aPos, rFormula, eGram, MM_FORMULA ); 155 pCell->SetMatColsRows( nCol2 - nCol1 + 1, nRow2 - nRow1 + 1 ); 156 for (i = 0; i <= MAXTAB; i++) 157 { 158 if (pTab[i] && rMark.GetTableSelect(i)) 159 { 160 if (i == nTab1) 161 pTab[i]->PutCell(nCol1, nRow1, pCell); 162 else 163 pTab[i]->PutCell(nCol1, nRow1, pCell->CloneWithoutNote(*this, ScAddress( nCol1, nRow1, i), SC_CLONECELL_STARTLISTENING)); 164 } 165 } 166 167 ScSingleRefData aRefData; 168 aRefData.InitFlags(); 169 aRefData.nCol = nCol1; 170 aRefData.nRow = nRow1; 171 aRefData.nTab = nTab1; 172 aRefData.SetColRel( sal_True ); 173 aRefData.SetRowRel( sal_True ); 174 aRefData.SetTabRel( sal_True ); 175 aRefData.CalcRelFromAbs( ScAddress( nCol1, nRow1, nTab1 ) ); 176 177 ScTokenArray aArr; 178 ScToken* t = static_cast<ScToken*>(aArr.AddMatrixSingleReference( aRefData)); 179 180 for (i = 0; i <= MAXTAB; i++) 181 { 182 if (pTab[i] && rMark.GetTableSelect(i)) 183 { 184 pTab[i]->DoColResize( nCol1, nCol2, static_cast<SCSIZE>(nRow2 - nRow1 + 1) ); 185 if (i != nTab1) 186 { 187 aRefData.nTab = i; 188 aRefData.nRelTab = i - nTab1; 189 t->GetSingleRef() = aRefData; 190 } 191 for (j = nCol1; j <= nCol2; j++) 192 { 193 for (k = nRow1; k <= nRow2; k++) 194 { 195 if (j != nCol1 || k != nRow1) // nicht in der ersten Zelle 196 { 197 // Array muss geklont werden, damit jede 198 // Zelle ein eigenes Array erhaelt! 199 aPos = ScAddress( j, k, i ); 200 t->CalcRelFromAbs( aPos ); 201 pCell = new ScFormulaCell( this, aPos, aArr.Clone(), eGram, MM_REFERENCE ); 202 pTab[i]->PutCell(j, k, (ScBaseCell*) pCell); 203 } 204 } 205 } 206 } 207 } 208 } 209 210 void ScDocument::InsertTableOp(const ScTabOpParam& rParam, // Mehrfachoperation 211 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, 212 const ScMarkData& rMark) 213 { 214 PutInOrder(nCol1, nCol2); 215 PutInOrder(nRow1, nRow2); 216 SCTAB i, nTab1; 217 SCCOL j; 218 SCROW k; 219 i = 0; 220 sal_Bool bStop = sal_False; 221 while (i <= MAXTAB && !bStop) // erste markierte Tabelle finden 222 { 223 if (pTab[i] && rMark.GetTableSelect(i)) 224 bStop = sal_True; 225 else 226 i++; 227 } 228 nTab1 = i; 229 if (i == MAXTAB + 1) 230 { 231 Sound::Beep(); 232 DBG_ERROR("ScDocument::InsertTableOp: Keine Tabelle markiert"); 233 return; 234 } 235 236 ScRefAddress aRef; 237 String aForString = '='; 238 aForString += ScCompiler::GetNativeSymbol(ocTableOp); 239 aForString += ScCompiler::GetNativeSymbol( ocOpen); 240 241 const String& sSep = ScCompiler::GetNativeSymbol( ocSep); 242 if (rParam.nMode == 0) // nur Spalte 243 { 244 aRef.Set( rParam.aRefFormulaCell.GetAddress(), sal_True, sal_False, sal_False ); 245 aForString += aRef.GetRefString(this, nTab1); 246 aForString += sSep; 247 aForString += rParam.aRefColCell.GetRefString(this, nTab1); 248 aForString += sSep; 249 aRef.Set( nCol1, nRow1, nTab1, sal_False, sal_True, sal_True ); 250 aForString += aRef.GetRefString(this, nTab1); 251 nCol1++; 252 nCol2 = Min( nCol2, (SCCOL)(rParam.aRefFormulaEnd.Col() - 253 rParam.aRefFormulaCell.Col() + nCol1 + 1)); 254 } 255 else if (rParam.nMode == 1) // nur zeilenweise 256 { 257 aRef.Set( rParam.aRefFormulaCell.GetAddress(), sal_False, sal_True, sal_False ); 258 aForString += aRef.GetRefString(this, nTab1); 259 aForString += sSep; 260 aForString += rParam.aRefRowCell.GetRefString(this, nTab1); 261 aForString += sSep; 262 aRef.Set( nCol1, nRow1, nTab1, sal_True, sal_False, sal_True ); 263 aForString += aRef.GetRefString(this, nTab1); 264 nRow1++; 265 nRow2 = Min( nRow2, (SCROW)(rParam.aRefFormulaEnd.Row() - 266 rParam.aRefFormulaCell.Row() + nRow1 + 1)); 267 } 268 else // beides 269 { 270 aForString += rParam.aRefFormulaCell.GetRefString(this, nTab1); 271 aForString += sSep; 272 aForString += rParam.aRefColCell.GetRefString(this, nTab1); 273 aForString += sSep; 274 aRef.Set( nCol1, nRow1 + 1, nTab1, sal_False, sal_True, sal_True ); 275 aForString += aRef.GetRefString(this, nTab1); 276 aForString += sSep; 277 aForString += rParam.aRefRowCell.GetRefString(this, nTab1); 278 aForString += sSep; 279 aRef.Set( nCol1 + 1, nRow1, nTab1, sal_True, sal_False, sal_True ); 280 aForString += aRef.GetRefString(this, nTab1); 281 nCol1++; nRow1++; 282 } 283 aForString += ScCompiler::GetNativeSymbol( ocClose); 284 285 ScFormulaCell aRefCell( this, ScAddress( nCol1, nRow1, nTab1 ), aForString, 286 formula::FormulaGrammar::GRAM_NATIVE, MM_NONE ); 287 for( j = nCol1; j <= nCol2; j++ ) 288 for( k = nRow1; k <= nRow2; k++ ) 289 for (i = 0; i <= MAXTAB; i++) 290 if( pTab[i] && rMark.GetTableSelect(i) ) 291 pTab[i]->PutCell( j, k, aRefCell.CloneWithoutNote( *this, ScAddress( j, k, i ), SC_CLONECELL_STARTLISTENING ) ); 292 } 293 294 bool ScDocument::MarkUsedExternalReferences( ScTokenArray & rArr ) 295 { 296 bool bAllMarked = false; 297 if (rArr.GetLen()) 298 { 299 ScExternalRefManager* pRefMgr = NULL; 300 rArr.Reset(); 301 ScToken* t; 302 while (!bAllMarked && (t = static_cast<ScToken*>(rArr.GetNextReferenceOrName())) != NULL) 303 { 304 if (t->GetOpCode() == ocExternalRef) 305 { 306 if (!pRefMgr) 307 pRefMgr = GetExternalRefManager(); 308 switch (t->GetType()) 309 { 310 case svExternalSingleRef: 311 bAllMarked = pRefMgr->setCacheTableReferenced( 312 t->GetIndex(), t->GetString(), 1); 313 break; 314 case svExternalDoubleRef: 315 { 316 const ScComplexRefData& rRef = t->GetDoubleRef(); 317 size_t nSheets = rRef.Ref2.nTab - rRef.Ref1.nTab + 1; 318 bAllMarked = pRefMgr->setCacheTableReferenced( 319 t->GetIndex(), t->GetString(), nSheets); 320 } 321 break; 322 case svExternalName: 323 /* TODO: external names aren't supported yet, but would 324 * have to be marked as well, if so. Mechanism would be 325 * different. */ 326 DBG_ERRORFILE("ScDocument::MarkUsedExternalReferences: implement the svExternalName case!"); 327 break; 328 default: break; 329 } 330 } 331 } 332 } 333 return bAllMarked; 334 } 335 336 sal_Bool ScDocument::GetNextSpellingCell(SCCOL& nCol, SCROW& nRow, SCTAB nTab, 337 sal_Bool bInSel, const ScMarkData& rMark) const 338 { 339 if (ValidTab(nTab) && pTab[nTab]) 340 return pTab[nTab]->GetNextSpellingCell( nCol, nRow, bInSel, rMark ); 341 else 342 return sal_False; 343 } 344 345 sal_Bool ScDocument::GetNextMarkedCell( SCCOL& rCol, SCROW& rRow, SCTAB nTab, 346 const ScMarkData& rMark ) 347 { 348 if (ValidTab(nTab) && pTab[nTab]) 349 return pTab[nTab]->GetNextMarkedCell( rCol, rRow, rMark ); 350 else 351 return sal_False; 352 } 353 354 sal_Bool ScDocument::ReplaceStyle(const SvxSearchItem& rSearchItem, 355 SCCOL nCol, SCROW nRow, SCTAB nTab, 356 ScMarkData& rMark, 357 sal_Bool bIsUndoP) 358 { 359 if (pTab[nTab]) 360 return pTab[nTab]->ReplaceStyle(rSearchItem, nCol, nRow, rMark, bIsUndoP); 361 else 362 return sal_False; 363 } 364 365 void ScDocument::CompileDBFormula() 366 { 367 for (SCTAB i=0; i<=MAXTAB; i++) 368 { 369 if (pTab[i]) pTab[i]->CompileDBFormula(); 370 } 371 } 372 373 void ScDocument::CompileDBFormula( sal_Bool bCreateFormulaString ) 374 { 375 for (SCTAB i=0; i<=MAXTAB; i++) 376 { 377 if (pTab[i]) pTab[i]->CompileDBFormula( bCreateFormulaString ); 378 } 379 } 380 381 void ScDocument::CompileNameFormula( sal_Bool bCreateFormulaString ) 382 { 383 if ( pCondFormList ) 384 pCondFormList->CompileAll(); // nach ScNameDlg noetig 385 386 for (SCTAB i=0; i<=MAXTAB; i++) 387 { 388 if (pTab[i]) pTab[i]->CompileNameFormula( bCreateFormulaString ); 389 } 390 } 391 392 void ScDocument::CompileColRowNameFormula() 393 { 394 for (SCTAB i=0; i<=MAXTAB; i++) 395 { 396 if (pTab[i]) pTab[i]->CompileColRowNameFormula(); 397 } 398 } 399 400 void ScDocument::DoColResize( SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCSIZE nAdd ) 401 { 402 if (ValidTab(nTab) && pTab[nTab]) 403 pTab[nTab]->DoColResize( nCol1, nCol2, nAdd ); 404 else 405 { 406 DBG_ERROR("DoColResize: falsche Tabelle"); 407 } 408 } 409 410 void ScDocument::InvalidateTableArea() 411 { 412 for (SCTAB nTab=0; nTab<=MAXTAB && pTab[nTab]; nTab++) 413 { 414 pTab[nTab]->InvalidateTableArea(); 415 if ( pTab[nTab]->IsScenario() ) 416 pTab[nTab]->InvalidateScenarioRanges(); 417 } 418 } 419 420 sal_Int32 ScDocument::GetMaxStringLen( SCTAB nTab, SCCOL nCol, 421 SCROW nRowStart, SCROW nRowEnd, CharSet eCharSet ) const 422 { 423 if (ValidTab(nTab) && pTab[nTab]) 424 return pTab[nTab]->GetMaxStringLen( nCol, nRowStart, nRowEnd, eCharSet ); 425 else 426 return 0; 427 } 428 429 xub_StrLen ScDocument::GetMaxNumberStringLen( sal_uInt16& nPrecision, SCTAB nTab, 430 SCCOL nCol, 431 SCROW nRowStart, SCROW nRowEnd ) const 432 { 433 if (ValidTab(nTab) && pTab[nTab]) 434 return pTab[nTab]->GetMaxNumberStringLen( nPrecision, nCol, 435 nRowStart, nRowEnd ); 436 else 437 return 0; 438 } 439 440 sal_Bool ScDocument::GetSelectionFunction( ScSubTotalFunc eFunc, 441 const ScAddress& rCursor, const ScMarkData& rMark, 442 double& rResult ) 443 { 444 ScFunctionData aData(eFunc); 445 446 ScRange aSingle( rCursor ); 447 if ( rMark.IsMarked() ) 448 rMark.GetMarkArea(aSingle); 449 450 SCCOL nStartCol = aSingle.aStart.Col(); 451 SCROW nStartRow = aSingle.aStart.Row(); 452 SCCOL nEndCol = aSingle.aEnd.Col(); 453 SCROW nEndRow = aSingle.aEnd.Row(); 454 455 for (SCTAB nTab=0; nTab<=MAXTAB && !aData.bError; nTab++) 456 if (pTab[nTab] && rMark.GetTableSelect(nTab)) 457 pTab[nTab]->UpdateSelectionFunction( aData, 458 nStartCol, nStartRow, nEndCol, nEndRow, rMark ); 459 460 //! rMark an UpdateSelectionFunction uebergeben !!!!! 461 462 if (!aData.bError) 463 switch (eFunc) 464 { 465 case SUBTOTAL_FUNC_SUM: 466 rResult = aData.nVal; 467 break; 468 case SUBTOTAL_FUNC_CNT: 469 case SUBTOTAL_FUNC_CNT2: 470 rResult = aData.nCount; 471 break; 472 case SUBTOTAL_FUNC_AVE: 473 if (aData.nCount) 474 rResult = aData.nVal / (double) aData.nCount; 475 else 476 aData.bError = sal_True; 477 break; 478 case SUBTOTAL_FUNC_MAX: 479 case SUBTOTAL_FUNC_MIN: 480 if (aData.nCount) 481 rResult = aData.nVal; 482 else 483 aData.bError = sal_True; 484 break; 485 default: 486 { 487 // added to avoid warnings 488 } 489 } 490 491 if (aData.bError) 492 rResult = 0.0; 493 494 return !aData.bError; 495 } 496 497 double ScDocument::RoundValueAsShown( double fVal, sal_uLong nFormat ) 498 { 499 short nType; 500 if ( (nType = GetFormatTable()->GetType( nFormat )) != NUMBERFORMAT_DATE 501 && nType != NUMBERFORMAT_TIME && nType != NUMBERFORMAT_DATETIME ) 502 { 503 short nPrecision; 504 if ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0) 505 { 506 nPrecision = (short)GetFormatTable()->GetFormatPrecision( nFormat ); 507 switch ( nType ) 508 { 509 case NUMBERFORMAT_PERCENT: // 0,41% == 0,0041 510 nPrecision += 2; 511 break; 512 case NUMBERFORMAT_SCIENTIFIC: // 1,23e-3 == 0,00123 513 { 514 if ( fVal > 0.0 ) 515 nPrecision = sal::static_int_cast<short>( nPrecision - (short)floor( log10( fVal ) ) ); 516 else if ( fVal < 0.0 ) 517 nPrecision = sal::static_int_cast<short>( nPrecision - (short)floor( log10( -fVal ) ) ); 518 break; 519 } 520 } 521 } 522 else 523 { 524 nPrecision = (short)GetDocOptions().GetStdPrecision(); 525 // #i115512# no rounding for automatic decimals 526 if (nPrecision == static_cast<short>(SvNumberFormatter::UNLIMITED_PRECISION)) 527 return fVal; 528 } 529 double fRound = ::rtl::math::round( fVal, nPrecision ); 530 if ( ::rtl::math::approxEqual( fVal, fRound ) ) 531 return fVal; // durch Rundung hoechstens Fehler 532 else 533 return fRound; 534 } 535 else 536 return fVal; 537 } 538 539 // 540 // bedingte Formate und Gueltigkeitsbereiche 541 // 542 543 sal_uLong ScDocument::AddCondFormat( const ScConditionalFormat& rNew ) 544 { 545 if (rNew.IsEmpty()) 546 return 0; // leer ist immer 0 547 548 if (!pCondFormList) 549 pCondFormList = new ScConditionalFormatList; 550 551 sal_uLong nMax = 0; 552 sal_uInt16 nCount = pCondFormList->Count(); 553 for (sal_uInt16 i=0; i<nCount; i++) 554 { 555 const ScConditionalFormat* pForm = (*pCondFormList)[i]; 556 sal_uLong nKey = pForm->GetKey(); 557 if ( pForm->EqualEntries( rNew ) ) 558 return nKey; 559 if ( nKey > nMax ) 560 nMax = nKey; 561 } 562 563 // Der Aufruf kann aus ScPatternAttr::PutInPool kommen, darum Clone (echte Kopie) 564 565 sal_uLong nNewKey = nMax + 1; 566 ScConditionalFormat* pInsert = rNew.Clone(this); 567 pInsert->SetKey( nNewKey ); 568 pCondFormList->InsertNew( pInsert ); 569 return nNewKey; 570 } 571 572 sal_uLong ScDocument::AddValidationEntry( const ScValidationData& rNew ) 573 { 574 if (rNew.IsEmpty()) 575 return 0; // leer ist immer 0 576 577 if (!pValidationList) 578 pValidationList = new ScValidationDataList; 579 580 sal_uLong nMax = 0; 581 sal_uInt16 nCount = pValidationList->Count(); 582 for (sal_uInt16 i=0; i<nCount; i++) 583 { 584 const ScValidationData* pData = (*pValidationList)[i]; 585 sal_uLong nKey = pData->GetKey(); 586 if ( pData->EqualEntries( rNew ) ) 587 return nKey; 588 if ( nKey > nMax ) 589 nMax = nKey; 590 } 591 592 // Der Aufruf kann aus ScPatternAttr::PutInPool kommen, darum Clone (echte Kopie) 593 594 sal_uLong nNewKey = nMax + 1; 595 ScValidationData* pInsert = rNew.Clone(this); 596 pInsert->SetKey( nNewKey ); 597 pValidationList->InsertNew( pInsert ); 598 return nNewKey; 599 } 600 601 const SfxPoolItem* ScDocument::GetEffItem( 602 SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt16 nWhich ) const 603 { 604 const ScPatternAttr* pPattern = GetPattern( nCol, nRow, nTab ); 605 if ( pPattern ) 606 { 607 const SfxItemSet& rSet = pPattern->GetItemSet(); 608 const SfxPoolItem* pItem; 609 if ( rSet.GetItemState( ATTR_CONDITIONAL, sal_True, &pItem ) == SFX_ITEM_SET ) 610 { 611 sal_uLong nIndex = ((const SfxUInt32Item*)pItem)->GetValue(); 612 if (nIndex && pCondFormList) 613 { 614 const ScConditionalFormat* pForm = pCondFormList->GetFormat( nIndex ); 615 if ( pForm ) 616 { 617 ScBaseCell* pCell = ((ScDocument*)this)->GetCell(ScAddress(nCol,nRow,nTab)); 618 String aStyle = pForm->GetCellStyle( pCell, ScAddress(nCol, nRow, nTab) ); 619 if (aStyle.Len()) 620 { 621 SfxStyleSheetBase* pStyleSheet = xPoolHelper->GetStylePool()->Find( 622 aStyle, SFX_STYLE_FAMILY_PARA ); 623 if ( pStyleSheet && pStyleSheet->GetItemSet().GetItemState( 624 nWhich, sal_True, &pItem ) == SFX_ITEM_SET ) 625 return pItem; 626 } 627 } 628 } 629 } 630 return &rSet.Get( nWhich ); 631 } 632 DBG_ERROR("kein Pattern"); 633 return NULL; 634 } 635 636 const SfxItemSet* ScDocument::GetCondResult( SCCOL nCol, SCROW nRow, SCTAB nTab ) const 637 { 638 const ScConditionalFormat* pForm = GetCondFormat( nCol, nRow, nTab ); 639 if ( pForm ) 640 { 641 ScBaseCell* pCell = ((ScDocument*)this)->GetCell(ScAddress(nCol,nRow,nTab)); 642 String aStyle = pForm->GetCellStyle( pCell, ScAddress(nCol, nRow, nTab) ); 643 if (aStyle.Len()) 644 { 645 SfxStyleSheetBase* pStyleSheet = xPoolHelper->GetStylePool()->Find( aStyle, SFX_STYLE_FAMILY_PARA ); 646 if ( pStyleSheet ) 647 return &pStyleSheet->GetItemSet(); 648 // if style is not there, treat like no condition 649 } 650 } 651 return NULL; 652 } 653 654 const ScConditionalFormat* ScDocument::GetCondFormat( 655 SCCOL nCol, SCROW nRow, SCTAB nTab ) const 656 { 657 sal_uLong nIndex = ((const SfxUInt32Item*)GetAttr(nCol,nRow,nTab,ATTR_CONDITIONAL))->GetValue(); 658 if (nIndex) 659 { 660 if (pCondFormList) 661 return pCondFormList->GetFormat( nIndex ); 662 else 663 { 664 DBG_ERROR("pCondFormList ist 0"); 665 } 666 } 667 668 return NULL; 669 } 670 671 const ScValidationData* ScDocument::GetValidationEntry( sal_uLong nIndex ) const 672 { 673 if ( pValidationList ) 674 return pValidationList->GetData( nIndex ); 675 else 676 return NULL; 677 } 678 679 void ScDocument::FindConditionalFormat( sal_uLong nKey, ScRangeList& rRanges ) 680 { 681 for (SCTAB i=0; i<=MAXTAB && pTab[i]; i++) 682 pTab[i]->FindConditionalFormat( nKey, rRanges ); 683 } 684 685 void ScDocument::FindConditionalFormat( sal_uLong nKey, ScRangeList& rRanges, SCTAB nTab ) 686 { 687 if(VALIDTAB(nTab) && pTab[nTab]) 688 pTab[nTab]->FindConditionalFormat( nKey, rRanges ); 689 } 690 691 void ScDocument::ConditionalChanged( sal_uLong nKey ) 692 { 693 if ( nKey && pCondFormList && !bIsClip && !bIsUndo ) // nKey==0 -> noop 694 { 695 ScConditionalFormat* pForm = pCondFormList->GetFormat( nKey ); 696 if (pForm) 697 pForm->InvalidateArea(); 698 } 699 } 700 701 void ScDocument::SetCondFormList(ScConditionalFormatList* pNew) 702 { 703 if (pCondFormList) 704 { 705 pCondFormList->DeleteAndDestroy( 0, pCondFormList->Count() ); 706 delete pCondFormList; 707 } 708 709 pCondFormList = pNew; 710 } 711 712 //------------------------------------------------------------------------ 713 714 sal_Bool ScDocument::HasDetectiveOperations() const 715 { 716 return pDetOpList && pDetOpList->Count(); 717 } 718 719 void ScDocument::AddDetectiveOperation( const ScDetOpData& rData ) 720 { 721 if (!pDetOpList) 722 pDetOpList = new ScDetOpList; 723 724 pDetOpList->Append( new ScDetOpData( rData ) ); 725 } 726 727 void ScDocument::ClearDetectiveOperations() 728 { 729 delete pDetOpList; // loescht auch die Eintraege 730 pDetOpList = NULL; 731 } 732 733 void ScDocument::SetDetOpList(ScDetOpList* pNew) 734 { 735 delete pDetOpList; // loescht auch die Eintraege 736 pDetOpList = pNew; 737 } 738 739 //------------------------------------------------------------------------ 740 // 741 // Vergleich von Dokumenten 742 // 743 //------------------------------------------------------------------------ 744 745 // Pfriemel-Faktoren 746 #define SC_DOCCOMP_MAXDIFF 256 747 #define SC_DOCCOMP_MINGOOD 128 748 #define SC_DOCCOMP_COLUMNS 10 749 #define SC_DOCCOMP_ROWS 100 750 751 752 sal_uInt16 ScDocument::RowDifferences( SCROW nThisRow, SCTAB nThisTab, 753 ScDocument& rOtherDoc, SCROW nOtherRow, SCTAB nOtherTab, 754 SCCOL nMaxCol, SCCOLROW* pOtherCols ) 755 { 756 sal_uLong nDif = 0; 757 sal_uLong nUsed = 0; 758 for (SCCOL nThisCol=0; nThisCol<=nMaxCol; nThisCol++) 759 { 760 SCCOL nOtherCol; 761 if ( pOtherCols ) 762 nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]); 763 else 764 nOtherCol = nThisCol; 765 766 if (ValidCol(nOtherCol)) // nur Spalten vergleichen, die in beiden Dateien sind 767 { 768 const ScBaseCell* pThisCell = GetCell( ScAddress( nThisCol, nThisRow, nThisTab ) ); 769 const ScBaseCell* pOtherCell = rOtherDoc.GetCell( ScAddress( nOtherCol, nOtherRow, nOtherTab ) ); 770 if (!ScBaseCell::CellEqual( pThisCell, pOtherCell )) 771 { 772 if ( pThisCell && pOtherCell ) 773 nDif += 3; 774 else 775 nDif += 4; // Inhalt <-> leer zaehlt mehr 776 } 777 778 if ( ( pThisCell && pThisCell->GetCellType()!=CELLTYPE_NOTE ) || 779 ( pOtherCell && pOtherCell->GetCellType()!=CELLTYPE_NOTE ) ) 780 ++nUsed; 781 } 782 } 783 784 if (nUsed > 0) 785 return static_cast<sal_uInt16>((nDif*64)/nUsed); // max.256 (SC_DOCCOMP_MAXDIFF) 786 787 DBG_ASSERT(!nDif,"Diff ohne Used"); 788 return 0; 789 } 790 791 sal_uInt16 ScDocument::ColDifferences( SCCOL nThisCol, SCTAB nThisTab, 792 ScDocument& rOtherDoc, SCCOL nOtherCol, SCTAB nOtherTab, 793 SCROW nMaxRow, SCCOLROW* pOtherRows ) 794 { 795 //! optimieren mit Iterator oder so 796 797 sal_uLong nDif = 0; 798 sal_uLong nUsed = 0; 799 for (SCROW nThisRow=0; nThisRow<=nMaxRow; nThisRow++) 800 { 801 SCROW nOtherRow; 802 if ( pOtherRows ) 803 nOtherRow = pOtherRows[nThisRow]; 804 else 805 nOtherRow = nThisRow; 806 807 if (ValidRow(nOtherRow)) // nur Zeilen vergleichen, die in beiden Dateien sind 808 { 809 const ScBaseCell* pThisCell = GetCell( ScAddress( nThisCol, nThisRow, nThisTab ) ); 810 const ScBaseCell* pOtherCell = rOtherDoc.GetCell( ScAddress( nOtherCol, nOtherRow, nOtherTab ) ); 811 if (!ScBaseCell::CellEqual( pThisCell, pOtherCell )) 812 { 813 if ( pThisCell && pOtherCell ) 814 nDif += 3; 815 else 816 nDif += 4; // Inhalt <-> leer zaehlt mehr 817 } 818 819 if ( ( pThisCell && pThisCell->GetCellType()!=CELLTYPE_NOTE ) || 820 ( pOtherCell && pOtherCell->GetCellType()!=CELLTYPE_NOTE ) ) 821 ++nUsed; 822 } 823 } 824 825 if (nUsed > 0) 826 return static_cast<sal_uInt16>((nDif*64)/nUsed); // max.256 827 828 DBG_ASSERT(!nDif,"Diff ohne Used"); 829 return 0; 830 } 831 832 void ScDocument::FindOrder( SCCOLROW* pOtherRows, SCCOLROW nThisEndRow, SCCOLROW nOtherEndRow, 833 sal_Bool bColumns, ScDocument& rOtherDoc, SCTAB nThisTab, SCTAB nOtherTab, 834 SCCOLROW nEndCol, SCCOLROW* pTranslate, ScProgress* pProgress, sal_uLong nProAdd ) 835 { 836 // bColumns=sal_True: Zeilen sind Spalten und umgekehrt 837 838 SCCOLROW nMaxCont; // wieviel weiter 839 SCCOLROW nMinGood; // was ist ein Treffer (incl.) 840 if ( bColumns ) 841 { 842 nMaxCont = SC_DOCCOMP_COLUMNS; // 10 Spalten 843 nMinGood = SC_DOCCOMP_MINGOOD; 844 //! Extra Durchgang mit nMinGood = 0 ???? 845 } 846 else 847 { 848 nMaxCont = SC_DOCCOMP_ROWS; // 100 Zeilen 849 nMinGood = SC_DOCCOMP_MINGOOD; 850 } 851 sal_Bool bUseTotal = bColumns && !pTranslate; // nur beim ersten Durchgang 852 853 854 SCCOLROW nOtherRow = 0; 855 sal_uInt16 nComp; 856 SCCOLROW nThisRow; 857 sal_Bool bTotal = sal_False; // ueber verschiedene nThisRow beibehalten 858 SCCOLROW nUnknown = 0; 859 for (nThisRow = 0; nThisRow <= nThisEndRow; nThisRow++) 860 { 861 SCCOLROW nTempOther = nOtherRow; 862 sal_Bool bFound = sal_False; 863 sal_uInt16 nBest = SC_DOCCOMP_MAXDIFF; 864 SCCOLROW nMax = Min( nOtherEndRow, static_cast<SCCOLROW>(( nTempOther + nMaxCont + nUnknown )) ); 865 for (SCCOLROW i=nTempOther; i<=nMax && nBest>0; i++) // bei 0 abbrechen 866 { 867 if (bColumns) 868 nComp = ColDifferences( static_cast<SCCOL>(nThisRow), nThisTab, rOtherDoc, static_cast<SCCOL>(i), nOtherTab, nEndCol, pTranslate ); 869 else 870 nComp = RowDifferences( nThisRow, nThisTab, rOtherDoc, i, nOtherTab, static_cast<SCCOL>(nEndCol), pTranslate ); 871 if ( nComp < nBest && ( nComp <= nMinGood || bTotal ) ) 872 { 873 nTempOther = i; 874 nBest = nComp; 875 bFound = sal_True; 876 } 877 if ( nComp < SC_DOCCOMP_MAXDIFF || bFound ) 878 bTotal = sal_False; 879 else if ( i == nTempOther && bUseTotal ) 880 bTotal = sal_True; // nur ganz oben 881 } 882 if ( bFound ) 883 { 884 pOtherRows[nThisRow] = nTempOther; 885 nOtherRow = nTempOther + 1; 886 nUnknown = 0; 887 } 888 else 889 { 890 pOtherRows[nThisRow] = SCROW_MAX; 891 ++nUnknown; 892 } 893 894 if (pProgress) 895 pProgress->SetStateOnPercent(nProAdd+static_cast<sal_uLong>(nThisRow)); 896 } 897 898 // Bloecke ohne Uebereinstimmung ausfuellen 899 900 SCROW nFillStart = 0; 901 SCROW nFillPos = 0; 902 sal_Bool bInFill = sal_False; 903 for (nThisRow = 0; nThisRow <= nThisEndRow+1; nThisRow++) 904 { 905 SCROW nThisOther = ( nThisRow <= nThisEndRow ) ? pOtherRows[nThisRow] : (nOtherEndRow+1); 906 if ( ValidRow(nThisOther) ) 907 { 908 if ( bInFill ) 909 { 910 if ( nThisOther > nFillStart ) // ist was zu verteilen da? 911 { 912 SCROW nDiff1 = nThisOther - nFillStart; 913 SCROW nDiff2 = nThisRow - nFillPos; 914 SCROW nMinDiff = Min(nDiff1, nDiff2); 915 for (SCROW i=0; i<nMinDiff; i++) 916 pOtherRows[nFillPos+i] = nFillStart+i; 917 } 918 919 bInFill = sal_False; 920 } 921 nFillStart = nThisOther + 1; 922 nFillPos = nThisRow + 1; 923 } 924 else 925 bInFill = sal_True; 926 } 927 } 928 929 void ScDocument::CompareDocument( ScDocument& rOtherDoc ) 930 { 931 if (!pChangeTrack) 932 return; 933 934 SCTAB nThisCount = GetTableCount(); 935 SCTAB nOtherCount = rOtherDoc.GetTableCount(); 936 SCTAB* pOtherTabs = new SCTAB[nThisCount]; 937 SCTAB nThisTab; 938 939 // Tabellen mit gleichen Namen vergleichen 940 String aThisName; 941 String aOtherName; 942 for (nThisTab=0; nThisTab<nThisCount; nThisTab++) 943 { 944 SCTAB nOtherTab = SCTAB_MAX; 945 if (!IsScenario(nThisTab)) // Szenarien weglassen 946 { 947 GetName( nThisTab, aThisName ); 948 for (SCTAB nTemp=0; nTemp<nOtherCount && nOtherTab>MAXTAB; nTemp++) 949 if (!rOtherDoc.IsScenario(nTemp)) 950 { 951 rOtherDoc.GetName( nTemp, aOtherName ); 952 if ( aThisName == aOtherName ) 953 nOtherTab = nTemp; 954 } 955 } 956 pOtherTabs[nThisTab] = nOtherTab; 957 } 958 // auffuellen, damit einzeln umbenannte Tabellen nicht wegfallen 959 SCTAB nFillStart = 0; 960 SCTAB nFillPos = 0; 961 sal_Bool bInFill = sal_False; 962 for (nThisTab = 0; nThisTab <= nThisCount; nThisTab++) 963 { 964 SCTAB nThisOther = ( nThisTab < nThisCount ) ? pOtherTabs[nThisTab] : nOtherCount; 965 if ( ValidTab(nThisOther) ) 966 { 967 if ( bInFill ) 968 { 969 if ( nThisOther > nFillStart ) // ist was zu verteilen da? 970 { 971 SCTAB nDiff1 = nThisOther - nFillStart; 972 SCTAB nDiff2 = nThisTab - nFillPos; 973 SCTAB nMinDiff = Min(nDiff1, nDiff2); 974 for (SCTAB i=0; i<nMinDiff; i++) 975 if ( !IsScenario(nFillPos+i) && !rOtherDoc.IsScenario(nFillStart+i) ) 976 pOtherTabs[nFillPos+i] = nFillStart+i; 977 } 978 979 bInFill = sal_False; 980 } 981 nFillStart = nThisOther + 1; 982 nFillPos = nThisTab + 1; 983 } 984 else 985 bInFill = sal_True; 986 } 987 988 // 989 // Tabellen in der gefundenen Reihenfolge vergleichen 990 // 991 992 for (nThisTab=0; nThisTab<nThisCount; nThisTab++) 993 { 994 SCTAB nOtherTab = pOtherTabs[nThisTab]; 995 if ( ValidTab(nOtherTab) ) 996 { 997 SCCOL nThisEndCol = 0; 998 SCROW nThisEndRow = 0; 999 SCCOL nOtherEndCol = 0; 1000 SCROW nOtherEndRow = 0; 1001 GetCellArea( nThisTab, nThisEndCol, nThisEndRow ); 1002 rOtherDoc.GetCellArea( nOtherTab, nOtherEndCol, nOtherEndRow ); 1003 SCCOL nEndCol = Max(nThisEndCol, nOtherEndCol); 1004 SCROW nEndRow = Max(nThisEndRow, nOtherEndRow); 1005 SCCOL nThisCol; 1006 SCROW nThisRow; 1007 sal_uLong n1,n2; // fuer AppendDeleteRange 1008 1009 //! ein Progress ueber alle Tabellen ??? 1010 String aTabName; 1011 GetName( nThisTab, aTabName ); 1012 String aTemplate = ScGlobal::GetRscString(STR_PROGRESS_COMPARING); 1013 String aProText = aTemplate.GetToken( 0, '#' ); 1014 aProText += aTabName; 1015 aProText += aTemplate.GetToken( 1, '#' ); 1016 ScProgress aProgress( GetDocumentShell(), 1017 aProText, 3*nThisEndRow ); // 2x FindOrder, 1x hier 1018 long nProgressStart = 2*nThisEndRow; // start fuer hier 1019 1020 SCCOLROW* pTempRows = new SCCOLROW[nThisEndRow+1]; 1021 SCCOLROW* pOtherRows = new SCCOLROW[nThisEndRow+1]; 1022 SCCOLROW* pOtherCols = new SCCOLROW[nThisEndCol+1]; 1023 1024 // eingefuegte/geloeschte Spalten/Zeilen finden: 1025 // Zwei Versuche: 1026 // 1) Original Zeilen vergleichen (pTempRows) 1027 // 2) Original Spalten vergleichen (pOtherCols) 1028 // mit dieser Spaltenreihenfolge Zeilen vergleichen (pOtherRows) 1029 1030 //! Spalten vergleichen zweimal mit unterschiedlichem nMinGood ??? 1031 1032 // 1 1033 FindOrder( pTempRows, nThisEndRow, nOtherEndRow, sal_False, 1034 rOtherDoc, nThisTab, nOtherTab, nEndCol, NULL, &aProgress, 0 ); 1035 // 2 1036 FindOrder( pOtherCols, nThisEndCol, nOtherEndCol, sal_True, 1037 rOtherDoc, nThisTab, nOtherTab, nEndRow, NULL, NULL, 0 ); 1038 FindOrder( pOtherRows, nThisEndRow, nOtherEndRow, sal_False, 1039 rOtherDoc, nThisTab, nOtherTab, nThisEndCol, 1040 pOtherCols, &aProgress, nThisEndRow ); 1041 1042 sal_uLong nMatch1 = 0; // pTempRows, keine Spalten 1043 for (nThisRow = 0; nThisRow<=nThisEndRow; nThisRow++) 1044 if (ValidRow(pTempRows[nThisRow])) 1045 nMatch1 += SC_DOCCOMP_MAXDIFF - 1046 RowDifferences( nThisRow, nThisTab, rOtherDoc, pTempRows[nThisRow], 1047 nOtherTab, nEndCol, NULL ); 1048 1049 sal_uLong nMatch2 = 0; // pOtherRows, pOtherCols 1050 for (nThisRow = 0; nThisRow<=nThisEndRow; nThisRow++) 1051 if (ValidRow(pOtherRows[nThisRow])) 1052 nMatch2 += SC_DOCCOMP_MAXDIFF - 1053 RowDifferences( nThisRow, nThisTab, rOtherDoc, pOtherRows[nThisRow], 1054 nOtherTab, nThisEndCol, pOtherCols ); 1055 1056 if ( nMatch1 >= nMatch2 ) // ohne Spalten ? 1057 { 1058 // Spalten zuruecksetzen 1059 for (nThisCol = 0; nThisCol<=nThisEndCol; nThisCol++) 1060 pOtherCols[nThisCol] = nThisCol; 1061 1062 // Zeilenarrays vertauschen (geloescht werden sowieso beide) 1063 SCCOLROW* pSwap = pTempRows; 1064 pTempRows = pOtherRows; 1065 pOtherRows = pSwap; 1066 } 1067 else 1068 { 1069 // bleibt bei pOtherCols, pOtherRows 1070 } 1071 1072 1073 // Change-Actions erzeugen 1074 // 1) Spalten von rechts 1075 // 2) Zeilen von unten 1076 // 3) einzelne Zellen in normaler Reihenfolge 1077 1078 // Actions fuer eingefuegte/geloeschte Spalten 1079 1080 SCCOL nLastOtherCol = static_cast<SCCOL>(nOtherEndCol + 1); 1081 // nThisEndCol ... 0 1082 for ( nThisCol = nThisEndCol+1; nThisCol > 0; ) 1083 { 1084 --nThisCol; 1085 SCCOL nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]); 1086 if ( ValidCol(nOtherCol) && nOtherCol+1 < nLastOtherCol ) 1087 { 1088 // Luecke -> geloescht 1089 ScRange aDelRange( nOtherCol+1, 0, nOtherTab, 1090 nLastOtherCol-1, MAXROW, nOtherTab ); 1091 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 ); 1092 } 1093 if ( nOtherCol > MAXCOL ) // eingefuegt 1094 { 1095 // zusammenfassen 1096 if ( nThisCol == nThisEndCol || ValidCol(static_cast<SCCOL>(pOtherCols[nThisCol+1])) ) 1097 { 1098 SCCOL nFirstNew = static_cast<SCCOL>(nThisCol); 1099 while ( nFirstNew > 0 && pOtherCols[nFirstNew-1] > MAXCOL ) 1100 --nFirstNew; 1101 SCCOL nDiff = nThisCol - nFirstNew; 1102 ScRange aRange( nLastOtherCol, 0, nOtherTab, 1103 nLastOtherCol+nDiff, MAXROW, nOtherTab ); 1104 pChangeTrack->AppendInsert( aRange ); 1105 } 1106 } 1107 else 1108 nLastOtherCol = nOtherCol; 1109 } 1110 if ( nLastOtherCol > 0 ) // ganz oben geloescht 1111 { 1112 ScRange aDelRange( 0, 0, nOtherTab, 1113 nLastOtherCol-1, MAXROW, nOtherTab ); 1114 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 ); 1115 } 1116 1117 // Actions fuer eingefuegte/geloeschte Zeilen 1118 1119 SCROW nLastOtherRow = nOtherEndRow + 1; 1120 // nThisEndRow ... 0 1121 for ( nThisRow = nThisEndRow+1; nThisRow > 0; ) 1122 { 1123 --nThisRow; 1124 SCROW nOtherRow = pOtherRows[nThisRow]; 1125 if ( ValidRow(nOtherRow) && nOtherRow+1 < nLastOtherRow ) 1126 { 1127 // Luecke -> geloescht 1128 ScRange aDelRange( 0, nOtherRow+1, nOtherTab, 1129 MAXCOL, nLastOtherRow-1, nOtherTab ); 1130 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 ); 1131 } 1132 if ( nOtherRow > MAXROW ) // eingefuegt 1133 { 1134 // zusammenfassen 1135 if ( nThisRow == nThisEndRow || ValidRow(pOtherRows[nThisRow+1]) ) 1136 { 1137 SCROW nFirstNew = nThisRow; 1138 while ( nFirstNew > 0 && pOtherRows[nFirstNew-1] > MAXROW ) 1139 --nFirstNew; 1140 SCROW nDiff = nThisRow - nFirstNew; 1141 ScRange aRange( 0, nLastOtherRow, nOtherTab, 1142 MAXCOL, nLastOtherRow+nDiff, nOtherTab ); 1143 pChangeTrack->AppendInsert( aRange ); 1144 } 1145 } 1146 else 1147 nLastOtherRow = nOtherRow; 1148 } 1149 if ( nLastOtherRow > 0 ) // ganz oben geloescht 1150 { 1151 ScRange aDelRange( 0, 0, nOtherTab, 1152 MAXCOL, nLastOtherRow-1, nOtherTab ); 1153 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 ); 1154 } 1155 1156 // Zeilen durchgehen um einzelne Zellen zu finden 1157 1158 for (nThisRow = 0; nThisRow <= nThisEndRow; nThisRow++) 1159 { 1160 SCROW nOtherRow = pOtherRows[nThisRow]; 1161 for (nThisCol = 0; nThisCol <= nThisEndCol; nThisCol++) 1162 { 1163 SCCOL nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]); 1164 ScAddress aThisPos( nThisCol, nThisRow, nThisTab ); 1165 const ScBaseCell* pThisCell = GetCell( aThisPos ); 1166 const ScBaseCell* pOtherCell = NULL; 1167 if ( ValidCol(nOtherCol) && ValidRow(nOtherRow) ) 1168 { 1169 ScAddress aOtherPos( nOtherCol, nOtherRow, nOtherTab ); 1170 pOtherCell = rOtherDoc.GetCell( aOtherPos ); 1171 } 1172 if ( !ScBaseCell::CellEqual( pThisCell, pOtherCell ) ) 1173 { 1174 ScRange aRange( aThisPos ); 1175 ScChangeActionContent* pAction = new ScChangeActionContent( aRange ); 1176 pAction->SetOldValue( pOtherCell, &rOtherDoc, this ); 1177 pAction->SetNewValue( pThisCell, this ); 1178 pChangeTrack->Append( pAction ); 1179 } 1180 } 1181 aProgress.SetStateOnPercent(nProgressStart+nThisRow); 1182 } 1183 1184 delete[] pOtherCols; 1185 delete[] pOtherRows; 1186 delete[] pTempRows; 1187 } 1188 } 1189 1190 //! Inhalt von eingefuegten / geloeschten Tabellen ??? 1191 //! Aktionen fuer eingefuegte / geloeschte Tabellen ??? 1192 1193 delete[] pOtherTabs; 1194 } 1195 1196 1197 1198 1199 1200