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 sal_Int64 sal_Unicode_strtol ( const sal_Unicode* p, const sal_Unicode** pEnd ) 137 { 138 sal_Int64 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 += std::u16string_view( 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 += std::u16string_view( 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 += std::u16string_view( 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 += std::u16string_view( 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, u""); 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 ScSheetLimits& rSheetLimits, 659 const sal_Unicode* p, 660 const ScAddress::Details& rDetails, 661 ScAddress* pAddr, ScRefFlags* nFlags ) 662 { 663 const sal_Unicode *pEnd; 664 sal_Int64 n; 665 bool isRelative; 666 667 if( p[0] == '\0' ) 668 return nullptr; 669 670 p++; 671 isRelative = *p == '['; 672 if( isRelative ) 673 p++; 674 n = sal_Unicode_strtol( p, &pEnd ); 675 if( nullptr == pEnd ) 676 return nullptr; 677 678 if( p == pEnd ) // C is a relative ref with offset 0 679 { 680 if( isRelative ) 681 return nullptr; 682 n = rDetails.nCol; 683 } 684 else if( isRelative ) 685 { 686 if( *pEnd != ']' ) 687 return nullptr; 688 n += rDetails.nCol; 689 pEnd++; 690 } 691 else 692 { 693 *nFlags |= ScRefFlags::COL_ABS; 694 n--; 695 } 696 697 if( n < 0 || n >= rSheetLimits.GetMaxColCount()) 698 return nullptr; 699 pAddr->SetCol( static_cast<SCCOL>( n ) ); 700 *nFlags |= ScRefFlags::COL_VALID; 701 702 return pEnd; 703 } 704 705 static const sal_Unicode* lcl_r1c1_get_row( 706 const ScSheetLimits& rSheetLimits, 707 const sal_Unicode* p, 708 const ScAddress::Details& rDetails, 709 ScAddress* pAddr, ScRefFlags* nFlags ) 710 { 711 const sal_Unicode *pEnd; 712 bool isRelative; 713 714 if( p[0] == '\0' ) 715 return nullptr; 716 717 p++; 718 isRelative = *p == '['; 719 if( isRelative ) 720 p++; 721 sal_Int64 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( rDoc.GetSheetLimits(), 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( rDoc.GetSheetLimits(), 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( rDoc.GetSheetLimits(), 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( rDoc.GetSheetLimits(), 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 if( *p == '$' ) 906 { 907 *nFlags |= ScRefFlags::COL_ABS; 908 p++; 909 } 910 911 if (pErrRef && lcl_isString( p, *pErrRef)) 912 { 913 p += pErrRef->getLength(); 914 *nFlags &= ~ScRefFlags::COL_VALID; 915 pAddr->SetCol(-1); 916 return p; 917 } 918 919 if( !rtl::isAsciiAlpha( *p ) ) 920 return nullptr; 921 922 sal_Int64 nCol = rtl::toAsciiUpperCase( *p++ ) - 'A'; 923 const SCCOL nMaxCol = rDoc.MaxCol(); 924 while (nCol <= nMaxCol && rtl::isAsciiAlpha(*p)) 925 nCol = ((nCol + 1) * 26) + rtl::toAsciiUpperCase( *p++ ) - 'A'; 926 if( nCol > nMaxCol || nCol < 0 || rtl::isAsciiAlpha( *p ) ) 927 return nullptr; 928 929 *nFlags |= ScRefFlags::COL_VALID; 930 pAddr->SetCol( sal::static_int_cast<SCCOL>( nCol )); 931 932 return p; 933 } 934 935 static const sal_Unicode* lcl_a1_get_row( const ScDocument& rDoc, 936 const sal_Unicode* p, 937 ScAddress* pAddr, 938 ScRefFlags* nFlags, 939 const OUString* pErrRef ) 940 { 941 const sal_Unicode *pEnd; 942 943 if( *p == '$' ) 944 { 945 *nFlags |= ScRefFlags::ROW_ABS; 946 p++; 947 } 948 949 if (pErrRef && lcl_isString( p, *pErrRef)) 950 { 951 p += pErrRef->getLength(); 952 *nFlags &= ~ScRefFlags::ROW_VALID; 953 pAddr->SetRow(-1); 954 return p; 955 } 956 957 sal_Int64 n = sal_Unicode_strtol( p, &pEnd ) - 1; 958 if( nullptr == pEnd || p == pEnd || n < 0 || n > rDoc.MaxRow() ) 959 return nullptr; 960 961 *nFlags |= ScRefFlags::ROW_VALID; 962 pAddr->SetRow( sal::static_int_cast<SCROW>(n) ); 963 964 return pEnd; 965 } 966 967 /// B:B or 2:2, but not B:2 or 2:B or B2:B or B:B2 or ... 968 static bool isValidSingleton( ScRefFlags nFlags, ScRefFlags nFlags2 ) 969 { 970 bool bCols = (nFlags & ScRefFlags::COL_VALID) && ((nFlags & ScRefFlags::COL2_VALID) || (nFlags2 & ScRefFlags::COL_VALID)); 971 bool bRows = (nFlags & ScRefFlags::ROW_VALID) && ((nFlags & ScRefFlags::ROW2_VALID) || (nFlags2 & ScRefFlags::ROW_VALID)); 972 return bCols != bRows; 973 } 974 975 static ScRefFlags lcl_ScRange_Parse_XL_A1( ScRange& r, 976 const sal_Unicode* p, 977 const ScDocument& rDoc, 978 bool bOnlyAcceptSingle, 979 ScAddress::ExternalInfo* pExtInfo, 980 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks, 981 sal_Int32* pSheetEndPos, 982 const OUString* pErrRef ) 983 { 984 const sal_Unicode* const pStart = p; 985 if (pSheetEndPos) 986 *pSheetEndPos = 0; 987 const sal_Unicode* tmp1, *tmp2; 988 OUString aExternDocName, aStartTabName, aEndTabName; // for external link table 989 ScRefFlags nFlags = ScRefFlags::VALID | ScRefFlags::TAB_VALID, nFlags2 = ScRefFlags::TAB_VALID; 990 991 p = r.Parse_XL_Header( p, rDoc, aExternDocName, aStartTabName, 992 aEndTabName, nFlags, bOnlyAcceptSingle, pExternalLinks, pErrRef ); 993 994 ScRefFlags nBailOutFlags = ScRefFlags::ZERO; 995 if (pSheetEndPos && pStart < p && (nFlags & ScRefFlags::TAB_VALID) && (nFlags & ScRefFlags::TAB_3D)) 996 { 997 *pSheetEndPos = p - pStart; 998 nBailOutFlags = ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D; 999 } 1000 1001 if (!aExternDocName.isEmpty()) 1002 lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName, 1003 aStartTabName, aEndTabName, rDoc); 1004 1005 if( nullptr == p ) 1006 return nBailOutFlags; 1007 1008 tmp1 = lcl_a1_get_col( rDoc, p, &r.aStart, &nFlags, pErrRef); 1009 if( tmp1 == nullptr ) // Is it a row only reference 3:5 1010 { 1011 if( bOnlyAcceptSingle ) // by definition full row refs are ranges 1012 return nBailOutFlags; 1013 1014 tmp1 = lcl_a1_get_row( rDoc, p, &r.aStart, &nFlags, pErrRef); 1015 1016 tmp1 = lcl_eatWhiteSpace( tmp1 ); 1017 if( !tmp1 || *tmp1++ != ':' ) // Even a singleton requires ':' (eg 2:2) 1018 return nBailOutFlags; 1019 1020 tmp1 = lcl_eatWhiteSpace( tmp1 ); 1021 tmp2 = lcl_a1_get_row( rDoc, tmp1, &r.aEnd, &nFlags2, pErrRef); 1022 if( !tmp2 || *tmp2 != 0 ) // Must have fully parsed a singleton. 1023 return nBailOutFlags; 1024 1025 r.aStart.SetCol( 0 ); r.aEnd.SetCol( rDoc.MaxCol() ); 1026 nFlags |= 1027 ScRefFlags::COL_VALID | ScRefFlags::COL2_VALID | 1028 ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS; 1029 applyStartToEndFlags(nFlags, nFlags2); 1030 return nFlags; 1031 } 1032 1033 tmp2 = lcl_a1_get_row( rDoc, tmp1, &r.aStart, &nFlags, pErrRef); 1034 if( tmp2 == nullptr ) // check for col only reference F:H 1035 { 1036 if( bOnlyAcceptSingle ) // by definition full col refs are ranges 1037 return nBailOutFlags; 1038 1039 tmp1 = lcl_eatWhiteSpace( tmp1 ); 1040 if( *tmp1++ != ':' ) // Even a singleton requires ':' (eg F:F) 1041 return nBailOutFlags; 1042 1043 tmp1 = lcl_eatWhiteSpace( tmp1 ); 1044 tmp2 = lcl_a1_get_col( rDoc, tmp1, &r.aEnd, &nFlags2, pErrRef); 1045 if( !tmp2 || *tmp2 != 0 ) // Must have fully parsed a singleton. 1046 return nBailOutFlags; 1047 1048 r.aStart.SetRow( 0 ); r.aEnd.SetRow( rDoc.MaxRow() ); 1049 nFlags |= 1050 ScRefFlags::ROW_VALID | ScRefFlags::ROW2_VALID | 1051 ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS; 1052 applyStartToEndFlags(nFlags, nFlags2); 1053 return nFlags; 1054 } 1055 1056 // prepare as if it's a singleton, in case we want to fall back */ 1057 r.aEnd.SetCol( r.aStart.Col() ); 1058 r.aEnd.SetRow( r.aStart.Row() ); // don't overwrite sheet number as parsed in Parse_XL_Header() 1059 1060 if ( bOnlyAcceptSingle ) 1061 { 1062 if ( *tmp2 == 0 ) 1063 return nFlags; 1064 else 1065 { 1066 // any trailing invalid character must invalidate the address. 1067 nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID); 1068 return nFlags; 1069 } 1070 } 1071 1072 tmp2 = lcl_eatWhiteSpace( tmp2 ); 1073 if( *tmp2 != ':' ) 1074 { 1075 // Sheet1:Sheet2!C4 is a valid range, without a second sheet it is 1076 // not. Any trailing invalid character invalidates the range. 1077 if (*tmp2 == 0 && (nFlags & ScRefFlags::TAB2_3D)) 1078 { 1079 if (nFlags & ScRefFlags::COL_ABS) 1080 nFlags |= ScRefFlags::COL2_ABS; 1081 if (nFlags & ScRefFlags::ROW_ABS) 1082 nFlags |= ScRefFlags::ROW2_ABS; 1083 } 1084 else 1085 nFlags &= ~ScRefFlags(ScRefFlags::VALID | 1086 ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID | 1087 ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID); 1088 return nFlags; 1089 } 1090 1091 p = lcl_eatWhiteSpace( tmp2+1 ); // after ':' 1092 tmp1 = lcl_a1_get_col( rDoc, p, &r.aEnd, &nFlags2, pErrRef); 1093 if( !tmp1 && aEndTabName.isEmpty() ) // Probably the aEndTabName was specified after the first range 1094 { 1095 p = lcl_XL_ParseSheetRef( p, aEndTabName, false, nullptr, pErrRef); 1096 if( p ) 1097 { 1098 SCTAB nTab = 0; 1099 if( !aEndTabName.isEmpty() && rDoc.GetTable( aEndTabName, nTab ) ) 1100 { 1101 r.aEnd.SetTab( nTab ); 1102 nFlags |= ScRefFlags::TAB2_VALID | ScRefFlags::TAB2_3D | ScRefFlags::TAB2_ABS; 1103 } 1104 if (*p == '!' || *p == ':') 1105 p = lcl_eatWhiteSpace( p+1 ); 1106 tmp1 = lcl_a1_get_col( rDoc, p, &r.aEnd, &nFlags2, pErrRef); 1107 } 1108 } 1109 if( !tmp1 ) // strange, but maybe valid singleton 1110 return isValidSingleton( nFlags, nFlags2) ? nFlags : (nFlags & ~ScRefFlags::VALID); 1111 1112 tmp2 = lcl_a1_get_row( rDoc, tmp1, &r.aEnd, &nFlags2, pErrRef); 1113 if( !tmp2 ) // strange, but maybe valid singleton 1114 return isValidSingleton( nFlags, nFlags2) ? nFlags : (nFlags & ~ScRefFlags::VALID); 1115 1116 if ( *tmp2 != 0 ) 1117 { 1118 // any trailing invalid character must invalidate the range. 1119 nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID | 1120 ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID); 1121 return nFlags; 1122 } 1123 1124 applyStartToEndFlags(nFlags, nFlags2); 1125 return nFlags; 1126 } 1127 1128 /** 1129 @param p pointer to null-terminated sal_Unicode string 1130 @param rRawRes returns ScRefFlags::... flags without the final check for full 1131 validity that is applied to the return value, with which 1132 two addresses that form a column or row singleton range, 1133 e.g. A:A or 1:1, can be detected. Used in 1134 lcl_ScRange_Parse_OOo(). 1135 @param pRange pointer to range where rAddr effectively is *pRange->aEnd, 1136 used in conjunction with pExtInfo to determine the tab span 1137 of a 3D reference. 1138 */ 1139 static ScRefFlags lcl_ScAddress_Parse_OOo( const sal_Unicode* p, const ScDocument& rDoc, ScAddress& rAddr, 1140 ScRefFlags& rRawRes, 1141 ScAddress::ExternalInfo* pExtInfo, 1142 ScRange* pRange, 1143 sal_Int32* pSheetEndPos, 1144 const OUString* pErrRef ) 1145 { 1146 const sal_Unicode* const pStart = p; 1147 if (pSheetEndPos) 1148 *pSheetEndPos = 0; 1149 ScRefFlags nRes = ScRefFlags::ZERO; 1150 rRawRes = ScRefFlags::ZERO; 1151 OUString aDocName; // the pure Document Name 1152 OUString aTab; 1153 bool bExtDoc = false; 1154 bool bExtDocInherited = false; 1155 1156 // Lets see if this is a reference to something in an external file. A 1157 // document name is always quoted and has a trailing #. 1158 if (*p == '\'') 1159 { 1160 OUString aTmp; 1161 p = parseQuotedName(p, aTmp); 1162 aDocName = aTmp; 1163 if (*p++ == SC_COMPILER_FILE_TAB_SEP) 1164 bExtDoc = true; 1165 else 1166 // This is not a document name. Perhaps a quoted relative table 1167 // name. 1168 p = pStart; 1169 } 1170 else if (pExtInfo && pExtInfo->mbExternal) 1171 { 1172 // This is an external reference. 1173 bExtDoc = bExtDocInherited = true; 1174 } 1175 1176 SCCOL nCol = 0; 1177 SCROW nRow = 0; 1178 SCTAB nTab = 0; 1179 ScRefFlags nBailOutFlags = ScRefFlags::ZERO; 1180 ScRefFlags nBits = ScRefFlags::TAB_VALID; 1181 const sal_Unicode* q; 1182 if ( ScGlobal::FindUnquoted( p, '.') ) 1183 { 1184 nRes |= ScRefFlags::TAB_3D; 1185 if ( bExtDoc ) 1186 nRes |= ScRefFlags::TAB_ABS; 1187 if (*p == '$') 1188 { 1189 nRes |= ScRefFlags::TAB_ABS; 1190 p++; 1191 } 1192 1193 if (pErrRef && lcl_isString( p, *pErrRef) && p[pErrRef->getLength()] == '.') 1194 { 1195 // #REF! particle of an invalidated reference plus sheet separator. 1196 p += pErrRef->getLength() + 1; 1197 nRes &= ~ScRefFlags::TAB_VALID; 1198 nTab = -1; 1199 } 1200 else 1201 { 1202 if (*p == '\'') 1203 { 1204 // Tokens that start at ' can have anything in them until a final 1205 // ' but '' marks an escaped '. We've earlier guaranteed that a 1206 // string containing '' will be surrounded by '. 1207 p = parseQuotedName(p, aTab); 1208 } 1209 else 1210 { 1211 OUStringBuffer aTabAcc; 1212 while (*p) 1213 { 1214 if( *p == '.') 1215 break; 1216 1217 if( *p == '\'' ) 1218 { 1219 p++; break; 1220 } 1221 aTabAcc.append(*p); 1222 p++; 1223 } 1224 aTab = aTabAcc.makeStringAndClear(); 1225 } 1226 if (*p != '.') 1227 nBits = ScRefFlags::ZERO; 1228 else 1229 { 1230 ++p; 1231 if (!bExtDoc && !rDoc.GetTable( aTab, nTab )) 1232 nBits = ScRefFlags::ZERO; 1233 } 1234 } 1235 1236 if (pSheetEndPos && (nBits & ScRefFlags::TAB_VALID)) 1237 { 1238 *pSheetEndPos = p - pStart; 1239 nBailOutFlags = ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D; 1240 } 1241 } 1242 else 1243 { 1244 if (bExtDoc && !bExtDocInherited) 1245 return nRes; // After a document a sheet must follow. 1246 nTab = rAddr.Tab(); 1247 } 1248 nRes |= nBits; 1249 1250 q = p; 1251 if (*p) 1252 { 1253 nBits = ScRefFlags::COL_VALID; 1254 if (*p == '$') 1255 { 1256 nBits |= ScRefFlags::COL_ABS; 1257 p++; 1258 } 1259 1260 if (pErrRef && lcl_isString( p, *pErrRef)) 1261 { 1262 // #REF! particle of an invalidated reference. 1263 p += pErrRef->getLength(); 1264 nBits &= ~ScRefFlags::COL_VALID; 1265 nCol = -1; 1266 } 1267 else 1268 { 1269 if (rtl::isAsciiAlpha( *p )) 1270 { 1271 const SCCOL nMaxCol = rDoc.MaxCol(); 1272 sal_Int64 n = rtl::toAsciiUpperCase( *p++ ) - 'A'; 1273 while (n < nMaxCol && rtl::isAsciiAlpha(*p)) 1274 n = ((n + 1) * 26) + rtl::toAsciiUpperCase( *p++ ) - 'A'; 1275 if (n > nMaxCol || n < 0 || (*p && *p != '$' && !rtl::isAsciiDigit( *p ) && 1276 (!pErrRef || !lcl_isString( p, *pErrRef)))) 1277 nBits = ScRefFlags::ZERO; 1278 else 1279 nCol = sal::static_int_cast<SCCOL>( n ); 1280 } 1281 else 1282 nBits = ScRefFlags::ZERO; 1283 1284 if( nBits == ScRefFlags::ZERO ) 1285 p = q; 1286 } 1287 nRes |= nBits; 1288 } 1289 1290 q = p; 1291 if (*p) 1292 { 1293 nBits = ScRefFlags::ROW_VALID; 1294 if (*p == '$') 1295 { 1296 nBits |= ScRefFlags::ROW_ABS; 1297 p++; 1298 } 1299 1300 if (pErrRef && lcl_isString( p, *pErrRef)) 1301 { 1302 // #REF! particle of an invalidated reference. 1303 p += pErrRef->getLength(); 1304 // Clearing the ROW_VALID bit here is not possible because of the 1305 // check at the end whether only a valid column was detected in 1306 // which case all bits are cleared because it could be any other 1307 // name. Instead, set to an absolute invalid row value. This will 1308 // display a $#REF! instead of #REF! if the error value was 1309 // relative, but live with it. 1310 nBits |= ScRefFlags::ROW_ABS; 1311 nRow = -1; 1312 } 1313 else 1314 { 1315 if( !rtl::isAsciiDigit( *p ) ) 1316 { 1317 nBits = ScRefFlags::ZERO; 1318 nRow = -1; 1319 } 1320 else 1321 { 1322 sal_Int64 n = rtl_ustr_toInt32( p, 10 ) - 1; 1323 while (rtl::isAsciiDigit( *p )) 1324 p++; 1325 const SCROW nMaxRow = rDoc.MaxRow(); 1326 if( n < 0 || n > nMaxRow ) 1327 nBits = ScRefFlags::ZERO; 1328 nRow = sal::static_int_cast<SCROW>(n); 1329 } 1330 if( nBits == ScRefFlags::ZERO ) 1331 p = q; 1332 } 1333 nRes |= nBits; 1334 } 1335 1336 rAddr.Set( nCol, nRow, nTab ); 1337 1338 if (!*p && bExtDoc) 1339 { 1340 ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager(); 1341 1342 // Need document name if inherited. 1343 if (bExtDocInherited) 1344 { 1345 // The FileId was created using the original file name, so 1346 // obtain that. Otherwise lcl_ScRange_External_TabSpan() would 1347 // retrieve a FileId for the real name and bail out if that 1348 // differed from pExtInfo->mnFileId, as is the case when 1349 // loading documents that refer external files relative to the 1350 // current own document but were saved from a different path 1351 // than loaded. 1352 const OUString* pFileName = pRefMgr->getExternalFileName( pExtInfo->mnFileId, true); 1353 if (pFileName) 1354 aDocName = *pFileName; 1355 else 1356 nRes = ScRefFlags::ZERO; 1357 } 1358 pRefMgr->convertToAbsName(aDocName); 1359 1360 if ((!pExtInfo || !pExtInfo->mbExternal) && pRefMgr->isOwnDocument(aDocName)) 1361 { 1362 if (!rDoc.GetTable( aTab, nTab )) 1363 nRes = ScRefFlags::ZERO; 1364 else 1365 { 1366 rAddr.SetTab( nTab); 1367 nRes |= ScRefFlags::TAB_VALID; 1368 } 1369 } 1370 else 1371 { 1372 if (!pExtInfo) 1373 nRes = ScRefFlags::ZERO; 1374 else 1375 { 1376 if (!pExtInfo->mbExternal) 1377 { 1378 sal_uInt16 nFileId = pRefMgr->getExternalFileId(aDocName); 1379 1380 pExtInfo->mbExternal = true; 1381 pExtInfo->maTabName = aTab; 1382 pExtInfo->mnFileId = nFileId; 1383 1384 if (pRefMgr->getSingleRefToken(nFileId, aTab, 1385 ScAddress(nCol, nRow, 0), nullptr, 1386 &nTab)) 1387 { 1388 rAddr.SetTab( nTab); 1389 nRes |= ScRefFlags::TAB_VALID; 1390 } 1391 else 1392 nRes = ScRefFlags::ZERO; 1393 } 1394 else 1395 { 1396 // This is a call for the second part of the reference, 1397 // we must have the range to adapt tab span. 1398 if (!pRange) 1399 nRes = ScRefFlags::ZERO; 1400 else 1401 { 1402 ScRefFlags nFlags = nRes | ScRefFlags::TAB2_VALID; 1403 if (!lcl_ScRange_External_TabSpan( *pRange, nFlags, 1404 pExtInfo, aDocName, 1405 pExtInfo->maTabName, aTab, rDoc)) 1406 nRes &= ~ScRefFlags::TAB_VALID; 1407 else 1408 { 1409 if (nFlags & ScRefFlags::TAB2_VALID) 1410 { 1411 rAddr.SetTab( pRange->aEnd.Tab()); 1412 nRes |= ScRefFlags::TAB_VALID; 1413 } 1414 else 1415 nRes &= ~ScRefFlags::TAB_VALID; 1416 } 1417 } 1418 } 1419 } 1420 } 1421 } 1422 else if (bExtDoc && pExtInfo && !bExtDocInherited && !pExtInfo->mbExternal && pSheetEndPos) 1423 { 1424 // Pass partial info up to caller, there may be an external name 1425 // following, and if after *pSheetEndPos it's external sheet-local. 1426 // For global names aTab is empty and *pSheetEndPos==0. 1427 pExtInfo->mbExternal = true; 1428 pExtInfo->maTabName = aTab; 1429 pExtInfo->mnFileId = rDoc.GetExternalRefManager()->getExternalFileId(aDocName); 1430 } 1431 1432 rRawRes |= nRes; 1433 1434 if ( !(nRes & ScRefFlags::ROW_VALID) && (nRes & ScRefFlags::COL_VALID) 1435 && !( (nRes & ScRefFlags::TAB_3D) && (nRes & ScRefFlags::TAB_VALID)) ) 1436 { // no Row, no Tab, but Col => DM (...), B (...) et al 1437 nRes = ScRefFlags::ZERO; 1438 } 1439 if( !*p ) 1440 { 1441 ScRefFlags nMask = nRes & ( ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID ); 1442 if( nMask == ( ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID ) ) 1443 nRes |= ScRefFlags::VALID; 1444 } 1445 else 1446 nRes = rRawRes = nBailOutFlags; 1447 return nRes; 1448 } 1449 1450 static ScRefFlags lcl_ScAddress_Parse ( const sal_Unicode* p, const ScDocument& rDoc, ScAddress& rAddr, 1451 const ScAddress::Details& rDetails, 1452 ScAddress::ExternalInfo* pExtInfo, 1453 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks, 1454 sal_Int32* pSheetEndPos, 1455 const OUString* pErrRef ) 1456 { 1457 if( !*p ) 1458 return ScRefFlags::ZERO; 1459 1460 switch (rDetails.eConv) 1461 { 1462 case formula::FormulaGrammar::CONV_XL_A1: 1463 case formula::FormulaGrammar::CONV_XL_OOX: 1464 { 1465 ScRange rRange = rAddr; 1466 ScRefFlags nFlags = lcl_ScRange_Parse_XL_A1( 1467 rRange, p, rDoc, true, pExtInfo, 1468 (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : nullptr), 1469 pSheetEndPos, pErrRef); 1470 rAddr = rRange.aStart; 1471 return nFlags; 1472 } 1473 case formula::FormulaGrammar::CONV_XL_R1C1: 1474 { 1475 ScRange rRange = rAddr; 1476 ScRefFlags nFlags = lcl_ScRange_Parse_XL_R1C1( rRange, p, rDoc, rDetails, true, pExtInfo, pSheetEndPos); 1477 rAddr = rRange.aStart; 1478 return nFlags; 1479 } 1480 default : 1481 case formula::FormulaGrammar::CONV_OOO: 1482 { 1483 ScRefFlags nRawRes = ScRefFlags::ZERO; 1484 return lcl_ScAddress_Parse_OOo( p, rDoc, rAddr, nRawRes, pExtInfo, nullptr, pSheetEndPos, pErrRef); 1485 } 1486 } 1487 } 1488 1489 bool ConvertSingleRef( const ScDocument& rDoc, const OUString& rRefString, 1490 SCTAB nDefTab, ScRefAddress& rRefAddress, 1491 const ScAddress::Details& rDetails, 1492 ScAddress::ExternalInfo* pExtInfo /* = NULL */ ) 1493 { 1494 bool bRet = false; 1495 if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == -1)) 1496 { 1497 ScAddress aAddr( 0, 0, nDefTab ); 1498 ScRefFlags nRes = aAddr.Parse( rRefString, rDoc, rDetails, pExtInfo); 1499 if ( nRes & ScRefFlags::VALID ) 1500 { 1501 rRefAddress.Set( aAddr, 1502 ((nRes & ScRefFlags::COL_ABS) == ScRefFlags::ZERO), 1503 ((nRes & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO), 1504 ((nRes & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO)); 1505 bRet = true; 1506 } 1507 } 1508 return bRet; 1509 } 1510 1511 bool ConvertDoubleRef( const ScDocument& rDoc, const OUString& rRefString, SCTAB nDefTab, 1512 ScRefAddress& rStartRefAddress, ScRefAddress& rEndRefAddress, 1513 const ScAddress::Details& rDetails, 1514 ScAddress::ExternalInfo* pExtInfo /* = NULL */ ) 1515 { 1516 bool bRet = false; 1517 if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == -1)) 1518 { 1519 ScRange aRange( ScAddress( 0, 0, nDefTab)); 1520 ScRefFlags nRes = aRange.Parse( rRefString, rDoc, rDetails, pExtInfo); 1521 if ( nRes & ScRefFlags::VALID ) 1522 { 1523 rStartRefAddress.Set( aRange.aStart, 1524 ((nRes & ScRefFlags::COL_ABS) == ScRefFlags::ZERO), 1525 ((nRes & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO), 1526 ((nRes & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO)); 1527 rEndRefAddress.Set( aRange.aEnd, 1528 ((nRes & ScRefFlags::COL2_ABS) == ScRefFlags::ZERO), 1529 ((nRes & ScRefFlags::ROW2_ABS) == ScRefFlags::ZERO), 1530 ((nRes & ScRefFlags::TAB2_ABS) == ScRefFlags::ZERO)); 1531 bRet = true; 1532 } 1533 } 1534 return bRet; 1535 } 1536 1537 ScRefFlags ScAddress::Parse( const OUString& r, const ScDocument& rDoc, 1538 const Details& rDetails, 1539 ExternalInfo* pExtInfo, 1540 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks, 1541 sal_Int32* pSheetEndPos, 1542 const OUString* pErrRef ) 1543 { 1544 return lcl_ScAddress_Parse( r.getStr(), rDoc, *this, rDetails, pExtInfo, pExternalLinks, pSheetEndPos, pErrRef); 1545 } 1546 1547 ScRange ScRange::Intersection( const ScRange& rOther ) const 1548 { 1549 SCCOL nCol1 = std::max(aStart.Col(), rOther.aStart.Col()); 1550 SCCOL nCol2 = std::min(aEnd.Col(), rOther.aEnd.Col()); 1551 SCROW nRow1 = std::max(aStart.Row(), rOther.aStart.Row()); 1552 SCROW nRow2 = std::min(aEnd.Row(), rOther.aEnd.Row()); 1553 SCTAB nTab1 = std::max(aStart.Tab(), rOther.aStart.Tab()); 1554 SCTAB nTab2 = std::min(aEnd.Tab(), rOther.aEnd.Tab()); 1555 1556 if (nCol1 > nCol2 || nRow1 > nRow2 || nTab1 > nTab2) 1557 return ScRange(ScAddress::INITIALIZE_INVALID); 1558 1559 return ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); 1560 } 1561 1562 void ScRange::ExtendTo( const ScRange& rRange ) 1563 { 1564 OSL_ENSURE( rRange.IsValid(), "ScRange::ExtendTo - cannot extend to invalid range" ); 1565 if( IsValid() ) 1566 { 1567 aStart.SetCol( std::min( aStart.Col(), rRange.aStart.Col() ) ); 1568 aStart.SetRow( std::min( aStart.Row(), rRange.aStart.Row() ) ); 1569 aStart.SetTab( std::min( aStart.Tab(), rRange.aStart.Tab() ) ); 1570 aEnd.SetCol( std::max( aEnd.Col(), rRange.aEnd.Col() ) ); 1571 aEnd.SetRow( std::max( aEnd.Row(), rRange.aEnd.Row() ) ); 1572 aEnd.SetTab( std::max( aEnd.Tab(), rRange.aEnd.Tab() ) ); 1573 } 1574 else 1575 *this = rRange; 1576 } 1577 1578 static ScRefFlags lcl_ScRange_Parse_OOo( ScRange& rRange, 1579 const OUString& r, 1580 const ScDocument& rDoc, 1581 ScAddress::ExternalInfo* pExtInfo, 1582 const OUString* pErrRef ) 1583 { 1584 ScRefFlags nRes1 = ScRefFlags::ZERO, nRes2 = ScRefFlags::ZERO; 1585 sal_Int32 nPos = ScGlobal::FindUnquoted( r, ':'); 1586 if (nPos != -1) 1587 { 1588 OUStringBuffer aTmp(r); 1589 aTmp[nPos] = 0; 1590 const sal_Unicode* p = aTmp.getStr(); 1591 ScRefFlags nRawRes1 = ScRefFlags::ZERO; 1592 nRes1 = lcl_ScAddress_Parse_OOo( p, rDoc, rRange.aStart, nRawRes1, pExtInfo, nullptr, nullptr, pErrRef); 1593 if ((nRes1 != ScRefFlags::ZERO) || 1594 ((nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) && 1595 (nRawRes1 & ScRefFlags::TAB_VALID))) 1596 { 1597 rRange.aEnd = rRange.aStart; // sheet must be initialized identical to first sheet 1598 ScRefFlags nRawRes2 = ScRefFlags::ZERO; 1599 nRes2 = lcl_ScAddress_Parse_OOo( p + nPos+ 1, rDoc, rRange.aEnd, nRawRes2, 1600 pExtInfo, &rRange, nullptr, pErrRef); 1601 if (!((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID)) && 1602 // If not fully valid addresses, check if both have a valid 1603 // column or row, and both have valid (or omitted) sheet references. 1604 (nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) && 1605 (nRawRes1 & ScRefFlags::TAB_VALID) && 1606 (nRawRes2 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) && 1607 (nRawRes2 & ScRefFlags::TAB_VALID) && 1608 // Both must be column XOR row references, A:A or 1:1 but not A:1 or 1:A 1609 ((nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) == 1610 (nRawRes2 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)))) 1611 { 1612 nRes1 = nRawRes1 | ScRefFlags::VALID; 1613 nRes2 = nRawRes2 | ScRefFlags::VALID; 1614 if (nRawRes1 & ScRefFlags::COL_VALID) 1615 { 1616 rRange.aStart.SetRow(0); 1617 rRange.aEnd.SetRow(rDoc.MaxRow()); 1618 nRes1 |= ScRefFlags::ROW_VALID | ScRefFlags::ROW_ABS; 1619 nRes2 |= ScRefFlags::ROW_VALID | ScRefFlags::ROW_ABS; 1620 } 1621 else 1622 { 1623 rRange.aStart.SetCol(0); 1624 rRange.aEnd.SetCol( rDoc.MaxCol() ); 1625 nRes1 |= ScRefFlags::COL_VALID | ScRefFlags::COL_ABS; 1626 nRes2 |= ScRefFlags::COL_VALID | ScRefFlags::COL_ABS; 1627 } 1628 } 1629 else if ((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID)) 1630 { 1631 // Flag entire column/row references so they can be displayed 1632 // as such. If the sticky reference parts are not both 1633 // absolute or relative, assume that the user thought about 1634 // something we should not touch. 1635 if (rRange.aStart.Row() == 0 && rRange.aEnd.Row() == rDoc.MaxRow() && 1636 ((nRes1 & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO) && 1637 ((nRes2 & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO)) 1638 { 1639 nRes1 |= ScRefFlags::ROW_ABS; 1640 nRes2 |= ScRefFlags::ROW_ABS; 1641 } 1642 else if (rRange.aStart.Col() == 0 && rRange.aEnd.Col() == rDoc.MaxCol() && 1643 ((nRes1 & ScRefFlags::COL_ABS) == ScRefFlags::ZERO) && ((nRes2 & ScRefFlags::COL_ABS) == ScRefFlags::ZERO)) 1644 { 1645 nRes1 |= ScRefFlags::COL_ABS; 1646 nRes2 |= ScRefFlags::COL_ABS; 1647 } 1648 } 1649 if ((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID)) 1650 { 1651 // PutInOrder / Justify 1652 ScRefFlags nMask, nBits1, nBits2; 1653 SCCOL nTempCol; 1654 if ( rRange.aEnd.Col() < (nTempCol = rRange.aStart.Col()) ) 1655 { 1656 rRange.aStart.SetCol(rRange.aEnd.Col()); rRange.aEnd.SetCol(nTempCol); 1657 nMask = (ScRefFlags::COL_VALID | ScRefFlags::COL_ABS); 1658 nBits1 = nRes1 & nMask; 1659 nBits2 = nRes2 & nMask; 1660 nRes1 = (nRes1 & ~nMask) | nBits2; 1661 nRes2 = (nRes2 & ~nMask) | nBits1; 1662 } 1663 SCROW nTempRow; 1664 if ( rRange.aEnd.Row() < (nTempRow = rRange.aStart.Row()) ) 1665 { 1666 rRange.aStart.SetRow(rRange.aEnd.Row()); rRange.aEnd.SetRow(nTempRow); 1667 nMask = (ScRefFlags::ROW_VALID | ScRefFlags::ROW_ABS); 1668 nBits1 = nRes1 & nMask; 1669 nBits2 = nRes2 & nMask; 1670 nRes1 = (nRes1 & ~nMask) | nBits2; 1671 nRes2 = (nRes2 & ~nMask) | nBits1; 1672 } 1673 SCTAB nTempTab; 1674 if ( rRange.aEnd.Tab() < (nTempTab = rRange.aStart.Tab()) ) 1675 { 1676 rRange.aStart.SetTab(rRange.aEnd.Tab()); rRange.aEnd.SetTab(nTempTab); 1677 nMask = (ScRefFlags::TAB_VALID | ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D); 1678 nBits1 = nRes1 & nMask; 1679 nBits2 = nRes2 & nMask; 1680 nRes1 = (nRes1 & ~nMask) | nBits2; 1681 nRes2 = (nRes2 & ~nMask) | nBits1; 1682 } 1683 if ( ((nRes1 & ( ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D )) 1684 == ( ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D )) 1685 && !(nRes2 & ScRefFlags::TAB_3D) ) 1686 nRes2 |= ScRefFlags::TAB_ABS; 1687 } 1688 else 1689 { 1690 // Don't leave around valid half references. 1691 nRes1 = nRes2 = ScRefFlags::ZERO; 1692 } 1693 } 1694 } 1695 applyStartToEndFlags(nRes1, nRes2 & ScRefFlags::BITS); 1696 nRes1 |= nRes2 & ScRefFlags::VALID; 1697 return nRes1; 1698 } 1699 1700 ScRefFlags ScRange::Parse( const OUString& rString, const ScDocument& rDoc, 1701 const ScAddress::Details& rDetails, 1702 ScAddress::ExternalInfo* pExtInfo, 1703 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks, 1704 const OUString* pErrRef ) 1705 { 1706 if (rString.isEmpty()) 1707 return ScRefFlags::ZERO; 1708 1709 switch (rDetails.eConv) 1710 { 1711 case formula::FormulaGrammar::CONV_XL_A1: 1712 case formula::FormulaGrammar::CONV_XL_OOX: 1713 { 1714 return lcl_ScRange_Parse_XL_A1( *this, rString.getStr(), rDoc, false, pExtInfo, 1715 (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : nullptr), 1716 nullptr, pErrRef ); 1717 } 1718 1719 case formula::FormulaGrammar::CONV_XL_R1C1: 1720 { 1721 return lcl_ScRange_Parse_XL_R1C1( *this, rString.getStr(), rDoc, rDetails, false, pExtInfo, nullptr ); 1722 } 1723 1724 default: 1725 case formula::FormulaGrammar::CONV_OOO: 1726 { 1727 return lcl_ScRange_Parse_OOo( *this, rString, rDoc, pExtInfo, pErrRef ); 1728 } 1729 } 1730 } 1731 1732 // Accept a full range, or an address 1733 ScRefFlags ScRange::ParseAny( const OUString& rString, const ScDocument& rDoc, 1734 const ScAddress::Details& rDetails ) 1735 { 1736 ScRefFlags nRet = Parse( rString, rDoc, rDetails ); 1737 const ScRefFlags nValid = ScRefFlags::VALID | ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID; 1738 1739 if ( (nRet & nValid) != nValid ) 1740 { 1741 ScAddress aAdr(aStart);//initialize with currentPos as fallback for table number 1742 nRet = aAdr.Parse( rString, rDoc, rDetails ); 1743 if ( nRet & ScRefFlags::VALID ) 1744 aStart = aEnd = aAdr; 1745 } 1746 return nRet; 1747 } 1748 1749 // Parse only full row references 1750 ScRefFlags ScRange::ParseCols( const ScDocument& rDoc, 1751 const OUString& rStr, 1752 const ScAddress::Details& rDetails ) 1753 { 1754 if (rStr.isEmpty()) 1755 return ScRefFlags::ZERO; 1756 1757 const sal_Unicode* p = rStr.getStr(); 1758 ScRefFlags nRes = ScRefFlags::ZERO; 1759 ScRefFlags ignored = ScRefFlags::ZERO; 1760 1761 switch (rDetails.eConv) 1762 { 1763 default : 1764 case formula::FormulaGrammar::CONV_OOO: // No full col refs in OOO yet, assume XL notation 1765 case formula::FormulaGrammar::CONV_XL_A1: 1766 case formula::FormulaGrammar::CONV_XL_OOX: 1767 if (nullptr != (p = lcl_a1_get_col( rDoc, p, &aStart, &ignored, nullptr) ) ) 1768 { 1769 if( p[0] == ':') 1770 { 1771 if( nullptr != (p = lcl_a1_get_col( rDoc, p+1, &aEnd, &ignored, nullptr))) 1772 { 1773 nRes = ScRefFlags::COL_VALID; 1774 } 1775 } 1776 else 1777 { 1778 aEnd = aStart; 1779 nRes = ScRefFlags::COL_VALID; 1780 } 1781 } 1782 break; 1783 1784 case formula::FormulaGrammar::CONV_XL_R1C1: 1785 if ((p[0] == 'C' || p[0] == 'c') && 1786 nullptr != (p = lcl_r1c1_get_col( rDoc.GetSheetLimits(), p, rDetails, &aStart, &ignored ))) 1787 { 1788 if( p[0] == ':') 1789 { 1790 if( (p[1] == 'C' || p[1] == 'c') && 1791 nullptr != (p = lcl_r1c1_get_col( rDoc.GetSheetLimits(), p+1, rDetails, &aEnd, &ignored ))) 1792 { 1793 nRes = ScRefFlags::COL_VALID; 1794 } 1795 } 1796 else 1797 { 1798 aEnd = aStart; 1799 nRes = ScRefFlags::COL_VALID; 1800 } 1801 } 1802 break; 1803 } 1804 1805 return (p != nullptr && *p == '\0') ? nRes : ScRefFlags::ZERO; 1806 } 1807 1808 // Parse only full row references 1809 void ScRange::ParseRows( const ScDocument& rDoc, 1810 const OUString& rStr, 1811 const ScAddress::Details& rDetails ) 1812 { 1813 if (rStr.isEmpty()) 1814 return; 1815 1816 const sal_Unicode* p = rStr.getStr(); 1817 ScRefFlags ignored = ScRefFlags::ZERO; 1818 1819 switch (rDetails.eConv) 1820 { 1821 default : 1822 case formula::FormulaGrammar::CONV_OOO: // No full row refs in OOO yet, assume XL notation 1823 case formula::FormulaGrammar::CONV_XL_A1: 1824 case formula::FormulaGrammar::CONV_XL_OOX: 1825 if (nullptr != (p = lcl_a1_get_row( rDoc, p, &aStart, &ignored, nullptr) ) ) 1826 { 1827 if( p[0] == ':') 1828 { 1829 lcl_a1_get_row( rDoc, p+1, &aEnd, &ignored, nullptr); 1830 } 1831 else 1832 { 1833 aEnd = aStart; 1834 } 1835 } 1836 break; 1837 1838 case formula::FormulaGrammar::CONV_XL_R1C1: 1839 if ((p[0] == 'R' || p[0] == 'r') && 1840 nullptr != (p = lcl_r1c1_get_row( rDoc.GetSheetLimits(), p, rDetails, &aStart, &ignored ))) 1841 { 1842 if( p[0] == ':') 1843 { 1844 if( p[1] == 'R' || p[1] == 'r' ) 1845 { 1846 lcl_r1c1_get_row( rDoc.GetSheetLimits(), p+1, rDetails, &aEnd, &ignored ); 1847 } 1848 } 1849 else 1850 { 1851 aEnd = aStart; 1852 } 1853 } 1854 break; 1855 } 1856 } 1857 1858 template<typename T > static void lcl_ScColToAlpha( T& rBuf, SCCOL nCol ) 1859 { 1860 if (nCol < 26*26) 1861 { 1862 if (nCol < 26) 1863 rBuf.append( static_cast<char>( 'A' + nCol )); 1864 else 1865 { 1866 rBuf.append( static_cast<char>( 'A' + nCol / 26 - 1 )); 1867 rBuf.append( static_cast<char>( 'A' + nCol % 26 )); 1868 } 1869 } 1870 else 1871 { 1872 sal_Int32 nInsert = rBuf.getLength(); 1873 while (nCol >= 26) 1874 { 1875 SCCOL nC = nCol % 26; 1876 rBuf.insert(nInsert, static_cast<char> ( 'A' + nC )); 1877 nCol = sal::static_int_cast<SCCOL>( nCol - nC ); 1878 nCol = nCol / 26 - 1; 1879 } 1880 rBuf.insert(nInsert, static_cast<char> ( 'A' + nCol )); 1881 } 1882 } 1883 1884 void ScColToAlpha( OUStringBuffer& rBuf, SCCOL nCol) 1885 { 1886 lcl_ScColToAlpha(rBuf, nCol); 1887 } 1888 1889 template <typename T > static void lcl_a1_append_c ( T &rString, int nCol, bool bIsAbs ) 1890 { 1891 if( bIsAbs ) 1892 rString.append("$"); 1893 lcl_ScColToAlpha( rString, sal::static_int_cast<SCCOL>(nCol) ); 1894 } 1895 1896 template <typename T > static void lcl_a1_append_r ( T &rString, sal_Int32 nRow, bool bIsAbs ) 1897 { 1898 if ( bIsAbs ) 1899 rString.append("$"); 1900 rString.append( nRow + 1 ); 1901 } 1902 1903 template <typename T > static void lcl_r1c1_append_c ( T &rString, sal_Int32 nCol, bool bIsAbs, 1904 const ScAddress::Details& rDetails ) 1905 { 1906 rString.append("C"); 1907 if (bIsAbs) 1908 { 1909 rString.append( nCol + 1 ); 1910 } 1911 else 1912 { 1913 nCol -= rDetails.nCol; 1914 if (nCol != 0) { 1915 rString.append("[").append(nCol).append("]"); 1916 } 1917 } 1918 } 1919 1920 template <typename T > static void lcl_r1c1_append_r ( T &rString, sal_Int32 nRow, bool bIsAbs, 1921 const ScAddress::Details& rDetails ) 1922 { 1923 rString.append("R"); 1924 if (bIsAbs) 1925 { 1926 rString.append( nRow + 1 ); 1927 } 1928 else 1929 { 1930 nRow -= rDetails.nRow; 1931 if (nRow != 0) { 1932 rString.append("[").append(nRow).append("]"); 1933 } 1934 } 1935 } 1936 1937 static OUString getFileNameFromDoc( const ScDocument* pDoc ) 1938 { 1939 // TODO : er points at ScGlobal::GetAbsDocName() 1940 // as a better template. Look into it 1941 OUString sFileName; 1942 SfxObjectShell* pShell; 1943 1944 if( nullptr != pDoc && 1945 nullptr != (pShell = pDoc->GetDocumentShell() ) ) 1946 { 1947 uno::Reference< frame::XModel > xModel = pShell->GetModel(); 1948 if( xModel.is() ) 1949 { 1950 if( !xModel->getURL().isEmpty() ) 1951 { 1952 INetURLObject aURL( xModel->getURL() ); 1953 sFileName = aURL.GetLastName(); 1954 } 1955 else 1956 sFileName = pShell->GetTitle(); 1957 } 1958 } 1959 return sFileName; 1960 } 1961 1962 1963 static void lcl_string_append(OUStringBuffer &rString, std::u16string_view sString) 1964 { 1965 rString.append(sString); 1966 } 1967 1968 static void lcl_string_append(OStringBuffer &rString, std::u16string_view sString) 1969 { 1970 rString.append(OUStringToOString( sString, RTL_TEXTENCODING_UTF8 )); 1971 } 1972 1973 template<typename T > static void lcl_Format( T& r, SCTAB nTab, SCROW nRow, SCCOL nCol, ScRefFlags nFlags, 1974 const ScDocument* pDoc, 1975 const ScAddress::Details& rDetails) 1976 { 1977 if( nFlags & ScRefFlags::VALID ) 1978 nFlags |= ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID; 1979 if( pDoc && (nFlags & ScRefFlags::TAB_VALID ) ) 1980 { 1981 if ( nTab < 0 || nTab >= pDoc->GetTableCount() ) 1982 { 1983 lcl_string_append(r, ScCompiler::GetNativeSymbol(ocErrRef)); 1984 return; 1985 } 1986 if( nFlags & ScRefFlags::TAB_3D ) 1987 { 1988 OUString aTabName, aDocName; 1989 pDoc->GetName(nTab, aTabName); 1990 assert( !aTabName.isEmpty() && "empty sheet name"); 1991 // External Reference, same as in ScCompiler::MakeTabStr() 1992 if( aTabName[0] == '\'' ) 1993 { // "'Doc'#Tab" 1994 sal_Int32 nPos = ScCompiler::GetDocTabPos( aTabName); 1995 if (nPos != -1) 1996 { 1997 aDocName = aTabName.copy( 0, nPos + 1 ); 1998 aTabName = aTabName.copy( nPos + 1 ); 1999 } 2000 } 2001 else if( nFlags & ScRefFlags::FORCE_DOC ) 2002 { 2003 // VBA has an 'external' flag that forces the addition of the 2004 // tab name _and_ the doc name. The VBA code would be 2005 // needlessly complicated if it constructed an actual external 2006 // reference so we add this somewhat cheesy kludge to force the 2007 // addition of the document name even for non-external references 2008 aDocName = getFileNameFromDoc( pDoc ); 2009 } 2010 ScCompiler::CheckTabQuotes( aTabName, rDetails.eConv); 2011 2012 switch( rDetails.eConv ) 2013 { 2014 default : 2015 case formula::FormulaGrammar::CONV_OOO: 2016 lcl_string_append(r, aDocName); 2017 if( nFlags & ScRefFlags::TAB_ABS ) 2018 r.append("$"); 2019 lcl_string_append(r, aTabName); 2020 r.append("."); 2021 break; 2022 2023 case formula::FormulaGrammar::CONV_XL_OOX: 2024 if (!aTabName.isEmpty() && aTabName[0] == '\'') 2025 { 2026 if (!aDocName.isEmpty()) 2027 { 2028 lcl_string_append(r.append("'["), aDocName); 2029 r.append("]"); 2030 lcl_string_append(r, aTabName.subView(1)); 2031 } 2032 else 2033 { 2034 lcl_string_append(r, aTabName); 2035 } 2036 r.append("!"); 2037 break; 2038 } 2039 [[fallthrough]]; 2040 case formula::FormulaGrammar::CONV_XL_A1: 2041 case formula::FormulaGrammar::CONV_XL_R1C1: 2042 if (!aDocName.isEmpty()) 2043 { 2044 lcl_string_append(r.append("["), aDocName); 2045 r.append("]"); 2046 } 2047 lcl_string_append(r, aTabName); 2048 r.append("!"); 2049 break; 2050 } 2051 } 2052 } 2053 switch( rDetails.eConv ) 2054 { 2055 default : 2056 case formula::FormulaGrammar::CONV_OOO: 2057 case formula::FormulaGrammar::CONV_XL_A1: 2058 case formula::FormulaGrammar::CONV_XL_OOX: 2059 if( nFlags & ScRefFlags::COL_VALID ) 2060 lcl_a1_append_c ( r, nCol, (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO ); 2061 if( nFlags & ScRefFlags::ROW_VALID ) 2062 lcl_a1_append_r ( r, nRow, (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO ); 2063 break; 2064 2065 case formula::FormulaGrammar::CONV_XL_R1C1: 2066 if( nFlags & ScRefFlags::ROW_VALID ) 2067 lcl_r1c1_append_r ( r, nRow, (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails ); 2068 if( nFlags & ScRefFlags::COL_VALID ) 2069 lcl_r1c1_append_c ( r, nCol, (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails ); 2070 break; 2071 } 2072 } 2073 2074 void ScAddress::Format( OStringBuffer& r, ScRefFlags nFlags, 2075 const ScDocument* pDoc, 2076 const Details& rDetails) const 2077 { 2078 lcl_Format(r, nTab, nRow, nCol, nFlags, pDoc, rDetails); 2079 } 2080 2081 OUString ScAddress::Format(ScRefFlags nFlags, const ScDocument* pDoc, 2082 const Details& rDetails) const 2083 { 2084 OUStringBuffer r; 2085 lcl_Format(r, nTab, nRow, nCol, nFlags, pDoc, rDetails); 2086 return r.makeStringAndClear(); 2087 } 2088 2089 static void lcl_Split_DocTab( const ScDocument& rDoc, SCTAB nTab, 2090 const ScAddress::Details& rDetails, 2091 ScRefFlags nFlags, 2092 OUString& rTabName, OUString& rDocName ) 2093 { 2094 rDoc.GetName(nTab, rTabName); 2095 rDocName.clear(); 2096 // External reference, same as in ScCompiler::MakeTabStr() 2097 if ( rTabName[0] == '\'' ) 2098 { // "'Doc'#Tab" 2099 sal_Int32 nPos = ScCompiler::GetDocTabPos( rTabName); 2100 if (nPos != -1) 2101 { 2102 rDocName = rTabName.copy( 0, nPos + 1 ); 2103 rTabName = rTabName.copy( nPos + 1 ); 2104 } 2105 } 2106 else if( nFlags & ScRefFlags::FORCE_DOC ) 2107 { 2108 // VBA has an 'external' flag that forces the addition of the 2109 // tab name _and_ the doc name. The VBA code would be 2110 // needlessly complicated if it constructed an actual external 2111 // reference so we add this somewhat cheesy kludge to force the 2112 // addition of the document name even for non-external references 2113 rDocName = getFileNameFromDoc(&rDoc); 2114 } 2115 ScCompiler::CheckTabQuotes( rTabName, rDetails.eConv); 2116 } 2117 2118 static void lcl_ScRange_Format_XL_Header( OUStringBuffer& rString, const ScRange& rRange, 2119 ScRefFlags nFlags, const ScDocument& rDoc, 2120 const ScAddress::Details& rDetails ) 2121 { 2122 if( !(nFlags & ScRefFlags::TAB_3D) ) 2123 return; 2124 2125 OUString aTabName, aDocName; 2126 lcl_Split_DocTab( rDoc, rRange.aStart.Tab(), rDetails, nFlags, aTabName, aDocName ); 2127 switch (rDetails.eConv) 2128 { 2129 case formula::FormulaGrammar::CONV_XL_OOX: 2130 if (!aTabName.isEmpty() && aTabName[0] == '\'') 2131 { 2132 if (!aDocName.isEmpty()) 2133 { 2134 rString.append("'[" + aDocName + "]" + aTabName.subView(1)); 2135 } 2136 else 2137 { 2138 rString.append(aTabName); 2139 } 2140 break; 2141 } 2142 [[fallthrough]]; 2143 default: 2144 if (!aDocName.isEmpty()) 2145 { 2146 rString.append("[" + aDocName + "]"); 2147 } 2148 rString.append(aTabName); 2149 break; 2150 } 2151 if( nFlags & ScRefFlags::TAB2_3D ) 2152 { 2153 lcl_Split_DocTab( rDoc, rRange.aEnd.Tab(), rDetails, nFlags, aTabName, aDocName ); 2154 rString.append(":"); 2155 rString.append(aTabName); 2156 } 2157 rString.append("!"); 2158 } 2159 2160 // helpers used in ScRange::Format 2161 static bool lcl_ColAbsFlagDiffer(const ScRefFlags nFlags) 2162 { 2163 return static_cast<bool>(nFlags & ScRefFlags::COL_ABS) != static_cast<bool>(nFlags & ScRefFlags::COL2_ABS); 2164 } 2165 static bool lcl_RowAbsFlagDiffer(const ScRefFlags nFlags) 2166 { 2167 return static_cast<bool>(nFlags & ScRefFlags::ROW_ABS) != static_cast<bool>(nFlags & ScRefFlags::ROW2_ABS); 2168 } 2169 2170 OUString ScRange::Format( const ScDocument& rDoc, ScRefFlags nFlags, 2171 const ScAddress::Details& rDetails, bool bFullAddressNotation ) const 2172 { 2173 if( !( nFlags & ScRefFlags::VALID ) ) 2174 { 2175 return ScCompiler::GetNativeSymbol(ocErrRef); 2176 } 2177 2178 OUStringBuffer r; 2179 switch( rDetails.eConv ) { 2180 default : 2181 case formula::FormulaGrammar::CONV_OOO: { 2182 bool bOneTab = (aStart.Tab() == aEnd.Tab()); 2183 if ( !bOneTab ) 2184 nFlags |= ScRefFlags::TAB_3D; 2185 r = aStart.Format(nFlags, &rDoc, rDetails); 2186 if( aStart != aEnd || 2187 lcl_ColAbsFlagDiffer( nFlags ) || 2188 lcl_RowAbsFlagDiffer( nFlags )) 2189 { 2190 const ScDocument* pDoc = &rDoc; 2191 // move flags of end reference to start reference, mask with BITS to exclude FORCE_DOC flag 2192 nFlags = ScRefFlags::VALID | (ScRefFlags(o3tl::to_underlying(nFlags) >> 4) & ScRefFlags::BITS); 2193 if ( bOneTab ) 2194 pDoc = nullptr; 2195 else 2196 nFlags |= ScRefFlags::TAB_3D; 2197 OUString aName(aEnd.Format(nFlags, pDoc, rDetails)); 2198 r.append(":"); 2199 r.append(aName); 2200 } 2201 break; 2202 } 2203 2204 case formula::FormulaGrammar::CONV_XL_A1: 2205 case formula::FormulaGrammar::CONV_XL_OOX: { 2206 SCCOL nMaxCol = rDoc.MaxCol(); 2207 SCROW nMaxRow = rDoc.MaxRow(); 2208 2209 lcl_ScRange_Format_XL_Header( r, *this, nFlags, rDoc, rDetails ); 2210 if( aStart.Col() == 0 && aEnd.Col() >= nMaxCol && !bFullAddressNotation ) 2211 { 2212 // Full col refs always require 2 rows (2:2) 2213 lcl_a1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO ); 2214 r.append(":"); 2215 lcl_a1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO ); 2216 } 2217 else if( aStart.Row() == 0 && aEnd.Row() >= nMaxRow && !bFullAddressNotation ) 2218 { 2219 // Full row refs always require 2 cols (A:A) 2220 lcl_a1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO ); 2221 r.append(":"); 2222 lcl_a1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO ); 2223 } 2224 else 2225 { 2226 lcl_a1_append_c ( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO ); 2227 lcl_a1_append_r ( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO ); 2228 if( aStart.Col() != aEnd.Col() || 2229 lcl_ColAbsFlagDiffer( nFlags ) || 2230 aStart.Row() != aEnd.Row() || 2231 lcl_RowAbsFlagDiffer( nFlags ) ) { 2232 r.append(":"); 2233 lcl_a1_append_c ( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO ); 2234 lcl_a1_append_r ( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO ); 2235 } 2236 } 2237 break; 2238 } 2239 2240 case formula::FormulaGrammar::CONV_XL_R1C1: { 2241 SCCOL nMaxCol = rDoc.MaxCol(); 2242 SCROW nMaxRow = rDoc.MaxRow(); 2243 2244 lcl_ScRange_Format_XL_Header( r, *this, nFlags, rDoc, rDetails ); 2245 if( aStart.Col() == 0 && aEnd.Col() >= nMaxCol && !bFullAddressNotation ) 2246 { 2247 lcl_r1c1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails ); 2248 if( aStart.Row() != aEnd.Row() || 2249 lcl_RowAbsFlagDiffer( nFlags ) ) { 2250 r.append(":"); 2251 lcl_r1c1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO, rDetails ); 2252 } 2253 } 2254 else if( aStart.Row() == 0 && aEnd.Row() >= nMaxRow && !bFullAddressNotation ) 2255 { 2256 lcl_r1c1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails ); 2257 if( aStart.Col() != aEnd.Col() || 2258 lcl_ColAbsFlagDiffer( nFlags )) { 2259 r.append(":"); 2260 lcl_r1c1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO, rDetails ); 2261 } 2262 } 2263 else 2264 { 2265 lcl_r1c1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails ); 2266 lcl_r1c1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails ); 2267 if( aStart.Col() != aEnd.Col() || 2268 lcl_ColAbsFlagDiffer( nFlags ) || 2269 aStart.Row() != aEnd.Row() || 2270 lcl_RowAbsFlagDiffer( nFlags ) ) { 2271 r.append(":"); 2272 lcl_r1c1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO, rDetails ); 2273 lcl_r1c1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO, rDetails ); 2274 } 2275 } 2276 break; 2277 } 2278 } 2279 return r.makeStringAndClear(); 2280 } 2281 2282 bool ScAddress::Move( SCCOL dx, SCROW dy, SCTAB dz, ScAddress& rErrorPos, const ScDocument& rDoc ) 2283 { 2284 SCTAB nMaxTab = rDoc.GetTableCount(); 2285 SCCOL nMaxCol = rDoc.MaxCol(); 2286 SCROW nMaxRow = rDoc.MaxRow(); 2287 dx = Col() + dx; 2288 dy = Row() + dy; 2289 dz = Tab() + dz; 2290 bool bValid = true; 2291 rErrorPos.SetCol(dx); 2292 if( dx < 0 ) 2293 { 2294 dx = 0; 2295 bValid = false; 2296 } 2297 else if( dx > nMaxCol ) 2298 { 2299 dx = nMaxCol; 2300 bValid =false; 2301 } 2302 rErrorPos.SetRow(dy); 2303 if( dy < 0 ) 2304 { 2305 dy = 0; 2306 bValid = false; 2307 } 2308 else if( dy > nMaxRow ) 2309 { 2310 dy = nMaxRow; 2311 bValid =false; 2312 } 2313 rErrorPos.SetTab(dz); 2314 if( dz < 0 ) 2315 { 2316 dz = 0; 2317 bValid = false; 2318 } 2319 else if( dz > nMaxTab ) 2320 { 2321 // Always set MAXTAB+1 so further checks without ScDocument detect invalid. 2322 rErrorPos.SetTab(MAXTAB+1); 2323 dz = nMaxTab; 2324 bValid =false; 2325 } 2326 Set( dx, dy, dz ); 2327 return bValid; 2328 } 2329 2330 bool ScRange::Move( SCCOL dx, SCROW dy, SCTAB dz, ScRange& rErrorRange, const ScDocument& rDoc ) 2331 { 2332 SCCOL nMaxCol = rDoc.MaxCol(); 2333 SCROW nMaxRow = rDoc.MaxRow(); 2334 if (dy && aStart.Row() == 0 && aEnd.Row() == nMaxRow) 2335 dy = 0; // Entire column not to be moved. 2336 if (dx && aStart.Col() == 0 && aEnd.Col() == nMaxCol) 2337 dx = 0; // Entire row not to be moved. 2338 bool b = aStart.Move( dx, dy, dz, rErrorRange.aStart, rDoc ); 2339 b &= aEnd.Move( dx, dy, dz, rErrorRange.aEnd, rDoc ); 2340 return b; 2341 } 2342 2343 bool ScRange::MoveSticky( const ScDocument& rDoc, SCCOL dx, SCROW dy, SCTAB dz, ScRange& rErrorRange ) 2344 { 2345 const SCCOL nMaxCol = rDoc.MaxCol(); 2346 const SCROW nMaxRow = rDoc.MaxRow(); 2347 bool bColRange = (aStart.Col() < aEnd.Col()); 2348 bool bRowRange = (aStart.Row() < aEnd.Row()); 2349 if (dy && aStart.Row() == 0 && aEnd.Row() == nMaxRow) 2350 dy = 0; // Entire column not to be moved. 2351 if (dx && aStart.Col() == 0 && aEnd.Col() == nMaxCol) 2352 dx = 0; // Entire row not to be moved. 2353 bool b1 = aStart.Move( dx, dy, dz, rErrorRange.aStart, rDoc ); 2354 if (dx && bColRange && aEnd.Col() == nMaxCol) 2355 dx = 0; // End column sticky. 2356 if (dy && bRowRange && aEnd.Row() == nMaxRow) 2357 dy = 0; // End row sticky. 2358 SCTAB nOldTab = aEnd.Tab(); 2359 bool b2 = aEnd.Move( dx, dy, dz, rErrorRange.aEnd, rDoc ); 2360 if (!b2) 2361 { 2362 // End column or row of a range may have become sticky. 2363 bColRange = (!dx || (bColRange && aEnd.Col() == nMaxCol)); 2364 if (dx && bColRange) 2365 rErrorRange.aEnd.SetCol(nMaxCol); 2366 bRowRange = (!dy || (bRowRange && aEnd.Row() == nMaxRow)); 2367 if (dy && bRowRange) 2368 rErrorRange.aEnd.SetRow(nMaxRow); 2369 b2 = bColRange && bRowRange && (aEnd.Tab() - nOldTab == dz); 2370 } 2371 return b1 && b2; 2372 } 2373 2374 void ScRange::IncColIfNotLessThan(const ScDocument& rDoc, SCCOL nStartCol, SCCOL nOffset) 2375 { 2376 if (aStart.Col() >= nStartCol) 2377 { 2378 aStart.IncCol(nOffset); 2379 if (aStart.Col() < 0) 2380 aStart.SetCol(0); 2381 else if(aStart.Col() > rDoc.MaxCol()) 2382 aStart.SetCol(rDoc.MaxCol()); 2383 } 2384 if (aEnd.Col() >= nStartCol) 2385 { 2386 aEnd.IncCol(nOffset); 2387 if (aEnd.Col() < 0) 2388 aEnd.SetCol(0); 2389 else if(aEnd.Col() > rDoc.MaxCol()) 2390 aEnd.SetCol(rDoc.MaxCol()); 2391 } 2392 } 2393 2394 void ScRange::IncRowIfNotLessThan(const ScDocument& rDoc, SCROW nStartRow, SCROW nOffset) 2395 { 2396 if (aStart.Row() >= nStartRow) 2397 { 2398 aStart.IncRow(nOffset); 2399 if (aStart.Row() < 0) 2400 aStart.SetRow(0); 2401 else if(aStart.Row() > rDoc.MaxRow()) 2402 aStart.SetRow(rDoc.MaxRow()); 2403 } 2404 if (aEnd.Row() >= nStartRow) 2405 { 2406 aEnd.IncRow(nOffset); 2407 if (aEnd.Row() < 0) 2408 aEnd.SetRow(0); 2409 else if(aEnd.Row() > rDoc.MaxRow()) 2410 aEnd.SetRow(rDoc.MaxRow()); 2411 } 2412 } 2413 2414 bool ScRange::IsEndColSticky( const ScDocument& rDoc ) const 2415 { 2416 // Only in an actual column range, i.e. not if both columns are MAXCOL. 2417 return aEnd.Col() == rDoc.MaxCol() && aStart.Col() < aEnd.Col(); 2418 } 2419 2420 bool ScRange::IsEndRowSticky( const ScDocument& rDoc ) const 2421 { 2422 // Only in an actual row range, i.e. not if both rows are MAXROW. 2423 return aEnd.Row() == rDoc.MaxRow() && aStart.Row() < aEnd.Row(); 2424 } 2425 2426 void ScRange::IncEndColSticky( const ScDocument& rDoc, SCCOL nDelta ) 2427 { 2428 SCCOL nCol = aEnd.Col(); 2429 if (aStart.Col() >= nCol) 2430 { 2431 // Less than two columns => not sticky. 2432 aEnd.IncCol( nDelta); 2433 return; 2434 } 2435 2436 const SCCOL nMaxCol = rDoc.MaxCol(); 2437 if (nCol == nMaxCol) 2438 // already sticky 2439 return; 2440 2441 if (nCol < nMaxCol) 2442 aEnd.SetCol( ::std::min( static_cast<SCCOL>(nCol + nDelta), nMaxCol)); 2443 else 2444 aEnd.IncCol( nDelta); // was greater than nMaxCol, caller should know... 2445 } 2446 2447 void ScRange::IncEndRowSticky( const ScDocument& rDoc, SCROW nDelta ) 2448 { 2449 SCROW nRow = aEnd.Row(); 2450 if (aStart.Row() >= nRow) 2451 { 2452 // Less than two rows => not sticky. 2453 aEnd.IncRow( nDelta); 2454 return; 2455 } 2456 2457 if (nRow == rDoc.MaxRow()) 2458 // already sticky 2459 return; 2460 2461 if (nRow < rDoc.MaxRow()) 2462 aEnd.SetRow( ::std::min( static_cast<SCROW>(nRow + nDelta), rDoc.MaxRow())); 2463 else 2464 aEnd.IncRow( nDelta); // was greater than rDoc.MaxRow(), caller should know... 2465 } 2466 2467 OUString ScAddress::GetColRowString() const 2468 { 2469 OUStringBuffer aString; 2470 2471 switch( detailsOOOa1.eConv ) 2472 { 2473 default : 2474 case formula::FormulaGrammar::CONV_OOO: 2475 case formula::FormulaGrammar::CONV_XL_A1: 2476 case formula::FormulaGrammar::CONV_XL_OOX: 2477 lcl_ScColToAlpha( aString, nCol); 2478 aString.append(nRow+1); 2479 break; 2480 2481 case formula::FormulaGrammar::CONV_XL_R1C1: 2482 lcl_r1c1_append_r ( aString, nRow, false/*bAbsolute*/, detailsOOOa1 ); 2483 lcl_r1c1_append_c ( aString, nCol, false/*bAbsolute*/, detailsOOOa1 ); 2484 break; 2485 } 2486 2487 return aString.makeStringAndClear(); 2488 } 2489 2490 OUString ScRefAddress::GetRefString( const ScDocument& rDoc, SCTAB nActTab, 2491 const ScAddress::Details& rDetails ) const 2492 { 2493 if ( Tab()+1 > rDoc.GetTableCount() ) 2494 return ScCompiler::GetNativeSymbol(ocErrRef); 2495 2496 ScRefFlags nFlags = ScRefFlags::VALID; 2497 if ( nActTab != Tab() ) 2498 { 2499 nFlags |= ScRefFlags::TAB_3D; 2500 if ( !bRelTab ) 2501 nFlags |= ScRefFlags::TAB_ABS; 2502 } 2503 if ( !bRelCol ) 2504 nFlags |= ScRefFlags::COL_ABS; 2505 if ( !bRelRow ) 2506 nFlags |= ScRefFlags::ROW_ABS; 2507 2508 return aAdr.Format(nFlags, &rDoc, rDetails); 2509 } 2510 2511 bool AlphaToCol(const ScDocument& rDoc, SCCOL& rCol, const OUString& rStr) 2512 { 2513 SCCOL nResult = 0; 2514 sal_Int32 nStop = rStr.getLength(); 2515 sal_Int32 nPos = 0; 2516 sal_Unicode c; 2517 const SCCOL nMaxCol = rDoc.MaxCol(); 2518 while (nResult <= nMaxCol && nPos < nStop && (c = rStr[nPos]) != 0 && 2519 rtl::isAsciiAlpha(c)) 2520 { 2521 if (nPos > 0) 2522 nResult = (nResult + 1) * 26; 2523 nResult += ScGlobal::ToUpperAlpha(c) - 'A'; 2524 ++nPos; 2525 } 2526 bool bOk = (rDoc.ValidCol(nResult) && nPos > 0); 2527 if (bOk) 2528 rCol = nResult; 2529 return bOk; 2530 } 2531 2532 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 2533
