1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include <memory> 21 #include <osl/diagnose.h> 22 #include <unotools/charclass.hxx> 23 #include <rangeutl.hxx> 24 #include <document.hxx> 25 #include <global.hxx> 26 #include <dbdata.hxx> 27 #include <rangenam.hxx> 28 #include <convuno.hxx> 29 #include <externalrefmgr.hxx> 30 #include <compiler.hxx> 31 #include <refupdatecontext.hxx> 32 33 using ::formula::FormulaGrammar; 34 using namespace ::com::sun::star; 35 36 bool ScRangeUtil::MakeArea( const OUString& rAreaStr, 37 ScArea& rArea, 38 const ScDocument& rDoc, 39 SCTAB nTab, 40 ScAddress::Details const & rDetails ) 41 { 42 // Input in rAreaStr: "$Tabelle1.$A1:$D17" 43 44 // BROKEN BROKEN BROKEN 45 // but it is only used in the consolidate dialog. Ignore for now. 46 47 bool bSuccess = false; 48 sal_Int32 nPointPos = rAreaStr.indexOf('.'); 49 sal_Int32 nColonPos = rAreaStr.indexOf(':'); 50 OUString aStrArea( rAreaStr ); 51 ScRefAddress startPos; 52 ScRefAddress endPos; 53 54 if ( nColonPos == -1 && nPointPos != -1 ) 55 { 56 aStrArea += OUString::Concat(":") + rAreaStr.subView( nPointPos+1 ); // do not include '.' in copy 57 } 58 59 bSuccess = ConvertDoubleRef( rDoc, aStrArea, nTab, startPos, endPos, rDetails ); 60 61 if ( bSuccess ) 62 rArea = ScArea( startPos.Tab(), 63 startPos.Col(), startPos.Row(), 64 endPos.Col(), endPos.Row() ); 65 66 return bSuccess; 67 } 68 69 void ScRangeUtil::CutPosString( const OUString& theAreaStr, 70 OUString& thePosStr ) 71 { 72 OUString aPosStr; 73 // BROKEN BROKEN BROKEN 74 // but it is only used in the consolidate dialog. Ignore for now. 75 76 sal_Int32 nColonPos = theAreaStr.indexOf(':'); 77 78 if ( nColonPos != -1 ) 79 aPosStr = theAreaStr.copy( 0, nColonPos ); // do not include ':' in copy 80 else 81 aPosStr = theAreaStr; 82 83 thePosStr = aPosStr; 84 } 85 86 bool ScRangeUtil::IsAbsTabArea( const OUString& rAreaStr, 87 const ScDocument* pDoc, 88 std::unique_ptr<ScArea[]>* ppAreas, 89 sal_uInt16* pAreaCount, 90 bool /* bAcceptCellRef */, 91 ScAddress::Details const & rDetails ) 92 { 93 OSL_ENSURE( pDoc, "No document given!" ); 94 if ( !pDoc ) 95 return false; 96 97 // BROKEN BROKEN BROKEN 98 // but it is only used in the consolidate dialog. Ignore for now. 99 100 /* 101 * Expects strings like: 102 * "$Tabelle1.$A$1:$Tabelle3.$D$17" 103 * If bAcceptCellRef == sal_True then also accept strings like: 104 * "$Tabelle1.$A$1" 105 * 106 * as result a ScArea-Array is created, 107 * which is published via ppAreas and also has to be deleted this route. 108 */ 109 110 bool bStrOk = false; 111 OUString aTempAreaStr(rAreaStr); 112 113 if ( -1 == aTempAreaStr.indexOf(':') ) 114 { 115 aTempAreaStr += ":" + rAreaStr; 116 } 117 118 sal_Int32 nColonPos = aTempAreaStr.indexOf(':'); 119 120 if ( -1 != nColonPos 121 && -1 != aTempAreaStr.indexOf('.') ) 122 { 123 ScRefAddress aStartPos; 124 125 OUString aStartPosStr = aTempAreaStr.copy( 0, nColonPos ); 126 OUString aEndPosStr = aTempAreaStr.copy( nColonPos+1 ); 127 128 if ( ConvertSingleRef( *pDoc, aStartPosStr, 0, aStartPos, rDetails ) ) 129 { 130 ScRefAddress aEndPos; 131 if ( ConvertSingleRef( *pDoc, aEndPosStr, aStartPos.Tab(), aEndPos, rDetails ) ) 132 { 133 aStartPos.SetRelCol( false ); 134 aStartPos.SetRelRow( false ); 135 aStartPos.SetRelTab( false ); 136 aEndPos.SetRelCol( false ); 137 aEndPos.SetRelRow( false ); 138 aEndPos.SetRelTab( false ); 139 140 bStrOk = true; 141 142 if ( ppAreas && pAreaCount ) // Array returned ? 143 { 144 SCTAB nStartTab = aStartPos.Tab(); 145 SCTAB nEndTab = aEndPos.Tab(); 146 sal_uInt16 nTabCount = static_cast<sal_uInt16>(nEndTab-nStartTab+1); 147 ppAreas->reset(new ScArea[nTabCount]); 148 SCTAB nTab = 0; 149 sal_uInt16 i = 0; 150 ScArea theArea( 0, aStartPos.Col(), aStartPos.Row(), 151 aEndPos.Col(), aEndPos.Row() ); 152 153 nTab = nStartTab; 154 for ( i=0; i<nTabCount; i++ ) 155 { 156 (*ppAreas)[i] = theArea; 157 (*ppAreas)[i].nTab = nTab; 158 nTab++; 159 } 160 *pAreaCount = nTabCount; 161 } 162 } 163 } 164 } 165 166 return bStrOk; 167 } 168 169 bool ScRangeUtil::IsAbsArea( const OUString& rAreaStr, 170 const ScDocument& rDoc, 171 SCTAB nTab, 172 OUString* pCompleteStr, 173 ScRefAddress* pStartPos, 174 ScRefAddress* pEndPos, 175 ScAddress::Details const & rDetails ) 176 { 177 ScRefAddress startPos; 178 ScRefAddress endPos; 179 180 bool bIsAbsArea = ConvertDoubleRef( rDoc, rAreaStr, nTab, startPos, endPos, rDetails ); 181 182 if ( bIsAbsArea ) 183 { 184 startPos.SetRelCol( false ); 185 startPos.SetRelRow( false ); 186 startPos.SetRelTab( false ); 187 endPos .SetRelCol( false ); 188 endPos .SetRelRow( false ); 189 endPos .SetRelTab( false ); 190 191 if ( pCompleteStr ) 192 { 193 *pCompleteStr = startPos.GetRefString( rDoc, MAXTAB+1, rDetails ); 194 *pCompleteStr += ":"; 195 *pCompleteStr += endPos.GetRefString( rDoc, nTab, rDetails ); 196 } 197 198 if ( pStartPos && pEndPos ) 199 { 200 *pStartPos = startPos; 201 *pEndPos = endPos; 202 } 203 } 204 205 return bIsAbsArea; 206 } 207 208 bool ScRangeUtil::IsAbsPos( const OUString& rPosStr, 209 const ScDocument& rDoc, 210 SCTAB nTab, 211 OUString* pCompleteStr, 212 ScRefAddress* pPosTripel, 213 ScAddress::Details const & rDetails ) 214 { 215 ScRefAddress thePos; 216 217 bool bIsAbsPos = ConvertSingleRef( rDoc, rPosStr, nTab, thePos, rDetails ); 218 thePos.SetRelCol( false ); 219 thePos.SetRelRow( false ); 220 thePos.SetRelTab( false ); 221 222 if ( bIsAbsPos ) 223 { 224 if ( pPosTripel ) 225 *pPosTripel = thePos; 226 if ( pCompleteStr ) 227 *pCompleteStr = thePos.GetRefString( rDoc, MAXTAB+1, rDetails ); 228 } 229 230 return bIsAbsPos; 231 } 232 233 bool ScRangeUtil::MakeRangeFromName ( 234 const OUString& rName, 235 const ScDocument& rDoc, 236 SCTAB nCurTab, 237 ScRange& rRange, 238 RutlNameScope eScope, 239 ScAddress::Details const & rDetails ) 240 { 241 bool bResult = false; 242 SCTAB nTab = 0; 243 SCCOL nColStart = 0; 244 SCCOL nColEnd = 0; 245 SCROW nRowStart = 0; 246 SCROW nRowEnd = 0; 247 248 if( eScope==RUTL_NAMES ) 249 { 250 //first handle ui names like local1 (Sheet1), which point to a local range name 251 OUString aName(rName); 252 sal_Int32 nEndPos = aName.lastIndexOf(')'); 253 sal_Int32 nStartPos = aName.lastIndexOf(" ("); 254 SCTAB nTable = nCurTab; 255 if (nEndPos != -1 && nStartPos != -1) 256 { 257 OUString aSheetName = aName.copy(nStartPos+2, nEndPos-nStartPos-2); 258 if (rDoc.GetTable(aSheetName, nTable)) 259 { 260 aName = aName.copy(0, nStartPos); 261 } 262 else 263 nTable = nCurTab; 264 } 265 //then check for local range names 266 ScRangeName* pRangeNames = rDoc.GetRangeName( nTable ); 267 ScRangeData* pData = nullptr; 268 aName = ScGlobal::getCharClassPtr()->uppercase(aName); 269 if ( pRangeNames ) 270 pData = pRangeNames->findByUpperName(aName); 271 if (!pData) 272 pData = rDoc.GetRangeName()->findByUpperName(aName); 273 if (pData) 274 { 275 OUString aStrArea; 276 ScRefAddress aStartPos; 277 ScRefAddress aEndPos; 278 279 pData->GetSymbol( aStrArea ); 280 281 if ( IsAbsArea( aStrArea, rDoc, nTable, 282 nullptr, &aStartPos, &aEndPos, rDetails ) ) 283 { 284 nTab = aStartPos.Tab(); 285 nColStart = aStartPos.Col(); 286 nRowStart = aStartPos.Row(); 287 nColEnd = aEndPos.Col(); 288 nRowEnd = aEndPos.Row(); 289 bResult = true; 290 } 291 else 292 { 293 CutPosString( aStrArea, aStrArea ); 294 295 if ( IsAbsPos( aStrArea, rDoc, nTable, 296 nullptr, &aStartPos, rDetails ) ) 297 { 298 nTab = aStartPos.Tab(); 299 nColStart = nColEnd = aStartPos.Col(); 300 nRowStart = nRowEnd = aStartPos.Row(); 301 bResult = true; 302 } 303 } 304 } 305 } 306 else if( eScope==RUTL_DBASE ) 307 { 308 ScDBCollection::NamedDBs& rDbNames = rDoc.GetDBCollection()->getNamedDBs(); 309 ScDBData* pData = rDbNames.findByUpperName(ScGlobal::getCharClassPtr()->uppercase(rName)); 310 if (pData) 311 { 312 pData->GetArea(nTab, nColStart, nRowStart, nColEnd, nRowEnd); 313 bResult = true; 314 } 315 } 316 else 317 { 318 OSL_FAIL( "ScRangeUtil::MakeRangeFromName" ); 319 } 320 321 if( bResult ) 322 { 323 rRange = ScRange( nColStart, nRowStart, nTab, nColEnd, nRowEnd, nTab ); 324 } 325 326 return bResult; 327 } 328 329 void ScRangeStringConverter::AssignString( 330 OUString& rString, 331 const OUString& rNewStr, 332 bool bAppendStr, 333 sal_Unicode cSeparator) 334 { 335 if( bAppendStr ) 336 { 337 if( !rNewStr.isEmpty() ) 338 { 339 if( !rString.isEmpty() ) 340 rString += OUStringChar(cSeparator); 341 rString += rNewStr; 342 } 343 } 344 else 345 rString = rNewStr; 346 } 347 348 sal_Int32 ScRangeStringConverter::IndexOf( 349 const OUString& rString, 350 sal_Unicode cSearchChar, 351 sal_Int32 nOffset, 352 sal_Unicode cQuote ) 353 { 354 sal_Int32 nLength = rString.getLength(); 355 sal_Int32 nIndex = nOffset; 356 bool bQuoted = false; 357 bool bExitLoop = false; 358 359 while( !bExitLoop && (nIndex >= 0 && nIndex < nLength) ) 360 { 361 sal_Unicode cCode = rString[ nIndex ]; 362 bExitLoop = (cCode == cSearchChar) && !bQuoted; 363 bQuoted = (bQuoted != (cCode == cQuote)); 364 if( !bExitLoop ) 365 nIndex++; 366 } 367 return (nIndex < nLength) ? nIndex : -1; 368 } 369 370 sal_Int32 ScRangeStringConverter::IndexOfDifferent( 371 const OUString& rString, 372 sal_Unicode cSearchChar, 373 sal_Int32 nOffset ) 374 { 375 sal_Int32 nLength = rString.getLength(); 376 sal_Int32 nIndex = nOffset; 377 bool bExitLoop = false; 378 379 while( !bExitLoop && (nIndex >= 0 && nIndex < nLength) ) 380 { 381 bExitLoop = (rString[ nIndex ] != cSearchChar); 382 if( !bExitLoop ) 383 nIndex++; 384 } 385 return (nIndex < nLength) ? nIndex : -1; 386 } 387 388 void ScRangeStringConverter::GetTokenByOffset( 389 OUString& rToken, 390 const OUString& rString, 391 sal_Int32& nOffset, 392 sal_Unicode cSeparator, 393 sal_Unicode cQuote) 394 { 395 sal_Int32 nLength = rString.getLength(); 396 if( nOffset == -1 || nOffset >= nLength ) 397 { 398 rToken.clear(); 399 nOffset = -1; 400 } 401 else 402 { 403 sal_Int32 nTokenEnd = IndexOf( rString, cSeparator, nOffset, cQuote ); 404 if( nTokenEnd < 0 ) 405 nTokenEnd = nLength; 406 rToken = rString.copy( nOffset, nTokenEnd - nOffset ); 407 408 sal_Int32 nNextBegin = IndexOfDifferent( rString, cSeparator, nTokenEnd ); 409 nOffset = (nNextBegin < 0) ? nLength : nNextBegin; 410 } 411 } 412 413 void ScRangeStringConverter::AppendTableName(OUStringBuffer& rBuf, const OUString& rTabName) 414 { 415 // quote character is always "'" 416 OUString aQuotedTab(rTabName); 417 ScCompiler::CheckTabQuotes(aQuotedTab); 418 rBuf.append(aQuotedTab); 419 } 420 421 sal_Int32 ScRangeStringConverter::GetTokenCount( const OUString& rString, sal_Unicode cSeparator ) 422 { 423 OUString sToken; 424 sal_Int32 nCount = 0; 425 sal_Int32 nOffset = 0; 426 while( nOffset >= 0 ) 427 { 428 GetTokenByOffset( sToken, rString, nOffset, '\'', cSeparator ); 429 if( nOffset >= 0 ) 430 nCount++; 431 } 432 return nCount; 433 } 434 435 bool ScRangeStringConverter::GetAddressFromString( 436 ScAddress& rAddress, 437 const OUString& rAddressStr, 438 const ScDocument& rDocument, 439 FormulaGrammar::AddressConvention eConv, 440 sal_Int32& nOffset, 441 sal_Unicode cSeparator, 442 sal_Unicode cQuote ) 443 { 444 OUString sToken; 445 GetTokenByOffset( sToken, rAddressStr, nOffset, cSeparator, cQuote ); 446 if( nOffset >= 0 ) 447 { 448 if ((rAddress.Parse( sToken, rDocument, eConv ) & ScRefFlags::VALID) == ScRefFlags::VALID) 449 return true; 450 ::formula::FormulaGrammar::AddressConvention eConvUI = rDocument.GetAddressConvention(); 451 if (eConv != eConvUI) 452 return ((rAddress.Parse(sToken, rDocument, eConvUI) & ScRefFlags::VALID) == ScRefFlags::VALID); 453 } 454 return false; 455 } 456 457 bool ScRangeStringConverter::GetRangeFromString( 458 ScRange& rRange, 459 const OUString& rRangeStr, 460 const ScDocument& rDocument, 461 FormulaGrammar::AddressConvention eConv, 462 sal_Int32& nOffset, 463 sal_Unicode cSeparator, 464 sal_Unicode cQuote ) 465 { 466 OUString sToken; 467 bool bResult(false); 468 GetTokenByOffset( sToken, rRangeStr, nOffset, cSeparator, cQuote ); 469 if( nOffset >= 0 ) 470 { 471 sal_Int32 nIndex = IndexOf( sToken, ':', 0, cQuote ); 472 OUString aUIString(sToken); 473 474 if( nIndex < 0 ) 475 { 476 if ( aUIString[0] == '.' ) 477 aUIString = aUIString.copy( 1 ); 478 bResult = (rRange.aStart.Parse( aUIString, rDocument, eConv) & ScRefFlags::VALID) == 479 ScRefFlags::VALID; 480 ::formula::FormulaGrammar::AddressConvention eConvUI = rDocument.GetAddressConvention(); 481 if (!bResult && eConv != eConvUI) 482 bResult = (rRange.aStart.Parse(aUIString, rDocument, eConvUI) & ScRefFlags::VALID) == 483 ScRefFlags::VALID; 484 rRange.aEnd = rRange.aStart; 485 } 486 else 487 { 488 if ( aUIString[0] == '.' ) 489 { 490 aUIString = aUIString.copy( 1 ); 491 --nIndex; 492 } 493 494 if ( nIndex < aUIString.getLength() - 1 && 495 aUIString[ nIndex + 1 ] == '.' ) 496 aUIString = aUIString.replaceAt( nIndex + 1, 1, "" ); 497 498 bResult = ((rRange.Parse(aUIString, rDocument, eConv) & ScRefFlags::VALID) == 499 ScRefFlags::VALID); 500 501 // #i77703# chart ranges in the file format contain both sheet names, even for an external reference sheet. 502 // This isn't parsed by ScRange, so try to parse the two Addresses then. 503 if (!bResult) 504 { 505 bResult = ((rRange.aStart.Parse( aUIString.copy(0, nIndex), rDocument, eConv) 506 & ScRefFlags::VALID) == ScRefFlags::VALID) 507 && 508 ((rRange.aEnd.Parse( aUIString.copy(nIndex+1), rDocument, eConv) 509 & ScRefFlags::VALID) == ScRefFlags::VALID); 510 511 ::formula::FormulaGrammar::AddressConvention eConvUI = rDocument.GetAddressConvention(); 512 if (!bResult && eConv != eConvUI) 513 { 514 bResult = ((rRange.aStart.Parse( aUIString.copy(0, nIndex), rDocument, eConvUI) 515 & ScRefFlags::VALID) == ScRefFlags::VALID) 516 && 517 ((rRange.aEnd.Parse( aUIString.copy(nIndex+1), rDocument, eConvUI) 518 & ScRefFlags::VALID) == ScRefFlags::VALID); 519 } 520 } 521 } 522 } 523 return bResult; 524 } 525 526 bool ScRangeStringConverter::GetRangeListFromString( 527 ScRangeList& rRangeList, 528 const OUString& rRangeListStr, 529 const ScDocument& rDocument, 530 FormulaGrammar::AddressConvention eConv, 531 sal_Unicode cSeparator, 532 sal_Unicode cQuote ) 533 { 534 bool bRet = true; 535 OSL_ENSURE( !rRangeListStr.isEmpty(), "ScXMLConverter::GetRangeListFromString - empty string!" ); 536 sal_Int32 nOffset = 0; 537 while( nOffset >= 0 ) 538 { 539 ScRange aRange; 540 if ( 541 GetRangeFromString( aRange, rRangeListStr, rDocument, eConv, nOffset, cSeparator, cQuote ) && 542 (nOffset >= 0) 543 ) 544 { 545 rRangeList.push_back( aRange ); 546 } 547 else if (nOffset > -1) 548 bRet = false; 549 } 550 return bRet; 551 } 552 553 bool ScRangeStringConverter::GetAreaFromString( 554 ScArea& rArea, 555 const OUString& rRangeStr, 556 const ScDocument& rDocument, 557 FormulaGrammar::AddressConvention eConv, 558 sal_Int32& nOffset, 559 sal_Unicode cSeparator ) 560 { 561 ScRange aScRange; 562 bool bResult(false); 563 if( GetRangeFromString( aScRange, rRangeStr, rDocument, eConv, nOffset, cSeparator ) && (nOffset >= 0) ) 564 { 565 rArea.nTab = aScRange.aStart.Tab(); 566 rArea.nColStart = aScRange.aStart.Col(); 567 rArea.nRowStart = aScRange.aStart.Row(); 568 rArea.nColEnd = aScRange.aEnd.Col(); 569 rArea.nRowEnd = aScRange.aEnd.Row(); 570 bResult = true; 571 } 572 return bResult; 573 } 574 575 bool ScRangeStringConverter::GetRangeFromString( 576 table::CellRangeAddress& rRange, 577 const OUString& rRangeStr, 578 const ScDocument& rDocument, 579 FormulaGrammar::AddressConvention eConv, 580 sal_Int32& nOffset, 581 sal_Unicode cSeparator ) 582 { 583 ScRange aScRange; 584 bool bResult(false); 585 if( GetRangeFromString( aScRange, rRangeStr, rDocument, eConv, nOffset, cSeparator ) && (nOffset >= 0) ) 586 { 587 ScUnoConversion::FillApiRange( rRange, aScRange ); 588 bResult = true; 589 } 590 return bResult; 591 } 592 593 void ScRangeStringConverter::GetStringFromAddress( 594 OUString& rString, 595 const ScAddress& rAddress, 596 const ScDocument* pDocument, 597 FormulaGrammar::AddressConvention eConv, 598 sal_Unicode cSeparator, 599 bool bAppendStr, 600 ScRefFlags nFormatFlags ) 601 { 602 if (pDocument && pDocument->HasTable(rAddress.Tab())) 603 { 604 OUString sAddress(rAddress.Format(nFormatFlags, pDocument, eConv)); 605 AssignString( rString, sAddress, bAppendStr, cSeparator ); 606 } 607 } 608 609 void ScRangeStringConverter::GetStringFromRange( 610 OUString& rString, 611 const ScRange& rRange, 612 const ScDocument* pDocument, 613 FormulaGrammar::AddressConvention eConv, 614 sal_Unicode cSeparator, 615 bool bAppendStr, 616 ScRefFlags nFormatFlags ) 617 { 618 if (pDocument && pDocument->HasTable(rRange.aStart.Tab())) 619 { 620 ScAddress aStartAddress( rRange.aStart ); 621 ScAddress aEndAddress( rRange.aEnd ); 622 OUString sStartAddress(aStartAddress.Format(nFormatFlags, pDocument, eConv)); 623 OUString sEndAddress(aEndAddress.Format(nFormatFlags, pDocument, eConv)); 624 AssignString( 625 rString, sStartAddress + ":" + sEndAddress, bAppendStr, cSeparator); 626 } 627 } 628 629 void ScRangeStringConverter::GetStringFromRangeList( 630 OUString& rString, 631 const ScRangeList* pRangeList, 632 const ScDocument* pDocument, 633 FormulaGrammar::AddressConvention eConv, 634 sal_Unicode cSeparator ) 635 { 636 OUString sRangeListStr; 637 if( pRangeList ) 638 { 639 for( size_t nIndex = 0, nCount = pRangeList->size(); nIndex < nCount; nIndex++ ) 640 { 641 const ScRange & rRange = (*pRangeList)[nIndex]; 642 GetStringFromRange( sRangeListStr, rRange, pDocument, eConv, cSeparator, true ); 643 } 644 } 645 rString = sRangeListStr; 646 } 647 648 void ScRangeStringConverter::GetStringFromArea( 649 OUString& rString, 650 const ScArea& rArea, 651 const ScDocument* pDocument, 652 FormulaGrammar::AddressConvention eConv, 653 sal_Unicode cSeparator, 654 bool bAppendStr, 655 ScRefFlags nFormatFlags ) 656 { 657 ScRange aRange( rArea.nColStart, rArea.nRowStart, rArea.nTab, rArea.nColEnd, rArea.nRowEnd, rArea.nTab ); 658 GetStringFromRange( rString, aRange, pDocument, eConv, cSeparator, bAppendStr, nFormatFlags ); 659 } 660 661 void ScRangeStringConverter::GetStringFromAddress( 662 OUString& rString, 663 const table::CellAddress& rAddress, 664 const ScDocument* pDocument, 665 FormulaGrammar::AddressConvention eConv, 666 sal_Unicode cSeparator, 667 bool bAppendStr ) 668 { 669 ScAddress aScAddress( static_cast<SCCOL>(rAddress.Column), static_cast<SCROW>(rAddress.Row), rAddress.Sheet ); 670 GetStringFromAddress( rString, aScAddress, pDocument, eConv, cSeparator, bAppendStr ); 671 } 672 673 void ScRangeStringConverter::GetStringFromRange( 674 OUString& rString, 675 const table::CellRangeAddress& rRange, 676 const ScDocument* pDocument, 677 FormulaGrammar::AddressConvention eConv, 678 sal_Unicode cSeparator, 679 bool bAppendStr, 680 ScRefFlags nFormatFlags ) 681 { 682 ScRange aScRange( static_cast<SCCOL>(rRange.StartColumn), static_cast<SCROW>(rRange.StartRow), rRange.Sheet, 683 static_cast<SCCOL>(rRange.EndColumn), static_cast<SCROW>(rRange.EndRow), rRange.Sheet ); 684 GetStringFromRange( rString, aScRange, pDocument, eConv, cSeparator, bAppendStr, nFormatFlags ); 685 } 686 687 void ScRangeStringConverter::GetStringFromRangeList( 688 OUString& rString, 689 const uno::Sequence< table::CellRangeAddress >& rRangeSeq, 690 const ScDocument* pDocument, 691 FormulaGrammar::AddressConvention eConv, 692 sal_Unicode cSeparator ) 693 { 694 OUString sRangeListStr; 695 for( const table::CellRangeAddress& rRange : rRangeSeq ) 696 { 697 GetStringFromRange( sRangeListStr, rRange, pDocument, eConv, cSeparator, true ); 698 } 699 rString = sRangeListStr; 700 } 701 702 static void lcl_appendCellAddress( 703 OUStringBuffer& rBuf, const ScDocument& rDoc, const ScAddress& rCell, 704 const ScAddress::ExternalInfo& rExtInfo) 705 { 706 if (rExtInfo.mbExternal) 707 { 708 ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager(); 709 const OUString* pFilePath = pRefMgr->getExternalFileName(rExtInfo.mnFileId, true); 710 if (!pFilePath) 711 return; 712 713 sal_Unicode cQuote = '\''; 714 rBuf.append(cQuote); 715 rBuf.append(*pFilePath); 716 rBuf.append(cQuote); 717 rBuf.append('#'); 718 rBuf.append('$'); 719 ScRangeStringConverter::AppendTableName(rBuf, rExtInfo.maTabName); 720 rBuf.append('.'); 721 722 OUString aAddr(rCell.Format(ScRefFlags::ADDR_ABS, nullptr, rDoc.GetAddressConvention())); 723 rBuf.append(aAddr); 724 } 725 else 726 { 727 OUString aAddr(rCell.Format(ScRefFlags::ADDR_ABS_3D, &rDoc, rDoc.GetAddressConvention())); 728 rBuf.append(aAddr); 729 } 730 } 731 732 static void lcl_appendCellRangeAddress( 733 OUStringBuffer& rBuf, const ScDocument& rDoc, const ScAddress& rCell1, const ScAddress& rCell2, 734 const ScAddress::ExternalInfo& rExtInfo1, const ScAddress::ExternalInfo& rExtInfo2) 735 { 736 if (rExtInfo1.mbExternal) 737 { 738 OSL_ENSURE(rExtInfo2.mbExternal, "2nd address is not external!?"); 739 OSL_ENSURE(rExtInfo1.mnFileId == rExtInfo2.mnFileId, "File IDs do not match between 1st and 2nd addresses."); 740 741 ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager(); 742 const OUString* pFilePath = pRefMgr->getExternalFileName(rExtInfo1.mnFileId, true); 743 if (!pFilePath) 744 return; 745 746 sal_Unicode cQuote = '\''; 747 rBuf.append(cQuote); 748 rBuf.append(*pFilePath); 749 rBuf.append(cQuote); 750 rBuf.append('#'); 751 rBuf.append('$'); 752 ScRangeStringConverter::AppendTableName(rBuf, rExtInfo1.maTabName); 753 rBuf.append('.'); 754 755 OUString aAddr(rCell1.Format(ScRefFlags::ADDR_ABS, nullptr, rDoc.GetAddressConvention())); 756 rBuf.append(aAddr); 757 758 rBuf.append(":"); 759 760 if (rExtInfo1.maTabName != rExtInfo2.maTabName) 761 { 762 rBuf.append('$'); 763 ScRangeStringConverter::AppendTableName(rBuf, rExtInfo2.maTabName); 764 rBuf.append('.'); 765 } 766 767 aAddr = rCell2.Format(ScRefFlags::ADDR_ABS, nullptr, rDoc.GetAddressConvention()); 768 rBuf.append(aAddr); 769 } 770 else 771 { 772 ScRange aRange; 773 aRange.aStart = rCell1; 774 aRange.aEnd = rCell2; 775 OUString aAddr(aRange.Format(rDoc, ScRefFlags::RANGE_ABS_3D, rDoc.GetAddressConvention())); 776 rBuf.append(aAddr); 777 } 778 } 779 780 void ScRangeStringConverter::GetStringFromXMLRangeString( OUString& rString, const OUString& rXMLRange, const ScDocument& rDoc ) 781 { 782 FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention(); 783 const sal_Unicode cSepNew = ScCompiler::GetNativeSymbolChar(ocSep); 784 785 OUStringBuffer aRetStr; 786 sal_Int32 nOffset = 0; 787 bool bFirst = true; 788 789 while (nOffset >= 0) 790 { 791 OUString aToken; 792 GetTokenByOffset(aToken, rXMLRange, nOffset); 793 if (nOffset < 0) 794 break; 795 796 sal_Int32 nSepPos = IndexOf(aToken, ':', 0); 797 if (nSepPos >= 0) 798 { 799 // Cell range 800 OUString aBeginCell = aToken.copy(0, nSepPos); 801 OUString aEndCell = aToken.copy(nSepPos+1); 802 803 if (aBeginCell.isEmpty() || aEndCell.isEmpty()) 804 // both cell addresses must exist for this to work. 805 continue; 806 807 sal_Int32 nEndCellDotPos = aEndCell.indexOf('.'); 808 if (nEndCellDotPos <= 0) 809 { 810 // initialize buffer with table name... 811 sal_Int32 nDotPos = IndexOf(aBeginCell, '.', 0); 812 OUStringBuffer aBuf(aBeginCell.subView(0, nDotPos)); 813 814 if (nEndCellDotPos == 0) 815 { 816 // workaround for old syntax (probably pre-chart2 age?) 817 // e.g. Sheet1.A1:.B2 818 aBuf.append(aEndCell); 819 } 820 else if (nEndCellDotPos < 0) 821 { 822 // sheet name in the end cell is omitted (e.g. Sheet2.A1:B2). 823 aBuf.append('.'); 824 aBuf.append(aEndCell); 825 } 826 aEndCell = aBuf.makeStringAndClear(); 827 } 828 829 ScAddress::ExternalInfo aExtInfo1, aExtInfo2; 830 ScAddress aCell1, aCell2; 831 ScRefFlags nRet = aCell1.Parse(aBeginCell, rDoc, FormulaGrammar::CONV_OOO, &aExtInfo1); 832 if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO) 833 { 834 // first cell is invalid. 835 if (eConv == FormulaGrammar::CONV_OOO) 836 continue; 837 838 nRet = aCell1.Parse(aBeginCell, rDoc, eConv, &aExtInfo1); 839 if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO) 840 // first cell is really invalid. 841 continue; 842 } 843 844 nRet = aCell2.Parse(aEndCell, rDoc, FormulaGrammar::CONV_OOO, &aExtInfo2); 845 if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO) 846 { 847 // second cell is invalid. 848 if (eConv == FormulaGrammar::CONV_OOO) 849 continue; 850 851 nRet = aCell2.Parse(aEndCell, rDoc, eConv, &aExtInfo2); 852 if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO) 853 // second cell is really invalid. 854 continue; 855 } 856 857 if (aExtInfo1.mnFileId != aExtInfo2.mnFileId || aExtInfo1.mbExternal != aExtInfo2.mbExternal) 858 // external info inconsistency. 859 continue; 860 861 // All looks good! 862 863 if (bFirst) 864 bFirst = false; 865 else 866 aRetStr.append(cSepNew); 867 868 lcl_appendCellRangeAddress(aRetStr, rDoc, aCell1, aCell2, aExtInfo1, aExtInfo2); 869 } 870 else 871 { 872 // Chart always saves ranges using CONV_OOO convention. 873 ScAddress::ExternalInfo aExtInfo; 874 ScAddress aCell; 875 ScRefFlags nRet = aCell.Parse(aToken, rDoc, ::formula::FormulaGrammar::CONV_OOO, &aExtInfo); 876 if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO ) 877 { 878 nRet = aCell.Parse(aToken, rDoc, eConv, &aExtInfo); 879 if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO) 880 continue; 881 } 882 883 // Looks good! 884 885 if (bFirst) 886 bFirst = false; 887 else 888 aRetStr.append(cSepNew); 889 890 lcl_appendCellAddress(aRetStr, rDoc, aCell, aExtInfo); 891 } 892 } 893 894 rString = aRetStr.makeStringAndClear(); 895 } 896 897 ScRangeData* ScRangeStringConverter::GetRangeDataFromString( const OUString& rString, const SCTAB nTab, 898 const ScDocument& rDoc, formula::FormulaGrammar::AddressConvention eConv ) 899 { 900 // This may be called with an external 'doc'#name but wouldn't find any. 901 902 // Dot '.' is not allowed in range names, if present only lookup if it's a 903 // sheet-local name. Same for '!' Excel syntax. 904 // If eConv == FormulaGrammar::CONV_A1_XL_A1 then try both, first our own. 905 sal_Int32 nIndex = -1; 906 if (eConv == FormulaGrammar::CONV_OOO || eConv == FormulaGrammar::CONV_A1_XL_A1) 907 nIndex = ScGlobal::FindUnquoted( rString, '.'); 908 if (nIndex < 0 && (eConv == FormulaGrammar::CONV_A1_XL_A1 909 || eConv == FormulaGrammar::CONV_XL_A1 910 || eConv == FormulaGrammar::CONV_XL_R1C1 911 || eConv == FormulaGrammar::CONV_XL_OOX)) 912 nIndex = ScGlobal::FindUnquoted( rString, '!'); 913 914 if (nIndex >= 0) 915 { 916 if (nIndex == 0) 917 return nullptr; // Can't be a name. 918 919 OUString aTab( rString.copy( 0, nIndex)); 920 ScGlobal::EraseQuotes( aTab, '\''); 921 SCTAB nLocalTab; 922 if (!rDoc.GetTable( aTab, nLocalTab)) 923 return nullptr; 924 925 ScRangeName* pLocalRangeName = rDoc.GetRangeName(nLocalTab); 926 if (!pLocalRangeName) 927 return nullptr; 928 929 const OUString aName( rString.copy( nIndex+1)); 930 return pLocalRangeName->findByUpperName( ScGlobal::getCharClassPtr()->uppercase( aName)); 931 } 932 933 ScRangeName* pLocalRangeName = rDoc.GetRangeName(nTab); 934 ScRangeData* pData = nullptr; 935 OUString aUpperName = ScGlobal::getCharClassPtr()->uppercase(rString); 936 if(pLocalRangeName) 937 { 938 pData = pLocalRangeName->findByUpperName(aUpperName); 939 } 940 if (!pData) 941 { 942 ScRangeName* pGlobalRangeName = rDoc.GetRangeName(); 943 if (pGlobalRangeName) 944 { 945 pData = pGlobalRangeName->findByUpperName(aUpperName); 946 } 947 } 948 return pData; 949 } 950 951 ScArea::ScArea( SCTAB tab, 952 SCCOL colStart, SCROW rowStart, 953 SCCOL colEnd, SCROW rowEnd ) : 954 nTab ( tab ), 955 nColStart( colStart ), nRowStart( rowStart ), 956 nColEnd ( colEnd ), nRowEnd ( rowEnd ) 957 { 958 } 959 960 bool ScArea::operator==( const ScArea& r ) const 961 { 962 return ( (nTab == r.nTab) 963 && (nColStart == r.nColStart) 964 && (nRowStart == r.nRowStart) 965 && (nColEnd == r.nColEnd) 966 && (nRowEnd == r.nRowEnd) ); 967 } 968 969 ScAreaNameIterator::ScAreaNameIterator( const ScDocument& rDoc ) : 970 pRangeName(rDoc.GetRangeName()), 971 pDBCollection(rDoc.GetDBCollection()), 972 bFirstPass(true) 973 { 974 if (pRangeName) 975 { 976 maRNPos = pRangeName->begin(); 977 maRNEnd = pRangeName->end(); 978 } 979 } 980 981 bool ScAreaNameIterator::Next( OUString& rName, ScRange& rRange ) 982 { 983 for (;;) 984 { 985 if ( bFirstPass ) // first the area names 986 { 987 if ( pRangeName && maRNPos != maRNEnd ) 988 { 989 const ScRangeData& rData = *maRNPos->second; 990 ++maRNPos; 991 bool bValid = rData.IsValidReference(rRange); 992 if (bValid) 993 { 994 rName = rData.GetName(); 995 return true; // found 996 } 997 } 998 else 999 { 1000 bFirstPass = false; 1001 if (pDBCollection) 1002 { 1003 const ScDBCollection::NamedDBs& rDBs = pDBCollection->getNamedDBs(); 1004 maDBPos = rDBs.begin(); 1005 maDBEnd = rDBs.end(); 1006 } 1007 } 1008 } 1009 1010 if ( !bFirstPass ) // then the DB areas 1011 { 1012 if (pDBCollection && maDBPos != maDBEnd) 1013 { 1014 const ScDBData& rData = **maDBPos; 1015 ++maDBPos; 1016 rData.GetArea(rRange); 1017 rName = rData.GetName(); 1018 return true; // found 1019 } 1020 else 1021 return false; // nothing left 1022 } 1023 } 1024 } 1025 1026 void ScRangeUpdater::UpdateInsertTab(ScAddress& rAddr, const sc::RefUpdateInsertTabContext& rCxt) 1027 { 1028 if (rCxt.mnInsertPos <= rAddr.Tab()) 1029 { 1030 rAddr.IncTab(rCxt.mnSheets); 1031 } 1032 } 1033 1034 void ScRangeUpdater::UpdateDeleteTab(ScAddress& rAddr, const sc::RefUpdateDeleteTabContext& rCxt) 1035 { 1036 if (rCxt.mnDeletePos <= rAddr.Tab()) 1037 { 1038 rAddr.IncTab(-rCxt.mnSheets); 1039 } 1040 } 1041 1042 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 1043
