LibreOffice Module sc (master) 1
address.cxx
Go to the documentation of this file.
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>
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>
40
41using namespace css;
42
44
46 const ScAddress& rAddr ) :
47 eConv( rDoc.GetAddressConvention() ),
48 nRow( rAddr.Row() ),
49 nCol( rAddr.Col() )
50{}
51
52namespace {
53
54const 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
102const 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
136static 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
166{
167 if ( p )
168 {
169 while( *p == ' ' )
170 ++p;
171 }
172 return p;
173}
174
175// Compare ignore case ASCII.
176static 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
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
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)
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
294static 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
424static 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
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 {
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 }
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.
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 {
614 // Use the current tab, it needs to be passed in. : aEnd.SetTab( .. );
615 }
616
617 if (!rExternDocName.isEmpty())
618 {
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
658static 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
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
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;
766 // Keep in mind that nFlags2 gets left-shifted by 4 bits before being merged.
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;
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.
813 return nFlags;
814 }
815
816 nFlags |=
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.
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.
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.
884 return nFlags;
885 }
886
887 nFlags |=
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
899static 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
935static 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
968static 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
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
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;
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 |=
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 |=
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.
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 |
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 );
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.
1121 return nFlags;
1122 }
1123
1124 applyStartToEndFlags(nFlags, nFlags2);
1125 return nFlags;
1126}
1127
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;
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;
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 {
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 {
1443 nRes |= ScRefFlags::VALID;
1444 }
1445 else
1446 nRes = rRawRes = nBailOutFlags;
1447 return nRes;
1448}
1449
1450static 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 {
1464 {
1465 ScRange rRange = rAddr;
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 }
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 :
1482 {
1483 ScRefFlags nRawRes = ScRefFlags::ZERO;
1484 return lcl_ScAddress_Parse_OOo( p, rDoc, rAddr, nRawRes, pExtInfo, nullptr, pSheetEndPos, pErrRef);
1485 }
1486 }
1487}
1488
1489bool 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,
1505 bRet = true;
1506 }
1507 }
1508 return bRet;
1509}
1510
1511bool 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,
1527 rEndRefAddress.Set( aRange.aEnd,
1531 bRet = true;
1532 }
1533 }
1534 return bRet;
1535}
1536
1537ScRefFlags 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
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)
1558
1559 return ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
1560}
1561
1562void 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
1579 const OUString& r,
1580 const ScDocument& rDoc,
1581 ScAddress::ExternalInfo* pExtInfo,
1582 const OUString* pErrRef )
1583{
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.
1605 (nRawRes1 & ScRefFlags::TAB_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)) ==
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());
1620 }
1621 else
1622 {
1623 rRange.aStart.SetCol(0);
1624 rRange.aEnd.SetCol( rDoc.MaxCol() );
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) &&
1638 {
1639 nRes1 |= ScRefFlags::ROW_ABS;
1640 nRes2 |= ScRefFlags::ROW_ABS;
1641 }
1642 else if (rRange.aStart.Col() == 0 && rRange.aEnd.Col() == rDoc.MaxCol() &&
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);
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);
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);
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 ))
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
1700ScRefFlags 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 {
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
1720 {
1721 return lcl_ScRange_Parse_XL_R1C1( *this, rString.getStr(), rDoc, rDetails, false, pExtInfo, nullptr );
1722 }
1723
1724 default:
1726 {
1727 return lcl_ScRange_Parse_OOo( *this, rString, rDoc, pExtInfo, pErrRef );
1728 }
1729 }
1730}
1731
1732// Accept a full range, or an address
1733ScRefFlags ScRange::ParseAny( const OUString& rString, const ScDocument& rDoc,
1734 const ScAddress::Details& rDetails )
1735{
1736 ScRefFlags nRet = Parse( rString, rDoc, rDetails );
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
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();
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
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
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
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
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
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
1858template<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
1884void ScColToAlpha( OUStringBuffer& rBuf, SCCOL nCol)
1885{
1886 lcl_ScColToAlpha(rBuf, nCol);
1887}
1888
1889template <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
1896template <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
1903template <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
1920template <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
1937static 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
1963static void lcl_string_append(OUStringBuffer &rString, std::u16string_view sString)
1964{
1965 rString.append(sString);
1966}
1967
1968static void lcl_string_append(OStringBuffer &rString, std::u16string_view sString)
1969{
1970 rString.append(OUStringToOString( sString, RTL_TEXTENCODING_UTF8 ));
1971}
1972
1973template<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 )
1979 if( pDoc && (nFlags & ScRefFlags::TAB_VALID ) )
1980 {
1981 if ( nTab < 0 || nTab >= pDoc->GetTableCount() )
1982 {
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 :
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
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]];
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 :
2059 if( nFlags & ScRefFlags::COL_VALID )
2061 if( nFlags & ScRefFlags::ROW_VALID )
2063 break;
2064
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
2074void 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
2081OUString 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
2089static 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
2118static 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 {
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
2161static bool lcl_ColAbsFlagDiffer(const ScRefFlags nFlags)
2162{
2163 return static_cast<bool>(nFlags & ScRefFlags::COL_ABS) != static_cast<bool>(nFlags & ScRefFlags::COL2_ABS);
2164}
2165static 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
2170OUString ScRange::Format( const ScDocument& rDoc, ScRefFlags nFlags,
2171 const ScAddress::Details& rDetails, bool bFullAddressNotation ) const
2172{
2173 if( !( nFlags & ScRefFlags::VALID ) )
2174 {
2176 }
2177
2178 OUStringBuffer r;
2179 switch( rDetails.eConv ) {
2180 default :
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
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
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
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
2282bool 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
2330bool 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
2343bool 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
2374void 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
2394void 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
2414bool 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
2420bool 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
2426void 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
2447void 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
2468{
2469 OUStringBuffer aString;
2470
2471 switch( detailsOOOa1.eConv )
2472 {
2473 default :
2477 lcl_ScColToAlpha( aString, nCol);
2478 aString.append(nRow+1);
2479 break;
2480
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
2490OUString ScRefAddress::GetRefString( const ScDocument& rDoc, SCTAB nActTab,
2491 const ScAddress::Details& rDetails ) const
2492{
2493 if ( Tab()+1 > rDoc.GetTableCount() )
2495
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
2511bool 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: */
static bool lcl_ScRange_External_TabSpan(ScRange &rRange, ScRefFlags &rFlags, ScAddress::ExternalInfo *pExtInfo, const OUString &rExternDocName, const OUString &rStartTabName, const OUString &rEndTabName, const ScDocument &rDoc)
Determines the number of sheets an external reference spans and sets rRange.aEnd.nTab accordingly.
Definition: address.cxx:215
void ScColToAlpha(OUStringBuffer &rBuf, SCCOL nCol)
append alpha representation of column to buffer
Definition: address.cxx:1884
static bool isValidSingleton(ScRefFlags nFlags, ScRefFlags nFlags2)
B:B or 2:2, but not B:2 or 2:B or B2:B or B:B2 or ...
Definition: address.cxx:968
static ScRefFlags lcl_ScRange_Parse_OOo(ScRange &rRange, const OUString &r, const ScDocument &rDoc, ScAddress::ExternalInfo *pExtInfo, const OUString *pErrRef)
Definition: address.cxx:1578
static bool lcl_XL_getExternalDoc(const sal_Unicode **ppErrRet, OUString &rExternDocName, const uno::Sequence< sheet::ExternalLinkInfo > *pExternalLinks)
Tries to obtain the external document index and replace by actual document name.
Definition: address.cxx:424
static void lcl_r1c1_append_r(T &rString, sal_Int32 nRow, bool bIsAbs, const ScAddress::Details &rDetails)
Definition: address.cxx:1920
static void lcl_ScRange_Format_XL_Header(OUStringBuffer &rString, const ScRange &rRange, ScRefFlags nFlags, const ScDocument &rDoc, const ScAddress::Details &rDetails)
Definition: address.cxx:2118
static bool lcl_isString(const sal_Unicode *p1, const OUString &rStr)
Definition: address.cxx:176
static ScRefFlags lcl_ScRange_Parse_XL_A1(ScRange &r, const sal_Unicode *p, const ScDocument &rDoc, bool bOnlyAcceptSingle, ScAddress::ExternalInfo *pExtInfo, const uno::Sequence< sheet::ExternalLinkInfo > *pExternalLinks, sal_Int32 *pSheetEndPos, const OUString *pErrRef)
Definition: address.cxx:975
static ScRefFlags lcl_ScRange_Parse_XL_R1C1(ScRange &r, const sal_Unicode *p, const ScDocument &rDoc, const ScAddress::Details &rDetails, bool bOnlyAcceptSingle, ScAddress::ExternalInfo *pExtInfo, sal_Int32 *pSheetEndPos)
Definition: address.cxx:752
bool ConvertSingleRef(const ScDocument &rDoc, const OUString &rRefString, SCTAB nDefTab, ScRefAddress &rRefAddress, const ScAddress::Details &rDetails, ScAddress::ExternalInfo *pExtInfo)
Definition: address.cxx:1489
static OUString getFileNameFromDoc(const ScDocument *pDoc)
Definition: address.cxx:1937
static const sal_Unicode * lcl_a1_get_col(const ScDocument &rDoc, const sal_Unicode *p, ScAddress *pAddr, ScRefFlags *nFlags, const OUString *pErrRef)
Definition: address.cxx:899
bool AlphaToCol(const ScDocument &rDoc, SCCOL &rCol, const OUString &rStr)
get column number of A..IV... string
Definition: address.cxx:2511
static const sal_Unicode * lcl_eatWhiteSpace(const sal_Unicode *p)
Definition: address.cxx:165
bool ConvertDoubleRef(const ScDocument &rDoc, const OUString &rRefString, SCTAB nDefTab, ScRefAddress &rStartRefAddress, ScRefAddress &rEndRefAddress, const ScAddress::Details &rDetails, ScAddress::ExternalInfo *pExtInfo)
Definition: address.cxx:1511
static sal_Int64 sal_Unicode_strtol(const sal_Unicode *p, const sal_Unicode **pEnd)
Definition: address.cxx:136
static void lcl_ScColToAlpha(T &rBuf, SCCOL nCol)
Definition: address.cxx:1858
static void lcl_Format(T &r, SCTAB nTab, SCROW nRow, SCCOL nCol, ScRefFlags nFlags, const ScDocument *pDoc, const ScAddress::Details &rDetails)
Definition: address.cxx:1973
static ScRefFlags lcl_ScAddress_Parse(const sal_Unicode *p, const ScDocument &rDoc, ScAddress &rAddr, const ScAddress::Details &rDetails, ScAddress::ExternalInfo *pExtInfo, const uno::Sequence< sheet::ExternalLinkInfo > *pExternalLinks, sal_Int32 *pSheetEndPos, const OUString *pErrRef)
Definition: address.cxx:1450
static bool lcl_ColAbsFlagDiffer(const ScRefFlags nFlags)
Definition: address.cxx:2161
static const sal_Unicode * lcl_a1_get_row(const ScDocument &rDoc, const sal_Unicode *p, ScAddress *pAddr, ScRefFlags *nFlags, const OUString *pErrRef)
Definition: address.cxx:935
static const sal_Unicode * lcl_r1c1_get_col(const ScSheetLimits &rSheetLimits, const sal_Unicode *p, const ScAddress::Details &rDetails, ScAddress *pAddr, ScRefFlags *nFlags)
Definition: address.cxx:658
static void lcl_a1_append_c(T &rString, int nCol, bool bIsAbs)
Definition: address.cxx:1889
static bool lcl_RowAbsFlagDiffer(const ScRefFlags nFlags)
Definition: address.cxx:2165
static ScRefFlags lcl_ScAddress_Parse_OOo(const sal_Unicode *p, const ScDocument &rDoc, ScAddress &rAddr, ScRefFlags &rRawRes, ScAddress::ExternalInfo *pExtInfo, ScRange *pRange, sal_Int32 *pSheetEndPos, const OUString *pErrRef)
Definition: address.cxx:1139
static void lcl_Split_DocTab(const ScDocument &rDoc, SCTAB nTab, const ScAddress::Details &rDetails, ScRefFlags nFlags, OUString &rTabName, OUString &rDocName)
Definition: address.cxx:2089
static const sal_Unicode * lcl_r1c1_get_row(const ScSheetLimits &rSheetLimits, const sal_Unicode *p, const ScAddress::Details &rDetails, ScAddress *pAddr, ScRefFlags *nFlags)
Definition: address.cxx:705
static void lcl_string_append(OUStringBuffer &rString, std::u16string_view sString)
Definition: address.cxx:1963
static void lcl_r1c1_append_c(T &rString, sal_Int32 nCol, bool bIsAbs, const ScAddress::Details &rDetails)
Definition: address.cxx:1903
static const sal_Unicode * lcl_XL_ParseSheetRef(const sal_Unicode *start, OUString &rExternTabName, bool bAllow3D, const sal_Unicode *pMsoxlQuoteStop, const OUString *pErrRef)
Returns NULL if the string should be a sheet name, but is invalid.
Definition: address.cxx:294
static void lcl_a1_append_r(T &rString, sal_Int32 nRow, bool bIsAbs)
Definition: address.cxx:1896
ScRefFlags
Definition: address.hxx:158
const SCTAB MAXTAB
Definition: address.hxx:70
void applyStartToEndFlags(ScRefFlags &target, const ScRefFlags source)
Definition: address.hxx:196
static bool isAsciiNumeric(std::u16string_view rStr)
SCTAB Tab() const
Definition: address.hxx:283
void Set(SCCOL nCol, SCROW nRow, SCTAB nTab)
Definition: address.hxx:403
void SetCol(SCCOL nColP)
Definition: address.hxx:291
SC_DLLPUBLIC void Format(OStringBuffer &r, ScRefFlags nFlags, const ScDocument *pDocument=nullptr, const Details &rDetails=detailsOOOa1) const
Definition: address.cxx:2074
SC_DLLPUBLIC bool Move(SCCOL nDeltaX, SCROW nDeltaY, SCTAB nDeltaZ, ScAddress &rErrorPos, const ScDocument &rDoc)
Definition: address.cxx:2282
bool IsValid() const
Definition: address.hxx:305
static SC_DLLPUBLIC const Details detailsOOOa1
Definition: address.hxx:241
SCROW nRow
Definition: address.hxx:214
SC_DLLPUBLIC ScRefFlags Parse(const OUString &, const ScDocument &, const Details &rDetails=detailsOOOa1, ExternalInfo *pExtInfo=nullptr, const css::uno::Sequence< css::sheet::ExternalLinkInfo > *pExternalLinks=nullptr, sal_Int32 *pSheetEndPos=nullptr, const OUString *pErrRef=nullptr)
Definition: address.cxx:1537
SCROW Row() const
Definition: address.hxx:274
void SetRow(SCROW nRowP)
Definition: address.hxx:287
void SetTab(SCTAB nTabP)
Definition: address.hxx:295
SCTAB nTab
Definition: address.hxx:216
@ INITIALIZE_INVALID
Definition: address.hxx:221
OUString GetColRowString() const
Create a human-readable string representation of the cell address.
Definition: address.cxx:2467
SCCOL Col() const
Definition: address.hxx:279
SCCOL nCol
Definition: address.hxx:215
static sal_Int32 GetDocTabPos(const OUString &rString)
Analyzes a string for a 'Doc'Tab construct, or 'Do''c'Tab etc...
Definition: compiler.cxx:1988
static void CheckTabQuotes(OUString &aTabName, const formula::FormulaGrammar::AddressConvention eConv=formula::FormulaGrammar::CONV_OOO)
all
Definition: compiler.cxx:1949
ScSheetLimits & GetSheetLimits() const
Definition: document.hxx:897
SC_DLLPUBLIC bool GetTable(const OUString &rName, SCTAB &rTab) const
Definition: document.cxx:263
SC_DLLPUBLIC SCCOL MaxCol() const
Definition: document.hxx:891
SC_DLLPUBLIC SCROW MaxRow() const
Definition: document.hxx:892
SC_DLLPUBLIC ScExternalRefManager * GetExternalRefManager() const
Definition: documen3.cxx:633
SfxObjectShell * GetDocumentShell() const
Definition: document.hxx:1081
bool ValidCol(SCCOL nCol) const
Definition: document.hxx:898
SC_DLLPUBLIC bool GetName(SCTAB nTab, OUString &rName) const
Definition: document.cxx:216
SC_DLLPUBLIC SCTAB GetTableCount() const
Definition: document.cxx:316
SCTAB getCachedTabSpan(sal_uInt16 nFileId, const OUString &rStartTabName, const OUString &rEndTabName) const
Get the span (distance+sign(distance)) of two sheets of a specified file.
sal_uInt16 getExternalFileId(const OUString &rFile)
const OUString * getExternalFileName(sal_uInt16 nFileId, bool bForceOriginal=false)
It returns a pointer to the name of the URI associated with a given external file ID.
void convertToAbsName(OUString &rFile) const
Takes a flat file name, and convert it to an absolute URL path.
bool isOwnDocument(std::u16string_view rFile) const
ScExternalRefCache::TokenRef getSingleRefToken(sal_uInt16 nFileId, const OUString &rTabName, const ScAddress &rCell, const ScAddress *pCurPos, SCTAB *pTab, ScExternalRefCache::CellFormat *pFmt=nullptr)
static const sal_Unicode * UnicodeStrChr(const sal_Unicode *pStr, sal_Unicode c)
strchr() functionality on unicode, as long as we need it for FormulaToken etc.
Definition: global.cxx:689
static SC_DLLPUBLIC sal_Int32 FindUnquoted(const OUString &rString, sal_Unicode cChar, sal_Int32 nStart=0)
Finds an unquoted instance of cChar in rString, starting at offset nStart.
Definition: global.cxx:747
static SC_DLLPUBLIC OUString GetAbsDocName(const OUString &rFileName, const SfxObjectShell *pShell)
Definition: global2.cxx:287
static sal_Unicode ToUpperAlpha(sal_Unicode c)
Definition: global.hxx:630
void ParseRows(const ScDocument &rDoc, const OUString &, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1)
Definition: address.cxx:1809
bool IsEndRowSticky(const ScDocument &rDoc) const
If maximum end row should not be adapted during reference update.
Definition: address.cxx:2420
bool IsEndColSticky(const ScDocument &rDoc) const
If maximum end column should not be adapted during reference update.
Definition: address.cxx:2414
ScRange Intersection(const ScRange &rOther) const
Definition: address.cxx:1547
bool Move(SCCOL aDeltaX, SCROW aDeltaY, SCTAB aDeltaZ, ScRange &rErrorRange, const ScDocument &rDoc)
Definition: address.cxx:2330
void IncEndRowSticky(const ScDocument &rDoc, SCROW nDelta)
Increment or decrement end row unless sticky or until it becomes sticky.
Definition: address.cxx:2447
OUString Format(const ScDocument &rDocument, ScRefFlags nFlags=ScRefFlags::ZERO, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1, bool bFullAddressNotation=false) const
Returns string with formatted cell range from aStart to aEnd, according to provided address conventio...
Definition: address.cxx:2170
ScAddress aEnd
Definition: address.hxx:498
void IncColIfNotLessThan(const ScDocument &rDoc, SCCOL nStartCol, SCCOL nOffset)
Definition: address.cxx:2374
void ExtendTo(const ScRange &rRange)
Definition: address.cxx:1562
void IncEndColSticky(const ScDocument &rDoc, SCCOL nDelta)
Increment or decrement end column unless sticky or until it becomes sticky.
Definition: address.cxx:2426
const sal_Unicode * Parse_XL_Header(const sal_Unicode *pString, const ScDocument &rDocument, OUString &rExternDocName, OUString &rStartTabName, OUString &rEndTabName, ScRefFlags &nFlags, bool bOnlyAcceptSingle, const css::uno::Sequence< css::sheet::ExternalLinkInfo > *pExternalLinks=nullptr, const OUString *pErrRef=nullptr)
Parse an Excel style reference up to and including the sheet name separator '!', including detection ...
Definition: address.cxx:473
ScRefFlags ParseAny(const OUString &, const ScDocument &, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1)
Definition: address.cxx:1733
void IncRowIfNotLessThan(const ScDocument &rDoc, SCROW nStartRow, SCROW nOffset)
Definition: address.cxx:2394
bool IsValid() const
Definition: address.hxx:544
ScRefFlags ParseCols(const ScDocument &rDoc, const OUString &, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1)
Definition: address.cxx:1750
bool MoveSticky(const ScDocument &rDoc, SCCOL aDeltaX, SCROW aDeltaY, SCTAB aDeltaZ, ScRange &rErrorRange)
Same as Move() but with sticky end col/row anchors.
Definition: address.cxx:2343
ScRefFlags Parse(const OUString &, const ScDocument &, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1, ScAddress::ExternalInfo *pExtInfo=nullptr, const css::uno::Sequence< css::sheet::ExternalLinkInfo > *pExternalLinks=nullptr, const OUString *pErrRef=nullptr)
Definition: address.cxx:1700
ScAddress aStart
Definition: address.hxx:497
OUString GetRefString(const ScDocument &rDocument, SCTAB nActTab, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1) const
Definition: address.cxx:2490
void Set(const ScAddress &rAdr, bool bNewRelCol, bool bNewRelRow, bool bNewRelTab)
Definition: address.hxx:914
css::uno::Reference< css::frame::XModel3 > GetModel() const
OUString GetTitle(sal_uInt16 nMaxLen=0) const
static const OUString & GetNativeSymbol(OpCode eOp)
#define SC_COMPILER_FILE_TAB_SEP
Definition: compiler.hxx:83
URL aURL
float u
OUString aName
void * p
sal_Int64 n
sal_uInt16 nPos
#define SAL_INFO(area, stream)
aStr
aBuf
int i
constexpr std::underlying_type_t< T > to_underlying(T e)
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
ocErrRef
Details(formula::FormulaGrammar::AddressConvention eConvP, SCROW nRowP, SCCOL nColP)
Definition: address.hxx:229
formula::FormulaGrammar::AddressConvention eConv
Definition: address.hxx:225
SCROW GetMaxRowCount() const
Definition: sheetlimits.hxx:66
SCCOL GetMaxColCount() const
Definition: sheetlimits.hxx:68
Reference< XModel > xModel
sal_uInt16 sal_Unicode
sal_Int16 SCTAB
Definition: types.hxx:22
sal_Int16 SCCOL
Definition: types.hxx:21
sal_Int32 SCROW
Definition: types.hxx:17