1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "address.hxx"
22 #include "compiler.hxx"
23 #include "document.hxx"
24 #include "externalrefmgr.hxx"
26 #include "globstr.hrc"
27 #include <sal/alloca.h>
28 #include <osl/diagnose.h>
30 #include <com/sun/star/frame/XModel.hpp>
31 #include <com/sun/star/beans/XPropertySet.hpp>
32 #include <com/sun/star/sheet/ExternalLinkInfo.hpp>
33 #include <com/sun/star/sheet/ExternalLinkType.hpp>
34 #include <comphelper/string.hxx>
35 #include <sfx2/objsh.hxx>
36 #include <tools/urlobj.hxx>
40 const ScAddress::Details
ScAddress::detailsOOOa1( formula::FormulaGrammar::CONV_OOO
, 0, 0 );
42 ScAddress::Details::Details ( const ScDocument
* pDoc
,
43 const ScAddress
& rAddr
) :
44 eConv( pDoc
->GetAddressConvention() ),
51 const sal_Unicode
* parseQuotedNameWithBuffer( const sal_Unicode
* pStart
, const sal_Unicode
* p
, OUString
& rName
)
53 // The current character must be on the 2nd quote.
55 // Push all the characters up to the current, but skip the very first
56 // character which is the opening quote.
57 OUStringBuffer
aBuf(OUString(pStart
+1, p
-pStart
-1));
59 ++p
; // Skip the 2nd quote.
60 sal_Unicode cPrev
= 0;
67 // double single-quote equals one single quote.
73 else if (cPrev
== '\'')
75 // We are past the closing quote. We're done!
76 rName
= aBuf
.makeStringAndClear();
88 * Parse from the opening single quote to the closing single quote. Inside
89 * the quotes, a single quote character is encoded by double single-quote
92 * @param p pointer to the first character to begin parsing.
93 * @param rName (reference) parsed name within the quotes. If the name is
94 * empty, either the parsing failed or it's an empty quote.
96 * @return pointer to the character immediately after the closing single
99 const sal_Unicode
* parseQuotedName( const sal_Unicode
* p
, OUString
& rName
)
104 const sal_Unicode
* pStart
= p
;
105 sal_Unicode cPrev
= 0;
112 // double single-quote equals one single quote.
113 return parseQuotedNameWithBuffer(pStart
, p
, rName
);
116 else if (cPrev
== '\'')
118 // We are past the closing quote. We're done! Skip the opening
119 // and closing quotes.
120 rName
= OUString(pStart
+1, p
- pStart
-2);
133 static long int sal_Unicode_strtol ( const sal_Unicode
* p
, const sal_Unicode
** pEnd
)
135 long int accum
= 0, prev
= 0;
146 while (rtl::isAsciiDigit( *p
))
148 accum
= accum
* 10 + *p
- '0';
159 return is_neg
? -accum
: accum
;
162 static const sal_Unicode
* lcl_eatWhiteSpace( const sal_Unicode
* p
)
172 /** Determines the number of sheets an external reference spans and sets
173 rRange.aEnd.nTab accordingly. If a sheet is not found, the corresponding
174 bits in rFlags are cleared. pExtInfo is filled if it wasn't already. If in
175 cached order rStartTabName comes after rEndTabName, pExtInfo->maTabName
176 is set to rEndTabName.
177 @returns <FALSE/> if pExtInfo is already filled and rExternDocName does not
178 result in the identical file ID. Else <TRUE/>.
180 static bool lcl_ScRange_External_TabSpan(
183 ScAddress::ExternalInfo
* pExtInfo
,
184 const OUString
& rExternDocName
,
185 const OUString
& rStartTabName
,
186 const OUString
& rEndTabName
,
189 if (rExternDocName
.isEmpty())
190 return !pExtInfo
|| !pExtInfo
->mbExternal
;
192 ScExternalRefManager
* pRefMgr
= pDoc
->GetExternalRefManager();
193 if (pRefMgr
->isOwnDocument( rExternDocName
))
195 // This is an internal document. Get the sheet positions from the
196 // ScDocument instance.
197 if (!rStartTabName
.isEmpty())
200 if (pDoc
->GetTable(rStartTabName
, nTab
))
201 rRange
.aStart
.SetTab(nTab
);
204 if (!rEndTabName
.isEmpty())
207 if (pDoc
->GetTable(rEndTabName
, nTab
))
208 rRange
.aEnd
.SetTab(nTab
);
210 return !pExtInfo
|| !pExtInfo
->mbExternal
;
213 sal_uInt16 nFileId
= pRefMgr
->getExternalFileId( rExternDocName
);
217 if (pExtInfo
->mbExternal
)
219 if (pExtInfo
->mnFileId
!= nFileId
)
224 pExtInfo
->mbExternal
= true;
225 pExtInfo
->maTabName
= rStartTabName
;
226 pExtInfo
->mnFileId
= nFileId
;
230 if (rEndTabName
.isEmpty() || rStartTabName
== rEndTabName
)
232 rRange
.aEnd
.SetTab( rRange
.aStart
.Tab());
236 SCsTAB nSpan
= pRefMgr
->getCachedTabSpan( nFileId
, rStartTabName
, rEndTabName
);
238 rFlags
&= ~(SCA_VALID_TAB
| SCA_VALID_TAB2
);
240 rFlags
&= ~SCA_VALID_TAB2
;
242 rRange
.aEnd
.SetTab( rRange
.aStart
.Tab() + nSpan
- 1);
245 rRange
.aEnd
.SetTab( rRange
.aStart
.Tab() - nSpan
- 1);
247 pExtInfo
->maTabName
= rEndTabName
;
252 /** Returns NULL if the string should be a sheet name, but is invalid.
253 Returns a pointer to the first character after the sheet name, if there was
254 any, else pointer to start.
255 @param pMsoxlQuoteStop
256 Starting _within_ a quoted name, but still may be 3D; quoted name stops
259 static const sal_Unicode
* lcl_XL_ParseSheetRef( const sal_Unicode
* start
,
260 OUString
& rExternTabName
,
262 const sal_Unicode
* pMsoxlQuoteStop
)
265 const sal_Unicode
*p
= start
;
267 // XL only seems to use single quotes for sheet names.
270 const sal_Unicode
* pCurrentStart
= p
;
271 while (p
< pMsoxlQuoteStop
)
275 // We pre-analyzed the quoting, no checks needed here.
278 aTabName
+= OUString( pCurrentStart
,
279 sal::static_int_cast
<sal_Int32
>( p
- pCurrentStart
));
290 if (pCurrentStart
< p
)
291 aTabName
+= OUString( pCurrentStart
, sal::static_int_cast
<sal_Int32
>( p
- pCurrentStart
));
292 if (aTabName
.isEmpty())
294 if (p
== pMsoxlQuoteStop
)
295 ++p
; // position on ! of ...'!...
296 if( *p
!= '!' && ( !bAllow3D
|| *p
!= ':' ) )
297 return (!bAllow3D
&& *p
== ':') ? p
: start
;
301 p
= parseQuotedName(p
, aTabName
);
302 if (aTabName
.isEmpty())
307 bool only_digits
= true;
314 * Some names starting with digits are actually valid, but
315 * unparse quoted. Things are quite tricky: most sheet names
316 * starting with a digit are ok, but not those starting with
317 * "[0-9]*\." or "[0-9]+[eE]".
326 const sal_Unicode uc
= *p
;
327 if( rtl::isAsciiAlpha( uc
) || uc
== '_' )
329 if( only_digits
&& p
!= start
&&
330 (uc
== 'e' || uc
== 'E' ) )
338 else if( rtl::isAsciiDigit( uc
))
344 if( only_digits
) // Valid, except after only digits.
353 // non ASCII character is allowed.
360 if( *p
!= '!' && ( !bAllow3D
|| *p
!= ':' ) )
361 return (!bAllow3D
&& *p
== ':') ? p
: start
;
363 aTabName
+= OUString( start
, sal::static_int_cast
<sal_Int32
>( p
- start
) );
366 rExternTabName
= aTabName
;
370 /** Tries to obtain the external document index and replace by actual document
374 Contains the default pointer the caller would return if this method
375 returns FALSE, may be replaced by NULL for type or data errors.
377 @returns FALSE only if the input name is numeric and not within the index
378 sequence, or the link type cannot be determined or data mismatch. Returns
379 TRUE in all other cases, also when there is no index sequence or the input
382 static bool lcl_XL_getExternalDoc( const sal_Unicode
** ppErrRet
, OUString
& rExternDocName
,
383 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
)
385 // 1-based, sequence starts with an empty element.
386 if (pExternalLinks
&& pExternalLinks
->hasElements())
388 // A numeric "document name" is an index into the sequence.
389 if (CharClass::isAsciiNumeric( rExternDocName
))
391 sal_Int32 i
= rExternDocName
.toInt32();
392 if (i
< 0 || i
>= pExternalLinks
->getLength())
393 return false; // with default *ppErrRet
394 const sheet::ExternalLinkInfo
& rInfo
= (*pExternalLinks
)[i
];
397 case sheet::ExternalLinkType::DOCUMENT
:
400 if (!(rInfo
.Data
>>= aStr
))
404 "Data type mismatch for ExternalLinkInfo "
409 rExternDocName
= aStr
;
412 case sheet::ExternalLinkType::SELF
:
414 case sheet::ExternalLinkType::SPECIAL
:
415 // silently return nothing (do not assert), caller has to handle this
421 "unhandled ExternalLinkType " << rInfo
.Type
422 << " for index " << i
);
431 const sal_Unicode
* ScRange::Parse_XL_Header(
432 const sal_Unicode
* p
,
433 const ScDocument
* pDoc
,
434 OUString
& rExternDocName
,
435 OUString
& rStartTabName
,
436 OUString
& rEndTabName
,
438 bool bOnlyAcceptSingle
,
439 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
)
441 const sal_Unicode
* startTabs
, *start
= p
;
442 sal_uInt16 nSaveFlags
= nFlags
;
444 // Is this an external reference ?
445 rStartTabName
.clear();
447 rExternDocName
.clear();
448 const sal_Unicode
* pMsoxlQuoteStop
= NULL
;
452 // Only single quotes are correct, and a double single quote escapes a
453 // single quote text inside the quoted text.
456 p
= parseQuotedName(p
, rExternDocName
);
457 if (!*p
|| *p
!= ']' || rExternDocName
.isEmpty())
459 rExternDocName
.clear();
465 // non-quoted file name.
466 p
= ScGlobal::UnicodeStrChr( start
+1, ']' );
469 rExternDocName
+= OUString( start
+1, sal::static_int_cast
<sal_Int32
>( p
-(start
+1) ) );
473 const sal_Unicode
* pErrRet
= start
;
474 if (!lcl_XL_getExternalDoc( &pErrRet
, rExternDocName
, pExternalLinks
))
477 rExternDocName
= ScGlobal::GetAbsDocName(rExternDocName
, pDoc
->GetDocumentShell());
481 // Sickness in Excel's ODF msoxl namespace:
482 // 'E:\[EXTDATA8.XLS]Sheet1'!$A$7 or
483 // 'E:\[EXTDATA12B.XLSB]Sheet1:Sheet3'!$A$11
484 // But, 'Sheet1'!B3 would also be a valid!
485 // Excel does not allow [ and ] characters in sheet names though.
486 // But, more sickness comes with MOOXML as there may be
487 // '[1]Sheet 4'!$A$1 where [1] is the external doc's index.
488 p
= parseQuotedName(p
, rExternDocName
);
489 if (!*p
|| *p
!= '!')
491 rExternDocName
.clear();
494 if (!rExternDocName
.isEmpty())
496 sal_Int32 nOpen
= rExternDocName
.indexOf( '[');
498 rExternDocName
.clear();
501 sal_Int32 nClose
= rExternDocName
.indexOf( ']', nOpen
+1);
503 rExternDocName
.clear();
506 rExternDocName
= rExternDocName
.copy(0, nClose
);
507 rExternDocName
= rExternDocName
.replaceAt( nOpen
, 1, "");
508 pMsoxlQuoteStop
= p
- 1; // the ' quote char
509 // There may be embedded escaped quotes, just matching the
510 // doc name's length may not work.
511 for (p
= start
; *p
!= '['; ++p
)
513 for ( ; *p
!= ']'; ++p
)
517 // Handle '[1]Sheet 4'!$A$1
520 const sal_Unicode
* pErrRet
= start
;
521 if (!lcl_XL_getExternalDoc( &pErrRet
, rExternDocName
, pExternalLinks
))
527 if (rExternDocName
.isEmpty())
532 p
= lcl_XL_ParseSheetRef( p
, rStartTabName
, !bOnlyAcceptSingle
, pMsoxlQuoteStop
);
534 return start
; // invalid tab
535 if (bOnlyAcceptSingle
&& *p
== ':')
539 nFlags
|= SCA_VALID_TAB
| SCA_TAB_3D
| SCA_TAB_ABSOLUTE
;
540 if( *p
== ':' ) // 3d ref
542 p
= lcl_XL_ParseSheetRef( p
+1, rEndTabName
, false, pMsoxlQuoteStop
);
546 return start
; // invalid tab
548 nFlags
|= SCA_VALID_TAB2
| SCA_TAB2_3D
| SCA_TAB2_ABSOLUTE
;
552 // If only one sheet is given, the full reference is still valid,
553 // only the second 3D flag is not set.
554 nFlags
|= SCA_VALID_TAB2
| SCA_TAB2_ABSOLUTE
;
555 aEnd
.SetTab( aStart
.Tab() );
561 return start
; // syntax error
564 p
= lcl_eatWhiteSpace( p
);
568 nFlags
|= SCA_VALID_TAB
| SCA_VALID_TAB2
;
569 // Use the current tab, it needs to be passed in. : aEnd.SetTab( .. );
572 if (!rExternDocName
.isEmpty())
574 ScExternalRefManager
* pRefMgr
= pDoc
->GetExternalRefManager();
575 pRefMgr
->convertToAbsName(rExternDocName
);
579 // Internal reference.
580 if (rStartTabName
.isEmpty())
587 if (!pDoc
->GetTable(rStartTabName
, nTab
))
589 // invalid table name.
590 nFlags
&= ~SCA_VALID_TAB
;
597 if (!rEndTabName
.isEmpty())
599 if (!pDoc
->GetTable(rEndTabName
, nTab
))
601 // invalid table name.
602 nFlags
&= ~SCA_VALID_TAB2
;
612 static const sal_Unicode
* lcl_r1c1_get_col( const sal_Unicode
* p
,
613 const ScAddress::Details
& rDetails
,
614 ScAddress
* pAddr
, sal_uInt16
* nFlags
)
616 const sal_Unicode
*pEnd
;
624 if( ( isRelative
= (*p
== '[') ) )
626 n
= sal_Unicode_strtol( p
, &pEnd
);
630 if( p
== pEnd
) // C is a relative ref with offset 0
636 else if( isRelative
)
645 *nFlags
|= SCA_COL_ABSOLUTE
;
649 if( n
< 0 || n
>= MAXCOLCOUNT
)
651 pAddr
->SetCol( static_cast<SCCOL
>( n
) );
652 *nFlags
|= SCA_VALID_COL
;
657 static inline const sal_Unicode
* lcl_r1c1_get_row(
658 const sal_Unicode
* p
,
659 const ScAddress::Details
& rDetails
,
660 ScAddress
* pAddr
, sal_uInt16
* nFlags
)
662 const sal_Unicode
*pEnd
;
670 if( ( isRelative
= (*p
== '[') ) )
672 n
= sal_Unicode_strtol( p
, &pEnd
);
676 if( p
== pEnd
) // R is a relative ref with offset 0
682 else if( isRelative
)
691 *nFlags
|= SCA_ROW_ABSOLUTE
;
695 if( n
< 0 || n
>= MAXROWCOUNT
)
697 pAddr
->SetRow( static_cast<SCROW
>( n
) );
698 *nFlags
|= SCA_VALID_ROW
;
703 static sal_uInt16
lcl_ScRange_Parse_XL_R1C1( ScRange
& r
,
704 const sal_Unicode
* p
,
706 const ScAddress::Details
& rDetails
,
707 bool bOnlyAcceptSingle
,
708 ScAddress::ExternalInfo
* pExtInfo
)
710 const sal_Unicode
* pTmp
= NULL
;
711 OUString aExternDocName
, aStartTabName
, aEndTabName
;
712 sal_uInt16 nFlags
= SCA_VALID
| SCA_VALID_TAB
;
713 // Keep in mind that nFlags2 gets left-shifted by 4 bits before being merged.
714 sal_uInt16 nFlags2
= SCA_VALID_TAB
;
716 p
= r
.Parse_XL_Header( p
, pDoc
, aExternDocName
, aStartTabName
,
717 aEndTabName
, nFlags
, bOnlyAcceptSingle
, NULL
);
719 if (!aExternDocName
.isEmpty())
720 lcl_ScRange_External_TabSpan( r
, nFlags
, pExtInfo
, aExternDocName
,
721 aStartTabName
, aEndTabName
, pDoc
);
726 if( *p
== 'R' || *p
== 'r' )
728 if( NULL
== (p
= lcl_r1c1_get_row( p
, rDetails
, &r
.aStart
, &nFlags
)) )
731 if( *p
!= 'C' && *p
!= 'c' ) // full row R#
733 if( p
[0] != ':' || (p
[1] != 'R' && p
[1] != 'r' ) ||
734 NULL
== (pTmp
= lcl_r1c1_get_row( p
+1, rDetails
, &r
.aEnd
, &nFlags2
)))
736 // Only the initial row number is given, or the second row
737 // number is invalid. Fallback to just the initial R
738 nFlags
|= (nFlags
<< 4);
739 r
.aEnd
.SetRow( r
.aStart
.Row() );
743 // Full row range successfully parsed.
744 nFlags
|= (nFlags2
<< 4);
750 // any trailing invalid character must invalidate the whole address.
751 nFlags
&= ~(SCA_VALID
| SCA_VALID_COL
| SCA_VALID_ROW
| SCA_VALID_TAB
|
752 SCA_VALID_COL2
| SCA_VALID_ROW2
| SCA_VALID_TAB2
);
757 SCA_VALID_COL
| SCA_VALID_COL2
|
758 SCA_COL_ABSOLUTE
| SCA_COL2_ABSOLUTE
;
759 r
.aStart
.SetCol( 0 );
760 r
.aEnd
.SetCol( MAXCOL
);
762 return bOnlyAcceptSingle
? 0 : nFlags
;
764 else if( NULL
== (p
= lcl_r1c1_get_col( p
, rDetails
, &r
.aStart
, &nFlags
)))
770 (p
[1] != 'R' && p
[1] != 'r') ||
771 NULL
== (pTmp
= lcl_r1c1_get_row( p
+1, rDetails
, &r
.aEnd
, &nFlags2
)) ||
772 (*pTmp
!= 'C' && *pTmp
!= 'c') ||
773 NULL
== (pTmp
= lcl_r1c1_get_col( pTmp
, rDetails
, &r
.aEnd
, &nFlags2
)))
775 // single cell reference
779 // any trailing invalid character must invalidate the whole address.
780 nFlags
&= ~(SCA_VALID
| SCA_VALID_COL
| SCA_VALID_ROW
| SCA_VALID_TAB
);
784 return bOnlyAcceptSingle
? nFlags
: 0;
792 // any trailing invalid character must invalidate the whole range.
793 nFlags
&= ~(SCA_VALID
| SCA_VALID_COL
| SCA_VALID_ROW
| SCA_VALID_TAB
|
794 SCA_VALID_COL2
| SCA_VALID_ROW2
| SCA_VALID_TAB2
);
798 nFlags
|= (nFlags2
<< 4);
799 return bOnlyAcceptSingle
? 0 : nFlags
;
801 else if( *p
== 'C' || *p
== 'c' ) // full col C#
803 if( NULL
== (p
= lcl_r1c1_get_col( p
, rDetails
, &r
.aStart
, &nFlags
)))
806 if( p
[0] != ':' || (p
[1] != 'C' && p
[1] != 'c') ||
807 NULL
== (pTmp
= lcl_r1c1_get_col( p
+1, rDetails
, &r
.aEnd
, &nFlags2
)))
808 { // Fallback to just the initial C
809 nFlags
|= (nFlags
<< 4);
810 r
.aEnd
.SetCol( r
.aStart
.Col() );
814 nFlags
|= (nFlags2
<< 4);
820 // any trailing invalid character must invalidate the whole address.
821 nFlags
&= ~(SCA_VALID
| SCA_VALID_COL
| SCA_VALID_ROW
| SCA_VALID_TAB
|
822 SCA_VALID_COL2
| SCA_VALID_ROW2
| SCA_VALID_TAB2
);
827 SCA_VALID_ROW
| SCA_VALID_ROW2
|
828 SCA_ROW_ABSOLUTE
| SCA_ROW2_ABSOLUTE
;
829 r
.aStart
.SetRow( 0 );
830 r
.aEnd
.SetRow( MAXROW
);
832 return bOnlyAcceptSingle
? 0 : nFlags
;
838 static inline const sal_Unicode
* lcl_a1_get_col( const sal_Unicode
* p
,
845 *nFlags
|= SCA_COL_ABSOLUTE
, p
++;
847 if( !rtl::isAsciiAlpha( *p
) )
850 nCol
= sal::static_int_cast
<SCCOL
>( toupper( char(*p
++) ) - 'A' );
851 while (nCol
<= MAXCOL
&& rtl::isAsciiAlpha(*p
))
852 nCol
= sal::static_int_cast
<SCCOL
>( ((nCol
+ 1) * 26) + toupper( char(*p
++) ) - 'A' );
853 if( nCol
> MAXCOL
|| rtl::isAsciiAlpha( *p
) )
856 *nFlags
|= SCA_VALID_COL
;
857 pAddr
->SetCol( nCol
);
862 static inline const sal_Unicode
* lcl_a1_get_row( const sal_Unicode
* p
,
866 const sal_Unicode
*pEnd
;
870 *nFlags
|= SCA_ROW_ABSOLUTE
, p
++;
872 n
= sal_Unicode_strtol( p
, &pEnd
) - 1;
873 if( NULL
== pEnd
|| p
== pEnd
|| n
< 0 || n
> MAXROW
)
876 *nFlags
|= SCA_VALID_ROW
;
877 pAddr
->SetRow( static_cast<SCROW
>(n
) );
882 /// B:B or 2:2, but not B:2 or 2:B or B2:B or B:B2 or ...
883 static bool isValidSingleton( sal_uInt16 nFlags
, sal_uInt16 nFlags2
)
885 bool bCols
= (nFlags
& SCA_VALID_COL
) && ((nFlags
& SCA_VALID_COL2
) || (nFlags2
& SCA_VALID_COL
));
886 bool bRows
= (nFlags
& SCA_VALID_ROW
) && ((nFlags
& SCA_VALID_ROW2
) || (nFlags2
& SCA_VALID_ROW
));
887 return (bCols
&& !bRows
) || (!bCols
&& bRows
);
890 static sal_uInt16
lcl_ScRange_Parse_XL_A1( ScRange
& r
,
891 const sal_Unicode
* p
,
893 bool bOnlyAcceptSingle
,
894 ScAddress::ExternalInfo
* pExtInfo
,
895 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
)
897 const sal_Unicode
* tmp1
, *tmp2
;
898 OUString aExternDocName
, aStartTabName
, aEndTabName
; // for external link table
899 sal_uInt16 nFlags
= SCA_VALID
| SCA_VALID_TAB
, nFlags2
= SCA_VALID_TAB
;
901 p
= r
.Parse_XL_Header( p
, pDoc
, aExternDocName
, aStartTabName
,
902 aEndTabName
, nFlags
, bOnlyAcceptSingle
, pExternalLinks
);
904 if (!aExternDocName
.isEmpty())
905 lcl_ScRange_External_TabSpan( r
, nFlags
, pExtInfo
, aExternDocName
,
906 aStartTabName
, aEndTabName
, pDoc
);
911 tmp1
= lcl_a1_get_col( p
, &r
.aStart
, &nFlags
);
912 if( tmp1
== NULL
) // Is it a row only reference 3:5
914 if( bOnlyAcceptSingle
) // by definition full row refs are ranges
917 tmp1
= lcl_a1_get_row( p
, &r
.aStart
, &nFlags
);
919 tmp1
= lcl_eatWhiteSpace( tmp1
);
920 if( !tmp1
|| *tmp1
++ != ':' ) // Even a singleton requires ':' (eg 2:2)
923 tmp1
= lcl_eatWhiteSpace( tmp1
);
924 tmp2
= lcl_a1_get_row( tmp1
, &r
.aEnd
, &nFlags2
);
925 if( !tmp2
|| *tmp2
!= 0 ) // Must have fully parsed a singleton.
928 r
.aStart
.SetCol( 0 ); r
.aEnd
.SetCol( MAXCOL
);
930 SCA_VALID_COL
| SCA_VALID_COL2
|
931 SCA_COL_ABSOLUTE
| SCA_COL2_ABSOLUTE
;
932 nFlags
|= (nFlags2
<< 4);
936 tmp2
= lcl_a1_get_row( tmp1
, &r
.aStart
, &nFlags
);
937 if( tmp2
== NULL
) // check for col only reference F:H
939 if( bOnlyAcceptSingle
) // by definition full col refs are ranges
942 tmp1
= lcl_eatWhiteSpace( tmp1
);
943 if( *tmp1
++ != ':' ) // Even a singleton requires ':' (eg F:F)
946 tmp1
= lcl_eatWhiteSpace( tmp1
);
947 tmp2
= lcl_a1_get_col( tmp1
, &r
.aEnd
, &nFlags2
);
948 if( !tmp2
|| *tmp2
!= 0 ) // Must have fully parsed a singleton.
951 r
.aStart
.SetRow( 0 ); r
.aEnd
.SetRow( MAXROW
);
953 SCA_VALID_ROW
| SCA_VALID_ROW2
|
954 SCA_ROW_ABSOLUTE
| SCA_ROW2_ABSOLUTE
;
955 nFlags
|= (nFlags2
<< 4);
959 // prepare as if it's a singleton, in case we want to fall back */
960 r
.aEnd
.SetCol( r
.aStart
.Col() );
961 r
.aEnd
.SetRow( r
.aStart
.Row() ); // don't overwrite sheet number as parsed in Parse_XL_Header()
963 if ( bOnlyAcceptSingle
)
969 // any trailing invalid character must invalidate the address.
970 nFlags
&= ~(SCA_VALID
| SCA_VALID_COL
| SCA_VALID_ROW
| SCA_VALID_TAB
);
975 tmp2
= lcl_eatWhiteSpace( tmp2
);
978 // Sheet1:Sheet2!C4 is a valid range, without a second sheet it is
979 // not. Any trailing invalid character invalidates the range.
980 if (*tmp2
== 0 && (nFlags
& SCA_TAB2_3D
))
982 if (nFlags
& SCA_COL_ABSOLUTE
)
983 nFlags
|= SCA_COL2_ABSOLUTE
;
984 if (nFlags
& SCA_ROW_ABSOLUTE
)
985 nFlags
|= SCA_ROW2_ABSOLUTE
;
988 nFlags
&= ~(SCA_VALID
|
989 SCA_VALID_COL
| SCA_VALID_ROW
| SCA_VALID_TAB
|
990 SCA_VALID_COL2
| SCA_VALID_ROW2
| SCA_VALID_TAB2
);
995 p
= lcl_eatWhiteSpace( p
+1 ); // after ':'
996 tmp1
= lcl_a1_get_col( p
, &r
.aEnd
, &nFlags2
);
997 if( !tmp1
&& aEndTabName
.isEmpty() ) // Probably the aEndTabName was specified after the first range
999 p
= lcl_XL_ParseSheetRef( p
, aEndTabName
, false, NULL
);
1003 if( !aEndTabName
.isEmpty() && pDoc
->GetTable( aEndTabName
, nTab
) )
1005 r
.aEnd
.SetTab( nTab
);
1006 nFlags
|= SCA_VALID_TAB2
| SCA_TAB2_3D
| SCA_TAB2_ABSOLUTE
;
1008 if (*p
== '!' || *p
== ':')
1009 p
= lcl_eatWhiteSpace( p
+1 );
1010 tmp1
= lcl_a1_get_col( p
, &r
.aEnd
, &nFlags2
);
1013 if( !tmp1
) // strange, but maybe valid singleton
1014 return isValidSingleton( nFlags
, nFlags2
) ? nFlags
: (nFlags
& ~SCA_VALID
);
1016 tmp2
= lcl_a1_get_row( tmp1
, &r
.aEnd
, &nFlags2
);
1017 if( !tmp2
) // strange, but maybe valid singleton
1018 return isValidSingleton( nFlags
, nFlags2
) ? nFlags
: (nFlags
& ~SCA_VALID
);
1022 // any trailing invalid character must invalidate the range.
1023 nFlags
&= ~(SCA_VALID
| SCA_VALID_COL
| SCA_VALID_ROW
| SCA_VALID_TAB
|
1024 SCA_VALID_COL2
| SCA_VALID_ROW2
| SCA_VALID_TAB2
);
1028 nFlags
|= (nFlags2
<< 4);
1033 @param p pointer to null-terminated sal_Unicode string
1034 @param rRawRes returns SCA_... flags without the final check for full
1035 validity that is applied to the return value, with which
1036 two addresses that form a column or row singleton range,
1037 e.g. A:A or 1:1, can be detected. Used in
1038 lcl_ScRange_Parse_OOo().
1039 @param pRange pointer to range where rAddr effectively is *pRange->aEnd,
1040 used in conjunction with pExtInfo to determine the tab span
1043 static sal_uInt16
lcl_ScAddress_Parse_OOo( const sal_Unicode
* p
, ScDocument
* pDoc
, ScAddress
& rAddr
,
1044 sal_uInt16
& rRawRes
,
1045 ScAddress::ExternalInfo
* pExtInfo
= NULL
, ScRange
* pRange
= NULL
)
1047 sal_uInt16 nRes
= 0;
1049 OUString aDocName
; // the pure Document Name
1051 bool bExtDoc
= false;
1052 bool bExtDocInherited
= false;
1053 const ScAddress
aCurPos(rAddr
);
1055 // Lets see if this is a reference to something in an external file. A
1056 // document name is always quoted and has a trailing #.
1059 const sal_Unicode
* pStart
= p
;
1061 p
= parseQuotedName(p
, aTmp
);
1063 if (*p
++ == SC_COMPILER_FILE_TAB_SEP
)
1066 // This is not a document name. Perhaps a quoted relative table
1070 else if (pExtInfo
&& pExtInfo
->mbExternal
)
1072 // This is an external reference.
1073 bExtDoc
= bExtDocInherited
= true;
1079 sal_uInt16 nBits
= SCA_VALID_TAB
;
1080 const sal_Unicode
* q
;
1081 if ( ScGlobal::FindUnquoted( p
, '.') )
1085 nRes
|= SCA_TAB_ABSOLUTE
;
1087 nRes
|= SCA_TAB_ABSOLUTE
, p
++;
1091 // Tokens that start at ' can have anything in them until a final
1092 // ' but '' marks an escaped '. We've earlier guaranteed that a
1093 // string containing '' will be surrounded by '.
1094 p
= parseQuotedName(p
, aTab
);
1098 OUStringBuffer aTabAcc
;
1111 aTab
= aTabAcc
.makeStringAndClear();
1116 if (!bExtDoc
&& (!pDoc
|| !pDoc
->GetTable( aTab
, nTab
)))
1118 // Specified table name is not found in this document. Assume this is an external document.
1120 sal_Int32 n
= aDocName
.lastIndexOf('.');
1123 // Extension found. Strip it.
1124 aTab
= aTab
.replaceAt(n
, 1, "");
1128 // No extension found. This is probably not an external document.
1134 if (bExtDoc
&& !bExtDocInherited
)
1135 return nRes
; // After a document a sheet must follow.
1143 nBits
= SCA_VALID_COL
;
1145 nBits
|= SCA_COL_ABSOLUTE
, p
++;
1147 if (rtl::isAsciiAlpha( *p
))
1149 nCol
= sal::static_int_cast
<SCCOL
>( toupper( char(*p
++) ) - 'A' );
1150 while (nCol
< MAXCOL
&& rtl::isAsciiAlpha(*p
))
1151 nCol
= sal::static_int_cast
<SCCOL
>( ((nCol
+ 1) * 26) + toupper( char(*p
++) ) - 'A' );
1156 if (nCol
> MAXCOL
|| (*p
&& *p
!= '$' && !rtl::isAsciiDigit( *p
)))
1166 nBits
= SCA_VALID_ROW
;
1168 nBits
|= SCA_ROW_ABSOLUTE
, p
++;
1169 if( !rtl::isAsciiDigit( *p
) )
1176 long n
= rtl_ustr_toInt32( p
, 10 ) - 1;
1177 while (rtl::isAsciiDigit( *p
))
1179 if( n
< 0 || n
> MAXROW
)
1181 nRow
= static_cast<SCROW
>(n
);
1188 rAddr
.Set( nCol
, nRow
, nTab
);
1196 ScExternalRefManager
* pRefMgr
= pDoc
->GetExternalRefManager();
1198 // Need document name if inherited.
1199 if (bExtDocInherited
)
1201 // The FileId was created using the original file name, so
1202 // obtain that. Otherwise lcl_ScRange_External_TabSpan() would
1203 // retrieve a FileId for the real name and bail out if that
1204 // differed from pExtInfo->mnFileId, as is the case when
1205 // loading documents that refer external files relative to the
1206 // current own document but were saved from a different path
1208 const OUString
* pFileName
= pRefMgr
->getExternalFileName( pExtInfo
->mnFileId
, true);
1210 aDocName
= *pFileName
;
1214 pRefMgr
->convertToAbsName(aDocName
);
1216 if ((!pExtInfo
|| !pExtInfo
->mbExternal
) && pRefMgr
->isOwnDocument(aDocName
))
1218 if (!pDoc
->GetTable( aTab
, nTab
))
1222 rAddr
.SetTab( nTab
);
1223 nRes
|= SCA_VALID_TAB
;
1232 if (!pExtInfo
->mbExternal
)
1234 sal_uInt16 nFileId
= pRefMgr
->getExternalFileId(aDocName
);
1236 pExtInfo
->mbExternal
= true;
1237 pExtInfo
->maTabName
= aTab
;
1238 pExtInfo
->mnFileId
= nFileId
;
1240 if (pRefMgr
->getSingleRefToken(nFileId
, aTab
,
1241 ScAddress(nCol
, nRow
, 0), NULL
,
1244 rAddr
.SetTab( nTab
);
1245 nRes
|= SCA_VALID_TAB
;
1252 // This is a call for the second part of the reference,
1253 // we must have the range to adapt tab span.
1258 sal_uInt16 nFlags
= nRes
| SCA_VALID_TAB2
;
1259 if (!lcl_ScRange_External_TabSpan( *pRange
, nFlags
,
1261 pExtInfo
->maTabName
, aTab
, pDoc
))
1262 nRes
&= ~SCA_VALID_TAB
;
1265 if (nFlags
& SCA_VALID_TAB2
)
1267 rAddr
.SetTab( pRange
->aEnd
.Tab());
1268 nRes
|= SCA_VALID_TAB
;
1271 nRes
&= ~SCA_VALID_TAB
;
1282 if ( !(nRes
& SCA_VALID_ROW
) && (nRes
& SCA_VALID_COL
)
1283 && !( (nRes
& SCA_TAB_3D
) && (nRes
& SCA_VALID_TAB
)) )
1284 { // no Row, no Tab, but Col => DM (...), B (...) et al
1289 sal_uInt16 nMask
= nRes
& ( SCA_VALID_ROW
| SCA_VALID_COL
| SCA_VALID_TAB
);
1290 if( nMask
== ( SCA_VALID_ROW
| SCA_VALID_COL
| SCA_VALID_TAB
) )
1298 static sal_uInt16
lcl_ScAddress_Parse ( const sal_Unicode
* p
, ScDocument
* pDoc
, ScAddress
& rAddr
,
1299 const ScAddress::Details
& rDetails
,
1300 ScAddress::ExternalInfo
* pExtInfo
= NULL
,
1301 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
= NULL
)
1306 switch (rDetails
.eConv
)
1308 case formula::FormulaGrammar::CONV_XL_A1
:
1309 case formula::FormulaGrammar::CONV_XL_OOX
:
1311 ScRange rRange
= rAddr
;
1312 sal_uInt16 nFlags
= lcl_ScRange_Parse_XL_A1(
1313 rRange
, p
, pDoc
, true, pExtInfo
,
1314 (rDetails
.eConv
== formula::FormulaGrammar::CONV_XL_OOX
? pExternalLinks
: NULL
) );
1315 rAddr
= rRange
.aStart
;
1318 case formula::FormulaGrammar::CONV_XL_R1C1
:
1320 ScRange rRange
= rAddr
;
1321 sal_uInt16 nFlags
= lcl_ScRange_Parse_XL_R1C1( rRange
, p
, pDoc
, rDetails
, true, pExtInfo
);
1322 rAddr
= rRange
.aStart
;
1326 case formula::FormulaGrammar::CONV_OOO
:
1328 sal_uInt16 nRawRes
= 0;
1329 return lcl_ScAddress_Parse_OOo( p
, pDoc
, rAddr
, nRawRes
, pExtInfo
, NULL
);
1334 bool ConvertSingleRef( ScDocument
* pDoc
, const OUString
& rRefString
,
1335 SCTAB nDefTab
, ScRefAddress
& rRefAddress
,
1336 const ScAddress::Details
& rDetails
,
1337 ScAddress::ExternalInfo
* pExtInfo
/* = NULL */ )
1340 if (pExtInfo
|| (ScGlobal::FindUnquoted( rRefString
, SC_COMPILER_FILE_TAB_SEP
) == -1))
1342 ScAddress
aAddr( 0, 0, nDefTab
);
1343 sal_uInt16 nRes
= aAddr
.Parse( rRefString
, pDoc
, rDetails
, pExtInfo
);
1344 if ( nRes
& SCA_VALID
)
1346 rRefAddress
.Set( aAddr
,
1347 ((nRes
& SCA_COL_ABSOLUTE
) == 0),
1348 ((nRes
& SCA_ROW_ABSOLUTE
) == 0),
1349 ((nRes
& SCA_TAB_ABSOLUTE
) == 0));
1356 bool ConvertDoubleRef( ScDocument
* pDoc
, const OUString
& rRefString
, SCTAB nDefTab
,
1357 ScRefAddress
& rStartRefAddress
, ScRefAddress
& rEndRefAddress
,
1358 const ScAddress::Details
& rDetails
,
1359 ScAddress::ExternalInfo
* pExtInfo
/* = NULL */ )
1362 if (pExtInfo
|| (ScGlobal::FindUnquoted( rRefString
, SC_COMPILER_FILE_TAB_SEP
) == -1))
1364 ScRange
aRange( ScAddress( 0, 0, nDefTab
));
1365 sal_uInt16 nRes
= aRange
.Parse( rRefString
, pDoc
, rDetails
, pExtInfo
);
1366 if ( nRes
& SCA_VALID
)
1368 rStartRefAddress
.Set( aRange
.aStart
,
1369 ((nRes
& SCA_COL_ABSOLUTE
) == 0),
1370 ((nRes
& SCA_ROW_ABSOLUTE
) == 0),
1371 ((nRes
& SCA_TAB_ABSOLUTE
) == 0));
1372 rEndRefAddress
.Set( aRange
.aEnd
,
1373 ((nRes
& SCA_COL2_ABSOLUTE
) == 0),
1374 ((nRes
& SCA_ROW2_ABSOLUTE
) == 0),
1375 ((nRes
& SCA_TAB2_ABSOLUTE
) == 0));
1382 sal_uInt16
ScAddress::Parse( const OUString
& r
, ScDocument
* pDoc
,
1383 const Details
& rDetails
,
1384 ExternalInfo
* pExtInfo
,
1385 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
)
1387 return lcl_ScAddress_Parse( r
.getStr(), pDoc
, *this, rDetails
, pExtInfo
, pExternalLinks
);
1390 bool ScRange::Intersects( const ScRange
& rRange
) const
1393 std::min( aEnd
.Col(), rRange
.aEnd
.Col() ) < std::max( aStart
.Col(), rRange
.aStart
.Col() )
1394 || std::min( aEnd
.Row(), rRange
.aEnd
.Row() ) < std::max( aStart
.Row(), rRange
.aStart
.Row() )
1395 || std::min( aEnd
.Tab(), rRange
.aEnd
.Tab() ) < std::max( aStart
.Tab(), rRange
.aStart
.Tab() )
1399 ScRange
ScRange::Intersection( const ScRange
& rOther
) const
1401 SCCOL nCol1
= std::max(aStart
.Col(), rOther
.aStart
.Col());
1402 SCCOL nCol2
= std::min(aEnd
.Col(), rOther
.aEnd
.Col());
1403 SCROW nRow1
= std::max(aStart
.Row(), rOther
.aStart
.Row());
1404 SCROW nRow2
= std::min(aEnd
.Row(), rOther
.aEnd
.Row());
1405 SCTAB nTab1
= std::max(aStart
.Tab(), rOther
.aStart
.Tab());
1406 SCTAB nTab2
= std::min(aEnd
.Tab(), rOther
.aEnd
.Tab());
1408 if (nCol1
> nCol2
|| nRow1
> nRow2
|| nTab1
> nTab2
)
1409 return ScRange(ScAddress::INITIALIZE_INVALID
);
1411 return ScRange(nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
1414 void ScRange::PutInOrder()
1416 SCCOL nCol1
= aStart
.Col(), nCol2
= aEnd
.Col();
1417 SCROW nRow1
= aStart
.Row(), nRow2
= aEnd
.Row();
1418 SCTAB nTab1
= aStart
.Tab(), nTab2
= aEnd
.Tab();
1420 ::PutInOrder(nCol1
, nCol2
);
1421 ::PutInOrder(nRow1
, nRow2
);
1422 ::PutInOrder(nTab1
, nTab2
);
1424 aStart
.SetCol(nCol1
);
1425 aStart
.SetRow(nRow1
);
1426 aStart
.SetTab(nTab1
);
1433 void ScRange::Justify()
1436 if ( aEnd
.Col() < (nTempCol
= aStart
.Col()) )
1438 aStart
.SetCol(aEnd
.Col()); aEnd
.SetCol(nTempCol
);
1441 if ( aEnd
.Row() < (nTempRow
= aStart
.Row()) )
1443 aStart
.SetRow(aEnd
.Row()); aEnd
.SetRow(nTempRow
);
1446 if ( aEnd
.Tab() < (nTempTab
= aStart
.Tab()) )
1448 aStart
.SetTab(aEnd
.Tab()); aEnd
.SetTab(nTempTab
);
1452 void ScRange::ExtendTo( const ScRange
& rRange
)
1454 OSL_ENSURE( rRange
.IsValid(), "ScRange::ExtendTo - cannot extend to invalid range" );
1457 aStart
.SetCol( std::min( aStart
.Col(), rRange
.aStart
.Col() ) );
1458 aStart
.SetRow( std::min( aStart
.Row(), rRange
.aStart
.Row() ) );
1459 aStart
.SetTab( std::min( aStart
.Tab(), rRange
.aStart
.Tab() ) );
1460 aEnd
.SetCol( std::max( aEnd
.Col(), rRange
.aEnd
.Col() ) );
1461 aEnd
.SetRow( std::max( aEnd
.Row(), rRange
.aEnd
.Row() ) );
1462 aEnd
.SetTab( std::max( aEnd
.Tab(), rRange
.aEnd
.Tab() ) );
1468 static sal_uInt16
lcl_ScRange_Parse_OOo( ScRange
& rRange
,
1471 ScAddress::ExternalInfo
* pExtInfo
= NULL
)
1473 sal_uInt16 nRes1
= 0, nRes2
= 0;
1474 sal_Int32 nPos
= ScGlobal::FindUnquoted( r
, ':');
1477 OUStringBuffer
aTmp(r
);
1479 const sal_Unicode
* p
= aTmp
.getStr();
1480 sal_uInt16 nRawRes1
= 0;
1481 if (((nRes1
= lcl_ScAddress_Parse_OOo( p
, pDoc
, rRange
.aStart
, nRawRes1
, pExtInfo
, NULL
)) != 0) ||
1482 ((nRawRes1
& (SCA_VALID_COL
| SCA_VALID_ROW
)) && (nRawRes1
& SCA_VALID_TAB
)))
1484 rRange
.aEnd
= rRange
.aStart
; // sheet must be initialized identical to first sheet
1485 sal_uInt16 nRawRes2
= 0;
1486 nRes2
= lcl_ScAddress_Parse_OOo( p
+ nPos
+ 1, pDoc
, rRange
.aEnd
, nRawRes2
, pExtInfo
, &rRange
);
1487 if (!((nRes1
& SCA_VALID
) && (nRes2
& SCA_VALID
)) &&
1488 // If not fully valid addresses, check if both have a valid
1489 // column or row, and both have valid (or omitted) sheet references.
1490 (nRawRes1
& (SCA_VALID_COL
| SCA_VALID_ROW
)) && (nRawRes1
& SCA_VALID_TAB
) &&
1491 (nRawRes2
& (SCA_VALID_COL
| SCA_VALID_ROW
)) && (nRawRes2
& SCA_VALID_TAB
) &&
1492 // Both must be column XOR row references, A:A or 1:1 but not A:1 or 1:A
1493 ((nRawRes1
& (SCA_VALID_COL
| SCA_VALID_ROW
)) == (nRawRes2
& (SCA_VALID_COL
| SCA_VALID_ROW
))))
1495 nRes1
= nRawRes1
| SCA_VALID
;
1496 nRes2
= nRawRes2
| SCA_VALID
;
1497 if (nRawRes1
& SCA_VALID_COL
)
1499 rRange
.aStart
.SetRow(0);
1500 rRange
.aEnd
.SetRow(MAXROW
);
1501 nRes1
|= SCA_VALID_ROW
| SCA_ROW_ABSOLUTE
;
1502 nRes2
|= SCA_VALID_ROW
| SCA_ROW_ABSOLUTE
;
1506 rRange
.aStart
.SetCol(0);
1507 rRange
.aEnd
.SetCol(MAXCOL
);
1508 nRes1
|= SCA_VALID_COL
| SCA_COL_ABSOLUTE
;
1509 nRes2
|= SCA_VALID_COL
| SCA_COL_ABSOLUTE
;
1512 else if ((nRes1
& SCA_VALID
) && (nRes2
& SCA_VALID
))
1514 // Flag entire column/row references so they can be displayed
1515 // as such. If the sticky reference parts are not both
1516 // absolute or relative, assume that the user thought about
1517 // something we should not touch.
1518 if (rRange
.aStart
.Row() == 0 && rRange
.aEnd
.Row() == MAXROW
&&
1519 ((nRes1
& SCA_ROW_ABSOLUTE
) == 0) && ((nRes2
& SCA_ROW_ABSOLUTE
) == 0))
1521 nRes1
|= SCA_ROW_ABSOLUTE
;
1522 nRes2
|= SCA_ROW_ABSOLUTE
;
1524 else if (rRange
.aStart
.Col() == 0 && rRange
.aEnd
.Col() == MAXCOL
&&
1525 ((nRes1
& SCA_COL_ABSOLUTE
) == 0) && ((nRes2
& SCA_COL_ABSOLUTE
) == 0))
1527 nRes1
|= SCA_COL_ABSOLUTE
;
1528 nRes2
|= SCA_COL_ABSOLUTE
;
1531 if ((nRes1
& SCA_VALID
) && (nRes2
& SCA_VALID
))
1533 // PutInOrder / Justify
1534 sal_uInt16 nMask
, nBits1
, nBits2
;
1536 if ( rRange
.aEnd
.Col() < (nTempCol
= rRange
.aStart
.Col()) )
1538 rRange
.aStart
.SetCol(rRange
.aEnd
.Col()); rRange
.aEnd
.SetCol(nTempCol
);
1539 nMask
= (SCA_VALID_COL
| SCA_COL_ABSOLUTE
);
1540 nBits1
= nRes1
& nMask
;
1541 nBits2
= nRes2
& nMask
;
1542 nRes1
= (nRes1
& ~nMask
) | nBits2
;
1543 nRes2
= (nRes2
& ~nMask
) | nBits1
;
1546 if ( rRange
.aEnd
.Row() < (nTempRow
= rRange
.aStart
.Row()) )
1548 rRange
.aStart
.SetRow(rRange
.aEnd
.Row()); rRange
.aEnd
.SetRow(nTempRow
);
1549 nMask
= (SCA_VALID_ROW
| SCA_ROW_ABSOLUTE
);
1550 nBits1
= nRes1
& nMask
;
1551 nBits2
= nRes2
& nMask
;
1552 nRes1
= (nRes1
& ~nMask
) | nBits2
;
1553 nRes2
= (nRes2
& ~nMask
) | nBits1
;
1556 if ( rRange
.aEnd
.Tab() < (nTempTab
= rRange
.aStart
.Tab()) )
1558 rRange
.aStart
.SetTab(rRange
.aEnd
.Tab()); rRange
.aEnd
.SetTab(nTempTab
);
1559 nMask
= (SCA_VALID_TAB
| SCA_TAB_ABSOLUTE
| SCA_TAB_3D
);
1560 nBits1
= nRes1
& nMask
;
1561 nBits2
= nRes2
& nMask
;
1562 nRes1
= (nRes1
& ~nMask
) | nBits2
;
1563 nRes2
= (nRes2
& ~nMask
) | nBits1
;
1565 if ( ((nRes1
& ( SCA_TAB_ABSOLUTE
| SCA_TAB_3D
))
1566 == ( SCA_TAB_ABSOLUTE
| SCA_TAB_3D
))
1567 && !(nRes2
& SCA_TAB_3D
) )
1568 nRes2
|= SCA_TAB_ABSOLUTE
;
1572 // Don't leave around valid half references.
1577 nRes1
= ( ( nRes1
| nRes2
) & SCA_VALID
)
1579 | ( ( nRes2
& SCA_BITS
) << 4 );
1583 sal_uInt16
ScRange::Parse( const OUString
& rString
, ScDocument
* pDoc
,
1584 const ScAddress::Details
& rDetails
,
1585 ScAddress::ExternalInfo
* pExtInfo
,
1586 const uno::Sequence
<sheet::ExternalLinkInfo
>* pExternalLinks
)
1588 if (rString
.isEmpty())
1591 switch (rDetails
.eConv
)
1593 case formula::FormulaGrammar::CONV_XL_A1
:
1594 case formula::FormulaGrammar::CONV_XL_OOX
:
1596 return lcl_ScRange_Parse_XL_A1( *this, rString
.getStr(), pDoc
, false, pExtInfo
,
1597 (rDetails
.eConv
== formula::FormulaGrammar::CONV_XL_OOX
? pExternalLinks
: NULL
) );
1600 case formula::FormulaGrammar::CONV_XL_R1C1
:
1602 return lcl_ScRange_Parse_XL_R1C1( *this, rString
.getStr(), pDoc
, rDetails
, false, pExtInfo
);
1606 case formula::FormulaGrammar::CONV_OOO
:
1608 return lcl_ScRange_Parse_OOo( *this, rString
, pDoc
, pExtInfo
);
1613 // Accept a full range, or an address
1614 sal_uInt16
ScRange::ParseAny( const OUString
& rString
, ScDocument
* pDoc
,
1615 const ScAddress::Details
& rDetails
)
1617 sal_uInt16 nRet
= Parse( rString
, pDoc
, rDetails
);
1618 const sal_uInt16 nValid
= SCA_VALID
| SCA_VALID_COL2
| SCA_VALID_ROW2
| SCA_VALID_TAB2
;
1620 if ( (nRet
& nValid
) != nValid
)
1622 ScAddress
aAdr(aStart
);//initialize with currentPos as fallback for table number
1623 nRet
= aAdr
.Parse( rString
, pDoc
, rDetails
);
1624 if ( nRet
& SCA_VALID
)
1625 aStart
= aEnd
= aAdr
;
1630 // Parse only full row references
1631 sal_uInt16
ScRange::ParseCols( const OUString
& rStr
, ScDocument
* pDoc
,
1632 const ScAddress::Details
& rDetails
)
1637 const sal_Unicode
* p
= rStr
.getStr();
1638 sal_uInt16 nRes
= 0, ignored
= 0;
1640 (void)pDoc
; // make compiler shutup we may need this later
1642 switch (rDetails
.eConv
)
1645 case formula::FormulaGrammar::CONV_OOO
: // No full col refs in OOO yet, assume XL notation
1646 case formula::FormulaGrammar::CONV_XL_A1
:
1647 case formula::FormulaGrammar::CONV_XL_OOX
:
1648 if (NULL
!= (p
= lcl_a1_get_col( p
, &aStart
, &ignored
) ) )
1652 if( NULL
!= (p
= lcl_a1_get_col( p
+1, &aEnd
, &ignored
)))
1654 nRes
= SCA_VALID_COL
;
1660 nRes
= SCA_VALID_COL
;
1665 case formula::FormulaGrammar::CONV_XL_R1C1
:
1666 if ((p
[0] == 'C' || p
[0] == 'c') &&
1667 NULL
!= (p
= lcl_r1c1_get_col( p
, rDetails
, &aStart
, &ignored
)))
1671 if( (p
[1] == 'C' || p
[1] == 'c') &&
1672 NULL
!= (p
= lcl_r1c1_get_col( p
+1, rDetails
, &aEnd
, &ignored
)))
1674 nRes
= SCA_VALID_COL
;
1680 nRes
= SCA_VALID_COL
;
1686 return (p
!= NULL
&& *p
== '\0') ? nRes
: 0;
1689 // Parse only full row references
1690 sal_uInt16
ScRange::ParseRows( const OUString
& rStr
, ScDocument
* pDoc
,
1691 const ScAddress::Details
& rDetails
)
1696 const sal_Unicode
* p
= rStr
.getStr();
1697 sal_uInt16 nRes
= 0, ignored
= 0;
1699 (void)pDoc
; // make compiler shutup we may need this later
1701 switch (rDetails
.eConv
)
1704 case formula::FormulaGrammar::CONV_OOO
: // No full row refs in OOO yet, assume XL notation
1705 case formula::FormulaGrammar::CONV_XL_A1
:
1706 case formula::FormulaGrammar::CONV_XL_OOX
:
1707 if (NULL
!= (p
= lcl_a1_get_row( p
, &aStart
, &ignored
) ) )
1711 if( NULL
!= (p
= lcl_a1_get_row( p
+1, &aEnd
, &ignored
)))
1713 nRes
= SCA_VALID_COL
;
1719 nRes
= SCA_VALID_COL
;
1724 case formula::FormulaGrammar::CONV_XL_R1C1
:
1725 if ((p
[0] == 'R' || p
[0] == 'r') &&
1726 NULL
!= (p
= lcl_r1c1_get_row( p
, rDetails
, &aStart
, &ignored
)))
1730 if( (p
[1] == 'R' || p
[1] == 'r') &&
1731 NULL
!= (p
= lcl_r1c1_get_row( p
+1, rDetails
, &aEnd
, &ignored
)))
1733 nRes
= SCA_VALID_COL
;
1739 nRes
= SCA_VALID_COL
;
1745 return (p
!= NULL
&& *p
== '\0') ? nRes
: 0;
1748 template<typename T
> static inline void lcl_ScColToAlpha( T
& rBuf
, SCCOL nCol
)
1753 rBuf
.append( static_cast<char>( 'A' + nCol
));
1756 rBuf
.append( static_cast<char>( 'A' + nCol
/ 26 - 1 ));
1757 rBuf
.append( static_cast<char>( 'A' + nCol
% 26 ));
1762 sal_Int32 nInsert
= rBuf
.getLength();
1765 SCCOL nC
= nCol
% 26;
1766 rBuf
.insert(nInsert
, static_cast<char> ( 'A' + nC
));
1767 nCol
= sal::static_int_cast
<SCCOL
>( nCol
- nC
);
1768 nCol
= nCol
/ 26 - 1;
1770 rBuf
.insert(nInsert
, static_cast<char> ( 'A' + nCol
));
1774 void ScColToAlpha( OUStringBuffer
& rBuf
, SCCOL nCol
)
1776 lcl_ScColToAlpha(rBuf
, nCol
);
1779 template <typename T
> static inline void lcl_a1_append_c ( T
&rString
, int nCol
, bool bIsAbs
)
1782 rString
.append("$");
1783 lcl_ScColToAlpha( rString
, sal::static_int_cast
<SCCOL
>(nCol
) );
1786 template <typename T
> static inline void lcl_a1_append_r ( T
&rString
, sal_Int32 nRow
, bool bIsAbs
)
1789 rString
.append("$");
1790 rString
.append( nRow
+ 1 );
1793 template <typename T
> static inline void lcl_r1c1_append_c ( T
&rString
, sal_Int32 nCol
, bool bIsAbs
,
1794 const ScAddress::Details
& rDetails
)
1796 rString
.append("C");
1799 rString
.append( nCol
+ 1 );
1803 nCol
-= rDetails
.nCol
;
1805 rString
.append("[").append(nCol
).append("]");
1810 template <typename T
> static inline void lcl_r1c1_append_r ( T
&rString
, sal_Int32 nRow
, bool bIsAbs
,
1811 const ScAddress::Details
& rDetails
)
1813 rString
.append("R");
1816 rString
.append( nRow
+ 1 );
1820 nRow
-= rDetails
.nRow
;
1822 rString
.append("[").append(nRow
).append("]");
1827 static OUString
getFileNameFromDoc( const ScDocument
* pDoc
)
1829 // TODO : er points at ScGlobal::GetAbsDocName()
1830 // as a better template. Look into it
1832 SfxObjectShell
* pShell
;
1835 NULL
!= (pShell
= pDoc
->GetDocumentShell() ) )
1837 uno::Reference
< frame::XModel
> xModel( pShell
->GetModel(), uno::UNO_QUERY
);
1840 if( !xModel
->getURL().isEmpty() )
1842 INetURLObject
aURL( xModel
->getURL() );
1843 sFileName
= aURL
.GetLastName();
1846 sFileName
= pShell
->GetTitle();
1853 static inline void lcl_string_append(OUStringBuffer
&rString
, const OUString
&sString
)
1855 rString
.append(sString
);
1858 static inline void lcl_string_append(OStringBuffer
&rString
, const OUString
&sString
)
1860 rString
.append(OUStringToOString( sString
, RTL_TEXTENCODING_UTF8
));
1863 template<typename T
> inline void lcl_Format( T
& r
, SCTAB nTab
, SCROW nRow
, SCCOL nCol
, sal_uInt16 nFlags
,
1864 const ScDocument
* pDoc
,
1865 const ScAddress::Details
& rDetails
)
1867 if( nFlags
& SCA_VALID
)
1868 nFlags
|= ( SCA_VALID_ROW
| SCA_VALID_COL
| SCA_VALID_TAB
);
1869 if( pDoc
&& (nFlags
& SCA_VALID_TAB
) )
1871 if ( nTab
>= pDoc
->GetTableCount() )
1873 lcl_string_append(r
, ScGlobal::GetRscString( STR_NOREF_STR
));
1876 if( nFlags
& SCA_TAB_3D
)
1878 OUString aTabName
, aDocName
;
1879 pDoc
->GetName(nTab
, aTabName
);
1880 // External Reference, same as in ScCompiler::MakeTabStr()
1881 if( aTabName
[0] == '\'' )
1883 sal_Int32 nPos
= ScCompiler::GetDocTabPos( aTabName
);
1886 aDocName
= aTabName
.copy( 0, nPos
+ 1 );
1887 aTabName
= aTabName
.copy( nPos
+ 1 );
1890 else if( nFlags
& SCA_FORCE_DOC
)
1892 // VBA has an 'external' flag that forces the addition of the
1893 // tab name _and_ the doc name. The VBA code would be
1894 // needlessly complicated if it constructed an actual external
1895 // reference so we add this somewhat cheesy kludge to force the
1896 // addition of the document name even for non-external references
1897 aDocName
= getFileNameFromDoc( pDoc
);
1899 ScCompiler::CheckTabQuotes( aTabName
, rDetails
.eConv
);
1901 switch( rDetails
.eConv
)
1904 case formula::FormulaGrammar::CONV_OOO
:
1905 lcl_string_append(r
, aDocName
);
1906 if( nFlags
& SCA_TAB_ABSOLUTE
)
1908 lcl_string_append(r
, aTabName
);
1912 case formula::FormulaGrammar::CONV_XL_A1
:
1913 case formula::FormulaGrammar::CONV_XL_R1C1
:
1914 case formula::FormulaGrammar::CONV_XL_OOX
:
1915 if (!aDocName
.isEmpty())
1917 lcl_string_append(r
.append("["), aDocName
);
1920 lcl_string_append(r
, aTabName
);
1926 switch( rDetails
.eConv
)
1929 case formula::FormulaGrammar::CONV_OOO
:
1930 case formula::FormulaGrammar::CONV_XL_A1
:
1931 case formula::FormulaGrammar::CONV_XL_OOX
:
1932 if( nFlags
& SCA_VALID_COL
)
1933 lcl_a1_append_c ( r
, nCol
, (nFlags
& SCA_COL_ABSOLUTE
) != 0 );
1934 if( nFlags
& SCA_VALID_ROW
)
1935 lcl_a1_append_r ( r
, nRow
, (nFlags
& SCA_ROW_ABSOLUTE
) != 0 );
1938 case formula::FormulaGrammar::CONV_XL_R1C1
:
1939 if( nFlags
& SCA_VALID_ROW
)
1940 lcl_r1c1_append_r ( r
, nRow
, (nFlags
& SCA_ROW_ABSOLUTE
) != 0, rDetails
);
1941 if( nFlags
& SCA_VALID_COL
)
1942 lcl_r1c1_append_c ( r
, nCol
, (nFlags
& SCA_COL_ABSOLUTE
) != 0, rDetails
);
1947 void ScAddress::Format( OStringBuffer
& r
, sal_uInt16 nFlags
,
1948 const ScDocument
* pDoc
,
1949 const Details
& rDetails
) const
1951 lcl_Format(r
, nTab
, nRow
, nCol
, nFlags
, pDoc
, rDetails
);
1954 OUString
ScAddress::Format(sal_uInt16 nFlags
, const ScDocument
* pDoc
,
1955 const Details
& rDetails
) const
1958 lcl_Format(r
, nTab
, nRow
, nCol
, nFlags
, pDoc
, rDetails
);
1959 return r
.makeStringAndClear();
1962 static void lcl_Split_DocTab( const ScDocument
* pDoc
, SCTAB nTab
,
1963 const ScAddress::Details
& rDetails
,
1965 OUString
& rTabName
, OUString
& rDocName
)
1967 pDoc
->GetName(nTab
, rTabName
);
1969 // External reference, same as in ScCompiler::MakeTabStr()
1970 if ( rTabName
[0] == '\'' )
1972 sal_Int32 nPos
= ScCompiler::GetDocTabPos( rTabName
);
1975 rDocName
= rTabName
.copy( 0, nPos
+ 1 );
1976 rTabName
= rTabName
.copy( nPos
+ 1 );
1979 else if( nFlags
& SCA_FORCE_DOC
)
1981 // VBA has an 'external' flag that forces the addition of the
1982 // tab name _and_ the doc name. The VBA code would be
1983 // needlessly complicated if it constructed an actual external
1984 // reference so we add this somewhat cheesy kludge to force the
1985 // addition of the document name even for non-external references
1986 rDocName
= getFileNameFromDoc( pDoc
);
1988 ScCompiler::CheckTabQuotes( rTabName
, rDetails
.eConv
);
1991 static void lcl_ScRange_Format_XL_Header( OUStringBuffer
& rString
, const ScRange
& rRange
,
1992 sal_uInt16 nFlags
, const ScDocument
* pDoc
,
1993 const ScAddress::Details
& rDetails
)
1995 if( nFlags
& SCA_TAB_3D
)
1997 OUString aTabName
, aDocName
;
1998 lcl_Split_DocTab( pDoc
, rRange
.aStart
.Tab(), rDetails
, nFlags
,
1999 aTabName
, aDocName
);
2000 if( !aDocName
.isEmpty() )
2002 rString
.append("[").append(aDocName
).append("]");
2004 rString
.append(aTabName
);
2006 if( nFlags
& SCA_TAB2_3D
)
2008 lcl_Split_DocTab( pDoc
, rRange
.aEnd
.Tab(), rDetails
, nFlags
,
2009 aTabName
, aDocName
);
2010 rString
.append(":");
2011 rString
.append(aTabName
);
2013 rString
.append("!");
2017 OUString
ScRange::Format( sal_uInt16 nFlags
, const ScDocument
* pDoc
,
2018 const ScAddress::Details
& rDetails
) const
2020 if( !( nFlags
& SCA_VALID
) )
2022 return ScGlobal::GetRscString( STR_NOREF_STR
);
2026 #define absrel_differ(nFlags, mask) (((nFlags) & (mask)) ^ (((nFlags) >> 4) & (mask)))
2027 switch( rDetails
.eConv
) {
2029 case formula::FormulaGrammar::CONV_OOO
: {
2030 bool bOneTab
= (aStart
.Tab() == aEnd
.Tab());
2032 nFlags
|= SCA_TAB_3D
;
2033 r
= aStart
.Format(nFlags
, pDoc
, rDetails
);
2034 if( aStart
!= aEnd
||
2035 absrel_differ( nFlags
, SCA_COL_ABSOLUTE
) ||
2036 absrel_differ( nFlags
, SCA_ROW_ABSOLUTE
))
2038 nFlags
= ( nFlags
& SCA_VALID
) | ( ( nFlags
>> 4 ) & 0x070F );
2042 nFlags
|= SCA_TAB_3D
;
2043 OUString
aName(aEnd
.Format(nFlags
, pDoc
, rDetails
));
2050 case formula::FormulaGrammar::CONV_XL_A1
:
2051 case formula::FormulaGrammar::CONV_XL_OOX
:
2052 lcl_ScRange_Format_XL_Header( r
, *this, nFlags
, pDoc
, rDetails
);
2053 if( aStart
.Col() == 0 && aEnd
.Col() >= MAXCOL
)
2055 // Full col refs always require 2 rows (2:2)
2056 lcl_a1_append_r( r
, aStart
.Row(), (nFlags
& SCA_ROW_ABSOLUTE
) != 0 );
2058 lcl_a1_append_r( r
, aEnd
.Row(), (nFlags
& SCA_ROW2_ABSOLUTE
) != 0 );
2060 else if( aStart
.Row() == 0 && aEnd
.Row() >= MAXROW
)
2062 // Full row refs always require 2 cols (A:A)
2063 lcl_a1_append_c( r
, aStart
.Col(), (nFlags
& SCA_COL_ABSOLUTE
) != 0 );
2065 lcl_a1_append_c( r
, aEnd
.Col(), (nFlags
& SCA_COL2_ABSOLUTE
) != 0 );
2069 lcl_a1_append_c ( r
, aStart
.Col(), (nFlags
& SCA_COL_ABSOLUTE
) != 0 );
2070 lcl_a1_append_r ( r
, aStart
.Row(), (nFlags
& SCA_ROW_ABSOLUTE
) != 0 );
2071 if( aStart
.Col() != aEnd
.Col() ||
2072 absrel_differ( nFlags
, SCA_COL_ABSOLUTE
) ||
2073 aStart
.Row() != aEnd
.Row() ||
2074 absrel_differ( nFlags
, SCA_ROW_ABSOLUTE
)) {
2076 lcl_a1_append_c ( r
, aEnd
.Col(), (nFlags
& SCA_COL2_ABSOLUTE
) != 0 );
2077 lcl_a1_append_r ( r
, aEnd
.Row(), (nFlags
& SCA_ROW2_ABSOLUTE
) != 0 );
2082 case formula::FormulaGrammar::CONV_XL_R1C1
:
2083 lcl_ScRange_Format_XL_Header( r
, *this, nFlags
, pDoc
, rDetails
);
2084 if( aStart
.Col() == 0 && aEnd
.Col() >= MAXCOL
)
2086 lcl_r1c1_append_r( r
, aStart
.Row(), (nFlags
& SCA_ROW_ABSOLUTE
) != 0, rDetails
);
2087 if( aStart
.Row() != aEnd
.Row() ||
2088 absrel_differ( nFlags
, SCA_ROW_ABSOLUTE
)) {
2090 lcl_r1c1_append_r( r
, aEnd
.Row(), (nFlags
& SCA_ROW2_ABSOLUTE
) != 0, rDetails
);
2093 else if( aStart
.Row() == 0 && aEnd
.Row() >= MAXROW
)
2095 lcl_r1c1_append_c( r
, aStart
.Col(), (nFlags
& SCA_COL_ABSOLUTE
) != 0, rDetails
);
2096 if( aStart
.Col() != aEnd
.Col() ||
2097 absrel_differ( nFlags
, SCA_COL_ABSOLUTE
)) {
2099 lcl_r1c1_append_c( r
, aEnd
.Col(), (nFlags
& SCA_COL2_ABSOLUTE
) != 0, rDetails
);
2104 lcl_r1c1_append_r( r
, aStart
.Row(), (nFlags
& SCA_ROW_ABSOLUTE
) != 0, rDetails
);
2105 lcl_r1c1_append_c( r
, aStart
.Col(), (nFlags
& SCA_COL_ABSOLUTE
) != 0, rDetails
);
2106 if( aStart
.Col() != aEnd
.Col() ||
2107 absrel_differ( nFlags
, SCA_COL_ABSOLUTE
) ||
2108 aStart
.Row() != aEnd
.Row() ||
2109 absrel_differ( nFlags
, SCA_ROW_ABSOLUTE
)) {
2111 lcl_r1c1_append_r( r
, aEnd
.Row(), (nFlags
& SCA_ROW2_ABSOLUTE
) != 0, rDetails
);
2112 lcl_r1c1_append_c( r
, aEnd
.Col(), (nFlags
& SCA_COL2_ABSOLUTE
) != 0, rDetails
);
2116 #undef absrel_differ
2117 return r
.makeStringAndClear();
2120 bool ScAddress::Move( SCsCOL dx
, SCsROW dy
, SCsTAB dz
, ScDocument
* pDoc
)
2122 SCsTAB nMaxTab
= pDoc
? pDoc
->GetTableCount() : MAXTAB
;
2128 dx
= 0, bValid
= false;
2129 else if( dx
> MAXCOL
)
2130 dx
= MAXCOL
, bValid
=false;
2132 dy
= 0, bValid
= false;
2133 else if( dy
> MAXROW
)
2134 dy
= MAXROW
, bValid
=false;
2136 dz
= 0, bValid
= false;
2137 else if( dz
> nMaxTab
)
2138 dz
= nMaxTab
, bValid
=false;
2143 bool ScRange::Move( SCsCOL dx
, SCsROW dy
, SCsTAB dz
, ScDocument
* pDoc
)
2145 if (dy
&& aStart
.Row() == 0 && aEnd
.Row() == MAXROW
)
2146 dy
= 0; // Entire column not to be moved.
2147 if (dx
&& aStart
.Col() == 0 && aEnd
.Col() == MAXCOL
)
2148 dx
= 0; // Entire row not to be moved.
2149 bool b
= aStart
.Move( dx
, dy
, dz
, pDoc
);
2150 b
&= aEnd
.Move( dx
, dy
, dz
, pDoc
);
2154 OUString
ScAddress::GetColRowString( bool bAbsolute
,
2155 const Details
& rDetails
) const
2157 OUStringBuffer aString
;
2159 switch( rDetails
.eConv
)
2162 case formula::FormulaGrammar::CONV_OOO
:
2163 case formula::FormulaGrammar::CONV_XL_A1
:
2164 case formula::FormulaGrammar::CONV_XL_OOX
:
2166 aString
.append("$");
2168 lcl_ScColToAlpha( aString
, nCol
);
2171 aString
.append("$");
2173 aString
.append(OUString::number(nRow
+1));
2176 case formula::FormulaGrammar::CONV_XL_R1C1
:
2177 lcl_r1c1_append_r ( aString
, nRow
, bAbsolute
, rDetails
);
2178 lcl_r1c1_append_c ( aString
, nCol
, bAbsolute
, rDetails
);
2182 return aString
.makeStringAndClear();
2185 OUString
ScRefAddress::GetRefString( ScDocument
* pDoc
, SCTAB nActTab
,
2186 const ScAddress::Details
& rDetails
) const
2189 return EMPTY_OUSTRING
;
2190 if ( Tab()+1 > pDoc
->GetTableCount() )
2191 return ScGlobal::GetRscString( STR_NOREF_STR
);
2193 sal_uInt16 nFlags
= SCA_VALID
;
2194 if ( nActTab
!= Tab() )
2196 nFlags
|= SCA_TAB_3D
;
2198 nFlags
|= SCA_TAB_ABSOLUTE
;
2201 nFlags
|= SCA_COL_ABSOLUTE
;
2203 nFlags
|= SCA_ROW_ABSOLUTE
;
2205 return aAdr
.Format(nFlags
, pDoc
, rDetails
);
2208 bool AlphaToCol( SCCOL
& rCol
, const OUString
& rStr
)
2211 sal_Int32 nStop
= rStr
.getLength();
2214 while (nResult
<= MAXCOL
&& nPos
< nStop
&& (c
= rStr
[nPos
]) != 0 &&
2215 rtl::isAsciiAlpha(c
))
2218 nResult
= (nResult
+ 1) * 26;
2219 nResult
+= ScGlobal::ToUpperAlpha(c
) - 'A';
2222 bool bOk
= (ValidCol(nResult
) && nPos
> 0);
2228 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */