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 <sal/config.h> 21 22 #include <string_view> 23 24 #include <address.hxx> 25 #include <global.hxx> 26 #include <compiler.hxx> 27 #include <document.hxx> 28 #include <externalrefmgr.hxx> 29 30 #include <osl/diagnose.h> 31 #include <o3tl/underlyingenumvalue.hxx> 32 #include <com/sun/star/frame/XModel.hpp> 33 #include <com/sun/star/sheet/ExternalLinkInfo.hpp> 34 #include <com/sun/star/sheet/ExternalLinkType.hpp> 35 #include <sfx2/objsh.hxx> 36 #include <tools/urlobj.hxx> 37 #include <sal/log.hxx> 38 #include <rtl/character.hxx> 39 #include <unotools/charclass.hxx> 40 41 using namespace css; 42 43 const ScAddress::Details ScAddress::detailsOOOa1( formula::FormulaGrammar::CONV_OOO, 0, 0 ); 44 45 ScAddress::Details::Details ( const ScDocument& rDoc, 46 const ScAddress& rAddr ) : 47 eConv( rDoc.GetAddressConvention() ), 48 nRow( rAddr.Row() ), 49 nCol( rAddr.Col() ) 50 {} 51 52 namespace { 53 54 const sal_Unicode* parseQuotedNameWithBuffer( const sal_Unicode* pStart, const sal_Unicode* p, OUString& rName ) 55 { 56 // The current character must be on the 2nd quote. 57 58 // Push all the characters up to the current, but skip the very first 59 // character which is the opening quote. 60 OUStringBuffer aBuf(std::u16string_view(pStart+1, p-pStart-1)); 61 62 ++p; // Skip the 2nd quote. 63 sal_Unicode cPrev = 0; 64 for (; *p; ++p) 65 { 66 if (*p == '\'') 67 { 68 if (cPrev == '\'') 69 { 70 // double single-quote equals one single quote. 71 aBuf.append(*p); 72 cPrev = 0; 73 continue; 74 } 75 } 76 else if (cPrev == '\'') 77 { 78 // We are past the closing quote. We're done! 79 rName = aBuf.makeStringAndClear(); 80 return p; 81 } 82 else 83 aBuf.append(*p); 84 cPrev = *p; 85 } 86 87 return pStart; 88 } 89 90 /** 91 * Parse from the opening single quote to the closing single quote. Inside 92 * the quotes, a single quote character is encoded by double single-quote 93 * characters. 94 * 95 * @param p pointer to the first character to begin parsing. 96 * @param rName (reference) parsed name within the quotes. If the name is 97 * empty, either the parsing failed or it's an empty quote. 98 * 99 * @return pointer to the character immediately after the closing single 100 * quote. 101 */ 102 const sal_Unicode* parseQuotedName( const sal_Unicode* p, OUString& rName ) 103 { 104 if (*p != '\'') 105 return p; 106 107 const sal_Unicode* pStart = p; 108 sal_Unicode cPrev = 0; 109 for (++p; *p; ++p) 110 { 111 if (*p == '\'') 112 { 113 if (cPrev == '\'') 114 { 115 // double single-quote equals one single quote. 116 return parseQuotedNameWithBuffer(pStart, p, rName); 117 } 118 } 119 else if (cPrev == '\'') 120 { 121 // We are past the closing quote. We're done! Skip the opening 122 // and closing quotes. 123 rName = OUString(pStart+1, p - pStart-2); 124 return p; 125 } 126 127 cPrev = *p; 128 } 129 130 rName.clear(); 131 return pStart; 132 } 133 134 } 135 136 static tools::Long sal_Unicode_strtol ( const sal_Unicode* p, const sal_Unicode** pEnd ) 137 { 138 tools::Long accum = 0, prev = 0; 139 bool is_neg = false; 140 141 if( *p == '-' ) 142 { 143 is_neg = true; 144 p++; 145 } 146 else if( *p == '+' ) 147 p++; 148 149 while (rtl::isAsciiDigit( *p )) 150 { 151 accum = accum * 10 + *p - '0'; 152 if( accum < prev ) 153 { 154 *pEnd = nullptr; 155 return 0; 156 } 157 prev = accum; 158 p++; 159 } 160 161 *pEnd = p; 162 return is_neg ? -accum : accum; 163 } 164 165 static const sal_Unicode* lcl_eatWhiteSpace( const sal_Unicode* p ) 166 { 167 if ( p ) 168 { 169 while( *p == ' ' ) 170 ++p; 171 } 172 return p; 173 } 174 175 // Compare ignore case ASCII. 176 static bool lcl_isString( const sal_Unicode* p1, const OUString& rStr ) 177 { 178 const size_t n = rStr.getLength(); 179 if (!n) 180 return false; 181 const sal_Unicode* p2 = rStr.getStr(); 182 for (size_t i=0; i<n; ++i) 183 { 184 if (!p1[i]) 185 return false; 186 if (p1[i] != p2[i]) 187 { 188 sal_Unicode c1 = p1[i]; 189 if ('A' <= c1 && c1 <= 'Z') 190 c1 += 0x20; 191 if (c1 < 'a' || 'z' < c1) 192 return false; // not a letter 193 194 sal_Unicode c2 = p2[i]; 195 if ('A' <= c2 && c2 <= 'Z') 196 c2 += 0x20; 197 if (c2 < 'a' || 'z' < c2) 198 return false; // not a letter to match 199 200 if (c1 != c2) 201 return false; // lower case doesn't match either 202 } 203 } 204 return true; 205 } 206 207 /** Determines the number of sheets an external reference spans and sets 208 rRange.aEnd.nTab accordingly. If a sheet is not found, the corresponding 209 bits in rFlags are cleared. pExtInfo is filled if it wasn't already. If in 210 cached order rStartTabName comes after rEndTabName, pExtInfo->maTabName 211 is set to rEndTabName. 212 @returns <FALSE/> if pExtInfo is already filled and rExternDocName does not 213 result in the identical file ID. Else <TRUE/>. 214 */ 215 static bool lcl_ScRange_External_TabSpan( 216 ScRange & rRange, 217 ScRefFlags & rFlags, 218 ScAddress::ExternalInfo* pExtInfo, 219 const OUString & rExternDocName, 220 const OUString & rStartTabName, 221 const OUString & rEndTabName, 222 const ScDocument& rDoc ) 223 { 224 if (rExternDocName.isEmpty()) 225 return !pExtInfo || !pExtInfo->mbExternal; 226 227 ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager(); 228 if (pRefMgr->isOwnDocument( rExternDocName)) 229 { 230 // This is an internal document. Get the sheet positions from the 231 // ScDocument instance. 232 if (!rStartTabName.isEmpty()) 233 { 234 SCTAB nTab; 235 if (rDoc.GetTable(rStartTabName, nTab)) 236 rRange.aStart.SetTab(nTab); 237 } 238 239 if (!rEndTabName.isEmpty()) 240 { 241 SCTAB nTab; 242 if (rDoc.GetTable(rEndTabName, nTab)) 243 rRange.aEnd.SetTab(nTab); 244 } 245 return !pExtInfo || !pExtInfo->mbExternal; 246 } 247 248 sal_uInt16 nFileId = pRefMgr->getExternalFileId( rExternDocName); 249 250 if (pExtInfo) 251 { 252 if (pExtInfo->mbExternal) 253 { 254 if (pExtInfo->mnFileId != nFileId) 255 return false; 256 } 257 else 258 { 259 pExtInfo->mbExternal = true; 260 pExtInfo->maTabName = rStartTabName; 261 pExtInfo->mnFileId = nFileId; 262 } 263 } 264 265 if (rEndTabName.isEmpty() || rStartTabName == rEndTabName) 266 { 267 rRange.aEnd.SetTab( rRange.aStart.Tab()); 268 return true; 269 } 270 271 SCTAB nSpan = pRefMgr->getCachedTabSpan( nFileId, rStartTabName, rEndTabName); 272 if (nSpan == -1) 273 rFlags &= ~ScRefFlags(ScRefFlags::TAB_VALID | ScRefFlags::TAB2_VALID); 274 else if (nSpan == 0) 275 rFlags &= ~ScRefFlags::TAB2_VALID; 276 else if (nSpan >= 1) 277 rRange.aEnd.SetTab( rRange.aStart.Tab() + nSpan - 1); 278 else // (nSpan < -1) 279 { 280 rRange.aEnd.SetTab( rRange.aStart.Tab() - nSpan - 1); 281 if (pExtInfo) 282 pExtInfo->maTabName = rEndTabName; 283 } 284 return true; 285 } 286 287 /** Returns NULL if the string should be a sheet name, but is invalid. 288 Returns a pointer to the first character after the sheet name, if there was 289 any, else pointer to start. 290 @param pMsoxlQuoteStop 291 Starting _within_ a quoted name, but still may be 3D; quoted name stops 292 at pMsoxlQuoteStop 293 */ 294 static const sal_Unicode * lcl_XL_ParseSheetRef( const sal_Unicode* start, 295 OUString& rExternTabName, 296 bool bAllow3D, 297 const sal_Unicode* pMsoxlQuoteStop, 298 const OUString* pErrRef ) 299 { 300 OUString aTabName; 301 const sal_Unicode *p = start; 302 303 // XL only seems to use single quotes for sheet names. 304 if (pMsoxlQuoteStop) 305 { 306 const sal_Unicode* pCurrentStart = p; 307 while (p < pMsoxlQuoteStop) 308 { 309 if (*p == '\'') 310 { 311 // We pre-analyzed the quoting, no checks needed here. 312 if (*++p == '\'') 313 { 314 aTabName += OUString( pCurrentStart, 315 sal::static_int_cast<sal_Int32>( p - pCurrentStart)); 316 pCurrentStart = ++p; 317 } 318 } 319 else if (*p == ':') 320 { 321 break; // while 322 } 323 else 324 ++p; 325 } 326 if (pCurrentStart < p) 327 aTabName += OUString( pCurrentStart, sal::static_int_cast<sal_Int32>( p - pCurrentStart)); 328 if (aTabName.isEmpty()) 329 return nullptr; 330 if (p == pMsoxlQuoteStop) 331 ++p; // position on ! of ...'!... 332 if( *p != '!' && ( !bAllow3D || *p != ':' ) ) 333 return (!bAllow3D && *p == ':') ? p : start; 334 } 335 else if( *p == '\'') 336 { 337 p = parseQuotedName(p, aTabName); 338 if (aTabName.isEmpty()) 339 return nullptr; 340 } 341 else if (pErrRef && lcl_isString( p, *pErrRef) && p[pErrRef->getLength()] == '!') 342 { 343 p += pErrRef->getLength(); // position after "#REF!" on '!' 344 // XXX NOTE: caller has to check the name and that it wasn't quoted. 345 aTabName = *pErrRef; 346 } 347 else 348 { 349 bool only_digits = true; 350 351 /* 352 * Valid: Normal!a1 353 * Valid: x.y!a1 354 * Invalid: .y!a1 355 * 356 * Some names starting with digits are actually valid, but 357 * unparse quoted. Things are quite tricky: most sheet names 358 * starting with a digit are ok, but not those starting with 359 * "[0-9]*\." or "[0-9]+[eE]". 360 * 361 * Valid: 42!a1 362 * Valid: 4x!a1 363 * Invalid: 1.!a1 364 * Invalid: 1e!a1 365 */ 366 while( true ) 367 { 368 const sal_Unicode uc = *p; 369 if( rtl::isAsciiAlpha( uc ) || uc == '_' ) 370 { 371 if( only_digits && p != start && 372 (uc == 'e' || uc == 'E' ) ) 373 { 374 p = start; 375 break; 376 } 377 only_digits = false; 378 p++; 379 } 380 else if( rtl::isAsciiDigit( uc )) 381 { 382 p++; 383 } 384 else if( uc == '.' ) 385 { 386 if( only_digits ) // Valid, except after only digits. 387 { 388 p = start; 389 break; 390 } 391 p++; 392 } 393 else if (uc > 127) 394 { 395 // non ASCII character is allowed. 396 ++p; 397 } 398 else 399 break; 400 } 401 402 if( *p != '!' && ( !bAllow3D || *p != ':' ) ) 403 return (!bAllow3D && *p == ':') ? p : start; 404 405 aTabName += OUString( start, sal::static_int_cast<sal_Int32>( p - start ) ); 406 } 407 408 rExternTabName = aTabName; 409 return p; 410 } 411 412 /** Tries to obtain the external document index and replace by actual document 413 name. 414 415 @param ppErrRet 416 Contains the default pointer the caller would return if this method 417 returns FALSE, may be replaced by NULL for type or data errors. 418 419 @returns FALSE only if the input name is numeric and not within the index 420 sequence, or the link type cannot be determined or data mismatch. Returns 421 TRUE in all other cases, also when there is no index sequence or the input 422 name is not numeric. 423 */ 424 static bool lcl_XL_getExternalDoc( const sal_Unicode** ppErrRet, OUString& rExternDocName, 425 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) 426 { 427 // 1-based, sequence starts with an empty element. 428 if (pExternalLinks && pExternalLinks->hasElements()) 429 { 430 // A numeric "document name" is an index into the sequence. 431 if (CharClass::isAsciiNumeric( rExternDocName)) 432 { 433 sal_Int32 i = rExternDocName.toInt32(); 434 if (i < 0 || i >= pExternalLinks->getLength()) 435 return false; // with default *ppErrRet 436 const sheet::ExternalLinkInfo & rInfo = (*pExternalLinks)[i]; 437 switch (rInfo.Type) 438 { 439 case sheet::ExternalLinkType::DOCUMENT : 440 { 441 OUString aStr; 442 if (!(rInfo.Data >>= aStr)) 443 { 444 SAL_INFO( 445 "sc.core", 446 "Data type mismatch for ExternalLinkInfo " 447 << i); 448 *ppErrRet = nullptr; 449 return false; 450 } 451 rExternDocName = aStr; 452 } 453 break; 454 case sheet::ExternalLinkType::SELF : 455 return false; // ??? 456 case sheet::ExternalLinkType::SPECIAL : 457 // silently return nothing (do not assert), caller has to handle this 458 *ppErrRet = nullptr; 459 return false; 460 default: 461 SAL_INFO( 462 "sc.core", 463 "unhandled ExternalLinkType " << rInfo.Type 464 << " for index " << i); 465 *ppErrRet = nullptr; 466 return false; 467 } 468 } 469 } 470 return true; 471 } 472 473 const sal_Unicode* ScRange::Parse_XL_Header( 474 const sal_Unicode* p, 475 const ScDocument& rDoc, 476 OUString& rExternDocName, 477 OUString& rStartTabName, 478 OUString& rEndTabName, 479 ScRefFlags& nFlags, 480 bool bOnlyAcceptSingle, 481 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks, 482 const OUString* pErrRef ) 483 { 484 const sal_Unicode* startTabs, *start = p; 485 ScRefFlags nSaveFlags = nFlags; 486 487 // Is this an external reference ? 488 rStartTabName.clear(); 489 rEndTabName.clear(); 490 rExternDocName.clear(); 491 const sal_Unicode* pMsoxlQuoteStop = nullptr; 492 if (*p == '[') 493 { 494 ++p; 495 // Only single quotes are correct, and a double single quote escapes a 496 // single quote text inside the quoted text. 497 if (*p == '\'') 498 { 499 p = parseQuotedName(p, rExternDocName); 500 if (*p != ']' || rExternDocName.isEmpty()) 501 { 502 rExternDocName.clear(); 503 return start; 504 } 505 } 506 else 507 { 508 // non-quoted file name. 509 p = ScGlobal::UnicodeStrChr( start+1, ']' ); 510 if( p == nullptr ) 511 return start; 512 rExternDocName += OUString( start+1, sal::static_int_cast<sal_Int32>( p-(start+1) ) ); 513 } 514 ++p; 515 516 const sal_Unicode* pErrRet = start; 517 if (!lcl_XL_getExternalDoc( &pErrRet, rExternDocName, pExternalLinks)) 518 return pErrRet; 519 520 rExternDocName = ScGlobal::GetAbsDocName(rExternDocName, rDoc.GetDocumentShell()); 521 } 522 else if (*p == '\'') 523 { 524 // Sickness in Excel's ODF msoxl namespace: 525 // 'E:\[EXTDATA8.XLS]Sheet1'!$A$7 or 526 // 'E:\[EXTDATA12B.XLSB]Sheet1:Sheet3'!$A$11 527 // But, 'Sheet1'!B3 would also be a valid! 528 // Excel does not allow [ and ] characters in sheet names though. 529 // But, more sickness comes with MOOXML as there may be 530 // '[1]Sheet 4'!$A$1 where [1] is the external doc's index. 531 p = parseQuotedName(p, rExternDocName); 532 if (*p != '!') 533 { 534 rExternDocName.clear(); 535 return start; 536 } 537 if (!rExternDocName.isEmpty()) 538 { 539 sal_Int32 nOpen = rExternDocName.indexOf( '['); 540 if (nOpen == -1) 541 rExternDocName.clear(); 542 else 543 { 544 sal_Int32 nClose = rExternDocName.indexOf( ']', nOpen+1); 545 if (nClose == -1) 546 rExternDocName.clear(); 547 else 548 { 549 rExternDocName = rExternDocName.copy(0, nClose); 550 rExternDocName = rExternDocName.replaceAt( nOpen, 1, ""); 551 pMsoxlQuoteStop = p - 1; // the ' quote char 552 // There may be embedded escaped quotes, just matching the 553 // doc name's length may not work. 554 for (p = start; *p != '['; ++p) 555 ; 556 for ( ; *p != ']'; ++p) 557 ; 558 ++p; 559 560 // Handle '[1]Sheet 4'!$A$1 561 if (nOpen == 0) 562 { 563 const sal_Unicode* pErrRet = start; 564 if (!lcl_XL_getExternalDoc( &pErrRet, rExternDocName, pExternalLinks)) 565 return pErrRet; 566 } 567 } 568 } 569 } 570 if (rExternDocName.isEmpty()) 571 p = start; 572 } 573 574 startTabs = p; 575 p = lcl_XL_ParseSheetRef( p, rStartTabName, !bOnlyAcceptSingle, pMsoxlQuoteStop, pErrRef); 576 if( nullptr == p ) 577 return start; // invalid tab 578 if (bOnlyAcceptSingle && *p == ':') 579 return nullptr; // 3D 580 const sal_Unicode* startEndTabs = nullptr; 581 if( p != startTabs ) 582 { 583 nFlags |= ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D | ScRefFlags::TAB_ABS; 584 if( *p == ':' ) // 3d ref 585 { 586 startEndTabs = p + 1; 587 p = lcl_XL_ParseSheetRef( startEndTabs, rEndTabName, false, pMsoxlQuoteStop, pErrRef); 588 if( p == nullptr ) 589 { 590 nFlags = nSaveFlags; 591 return start; // invalid tab 592 } 593 nFlags |= ScRefFlags::TAB2_VALID | ScRefFlags::TAB2_3D | ScRefFlags::TAB2_ABS; 594 } 595 else 596 { 597 // If only one sheet is given, the full reference is still valid, 598 // only the second 3D flag is not set. 599 nFlags |= ScRefFlags::TAB2_VALID | ScRefFlags::TAB2_ABS; 600 aEnd.SetTab( aStart.Tab() ); 601 } 602 603 if( *p++ != '!' ) 604 { 605 nFlags = nSaveFlags; 606 return start; // syntax error 607 } 608 else 609 p = lcl_eatWhiteSpace( p ); 610 } 611 else 612 { 613 nFlags |= ScRefFlags::TAB_VALID | ScRefFlags::TAB2_VALID; 614 // Use the current tab, it needs to be passed in. : aEnd.SetTab( .. ); 615 } 616 617 if (!rExternDocName.isEmpty()) 618 { 619 ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager(); 620 pRefMgr->convertToAbsName(rExternDocName); 621 } 622 else 623 { 624 // Internal reference. 625 if (rStartTabName.isEmpty()) 626 { 627 nFlags = nSaveFlags; 628 return start; 629 } 630 631 SCTAB nTab; 632 if ((pErrRef && *startTabs != '\'' && rStartTabName == *pErrRef) || !rDoc.GetTable(rStartTabName, nTab)) 633 { 634 // invalid table name. 635 nFlags &= ~ScRefFlags::TAB_VALID; 636 nTab = -1; 637 } 638 639 aStart.SetTab(nTab); 640 aEnd.SetTab(nTab); 641 642 if (!rEndTabName.isEmpty()) 643 { 644 if ((pErrRef && startEndTabs && *startEndTabs != '\'' && rEndTabName == *pErrRef) || 645 !rDoc.GetTable(rEndTabName, nTab)) 646 { 647 // invalid table name. 648 nFlags &= ~ScRefFlags::TAB2_VALID; 649 nTab = -1; 650 } 651 652 aEnd.SetTab(nTab); 653 } 654 } 655 return p; 656 } 657 658 static const sal_Unicode* lcl_r1c1_get_col( const sal_Unicode* p, 659 const ScAddress::Details& rDetails, 660 ScAddress* pAddr, ScRefFlags* nFlags ) 661 { 662 const sal_Unicode *pEnd; 663 tools::Long n; 664 bool isRelative; 665 666 if( p[0] == '\0' ) 667 return nullptr; 668 669 p++; 670 isRelative = *p == '['; 671 if( isRelative ) 672 p++; 673 n = sal_Unicode_strtol( p, &pEnd ); 674 if( nullptr == pEnd ) 675 return nullptr; 676 677 if( p == pEnd ) // C is a relative ref with offset 0 678 { 679 if( isRelative ) 680 return nullptr; 681 n = rDetails.nCol; 682 } 683 else if( isRelative ) 684 { 685 if( *pEnd != ']' ) 686 return nullptr; 687 n += rDetails.nCol; 688 pEnd++; 689 } 690 else 691 { 692 *nFlags |= ScRefFlags::COL_ABS; 693 n--; 694 } 695 696 if( n < 0 || n >= MAXCOLCOUNT ) 697 return nullptr; 698 pAddr->SetCol( static_cast<SCCOL>( n ) ); 699 *nFlags |= ScRefFlags::COL_VALID; 700 701 return pEnd; 702 } 703 704 static const sal_Unicode* lcl_r1c1_get_row( 705 const ScSheetLimits& rSheetLimits, 706 const sal_Unicode* p, 707 const ScAddress::Details& rDetails, 708 ScAddress* pAddr, ScRefFlags* nFlags ) 709 { 710 const sal_Unicode *pEnd; 711 tools::Long n; 712 bool isRelative; 713 714 if( p[0] == '\0' ) 715 return nullptr; 716 717 p++; 718 isRelative = *p == '['; 719 if( isRelative ) 720 p++; 721 n = sal_Unicode_strtol( p, &pEnd ); 722 if( nullptr == pEnd ) 723 return nullptr; 724 725 if( p == pEnd ) // R is a relative ref with offset 0 726 { 727 if( isRelative ) 728 return nullptr; 729 n = rDetails.nRow; 730 } 731 else if( isRelative ) 732 { 733 if( *pEnd != ']' ) 734 return nullptr; 735 n += rDetails.nRow; 736 pEnd++; 737 } 738 else 739 { 740 *nFlags |= ScRefFlags::ROW_ABS; 741 n--; 742 } 743 744 if( n < 0 || n >= rSheetLimits.GetMaxRowCount() ) 745 return nullptr; 746 pAddr->SetRow( static_cast<SCROW>( n ) ); 747 *nFlags |= ScRefFlags::ROW_VALID; 748 749 return pEnd; 750 } 751 752 static ScRefFlags lcl_ScRange_Parse_XL_R1C1( ScRange& r, 753 const sal_Unicode* p, 754 const ScDocument& rDoc, 755 const ScAddress::Details& rDetails, 756 bool bOnlyAcceptSingle, 757 ScAddress::ExternalInfo* pExtInfo, 758 sal_Int32* pSheetEndPos ) 759 { 760 const sal_Unicode* const pStart = p; 761 if (pSheetEndPos) 762 *pSheetEndPos = 0; 763 const sal_Unicode* pTmp = nullptr; 764 OUString aExternDocName, aStartTabName, aEndTabName; 765 ScRefFlags nFlags = ScRefFlags::VALID | ScRefFlags::TAB_VALID; 766 // Keep in mind that nFlags2 gets left-shifted by 4 bits before being merged. 767 ScRefFlags nFlags2 = ScRefFlags::TAB_VALID; 768 769 p = r.Parse_XL_Header( p, rDoc, aExternDocName, aStartTabName, 770 aEndTabName, nFlags, bOnlyAcceptSingle ); 771 772 ScRefFlags nBailOutFlags = ScRefFlags::ZERO; 773 if (pSheetEndPos && pStart < p && (nFlags & ScRefFlags::TAB_VALID) && (nFlags & ScRefFlags::TAB_3D)) 774 { 775 *pSheetEndPos = p - pStart; 776 nBailOutFlags = ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D; 777 } 778 779 if (!aExternDocName.isEmpty()) 780 lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName, 781 aStartTabName, aEndTabName, rDoc); 782 783 if( nullptr == p ) 784 return ScRefFlags::ZERO; 785 786 if( *p == 'R' || *p == 'r' ) 787 { 788 if( nullptr == (p = lcl_r1c1_get_row( rDoc.GetSheetLimits(), p, rDetails, &r.aStart, &nFlags )) ) 789 return nBailOutFlags; 790 791 if( *p != 'C' && *p != 'c' ) // full row R# 792 { 793 if( p[0] != ':' || (p[1] != 'R' && p[1] != 'r' ) || 794 nullptr == (pTmp = lcl_r1c1_get_row( rDoc.GetSheetLimits(), p+1, rDetails, &r.aEnd, &nFlags2 ))) 795 { 796 // Only the initial row number is given, or the second row 797 // number is invalid. Fallback to just the initial R 798 applyStartToEndFlags(nFlags); 799 r.aEnd.SetRow( r.aStart.Row() ); 800 } 801 else // pTmp != nullptr 802 { 803 // Full row range successfully parsed. 804 applyStartToEndFlags(nFlags, nFlags2); 805 p = pTmp; 806 } 807 808 if (p[0] != 0) 809 { 810 // any trailing invalid character must invalidate the whole address. 811 nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID | 812 ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID); 813 return nFlags; 814 } 815 816 nFlags |= 817 ScRefFlags::COL_VALID | ScRefFlags::COL2_VALID | 818 ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS; 819 r.aStart.SetCol( 0 ); 820 r.aEnd.SetCol( rDoc.MaxCol() ); 821 822 return bOnlyAcceptSingle ? ScRefFlags::ZERO : nFlags; 823 } 824 else if( nullptr == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, &nFlags ))) 825 { 826 return ScRefFlags::ZERO; 827 } 828 829 if( p[0] != ':' || 830 (p[1] != 'R' && p[1] != 'r') || 831 nullptr == (pTmp = lcl_r1c1_get_row( rDoc.GetSheetLimits(), p+1, rDetails, &r.aEnd, &nFlags2 )) || 832 (*pTmp != 'C' && *pTmp != 'c') || 833 nullptr == (pTmp = lcl_r1c1_get_col( pTmp, rDetails, &r.aEnd, &nFlags2 ))) 834 { 835 // single cell reference 836 837 if (p[0] != 0) 838 { 839 // any trailing invalid character must invalidate the whole address. 840 nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID); 841 return nFlags; 842 } 843 844 return bOnlyAcceptSingle ? nFlags : ScRefFlags::ZERO; 845 } 846 assert(pTmp); 847 p = pTmp; 848 849 // double reference 850 851 if (p[0] != 0) 852 { 853 // any trailing invalid character must invalidate the whole range. 854 nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID | 855 ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID); 856 return nFlags; 857 } 858 859 applyStartToEndFlags(nFlags, nFlags2); 860 return bOnlyAcceptSingle ? ScRefFlags::ZERO : nFlags; 861 } 862 else if( *p == 'C' || *p == 'c' ) // full col C# 863 { 864 if( nullptr == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, &nFlags ))) 865 return nBailOutFlags; 866 867 if( p[0] != ':' || (p[1] != 'C' && p[1] != 'c') || 868 nullptr == (pTmp = lcl_r1c1_get_col( p+1, rDetails, &r.aEnd, &nFlags2 ))) 869 { // Fallback to just the initial C 870 applyStartToEndFlags(nFlags); 871 r.aEnd.SetCol( r.aStart.Col() ); 872 } 873 else // pTmp != nullptr 874 { 875 applyStartToEndFlags(nFlags, nFlags2); 876 p = pTmp; 877 } 878 879 if (p[0] != 0) 880 { 881 // any trailing invalid character must invalidate the whole address. 882 nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID | 883 ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID); 884 return nFlags; 885 } 886 887 nFlags |= 888 ScRefFlags::ROW_VALID | ScRefFlags::ROW2_VALID | 889 ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS; 890 r.aStart.SetRow( 0 ); 891 r.aEnd.SetRow( rDoc.MaxRow() ); 892 893 return bOnlyAcceptSingle ? ScRefFlags::ZERO : nFlags; 894 } 895 896 return nBailOutFlags; 897 } 898 899 static const sal_Unicode* lcl_a1_get_col( const ScDocument& rDoc, 900 const sal_Unicode* p, 901 ScAddress* pAddr, 902 ScRefFlags* nFlags, 903 const OUString* pErrRef ) 904 { 905 SCCOL nCol; 906 907 if( *p == '$' ) 908 { 909 *nFlags |= ScRefFlags::COL_ABS; 910 p++; 911 } 912 913 if (pErrRef && lcl_isString( p, *pErrRef)) 914 { 915 p += pErrRef->getLength(); 916 *nFlags &= ~ScRefFlags::COL_VALID; 917 pAddr->SetCol(-1); 918 return p; 919 } 920 921 if( !rtl::isAsciiAlpha( *p ) ) 922 return nullptr; 923 924 nCol = sal::static_int_cast<SCCOL>( rtl::toAsciiUpperCase( *p++ ) - 'A' ); 925 const SCCOL nMaxCol = rDoc.MaxCol(); 926 while (nCol <= nMaxCol && rtl::isAsciiAlpha(*p)) 927 nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + rtl::toAsciiUpperCase( *p++ ) - 'A' ); 928 if( nCol > nMaxCol || rtl::isAsciiAlpha( *p ) ) 929 return nullptr; 930 931 *nFlags |= ScRefFlags::COL_VALID; 932 pAddr->SetCol( nCol ); 933 934 return p; 935 } 936 937 static const sal_Unicode* lcl_a1_get_row( const ScDocument& rDoc, 938 const sal_Unicode* p, 939 ScAddress* pAddr, 940 ScRefFlags* nFlags, 941 const OUString* pErrRef ) 942 { 943 const sal_Unicode *pEnd; 944 tools::Long n; 945 946 if( *p == '$' ) 947 { 948 *nFlags |= ScRefFlags::ROW_ABS; 949 p++; 950 } 951 952 if (pErrRef && lcl_isString( p, *pErrRef)) 953 { 954 p += pErrRef->getLength(); 955 *nFlags &= ~ScRefFlags::ROW_VALID; 956 pAddr->SetRow(-1); 957 return p; 958 } 959 960 n = sal_Unicode_strtol( p, &pEnd ) - 1; 961 if( nullptr == pEnd || p == pEnd || n < 0 || n > rDoc.MaxRow() ) 962 return nullptr; 963 964 *nFlags |= ScRefFlags::ROW_VALID; 965 pAddr->SetRow( static_cast<SCROW>(n) ); 966 967 return pEnd; 968 } 969 970 /// B:B or 2:2, but not B:2 or 2:B or B2:B or B:B2 or ... 971 static bool isValidSingleton( ScRefFlags nFlags, ScRefFlags nFlags2 ) 972 { 973 bool bCols = (nFlags & ScRefFlags::COL_VALID) && ((nFlags & ScRefFlags::COL2_VALID) || (nFlags2 & ScRefFlags::COL_VALID)); 974 bool bRows = (nFlags & ScRefFlags::ROW_VALID) && ((nFlags & ScRefFlags::ROW2_VALID) || (nFlags2 & ScRefFlags::ROW_VALID)); 975 return bCols != bRows; 976 } 977 978 static ScRefFlags lcl_ScRange_Parse_XL_A1( ScRange& r, 979 const sal_Unicode* p, 980 const ScDocument& rDoc, 981 bool bOnlyAcceptSingle, 982 ScAddress::ExternalInfo* pExtInfo, 983 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks, 984 sal_Int32* pSheetEndPos, 985 const OUString* pErrRef ) 986 { 987 const sal_Unicode* const pStart = p; 988 if (pSheetEndPos) 989 *pSheetEndPos = 0; 990 const sal_Unicode* tmp1, *tmp2; 991 OUString aExternDocName, aStartTabName, aEndTabName; // for external link table 992 ScRefFlags nFlags = ScRefFlags::VALID | ScRefFlags::TAB_VALID, nFlags2 = ScRefFlags::TAB_VALID; 993 994 p = r.Parse_XL_Header( p, rDoc, aExternDocName, aStartTabName, 995 aEndTabName, nFlags, bOnlyAcceptSingle, pExternalLinks, pErrRef ); 996 997 ScRefFlags nBailOutFlags = ScRefFlags::ZERO; 998 if (pSheetEndPos && pStart < p && (nFlags & ScRefFlags::TAB_VALID) && (nFlags & ScRefFlags::TAB_3D)) 999 { 1000 *pSheetEndPos = p - pStart; 1001 nBailOutFlags = ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D; 1002 } 1003 1004 if (!aExternDocName.isEmpty()) 1005 lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName, 1006 aStartTabName, aEndTabName, rDoc); 1007 1008 if( nullptr == p ) 1009 return nBailOutFlags; 1010 1011 tmp1 = lcl_a1_get_col( rDoc, p, &r.aStart, &nFlags, pErrRef); 1012 if( tmp1 == nullptr ) // Is it a row only reference 3:5 1013 { 1014 if( bOnlyAcceptSingle ) // by definition full row refs are ranges 1015 return nBailOutFlags; 1016 1017 tmp1 = lcl_a1_get_row( rDoc, p, &r.aStart, &nFlags, pErrRef); 1018 1019 tmp1 = lcl_eatWhiteSpace( tmp1 ); 1020 if( !tmp1 || *tmp1++ != ':' ) // Even a singleton requires ':' (eg 2:2) 1021 return nBailOutFlags; 1022 1023 tmp1 = lcl_eatWhiteSpace( tmp1 ); 1024 tmp2 = lcl_a1_get_row( rDoc, tmp1, &r.aEnd, &nFlags2, pErrRef); 1025 if( !tmp2 || *tmp2 != 0 ) // Must have fully parsed a singleton. 1026 return nBailOutFlags; 1027 1028 r.aStart.SetCol( 0 ); r.aEnd.SetCol( rDoc.MaxCol() ); 1029 nFlags |= 1030 ScRefFlags::COL_VALID | ScRefFlags::COL2_VALID | 1031 ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS; 1032 applyStartToEndFlags(nFlags, nFlags2); 1033 return nFlags; 1034 } 1035 1036 tmp2 = lcl_a1_get_row( rDoc, tmp1, &r.aStart, &nFlags, pErrRef); 1037 if( tmp2 == nullptr ) // check for col only reference F:H 1038 { 1039 if( bOnlyAcceptSingle ) // by definition full col refs are ranges 1040 return nBailOutFlags; 1041 1042 tmp1 = lcl_eatWhiteSpace( tmp1 ); 1043 if( *tmp1++ != ':' ) // Even a singleton requires ':' (eg F:F) 1044 return nBailOutFlags; 1045 1046 tmp1 = lcl_eatWhiteSpace( tmp1 ); 1047 tmp2 = lcl_a1_get_col( rDoc, tmp1, &r.aEnd, &nFlags2, pErrRef); 1048 if( !tmp2 || *tmp2 != 0 ) // Must have fully parsed a singleton. 1049 return nBailOutFlags; 1050 1051 r.aStart.SetRow( 0 ); r.aEnd.SetRow( rDoc.MaxRow() ); 1052 nFlags |= 1053 ScRefFlags::ROW_VALID | ScRefFlags::ROW2_VALID | 1054 ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS; 1055 applyStartToEndFlags(nFlags, nFlags2); 1056 return nFlags; 1057 } 1058 1059 // prepare as if it's a singleton, in case we want to fall back */ 1060 r.aEnd.SetCol( r.aStart.Col() ); 1061 r.aEnd.SetRow( r.aStart.Row() ); // don't overwrite sheet number as parsed in Parse_XL_Header() 1062 1063 if ( bOnlyAcceptSingle ) 1064 { 1065 if ( *tmp2 == 0 ) 1066 return nFlags; 1067 else 1068 { 1069 // any trailing invalid character must invalidate the address. 1070 nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID); 1071 return nFlags; 1072 } 1073 } 1074 1075 tmp2 = lcl_eatWhiteSpace( tmp2 ); 1076 if( *tmp2 != ':' ) 1077 { 1078 // Sheet1:Sheet2!C4 is a valid range, without a second sheet it is 1079 // not. Any trailing invalid character invalidates the range. 1080 if (*tmp2 == 0 && (nFlags & ScRefFlags::TAB2_3D)) 1081 { 1082 if (nFlags & ScRefFlags::COL_ABS) 1083 nFlags |= ScRefFlags::COL2_ABS; 1084 if (nFlags & ScRefFlags::ROW_ABS) 1085 nFlags |= ScRefFlags::ROW2_ABS; 1086 } 1087 else 1088 nFlags &= ~ScRefFlags(ScRefFlags::VALID | 1089 ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID | 1090 ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID); 1091 return nFlags; 1092 } 1093 1094 p = lcl_eatWhiteSpace( tmp2+1 ); // after ':' 1095 tmp1 = lcl_a1_get_col( rDoc, p, &r.aEnd, &nFlags2, pErrRef); 1096 if( !tmp1 && aEndTabName.isEmpty() ) // Probably the aEndTabName was specified after the first range 1097 { 1098 p = lcl_XL_ParseSheetRef( p, aEndTabName, false, nullptr, pErrRef); 1099 if( p ) 1100 { 1101 SCTAB nTab = 0; 1102 if( !aEndTabName.isEmpty() && rDoc.GetTable( aEndTabName, nTab ) ) 1103 { 1104 r.aEnd.SetTab( nTab ); 1105 nFlags |= ScRefFlags::TAB2_VALID | ScRefFlags::TAB2_3D | ScRefFlags::TAB2_ABS; 1106 } 1107 if (*p == '!' || *p == ':') 1108 p = lcl_eatWhiteSpace( p+1 ); 1109 tmp1 = lcl_a1_get_col( rDoc, p, &r.aEnd, &nFlags2, pErrRef); 1110 } 1111 } 1112 if( !tmp1 ) // strange, but maybe valid singleton 1113 return isValidSingleton( nFlags, nFlags2) ? nFlags : (nFlags & ~ScRefFlags::VALID); 1114 1115 tmp2 = lcl_a1_get_row( rDoc, tmp1, &r.aEnd, &nFlags2, pErrRef); 1116 if( !tmp2 ) // strange, but maybe valid singleton 1117 return isValidSingleton( nFlags, nFlags2) ? nFlags : (nFlags & ~ScRefFlags::VALID); 1118 1119 if ( *tmp2 != 0 ) 1120 { 1121 // any trailing invalid character must invalidate the range. 1122 nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID | 1123 ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID); 1124 return nFlags; 1125 } 1126 1127 applyStartToEndFlags(nFlags, nFlags2); 1128 return nFlags; 1129 } 1130 1131 /** 1132 @param p pointer to null-terminated sal_Unicode string 1133 @param rRawRes returns ScRefFlags::... flags without the final check for full 1134 validity that is applied to the return value, with which 1135 two addresses that form a column or row singleton range, 1136 e.g. A:A or 1:1, can be detected. Used in 1137 lcl_ScRange_Parse_OOo(). 1138 @param pRange pointer to range where rAddr effectively is *pRange->aEnd, 1139 used in conjunction with pExtInfo to determine the tab span 1140 of a 3D reference. 1141 */ 1142 static ScRefFlags lcl_ScAddress_Parse_OOo( const sal_Unicode* p, const ScDocument& rDoc, ScAddress& rAddr, 1143 ScRefFlags& rRawRes, 1144 ScAddress::ExternalInfo* pExtInfo, 1145 ScRange* pRange, 1146 sal_Int32* pSheetEndPos, 1147 const OUString* pErrRef ) 1148 { 1149 const sal_Unicode* const pStart = p; 1150 if (pSheetEndPos) 1151 *pSheetEndPos = 0; 1152 ScRefFlags nRes = ScRefFlags::ZERO; 1153 rRawRes = ScRefFlags::ZERO; 1154 OUString aDocName; // the pure Document Name 1155 OUString aTab; 1156 bool bExtDoc = false; 1157 bool bExtDocInherited = false; 1158 1159 // Lets see if this is a reference to something in an external file. A 1160 // document name is always quoted and has a trailing #. 1161 if (*p == '\'') 1162 { 1163 OUString aTmp; 1164 p = parseQuotedName(p, aTmp); 1165 aDocName = aTmp; 1166 if (*p++ == SC_COMPILER_FILE_TAB_SEP) 1167 bExtDoc = true; 1168 else 1169 // This is not a document name. Perhaps a quoted relative table 1170 // name. 1171 p = pStart; 1172 } 1173 else if (pExtInfo && pExtInfo->mbExternal) 1174 { 1175 // This is an external reference. 1176 bExtDoc = bExtDocInherited = true; 1177 } 1178 1179 SCCOL nCol = 0; 1180 SCROW nRow = 0; 1181 SCTAB nTab = 0; 1182 ScRefFlags nBailOutFlags = ScRefFlags::ZERO; 1183 ScRefFlags nBits = ScRefFlags::TAB_VALID; 1184 const sal_Unicode* q; 1185 if ( ScGlobal::FindUnquoted( p, '.') ) 1186 { 1187 nRes |= ScRefFlags::TAB_3D; 1188 if ( bExtDoc ) 1189 nRes |= ScRefFlags::TAB_ABS; 1190 if (*p == '$') 1191 { 1192 nRes |= ScRefFlags::TAB_ABS; 1193 p++; 1194 } 1195 1196 if (pErrRef && lcl_isString( p, *pErrRef) && p[pErrRef->getLength()] == '.') 1197 { 1198 // #REF! particle of an invalidated reference plus sheet separator. 1199 p += pErrRef->getLength() + 1; 1200 nRes &= ~ScRefFlags::TAB_VALID; 1201 nTab = -1; 1202 } 1203 else 1204 { 1205 if (*p == '\'') 1206 { 1207 // Tokens that start at ' can have anything in them until a final 1208 // ' but '' marks an escaped '. We've earlier guaranteed that a 1209 // string containing '' will be surrounded by '. 1210 p = parseQuotedName(p, aTab); 1211 } 1212 else 1213 { 1214 OUStringBuffer aTabAcc; 1215 while (*p) 1216 { 1217 if( *p == '.') 1218 break; 1219 1220 if( *p == '\'' ) 1221 { 1222 p++; break; 1223 } 1224 aTabAcc.append(*p); 1225 p++; 1226 } 1227 aTab = aTabAcc.makeStringAndClear(); 1228 } 1229 if( *p++ != '.' ) 1230 nBits = ScRefFlags::ZERO; 1231 1232 if (!bExtDoc && !rDoc.GetTable( aTab, nTab )) 1233 { 1234 // Specified table name is not found in this document. Assume this is an external document. 1235 aDocName = aTab; 1236 sal_Int32 n = aDocName.lastIndexOf('.'); 1237 if (n > 0) 1238 { 1239 // Extension found. Strip it. 1240 aTab = aTab.replaceAt(n, 1, ""); 1241 bExtDoc = true; 1242 } 1243 else 1244 // No extension found. This is probably not an external document. 1245 nBits = ScRefFlags::ZERO; 1246 } 1247 } 1248 1249 if (pSheetEndPos && (nBits & ScRefFlags::TAB_VALID)) 1250 { 1251 *pSheetEndPos = p - pStart; 1252 nBailOutFlags = ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D; 1253 } 1254 } 1255 else 1256 { 1257 if (bExtDoc && !bExtDocInherited) 1258 return nRes; // After a document a sheet must follow. 1259 nTab = rAddr.Tab(); 1260 } 1261 nRes |= nBits; 1262 1263 q = p; 1264 if (*p) 1265 { 1266 nBits = ScRefFlags::COL_VALID; 1267 if (*p == '$') 1268 { 1269 nBits |= ScRefFlags::COL_ABS; 1270 p++; 1271 } 1272 1273 if (pErrRef && lcl_isString( p, *pErrRef)) 1274 { 1275 // #REF! particle of an invalidated reference. 1276 p += pErrRef->getLength(); 1277 nBits &= ~ScRefFlags::COL_VALID; 1278 nCol = -1; 1279 } 1280 else 1281 { 1282 const SCCOL nMaxCol = rDoc.MaxCol(); 1283 if (rtl::isAsciiAlpha( *p )) 1284 { 1285 nCol = sal::static_int_cast<SCCOL>( rtl::toAsciiUpperCase( *p++ ) - 'A' ); 1286 while (nCol < nMaxCol && rtl::isAsciiAlpha(*p)) 1287 nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + rtl::toAsciiUpperCase( *p++ ) - 'A' ); 1288 } 1289 else 1290 nBits = ScRefFlags::ZERO; 1291 1292 if (nCol > nMaxCol || (*p && *p != '$' && !rtl::isAsciiDigit( *p ) && 1293 (!pErrRef || !lcl_isString( p, *pErrRef)))) 1294 nBits = ScRefFlags::ZERO; 1295 if( nBits == ScRefFlags::ZERO ) 1296 p = q; 1297 } 1298 nRes |= nBits; 1299 } 1300 1301 q = p; 1302 if (*p) 1303 { 1304 nBits = ScRefFlags::ROW_VALID; 1305 if (*p == '$') 1306 { 1307 nBits |= ScRefFlags::ROW_ABS; 1308 p++; 1309 } 1310 1311 if (pErrRef && lcl_isString( p, *pErrRef)) 1312 { 1313 // #REF! particle of an invalidated reference. 1314 p += pErrRef->getLength(); 1315 // Clearing the ROW_VALID bit here is not possible because of the 1316 // check at the end whether only a valid column was detected in 1317 // which case all bits are cleared because it could be any other 1318 // name. Instead, set to an absolute invalid row value. This will 1319 // display a $#REF! instead of #REF! if the error value was 1320 // relative, but live with it. 1321 nBits |= ScRefFlags::ROW_ABS; 1322 nRow = -1; 1323 } 1324 else 1325 { 1326 if( !rtl::isAsciiDigit( *p ) ) 1327 { 1328 nBits = ScRefFlags::ZERO; 1329 nRow = SCROW(-1); 1330 } 1331 else 1332 { 1333 tools::Long n = rtl_ustr_toInt32( p, 10 ) - 1; 1334 while (rtl::isAsciiDigit( *p )) 1335 p++; 1336 const SCROW nMaxRow = rDoc.MaxRow(); 1337 if( n < 0 || n > nMaxRow ) 1338 nBits = ScRefFlags::ZERO; 1339 nRow = static_cast<SCROW>(n); 1340 } 1341 if( nBits == ScRefFlags::ZERO ) 1342 p = q; 1343 } 1344 nRes |= nBits; 1345 } 1346 1347 rAddr.Set( nCol, nRow, nTab ); 1348 1349 if (!*p && bExtDoc) 1350 { 1351 ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager(); 1352 1353 // Need document name if inherited. 1354 if (bExtDocInherited) 1355 { 1356 // The FileId was created using the original file name, so 1357 // obtain that. Otherwise lcl_ScRange_External_TabSpan() would 1358 // retrieve a FileId for the real name and bail out if that 1359 // differed from pExtInfo->mnFileId, as is the case when 1360 // loading documents that refer external files relative to the 1361 // current own document but were saved from a different path 1362 // than loaded. 1363 const OUString* pFileName = pRefMgr->getExternalFileName( pExtInfo->mnFileId, true); 1364 if (pFileName) 1365 aDocName = *pFileName; 1366 else 1367 nRes = ScRefFlags::ZERO; 1368 } 1369 pRefMgr->convertToAbsName(aDocName); 1370 1371 if ((!pExtInfo || !pExtInfo->mbExternal) && pRefMgr->isOwnDocument(aDocName)) 1372 { 1373 if (!rDoc.GetTable( aTab, nTab )) 1374 nRes = ScRefFlags::ZERO; 1375 else 1376 { 1377 rAddr.SetTab( nTab); 1378 nRes |= ScRefFlags::TAB_VALID; 1379 } 1380 } 1381 else 1382 { 1383 if (!pExtInfo) 1384 nRes = ScRefFlags::ZERO; 1385 else 1386 { 1387 if (!pExtInfo->mbExternal) 1388 { 1389 sal_uInt16 nFileId = pRefMgr->getExternalFileId(aDocName); 1390 1391 pExtInfo->mbExternal = true; 1392 pExtInfo->maTabName = aTab; 1393 pExtInfo->mnFileId = nFileId; 1394 1395 if (pRefMgr->getSingleRefToken(nFileId, aTab, 1396 ScAddress(nCol, nRow, 0), nullptr, 1397 &nTab)) 1398 { 1399 rAddr.SetTab( nTab); 1400 nRes |= ScRefFlags::TAB_VALID; 1401 } 1402 else 1403 nRes = ScRefFlags::ZERO; 1404 } 1405 else 1406 { 1407 // This is a call for the second part of the reference, 1408 // we must have the range to adapt tab span. 1409 if (!pRange) 1410 nRes = ScRefFlags::ZERO; 1411 else 1412 { 1413 ScRefFlags nFlags = nRes | ScRefFlags::TAB2_VALID; 1414 if (!lcl_ScRange_External_TabSpan( *pRange, nFlags, 1415 pExtInfo, aDocName, 1416 pExtInfo->maTabName, aTab, rDoc)) 1417 nRes &= ~ScRefFlags::TAB_VALID; 1418 else 1419 { 1420 if (nFlags & ScRefFlags::TAB2_VALID) 1421 { 1422 rAddr.SetTab( pRange->aEnd.Tab()); 1423 nRes |= ScRefFlags::TAB_VALID; 1424 } 1425 else 1426 nRes &= ~ScRefFlags::TAB_VALID; 1427 } 1428 } 1429 } 1430 } 1431 } 1432 } 1433 else if (bExtDoc && pExtInfo && !bExtDocInherited && !pExtInfo->mbExternal && pSheetEndPos) 1434 { 1435 // Pass partial info up to caller, there may be an external name 1436 // following, and if after *pSheetEndPos it's external sheet-local. 1437 // For global names aTab is empty and *pSheetEndPos==0. 1438 pExtInfo->mbExternal = true; 1439 pExtInfo->maTabName = aTab; 1440 pExtInfo->mnFileId = rDoc.GetExternalRefManager()->getExternalFileId(aDocName); 1441 } 1442 1443 rRawRes |= nRes; 1444 1445 if ( !(nRes & ScRefFlags::ROW_VALID) && (nRes & ScRefFlags::COL_VALID) 1446 && !( (nRes & ScRefFlags::TAB_3D) && (nRes & ScRefFlags::TAB_VALID)) ) 1447 { // no Row, no Tab, but Col => DM (...), B (...) et al 1448 nRes = ScRefFlags::ZERO; 1449 } 1450 if( !*p ) 1451 { 1452 ScRefFlags nMask = nRes & ( ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID ); 1453 if( nMask == ( ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID ) ) 1454 nRes |= ScRefFlags::VALID; 1455 } 1456 else 1457 nRes = rRawRes = nBailOutFlags; 1458 return nRes; 1459 } 1460 1461 static ScRefFlags lcl_ScAddress_Parse ( const sal_Unicode* p, const ScDocument& rDoc, ScAddress& rAddr, 1462 const ScAddress::Details& rDetails, 1463 ScAddress::ExternalInfo* pExtInfo, 1464 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks, 1465 sal_Int32* pSheetEndPos, 1466 const OUString* pErrRef ) 1467 { 1468 if( !*p ) 1469 return ScRefFlags::ZERO; 1470 1471 switch (rDetails.eConv) 1472 { 1473 case formula::FormulaGrammar::CONV_XL_A1: 1474 case formula::FormulaGrammar::CONV_XL_OOX: 1475 { 1476 ScRange rRange = rAddr; 1477 ScRefFlags nFlags = lcl_ScRange_Parse_XL_A1( 1478 rRange, p, rDoc, true, pExtInfo, 1479 (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : nullptr), 1480 pSheetEndPos, pErrRef); 1481 rAddr = rRange.aStart; 1482 return nFlags; 1483 } 1484 case formula::FormulaGrammar::CONV_XL_R1C1: 1485 { 1486 ScRange rRange = rAddr; 1487 ScRefFlags nFlags = lcl_ScRange_Parse_XL_R1C1( rRange, p, rDoc, rDetails, true, pExtInfo, pSheetEndPos); 1488 rAddr = rRange.aStart; 1489 return nFlags; 1490 } 1491 default : 1492 case formula::FormulaGrammar::CONV_OOO: 1493 { 1494 ScRefFlags nRawRes = ScRefFlags::ZERO; 1495 return lcl_ScAddress_Parse_OOo( p, rDoc, rAddr, nRawRes, pExtInfo, nullptr, pSheetEndPos, pErrRef); 1496 } 1497 } 1498 } 1499 1500 bool ConvertSingleRef( const ScDocument& rDoc, const OUString& rRefString, 1501 SCTAB nDefTab, ScRefAddress& rRefAddress, 1502 const ScAddress::Details& rDetails, 1503 ScAddress::ExternalInfo* pExtInfo /* = NULL */ ) 1504 { 1505 bool bRet = false; 1506 if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == -1)) 1507 { 1508 ScAddress aAddr( 0, 0, nDefTab ); 1509 ScRefFlags nRes = aAddr.Parse( rRefString, rDoc, rDetails, pExtInfo); 1510 if ( nRes & ScRefFlags::VALID ) 1511 { 1512 rRefAddress.Set( aAddr, 1513 ((nRes & ScRefFlags::COL_ABS) == ScRefFlags::ZERO), 1514 ((nRes & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO), 1515 ((nRes & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO)); 1516 bRet = true; 1517 } 1518 } 1519 return bRet; 1520 } 1521 1522 bool ConvertDoubleRef( const ScDocument& rDoc, const OUString& rRefString, SCTAB nDefTab, 1523 ScRefAddress& rStartRefAddress, ScRefAddress& rEndRefAddress, 1524 const ScAddress::Details& rDetails, 1525 ScAddress::ExternalInfo* pExtInfo /* = NULL */ ) 1526 { 1527 bool bRet = false; 1528 if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == -1)) 1529 { 1530 ScRange aRange( ScAddress( 0, 0, nDefTab)); 1531 ScRefFlags nRes = aRange.Parse( rRefString, rDoc, rDetails, pExtInfo); 1532 if ( nRes & ScRefFlags::VALID ) 1533 { 1534 rStartRefAddress.Set( aRange.aStart, 1535 ((nRes & ScRefFlags::COL_ABS) == ScRefFlags::ZERO), 1536 ((nRes & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO), 1537 ((nRes & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO)); 1538 rEndRefAddress.Set( aRange.aEnd, 1539 ((nRes & ScRefFlags::COL2_ABS) == ScRefFlags::ZERO), 1540 ((nRes & ScRefFlags::ROW2_ABS) == ScRefFlags::ZERO), 1541 ((nRes & ScRefFlags::TAB2_ABS) == ScRefFlags::ZERO)); 1542 bRet = true; 1543 } 1544 } 1545 return bRet; 1546 } 1547 1548 ScRefFlags ScAddress::Parse( const OUString& r, const ScDocument& rDoc, 1549 const Details& rDetails, 1550 ExternalInfo* pExtInfo, 1551 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks, 1552 sal_Int32* pSheetEndPos, 1553 const OUString* pErrRef ) 1554 { 1555 return lcl_ScAddress_Parse( r.getStr(), rDoc, *this, rDetails, pExtInfo, pExternalLinks, pSheetEndPos, pErrRef); 1556 } 1557 1558 bool ScRange::Intersects( const ScRange& rRange ) const 1559 { 1560 return !( 1561 std::min( aEnd.Col(), rRange.aEnd.Col() ) < std::max( aStart.Col(), rRange.aStart.Col() ) 1562 || std::min( aEnd.Row(), rRange.aEnd.Row() ) < std::max( aStart.Row(), rRange.aStart.Row() ) 1563 || std::min( aEnd.Tab(), rRange.aEnd.Tab() ) < std::max( aStart.Tab(), rRange.aStart.Tab() ) 1564 ); 1565 } 1566 1567 ScRange ScRange::Intersection( const ScRange& rOther ) const 1568 { 1569 SCCOL nCol1 = std::max(aStart.Col(), rOther.aStart.Col()); 1570 SCCOL nCol2 = std::min(aEnd.Col(), rOther.aEnd.Col()); 1571 SCROW nRow1 = std::max(aStart.Row(), rOther.aStart.Row()); 1572 SCROW nRow2 = std::min(aEnd.Row(), rOther.aEnd.Row()); 1573 SCTAB nTab1 = std::max(aStart.Tab(), rOther.aStart.Tab()); 1574 SCTAB nTab2 = std::min(aEnd.Tab(), rOther.aEnd.Tab()); 1575 1576 if (nCol1 > nCol2 || nRow1 > nRow2 || nTab1 > nTab2) 1577 return ScRange(ScAddress::INITIALIZE_INVALID); 1578 1579 return ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); 1580 } 1581 1582 void ScRange::PutInOrder() 1583 { 1584 SCCOL nTempCol; 1585 if ( aEnd.Col() < (nTempCol = aStart.Col()) ) 1586 { 1587 aStart.SetCol(aEnd.Col()); 1588 aEnd.SetCol(nTempCol); 1589 } 1590 SCROW nTempRow; 1591 if ( aEnd.Row() < (nTempRow = aStart.Row()) ) 1592 { 1593 aStart.SetRow(aEnd.Row()); 1594 aEnd.SetRow(nTempRow); 1595 } 1596 SCTAB nTempTab; 1597 if ( aEnd.Tab() < (nTempTab = aStart.Tab()) ) 1598 { 1599 aStart.SetTab(aEnd.Tab()); 1600 aEnd.SetTab(nTempTab); 1601 } 1602 } 1603 1604 void ScRange::ExtendTo( const ScRange& rRange ) 1605 { 1606 OSL_ENSURE( rRange.IsValid(), "ScRange::ExtendTo - cannot extend to invalid range" ); 1607 if( IsValid() ) 1608 { 1609 aStart.SetCol( std::min( aStart.Col(), rRange.aStart.Col() ) ); 1610 aStart.SetRow( std::min( aStart.Row(), rRange.aStart.Row() ) ); 1611 aStart.SetTab( std::min( aStart.Tab(), rRange.aStart.Tab() ) ); 1612 aEnd.SetCol( std::max( aEnd.Col(), rRange.aEnd.Col() ) ); 1613 aEnd.SetRow( std::max( aEnd.Row(), rRange.aEnd.Row() ) ); 1614 aEnd.SetTab( std::max( aEnd.Tab(), rRange.aEnd.Tab() ) ); 1615 } 1616 else 1617 *this = rRange; 1618 } 1619 1620 static ScRefFlags lcl_ScRange_Parse_OOo( ScRange& rRange, 1621 const OUString& r, 1622 const ScDocument& rDoc, 1623 ScAddress::ExternalInfo* pExtInfo, 1624 const OUString* pErrRef ) 1625 { 1626 ScRefFlags nRes1 = ScRefFlags::ZERO, nRes2 = ScRefFlags::ZERO; 1627 sal_Int32 nPos = ScGlobal::FindUnquoted( r, ':'); 1628 if (nPos != -1) 1629 { 1630 OUStringBuffer aTmp(r); 1631 aTmp[nPos] = 0; 1632 const sal_Unicode* p = aTmp.getStr(); 1633 ScRefFlags nRawRes1 = ScRefFlags::ZERO; 1634 nRes1 = lcl_ScAddress_Parse_OOo( p, rDoc, rRange.aStart, nRawRes1, pExtInfo, nullptr, nullptr, pErrRef); 1635 if ((nRes1 != ScRefFlags::ZERO) || 1636 ((nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) && 1637 (nRawRes1 & ScRefFlags::TAB_VALID))) 1638 { 1639 rRange.aEnd = rRange.aStart; // sheet must be initialized identical to first sheet 1640 ScRefFlags nRawRes2 = ScRefFlags::ZERO; 1641 nRes2 = lcl_ScAddress_Parse_OOo( p + nPos+ 1, rDoc, rRange.aEnd, nRawRes2, 1642 pExtInfo, &rRange, nullptr, pErrRef); 1643 if (!((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID)) && 1644 // If not fully valid addresses, check if both have a valid 1645 // column or row, and both have valid (or omitted) sheet references. 1646 (nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) && 1647 (nRawRes1 & ScRefFlags::TAB_VALID) && 1648 (nRawRes2 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) && 1649 (nRawRes2 & ScRefFlags::TAB_VALID) && 1650 // Both must be column XOR row references, A:A or 1:1 but not A:1 or 1:A 1651 ((nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) == 1652 (nRawRes2 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)))) 1653 { 1654 nRes1 = nRawRes1 | ScRefFlags::VALID; 1655 nRes2 = nRawRes2 | ScRefFlags::VALID; 1656 if (nRawRes1 & ScRefFlags::COL_VALID) 1657 { 1658 rRange.aStart.SetRow(0); 1659 rRange.aEnd.SetRow(rDoc.MaxRow()); 1660 nRes1 |= ScRefFlags::ROW_VALID | ScRefFlags::ROW_ABS; 1661 nRes2 |= ScRefFlags::ROW_VALID | ScRefFlags::ROW_ABS; 1662 } 1663 else 1664 { 1665 rRange.aStart.SetCol(0); 1666 rRange.aEnd.SetCol( rDoc.MaxCol() ); 1667 nRes1 |= ScRefFlags::COL_VALID | ScRefFlags::COL_ABS; 1668 nRes2 |= ScRefFlags::COL_VALID | ScRefFlags::COL_ABS; 1669 } 1670 } 1671 else if ((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID)) 1672 { 1673 // Flag entire column/row references so they can be displayed 1674 // as such. If the sticky reference parts are not both 1675 // absolute or relative, assume that the user thought about 1676 // something we should not touch. 1677 if (rRange.aStart.Row() == 0 && rRange.aEnd.Row() == rDoc.MaxRow() && 1678 ((nRes1 & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO) && 1679 ((nRes2 & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO)) 1680 { 1681 nRes1 |= ScRefFlags::ROW_ABS; 1682 nRes2 |= ScRefFlags::ROW_ABS; 1683 } 1684 else if (rRange.aStart.Col() == 0 && rRange.aEnd.Col() == rDoc.MaxCol() && 1685 ((nRes1 & ScRefFlags::COL_ABS) == ScRefFlags::ZERO) && ((nRes2 & ScRefFlags::COL_ABS) == ScRefFlags::ZERO)) 1686 { 1687 nRes1 |= ScRefFlags::COL_ABS; 1688 nRes2 |= ScRefFlags::COL_ABS; 1689 } 1690 } 1691 if ((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID)) 1692 { 1693 // PutInOrder / Justify 1694 ScRefFlags nMask, nBits1, nBits2; 1695 SCCOL nTempCol; 1696 if ( rRange.aEnd.Col() < (nTempCol = rRange.aStart.Col()) ) 1697 { 1698 rRange.aStart.SetCol(rRange.aEnd.Col()); rRange.aEnd.SetCol(nTempCol); 1699 nMask = (ScRefFlags::COL_VALID | ScRefFlags::COL_ABS); 1700 nBits1 = nRes1 & nMask; 1701 nBits2 = nRes2 & nMask; 1702 nRes1 = (nRes1 & ~nMask) | nBits2; 1703 nRes2 = (nRes2 & ~nMask) | nBits1; 1704 } 1705 SCROW nTempRow; 1706 if ( rRange.aEnd.Row() < (nTempRow = rRange.aStart.Row()) ) 1707 { 1708 rRange.aStart.SetRow(rRange.aEnd.Row()); rRange.aEnd.SetRow(nTempRow); 1709 nMask = (ScRefFlags::ROW_VALID | ScRefFlags::ROW_ABS); 1710 nBits1 = nRes1 & nMask; 1711 nBits2 = nRes2 & nMask; 1712 nRes1 = (nRes1 & ~nMask) | nBits2; 1713 nRes2 = (nRes2 & ~nMask) | nBits1; 1714 } 1715 SCTAB nTempTab; 1716 if ( rRange.aEnd.Tab() < (nTempTab = rRange.aStart.Tab()) ) 1717 { 1718 rRange.aStart.SetTab(rRange.aEnd.Tab()); rRange.aEnd.SetTab(nTempTab); 1719 nMask = (ScRefFlags::TAB_VALID | ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D); 1720 nBits1 = nRes1 & nMask; 1721 nBits2 = nRes2 & nMask; 1722 nRes1 = (nRes1 & ~nMask) | nBits2; 1723 nRes2 = (nRes2 & ~nMask) | nBits1; 1724 } 1725 if ( ((nRes1 & ( ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D )) 1726 == ( ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D )) 1727 && !(nRes2 & ScRefFlags::TAB_3D) ) 1728 nRes2 |= ScRefFlags::TAB_ABS; 1729 } 1730 else 1731 { 1732 // Don't leave around valid half references. 1733 nRes1 = nRes2 = ScRefFlags::ZERO; 1734 } 1735 } 1736 } 1737 applyStartToEndFlags(nRes1, nRes2 & ScRefFlags::BITS); 1738 nRes1 |= nRes2 & ScRefFlags::VALID; 1739 return nRes1; 1740 } 1741 1742 ScRefFlags ScRange::Parse( const OUString& rString, const ScDocument& rDoc, 1743 const ScAddress::Details& rDetails, 1744 ScAddress::ExternalInfo* pExtInfo, 1745 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks, 1746 const OUString* pErrRef ) 1747 { 1748 if (rString.isEmpty()) 1749 return ScRefFlags::ZERO; 1750 1751 switch (rDetails.eConv) 1752 { 1753 case formula::FormulaGrammar::CONV_XL_A1: 1754 case formula::FormulaGrammar::CONV_XL_OOX: 1755 { 1756 return lcl_ScRange_Parse_XL_A1( *this, rString.getStr(), rDoc, false, pExtInfo, 1757 (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : nullptr), 1758 nullptr, pErrRef ); 1759 } 1760 1761 case formula::FormulaGrammar::CONV_XL_R1C1: 1762 { 1763 return lcl_ScRange_Parse_XL_R1C1( *this, rString.getStr(), rDoc, rDetails, false, pExtInfo, nullptr ); 1764 } 1765 1766 default: 1767 case formula::FormulaGrammar::CONV_OOO: 1768 { 1769 return lcl_ScRange_Parse_OOo( *this, rString, rDoc, pExtInfo, pErrRef ); 1770 } 1771 } 1772 } 1773 1774 // Accept a full range, or an address 1775 ScRefFlags ScRange::ParseAny( const OUString& rString, const ScDocument& rDoc, 1776 const ScAddress::Details& rDetails ) 1777 { 1778 ScRefFlags nRet = Parse( rString, rDoc, rDetails ); 1779 const ScRefFlags nValid = ScRefFlags::VALID | ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID; 1780 1781 if ( (nRet & nValid) != nValid ) 1782 { 1783 ScAddress aAdr(aStart);//initialize with currentPos as fallback for table number 1784 nRet = aAdr.Parse( rString, rDoc, rDetails ); 1785 if ( nRet & ScRefFlags::VALID ) 1786 aStart = aEnd = aAdr; 1787 } 1788 return nRet; 1789 } 1790 1791 // Parse only full row references 1792 ScRefFlags ScRange::ParseCols( const ScDocument& rDoc, 1793 const OUString& rStr, 1794 const ScAddress::Details& rDetails ) 1795 { 1796 if (rStr.isEmpty()) 1797 return ScRefFlags::ZERO; 1798 1799 const sal_Unicode* p = rStr.getStr(); 1800 ScRefFlags nRes = ScRefFlags::ZERO; 1801 ScRefFlags ignored = ScRefFlags::ZERO; 1802 1803 switch (rDetails.eConv) 1804 { 1805 default : 1806 case formula::FormulaGrammar::CONV_OOO: // No full col refs in OOO yet, assume XL notation 1807 case formula::FormulaGrammar::CONV_XL_A1: 1808 case formula::FormulaGrammar::CONV_XL_OOX: 1809 if (nullptr != (p = lcl_a1_get_col( rDoc, p, &aStart, &ignored, nullptr) ) ) 1810 { 1811 if( p[0] == ':') 1812 { 1813 if( nullptr != (p = lcl_a1_get_col( rDoc, p+1, &aEnd, &ignored, nullptr))) 1814 { 1815 nRes = ScRefFlags::COL_VALID; 1816 } 1817 } 1818 else 1819 { 1820 aEnd = aStart; 1821 nRes = ScRefFlags::COL_VALID; 1822 } 1823 } 1824 break; 1825 1826 case formula::FormulaGrammar::CONV_XL_R1C1: 1827 if ((p[0] == 'C' || p[0] == 'c') && 1828 nullptr != (p = lcl_r1c1_get_col( p, rDetails, &aStart, &ignored ))) 1829 { 1830 if( p[0] == ':') 1831 { 1832 if( (p[1] == 'C' || p[1] == 'c') && 1833 nullptr != (p = lcl_r1c1_get_col( p+1, rDetails, &aEnd, &ignored ))) 1834 { 1835 nRes = ScRefFlags::COL_VALID; 1836 } 1837 } 1838 else 1839 { 1840 aEnd = aStart; 1841 nRes = ScRefFlags::COL_VALID; 1842 } 1843 } 1844 break; 1845 } 1846 1847 return (p != nullptr && *p == '\0') ? nRes : ScRefFlags::ZERO; 1848 } 1849 1850 // Parse only full row references 1851 void ScRange::ParseRows( const ScDocument& rDoc, 1852 const OUString& rStr, 1853 const ScAddress::Details& rDetails ) 1854 { 1855 if (rStr.isEmpty()) 1856 return; 1857 1858 const sal_Unicode* p = rStr.getStr(); 1859 ScRefFlags ignored = ScRefFlags::ZERO; 1860 1861 switch (rDetails.eConv) 1862 { 1863 default : 1864 case formula::FormulaGrammar::CONV_OOO: // No full row refs in OOO yet, assume XL notation 1865 case formula::FormulaGrammar::CONV_XL_A1: 1866 case formula::FormulaGrammar::CONV_XL_OOX: 1867 if (nullptr != (p = lcl_a1_get_row( rDoc, p, &aStart, &ignored, nullptr) ) ) 1868 { 1869 if( p[0] == ':') 1870 { 1871 lcl_a1_get_row( rDoc, p+1, &aEnd, &ignored, nullptr); 1872 } 1873 else 1874 { 1875 aEnd = aStart; 1876 } 1877 } 1878 break; 1879 1880 case formula::FormulaGrammar::CONV_XL_R1C1: 1881 if ((p[0] == 'R' || p[0] == 'r') && 1882 nullptr != (p = lcl_r1c1_get_row( rDoc.GetSheetLimits(), p, rDetails, &aStart, &ignored ))) 1883 { 1884 if( p[0] == ':') 1885 { 1886 if( p[1] == 'R' || p[1] == 'r' ) 1887 { 1888 lcl_r1c1_get_row( rDoc.GetSheetLimits(), p+1, rDetails, &aEnd, &ignored ); 1889 } 1890 } 1891 else 1892 { 1893 aEnd = aStart; 1894 } 1895 } 1896 break; 1897 } 1898 } 1899 1900 template<typename T > static void lcl_ScColToAlpha( T& rBuf, SCCOL nCol ) 1901 { 1902 if (nCol < 26*26) 1903 { 1904 if (nCol < 26) 1905 rBuf.append( static_cast<char>( 'A' + nCol )); 1906 else 1907 { 1908 rBuf.append( static_cast<char>( 'A' + nCol / 26 - 1 )); 1909 rBuf.append( static_cast<char>( 'A' + nCol % 26 )); 1910 } 1911 } 1912 else 1913 { 1914 sal_Int32 nInsert = rBuf.getLength(); 1915 while (nCol >= 26) 1916 { 1917 SCCOL nC = nCol % 26; 1918 rBuf.insert(nInsert, static_cast<char> ( 'A' + nC )); 1919 nCol = sal::static_int_cast<SCCOL>( nCol - nC ); 1920 nCol = nCol / 26 - 1; 1921 } 1922 rBuf.insert(nInsert, static_cast<char> ( 'A' + nCol )); 1923 } 1924 } 1925 1926 void ScColToAlpha( OUStringBuffer& rBuf, SCCOL nCol) 1927 { 1928 lcl_ScColToAlpha(rBuf, nCol); 1929 } 1930 1931 template <typename T > static void lcl_a1_append_c ( T &rString, int nCol, bool bIsAbs ) 1932 { 1933 if( bIsAbs ) 1934 rString.append("$"); 1935 lcl_ScColToAlpha( rString, sal::static_int_cast<SCCOL>(nCol) ); 1936 } 1937 1938 template <typename T > static void lcl_a1_append_r ( T &rString, sal_Int32 nRow, bool bIsAbs ) 1939 { 1940 if ( bIsAbs ) 1941 rString.append("$"); 1942 rString.append( nRow + 1 ); 1943 } 1944 1945 template <typename T > static void lcl_r1c1_append_c ( T &rString, sal_Int32 nCol, bool bIsAbs, 1946 const ScAddress::Details& rDetails ) 1947 { 1948 rString.append("C"); 1949 if (bIsAbs) 1950 { 1951 rString.append( nCol + 1 ); 1952 } 1953 else 1954 { 1955 nCol -= rDetails.nCol; 1956 if (nCol != 0) { 1957 rString.append("[").append(nCol).append("]"); 1958 } 1959 } 1960 } 1961 1962 template <typename T > static void lcl_r1c1_append_r ( T &rString, sal_Int32 nRow, bool bIsAbs, 1963 const ScAddress::Details& rDetails ) 1964 { 1965 rString.append("R"); 1966 if (bIsAbs) 1967 { 1968 rString.append( nRow + 1 ); 1969 } 1970 else 1971 { 1972 nRow -= rDetails.nRow; 1973 if (nRow != 0) { 1974 rString.append("[").append(nRow).append("]"); 1975 } 1976 } 1977 } 1978 1979 static OUString getFileNameFromDoc( const ScDocument* pDoc ) 1980 { 1981 // TODO : er points at ScGlobal::GetAbsDocName() 1982 // as a better template. Look into it 1983 OUString sFileName; 1984 SfxObjectShell* pShell; 1985 1986 if( nullptr != pDoc && 1987 nullptr != (pShell = pDoc->GetDocumentShell() ) ) 1988 { 1989 uno::Reference< frame::XModel > xModel = pShell->GetModel(); 1990 if( xModel.is() ) 1991 { 1992 if( !xModel->getURL().isEmpty() ) 1993 { 1994 INetURLObject aURL( xModel->getURL() ); 1995 sFileName = aURL.GetLastName(); 1996 } 1997 else 1998 sFileName = pShell->GetTitle(); 1999 } 2000 } 2001 return sFileName; 2002 } 2003 2004 2005 static void lcl_string_append(OUStringBuffer &rString, std::u16string_view sString) 2006 { 2007 rString.append(sString); 2008 } 2009 2010 static void lcl_string_append(OStringBuffer &rString, std::u16string_view sString) 2011 { 2012 rString.append(OUStringToOString( sString, RTL_TEXTENCODING_UTF8 )); 2013 } 2014 2015 template<typename T > static void lcl_Format( T& r, SCTAB nTab, SCROW nRow, SCCOL nCol, ScRefFlags nFlags, 2016 const ScDocument* pDoc, 2017 const ScAddress::Details& rDetails) 2018 { 2019 if( nFlags & ScRefFlags::VALID ) 2020 nFlags |= ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID; 2021 if( pDoc && (nFlags & ScRefFlags::TAB_VALID ) ) 2022 { 2023 if ( nTab < 0 || nTab >= pDoc->GetTableCount() ) 2024 { 2025 lcl_string_append(r, ScCompiler::GetNativeSymbol(ocErrRef)); 2026 return; 2027 } 2028 if( nFlags & ScRefFlags::TAB_3D ) 2029 { 2030 OUString aTabName, aDocName; 2031 pDoc->GetName(nTab, aTabName); 2032 assert( !aTabName.isEmpty() && "empty sheet name"); 2033 // External Reference, same as in ScCompiler::MakeTabStr() 2034 if( aTabName[0] == '\'' ) 2035 { // "'Doc'#Tab" 2036 sal_Int32 nPos = ScCompiler::GetDocTabPos( aTabName); 2037 if (nPos != -1) 2038 { 2039 aDocName = aTabName.copy( 0, nPos + 1 ); 2040 aTabName = aTabName.copy( nPos + 1 ); 2041 } 2042 } 2043 else if( nFlags & ScRefFlags::FORCE_DOC ) 2044 { 2045 // VBA has an 'external' flag that forces the addition of the 2046 // tab name _and_ the doc name. The VBA code would be 2047 // needlessly complicated if it constructed an actual external 2048 // reference so we add this somewhat cheesy kludge to force the 2049 // addition of the document name even for non-external references 2050 aDocName = getFileNameFromDoc( pDoc ); 2051 } 2052 ScCompiler::CheckTabQuotes( aTabName, rDetails.eConv); 2053 2054 switch( rDetails.eConv ) 2055 { 2056 default : 2057 case formula::FormulaGrammar::CONV_OOO: 2058 lcl_string_append(r, aDocName); 2059 if( nFlags & ScRefFlags::TAB_ABS ) 2060 r.append("$"); 2061 lcl_string_append(r, aTabName); 2062 r.append("."); 2063 break; 2064 2065 case formula::FormulaGrammar::CONV_XL_OOX: 2066 if (!aTabName.isEmpty() && aTabName[0] == '\'') 2067 { 2068 if (!aDocName.isEmpty()) 2069 { 2070 lcl_string_append(r.append("'["), aDocName); 2071 r.append("]"); 2072 lcl_string_append(r, aTabName.copy(1)); 2073 } 2074 else 2075 { 2076 lcl_string_append(r, aTabName); 2077 } 2078 r.append("!"); 2079 break; 2080 } 2081 [[fallthrough]]; 2082 case formula::FormulaGrammar::CONV_XL_A1: 2083 case formula::FormulaGrammar::CONV_XL_R1C1: 2084 if (!aDocName.isEmpty()) 2085 { 2086 lcl_string_append(r.append("["), aDocName); 2087 r.append("]"); 2088 } 2089 lcl_string_append(r, aTabName); 2090 r.append("!"); 2091 break; 2092 } 2093 } 2094 } 2095 switch( rDetails.eConv ) 2096 { 2097 default : 2098 case formula::FormulaGrammar::CONV_OOO: 2099 case formula::FormulaGrammar::CONV_XL_A1: 2100 case formula::FormulaGrammar::CONV_XL_OOX: 2101 if( nFlags & ScRefFlags::COL_VALID ) 2102 lcl_a1_append_c ( r, nCol, (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO ); 2103 if( nFlags & ScRefFlags::ROW_VALID ) 2104 lcl_a1_append_r ( r, nRow, (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO ); 2105 break; 2106 2107 case formula::FormulaGrammar::CONV_XL_R1C1: 2108 if( nFlags & ScRefFlags::ROW_VALID ) 2109 lcl_r1c1_append_r ( r, nRow, (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails ); 2110 if( nFlags & ScRefFlags::COL_VALID ) 2111 lcl_r1c1_append_c ( r, nCol, (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails ); 2112 break; 2113 } 2114 } 2115 2116 void ScAddress::Format( OStringBuffer& r, ScRefFlags nFlags, 2117 const ScDocument* pDoc, 2118 const Details& rDetails) const 2119 { 2120 lcl_Format(r, nTab, nRow, nCol, nFlags, pDoc, rDetails); 2121 } 2122 2123 OUString ScAddress::Format(ScRefFlags nFlags, const ScDocument* pDoc, 2124 const Details& rDetails) const 2125 { 2126 OUStringBuffer r; 2127 lcl_Format(r, nTab, nRow, nCol, nFlags, pDoc, rDetails); 2128 return r.makeStringAndClear(); 2129 } 2130 2131 static void lcl_Split_DocTab( const ScDocument& rDoc, SCTAB nTab, 2132 const ScAddress::Details& rDetails, 2133 ScRefFlags nFlags, 2134 OUString& rTabName, OUString& rDocName ) 2135 { 2136 rDoc.GetName(nTab, rTabName); 2137 rDocName.clear(); 2138 // External reference, same as in ScCompiler::MakeTabStr() 2139 if ( rTabName[0] == '\'' ) 2140 { // "'Doc'#Tab" 2141 sal_Int32 nPos = ScCompiler::GetDocTabPos( rTabName); 2142 if (nPos != -1) 2143 { 2144 rDocName = rTabName.copy( 0, nPos + 1 ); 2145 rTabName = rTabName.copy( nPos + 1 ); 2146 } 2147 } 2148 else if( nFlags & ScRefFlags::FORCE_DOC ) 2149 { 2150 // VBA has an 'external' flag that forces the addition of the 2151 // tab name _and_ the doc name. The VBA code would be 2152 // needlessly complicated if it constructed an actual external 2153 // reference so we add this somewhat cheesy kludge to force the 2154 // addition of the document name even for non-external references 2155 rDocName = getFileNameFromDoc(&rDoc); 2156 } 2157 ScCompiler::CheckTabQuotes( rTabName, rDetails.eConv); 2158 } 2159 2160 static void lcl_ScRange_Format_XL_Header( OUStringBuffer& rString, const ScRange& rRange, 2161 ScRefFlags nFlags, const ScDocument& rDoc, 2162 const ScAddress::Details& rDetails ) 2163 { 2164 if( !(nFlags & ScRefFlags::TAB_3D) ) 2165 return; 2166 2167 OUString aTabName, aDocName; 2168 lcl_Split_DocTab( rDoc, rRange.aStart.Tab(), rDetails, nFlags, aTabName, aDocName ); 2169 switch (rDetails.eConv) 2170 { 2171 case formula::FormulaGrammar::CONV_XL_OOX: 2172 if (!aTabName.isEmpty() && aTabName[0] == '\'') 2173 { 2174 if (!aDocName.isEmpty()) 2175 { 2176 rString.append("'[" + aDocName + "]" + aTabName.subView(1)); 2177 } 2178 else 2179 { 2180 rString.append(aTabName); 2181 } 2182 break; 2183 } 2184 [[fallthrough]]; 2185 default: 2186 if (!aDocName.isEmpty()) 2187 { 2188 rString.append("[").append(aDocName).append("]"); 2189 } 2190 rString.append(aTabName); 2191 break; 2192 } 2193 if( nFlags & ScRefFlags::TAB2_3D ) 2194 { 2195 lcl_Split_DocTab( rDoc, rRange.aEnd.Tab(), rDetails, nFlags, aTabName, aDocName ); 2196 rString.append(":"); 2197 rString.append(aTabName); 2198 } 2199 rString.append("!"); 2200 } 2201 2202 // helpers used in ScRange::Format 2203 static bool lcl_ColAbsFlagDiffer(const ScRefFlags nFlags) 2204 { 2205 return static_cast<bool>(nFlags & ScRefFlags::COL_ABS) != static_cast<bool>(nFlags & ScRefFlags::COL2_ABS); 2206 } 2207 static bool lcl_RowAbsFlagDiffer(const ScRefFlags nFlags) 2208 { 2209 return static_cast<bool>(nFlags & ScRefFlags::ROW_ABS) != static_cast<bool>(nFlags & ScRefFlags::ROW2_ABS); 2210 } 2211 2212 OUString ScRange::Format( const ScDocument& rDoc, ScRefFlags nFlags, 2213 const ScAddress::Details& rDetails, bool bFullAddressNotation ) const 2214 { 2215 if( !( nFlags & ScRefFlags::VALID ) ) 2216 { 2217 return ScCompiler::GetNativeSymbol(ocErrRef); 2218 } 2219 2220 OUStringBuffer r; 2221 switch( rDetails.eConv ) { 2222 default : 2223 case formula::FormulaGrammar::CONV_OOO: { 2224 bool bOneTab = (aStart.Tab() == aEnd.Tab()); 2225 if ( !bOneTab ) 2226 nFlags |= ScRefFlags::TAB_3D; 2227 r = aStart.Format(nFlags, &rDoc, rDetails); 2228 if( aStart != aEnd || 2229 lcl_ColAbsFlagDiffer( nFlags ) || 2230 lcl_RowAbsFlagDiffer( nFlags )) 2231 { 2232 const ScDocument* pDoc = &rDoc; 2233 // move flags of end reference to start reference, mask with BITS to exclude FORCE_DOC flag 2234 nFlags = ScRefFlags::VALID | (ScRefFlags(o3tl::underlyingEnumValue(nFlags) >> 4) & ScRefFlags::BITS); 2235 if ( bOneTab ) 2236 pDoc = nullptr; 2237 else 2238 nFlags |= ScRefFlags::TAB_3D; 2239 OUString aName(aEnd.Format(nFlags, pDoc, rDetails)); 2240 r.append(":"); 2241 r.append(aName); 2242 } 2243 break; 2244 } 2245 2246 case formula::FormulaGrammar::CONV_XL_A1: 2247 case formula::FormulaGrammar::CONV_XL_OOX: { 2248 SCCOL nMaxCol = rDoc.MaxCol(); 2249 SCROW nMaxRow = rDoc.MaxRow(); 2250 2251 lcl_ScRange_Format_XL_Header( r, *this, nFlags, rDoc, rDetails ); 2252 if( aStart.Col() == 0 && aEnd.Col() >= nMaxCol && !bFullAddressNotation ) 2253 { 2254 // Full col refs always require 2 rows (2:2) 2255 lcl_a1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO ); 2256 r.append(":"); 2257 lcl_a1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO ); 2258 } 2259 else if( aStart.Row() == 0 && aEnd.Row() >= nMaxRow && !bFullAddressNotation ) 2260 { 2261 // Full row refs always require 2 cols (A:A) 2262 lcl_a1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO ); 2263 r.append(":"); 2264 lcl_a1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO ); 2265 } 2266 else 2267 { 2268 lcl_a1_append_c ( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO ); 2269 lcl_a1_append_r ( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO ); 2270 if( aStart.Col() != aEnd.Col() || 2271 lcl_ColAbsFlagDiffer( nFlags ) || 2272 aStart.Row() != aEnd.Row() || 2273 lcl_RowAbsFlagDiffer( nFlags ) ) { 2274 r.append(":"); 2275 lcl_a1_append_c ( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO ); 2276 lcl_a1_append_r ( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO ); 2277 } 2278 } 2279 break; 2280 } 2281 2282 case formula::FormulaGrammar::CONV_XL_R1C1: { 2283 SCCOL nMaxCol = rDoc.MaxCol(); 2284 SCROW nMaxRow = rDoc.MaxRow(); 2285 2286 lcl_ScRange_Format_XL_Header( r, *this, nFlags, rDoc, rDetails ); 2287 if( aStart.Col() == 0 && aEnd.Col() >= nMaxCol && !bFullAddressNotation ) 2288 { 2289 lcl_r1c1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails ); 2290 if( aStart.Row() != aEnd.Row() || 2291 lcl_RowAbsFlagDiffer( nFlags ) ) { 2292 r.append(":"); 2293 lcl_r1c1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO, rDetails ); 2294 } 2295 } 2296 else if( aStart.Row() == 0 && aEnd.Row() >= nMaxRow && !bFullAddressNotation ) 2297 { 2298 lcl_r1c1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails ); 2299 if( aStart.Col() != aEnd.Col() || 2300 lcl_ColAbsFlagDiffer( nFlags )) { 2301 r.append(":"); 2302 lcl_r1c1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO, rDetails ); 2303 } 2304 } 2305 else 2306 { 2307 lcl_r1c1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails ); 2308 lcl_r1c1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails ); 2309 if( aStart.Col() != aEnd.Col() || 2310 lcl_ColAbsFlagDiffer( nFlags ) || 2311 aStart.Row() != aEnd.Row() || 2312 lcl_RowAbsFlagDiffer( nFlags ) ) { 2313 r.append(":"); 2314 lcl_r1c1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO, rDetails ); 2315 lcl_r1c1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO, rDetails ); 2316 } 2317 } 2318 break; 2319 } 2320 } 2321 return r.makeStringAndClear(); 2322 } 2323 2324 bool ScAddress::Move( SCCOL dx, SCROW dy, SCTAB dz, ScAddress& rErrorPos, const ScDocument* pDoc ) 2325 { 2326 SCTAB nMaxTab = pDoc ? pDoc->GetTableCount() : MAXTAB; 2327 SCCOL nMaxCol = pDoc ? pDoc->MaxCol() : MAXCOL; 2328 SCROW nMaxRow = pDoc ? pDoc->MaxRow() : MAXROW; 2329 dx = Col() + dx; 2330 dy = Row() + dy; 2331 dz = Tab() + dz; 2332 bool bValid = true; 2333 rErrorPos.SetCol(dx); 2334 if( dx < 0 ) 2335 { 2336 dx = 0; 2337 bValid = false; 2338 } 2339 else if( dx > nMaxCol ) 2340 { 2341 dx = nMaxCol; 2342 bValid =false; 2343 } 2344 rErrorPos.SetRow(dy); 2345 if( dy < 0 ) 2346 { 2347 dy = 0; 2348 bValid = false; 2349 } 2350 else if( dy > nMaxRow ) 2351 { 2352 dy = nMaxRow; 2353 bValid =false; 2354 } 2355 rErrorPos.SetTab(dz); 2356 if( dz < 0 ) 2357 { 2358 dz = 0; 2359 bValid = false; 2360 } 2361 else if( dz > nMaxTab ) 2362 { 2363 // Always set MAXTAB+1 so further checks without ScDocument detect invalid. 2364 rErrorPos.SetTab(MAXTAB+1); 2365 dz = nMaxTab; 2366 bValid =false; 2367 } 2368 Set( dx, dy, dz ); 2369 return bValid; 2370 } 2371 2372 bool ScRange::Move( SCCOL dx, SCROW dy, SCTAB dz, ScRange& rErrorRange, const ScDocument* pDoc ) 2373 { 2374 SCCOL nMaxCol = pDoc ? pDoc->MaxCol() : MAXCOL; 2375 SCROW nMaxRow = pDoc ? pDoc->MaxRow() : MAXROW; 2376 if (dy && aStart.Row() == 0 && aEnd.Row() == nMaxRow) 2377 dy = 0; // Entire column not to be moved. 2378 if (dx && aStart.Col() == 0 && aEnd.Col() == nMaxCol) 2379 dx = 0; // Entire row not to be moved. 2380 bool b = aStart.Move( dx, dy, dz, rErrorRange.aStart, pDoc ); 2381 b &= aEnd.Move( dx, dy, dz, rErrorRange.aEnd, pDoc ); 2382 return b; 2383 } 2384 2385 bool ScRange::MoveSticky( const ScDocument& rDoc, SCCOL dx, SCROW dy, SCTAB dz, ScRange& rErrorRange ) 2386 { 2387 const SCCOL nMaxCol = rDoc.MaxCol(); 2388 const SCROW nMaxRow = rDoc.MaxRow(); 2389 bool bColRange = (aStart.Col() < aEnd.Col()); 2390 bool bRowRange = (aStart.Row() < aEnd.Row()); 2391 if (dy && aStart.Row() == 0 && aEnd.Row() == nMaxRow) 2392 dy = 0; // Entire column not to be moved. 2393 if (dx && aStart.Col() == 0 && aEnd.Col() == nMaxCol) 2394 dx = 0; // Entire row not to be moved. 2395 bool b1 = aStart.Move( dx, dy, dz, rErrorRange.aStart ); 2396 if (dx && bColRange && aEnd.Col() == nMaxCol) 2397 dx = 0; // End column sticky. 2398 if (dy && bRowRange && aEnd.Row() == nMaxRow) 2399 dy = 0; // End row sticky. 2400 SCTAB nOldTab = aEnd.Tab(); 2401 bool b2 = aEnd.Move( dx, dy, dz, rErrorRange.aEnd ); 2402 if (!b2) 2403 { 2404 // End column or row of a range may have become sticky. 2405 bColRange = (!dx || (bColRange && aEnd.Col() == nMaxCol)); 2406 if (dx && bColRange) 2407 rErrorRange.aEnd.SetCol(nMaxCol); 2408 bRowRange = (!dy || (bRowRange && aEnd.Row() == nMaxRow)); 2409 if (dy && bRowRange) 2410 rErrorRange.aEnd.SetRow(nMaxRow); 2411 b2 = bColRange && bRowRange && (aEnd.Tab() - nOldTab == dz); 2412 } 2413 return b1 && b2; 2414 } 2415 2416 void ScRange::IncColIfNotLessThan(const ScDocument& rDoc, SCCOL nStartCol, SCCOL nOffset) 2417 { 2418 if (aStart.Col() >= nStartCol) 2419 { 2420 aStart.IncCol(nOffset); 2421 if (aStart.Col() < 0) 2422 aStart.SetCol(0); 2423 else if(aStart.Col() > rDoc.MaxCol()) 2424 aStart.SetCol(rDoc.MaxCol()); 2425 } 2426 if (aEnd.Col() >= nStartCol) 2427 { 2428 aEnd.IncCol(nOffset); 2429 if (aEnd.Col() < 0) 2430 aEnd.SetCol(0); 2431 else if(aEnd.Col() > rDoc.MaxCol()) 2432 aEnd.SetCol(rDoc.MaxCol()); 2433 } 2434 } 2435 2436 void ScRange::IncRowIfNotLessThan(const ScDocument& rDoc, SCROW nStartRow, SCROW nOffset) 2437 { 2438 if (aStart.Row() >= nStartRow) 2439 { 2440 aStart.IncRow(nOffset); 2441 if (aStart.Row() < 0) 2442 aStart.SetRow(0); 2443 else if(aStart.Row() > rDoc.MaxRow()) 2444 aStart.SetRow(rDoc.MaxRow()); 2445 } 2446 if (aEnd.Row() >= nStartRow) 2447 { 2448 aEnd.IncRow(nOffset); 2449 if (aEnd.Row() < 0) 2450 aEnd.SetRow(0); 2451 else if(aEnd.Row() > rDoc.MaxRow()) 2452 aEnd.SetRow(rDoc.MaxRow()); 2453 } 2454 } 2455 2456 void ScRange::IncEndColSticky( const ScDocument& rDoc, SCCOL nDelta ) 2457 { 2458 SCCOL nCol = aEnd.Col(); 2459 if (aStart.Col() >= nCol) 2460 { 2461 // Less than two columns => not sticky. 2462 aEnd.IncCol( nDelta); 2463 return; 2464 } 2465 2466 const SCCOL nMaxCol = rDoc.MaxCol(); 2467 if (nCol == nMaxCol) 2468 // already sticky 2469 return; 2470 2471 if (nCol < nMaxCol) 2472 aEnd.SetCol( ::std::min( static_cast<SCCOL>(nCol + nDelta), nMaxCol)); 2473 else 2474 aEnd.IncCol( nDelta); // was greater than nMaxCol, caller should know... 2475 } 2476 2477 void ScRange::IncEndRowSticky( const ScDocument& rDoc, SCROW nDelta ) 2478 { 2479 SCROW nRow = aEnd.Row(); 2480 if (aStart.Row() >= nRow) 2481 { 2482 // Less than two rows => not sticky. 2483 aEnd.IncRow( nDelta); 2484 return; 2485 } 2486 2487 if (nRow == rDoc.MaxRow()) 2488 // already sticky 2489 return; 2490 2491 if (nRow < rDoc.MaxRow()) 2492 aEnd.SetRow( ::std::min( static_cast<SCROW>(nRow + nDelta), rDoc.MaxRow())); 2493 else 2494 aEnd.IncRow( nDelta); // was greater than rDoc.MaxRow(), caller should know... 2495 } 2496 2497 OUString ScAddress::GetColRowString() const 2498 { 2499 OUStringBuffer aString; 2500 2501 switch( detailsOOOa1.eConv ) 2502 { 2503 default : 2504 case formula::FormulaGrammar::CONV_OOO: 2505 case formula::FormulaGrammar::CONV_XL_A1: 2506 case formula::FormulaGrammar::CONV_XL_OOX: 2507 lcl_ScColToAlpha( aString, nCol); 2508 aString.append(OUString::number(nRow+1)); 2509 break; 2510 2511 case formula::FormulaGrammar::CONV_XL_R1C1: 2512 lcl_r1c1_append_r ( aString, nRow, false/*bAbsolute*/, detailsOOOa1 ); 2513 lcl_r1c1_append_c ( aString, nCol, false/*bAbsolute*/, detailsOOOa1 ); 2514 break; 2515 } 2516 2517 return aString.makeStringAndClear(); 2518 } 2519 2520 OUString ScRefAddress::GetRefString( const ScDocument& rDoc, SCTAB nActTab, 2521 const ScAddress::Details& rDetails ) const 2522 { 2523 if ( Tab()+1 > rDoc.GetTableCount() ) 2524 return ScCompiler::GetNativeSymbol(ocErrRef); 2525 2526 ScRefFlags nFlags = ScRefFlags::VALID; 2527 if ( nActTab != Tab() ) 2528 { 2529 nFlags |= ScRefFlags::TAB_3D; 2530 if ( !bRelTab ) 2531 nFlags |= ScRefFlags::TAB_ABS; 2532 } 2533 if ( !bRelCol ) 2534 nFlags |= ScRefFlags::COL_ABS; 2535 if ( !bRelRow ) 2536 nFlags |= ScRefFlags::ROW_ABS; 2537 2538 return aAdr.Format(nFlags, &rDoc, rDetails); 2539 } 2540 2541 bool AlphaToCol(const ScDocument& rDoc, SCCOL& rCol, const OUString& rStr) 2542 { 2543 SCCOL nResult = 0; 2544 sal_Int32 nStop = rStr.getLength(); 2545 sal_Int32 nPos = 0; 2546 sal_Unicode c; 2547 const SCCOL nMaxCol = rDoc.MaxCol(); 2548 while (nResult <= nMaxCol && nPos < nStop && (c = rStr[nPos]) != 0 && 2549 rtl::isAsciiAlpha(c)) 2550 { 2551 if (nPos > 0) 2552 nResult = (nResult + 1) * 26; 2553 nResult += ScGlobal::ToUpperAlpha(c) - 'A'; 2554 ++nPos; 2555 } 2556 bool bOk = (rDoc.ValidCol(nResult) && nPos > 0); 2557 if (bOk) 2558 rCol = nResult; 2559 return bOk; 2560 } 2561 2562 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 2563
