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