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 // Was stored !bDirty but an accompanying matrix cell was bDirty? 448 // => we need to get the matrix. 449 if (!bDirty && cMatrixFlag == MM_FORMULA && !aResult.GetMatrix().Is()) 450 bDirty = sal_True; 451 if ( IsDirtyOrInTableOpDirty() ) 452 Interpret(); 453 } 454 return aResult.GetMatrix(); 455 } 456 457 sal_Bool ScFormulaCell::GetMatrixOrigin( ScAddress& rPos ) const 458 { 459 switch ( cMatrixFlag ) 460 { 461 case MM_FORMULA : 462 rPos = aPos; 463 return sal_True; 464 // break; 465 case MM_REFERENCE : 466 { 467 pCode->Reset(); 468 ScToken* t = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); 469 if( t ) 470 { 471 ScSingleRefData& rRef = t->GetSingleRef(); 472 rRef.CalcAbsIfRel( aPos ); 473 if ( rRef.Valid() ) 474 { 475 rPos.Set( rRef.nCol, rRef.nRow, rRef.nTab ); 476 return sal_True; 477 } 478 } 479 } 480 break; 481 } 482 return sal_False; 483 } 484 485 486 /* 487 Edge-Values: 488 489 8 490 4 16 491 2 492 493 innerhalb: 1 494 ausserhalb: 0 495 (reserviert: offen: 32) 496 */ 497 498 sal_uInt16 ScFormulaCell::GetMatrixEdge( ScAddress& rOrgPos ) 499 { 500 switch ( cMatrixFlag ) 501 { 502 case MM_FORMULA : 503 case MM_REFERENCE : 504 { 505 static SCCOL nC; 506 static SCROW nR; 507 ScAddress aOrg; 508 if ( !GetMatrixOrigin( aOrg ) ) 509 return 0; // dumm gelaufen.. 510 if ( aOrg != rOrgPos ) 511 { // erstes Mal oder andere Matrix als letztes Mal 512 rOrgPos = aOrg; 513 ScFormulaCell* pFCell; 514 if ( cMatrixFlag == MM_REFERENCE ) 515 pFCell = (ScFormulaCell*) pDocument->GetCell( aOrg ); 516 else 517 pFCell = this; // this MM_FORMULA 518 // this gibt's nur einmal, kein Vergleich auf pFCell==this 519 if ( pFCell && pFCell->GetCellType() == CELLTYPE_FORMULA 520 && pFCell->cMatrixFlag == MM_FORMULA ) 521 { 522 pFCell->GetMatColsRows( nC, nR ); 523 if ( nC == 0 || nR == 0 ) 524 { // aus altem Dokument geladen, neu erzeugen 525 nC = 1; 526 nR = 1; 527 ScAddress aTmpOrg; 528 ScBaseCell* pCell; 529 ScAddress aAdr( aOrg ); 530 aAdr.IncCol(); 531 sal_Bool bCont = sal_True; 532 do 533 { 534 pCell = pDocument->GetCell( aAdr ); 535 if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA 536 && ((ScFormulaCell*)pCell)->cMatrixFlag == MM_REFERENCE 537 && GetMatrixOrigin( aTmpOrg ) && aTmpOrg == aOrg ) 538 { 539 nC++; 540 aAdr.IncCol(); 541 } 542 else 543 bCont = sal_False; 544 } while ( bCont ); 545 aAdr = aOrg; 546 aAdr.IncRow(); 547 bCont = sal_True; 548 do 549 { 550 pCell = pDocument->GetCell( aAdr ); 551 if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA 552 && ((ScFormulaCell*)pCell)->cMatrixFlag == MM_REFERENCE 553 && GetMatrixOrigin( aTmpOrg ) && aTmpOrg == aOrg ) 554 { 555 nR++; 556 aAdr.IncRow(); 557 } 558 else 559 bCont = sal_False; 560 } while ( bCont ); 561 pFCell->SetMatColsRows( nC, nR ); 562 } 563 } 564 else 565 { 566 #ifdef DBG_UTIL 567 String aTmp; 568 ByteString aMsg( "broken Matrix, no MatFormula at origin, Pos: " ); 569 aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument ); 570 aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US ); 571 aMsg += ", MatOrg: "; 572 aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument ); 573 aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US ); 574 DBG_ERRORFILE( aMsg.GetBuffer() ); 575 #endif 576 return 0; // bad luck ... 577 } 578 } 579 // here we are, healthy and clean, somewhere in between 580 SCsCOL dC = aPos.Col() - aOrg.Col(); 581 SCsROW dR = aPos.Row() - aOrg.Row(); 582 sal_uInt16 nEdges = 0; 583 if ( dC >= 0 && dR >= 0 && dC < nC && dR < nR ) 584 { 585 if ( dC == 0 ) 586 nEdges |= 4; // linke Kante 587 if ( dC+1 == nC ) 588 nEdges |= 16; // rechte Kante 589 if ( dR == 0 ) 590 nEdges |= 8; // obere Kante 591 if ( dR+1 == nR ) 592 nEdges |= 2; // untere Kante 593 if ( !nEdges ) 594 nEdges = 1; // mittendrin 595 } 596 #ifdef DBG_UTIL 597 else 598 { 599 String aTmp; 600 ByteString aMsg( "broken Matrix, Pos: " ); 601 aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument ); 602 aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US ); 603 aMsg += ", MatOrg: "; 604 aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument ); 605 aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US ); 606 aMsg += ", MatCols: "; 607 aMsg += ByteString::CreateFromInt32( nC ); 608 aMsg += ", MatRows: "; 609 aMsg += ByteString::CreateFromInt32( nR ); 610 aMsg += ", DiffCols: "; 611 aMsg += ByteString::CreateFromInt32( dC ); 612 aMsg += ", DiffRows: "; 613 aMsg += ByteString::CreateFromInt32( dR ); 614 DBG_ERRORFILE( aMsg.GetBuffer() ); 615 } 616 #endif 617 return nEdges; 618 // break; 619 } 620 default: 621 return 0; 622 } 623 } 624 625 sal_uInt16 ScFormulaCell::GetErrCode() 626 { 627 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) 628 Interpret(); 629 /* FIXME: If ScTokenArray::SetCodeError() was really only for code errors 630 * and not also abused for signaling other error conditions we could bail 631 * out even before attempting to interpret broken code. */ 632 sal_uInt16 nErr = pCode->GetCodeError(); 633 if (nErr) 634 return nErr; 635 return aResult.GetResultError(); 636 } 637 638 sal_uInt16 ScFormulaCell::GetRawError() 639 { 640 sal_uInt16 nErr = pCode->GetCodeError(); 641 if (nErr) 642 return nErr; 643 return aResult.GetResultError(); 644 } 645 646 sal_Bool ScFormulaCell::HasOneReference( ScRange& r ) const 647 { 648 pCode->Reset(); 649 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); 650 if( p && !pCode->GetNextReferenceRPN() ) // nur eine! 651 { 652 p->CalcAbsIfRel( aPos ); 653 SingleDoubleRefProvider aProv( *p ); 654 r.aStart.Set( aProv.Ref1.nCol, 655 aProv.Ref1.nRow, 656 aProv.Ref1.nTab ); 657 r.aEnd.Set( aProv.Ref2.nCol, 658 aProv.Ref2.nRow, 659 aProv.Ref2.nTab ); 660 return sal_True; 661 } 662 else 663 return sal_False; 664 } 665 666 bool 667 ScFormulaCell::HasRefListExpressibleAsOneReference(ScRange& rRange) const 668 { 669 /* If there appears just one reference in the formula, it's the same 670 as HasOneReference(). If there are more of them, they can denote 671 one range if they are (sole) arguments of one function. 672 Union of these references must form one range and their 673 intersection must be empty set. 674 */ 675 676 // Detect the simple case of exactly one reference in advance without all 677 // overhead. 678 // #i107741# Doing so actually makes outlines using SUBTOTAL(x;reference) 679 // work again, where the function does not have only references. 680 if (HasOneReference( rRange)) 681 return true; 682 683 pCode->Reset(); 684 // Get first reference, if any 685 ScToken* const pFirstReference( 686 dynamic_cast<ScToken*>(pCode->GetNextReferenceRPN())); 687 if (pFirstReference) 688 { 689 // Collect all consecutive references, starting by the one 690 // already found 691 std::deque<ScToken*> aReferences; 692 aReferences.push_back(pFirstReference); 693 FormulaToken* pToken(pCode->NextRPN()); 694 FormulaToken* pFunction(0); 695 while (pToken) 696 { 697 if (lcl_isReference(*pToken)) 698 { 699 aReferences.push_back(dynamic_cast<ScToken*>(pToken)); 700 pToken = pCode->NextRPN(); 701 } 702 else 703 { 704 if (pToken->IsFunction()) 705 { 706 pFunction = pToken; 707 } 708 break; 709 } 710 } 711 if (pFunction && !pCode->GetNextReferenceRPN() 712 && (pFunction->GetParamCount() == aReferences.size())) 713 { 714 return lcl_refListFormsOneRange(aPos, aReferences, rRange); 715 } 716 } 717 return false; 718 } 719 720 sal_Bool ScFormulaCell::HasRelNameReference() const 721 { 722 pCode->Reset(); 723 ScToken* t; 724 while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceRPN()) ) != NULL ) 725 { 726 if ( t->GetSingleRef().IsRelName() || 727 (t->GetType() == formula::svDoubleRef && 728 t->GetDoubleRef().Ref2.IsRelName()) ) 729 return sal_True; 730 } 731 return sal_False; 732 } 733 734 sal_Bool ScFormulaCell::HasColRowName() const 735 { 736 pCode->Reset(); 737 return (pCode->GetNextColRowName() != NULL); 738 } 739 740 void ScFormulaCell::UpdateReference(UpdateRefMode eUpdateRefMode, 741 const ScRange& r, 742 SCsCOL nDx, SCsROW nDy, SCsTAB nDz, 743 ScDocument* pUndoDoc, const ScAddress* pUndoCellPos ) 744 { 745 SCCOL nCol1 = r.aStart.Col(); 746 SCROW nRow1 = r.aStart.Row(); 747 SCTAB nTab1 = r.aStart.Tab(); 748 SCCOL nCol2 = r.aEnd.Col(); 749 SCROW nRow2 = r.aEnd.Row(); 750 SCTAB nTab2 = r.aEnd.Tab(); 751 SCCOL nCol = aPos.Col(); 752 SCROW nRow = aPos.Row(); 753 SCTAB nTab = aPos.Tab(); 754 ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc 755 if ( pUndoCellPos ) 756 aUndoPos = *pUndoCellPos; 757 ScAddress aOldPos( aPos ); 758 // sal_Bool bPosChanged = sal_False; // ob diese Zelle bewegt wurde 759 sal_Bool bIsInsert = sal_False; 760 if (eUpdateRefMode == URM_INSDEL) 761 { 762 bIsInsert = (nDx >= 0 && nDy >= 0 && nDz >= 0); 763 if ( nDx && nRow >= nRow1 && nRow <= nRow2 && 764 nTab >= nTab1 && nTab <= nTab2 ) 765 { 766 if (nCol >= nCol1) 767 { 768 nCol = sal::static_int_cast<SCCOL>( nCol + nDx ); 769 if ((SCsCOL) nCol < 0) 770 nCol = 0; 771 else if ( nCol > MAXCOL ) 772 nCol = MAXCOL; 773 aPos.SetCol( nCol ); 774 // bPosChanged = sal_True; 775 } 776 } 777 if ( nDy && nCol >= nCol1 && nCol <= nCol2 && 778 nTab >= nTab1 && nTab <= nTab2 ) 779 { 780 if (nRow >= nRow1) 781 { 782 nRow = sal::static_int_cast<SCROW>( nRow + nDy ); 783 if ((SCsROW) nRow < 0) 784 nRow = 0; 785 else if ( nRow > MAXROW ) 786 nRow = MAXROW; 787 aPos.SetRow( nRow ); 788 // bPosChanged = sal_True; 789 } 790 } 791 if ( nDz && nCol >= nCol1 && nCol <= nCol2 && 792 nRow >= nRow1 && nRow <= nRow2 ) 793 { 794 if (nTab >= nTab1) 795 { 796 SCTAB nMaxTab = pDocument->GetTableCount() - 1; 797 nTab = sal::static_int_cast<SCTAB>( nTab + nDz ); 798 if ((SCsTAB) nTab < 0) 799 nTab = 0; 800 else if ( nTab > nMaxTab ) 801 nTab = nMaxTab; 802 aPos.SetTab( nTab ); 803 // bPosChanged = sal_True; 804 } 805 } 806 } 807 else if ( r.In( aPos ) ) 808 { 809 aOldPos.Set( nCol - nDx, nRow - nDy, nTab - nDz ); 810 // bPosChanged = sal_True; 811 } 812 813 sal_Bool bHasRefs = sal_False; 814 sal_Bool bHasColRowNames = sal_False; 815 sal_Bool bOnRefMove = sal_False; 816 if ( !pDocument->IsClipOrUndo() ) 817 { 818 pCode->Reset(); 819 bHasRefs = (pCode->GetNextReferenceRPN() != NULL); 820 if ( !bHasRefs || eUpdateRefMode == URM_COPY ) 821 { 822 pCode->Reset(); 823 bHasColRowNames = (pCode->GetNextColRowName() != NULL); 824 bHasRefs = bHasRefs || bHasColRowNames; 825 } 826 bOnRefMove = pCode->IsRecalcModeOnRefMove(); 827 } 828 if( bHasRefs || bOnRefMove ) 829 { 830 ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL; 831 sal_Bool bValChanged; 832 ScRangeData* pRangeData; 833 sal_Bool bRangeModified; // any range, not only shared formula 834 sal_Bool bRefSizeChanged; 835 if ( bHasRefs ) 836 { 837 ScCompiler aComp(pDocument, aPos, *pCode); 838 aComp.SetGrammar(pDocument->GetGrammar()); 839 pRangeData = aComp.UpdateReference(eUpdateRefMode, aOldPos, r, 840 nDx, nDy, nDz, 841 bValChanged, bRefSizeChanged); 842 bRangeModified = aComp.HasModifiedRange(); 843 } 844 else 845 { 846 bValChanged = sal_False; 847 pRangeData = NULL; 848 bRangeModified = sal_False; 849 bRefSizeChanged = sal_False; 850 } 851 if ( bOnRefMove ) 852 bOnRefMove = (bValChanged || (aPos != aOldPos)); 853 // Cell may reference itself, e.g. ocColumn, ocRow without parameter 854 855 sal_Bool bColRowNameCompile, bHasRelName, bNewListening, bInDeleteUndo; 856 if ( bHasRefs ) 857 { 858 // Upon Insert ColRowNames have to be recompiled in case the 859 // insertion occurs right in front of the range. 860 bColRowNameCompile = 861 (eUpdateRefMode == URM_INSDEL && (nDx > 0 || nDy > 0)); 862 if ( bColRowNameCompile ) 863 { 864 bColRowNameCompile = sal_False; 865 ScToken* t; 866 ScRangePairList* pColList = pDocument->GetColNameRanges(); 867 ScRangePairList* pRowList = pDocument->GetRowNameRanges(); 868 pCode->Reset(); 869 while ( !bColRowNameCompile && (t = static_cast<ScToken*>(pCode->GetNextColRowName())) != NULL ) 870 { 871 ScSingleRefData& rRef = t->GetSingleRef(); 872 if ( nDy > 0 && rRef.IsColRel() ) 873 { // ColName 874 rRef.CalcAbsIfRel( aPos ); 875 ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab ); 876 ScRangePair* pR = pColList->Find( aAdr ); 877 if ( pR ) 878 { // definiert 879 if ( pR->GetRange(1).aStart.Row() == nRow1 ) 880 bColRowNameCompile = sal_True; 881 } 882 else 883 { // on the fly 884 if ( rRef.nRow + 1 == nRow1 ) 885 bColRowNameCompile = sal_True; 886 } 887 } 888 if ( nDx > 0 && rRef.IsRowRel() ) 889 { // RowName 890 rRef.CalcAbsIfRel( aPos ); 891 ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab ); 892 ScRangePair* pR = pRowList->Find( aAdr ); 893 if ( pR ) 894 { // definiert 895 if ( pR->GetRange(1).aStart.Col() == nCol1 ) 896 bColRowNameCompile = sal_True; 897 } 898 else 899 { // on the fly 900 if ( rRef.nCol + 1 == nCol1 ) 901 bColRowNameCompile = sal_True; 902 } 903 } 904 } 905 } 906 else if ( eUpdateRefMode == URM_MOVE ) 907 { // bei Move/D&D neu kompilieren wenn ColRowName verschoben wurde 908 // oder diese Zelle auf einen zeigt und verschoben wurde 909 bColRowNameCompile = bCompile; // evtl. aus Copy-ctor 910 if ( !bColRowNameCompile ) 911 { 912 sal_Bool bMoved = (aPos != aOldPos); 913 pCode->Reset(); 914 ScToken* t = static_cast<ScToken*>(pCode->GetNextColRowName()); 915 if ( t && bMoved ) 916 bColRowNameCompile = sal_True; 917 while ( t && !bColRowNameCompile ) 918 { 919 ScSingleRefData& rRef = t->GetSingleRef(); 920 rRef.CalcAbsIfRel( aPos ); 921 if ( rRef.Valid() ) 922 { 923 ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab ); 924 if ( r.In( aAdr ) ) 925 bColRowNameCompile = sal_True; 926 } 927 t = static_cast<ScToken*>(pCode->GetNextColRowName()); 928 } 929 } 930 } 931 else if ( eUpdateRefMode == URM_COPY && bHasColRowNames && bValChanged ) 932 { 933 bColRowNameCompile = sal_True; 934 } 935 ScChangeTrack* pChangeTrack = pDocument->GetChangeTrack(); 936 if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() ) 937 bInDeleteUndo = sal_True; 938 else 939 bInDeleteUndo = sal_False; 940 // RelNameRefs are always moved 941 bHasRelName = HasRelNameReference(); 942 // Reference changed and new listening needed? 943 // Except in Insert/Delete without specialties. 944 bNewListening = (bRangeModified || pRangeData || bColRowNameCompile 945 || (bValChanged && (eUpdateRefMode != URM_INSDEL || 946 bInDeleteUndo || bRefSizeChanged)) || 947 (bHasRelName && eUpdateRefMode != URM_COPY)) 948 // #i36299# Don't duplicate action during cut&paste / drag&drop 949 // on a cell in the range moved, start/end listeners is done 950 // via ScDocument::DeleteArea() and ScDocument::CopyFromClip(). 951 && !(eUpdateRefMode == URM_MOVE && 952 pDocument->IsInsertingFromOtherDoc() && r.In(aPos)); 953 if ( bNewListening ) 954 EndListeningTo( pDocument, pOld, aOldPos ); 955 } 956 else 957 { 958 bColRowNameCompile = bHasRelName = bNewListening = bInDeleteUndo = 959 sal_False; 960 } 961 962 sal_Bool bNeedDirty; 963 // NeedDirty bei Aenderungen ausser Copy und Move/Insert ohne RelNames 964 if ( bRangeModified || pRangeData || bColRowNameCompile || 965 (bValChanged && eUpdateRefMode != URM_COPY && 966 (eUpdateRefMode != URM_MOVE || bHasRelName) && 967 (!bIsInsert || bHasRelName || bInDeleteUndo || 968 bRefSizeChanged)) || bOnRefMove) 969 bNeedDirty = sal_True; 970 else 971 bNeedDirty = sal_False; 972 if (pUndoDoc && (bValChanged || pRangeData || bOnRefMove)) 973 { 974 // Copy the cell to aUndoPos, which is its current position in the document, 975 // so this works when UpdateReference is called before moving the cells 976 // (InsertCells/DeleteCells - aPos is changed above) as well as when UpdateReference 977 // is called after moving the cells (MoveBlock/PasteFromClip - aOldPos is changed). 978 979 // If there is already a formula cell in the undo document, don't overwrite it, 980 // the first (oldest) is the important cell. 981 if ( pUndoDoc->GetCellType( aUndoPos ) != CELLTYPE_FORMULA ) 982 { 983 ScFormulaCell* pFCell = new ScFormulaCell( pUndoDoc, aUndoPos, 984 pOld, eTempGrammar, cMatrixFlag ); 985 pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!) 986 pUndoDoc->PutCell( aUndoPos, pFCell ); 987 } 988 } 989 // #i116833# If the formula is changed, always invalidate the stream (even if the result is the same). 990 // If the formula is moved, the change is recognized separately. 991 if (bValChanged && pDocument->IsStreamValid(aPos.Tab())) 992 pDocument->SetStreamValid(aPos.Tab(), sal_False); 993 bValChanged = sal_False; 994 if ( pRangeData ) 995 { // Replace shared formula with own formula 996 pDocument->RemoveFromFormulaTree( this ); // update formula count 997 delete pCode; 998 pCode = pRangeData->GetCode()->Clone(); 999 // #i18937# #i110008# call MoveRelWrap, but with the old position 1000 ScCompiler::MoveRelWrap(*pCode, pDocument, aOldPos, pRangeData->GetMaxCol(), pRangeData->GetMaxRow()); 1001 ScCompiler aComp2(pDocument, aPos, *pCode); 1002 aComp2.SetGrammar(pDocument->GetGrammar()); 1003 aComp2.UpdateSharedFormulaReference( eUpdateRefMode, aOldPos, r, 1004 nDx, nDy, nDz ); 1005 bValChanged = sal_True; 1006 bNeedDirty = sal_True; 1007 } 1008 if ( ( bCompile = (bCompile || bValChanged || bRangeModified || bColRowNameCompile) ) != 0 ) 1009 { 1010 CompileTokenArray( bNewListening ); // kein Listening 1011 bNeedDirty = sal_True; 1012 } 1013 if ( !bInDeleteUndo ) 1014 { // In ChangeTrack Delete-Reject listeners are established in 1015 // InsertCol/InsertRow 1016 if ( bNewListening ) 1017 { 1018 if ( eUpdateRefMode == URM_INSDEL ) 1019 { 1020 // Inserts/Deletes re-establish listeners after all 1021 // UpdateReference calls. 1022 // All replaced shared formula listeners have to be 1023 // established after an Insert or Delete. Do nothing here. 1024 SetNeedsListening( sal_True); 1025 } 1026 else 1027 StartListeningTo( pDocument ); 1028 } 1029 } 1030 if ( bNeedDirty && (!(eUpdateRefMode == URM_INSDEL && bHasRelName) || pRangeData) ) 1031 { // Referenzen abgeschnitten, ungueltig o.ae.? 1032 sal_Bool bOldAutoCalc = pDocument->GetAutoCalc(); 1033 // kein Interpret in SubMinimalRecalc wegen evtl. falscher Referenzen 1034 pDocument->SetAutoCalc( sal_False ); 1035 SetDirty(); 1036 pDocument->SetAutoCalc( bOldAutoCalc ); 1037 } 1038 1039 delete pOld; 1040 } 1041 } 1042 1043 void ScFormulaCell::UpdateInsertTab(SCTAB nTable) 1044 { 1045 sal_Bool bPosChanged = ( aPos.Tab() >= nTable ? sal_True : sal_False ); 1046 pCode->Reset(); 1047 if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() ) 1048 { 1049 EndListeningTo( pDocument ); 1050 // IncTab _nach_ EndListeningTo und _vor_ Compiler UpdateInsertTab ! 1051 if ( bPosChanged ) 1052 aPos.IncTab(); 1053 ScRangeData* pRangeData; 1054 ScCompiler aComp(pDocument, aPos, *pCode); 1055 aComp.SetGrammar(pDocument->GetGrammar()); 1056 pRangeData = aComp.UpdateInsertTab( nTable, sal_False ); 1057 if (pRangeData) // Shared Formula gegen echte Formel 1058 { // austauschen 1059 sal_Bool bRefChanged; 1060 pDocument->RemoveFromFormulaTree( this ); // update formula count 1061 delete pCode; 1062 pCode = new ScTokenArray( *pRangeData->GetCode() ); 1063 ScCompiler aComp2(pDocument, aPos, *pCode); 1064 aComp2.SetGrammar(pDocument->GetGrammar()); 1065 aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow()); 1066 aComp2.UpdateInsertTab( nTable, sal_False ); 1067 // If the shared formula contained a named range/formula containing 1068 // an absolute reference to a sheet, those have to be readjusted. 1069 aComp2.UpdateDeleteTab( nTable, sal_False, sal_True, bRefChanged ); 1070 bCompile = sal_True; 1071 } 1072 // kein StartListeningTo weil pTab[nTab] noch nicht existiert! 1073 } 1074 else if ( bPosChanged ) 1075 aPos.IncTab(); 1076 } 1077 1078 sal_Bool ScFormulaCell::UpdateDeleteTab(SCTAB nTable, sal_Bool bIsMove) 1079 { 1080 sal_Bool bRefChanged = sal_False; 1081 sal_Bool bPosChanged = ( aPos.Tab() > nTable ? sal_True : sal_False ); 1082 pCode->Reset(); 1083 if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() ) 1084 { 1085 EndListeningTo( pDocument ); 1086 // IncTab _nach_ EndListeningTo und _vor_ Compiler UpdateDeleteTab ! 1087 if ( bPosChanged ) 1088 aPos.IncTab(-1); 1089 ScRangeData* pRangeData; 1090 ScCompiler aComp(pDocument, aPos, *pCode); 1091 aComp.SetGrammar(pDocument->GetGrammar()); 1092 pRangeData = aComp.UpdateDeleteTab(nTable, bIsMove, sal_False, bRefChanged); 1093 if (pRangeData) // Shared Formula gegen echte Formel 1094 { // austauschen 1095 pDocument->RemoveFromFormulaTree( this ); // update formula count 1096 delete pCode; 1097 pCode = pRangeData->GetCode()->Clone(); 1098 ScCompiler aComp2(pDocument, aPos, *pCode); 1099 aComp2.SetGrammar(pDocument->GetGrammar()); 1100 aComp2.CompileTokenArray(); 1101 aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow()); 1102 aComp2.UpdateDeleteTab( nTable, sal_False, sal_False, bRefChanged ); 1103 // If the shared formula contained a named range/formula containing 1104 // an absolute reference to a sheet, those have to be readjusted. 1105 aComp2.UpdateInsertTab( nTable,sal_True ); 1106 // bRefChanged kann beim letzten UpdateDeleteTab zurueckgesetzt worden sein 1107 bRefChanged = sal_True; 1108 bCompile = sal_True; 1109 } 1110 // kein StartListeningTo weil pTab[nTab] noch nicht korrekt! 1111 } 1112 else if ( bPosChanged ) 1113 aPos.IncTab(-1); 1114 1115 return bRefChanged; 1116 } 1117 1118 void ScFormulaCell::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos, SCTAB nTabNo ) 1119 { 1120 pCode->Reset(); 1121 if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() ) 1122 { 1123 EndListeningTo( pDocument ); 1124 // SetTab _nach_ EndListeningTo und _vor_ Compiler UpdateMoveTab ! 1125 aPos.SetTab( nTabNo ); 1126 ScRangeData* pRangeData; 1127 ScCompiler aComp(pDocument, aPos, *pCode); 1128 aComp.SetGrammar(pDocument->GetGrammar()); 1129 pRangeData = aComp.UpdateMoveTab( nOldPos, nNewPos, sal_False ); 1130 if (pRangeData) // Shared Formula gegen echte Formel 1131 { // austauschen 1132 pDocument->RemoveFromFormulaTree( this ); // update formula count 1133 delete pCode; 1134 pCode = pRangeData->GetCode()->Clone(); 1135 ScCompiler aComp2(pDocument, aPos, *pCode); 1136 aComp2.SetGrammar(pDocument->GetGrammar()); 1137 aComp2.CompileTokenArray(); 1138 aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow()); 1139 aComp2.UpdateMoveTab( nOldPos, nNewPos, sal_True ); 1140 bCompile = sal_True; 1141 } 1142 // kein StartListeningTo weil pTab[nTab] noch nicht korrekt! 1143 } 1144 else 1145 aPos.SetTab( nTabNo ); 1146 } 1147 1148 void ScFormulaCell::UpdateInsertTabAbs(SCTAB nTable) 1149 { 1150 if( !pDocument->IsClipOrUndo() ) 1151 { 1152 pCode->Reset(); 1153 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); 1154 while( p ) 1155 { 1156 ScSingleRefData& rRef1 = p->GetSingleRef(); 1157 if( !rRef1.IsTabRel() && (SCsTAB) nTable <= rRef1.nTab ) 1158 rRef1.nTab++; 1159 if( p->GetType() == formula::svDoubleRef ) 1160 { 1161 ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2; 1162 if( !rRef2.IsTabRel() && (SCsTAB) nTable <= rRef2.nTab ) 1163 rRef2.nTab++; 1164 } 1165 p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); 1166 } 1167 } 1168 } 1169 1170 sal_Bool ScFormulaCell::TestTabRefAbs(SCTAB nTable) 1171 { 1172 sal_Bool bRet = sal_False; 1173 if( !pDocument->IsClipOrUndo() ) 1174 { 1175 pCode->Reset(); 1176 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); 1177 while( p ) 1178 { 1179 ScSingleRefData& rRef1 = p->GetSingleRef(); 1180 if( !rRef1.IsTabRel() ) 1181 { 1182 if( (SCsTAB) nTable != rRef1.nTab ) 1183 bRet = sal_True; 1184 else if (nTable != aPos.Tab()) 1185 rRef1.nTab = aPos.Tab(); 1186 } 1187 if( p->GetType() == formula::svDoubleRef ) 1188 { 1189 ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2; 1190 if( !rRef2.IsTabRel() ) 1191 { 1192 if( (SCsTAB) nTable != rRef2.nTab ) 1193 bRet = sal_True; 1194 else if (nTable != aPos.Tab()) 1195 rRef2.nTab = aPos.Tab(); 1196 } 1197 } 1198 p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); 1199 } 1200 } 1201 return bRet; 1202 } 1203 1204 void ScFormulaCell::UpdateCompile( sal_Bool bForceIfNameInUse ) 1205 { 1206 if ( bForceIfNameInUse && !bCompile ) 1207 bCompile = pCode->HasNameOrColRowName(); 1208 if ( bCompile ) 1209 pCode->SetCodeError( 0 ); // make sure it will really be compiled 1210 CompileTokenArray(); 1211 } 1212 1213 // Referenzen transponieren - wird nur in Clipboard-Dokumenten aufgerufen 1214 1215 void ScFormulaCell::TransposeReference() 1216 { 1217 sal_Bool bFound = sal_False; 1218 pCode->Reset(); 1219 ScToken* t; 1220 while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL ) 1221 { 1222 ScSingleRefData& rRef1 = t->GetSingleRef(); 1223 if ( rRef1.IsColRel() && rRef1.IsRowRel() ) 1224 { 1225 sal_Bool bDouble = (t->GetType() == formula::svDoubleRef); 1226 ScSingleRefData& rRef2 = (bDouble ? t->GetDoubleRef().Ref2 : rRef1); 1227 if ( !bDouble || (rRef2.IsColRel() && rRef2.IsRowRel()) ) 1228 { 1229 sal_Int16 nTemp; 1230 1231 nTemp = rRef1.nRelCol; 1232 rRef1.nRelCol = static_cast<SCCOL>(rRef1.nRelRow); 1233 rRef1.nRelRow = static_cast<SCROW>(nTemp); 1234 1235 if ( bDouble ) 1236 { 1237 nTemp = rRef2.nRelCol; 1238 rRef2.nRelCol = static_cast<SCCOL>(rRef2.nRelRow); 1239 rRef2.nRelRow = static_cast<SCROW>(nTemp); 1240 } 1241 1242 bFound = sal_True; 1243 } 1244 } 1245 } 1246 1247 if (bFound) 1248 bCompile = sal_True; 1249 } 1250 1251 void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest, 1252 ScDocument* pUndoDoc ) 1253 { 1254 EndListeningTo( pDocument ); 1255 1256 ScAddress aOldPos = aPos; 1257 sal_Bool bPosChanged = sal_False; // ob diese Zelle bewegt wurde 1258 1259 ScRange aDestRange( rDest, ScAddress( 1260 static_cast<SCCOL>(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()), 1261 static_cast<SCROW>(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()), 1262 rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) ); 1263 if ( aDestRange.In( aOldPos ) ) 1264 { 1265 // Position zurueckrechnen 1266 SCsCOL nRelPosX = aOldPos.Col(); 1267 SCsROW nRelPosY = aOldPos.Row(); 1268 SCsTAB nRelPosZ = aOldPos.Tab(); 1269 ScRefUpdate::DoTranspose( nRelPosX, nRelPosY, nRelPosZ, pDocument, aDestRange, rSource.aStart ); 1270 aOldPos.Set( nRelPosX, nRelPosY, nRelPosZ ); 1271 bPosChanged = sal_True; 1272 } 1273 1274 ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL; 1275 sal_Bool bRefChanged = sal_False; 1276 ScToken* t; 1277 1278 ScRangeData* pShared = NULL; 1279 pCode->Reset(); 1280 while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL ) 1281 { 1282 if( t->GetOpCode() == ocName ) 1283 { 1284 ScRangeData* pName = pDocument->GetRangeName()->FindIndex( t->GetIndex() ); 1285 if (pName) 1286 { 1287 if (pName->IsModified()) 1288 bRefChanged = sal_True; 1289 if (pName->HasType(RT_SHAREDMOD)) 1290 pShared = pName; 1291 } 1292 } 1293 else if( t->GetType() != svIndex ) 1294 { 1295 t->CalcAbsIfRel( aOldPos ); 1296 sal_Bool bMod; 1297 { // own scope for SingleDoubleRefModifier dtor if SingleRef 1298 SingleDoubleRefModifier aMod( *t ); 1299 ScComplexRefData& rRef = aMod.Ref(); 1300 bMod = (ScRefUpdate::UpdateTranspose( pDocument, rSource, 1301 rDest, rRef ) != UR_NOTHING || bPosChanged); 1302 } 1303 if ( bMod ) 1304 { 1305 t->CalcRelFromAbs( aPos ); 1306 bRefChanged = sal_True; 1307 } 1308 } 1309 } 1310 1311 if (pShared) // Shared Formula gegen echte Formel austauschen 1312 { 1313 pDocument->RemoveFromFormulaTree( this ); // update formula count 1314 delete pCode; 1315 pCode = new ScTokenArray( *pShared->GetCode() ); 1316 bRefChanged = sal_True; 1317 pCode->Reset(); 1318 while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL ) 1319 { 1320 if( t->GetType() != svIndex ) 1321 { 1322 t->CalcAbsIfRel( aOldPos ); 1323 sal_Bool bMod; 1324 { // own scope for SingleDoubleRefModifier dtor if SingleRef 1325 SingleDoubleRefModifier aMod( *t ); 1326 ScComplexRefData& rRef = aMod.Ref(); 1327 bMod = (ScRefUpdate::UpdateTranspose( pDocument, rSource, 1328 rDest, rRef ) != UR_NOTHING || bPosChanged); 1329 } 1330 if ( bMod ) 1331 t->CalcRelFromAbs( aPos ); 1332 } 1333 } 1334 } 1335 1336 if (bRefChanged) 1337 { 1338 if (pUndoDoc) 1339 { 1340 ScFormulaCell* pFCell = new ScFormulaCell( pUndoDoc, aPos, pOld, 1341 eTempGrammar, cMatrixFlag); 1342 pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!) 1343 pUndoDoc->PutCell( aPos.Col(), aPos.Row(), aPos.Tab(), pFCell ); 1344 } 1345 1346 bCompile = sal_True; 1347 CompileTokenArray(); // ruft auch StartListeningTo 1348 SetDirty(); 1349 } 1350 else 1351 StartListeningTo( pDocument ); // Listener wie vorher 1352 1353 delete pOld; 1354 } 1355 1356 void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY ) 1357 { 1358 EndListeningTo( pDocument ); 1359 1360 sal_Bool bRefChanged = sal_False; 1361 ScToken* t; 1362 ScRangeData* pShared = NULL; 1363 1364 pCode->Reset(); 1365 while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL ) 1366 { 1367 if( t->GetOpCode() == ocName ) 1368 { 1369 ScRangeData* pName = pDocument->GetRangeName()->FindIndex( t->GetIndex() ); 1370 if (pName) 1371 { 1372 if (pName->IsModified()) 1373 bRefChanged = sal_True; 1374 if (pName->HasType(RT_SHAREDMOD)) 1375 pShared = pName; 1376 } 1377 } 1378 else if( t->GetType() != svIndex ) 1379 { 1380 t->CalcAbsIfRel( aPos ); 1381 sal_Bool bMod; 1382 { // own scope for SingleDoubleRefModifier dtor if SingleRef 1383 SingleDoubleRefModifier aMod( *t ); 1384 ScComplexRefData& rRef = aMod.Ref(); 1385 bMod = (ScRefUpdate::UpdateGrow( rArea,nGrowX,nGrowY, 1386 rRef ) != UR_NOTHING); 1387 } 1388 if ( bMod ) 1389 { 1390 t->CalcRelFromAbs( aPos ); 1391 bRefChanged = sal_True; 1392 } 1393 } 1394 } 1395 1396 if (pShared) // Shared Formula gegen echte Formel austauschen 1397 { 1398 pDocument->RemoveFromFormulaTree( this ); // update formula count 1399 delete pCode; 1400 pCode = new ScTokenArray( *pShared->GetCode() ); 1401 bRefChanged = sal_True; 1402 pCode->Reset(); 1403 while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL ) 1404 { 1405 if( t->GetType() != svIndex ) 1406 { 1407 t->CalcAbsIfRel( aPos ); 1408 sal_Bool bMod; 1409 { // own scope for SingleDoubleRefModifier dtor if SingleRef 1410 SingleDoubleRefModifier aMod( *t ); 1411 ScComplexRefData& rRef = aMod.Ref(); 1412 bMod = (ScRefUpdate::UpdateGrow( rArea,nGrowX,nGrowY, 1413 rRef ) != UR_NOTHING); 1414 } 1415 if ( bMod ) 1416 t->CalcRelFromAbs( aPos ); 1417 } 1418 } 1419 } 1420 1421 if (bRefChanged) 1422 { 1423 bCompile = sal_True; 1424 CompileTokenArray(); // ruft auch StartListeningTo 1425 SetDirty(); 1426 } 1427 else 1428 StartListeningTo( pDocument ); // Listener wie vorher 1429 } 1430 1431 sal_Bool lcl_IsRangeNameInUse(sal_uInt16 nIndex, ScTokenArray* pCode, ScRangeName* pNames) 1432 { 1433 for (FormulaToken* p = pCode->First(); p; p = pCode->Next()) 1434 { 1435 if (p->GetOpCode() == ocName) 1436 { 1437 if (p->GetIndex() == nIndex) 1438 return sal_True; 1439 else 1440 { 1441 // RangeData kann Null sein in bestimmten Excel-Dateien (#31168#) 1442 ScRangeData* pSubName = pNames->FindIndex(p->GetIndex()); 1443 if (pSubName && lcl_IsRangeNameInUse(nIndex, 1444 pSubName->GetCode(), pNames)) 1445 return sal_True; 1446 } 1447 } 1448 } 1449 return sal_False; 1450 } 1451 1452 sal_Bool ScFormulaCell::IsRangeNameInUse(sal_uInt16 nIndex) const 1453 { 1454 return lcl_IsRangeNameInUse( nIndex, pCode, pDocument->GetRangeName() ); 1455 } 1456 1457 void lcl_FindRangeNamesInUse(std::set<sal_uInt16>& rIndexes, ScTokenArray* pCode, ScRangeName* pNames) 1458 { 1459 for (FormulaToken* p = pCode->First(); p; p = pCode->Next()) 1460 { 1461 if (p->GetOpCode() == ocName) 1462 { 1463 sal_uInt16 nTokenIndex = p->GetIndex(); 1464 rIndexes.insert( nTokenIndex ); 1465 1466 ScRangeData* pSubName = pNames->FindIndex(p->GetIndex()); 1467 if (pSubName) 1468 lcl_FindRangeNamesInUse(rIndexes, pSubName->GetCode(), pNames); 1469 } 1470 } 1471 } 1472 1473 void ScFormulaCell::FindRangeNamesInUse(std::set<sal_uInt16>& rIndexes) const 1474 { 1475 lcl_FindRangeNamesInUse( rIndexes, pCode, pDocument->GetRangeName() ); 1476 } 1477 1478 void ScFormulaCell::ReplaceRangeNamesInUse( const ScRangeData::IndexMap& rMap ) 1479 { 1480 for( FormulaToken* p = pCode->First(); p; p = pCode->Next() ) 1481 { 1482 if( p->GetOpCode() == ocName ) 1483 { 1484 sal_uInt16 nIndex = p->GetIndex(); 1485 ScRangeData::IndexMap::const_iterator itr = rMap.find(nIndex); 1486 sal_uInt16 nNewIndex = itr == rMap.end() ? nIndex : itr->second; 1487 if ( nIndex != nNewIndex ) 1488 { 1489 p->SetIndex( nNewIndex ); 1490 bCompile = sal_True; 1491 } 1492 } 1493 } 1494 if( bCompile ) 1495 CompileTokenArray(); 1496 } 1497 1498 void ScFormulaCell::CompileDBFormula() 1499 { 1500 for( FormulaToken* p = pCode->First(); p; p = pCode->Next() ) 1501 { 1502 if ( p->GetOpCode() == ocDBArea 1503 || (p->GetOpCode() == ocName && p->GetIndex() >= SC_START_INDEX_DB_COLL) ) 1504 { 1505 bCompile = sal_True; 1506 CompileTokenArray(); 1507 SetDirty(); 1508 break; 1509 } 1510 } 1511 } 1512 1513 void ScFormulaCell::CompileDBFormula( sal_Bool bCreateFormulaString ) 1514 { 1515 // zwei Phasen, muessen (!) nacheinander aufgerufen werden: 1516 // 1. FormelString mit alten Namen erzeugen 1517 // 2. FormelString mit neuen Namen kompilieren 1518 if ( bCreateFormulaString ) 1519 { 1520 sal_Bool bRecompile = sal_False; 1521 pCode->Reset(); 1522 for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() ) 1523 { 1524 switch ( p->GetOpCode() ) 1525 { 1526 case ocBad: // DB-Bereich evtl. zugefuegt 1527 case ocColRowName: // #36762# falls Namensgleichheit 1528 case ocDBArea: // DB-Bereich 1529 bRecompile = sal_True; 1530 break; 1531 case ocName: 1532 if ( p->GetIndex() >= SC_START_INDEX_DB_COLL ) 1533 bRecompile = sal_True; // DB-Bereich 1534 break; 1535 default: 1536 ; // nothing 1537 } 1538 } 1539 if ( bRecompile ) 1540 { 1541 String aFormula; 1542 GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE); 1543 if ( GetMatrixFlag() != MM_NONE && aFormula.Len() ) 1544 { 1545 if ( aFormula.GetChar( aFormula.Len()-1 ) == '}' ) 1546 aFormula.Erase( aFormula.Len()-1 , 1 ); 1547 if ( aFormula.GetChar(0) == '{' ) 1548 aFormula.Erase( 0, 1 ); 1549 } 1550 EndListeningTo( pDocument ); 1551 pDocument->RemoveFromFormulaTree( this ); 1552 pCode->Clear(); 1553 SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE); 1554 } 1555 } 1556 else if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() ) 1557 { 1558 Compile( aResult.GetHybridFormula(), sal_False, eTempGrammar ); 1559 aResult.SetToken( NULL); 1560 SetDirty(); 1561 } 1562 } 1563 1564 void ScFormulaCell::CompileNameFormula( sal_Bool bCreateFormulaString ) 1565 { 1566 // zwei Phasen, muessen (!) nacheinander aufgerufen werden: 1567 // 1. FormelString mit alten RangeNames erzeugen 1568 // 2. FormelString mit neuen RangeNames kompilieren 1569 if ( bCreateFormulaString ) 1570 { 1571 sal_Bool bRecompile = sal_False; 1572 pCode->Reset(); 1573 for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() ) 1574 { 1575 switch ( p->GetOpCode() ) 1576 { 1577 case ocBad: // RangeName evtl. zugefuegt 1578 case ocColRowName: // #36762# falls Namensgleichheit 1579 bRecompile = sal_True; 1580 break; 1581 default: 1582 if ( p->GetType() == svIndex ) 1583 bRecompile = sal_True; // RangeName 1584 } 1585 } 1586 if ( bRecompile ) 1587 { 1588 String aFormula; 1589 GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE); 1590 if ( GetMatrixFlag() != MM_NONE && aFormula.Len() ) 1591 { 1592 if ( aFormula.GetChar( aFormula.Len()-1 ) == '}' ) 1593 aFormula.Erase( aFormula.Len()-1 , 1 ); 1594 if ( aFormula.GetChar(0) == '{' ) 1595 aFormula.Erase( 0, 1 ); 1596 } 1597 EndListeningTo( pDocument ); 1598 pDocument->RemoveFromFormulaTree( this ); 1599 pCode->Clear(); 1600 SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE); 1601 } 1602 } 1603 else if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() ) 1604 { 1605 Compile( aResult.GetHybridFormula(), sal_False, eTempGrammar ); 1606 aResult.SetToken( NULL); 1607 SetDirty(); 1608 } 1609 } 1610 1611 void ScFormulaCell::CompileColRowNameFormula() 1612 { 1613 pCode->Reset(); 1614 for ( FormulaToken* p = pCode->First(); p; p = pCode->Next() ) 1615 { 1616 if ( p->GetOpCode() == ocColRowName ) 1617 { 1618 bCompile = sal_True; 1619 CompileTokenArray(); 1620 SetDirty(); 1621 break; 1622 } 1623 } 1624 } 1625 1626 // ============================================================================ 1627 1628