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