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 #include <algorithm> 35 #include <deque> 36 37 #include <boost/bind.hpp> 38 39 #include <vcl/mapmod.hxx> 40 #include <editeng/editobj.hxx> 41 #include <editeng/editstat.hxx> 42 43 #include "cell.hxx" 44 #include "compiler.hxx" 45 #include "formula/errorcodes.hxx" 46 #include "document.hxx" 47 #include "rangenam.hxx" 48 #include "rechead.hxx" 49 #include "refupdat.hxx" 50 #include "scmatrix.hxx" 51 #include "editutil.hxx" 52 #include "chgtrack.hxx" 53 #include "externalrefmgr.hxx" 54 55 using namespace formula; 56 57 // STATIC DATA ----------------------------------------------------------- 58 59 #ifdef USE_MEMPOOL 60 const sal_uInt16 nMemPoolEditCell = (0x1000 - 64) / sizeof(ScNoteCell); 61 IMPL_FIXEDMEMPOOL_NEWDEL( ScEditCell, nMemPoolEditCell, nMemPoolEditCell ) 62 #endif 63 64 // ============================================================================ 65 66 ScEditCell::ScEditCell( const EditTextObject* pObject, ScDocument* pDocP, 67 const SfxItemPool* pFromPool ) : 68 ScBaseCell( CELLTYPE_EDIT ), 69 pString( NULL ), 70 pDoc( pDocP ) 71 { 72 SetTextObject( pObject, pFromPool ); 73 } 74 75 ScEditCell::ScEditCell( const ScEditCell& rCell, ScDocument& rDoc ) : 76 ScBaseCell( rCell ), 77 pString( NULL ), 78 pDoc( &rDoc ) 79 { 80 SetTextObject( rCell.pData, rCell.pDoc->GetEditPool() ); 81 } 82 83 ScEditCell::ScEditCell( const String& rString, ScDocument* pDocP ) : 84 ScBaseCell( CELLTYPE_EDIT ), 85 pString( NULL ), 86 pDoc( pDocP ) 87 { 88 DBG_ASSERT( rString.Search('\n') != STRING_NOTFOUND || 89 rString.Search(CHAR_CR) != STRING_NOTFOUND, 90 "EditCell mit einfachem Text !?!?" ); 91 92 EditEngine& rEngine = pDoc->GetEditEngine(); 93 rEngine.SetText( rString ); 94 pData = rEngine.CreateTextObject(); 95 } 96 97 ScEditCell::~ScEditCell() 98 { 99 delete pData; 100 delete pString; 101 102 #ifdef DBG_UTIL 103 eCellType = CELLTYPE_DESTROYED; 104 #endif 105 } 106 107 void ScEditCell::SetData( const EditTextObject* pObject, 108 const SfxItemPool* pFromPool ) 109 { 110 if ( pString ) 111 { 112 delete pString; 113 pString = NULL; 114 } 115 delete pData; 116 SetTextObject( pObject, pFromPool ); 117 } 118 119 void ScEditCell::GetData( const EditTextObject*& rpObject ) const 120 { 121 rpObject = pData; 122 } 123 124 void ScEditCell::GetString( String& rString ) const 125 { 126 if ( pString ) 127 rString = *pString; 128 else if ( pData ) 129 { 130 // auch Text von URL-Feldern, Doc-Engine ist eine ScFieldEditEngine 131 EditEngine& rEngine = pDoc->GetEditEngine(); 132 rEngine.SetText( *pData ); 133 rString = ScEditUtil::GetMultilineString(rEngine); // string with line separators between paragraphs 134 // cache short strings for formulas 135 if ( rString.Len() < 256 ) 136 ((ScEditCell*)this)->pString = new String( rString ); //! non-const 137 } 138 else 139 rString.Erase(); 140 } 141 142 void ScEditCell::SetTextObject( const EditTextObject* pObject, 143 const SfxItemPool* pFromPool ) 144 { 145 if ( pObject ) 146 { 147 if ( pFromPool && pDoc->GetEditPool() == pFromPool ) 148 pData = pObject->Clone(); 149 else 150 { //! anderer Pool 151 // Leider gibt es keinen anderen Weg, um den Pool umzuhaengen, 152 // als das Object durch eine entsprechende Engine zu schleusen.. 153 EditEngine& rEngine = pDoc->GetEditEngine(); 154 if ( pObject->HasOnlineSpellErrors() ) 155 { 156 sal_uLong nControl = rEngine.GetControlWord(); 157 const sal_uLong nSpellControl = EE_CNTRL_ONLINESPELLING | EE_CNTRL_ALLOWBIGOBJS; 158 sal_Bool bNewControl = ( (nControl & nSpellControl) != nSpellControl ); 159 if ( bNewControl ) 160 rEngine.SetControlWord( nControl | nSpellControl ); 161 rEngine.SetText( *pObject ); 162 pData = rEngine.CreateTextObject(); 163 if ( bNewControl ) 164 rEngine.SetControlWord( nControl ); 165 } 166 else 167 { 168 rEngine.SetText( *pObject ); 169 pData = rEngine.CreateTextObject(); 170 } 171 } 172 } 173 else 174 pData = NULL; 175 } 176 177 // ============================================================================ 178 179 namespace 180 { 181 182 using std::deque; 183 184 typedef SCCOLROW(*DimensionSelector)(const ScSingleRefData&); 185 186 187 static SCCOLROW lcl_GetCol(const ScSingleRefData& rData) 188 { 189 return rData.nCol; 190 } 191 192 193 static SCCOLROW lcl_GetRow(const ScSingleRefData& rData) 194 { 195 return rData.nRow; 196 } 197 198 199 static SCCOLROW lcl_GetTab(const ScSingleRefData& rData) 200 { 201 return rData.nTab; 202 } 203 204 205 /** Check if both references span the same range in selected dimension. 206 */ 207 static bool 208 lcl_checkRangeDimension( 209 const SingleDoubleRefProvider& rRef1, 210 const SingleDoubleRefProvider& rRef2, 211 const DimensionSelector aWhich) 212 { 213 return 214 aWhich(rRef1.Ref1) == aWhich(rRef2.Ref1) 215 && aWhich(rRef1.Ref2) == aWhich(rRef2.Ref2); 216 } 217 218 219 static bool 220 lcl_checkRangeDimensions( 221 const SingleDoubleRefProvider& rRef1, 222 const SingleDoubleRefProvider& rRef2, 223 bool& bCol, bool& bRow, bool& bTab) 224 { 225 const bool bSameCols(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetCol)); 226 const bool bSameRows(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetRow)); 227 const bool bSameTabs(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetTab)); 228 229 // Test if exactly two dimensions are equal 230 if (!(bSameCols ^ bSameRows ^ bSameTabs) 231 && (bSameCols || bSameRows || bSameTabs)) 232 { 233 bCol = !bSameCols; 234 bRow = !bSameRows; 235 bTab = !bSameTabs; 236 return true; 237 } 238 return false; 239 } 240 241 242 /** Check if references in given reference list can possibly 243 form a range. To do that, two of their dimensions must be the same. 244 */ 245 static bool 246 lcl_checkRangeDimensions( 247 const deque<ScToken*>::const_iterator aBegin, 248 const deque<ScToken*>::const_iterator aEnd, 249 bool& bCol, bool& bRow, bool& bTab) 250 { 251 deque<ScToken*>::const_iterator aCur(aBegin); 252 ++aCur; 253 const SingleDoubleRefProvider aRef(**aBegin); 254 bool bOk(false); 255 { 256 const SingleDoubleRefProvider aRefCur(**aCur); 257 bOk = lcl_checkRangeDimensions(aRef, aRefCur, bCol, bRow, bTab); 258 } 259 while (bOk && aCur != aEnd) 260 { 261 const SingleDoubleRefProvider aRefCur(**aCur); 262 bool bColTmp(false); 263 bool bRowTmp(false); 264 bool bTabTmp(false); 265 bOk = lcl_checkRangeDimensions(aRef, aRefCur, bColTmp, bRowTmp, bTabTmp); 266 bOk = bOk && (bCol == bColTmp && bRow == bRowTmp && bTab == bTabTmp); 267 ++aCur; 268 } 269 270 if (bOk && aCur == aEnd) 271 { 272 bCol = bCol; 273 bRow = bRow; 274 bTab = bTab; 275 return true; 276 } 277 return false; 278 } 279 280 281 bool 282 lcl_lessReferenceBy( 283 const ScToken* const pRef1, const ScToken* const pRef2, 284 const DimensionSelector aWhich) 285 { 286 const SingleDoubleRefProvider rRef1(*pRef1); 287 const SingleDoubleRefProvider rRef2(*pRef2); 288 return aWhich(rRef1.Ref1) < aWhich(rRef2.Ref1); 289 } 290 291 292 /** Returns true if range denoted by token pRef2 starts immediately after 293 range denoted by token pRef1. Dimension, in which the comparison takes 294 place, is given by aWhich. 295 */ 296 bool 297 lcl_isImmediatelyFollowing( 298 const ScToken* const pRef1, const ScToken* const pRef2, 299 const DimensionSelector aWhich) 300 { 301 const SingleDoubleRefProvider rRef1(*pRef1); 302 const SingleDoubleRefProvider rRef2(*pRef2); 303 return aWhich(rRef2.Ref1) - aWhich(rRef1.Ref2) == 1; 304 } 305 306 307 static bool 308 lcl_checkIfAdjacent( 309 const deque<ScToken*>& rReferences, 310 const DimensionSelector aWhich) 311 { 312 typedef deque<ScToken*>::const_iterator Iter; 313 Iter aBegin(rReferences.begin()); 314 Iter aEnd(rReferences.end()); 315 Iter aBegin1(aBegin); 316 ++aBegin1, --aEnd; 317 return std::equal( 318 aBegin, aEnd, aBegin1, 319 boost::bind(lcl_isImmediatelyFollowing, _1, _2, aWhich)); 320 } 321 322 323 static void 324 lcl_fillRangeFromRefList( 325 const deque<ScToken*>& rReferences, ScRange& rRange) 326 { 327 const ScSingleRefData aStart( 328 SingleDoubleRefProvider(*rReferences.front()).Ref1); 329 rRange.aStart.Set(aStart.nCol, aStart.nRow, aStart.nTab); 330 const ScSingleRefData aEnd( 331 SingleDoubleRefProvider(*rReferences.back()).Ref2); 332 rRange.aEnd.Set(aEnd.nCol, aEnd.nRow, aEnd.nTab); 333 } 334 335 336 static bool 337 lcl_refListFormsOneRange( 338 const ScAddress& aPos, deque<ScToken*>& rReferences, 339 ScRange& rRange) 340 { 341 std::for_each( 342 rReferences.begin(), rReferences.end(), 343 bind(&ScToken::CalcAbsIfRel, _1, aPos)) 344 ; 345 if (rReferences.size() == 1) { 346 lcl_fillRangeFromRefList(rReferences, rRange); 347 return true; 348 } 349 350 bool bCell(false); 351 bool bRow(false); 352 bool bTab(false); 353 if (lcl_checkRangeDimensions(rReferences.begin(), rReferences.end(), 354 bCell, bRow, bTab)) 355 { 356 DimensionSelector aWhich; 357 if (bCell) 358 { 359 aWhich = lcl_GetCol; 360 } 361 else if (bRow) 362 { 363 aWhich = lcl_GetRow; 364 } 365 else if (bTab) 366 { 367 aWhich = lcl_GetTab; 368 } 369 else 370 { 371 OSL_ENSURE(false, "lcl_checkRangeDimensions shouldn't allow that!"); 372 aWhich = lcl_GetRow; // initialize to avoid warning 373 } 374 // Sort the references by start of range 375 std::sort(rReferences.begin(), rReferences.end(), 376 boost::bind(lcl_lessReferenceBy, _1, _2, aWhich)); 377 if (lcl_checkIfAdjacent(rReferences, aWhich)) 378 { 379 lcl_fillRangeFromRefList(rReferences, rRange); 380 return true; 381 } 382 } 383 return false; 384 } 385 386 387 bool lcl_isReference(const FormulaToken& rToken) 388 { 389 return 390 rToken.GetType() == svSingleRef || 391 rToken.GetType() == svDoubleRef; 392 } 393 394 } 395 396 sal_Bool ScFormulaCell::IsEmpty() 397 { 398 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) 399 Interpret(); 400 return aResult.GetCellResultType() == formula::svEmptyCell; 401 } 402 403 sal_Bool ScFormulaCell::IsEmptyDisplayedAsString() 404 { 405 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) 406 Interpret(); 407 return aResult.IsEmptyDisplayedAsString(); 408 } 409 410 sal_Bool ScFormulaCell::IsValue() 411 { 412 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) 413 Interpret(); 414 return aResult.IsValue(); 415 } 416 417 double ScFormulaCell::GetValue() 418 { 419 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) 420 Interpret(); 421 if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) && 422 !aResult.GetResultError()) 423 return aResult.GetDouble(); 424 return 0.0; 425 } 426 427 double ScFormulaCell::GetValueAlways() 428 { 429 // for goal seek: return result value even if error code is set 430 431 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) 432 Interpret(); 433 return aResult.GetDouble(); 434 } 435 436 void ScFormulaCell::GetString( String& rString ) 437 { 438 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) 439 Interpret(); 440 if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) && 441 !aResult.GetResultError()) 442 rString = aResult.GetString(); 443 else 444 rString.Erase(); 445 } 446 447 const ScMatrix* ScFormulaCell::GetMatrix() 448 { 449 if ( pDocument->GetAutoCalc() ) 450 { 451 // Was stored !bDirty but an accompanying matrix cell was bDirty? 452 // => we need to get the matrix. 453 if (!bDirty && cMatrixFlag == MM_FORMULA && !aResult.GetMatrix().Is()) 454 bDirty = sal_True; 455 if ( IsDirtyOrInTableOpDirty() ) 456 Interpret(); 457 } 458 return aResult.GetMatrix(); 459 } 460 461 sal_Bool ScFormulaCell::GetMatrixOrigin( ScAddress& rPos ) const 462 { 463 switch ( cMatrixFlag ) 464 { 465 case MM_FORMULA : 466 rPos = aPos; 467 return sal_True; 468 // break; 469 case MM_REFERENCE : 470 { 471 pCode->Reset(); 472 ScToken* t = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); 473 if( t ) 474 { 475 ScSingleRefData& rRef = t->GetSingleRef(); 476 rRef.CalcAbsIfRel( aPos ); 477 if ( rRef.Valid() ) 478 { 479 rPos.Set( rRef.nCol, rRef.nRow, rRef.nTab ); 480 return sal_True; 481 } 482 } 483 } 484 break; 485 } 486 return sal_False; 487 } 488 489 490 /* 491 Edge-Values: 492 493 8 494 4 16 495 2 496 497 innerhalb: 1 498 ausserhalb: 0 499 (reserviert: offen: 32) 500 */ 501 502 sal_uInt16 ScFormulaCell::GetMatrixEdge( ScAddress& rOrgPos ) 503 { 504 switch ( cMatrixFlag ) 505 { 506 case MM_FORMULA : 507 case MM_REFERENCE : 508 { 509 static SCCOL nC; 510 static SCROW nR; 511 ScAddress aOrg; 512 if ( !GetMatrixOrigin( aOrg ) ) 513 return 0; // dumm gelaufen.. 514 if ( aOrg != rOrgPos ) 515 { // erstes Mal oder andere Matrix als letztes Mal 516 rOrgPos = aOrg; 517 ScFormulaCell* pFCell; 518 if ( cMatrixFlag == MM_REFERENCE ) 519 pFCell = (ScFormulaCell*) pDocument->GetCell( aOrg ); 520 else 521 pFCell = this; // this MM_FORMULA 522 // this gibt's nur einmal, kein Vergleich auf pFCell==this 523 if ( pFCell && pFCell->GetCellType() == CELLTYPE_FORMULA 524 && pFCell->cMatrixFlag == MM_FORMULA ) 525 { 526 pFCell->GetMatColsRows( nC, nR ); 527 if ( nC == 0 || nR == 0 ) 528 { // aus altem Dokument geladen, neu erzeugen 529 nC = 1; 530 nR = 1; 531 ScAddress aTmpOrg; 532 ScBaseCell* pCell; 533 ScAddress aAdr( aOrg ); 534 aAdr.IncCol(); 535 sal_Bool bCont = sal_True; 536 do 537 { 538 pCell = pDocument->GetCell( aAdr ); 539 if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA 540 && ((ScFormulaCell*)pCell)->cMatrixFlag == MM_REFERENCE 541 && GetMatrixOrigin( aTmpOrg ) && aTmpOrg == aOrg ) 542 { 543 nC++; 544 aAdr.IncCol(); 545 } 546 else 547 bCont = sal_False; 548 } while ( bCont ); 549 aAdr = aOrg; 550 aAdr.IncRow(); 551 bCont = sal_True; 552 do 553 { 554 pCell = pDocument->GetCell( aAdr ); 555 if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA 556 && ((ScFormulaCell*)pCell)->cMatrixFlag == MM_REFERENCE 557 && GetMatrixOrigin( aTmpOrg ) && aTmpOrg == aOrg ) 558 { 559 nR++; 560 aAdr.IncRow(); 561 } 562 else 563 bCont = sal_False; 564 } while ( bCont ); 565 pFCell->SetMatColsRows( nC, nR ); 566 } 567 } 568 else 569 { 570 #ifdef DBG_UTIL 571 String aTmp; 572 ByteString aMsg( "broken Matrix, no MatFormula at origin, Pos: " ); 573 aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument ); 574 aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US ); 575 aMsg += ", MatOrg: "; 576 aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument ); 577 aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US ); 578 DBG_ERRORFILE( aMsg.GetBuffer() ); 579 #endif 580 return 0; // bad luck ... 581 } 582 } 583 // here we are, healthy and clean, somewhere in between 584 SCsCOL dC = aPos.Col() - aOrg.Col(); 585 SCsROW dR = aPos.Row() - aOrg.Row(); 586 sal_uInt16 nEdges = 0; 587 if ( dC >= 0 && dR >= 0 && dC < nC && dR < nR ) 588 { 589 if ( dC == 0 ) 590 nEdges |= 4; // linke Kante 591 if ( dC+1 == nC ) 592 nEdges |= 16; // rechte Kante 593 if ( dR == 0 ) 594 nEdges |= 8; // obere Kante 595 if ( dR+1 == nR ) 596 nEdges |= 2; // untere Kante 597 if ( !nEdges ) 598 nEdges = 1; // mittendrin 599 } 600 #ifdef DBG_UTIL 601 else 602 { 603 String aTmp; 604 ByteString aMsg( "broken Matrix, Pos: " ); 605 aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument ); 606 aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US ); 607 aMsg += ", MatOrg: "; 608 aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument ); 609 aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US ); 610 aMsg += ", MatCols: "; 611 aMsg += ByteString::CreateFromInt32( nC ); 612 aMsg += ", MatRows: "; 613 aMsg += ByteString::CreateFromInt32( nR ); 614 aMsg += ", DiffCols: "; 615 aMsg += ByteString::CreateFromInt32( dC ); 616 aMsg += ", DiffRows: "; 617 aMsg += ByteString::CreateFromInt32( dR ); 618 DBG_ERRORFILE( aMsg.GetBuffer() ); 619 } 620 #endif 621 return nEdges; 622 // break; 623 } 624 default: 625 return 0; 626 } 627 } 628 629 sal_uInt16 ScFormulaCell::GetErrCode() 630 { 631 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) 632 Interpret(); 633 /* FIXME: If ScTokenArray::SetCodeError() was really only for code errors 634 * and not also abused for signaling other error conditions we could bail 635 * out even before attempting to interpret broken code. */ 636 sal_uInt16 nErr = pCode->GetCodeError(); 637 if (nErr) 638 return nErr; 639 return aResult.GetResultError(); 640 } 641 642 sal_uInt16 ScFormulaCell::GetRawError() 643 { 644 sal_uInt16 nErr = pCode->GetCodeError(); 645 if (nErr) 646 return nErr; 647 return aResult.GetResultError(); 648 } 649 650 sal_Bool ScFormulaCell::HasOneReference( ScRange& r ) const 651 { 652 pCode->Reset(); 653 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); 654 if( p && !pCode->GetNextReferenceRPN() ) // nur eine! 655 { 656 p->CalcAbsIfRel( aPos ); 657 SingleDoubleRefProvider aProv( *p ); 658 r.aStart.Set( aProv.Ref1.nCol, 659 aProv.Ref1.nRow, 660 aProv.Ref1.nTab ); 661 r.aEnd.Set( aProv.Ref2.nCol, 662 aProv.Ref2.nRow, 663 aProv.Ref2.nTab ); 664 return sal_True; 665 } 666 else 667 return sal_False; 668 } 669 670 bool 671 ScFormulaCell::HasRefListExpressibleAsOneReference(ScRange& rRange) const 672 { 673 /* If there appears just one reference in the formula, it's the same 674 as HasOneReference(). If there are more of them, they can denote 675 one range if they are (sole) arguments of one function. 676 Union of these references must form one range and their 677 intersection must be empty set. 678 */ 679 680 // Detect the simple case of exactly one reference in advance without all 681 // overhead. 682 // #i107741# Doing so actually makes outlines using SUBTOTAL(x;reference) 683 // work again, where the function does not have only references. 684 if (HasOneReference( rRange)) 685 return true; 686 687 pCode->Reset(); 688 // Get first reference, if any 689 ScToken* const pFirstReference( 690 dynamic_cast<ScToken*>(pCode->GetNextReferenceRPN())); 691 if (pFirstReference) 692 { 693 // Collect all consecutive references, starting by the one 694 // already found 695 std::deque<ScToken*> aReferences; 696 aReferences.push_back(pFirstReference); 697 FormulaToken* pToken(pCode->NextRPN()); 698 FormulaToken* pFunction(0); 699 while (pToken) 700 { 701 if (lcl_isReference(*pToken)) 702 { 703 aReferences.push_back(dynamic_cast<ScToken*>(pToken)); 704 pToken = pCode->NextRPN(); 705 } 706 else 707 { 708 if (pToken->IsFunction()) 709 { 710 pFunction = pToken; 711 } 712 break; 713 } 714 } 715 if (pFunction && !pCode->GetNextReferenceRPN() 716 && (pFunction->GetParamCount() == aReferences.size())) 717 { 718 return lcl_refListFormsOneRange(aPos, aReferences, rRange); 719 } 720 } 721 return false; 722 } 723 724 sal_Bool ScFormulaCell::HasRelNameReference() const 725 { 726 pCode->Reset(); 727 ScToken* t; 728 while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceRPN()) ) != NULL ) 729 { 730 if ( t->GetSingleRef().IsRelName() || 731 (t->GetType() == formula::svDoubleRef && 732 t->GetDoubleRef().Ref2.IsRelName()) ) 733 return sal_True; 734 } 735 return sal_False; 736 } 737 738 sal_Bool ScFormulaCell::HasColRowName() const 739 { 740 pCode->Reset(); 741 return (pCode->GetNextColRowName() != NULL); 742 } 743 744 void ScFormulaCell::UpdateReference(UpdateRefMode eUpdateRefMode, 745 const ScRange& r, 746 SCsCOL nDx, SCsROW nDy, SCsTAB nDz, 747 ScDocument* pUndoDoc, const ScAddress* pUndoCellPos ) 748 { 749 SCCOL nCol1 = r.aStart.Col(); 750 SCROW nRow1 = r.aStart.Row(); 751 SCTAB nTab1 = r.aStart.Tab(); 752 SCCOL nCol2 = r.aEnd.Col(); 753 SCROW nRow2 = r.aEnd.Row(); 754 SCTAB nTab2 = r.aEnd.Tab(); 755 SCCOL nCol = aPos.Col(); 756 SCROW nRow = aPos.Row(); 757 SCTAB nTab = aPos.Tab(); 758 ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc 759 if ( pUndoCellPos ) 760 aUndoPos = *pUndoCellPos; 761 ScAddress aOldPos( aPos ); 762 // sal_Bool bPosChanged = sal_False; // ob diese Zelle bewegt wurde 763 sal_Bool bIsInsert = sal_False; 764 if (eUpdateRefMode == URM_INSDEL) 765 { 766 bIsInsert = (nDx >= 0 && nDy >= 0 && nDz >= 0); 767 if ( nDx && nRow >= nRow1 && nRow <= nRow2 && 768 nTab >= nTab1 && nTab <= nTab2 ) 769 { 770 if (nCol >= nCol1) 771 { 772 nCol = sal::static_int_cast<SCCOL>( nCol + nDx ); 773 if ((SCsCOL) nCol < 0) 774 nCol = 0; 775 else if ( nCol > MAXCOL ) 776 nCol = MAXCOL; 777 aPos.SetCol( nCol ); 778 // bPosChanged = sal_True; 779 } 780 } 781 if ( nDy && nCol >= nCol1 && nCol <= nCol2 && 782 nTab >= nTab1 && nTab <= nTab2 ) 783 { 784 if (nRow >= nRow1) 785 { 786 nRow = sal::static_int_cast<SCROW>( nRow + nDy ); 787 if ((SCsROW) nRow < 0) 788 nRow = 0; 789 else if ( nRow > MAXROW ) 790 nRow = MAXROW; 791 aPos.SetRow( nRow ); 792 // bPosChanged = sal_True; 793 } 794 } 795 if ( nDz && nCol >= nCol1 && nCol <= nCol2 && 796 nRow >= nRow1 && nRow <= nRow2 ) 797 { 798 if (nTab >= nTab1) 799 { 800 SCTAB nMaxTab = pDocument->GetTableCount() - 1; 801 nTab = sal::static_int_cast<SCTAB>( nTab + nDz ); 802 if ((SCsTAB) nTab < 0) 803 nTab = 0; 804 else if ( nTab > nMaxTab ) 805 nTab = nMaxTab; 806 aPos.SetTab( nTab ); 807 // bPosChanged = sal_True; 808 } 809 } 810 } 811 else if ( r.In( aPos ) ) 812 { 813 aOldPos.Set( nCol - nDx, nRow - nDy, nTab - nDz ); 814 // bPosChanged = sal_True; 815 } 816 817 sal_Bool bHasRefs = sal_False; 818 sal_Bool bHasColRowNames = sal_False; 819 sal_Bool bOnRefMove = sal_False; 820 if ( !pDocument->IsClipOrUndo() ) 821 { 822 pCode->Reset(); 823 bHasRefs = (pCode->GetNextReferenceRPN() != NULL); 824 if ( !bHasRefs || eUpdateRefMode == URM_COPY ) 825 { 826 pCode->Reset(); 827 bHasColRowNames = (pCode->GetNextColRowName() != NULL); 828 bHasRefs = bHasRefs || bHasColRowNames; 829 } 830 bOnRefMove = pCode->IsRecalcModeOnRefMove(); 831 } 832 if( bHasRefs || bOnRefMove ) 833 { 834 ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL; 835 sal_Bool bValChanged; 836 ScRangeData* pRangeData; 837 sal_Bool bRangeModified; // any range, not only shared formula 838 sal_Bool bRefSizeChanged; 839 if ( bHasRefs ) 840 { 841 ScCompiler aComp(pDocument, aPos, *pCode); 842 aComp.SetGrammar(pDocument->GetGrammar()); 843 pRangeData = aComp.UpdateReference(eUpdateRefMode, aOldPos, r, 844 nDx, nDy, nDz, 845 bValChanged, bRefSizeChanged); 846 bRangeModified = aComp.HasModifiedRange(); 847 } 848 else 849 { 850 bValChanged = sal_False; 851 pRangeData = NULL; 852 bRangeModified = sal_False; 853 bRefSizeChanged = sal_False; 854 } 855 if ( bOnRefMove ) 856 bOnRefMove = (bValChanged || (aPos != aOldPos)); 857 // Cell may reference itself, e.g. ocColumn, ocRow without parameter 858 859 sal_Bool bColRowNameCompile, bHasRelName, bNewListening, bInDeleteUndo; 860 if ( bHasRefs ) 861 { 862 // Upon Insert ColRowNames have to be recompiled in case the 863 // insertion occurs right in front of the range. 864 bColRowNameCompile = 865 (eUpdateRefMode == URM_INSDEL && (nDx > 0 || nDy > 0)); 866 if ( bColRowNameCompile ) 867 { 868 bColRowNameCompile = sal_False; 869 ScToken* t; 870 ScRangePairList* pColList = pDocument->GetColNameRanges(); 871 ScRangePairList* pRowList = pDocument->GetRowNameRanges(); 872 pCode->Reset(); 873 while ( !bColRowNameCompile && (t = static_cast<ScToken*>(pCode->GetNextColRowName())) != NULL ) 874 { 875 ScSingleRefData& rRef = t->GetSingleRef(); 876 if ( nDy > 0 && rRef.IsColRel() ) 877 { // ColName 878 rRef.CalcAbsIfRel( aPos ); 879 ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab ); 880 ScRangePair* pR = pColList->Find( aAdr ); 881 if ( pR ) 882 { // definiert 883 if ( pR->GetRange(1).aStart.Row() == nRow1 ) 884 bColRowNameCompile = sal_True; 885 } 886 else 887 { // on the fly 888 if ( rRef.nRow + 1 == nRow1 ) 889 bColRowNameCompile = sal_True; 890 } 891 } 892 if ( nDx > 0 && rRef.IsRowRel() ) 893 { // RowName 894 rRef.CalcAbsIfRel( aPos ); 895 ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab ); 896 ScRangePair* pR = pRowList->Find( aAdr ); 897 if ( pR ) 898 { // definiert 899 if ( pR->GetRange(1).aStart.Col() == nCol1 ) 900 bColRowNameCompile = sal_True; 901 } 902 else 903 { // on the fly 904 if ( rRef.nCol + 1 == nCol1 ) 905 bColRowNameCompile = sal_True; 906 } 907 } 908 } 909 } 910 else if ( eUpdateRefMode == URM_MOVE ) 911 { // bei Move/D&D neu kompilieren wenn ColRowName verschoben wurde 912 // oder diese Zelle auf einen zeigt und verschoben wurde 913 bColRowNameCompile = bCompile; // evtl. aus Copy-ctor 914 if ( !bColRowNameCompile ) 915 { 916 sal_Bool bMoved = (aPos != aOldPos); 917 pCode->Reset(); 918 ScToken* t = static_cast<ScToken*>(pCode->GetNextColRowName()); 919 if ( t && bMoved ) 920 bColRowNameCompile = sal_True; 921 while ( t && !bColRowNameCompile ) 922 { 923 ScSingleRefData& rRef = t->GetSingleRef(); 924 rRef.CalcAbsIfRel( aPos ); 925 if ( rRef.Valid() ) 926 { 927 ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab ); 928 if ( r.In( aAdr ) ) 929 bColRowNameCompile = sal_True; 930 } 931 t = static_cast<ScToken*>(pCode->GetNextColRowName()); 932 } 933 } 934 } 935 else if ( eUpdateRefMode == URM_COPY && bHasColRowNames && bValChanged ) 936 { 937 bColRowNameCompile = sal_True; 938 } 939 ScChangeTrack* pChangeTrack = pDocument->GetChangeTrack(); 940 if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() ) 941 bInDeleteUndo = sal_True; 942 else 943 bInDeleteUndo = sal_False; 944 // RelNameRefs are always moved 945 bHasRelName = HasRelNameReference(); 946 // Reference changed and new listening needed? 947 // Except in Insert/Delete without specialties. 948 bNewListening = (bRangeModified || pRangeData || bColRowNameCompile 949 || (bValChanged && (eUpdateRefMode != URM_INSDEL || 950 bInDeleteUndo || bRefSizeChanged)) || 951 (bHasRelName && eUpdateRefMode != URM_COPY)) 952 // #i36299# Don't duplicate action during cut&paste / drag&drop 953 // on a cell in the range moved, start/end listeners is done 954 // via ScDocument::DeleteArea() and ScDocument::CopyFromClip(). 955 && !(eUpdateRefMode == URM_MOVE && 956 pDocument->IsInsertingFromOtherDoc() && r.In(aPos)); 957 if ( bNewListening ) 958 EndListeningTo( pDocument, pOld, aOldPos ); 959 } 960 else 961 { 962 bColRowNameCompile = bHasRelName = bNewListening = bInDeleteUndo = 963 sal_False; 964 } 965 966 sal_Bool bNeedDirty; 967 // NeedDirty bei Aenderungen ausser Copy und Move/Insert ohne RelNames 968 if ( bRangeModified || pRangeData || bColRowNameCompile || 969 (bValChanged && eUpdateRefMode != URM_COPY && 970 (eUpdateRefMode != URM_MOVE || bHasRelName) && 971 (!bIsInsert || bHasRelName || bInDeleteUndo || 972 bRefSizeChanged)) || bOnRefMove) 973 bNeedDirty = sal_True; 974 else 975 bNeedDirty = sal_False; 976 if (pUndoDoc && (bValChanged || pRangeData || bOnRefMove)) 977 { 978 // Copy the cell to aUndoPos, which is its current position in the document, 979 // so this works when UpdateReference is called before moving the cells 980 // (InsertCells/DeleteCells - aPos is changed above) as well as when UpdateReference 981 // is called after moving the cells (MoveBlock/PasteFromClip - aOldPos is changed). 982 983 // If there is already a formula cell in the undo document, don't overwrite it, 984 // the first (oldest) is the important cell. 985 if ( pUndoDoc->GetCellType( aUndoPos ) != CELLTYPE_FORMULA ) 986 { 987 ScFormulaCell* pFCell = new ScFormulaCell( pUndoDoc, aUndoPos, 988 pOld, eTempGrammar, cMatrixFlag ); 989 pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!) 990 pUndoDoc->PutCell( aUndoPos, pFCell ); 991 } 992 } 993 // #i116833# If the formula is changed, always invalidate the stream (even if the result is the same). 994 // If the formula is moved, the change is recognized separately. 995 if (bValChanged && pDocument->IsStreamValid(aPos.Tab())) 996 pDocument->SetStreamValid(aPos.Tab(), sal_False); 997 bValChanged = sal_False; 998 if ( pRangeData ) 999 { // Replace shared formula with own formula 1000 pDocument->RemoveFromFormulaTree( this ); // update formula count 1001 delete pCode; 1002 pCode = pRangeData->GetCode()->Clone(); 1003 // #i18937# #i110008# call MoveRelWrap, but with the old position 1004 ScCompiler::MoveRelWrap(*pCode, pDocument, aOldPos, pRangeData->GetMaxCol(), pRangeData->GetMaxRow()); 1005 ScCompiler aComp2(pDocument, aPos, *pCode); 1006 aComp2.SetGrammar(pDocument->GetGrammar()); 1007 aComp2.UpdateSharedFormulaReference( eUpdateRefMode, aOldPos, r, 1008 nDx, nDy, nDz ); 1009 bValChanged = sal_True; 1010 bNeedDirty = sal_True; 1011 } 1012 if ( ( bCompile = (bCompile || bValChanged || bRangeModified || bColRowNameCompile) ) != 0 ) 1013 { 1014 CompileTokenArray( bNewListening ); // kein Listening 1015 bNeedDirty = sal_True; 1016 } 1017 if ( !bInDeleteUndo ) 1018 { // In ChangeTrack Delete-Reject listeners are established in 1019 // InsertCol/InsertRow 1020 if ( bNewListening ) 1021 { 1022 if ( eUpdateRefMode == URM_INSDEL ) 1023 { 1024 // Inserts/Deletes re-establish listeners after all 1025 // UpdateReference calls. 1026 // All replaced shared formula listeners have to be 1027 // established after an Insert or Delete. Do nothing here. 1028 SetNeedsListening( sal_True); 1029 } 1030 else 1031 StartListeningTo( pDocument ); 1032 } 1033 } 1034 if ( bNeedDirty && (!(eUpdateRefMode == URM_INSDEL && bHasRelName) || pRangeData) ) 1035 { // Referenzen abgeschnitten, ungueltig o.ae.? 1036 sal_Bool bOldAutoCalc = pDocument->GetAutoCalc(); 1037 // kein Interpret in SubMinimalRecalc wegen evtl. falscher Referenzen 1038 pDocument->SetAutoCalc( sal_False ); 1039 SetDirty(); 1040 pDocument->SetAutoCalc( bOldAutoCalc ); 1041 } 1042 1043 delete pOld; 1044 } 1045 } 1046 1047 void ScFormulaCell::UpdateInsertTab(SCTAB nTable) 1048 { 1049 sal_Bool bPosChanged = ( aPos.Tab() >= nTable ? sal_True : sal_False ); 1050 pCode->Reset(); 1051 if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() ) 1052 { 1053 EndListeningTo( pDocument ); 1054 // IncTab _nach_ EndListeningTo und _vor_ Compiler UpdateInsertTab ! 1055 if ( bPosChanged ) 1056 aPos.IncTab(); 1057 ScRangeData* pRangeData; 1058 ScCompiler aComp(pDocument, aPos, *pCode); 1059 aComp.SetGrammar(pDocument->GetGrammar()); 1060 pRangeData = aComp.UpdateInsertTab( nTable, sal_False ); 1061 if (pRangeData) // Shared Formula gegen echte Formel 1062 { // austauschen 1063 sal_Bool bRefChanged; 1064 pDocument->RemoveFromFormulaTree( this ); // update formula count 1065 delete pCode; 1066 pCode = new ScTokenArray( *pRangeData->GetCode() ); 1067 ScCompiler aComp2(pDocument, aPos, *pCode); 1068 aComp2.SetGrammar(pDocument->GetGrammar()); 1069 aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow()); 1070 aComp2.UpdateInsertTab( nTable, sal_False ); 1071 // If the shared formula contained a named range/formula containing 1072 // an absolute reference to a sheet, those have to be readjusted. 1073 aComp2.UpdateDeleteTab( nTable, sal_False, sal_True, bRefChanged ); 1074 bCompile = sal_True; 1075 } 1076 // kein StartListeningTo weil pTab[nTab] noch nicht existiert! 1077 } 1078 else if ( bPosChanged ) 1079 aPos.IncTab(); 1080 } 1081 1082 sal_Bool ScFormulaCell::UpdateDeleteTab(SCTAB nTable, sal_Bool bIsMove) 1083 { 1084 sal_Bool bRefChanged = sal_False; 1085 sal_Bool bPosChanged = ( aPos.Tab() > nTable ? sal_True : sal_False ); 1086 pCode->Reset(); 1087 if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() ) 1088 { 1089 EndListeningTo( pDocument ); 1090 // IncTab _nach_ EndListeningTo und _vor_ Compiler UpdateDeleteTab ! 1091 if ( bPosChanged ) 1092 aPos.IncTab(-1); 1093 ScRangeData* pRangeData; 1094 ScCompiler aComp(pDocument, aPos, *pCode); 1095 aComp.SetGrammar(pDocument->GetGrammar()); 1096 pRangeData = aComp.UpdateDeleteTab(nTable, bIsMove, sal_False, bRefChanged); 1097 if (pRangeData) // Shared Formula gegen echte Formel 1098 { // austauschen 1099 pDocument->RemoveFromFormulaTree( this ); // update formula count 1100 delete pCode; 1101 pCode = pRangeData->GetCode()->Clone(); 1102 ScCompiler aComp2(pDocument, aPos, *pCode); 1103 aComp2.SetGrammar(pDocument->GetGrammar()); 1104 aComp2.CompileTokenArray(); 1105 aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow()); 1106 aComp2.UpdateDeleteTab( nTable, sal_False, sal_False, bRefChanged ); 1107 // If the shared formula contained a named range/formula containing 1108 // an absolute reference to a sheet, those have to be readjusted. 1109 aComp2.UpdateInsertTab( nTable,sal_True ); 1110 // bRefChanged kann beim letzten UpdateDeleteTab zurueckgesetzt worden sein 1111 bRefChanged = sal_True; 1112 bCompile = sal_True; 1113 } 1114 // kein StartListeningTo weil pTab[nTab] noch nicht korrekt! 1115 } 1116 else if ( bPosChanged ) 1117 aPos.IncTab(-1); 1118 1119 return bRefChanged; 1120 } 1121 1122 void ScFormulaCell::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos, SCTAB nTabNo ) 1123 { 1124 pCode->Reset(); 1125 if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() ) 1126 { 1127 EndListeningTo( pDocument ); 1128 // SetTab _nach_ EndListeningTo und _vor_ Compiler UpdateMoveTab ! 1129 aPos.SetTab( nTabNo ); 1130 ScRangeData* pRangeData; 1131 ScCompiler aComp(pDocument, aPos, *pCode); 1132 aComp.SetGrammar(pDocument->GetGrammar()); 1133 pRangeData = aComp.UpdateMoveTab( nOldPos, nNewPos, sal_False ); 1134 if (pRangeData) // Shared Formula gegen echte Formel 1135 { // austauschen 1136 pDocument->RemoveFromFormulaTree( this ); // update formula count 1137 delete pCode; 1138 pCode = pRangeData->GetCode()->Clone(); 1139 ScCompiler aComp2(pDocument, aPos, *pCode); 1140 aComp2.SetGrammar(pDocument->GetGrammar()); 1141 aComp2.CompileTokenArray(); 1142 aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow()); 1143 aComp2.UpdateMoveTab( nOldPos, nNewPos, sal_True ); 1144 bCompile = sal_True; 1145 } 1146 // kein StartListeningTo weil pTab[nTab] noch nicht korrekt! 1147 } 1148 else 1149 aPos.SetTab( nTabNo ); 1150 } 1151 1152 void ScFormulaCell::UpdateInsertTabAbs(SCTAB nTable) 1153 { 1154 if( !pDocument->IsClipOrUndo() ) 1155 { 1156 pCode->Reset(); 1157 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); 1158 while( p ) 1159 { 1160 ScSingleRefData& rRef1 = p->GetSingleRef(); 1161 if( !rRef1.IsTabRel() && (SCsTAB) nTable <= rRef1.nTab ) 1162 rRef1.nTab++; 1163 if( p->GetType() == formula::svDoubleRef ) 1164 { 1165 ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2; 1166 if( !rRef2.IsTabRel() && (SCsTAB) nTable <= rRef2.nTab ) 1167 rRef2.nTab++; 1168 } 1169 p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); 1170 } 1171 } 1172 } 1173 1174 sal_Bool ScFormulaCell::TestTabRefAbs(SCTAB nTable) 1175 { 1176 sal_Bool bRet = sal_False; 1177 if( !pDocument->IsClipOrUndo() ) 1178 { 1179 pCode->Reset(); 1180 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); 1181 while( p ) 1182 { 1183 ScSingleRefData& rRef1 = p->GetSingleRef(); 1184 if( !rRef1.IsTabRel() ) 1185 { 1186 if( (SCsTAB) nTable != rRef1.nTab ) 1187 bRet = sal_True; 1188 else if (nTable != aPos.Tab()) 1189 rRef1.nTab = aPos.Tab(); 1190 } 1191 if( p->GetType() == formula::svDoubleRef ) 1192 { 1193 ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2; 1194 if( !rRef2.IsTabRel() ) 1195 { 1196 if( (SCsTAB) nTable != rRef2.nTab ) 1197 bRet = sal_True; 1198 else if (nTable != aPos.Tab()) 1199 rRef2.nTab = aPos.Tab(); 1200 } 1201 } 1202 p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); 1203 } 1204 } 1205 return bRet; 1206 } 1207 1208 void ScFormulaCell::UpdateCompile( sal_Bool bForceIfNameInUse ) 1209 { 1210 if ( bForceIfNameInUse && !bCompile ) 1211 bCompile = pCode->HasNameOrColRowName(); 1212 if ( bCompile ) 1213 pCode->SetCodeError( 0 ); // make sure it will really be compiled 1214 CompileTokenArray(); 1215 } 1216 1217 // Referenzen transponieren - wird nur in Clipboard-Dokumenten aufgerufen 1218 1219 void ScFormulaCell::TransposeReference() 1220 { 1221 sal_Bool bFound = sal_False; 1222 pCode->Reset(); 1223 ScToken* t; 1224 while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL ) 1225 { 1226 ScSingleRefData& rRef1 = t->GetSingleRef(); 1227 if ( rRef1.IsColRel() && rRef1.IsRowRel() ) 1228 { 1229 sal_Bool bDouble = (t->GetType() == formula::svDoubleRef); 1230 ScSingleRefData& rRef2 = (bDouble ? t->GetDoubleRef().Ref2 : rRef1); 1231 if ( !bDouble || (rRef2.IsColRel() && rRef2.IsRowRel()) ) 1232 { 1233 sal_Int16 nTemp; 1234 1235 nTemp = rRef1.nRelCol; 1236 rRef1.nRelCol = static_cast<SCCOL>(rRef1.nRelRow); 1237 rRef1.nRelRow = static_cast<SCROW>(nTemp); 1238 1239 if ( bDouble ) 1240 { 1241 nTemp = rRef2.nRelCol; 1242 rRef2.nRelCol = static_cast<SCCOL>(rRef2.nRelRow); 1243 rRef2.nRelRow = static_cast<SCROW>(nTemp); 1244 } 1245 1246 bFound = sal_True; 1247 } 1248 } 1249 } 1250 1251 if (bFound) 1252 bCompile = sal_True; 1253 } 1254 1255 void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest, 1256 ScDocument* pUndoDoc ) 1257 { 1258 EndListeningTo( pDocument ); 1259 1260 ScAddress aOldPos = aPos; 1261 sal_Bool bPosChanged = sal_False; // ob diese Zelle bewegt wurde 1262 1263 ScRange aDestRange( rDest, ScAddress( 1264 static_cast<SCCOL>(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()), 1265 static_cast<SCROW>(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()), 1266 rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) ); 1267 if ( aDestRange.In( aOldPos ) ) 1268 { 1269 // Position zurueckrechnen 1270 SCsCOL nRelPosX = aOldPos.Col(); 1271 SCsROW nRelPosY = aOldPos.Row(); 1272 SCsTAB nRelPosZ = aOldPos.Tab(); 1273 ScRefUpdate::DoTranspose( nRelPosX, nRelPosY, nRelPosZ, pDocument, aDestRange, rSource.aStart ); 1274 aOldPos.Set( nRelPosX, nRelPosY, nRelPosZ ); 1275 bPosChanged = sal_True; 1276 } 1277 1278 ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL; 1279 sal_Bool bRefChanged = sal_False; 1280 ScToken* t; 1281 1282 ScRangeData* pShared = NULL; 1283 pCode->Reset(); 1284 while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL ) 1285 { 1286 if( t->GetOpCode() == ocName ) 1287 { 1288 ScRangeData* pName = pDocument->GetRangeName()->FindIndex( t->GetIndex() ); 1289 if (pName) 1290 { 1291 if (pName->IsModified()) 1292 bRefChanged = sal_True; 1293 if (pName->HasType(RT_SHAREDMOD)) 1294 pShared = pName; 1295 } 1296 } 1297 else if( t->GetType() != svIndex ) 1298 { 1299 t->CalcAbsIfRel( aOldPos ); 1300 sal_Bool bMod; 1301 { // own scope for SingleDoubleRefModifier dtor if SingleRef 1302 SingleDoubleRefModifier aMod( *t ); 1303 ScComplexRefData& rRef = aMod.Ref(); 1304 bMod = (ScRefUpdate::UpdateTranspose( pDocument, rSource, 1305 rDest, rRef ) != UR_NOTHING || bPosChanged); 1306 } 1307 if ( bMod ) 1308 { 1309 t->CalcRelFromAbs( aPos ); 1310 bRefChanged = sal_True; 1311 } 1312 } 1313 } 1314 1315 if (pShared) // Shared Formula gegen echte Formel austauschen 1316 { 1317 pDocument->RemoveFromFormulaTree( this ); // update formula count 1318 delete pCode; 1319 pCode = new ScTokenArray( *pShared->GetCode() ); 1320 bRefChanged = sal_True; 1321 pCode->Reset(); 1322 while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL ) 1323 { 1324 if( t->GetType() != svIndex ) 1325 { 1326 t->CalcAbsIfRel( aOldPos ); 1327 sal_Bool bMod; 1328 { // own scope for SingleDoubleRefModifier dtor if SingleRef 1329 SingleDoubleRefModifier aMod( *t ); 1330 ScComplexRefData& rRef = aMod.Ref(); 1331 bMod = (ScRefUpdate::UpdateTranspose( pDocument, rSource, 1332 rDest, rRef ) != UR_NOTHING || bPosChanged); 1333 } 1334 if ( bMod ) 1335 t->CalcRelFromAbs( aPos ); 1336 } 1337 } 1338 } 1339 1340 if (bRefChanged) 1341 { 1342 if (pUndoDoc) 1343 { 1344 ScFormulaCell* pFCell = new ScFormulaCell( pUndoDoc, aPos, pOld, 1345 eTempGrammar, cMatrixFlag); 1346 pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!) 1347 pUndoDoc->PutCell( aPos.Col(), aPos.Row(), aPos.Tab(), pFCell ); 1348 } 1349 1350 bCompile = sal_True; 1351 CompileTokenArray(); // ruft auch StartListeningTo 1352 SetDirty(); 1353 } 1354 else 1355 StartListeningTo( pDocument ); // Listener wie vorher 1356 1357 delete pOld; 1358 } 1359 1360 void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY ) 1361 { 1362 EndListeningTo( pDocument ); 1363 1364 sal_Bool bRefChanged = sal_False; 1365 ScToken* t; 1366 ScRangeData* pShared = NULL; 1367 1368 pCode->Reset(); 1369 while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL ) 1370 { 1371 if( t->GetOpCode() == ocName ) 1372 { 1373 ScRangeData* pName = pDocument->GetRangeName()->FindIndex( t->GetIndex() ); 1374 if (pName) 1375 { 1376 if (pName->IsModified()) 1377 bRefChanged = sal_True; 1378 if (pName->HasType(RT_SHAREDMOD)) 1379 pShared = pName; 1380 } 1381 } 1382 else if( t->GetType() != svIndex ) 1383 { 1384 t->CalcAbsIfRel( aPos ); 1385 sal_Bool bMod; 1386 { // own scope for SingleDoubleRefModifier dtor if SingleRef 1387 SingleDoubleRefModifier aMod( *t ); 1388 ScComplexRefData& rRef = aMod.Ref(); 1389 bMod = (ScRefUpdate::UpdateGrow( rArea,nGrowX,nGrowY, 1390 rRef ) != UR_NOTHING); 1391 } 1392 if ( bMod ) 1393 { 1394 t->CalcRelFromAbs( aPos ); 1395 bRefChanged = sal_True; 1396 } 1397 } 1398 } 1399 1400 if (pShared) // Shared Formula gegen echte Formel austauschen 1401 { 1402 pDocument->RemoveFromFormulaTree( this ); // update formula count 1403 delete pCode; 1404 pCode = new ScTokenArray( *pShared->GetCode() ); 1405 bRefChanged = sal_True; 1406 pCode->Reset(); 1407 while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL ) 1408 { 1409 if( t->GetType() != svIndex ) 1410 { 1411 t->CalcAbsIfRel( aPos ); 1412 sal_Bool bMod; 1413 { // own scope for SingleDoubleRefModifier dtor if SingleRef 1414 SingleDoubleRefModifier aMod( *t ); 1415 ScComplexRefData& rRef = aMod.Ref(); 1416 bMod = (ScRefUpdate::UpdateGrow( rArea,nGrowX,nGrowY, 1417 rRef ) != UR_NOTHING); 1418 } 1419 if ( bMod ) 1420 t->CalcRelFromAbs( aPos ); 1421 } 1422 } 1423 } 1424 1425 if (bRefChanged) 1426 { 1427 bCompile = sal_True; 1428 CompileTokenArray(); // ruft auch StartListeningTo 1429 SetDirty(); 1430 } 1431 else 1432 StartListeningTo( pDocument ); // Listener wie vorher 1433 } 1434 1435 sal_Bool lcl_IsRangeNameInUse(sal_uInt16 nIndex, ScTokenArray* pCode, ScRangeName* pNames) 1436 { 1437 for (FormulaToken* p = pCode->First(); p; p = pCode->Next()) 1438 { 1439 if (p->GetOpCode() == ocName) 1440 { 1441 if (p->GetIndex() == nIndex) 1442 return sal_True; 1443 else 1444 { 1445 // RangeData kann Null sein in bestimmten Excel-Dateien (#31168#) 1446 ScRangeData* pSubName = pNames->FindIndex(p->GetIndex()); 1447 if (pSubName && lcl_IsRangeNameInUse(nIndex, 1448 pSubName->GetCode(), pNames)) 1449 return sal_True; 1450 } 1451 } 1452 } 1453 return sal_False; 1454 } 1455 1456 sal_Bool ScFormulaCell::IsRangeNameInUse(sal_uInt16 nIndex) const 1457 { 1458 return lcl_IsRangeNameInUse( nIndex, pCode, pDocument->GetRangeName() ); 1459 } 1460 1461 void lcl_FindRangeNamesInUse(std::set<sal_uInt16>& rIndexes, ScTokenArray* pCode, ScRangeName* pNames) 1462 { 1463 for (FormulaToken* p = pCode->First(); p; p = pCode->Next()) 1464 { 1465 if (p->GetOpCode() == ocName) 1466 { 1467 sal_uInt16 nTokenIndex = p->GetIndex(); 1468 rIndexes.insert( nTokenIndex ); 1469 1470 ScRangeData* pSubName = pNames->FindIndex(p->GetIndex()); 1471 if (pSubName) 1472 lcl_FindRangeNamesInUse(rIndexes, pSubName->GetCode(), pNames); 1473 } 1474 } 1475 } 1476 1477 void ScFormulaCell::FindRangeNamesInUse(std::set<sal_uInt16>& rIndexes) const 1478 { 1479 lcl_FindRangeNamesInUse( rIndexes, pCode, pDocument->GetRangeName() ); 1480 } 1481 1482 void ScFormulaCell::ReplaceRangeNamesInUse( const ScRangeData::IndexMap& rMap ) 1483 { 1484 for( FormulaToken* p = pCode->First(); p; p = pCode->Next() ) 1485 { 1486 if( p->GetOpCode() == ocName ) 1487 { 1488 sal_uInt16 nIndex = p->GetIndex(); 1489 ScRangeData::IndexMap::const_iterator itr = rMap.find(nIndex); 1490 sal_uInt16 nNewIndex = itr == rMap.end() ? nIndex : itr->second; 1491 if ( nIndex != nNewIndex ) 1492 { 1493 p->SetIndex( nNewIndex ); 1494 bCompile = sal_True; 1495 } 1496 } 1497 } 1498 if( bCompile ) 1499 CompileTokenArray(); 1500 } 1501 1502 void ScFormulaCell::CompileDBFormula() 1503 { 1504 for( FormulaToken* p = pCode->First(); p; p = pCode->Next() ) 1505 { 1506 if ( p->GetOpCode() == ocDBArea 1507 || (p->GetOpCode() == ocName && p->GetIndex() >= SC_START_INDEX_DB_COLL) ) 1508 { 1509 bCompile = sal_True; 1510 CompileTokenArray(); 1511 SetDirty(); 1512 break; 1513 } 1514 } 1515 } 1516 1517 void ScFormulaCell::CompileDBFormula( sal_Bool bCreateFormulaString ) 1518 { 1519 // zwei Phasen, muessen (!) nacheinander aufgerufen werden: 1520 // 1. FormelString mit alten Namen erzeugen 1521 // 2. FormelString mit neuen Namen kompilieren 1522 if ( bCreateFormulaString ) 1523 { 1524 sal_Bool bRecompile = sal_False; 1525 pCode->Reset(); 1526 for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() ) 1527 { 1528 switch ( p->GetOpCode() ) 1529 { 1530 case ocBad: // DB-Bereich evtl. zugefuegt 1531 case ocColRowName: // #36762# falls Namensgleichheit 1532 case ocDBArea: // DB-Bereich 1533 bRecompile = sal_True; 1534 break; 1535 case ocName: 1536 if ( p->GetIndex() >= SC_START_INDEX_DB_COLL ) 1537 bRecompile = sal_True; // DB-Bereich 1538 break; 1539 default: 1540 ; // nothing 1541 } 1542 } 1543 if ( bRecompile ) 1544 { 1545 String aFormula; 1546 GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE); 1547 if ( GetMatrixFlag() != MM_NONE && aFormula.Len() ) 1548 { 1549 if ( aFormula.GetChar( aFormula.Len()-1 ) == '}' ) 1550 aFormula.Erase( aFormula.Len()-1 , 1 ); 1551 if ( aFormula.GetChar(0) == '{' ) 1552 aFormula.Erase( 0, 1 ); 1553 } 1554 EndListeningTo( pDocument ); 1555 pDocument->RemoveFromFormulaTree( this ); 1556 pCode->Clear(); 1557 SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE); 1558 } 1559 } 1560 else if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() ) 1561 { 1562 Compile( aResult.GetHybridFormula(), sal_False, eTempGrammar ); 1563 aResult.SetToken( NULL); 1564 SetDirty(); 1565 } 1566 } 1567 1568 void ScFormulaCell::CompileNameFormula( sal_Bool bCreateFormulaString ) 1569 { 1570 // zwei Phasen, muessen (!) nacheinander aufgerufen werden: 1571 // 1. FormelString mit alten RangeNames erzeugen 1572 // 2. FormelString mit neuen RangeNames kompilieren 1573 if ( bCreateFormulaString ) 1574 { 1575 sal_Bool bRecompile = sal_False; 1576 pCode->Reset(); 1577 for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() ) 1578 { 1579 switch ( p->GetOpCode() ) 1580 { 1581 case ocBad: // RangeName evtl. zugefuegt 1582 case ocColRowName: // #36762# falls Namensgleichheit 1583 bRecompile = sal_True; 1584 break; 1585 default: 1586 if ( p->GetType() == svIndex ) 1587 bRecompile = sal_True; // RangeName 1588 } 1589 } 1590 if ( bRecompile ) 1591 { 1592 String aFormula; 1593 GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE); 1594 if ( GetMatrixFlag() != MM_NONE && aFormula.Len() ) 1595 { 1596 if ( aFormula.GetChar( aFormula.Len()-1 ) == '}' ) 1597 aFormula.Erase( aFormula.Len()-1 , 1 ); 1598 if ( aFormula.GetChar(0) == '{' ) 1599 aFormula.Erase( 0, 1 ); 1600 } 1601 EndListeningTo( pDocument ); 1602 pDocument->RemoveFromFormulaTree( this ); 1603 pCode->Clear(); 1604 SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE); 1605 } 1606 } 1607 else if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() ) 1608 { 1609 Compile( aResult.GetHybridFormula(), sal_False, eTempGrammar ); 1610 aResult.SetToken( NULL); 1611 SetDirty(); 1612 } 1613 } 1614 1615 void ScFormulaCell::CompileColRowNameFormula() 1616 { 1617 pCode->Reset(); 1618 for ( FormulaToken* p = pCode->First(); p; p = pCode->Next() ) 1619 { 1620 if ( p->GetOpCode() == ocColRowName ) 1621 { 1622 bCompile = sal_True; 1623 CompileTokenArray(); 1624 SetDirty(); 1625 break; 1626 } 1627 } 1628 } 1629 1630 // ============================================================================ 1631 1632