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 void ScDocument::GetLastAttrCell( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow ) const 421 { 422 if ( ValidTab( nTab ) && pTab[nTab] ) 423 { 424 pTab[nTab]->GetLastAttrCell( rEndCol, rEndRow ); 425 } 426 } 427 428 sal_Int32 ScDocument::GetMaxStringLen( SCTAB nTab, SCCOL nCol, 429 SCROW nRowStart, SCROW nRowEnd, CharSet eCharSet ) const 430 { 431 if (ValidTab(nTab) && pTab[nTab]) 432 return pTab[nTab]->GetMaxStringLen( nCol, nRowStart, nRowEnd, eCharSet ); 433 else 434 return 0; 435 } 436 437 xub_StrLen ScDocument::GetMaxNumberStringLen( sal_uInt16& nPrecision, SCTAB nTab, 438 SCCOL nCol, 439 SCROW nRowStart, SCROW nRowEnd ) const 440 { 441 if (ValidTab(nTab) && pTab[nTab]) 442 return pTab[nTab]->GetMaxNumberStringLen( nPrecision, nCol, 443 nRowStart, nRowEnd ); 444 else 445 return 0; 446 } 447 448 sal_Bool ScDocument::GetSelectionFunction( ScSubTotalFunc eFunc, 449 const ScAddress& rCursor, const ScMarkData& rMark, 450 double& rResult ) 451 { 452 ScFunctionData aData(eFunc); 453 454 ScRange aSingle( rCursor ); 455 if ( rMark.IsMarked() ) 456 rMark.GetMarkArea(aSingle); 457 458 SCCOL nStartCol = aSingle.aStart.Col(); 459 SCROW nStartRow = aSingle.aStart.Row(); 460 SCCOL nEndCol = aSingle.aEnd.Col(); 461 SCROW nEndRow = aSingle.aEnd.Row(); 462 463 for (SCTAB nTab=0; nTab<=MAXTAB && !aData.bError; nTab++) 464 if (pTab[nTab] && rMark.GetTableSelect(nTab)) 465 pTab[nTab]->UpdateSelectionFunction( aData, 466 nStartCol, nStartRow, nEndCol, nEndRow, rMark ); 467 468 //! rMark an UpdateSelectionFunction uebergeben !!!!! 469 470 if (!aData.bError) 471 switch (eFunc) 472 { 473 case SUBTOTAL_FUNC_SUM: 474 rResult = aData.nVal; 475 break; 476 case SUBTOTAL_FUNC_CNT: 477 case SUBTOTAL_FUNC_CNT2: 478 rResult = aData.nCount; 479 break; 480 case SUBTOTAL_FUNC_AVE: 481 if (aData.nCount) 482 rResult = aData.nVal / (double) aData.nCount; 483 else 484 aData.bError = sal_True; 485 break; 486 case SUBTOTAL_FUNC_MAX: 487 case SUBTOTAL_FUNC_MIN: 488 if (aData.nCount) 489 rResult = aData.nVal; 490 else 491 aData.bError = sal_True; 492 break; 493 default: 494 { 495 // added to avoid warnings 496 } 497 } 498 499 if (aData.bError) 500 rResult = 0.0; 501 502 return !aData.bError; 503 } 504 505 double ScDocument::RoundValueAsShown( double fVal, sal_uLong nFormat ) 506 { 507 short nType; 508 if ( (nType = GetFormatTable()->GetType( nFormat )) != NUMBERFORMAT_DATE 509 && nType != NUMBERFORMAT_TIME && nType != NUMBERFORMAT_DATETIME ) 510 { 511 short nPrecision; 512 if ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0) 513 { 514 nPrecision = (short)GetFormatTable()->GetFormatPrecision( nFormat ); 515 switch ( nType ) 516 { 517 case NUMBERFORMAT_PERCENT: // 0,41% == 0,0041 518 nPrecision += 2; 519 break; 520 case NUMBERFORMAT_SCIENTIFIC: // 1,23e-3 == 0,00123 521 { 522 if ( fVal > 0.0 ) 523 nPrecision = sal::static_int_cast<short>( nPrecision - (short)floor( log10( fVal ) ) ); 524 else if ( fVal < 0.0 ) 525 nPrecision = sal::static_int_cast<short>( nPrecision - (short)floor( log10( -fVal ) ) ); 526 break; 527 } 528 } 529 } 530 else 531 { 532 nPrecision = (short)GetDocOptions().GetStdPrecision(); 533 // #i115512# no rounding for automatic decimals 534 if (nPrecision == static_cast<short>(SvNumberFormatter::UNLIMITED_PRECISION)) 535 return fVal; 536 } 537 double fRound = ::rtl::math::round( fVal, nPrecision ); 538 if ( ::rtl::math::approxEqual( fVal, fRound ) ) 539 return fVal; // durch Rundung hoechstens Fehler 540 else 541 return fRound; 542 } 543 else 544 return fVal; 545 } 546 547 // 548 // bedingte Formate und Gueltigkeitsbereiche 549 // 550 551 sal_uLong ScDocument::AddCondFormat( const ScConditionalFormat& rNew ) 552 { 553 if (rNew.IsEmpty()) 554 return 0; // leer ist immer 0 555 556 if (!pCondFormList) 557 pCondFormList = new ScConditionalFormatList; 558 559 sal_uLong nMax = 0; 560 sal_uInt16 nCount = pCondFormList->Count(); 561 for (sal_uInt16 i=0; i<nCount; i++) 562 { 563 const ScConditionalFormat* pForm = (*pCondFormList)[i]; 564 sal_uLong nKey = pForm->GetKey(); 565 if ( pForm->EqualEntries( rNew ) ) 566 return nKey; 567 if ( nKey > nMax ) 568 nMax = nKey; 569 } 570 571 // Der Aufruf kann aus ScPatternAttr::PutInPool kommen, darum Clone (echte Kopie) 572 573 sal_uLong nNewKey = nMax + 1; 574 ScConditionalFormat* pInsert = rNew.Clone(this); 575 pInsert->SetKey( nNewKey ); 576 pCondFormList->InsertNew( pInsert ); 577 return nNewKey; 578 } 579 580 sal_uLong ScDocument::AddValidationEntry( const ScValidationData& rNew ) 581 { 582 if (rNew.IsEmpty()) 583 return 0; // leer ist immer 0 584 585 if (!pValidationList) 586 pValidationList = new ScValidationDataList; 587 588 sal_uLong nMax = 0; 589 sal_uInt16 nCount = pValidationList->Count(); 590 for (sal_uInt16 i=0; i<nCount; i++) 591 { 592 const ScValidationData* pData = (*pValidationList)[i]; 593 sal_uLong nKey = pData->GetKey(); 594 if ( pData->EqualEntries( rNew ) ) 595 return nKey; 596 if ( nKey > nMax ) 597 nMax = nKey; 598 } 599 600 // Der Aufruf kann aus ScPatternAttr::PutInPool kommen, darum Clone (echte Kopie) 601 602 sal_uLong nNewKey = nMax + 1; 603 ScValidationData* pInsert = rNew.Clone(this); 604 pInsert->SetKey( nNewKey ); 605 pValidationList->InsertNew( pInsert ); 606 return nNewKey; 607 } 608 609 const SfxPoolItem* ScDocument::GetEffItem( 610 SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt16 nWhich ) const 611 { 612 const ScPatternAttr* pPattern = GetPattern( nCol, nRow, nTab ); 613 if ( pPattern ) 614 { 615 const SfxItemSet& rSet = pPattern->GetItemSet(); 616 const SfxPoolItem* pItem; 617 if ( rSet.GetItemState( ATTR_CONDITIONAL, sal_True, &pItem ) == SFX_ITEM_SET ) 618 { 619 sal_uLong nIndex = ((const SfxUInt32Item*)pItem)->GetValue(); 620 if (nIndex && pCondFormList) 621 { 622 const ScConditionalFormat* pForm = pCondFormList->GetFormat( nIndex ); 623 if ( pForm ) 624 { 625 ScBaseCell* pCell = ((ScDocument*)this)->GetCell(ScAddress(nCol,nRow,nTab)); 626 String aStyle = pForm->GetCellStyle( pCell, ScAddress(nCol, nRow, nTab) ); 627 if (aStyle.Len()) 628 { 629 SfxStyleSheetBase* pStyleSheet = xPoolHelper->GetStylePool()->Find( 630 aStyle, SFX_STYLE_FAMILY_PARA ); 631 if ( pStyleSheet && pStyleSheet->GetItemSet().GetItemState( 632 nWhich, sal_True, &pItem ) == SFX_ITEM_SET ) 633 return pItem; 634 } 635 } 636 } 637 } 638 return &rSet.Get( nWhich ); 639 } 640 DBG_ERROR("kein Pattern"); 641 return NULL; 642 } 643 644 const SfxItemSet* ScDocument::GetCondResult( SCCOL nCol, SCROW nRow, SCTAB nTab ) const 645 { 646 const ScConditionalFormat* pForm = GetCondFormat( nCol, nRow, nTab ); 647 if ( pForm ) 648 { 649 ScBaseCell* pCell = ((ScDocument*)this)->GetCell(ScAddress(nCol,nRow,nTab)); 650 String aStyle = pForm->GetCellStyle( pCell, ScAddress(nCol, nRow, nTab) ); 651 if (aStyle.Len()) 652 { 653 SfxStyleSheetBase* pStyleSheet = xPoolHelper->GetStylePool()->Find( aStyle, SFX_STYLE_FAMILY_PARA ); 654 if ( pStyleSheet ) 655 return &pStyleSheet->GetItemSet(); 656 // if style is not there, treat like no condition 657 } 658 } 659 return NULL; 660 } 661 662 const ScConditionalFormat* ScDocument::GetCondFormat( 663 SCCOL nCol, SCROW nRow, SCTAB nTab ) const 664 { 665 sal_uLong nIndex = ((const SfxUInt32Item*)GetAttr(nCol,nRow,nTab,ATTR_CONDITIONAL))->GetValue(); 666 if (nIndex) 667 { 668 if (pCondFormList) 669 return pCondFormList->GetFormat( nIndex ); 670 else 671 { 672 DBG_ERROR("pCondFormList ist 0"); 673 } 674 } 675 676 return NULL; 677 } 678 679 const ScValidationData* ScDocument::GetValidationEntry( sal_uLong nIndex ) const 680 { 681 if ( pValidationList ) 682 return pValidationList->GetData( nIndex ); 683 else 684 return NULL; 685 } 686 687 void ScDocument::FindConditionalFormat( sal_uLong nKey, ScRangeList& rRanges ) 688 { 689 for (SCTAB i=0; i<=MAXTAB && pTab[i]; i++) 690 pTab[i]->FindConditionalFormat( nKey, rRanges ); 691 } 692 693 void ScDocument::FindConditionalFormat( sal_uLong nKey, ScRangeList& rRanges, SCTAB nTab ) 694 { 695 if(VALIDTAB(nTab) && pTab[nTab]) 696 pTab[nTab]->FindConditionalFormat( nKey, rRanges ); 697 } 698 699 void ScDocument::ConditionalChanged( sal_uLong nKey ) 700 { 701 if ( nKey && pCondFormList && !bIsClip && !bIsUndo ) // nKey==0 -> noop 702 { 703 ScConditionalFormat* pForm = pCondFormList->GetFormat( nKey ); 704 if (pForm) 705 pForm->InvalidateArea(); 706 } 707 } 708 709 void ScDocument::SetCondFormList(ScConditionalFormatList* pNew) 710 { 711 if (pCondFormList) 712 { 713 pCondFormList->DeleteAndDestroy( 0, pCondFormList->Count() ); 714 delete pCondFormList; 715 } 716 717 pCondFormList = pNew; 718 } 719 720 //------------------------------------------------------------------------ 721 722 sal_Bool ScDocument::HasDetectiveOperations() const 723 { 724 return pDetOpList && pDetOpList->Count(); 725 } 726 727 void ScDocument::AddDetectiveOperation( const ScDetOpData& rData ) 728 { 729 if (!pDetOpList) 730 pDetOpList = new ScDetOpList; 731 732 pDetOpList->Append( new ScDetOpData( rData ) ); 733 } 734 735 void ScDocument::ClearDetectiveOperations() 736 { 737 delete pDetOpList; // loescht auch die Eintraege 738 pDetOpList = NULL; 739 } 740 741 void ScDocument::SetDetOpList(ScDetOpList* pNew) 742 { 743 delete pDetOpList; // loescht auch die Eintraege 744 pDetOpList = pNew; 745 } 746 747 //------------------------------------------------------------------------ 748 // 749 // Vergleich von Dokumenten 750 // 751 //------------------------------------------------------------------------ 752 753 // Pfriemel-Faktoren 754 #define SC_DOCCOMP_MAXDIFF 256 755 #define SC_DOCCOMP_MINGOOD 128 756 #define SC_DOCCOMP_COLUMNS 10 757 #define SC_DOCCOMP_ROWS 100 758 759 760 sal_uInt16 ScDocument::RowDifferences( SCROW nThisRow, SCTAB nThisTab, 761 ScDocument& rOtherDoc, SCROW nOtherRow, SCTAB nOtherTab, 762 SCCOL nMaxCol, SCCOLROW* pOtherCols ) 763 { 764 sal_uLong nDif = 0; 765 sal_uLong nUsed = 0; 766 for (SCCOL nThisCol=0; nThisCol<=nMaxCol; nThisCol++) 767 { 768 SCCOL nOtherCol; 769 if ( pOtherCols ) 770 nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]); 771 else 772 nOtherCol = nThisCol; 773 774 if (ValidCol(nOtherCol)) // nur Spalten vergleichen, die in beiden Dateien sind 775 { 776 const ScBaseCell* pThisCell = GetCell( ScAddress( nThisCol, nThisRow, nThisTab ) ); 777 const ScBaseCell* pOtherCell = rOtherDoc.GetCell( ScAddress( nOtherCol, nOtherRow, nOtherTab ) ); 778 if (!ScBaseCell::CellEqual( pThisCell, pOtherCell )) 779 { 780 if ( pThisCell && pOtherCell ) 781 nDif += 3; 782 else 783 nDif += 4; // Inhalt <-> leer zaehlt mehr 784 } 785 786 if ( ( pThisCell && pThisCell->GetCellType()!=CELLTYPE_NOTE ) || 787 ( pOtherCell && pOtherCell->GetCellType()!=CELLTYPE_NOTE ) ) 788 ++nUsed; 789 } 790 } 791 792 if (nUsed > 0) 793 return static_cast<sal_uInt16>((nDif*64)/nUsed); // max.256 (SC_DOCCOMP_MAXDIFF) 794 795 DBG_ASSERT(!nDif,"Diff ohne Used"); 796 return 0; 797 } 798 799 sal_uInt16 ScDocument::ColDifferences( SCCOL nThisCol, SCTAB nThisTab, 800 ScDocument& rOtherDoc, SCCOL nOtherCol, SCTAB nOtherTab, 801 SCROW nMaxRow, SCCOLROW* pOtherRows ) 802 { 803 //! optimieren mit Iterator oder so 804 805 sal_uLong nDif = 0; 806 sal_uLong nUsed = 0; 807 for (SCROW nThisRow=0; nThisRow<=nMaxRow; nThisRow++) 808 { 809 SCROW nOtherRow; 810 if ( pOtherRows ) 811 nOtherRow = pOtherRows[nThisRow]; 812 else 813 nOtherRow = nThisRow; 814 815 if (ValidRow(nOtherRow)) // nur Zeilen vergleichen, die in beiden Dateien sind 816 { 817 const ScBaseCell* pThisCell = GetCell( ScAddress( nThisCol, nThisRow, nThisTab ) ); 818 const ScBaseCell* pOtherCell = rOtherDoc.GetCell( ScAddress( nOtherCol, nOtherRow, nOtherTab ) ); 819 if (!ScBaseCell::CellEqual( pThisCell, pOtherCell )) 820 { 821 if ( pThisCell && pOtherCell ) 822 nDif += 3; 823 else 824 nDif += 4; // Inhalt <-> leer zaehlt mehr 825 } 826 827 if ( ( pThisCell && pThisCell->GetCellType()!=CELLTYPE_NOTE ) || 828 ( pOtherCell && pOtherCell->GetCellType()!=CELLTYPE_NOTE ) ) 829 ++nUsed; 830 } 831 } 832 833 if (nUsed > 0) 834 return static_cast<sal_uInt16>((nDif*64)/nUsed); // max.256 835 836 DBG_ASSERT(!nDif,"Diff ohne Used"); 837 return 0; 838 } 839 840 void ScDocument::FindOrder( SCCOLROW* pOtherRows, SCCOLROW nThisEndRow, SCCOLROW nOtherEndRow, 841 sal_Bool bColumns, ScDocument& rOtherDoc, SCTAB nThisTab, SCTAB nOtherTab, 842 SCCOLROW nEndCol, SCCOLROW* pTranslate, ScProgress* pProgress, sal_uLong nProAdd ) 843 { 844 // bColumns=sal_True: Zeilen sind Spalten und umgekehrt 845 846 SCCOLROW nMaxCont; // wieviel weiter 847 SCCOLROW nMinGood; // was ist ein Treffer (incl.) 848 if ( bColumns ) 849 { 850 nMaxCont = SC_DOCCOMP_COLUMNS; // 10 Spalten 851 nMinGood = SC_DOCCOMP_MINGOOD; 852 //! Extra Durchgang mit nMinGood = 0 ???? 853 } 854 else 855 { 856 nMaxCont = SC_DOCCOMP_ROWS; // 100 Zeilen 857 nMinGood = SC_DOCCOMP_MINGOOD; 858 } 859 sal_Bool bUseTotal = bColumns && !pTranslate; // nur beim ersten Durchgang 860 861 862 SCCOLROW nOtherRow = 0; 863 sal_uInt16 nComp; 864 SCCOLROW nThisRow; 865 sal_Bool bTotal = sal_False; // ueber verschiedene nThisRow beibehalten 866 SCCOLROW nUnknown = 0; 867 for (nThisRow = 0; nThisRow <= nThisEndRow; nThisRow++) 868 { 869 SCCOLROW nTempOther = nOtherRow; 870 sal_Bool bFound = sal_False; 871 sal_uInt16 nBest = SC_DOCCOMP_MAXDIFF; 872 SCCOLROW nMax = Min( nOtherEndRow, static_cast<SCCOLROW>(( nTempOther + nMaxCont + nUnknown )) ); 873 for (SCCOLROW i=nTempOther; i<=nMax && nBest>0; i++) // bei 0 abbrechen 874 { 875 if (bColumns) 876 nComp = ColDifferences( static_cast<SCCOL>(nThisRow), nThisTab, rOtherDoc, static_cast<SCCOL>(i), nOtherTab, nEndCol, pTranslate ); 877 else 878 nComp = RowDifferences( nThisRow, nThisTab, rOtherDoc, i, nOtherTab, static_cast<SCCOL>(nEndCol), pTranslate ); 879 if ( nComp < nBest && ( nComp <= nMinGood || bTotal ) ) 880 { 881 nTempOther = i; 882 nBest = nComp; 883 bFound = sal_True; 884 } 885 if ( nComp < SC_DOCCOMP_MAXDIFF || bFound ) 886 bTotal = sal_False; 887 else if ( i == nTempOther && bUseTotal ) 888 bTotal = sal_True; // nur ganz oben 889 } 890 if ( bFound ) 891 { 892 pOtherRows[nThisRow] = nTempOther; 893 nOtherRow = nTempOther + 1; 894 nUnknown = 0; 895 } 896 else 897 { 898 pOtherRows[nThisRow] = SCROW_MAX; 899 ++nUnknown; 900 } 901 902 if (pProgress) 903 pProgress->SetStateOnPercent(nProAdd+static_cast<sal_uLong>(nThisRow)); 904 } 905 906 // Bloecke ohne Uebereinstimmung ausfuellen 907 908 SCROW nFillStart = 0; 909 SCROW nFillPos = 0; 910 sal_Bool bInFill = sal_False; 911 for (nThisRow = 0; nThisRow <= nThisEndRow+1; nThisRow++) 912 { 913 SCROW nThisOther = ( nThisRow <= nThisEndRow ) ? pOtherRows[nThisRow] : (nOtherEndRow+1); 914 if ( ValidRow(nThisOther) ) 915 { 916 if ( bInFill ) 917 { 918 if ( nThisOther > nFillStart ) // ist was zu verteilen da? 919 { 920 SCROW nDiff1 = nThisOther - nFillStart; 921 SCROW nDiff2 = nThisRow - nFillPos; 922 SCROW nMinDiff = Min(nDiff1, nDiff2); 923 for (SCROW i=0; i<nMinDiff; i++) 924 pOtherRows[nFillPos+i] = nFillStart+i; 925 } 926 927 bInFill = sal_False; 928 } 929 nFillStart = nThisOther + 1; 930 nFillPos = nThisRow + 1; 931 } 932 else 933 bInFill = sal_True; 934 } 935 } 936 937 void ScDocument::CompareDocument( ScDocument& rOtherDoc ) 938 { 939 if (!pChangeTrack) 940 return; 941 942 SCTAB nThisCount = GetTableCount(); 943 SCTAB nOtherCount = rOtherDoc.GetTableCount(); 944 SCTAB* pOtherTabs = new SCTAB[nThisCount]; 945 SCTAB nThisTab; 946 947 // Tabellen mit gleichen Namen vergleichen 948 String aThisName; 949 String aOtherName; 950 for (nThisTab=0; nThisTab<nThisCount; nThisTab++) 951 { 952 SCTAB nOtherTab = SCTAB_MAX; 953 if (!IsScenario(nThisTab)) // Szenarien weglassen 954 { 955 GetName( nThisTab, aThisName ); 956 for (SCTAB nTemp=0; nTemp<nOtherCount && nOtherTab>MAXTAB; nTemp++) 957 if (!rOtherDoc.IsScenario(nTemp)) 958 { 959 rOtherDoc.GetName( nTemp, aOtherName ); 960 if ( aThisName == aOtherName ) 961 nOtherTab = nTemp; 962 } 963 } 964 pOtherTabs[nThisTab] = nOtherTab; 965 } 966 // auffuellen, damit einzeln umbenannte Tabellen nicht wegfallen 967 SCTAB nFillStart = 0; 968 SCTAB nFillPos = 0; 969 sal_Bool bInFill = sal_False; 970 for (nThisTab = 0; nThisTab <= nThisCount; nThisTab++) 971 { 972 SCTAB nThisOther = ( nThisTab < nThisCount ) ? pOtherTabs[nThisTab] : nOtherCount; 973 if ( ValidTab(nThisOther) ) 974 { 975 if ( bInFill ) 976 { 977 if ( nThisOther > nFillStart ) // ist was zu verteilen da? 978 { 979 SCTAB nDiff1 = nThisOther - nFillStart; 980 SCTAB nDiff2 = nThisTab - nFillPos; 981 SCTAB nMinDiff = Min(nDiff1, nDiff2); 982 for (SCTAB i=0; i<nMinDiff; i++) 983 if ( !IsScenario(nFillPos+i) && !rOtherDoc.IsScenario(nFillStart+i) ) 984 pOtherTabs[nFillPos+i] = nFillStart+i; 985 } 986 987 bInFill = sal_False; 988 } 989 nFillStart = nThisOther + 1; 990 nFillPos = nThisTab + 1; 991 } 992 else 993 bInFill = sal_True; 994 } 995 996 // 997 // Tabellen in der gefundenen Reihenfolge vergleichen 998 // 999 1000 for (nThisTab=0; nThisTab<nThisCount; nThisTab++) 1001 { 1002 SCTAB nOtherTab = pOtherTabs[nThisTab]; 1003 if ( ValidTab(nOtherTab) ) 1004 { 1005 SCCOL nThisEndCol = 0; 1006 SCROW nThisEndRow = 0; 1007 SCCOL nOtherEndCol = 0; 1008 SCROW nOtherEndRow = 0; 1009 GetCellArea( nThisTab, nThisEndCol, nThisEndRow ); 1010 rOtherDoc.GetCellArea( nOtherTab, nOtherEndCol, nOtherEndRow ); 1011 SCCOL nEndCol = Max(nThisEndCol, nOtherEndCol); 1012 SCROW nEndRow = Max(nThisEndRow, nOtherEndRow); 1013 SCCOL nThisCol; 1014 SCROW nThisRow; 1015 sal_uLong n1,n2; // fuer AppendDeleteRange 1016 1017 //! ein Progress ueber alle Tabellen ??? 1018 String aTabName; 1019 GetName( nThisTab, aTabName ); 1020 String aTemplate = ScGlobal::GetRscString(STR_PROGRESS_COMPARING); 1021 String aProText = aTemplate.GetToken( 0, '#' ); 1022 aProText += aTabName; 1023 aProText += aTemplate.GetToken( 1, '#' ); 1024 ScProgress aProgress( GetDocumentShell(), 1025 aProText, 3*nThisEndRow ); // 2x FindOrder, 1x hier 1026 long nProgressStart = 2*nThisEndRow; // start fuer hier 1027 1028 SCCOLROW* pTempRows = new SCCOLROW[nThisEndRow+1]; 1029 SCCOLROW* pOtherRows = new SCCOLROW[nThisEndRow+1]; 1030 SCCOLROW* pOtherCols = new SCCOLROW[nThisEndCol+1]; 1031 1032 // eingefuegte/geloeschte Spalten/Zeilen finden: 1033 // Zwei Versuche: 1034 // 1) Original Zeilen vergleichen (pTempRows) 1035 // 2) Original Spalten vergleichen (pOtherCols) 1036 // mit dieser Spaltenreihenfolge Zeilen vergleichen (pOtherRows) 1037 1038 //! Spalten vergleichen zweimal mit unterschiedlichem nMinGood ??? 1039 1040 // 1 1041 FindOrder( pTempRows, nThisEndRow, nOtherEndRow, sal_False, 1042 rOtherDoc, nThisTab, nOtherTab, nEndCol, NULL, &aProgress, 0 ); 1043 // 2 1044 FindOrder( pOtherCols, nThisEndCol, nOtherEndCol, sal_True, 1045 rOtherDoc, nThisTab, nOtherTab, nEndRow, NULL, NULL, 0 ); 1046 FindOrder( pOtherRows, nThisEndRow, nOtherEndRow, sal_False, 1047 rOtherDoc, nThisTab, nOtherTab, nThisEndCol, 1048 pOtherCols, &aProgress, nThisEndRow ); 1049 1050 sal_uLong nMatch1 = 0; // pTempRows, keine Spalten 1051 for (nThisRow = 0; nThisRow<=nThisEndRow; nThisRow++) 1052 if (ValidRow(pTempRows[nThisRow])) 1053 nMatch1 += SC_DOCCOMP_MAXDIFF - 1054 RowDifferences( nThisRow, nThisTab, rOtherDoc, pTempRows[nThisRow], 1055 nOtherTab, nEndCol, NULL ); 1056 1057 sal_uLong nMatch2 = 0; // pOtherRows, pOtherCols 1058 for (nThisRow = 0; nThisRow<=nThisEndRow; nThisRow++) 1059 if (ValidRow(pOtherRows[nThisRow])) 1060 nMatch2 += SC_DOCCOMP_MAXDIFF - 1061 RowDifferences( nThisRow, nThisTab, rOtherDoc, pOtherRows[nThisRow], 1062 nOtherTab, nThisEndCol, pOtherCols ); 1063 1064 if ( nMatch1 >= nMatch2 ) // ohne Spalten ? 1065 { 1066 // Spalten zuruecksetzen 1067 for (nThisCol = 0; nThisCol<=nThisEndCol; nThisCol++) 1068 pOtherCols[nThisCol] = nThisCol; 1069 1070 // Zeilenarrays vertauschen (geloescht werden sowieso beide) 1071 SCCOLROW* pSwap = pTempRows; 1072 pTempRows = pOtherRows; 1073 pOtherRows = pSwap; 1074 } 1075 else 1076 { 1077 // bleibt bei pOtherCols, pOtherRows 1078 } 1079 1080 1081 // Change-Actions erzeugen 1082 // 1) Spalten von rechts 1083 // 2) Zeilen von unten 1084 // 3) einzelne Zellen in normaler Reihenfolge 1085 1086 // Actions fuer eingefuegte/geloeschte Spalten 1087 1088 SCCOL nLastOtherCol = static_cast<SCCOL>(nOtherEndCol + 1); 1089 // nThisEndCol ... 0 1090 for ( nThisCol = nThisEndCol+1; nThisCol > 0; ) 1091 { 1092 --nThisCol; 1093 SCCOL nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]); 1094 if ( ValidCol(nOtherCol) && nOtherCol+1 < nLastOtherCol ) 1095 { 1096 // Luecke -> geloescht 1097 ScRange aDelRange( nOtherCol+1, 0, nOtherTab, 1098 nLastOtherCol-1, MAXROW, nOtherTab ); 1099 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 ); 1100 } 1101 if ( nOtherCol > MAXCOL ) // eingefuegt 1102 { 1103 // zusammenfassen 1104 if ( nThisCol == nThisEndCol || ValidCol(static_cast<SCCOL>(pOtherCols[nThisCol+1])) ) 1105 { 1106 SCCOL nFirstNew = static_cast<SCCOL>(nThisCol); 1107 while ( nFirstNew > 0 && pOtherCols[nFirstNew-1] > MAXCOL ) 1108 --nFirstNew; 1109 SCCOL nDiff = nThisCol - nFirstNew; 1110 ScRange aRange( nLastOtherCol, 0, nOtherTab, 1111 nLastOtherCol+nDiff, MAXROW, nOtherTab ); 1112 pChangeTrack->AppendInsert( aRange ); 1113 } 1114 } 1115 else 1116 nLastOtherCol = nOtherCol; 1117 } 1118 if ( nLastOtherCol > 0 ) // ganz oben geloescht 1119 { 1120 ScRange aDelRange( 0, 0, nOtherTab, 1121 nLastOtherCol-1, MAXROW, nOtherTab ); 1122 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 ); 1123 } 1124 1125 // Actions fuer eingefuegte/geloeschte Zeilen 1126 1127 SCROW nLastOtherRow = nOtherEndRow + 1; 1128 // nThisEndRow ... 0 1129 for ( nThisRow = nThisEndRow+1; nThisRow > 0; ) 1130 { 1131 --nThisRow; 1132 SCROW nOtherRow = pOtherRows[nThisRow]; 1133 if ( ValidRow(nOtherRow) && nOtherRow+1 < nLastOtherRow ) 1134 { 1135 // Luecke -> geloescht 1136 ScRange aDelRange( 0, nOtherRow+1, nOtherTab, 1137 MAXCOL, nLastOtherRow-1, nOtherTab ); 1138 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 ); 1139 } 1140 if ( nOtherRow > MAXROW ) // eingefuegt 1141 { 1142 // zusammenfassen 1143 if ( nThisRow == nThisEndRow || ValidRow(pOtherRows[nThisRow+1]) ) 1144 { 1145 SCROW nFirstNew = nThisRow; 1146 while ( nFirstNew > 0 && pOtherRows[nFirstNew-1] > MAXROW ) 1147 --nFirstNew; 1148 SCROW nDiff = nThisRow - nFirstNew; 1149 ScRange aRange( 0, nLastOtherRow, nOtherTab, 1150 MAXCOL, nLastOtherRow+nDiff, nOtherTab ); 1151 pChangeTrack->AppendInsert( aRange ); 1152 } 1153 } 1154 else 1155 nLastOtherRow = nOtherRow; 1156 } 1157 if ( nLastOtherRow > 0 ) // ganz oben geloescht 1158 { 1159 ScRange aDelRange( 0, 0, nOtherTab, 1160 MAXCOL, nLastOtherRow-1, nOtherTab ); 1161 pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 ); 1162 } 1163 1164 // Zeilen durchgehen um einzelne Zellen zu finden 1165 1166 for (nThisRow = 0; nThisRow <= nThisEndRow; nThisRow++) 1167 { 1168 SCROW nOtherRow = pOtherRows[nThisRow]; 1169 for (nThisCol = 0; nThisCol <= nThisEndCol; nThisCol++) 1170 { 1171 SCCOL nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]); 1172 ScAddress aThisPos( nThisCol, nThisRow, nThisTab ); 1173 const ScBaseCell* pThisCell = GetCell( aThisPos ); 1174 const ScBaseCell* pOtherCell = NULL; 1175 if ( ValidCol(nOtherCol) && ValidRow(nOtherRow) ) 1176 { 1177 ScAddress aOtherPos( nOtherCol, nOtherRow, nOtherTab ); 1178 pOtherCell = rOtherDoc.GetCell( aOtherPos ); 1179 } 1180 if ( !ScBaseCell::CellEqual( pThisCell, pOtherCell ) ) 1181 { 1182 ScRange aRange( aThisPos ); 1183 ScChangeActionContent* pAction = new ScChangeActionContent( aRange ); 1184 pAction->SetOldValue( pOtherCell, &rOtherDoc, this ); 1185 pAction->SetNewValue( pThisCell, this ); 1186 pChangeTrack->Append( pAction ); 1187 } 1188 } 1189 aProgress.SetStateOnPercent(nProgressStart+nThisRow); 1190 } 1191 1192 delete[] pOtherCols; 1193 delete[] pOtherRows; 1194 delete[] pTempRows; 1195 } 1196 } 1197 1198 //! Inhalt von eingefuegten / geloeschten Tabellen ??? 1199 //! Aktionen fuer eingefuegte / geloeschte Tabellen ??? 1200 1201 delete[] pOtherTabs; 1202 } 1203 1204 1205 1206 1207 1208