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>
29 #include <com/sun/star/frame/XModel.hpp>
30 #include <com/sun/star/beans/XPropertySet.hpp>
31 #include <com/sun/star/sheet/ExternalLinkInfo.hpp>
32 #include <com/sun/star/sheet/ExternalLinkType.hpp>
33 #include <comphelper/string.hxx>
34 #include <sfx2/objsh.hxx>
35 #include <tools/string.hxx>
36 #include <tools/urlobj.hxx>
38 using namespace ::com::sun::star
;
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() ),
52 const sal_Unicode
* parseQuotedNameWithBuffer( const sal_Unicode
* pStart
, const sal_Unicode
* p
, OUString
& rName
)
54 // The current character must be on the 2nd quote.
56 // Push all the characters up to the current, but skip the very first
57 // character which is the opening quote.
58 OUStringBuffer
aBuf(OUString(pStart
+1, p
-pStart
-1));
60 ++p
; // Skip the 2nd quote.
61 sal_Unicode cPrev
= 0;
68 // double single-quote equals one single quote.
74 else if (cPrev
== '\'')
76 // We are past the closing quote. We're done!
77 rName
= aBuf
.makeStringAndClear();
89 * Parse from the opening single quote to the closing single quote. Inside
90 * the quotes, a single quote character is encoded by double single-quote
93 * @param p pointer to the first character to begin parsing.
94 * @param rName (reference) parsed name within the quotes. If the name is
95 * empty, either the parsing failed or it's an empty quote.
97 * @return pointer to the character immediately after the closing single
100 const sal_Unicode
* parseQuotedName( const sal_Unicode
* p
, OUString
& rName
)
105 const sal_Unicode
* pStart
= p
;
106 sal_Unicode cPrev
= 0;
113 // double single-quote equals one single quote.
114 return parseQuotedNameWithBuffer(pStart
, p
, rName
);
117 else if (cPrev
== '\'')
119 // We are past the closing quote. We're done! Skip the opening
120 // and closing quotes.
121 rName
= OUString(pStart
+1, p
- pStart
-2);
135 sal_Unicode_strtol ( const sal_Unicode
* p
,
136 const sal_Unicode
** pEnd
)
138 long int accum
= 0, prev
= 0;
149 while (rtl::isAsciiDigit( *p
))
151 accum
= accum
* 10 + *p
- '0';
162 return is_neg
? -accum
: accum
;
165 static const sal_Unicode
* lcl_eatWhiteSpace( const sal_Unicode
* p
)
175 /** Determines the number of sheets an external reference spans and sets
176 rRange.aEnd.nTab accordingly. If a sheet is not found, the corresponding
177 bits in rFlags are cleared. pExtInfo is filled if it wasn't already. If in
178 cached order rStartTabName comes after rEndTabName, pExtInfo->maTabName
179 is set to rEndTabName.
180 @returns <FALSE/> if pExtInfo is already filled and rExternDocName does not
181 result in the identical file ID. Else <TRUE/>.
183 static bool lcl_ScRange_External_TabSpan(
186 ScAddress::ExternalInfo
* pExtInfo
,
187 const OUString
& rExternDocName
,
188 const OUString
& rStartTabName
,
189 const OUString
& rEndTabName
,
192 if (rExternDocName
.isEmpty())
193 return !pExtInfo
|| !pExtInfo
->mbExternal
;
195 ScExternalRefManager
* pRefMgr
= pDoc
->GetExternalRefManager();
196 if (pRefMgr
->isOwnDocument( rExternDocName
))
198 // This is an internal document. Get the sheet positions from the
199 // ScDocument instance.
200 if (!rStartTabName
.isEmpty())
203 if (pDoc
->GetTable(rStartTabName
, nTab
))
204 rRange
.aStart
.SetTab(nTab
);
207 if (!rEndTabName
.isEmpty())
210 if (pDoc
->GetTable(rEndTabName
, nTab
))
211 rRange
.aEnd
.SetTab(nTab
);
213 return !pExtInfo
|| !pExtInfo
->mbExternal
;
216 sal_uInt16 nFileId
= pRefMgr
->getExternalFileId( rExternDocName
);
220 if (pExtInfo
->mbExternal
)
222 if (pExtInfo
->mnFileId
!= nFileId
)
227 pExtInfo
->mbExternal
= true;
228 pExtInfo
->maTabName
= rStartTabName
;
229 pExtInfo
->mnFileId
= nFileId
;
233 if (rEndTabName
.isEmpty() || rStartTabName
== rEndTabName
)
235 rRange
.aEnd
.SetTab( rRange
.aStart
.Tab());
239 SCsTAB nSpan
= pRefMgr
->getCachedTabSpan( nFileId
, rStartTabName
, rEndTabName
);
241 rFlags
&= ~(SCA_VALID_TAB
| SCA_VALID_TAB2
);
243 rFlags
&= ~SCA_VALID_TAB2
;
245 rRange
.aEnd
.SetTab( rRange
.aStart
.Tab() + nSpan
- 1);
248 rRange
.aEnd
.SetTab( rRange
.aStart
.Tab() - nSpan
- 1);
250 pExtInfo
->maTabName
= rEndTabName
;
255 /** Returns NULL if the string should be a sheet name, but is invalid.
256 Returns a pointer to the first character after the sheet name, if there was
257 any, else pointer to start.
258 @param pMsoxlQuoteStop
259 Starting _within_ a quoted name, but still may be 3D; quoted name stops
262 static const sal_Unicode
*
263 lcl_XL_ParseSheetRef( const sal_Unicode
* start
,
264 OUString
& rExternTabName
,
266 const sal_Unicode
* pMsoxlQuoteStop
)
269 const sal_Unicode
*p
= start
;
271 // XL only seems to use single quotes for sheet names.
274 const sal_Unicode
* pCurrentStart
= p
;
275 while (p
< pMsoxlQuoteStop
)
279 // We pre-analyzed the quoting, no checks needed here.
282 aTabName
+= OUString( pCurrentStart
,
283 sal::static_int_cast
<xub_StrLen
>( p
- pCurrentStart
));
294 if (pCurrentStart
< p
)
295 aTabName
+= OUString( pCurrentStart
, sal::static_int_cast
<xub_StrLen
>( p
- pCurrentStart
));
296 if (aTabName
.isEmpty())
298 if (p
== pMsoxlQuoteStop
)
299 ++p
; // position on ! of ...'!...
300 if( *p
!= '!' && ( !allow_3d
|| *p
!= ':' ) )
301 return (!allow_3d
&& *p
== ':') ? p
: start
;
305 p
= parseQuotedName(p
, aTabName
);
306 if (aTabName
.isEmpty())
311 bool only_digits
= true;
318 * Some names starting with digits are actually valid, but
319 * unparse quoted. Things are quite tricky: most sheet names
320 * starting with a digit are ok, but not those starting with
321 * "[0-9]*\." or "[0-9]+[eE]".
330 const sal_Unicode uc
= *p
;
331 if( rtl::isAsciiAlpha( uc
) || uc
== '_' )
333 if( only_digits
&& p
!= start
&&
334 (uc
== 'e' || uc
== 'E' ) )
342 else if( rtl::isAsciiDigit( uc
))
348 if( only_digits
) // Valid, except after only digits.
357 // non ASCII character is allowed.
364 if( *p
!= '!' && ( !allow_3d
|| *p
!= ':' ) )
365 return (!allow_3d
&& *p
== ':') ? p
: start
;
367 aTabName
+= OUString( start
, sal::static_int_cast
<xub_StrLen
>( p
- start
) );
370 rExternTabName
= aTabName
;
374 /** Tries to obtain the external document index and replace by actual document
378 Contains the default pointer the caller would return if this method
379 returns FALSE, may be replaced by NULL for type or data errors.
381 @returns FALSE only if the input name is numeric and not within the index
382 sequence, or the link type cannot be determined or data mismatch. Returns
383 TRUE in all other cases, also when there is no index sequence or the input
386 static bool lcl_XL_getExternalDoc( const sal_Unicode
** ppErrRet
, OUString
& rExternDocName
,
387 const uno::Sequence
< const sheet::ExternalLinkInfo
> * pExternalLinks
)
389 // 1-based, sequence starts with an empty element.
390 if (pExternalLinks
&& pExternalLinks
->hasElements())
392 // A numeric "document name" is an index into the sequence.
393 if (CharClass::isAsciiNumeric( rExternDocName
))
395 sal_Int32 i
= rExternDocName
.toInt32();
396 if (i
< 0 || i
>= pExternalLinks
->getLength())
397 return false; // with default *ppErrRet
398 const sheet::ExternalLinkInfo
& rInfo
= (*pExternalLinks
)[i
];
401 case sheet::ExternalLinkType::DOCUMENT
:
404 if (!(rInfo
.Data
>>= aStr
))
406 OSL_TRACE( "ScRange::Parse_XL_Header: Data type mismatch for ExternalLinkInfo %d", i
);
410 rExternDocName
= aStr
;
413 case sheet::ExternalLinkType::SELF
:
415 case sheet::ExternalLinkType::SPECIAL
:
416 // silently return nothing (do not assert), caller has to handle this
420 OSL_TRACE( "ScRange::Parse_XL_Header: unhandled ExternalLinkType %d for index %d",
430 const sal_Unicode
* ScRange::Parse_XL_Header(
431 const sal_Unicode
* p
,
432 const ScDocument
* pDoc
,
433 OUString
& rExternDocName
,
434 OUString
& rStartTabName
,
435 OUString
& rEndTabName
,
437 bool bOnlyAcceptSingle
,
438 const uno::Sequence
< const sheet::ExternalLinkInfo
> * pExternalLinks
)
440 const sal_Unicode
* startTabs
, *start
= p
;
441 sal_uInt16 nSaveFlags
= nFlags
;
443 // Is this an external reference ?
447 const sal_Unicode
* pMsoxlQuoteStop
= NULL
;
451 // Only single quotes are correct, and a double single quote escapes a
452 // single quote text inside the quoted text.
455 p
= parseQuotedName(p
, rExternDocName
);
456 if (!*p
|| *p
!= ']' || rExternDocName
.isEmpty())
464 // non-quoted file name.
465 p
= ScGlobal::UnicodeStrChr( start
+1, ']' );
468 rExternDocName
+= OUString( start
+1, sal::static_int_cast
<xub_StrLen
>( p
-(start
+1) ) );
472 const sal_Unicode
* pErrRet
= start
;
473 if (!lcl_XL_getExternalDoc( &pErrRet
, rExternDocName
, pExternalLinks
))
476 rExternDocName
= ScGlobal::GetAbsDocName(rExternDocName
, pDoc
->GetDocumentShell());
480 // Sickness in Excel's ODF msoxl namespace:
481 // 'E:\[EXTDATA8.XLS]Sheet1'!$A$7 or
482 // 'E:\[EXTDATA12B.XLSB]Sheet1:Sheet3'!$A$11
483 // But, 'Sheet1'!B3 would also be a valid!
484 // Excel does not allow [ and ] characters in sheet names though.
485 // But, more sickness comes with MOOXML as there may be
486 // '[1]Sheet 4'!$A$1 where [1] is the external doc's index.
487 p
= parseQuotedName(p
, rExternDocName
);
488 if (!*p
|| *p
!= '!')
493 if (!rExternDocName
.isEmpty())
495 sal_Int32 nOpen
= rExternDocName
.indexOf( '[');
500 sal_Int32 nClose
= rExternDocName
.indexOf( ']', nOpen
+1);
505 rExternDocName
= rExternDocName
.copy(0, nClose
);
506 rExternDocName
= rExternDocName
.replaceAt( nOpen
, 1, "");
507 pMsoxlQuoteStop
= p
- 1; // the ' quote char
508 // There may be embedded escaped quotes, just matching the
509 // doc name's length may not work.
510 for (p
= start
; *p
!= '['; ++p
)
512 for ( ; *p
!= ']'; ++p
)
516 // Handle '[1]Sheet 4'!$A$1
519 const sal_Unicode
* pErrRet
= start
;
520 if (!lcl_XL_getExternalDoc( &pErrRet
, rExternDocName
, pExternalLinks
))
526 if (rExternDocName
.isEmpty())
531 p
= lcl_XL_ParseSheetRef( p
, rStartTabName
, !bOnlyAcceptSingle
, pMsoxlQuoteStop
);
533 return start
; // invalid tab
534 if (bOnlyAcceptSingle
&& *p
== ':')
538 nFlags
|= SCA_VALID_TAB
| SCA_TAB_3D
| SCA_TAB_ABSOLUTE
;
539 if( *p
== ':' ) // 3d ref
541 p
= lcl_XL_ParseSheetRef( p
+1, rEndTabName
, false, pMsoxlQuoteStop
);
545 return start
; // invalid tab
547 nFlags
|= SCA_VALID_TAB2
| SCA_TAB2_3D
| SCA_TAB2_ABSOLUTE
;
551 // If only one sheet is given, the full reference is still valid,
552 // only the second 3D flag is not set.
553 nFlags
|= SCA_VALID_TAB2
| SCA_TAB2_ABSOLUTE
;
554 aEnd
.SetTab( aStart
.Tab() );
560 return start
; // syntax error
563 p
= lcl_eatWhiteSpace( p
);
567 nFlags
|= SCA_VALID_TAB
| SCA_VALID_TAB2
;
568 // Use the current tab, it needs to be passed in. : aEnd.SetTab( .. );
571 if (!rExternDocName
.isEmpty())
573 ScExternalRefManager
* pRefMgr
= pDoc
->GetExternalRefManager();
574 pRefMgr
->convertToAbsName(rExternDocName
);
578 // Internal reference.
579 if (rStartTabName
.isEmpty())
586 if (!pDoc
->GetTable(rStartTabName
, nTab
))
588 // invalid table name.
589 nFlags
&= ~SCA_VALID_TAB
;
596 if (!rEndTabName
.isEmpty())
598 if (!pDoc
->GetTable(rEndTabName
, nTab
))
600 // invalid table name.
601 nFlags
&= ~SCA_VALID_TAB2
;
611 static const sal_Unicode
*
612 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
== '[') ) != false )
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
;
656 static inline const sal_Unicode
*
657 lcl_r1c1_get_row( const sal_Unicode
* p
,
658 const ScAddress::Details
& rDetails
,
659 ScAddress
* pAddr
, sal_uInt16
* nFlags
)
661 const sal_Unicode
*pEnd
;
669 if( (isRelative
= (*p
== '[') ) != false )
671 n
= sal_Unicode_strtol( p
, &pEnd
);
675 if( p
== pEnd
) // R is a relative ref with offset 0
681 else if( isRelative
)
690 *nFlags
|= SCA_ROW_ABSOLUTE
;
694 if( n
< 0 || n
>= MAXROWCOUNT
)
696 pAddr
->SetRow( static_cast<SCROW
>( n
) );
697 *nFlags
|= SCA_VALID_ROW
;
703 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
)))
768 (p
[1] != 'R' && p
[1] != 'r') ||
769 NULL
== (pTmp
= lcl_r1c1_get_row( p
+1, rDetails
, &r
.aEnd
, &nFlags2
)) ||
770 (*pTmp
!= 'C' && *pTmp
!= 'c') ||
771 NULL
== (pTmp
= lcl_r1c1_get_col( pTmp
, rDetails
, &r
.aEnd
, &nFlags2
)))
773 // single cell reference
777 // any trailing invalid character must invalidate the whole address.
778 nFlags
&= ~(SCA_VALID
| SCA_VALID_COL
| SCA_VALID_ROW
| SCA_VALID_TAB
);
782 return bOnlyAcceptSingle
? nFlags
: 0;
790 // any trailing invalid character must invalidate the whole range.
791 nFlags
&= ~(SCA_VALID
| SCA_VALID_COL
| SCA_VALID_ROW
| SCA_VALID_TAB
|
792 SCA_VALID_COL2
| SCA_VALID_ROW2
| SCA_VALID_TAB2
);
796 nFlags
|= (nFlags2
<< 4);
797 return bOnlyAcceptSingle
? 0 : nFlags
;
799 else if( *p
== 'C' || *p
== 'c' ) // full col C#
801 if( NULL
== (p
= lcl_r1c1_get_col( p
, rDetails
, &r
.aStart
, &nFlags
)))
804 if( p
[0] != ':' || (p
[1] != 'C' && p
[1] != 'c') ||
805 NULL
== (pTmp
= lcl_r1c1_get_col( p
+1, rDetails
, &r
.aEnd
, &nFlags2
)))
806 { // Fallback to just the initial C
807 nFlags
|= (nFlags
<< 4);
808 r
.aEnd
.SetCol( r
.aStart
.Col() );
812 nFlags
|= (nFlags2
<< 4);
818 // any trailing invalid character must invalidate the whole address.
819 nFlags
&= ~(SCA_VALID
| SCA_VALID_COL
| SCA_VALID_ROW
| SCA_VALID_TAB
|
820 SCA_VALID_COL2
| SCA_VALID_ROW2
| SCA_VALID_TAB2
);
825 SCA_VALID_ROW
| SCA_VALID_ROW2
|
826 SCA_ROW_ABSOLUTE
| SCA_ROW2_ABSOLUTE
;
827 r
.aStart
.SetRow( 0 );
828 r
.aEnd
.SetRow( MAXROW
);
830 return bOnlyAcceptSingle
? 0 : nFlags
;
837 static inline const sal_Unicode
*
838 lcl_a1_get_col( const sal_Unicode
* p
, ScAddress
* pAddr
, sal_uInt16
* nFlags
)
843 *nFlags
|= SCA_COL_ABSOLUTE
, p
++;
845 if( !rtl::isAsciiAlpha( *p
) )
848 nCol
= sal::static_int_cast
<SCCOL
>( toupper( char(*p
++) ) - 'A' );
849 while (nCol
<= MAXCOL
&& rtl::isAsciiAlpha(*p
))
850 nCol
= sal::static_int_cast
<SCCOL
>( ((nCol
+ 1) * 26) + toupper( char(*p
++) ) - 'A' );
851 if( nCol
> MAXCOL
|| rtl::isAsciiAlpha( *p
) )
854 *nFlags
|= SCA_VALID_COL
;
855 pAddr
->SetCol( nCol
);
860 static inline const sal_Unicode
*
861 lcl_a1_get_row( const sal_Unicode
* p
, ScAddress
* pAddr
, sal_uInt16
* nFlags
)
863 const sal_Unicode
*pEnd
;
867 *nFlags
|= SCA_ROW_ABSOLUTE
, p
++;
869 n
= sal_Unicode_strtol( p
, &pEnd
) - 1;
870 if( NULL
== pEnd
|| p
== pEnd
|| n
< 0 || n
> MAXROW
)
873 *nFlags
|= SCA_VALID_ROW
;
874 pAddr
->SetRow( static_cast<SCROW
>(n
) );
880 lcl_ScRange_Parse_XL_A1( ScRange
& r
,
881 const sal_Unicode
* p
,
883 bool bOnlyAcceptSingle
,
884 ScAddress::ExternalInfo
* pExtInfo
,
885 const uno::Sequence
< const sheet::ExternalLinkInfo
> * pExternalLinks
)
887 const sal_Unicode
* tmp1
, *tmp2
;
888 OUString aExternDocName
, aStartTabName
, aEndTabName
; // for external link table
889 sal_uInt16 nFlags
= SCA_VALID
| SCA_VALID_TAB
, nFlags2
= SCA_VALID_TAB
;
891 p
= r
.Parse_XL_Header( p
, pDoc
, aExternDocName
, aStartTabName
,
892 aEndTabName
, nFlags
, bOnlyAcceptSingle
, pExternalLinks
);
894 if (!aExternDocName
.isEmpty())
895 lcl_ScRange_External_TabSpan( r
, nFlags
, pExtInfo
, aExternDocName
,
896 aStartTabName
, aEndTabName
, pDoc
);
901 tmp1
= lcl_a1_get_col( p
, &r
.aStart
, &nFlags
);
902 if( tmp1
== NULL
) // Is it a row only reference 3:5
904 if( bOnlyAcceptSingle
) // by definition full row refs are ranges
907 tmp1
= lcl_a1_get_row( p
, &r
.aStart
, &nFlags
);
909 tmp1
= lcl_eatWhiteSpace( tmp1
);
910 if( !tmp1
|| *tmp1
++ != ':' ) // Even a singleton requires ':' (eg 2:2)
913 tmp1
= lcl_eatWhiteSpace( tmp1
);
914 tmp2
= lcl_a1_get_row( tmp1
, &r
.aEnd
, &nFlags2
);
918 r
.aStart
.SetCol( 0 ); r
.aEnd
.SetCol( MAXCOL
);
920 SCA_VALID_COL
| SCA_VALID_COL2
|
921 SCA_COL_ABSOLUTE
| SCA_COL2_ABSOLUTE
;
922 nFlags
|= (nFlags2
<< 4);
926 tmp2
= lcl_a1_get_row( tmp1
, &r
.aStart
, &nFlags
);
927 if( tmp2
== NULL
) // check for col only reference F:H
929 if( bOnlyAcceptSingle
) // by definition full col refs are ranges
932 tmp1
= lcl_eatWhiteSpace( tmp1
);
933 if( *tmp1
++ != ':' ) // Even a singleton requires ':' (eg F:F)
936 tmp1
= lcl_eatWhiteSpace( tmp1
);
937 tmp2
= lcl_a1_get_col( tmp1
, &r
.aEnd
, &nFlags2
);
941 r
.aStart
.SetRow( 0 ); r
.aEnd
.SetRow( MAXROW
);
943 SCA_VALID_ROW
| SCA_VALID_ROW2
|
944 SCA_ROW_ABSOLUTE
| SCA_ROW2_ABSOLUTE
;
945 nFlags
|= (nFlags2
<< 4);
949 // prepare as if it's a singleton, in case we want to fall back */
950 r
.aEnd
.SetCol( r
.aStart
.Col() );
951 r
.aEnd
.SetRow( r
.aStart
.Row() ); // don't overwrite sheet number as parsed in Parse_XL_Header()
953 if ( bOnlyAcceptSingle
)
959 // any trailing invalid character must invalidate the address.
960 nFlags
&= ~(SCA_VALID
| SCA_VALID_COL
| SCA_VALID_ROW
| SCA_VALID_TAB
);
965 tmp2
= lcl_eatWhiteSpace( tmp2
);
968 // Sheet1:Sheet2!C4 is a valid range, without a second sheet it is
969 // not. Any trailing invalid character invalidates the range.
970 if (*tmp2
== 0 && (nFlags
& SCA_TAB2_3D
))
972 if (nFlags
& SCA_COL_ABSOLUTE
)
973 nFlags
|= SCA_COL2_ABSOLUTE
;
974 if (nFlags
& SCA_ROW_ABSOLUTE
)
975 nFlags
|= SCA_ROW2_ABSOLUTE
;
978 nFlags
&= ~(SCA_VALID
|
979 SCA_VALID_COL
| SCA_VALID_ROW
| SCA_VALID_TAB
|
980 SCA_VALID_COL2
| SCA_VALID_ROW2
| SCA_VALID_TAB2
);
985 p
= lcl_eatWhiteSpace( p
+1 );
986 tmp1
= lcl_a1_get_col( p
, &r
.aEnd
, &nFlags2
);
987 if( !tmp1
&& aEndTabName
.isEmpty() ) // Probably the aEndTabName was specified after the first range
989 p
= lcl_XL_ParseSheetRef( p
, aEndTabName
, false, NULL
);
993 if( !aEndTabName
.isEmpty() && pDoc
->GetTable( aEndTabName
, nTab
) )
995 r
.aEnd
.SetTab( nTab
);
996 nFlags
|= SCA_VALID_TAB2
| SCA_TAB2_3D
| SCA_TAB2_ABSOLUTE
;
998 p
= lcl_eatWhiteSpace( p
+1 );
999 tmp1
= lcl_a1_get_col( p
, &r
.aEnd
, &nFlags2
);
1002 if( !tmp1
) // strange, but valid singleton
1005 tmp2
= lcl_a1_get_row( tmp1
, &r
.aEnd
, &nFlags2
);
1006 if( !tmp2
) // strange, but valid singleton
1011 // any trailing invalid character must invalidate the range.
1012 nFlags
&= ~(SCA_VALID
| SCA_VALID_COL
| SCA_VALID_ROW
| SCA_VALID_TAB
|
1013 SCA_VALID_COL2
| SCA_VALID_ROW2
| SCA_VALID_TAB2
);
1017 nFlags
|= (nFlags2
<< 4);
1022 @param pRange pointer to range where rAddr effectively is *pRange->aEnd,
1023 used in conjunction with pExtInfo to determine the tab span
1027 lcl_ScAddress_Parse_OOo( const sal_Unicode
* p
, ScDocument
* pDoc
, ScAddress
& rAddr
,
1028 ScAddress::ExternalInfo
* pExtInfo
= NULL
, ScRange
* pRange
= NULL
)
1030 sal_uInt16 nRes
= 0;
1031 OUString aDocName
; // der pure Dokumentenname
1033 bool bExtDoc
= false;
1034 bool bExtDocInherited
= false;
1035 const ScAddress
aCurPos(rAddr
);
1037 // Lets see if this is a reference to something in an external file. A
1038 // document name is always quoted and has a trailing #.
1041 const sal_Unicode
* pStart
= p
;
1043 p
= parseQuotedName(p
, aTmp
);
1045 if (*p
++ == SC_COMPILER_FILE_TAB_SEP
)
1048 // This is not a document name. Perhaps a quoted relative table
1052 else if (pExtInfo
&& pExtInfo
->mbExternal
)
1054 // This is an external reference.
1055 bExtDoc
= bExtDocInherited
= true;
1061 sal_uInt16 nBits
= SCA_VALID_TAB
;
1062 const sal_Unicode
* q
;
1063 if ( ScGlobal::FindUnquoted( p
, '.') )
1067 nRes
|= SCA_TAB_ABSOLUTE
;
1069 nRes
|= SCA_TAB_ABSOLUTE
, p
++;
1073 // Tokens that start at ' can have anything in them until a final
1074 // ' but '' marks an escaped '. We've earlier guaranteed that a
1075 // string containing '' will be surrounded by '.
1076 p
= parseQuotedName(p
, aTab
);
1089 aTab
+= OUString(*p
);
1096 if (!bExtDoc
&& (!pDoc
|| !pDoc
->GetTable( aTab
, nTab
)))
1098 // Specified table name is not found in this document. Assume this is an external document.
1100 sal_Int32 n
= aDocName
.lastIndexOf('.');
1101 if (n
!= -1 && n
> 0)
1103 // Extension found. Strip it.
1104 aTab
= aTab
.replaceAt(n
, 1, "");
1108 // No extension found. This is probably not an external document.
1114 if (bExtDoc
&& !bExtDocInherited
)
1115 return nRes
; // After a document a sheet must follow.
1123 nBits
= SCA_VALID_COL
;
1125 nBits
|= SCA_COL_ABSOLUTE
, p
++;
1127 if (rtl::isAsciiAlpha( *p
))
1129 nCol
= sal::static_int_cast
<SCCOL
>( toupper( char(*p
++) ) - 'A' );
1130 while (nCol
< MAXCOL
&& rtl::isAsciiAlpha(*p
))
1131 nCol
= sal::static_int_cast
<SCCOL
>( ((nCol
+ 1) * 26) + toupper( char(*p
++) ) - 'A' );
1136 if( nCol
> MAXCOL
|| rtl::isAsciiAlpha( *p
) )
1146 nBits
= SCA_VALID_ROW
;
1148 nBits
|= SCA_ROW_ABSOLUTE
, p
++;
1149 if( !rtl::isAsciiDigit( *p
) )
1157 long n
= aTmp
.toInt32() - 1;
1158 while (rtl::isAsciiDigit( *p
))
1160 if( n
< 0 || n
> MAXROW
)
1162 nRow
= static_cast<SCROW
>(n
);
1169 rAddr
.Set( nCol
, nRow
, nTab
);
1177 ScExternalRefManager
* pRefMgr
= pDoc
->GetExternalRefManager();
1179 // Need document name if inherited.
1180 if (bExtDocInherited
)
1182 const OUString
* pFileName
= pRefMgr
->getExternalFileName( pExtInfo
->mnFileId
);
1184 aDocName
= *pFileName
;
1188 pRefMgr
->convertToAbsName(aDocName
);
1190 if ((!pExtInfo
|| !pExtInfo
->mbExternal
) && pRefMgr
->isOwnDocument(aDocName
))
1192 if (!pDoc
->GetTable( aTab
, nTab
))
1196 rAddr
.SetTab( nTab
);
1197 nRes
|= SCA_VALID_TAB
;
1206 if (!pExtInfo
->mbExternal
)
1208 sal_uInt16 nFileId
= pRefMgr
->getExternalFileId(aDocName
);
1210 pExtInfo
->mbExternal
= true;
1211 pExtInfo
->maTabName
= aTab
;
1212 pExtInfo
->mnFileId
= nFileId
;
1214 if (pRefMgr
->getSingleRefToken(nFileId
, aTab
,
1215 ScAddress(nCol
, nRow
, 0), NULL
,
1218 rAddr
.SetTab( nTab
);
1219 nRes
|= SCA_VALID_TAB
;
1226 // This is a call for the second part of the reference,
1227 // we must have the range to adapt tab span.
1232 sal_uInt16 nFlags
= nRes
| SCA_VALID_TAB2
;
1233 if (!lcl_ScRange_External_TabSpan( *pRange
, nFlags
,
1235 pExtInfo
->maTabName
, aTab
, pDoc
))
1236 nRes
&= ~SCA_VALID_TAB
;
1239 if (nFlags
& SCA_VALID_TAB2
)
1241 rAddr
.SetTab( pRange
->aEnd
.Tab());
1242 nRes
|= SCA_VALID_TAB
;
1245 nRes
&= ~SCA_VALID_TAB
;
1254 if ( !(nRes
& SCA_VALID_ROW
) && (nRes
& SCA_VALID_COL
)
1255 && !( (nRes
& SCA_TAB_3D
) && (nRes
& SCA_VALID_TAB
)) )
1256 { // no Row, no Tab, but Col => DM (...), B (...) et al
1261 sal_uInt16 nMask
= nRes
& ( SCA_VALID_ROW
| SCA_VALID_COL
| SCA_VALID_TAB
);
1262 if( nMask
== ( SCA_VALID_ROW
| SCA_VALID_COL
| SCA_VALID_TAB
) )
1271 lcl_ScAddress_Parse ( const sal_Unicode
* p
, ScDocument
* pDoc
, ScAddress
& rAddr
,
1272 const ScAddress::Details
& rDetails
,
1273 ScAddress::ExternalInfo
* pExtInfo
= NULL
,
1274 const uno::Sequence
< const sheet::ExternalLinkInfo
> * pExternalLinks
= NULL
)
1279 switch (rDetails
.eConv
)
1282 case formula::FormulaGrammar::CONV_OOO
:
1284 return lcl_ScAddress_Parse_OOo( p
, pDoc
, rAddr
, pExtInfo
, NULL
);
1287 case formula::FormulaGrammar::CONV_XL_A1
:
1288 case formula::FormulaGrammar::CONV_XL_OOX
:
1291 sal_uInt16 nFlags
= lcl_ScRange_Parse_XL_A1( r
, p
, pDoc
, true, pExtInfo
,
1292 (rDetails
.eConv
== formula::FormulaGrammar::CONV_XL_OOX
? pExternalLinks
: NULL
) );
1296 case formula::FormulaGrammar::CONV_XL_R1C1
:
1299 sal_uInt16 nFlags
= lcl_ScRange_Parse_XL_R1C1( r
, p
, pDoc
, rDetails
, true, pExtInfo
);
1306 bool ConvertSingleRef( ScDocument
* pDoc
, const OUString
& rRefString
,
1307 SCTAB nDefTab
, ScRefAddress
& rRefAddress
,
1308 const ScAddress::Details
& rDetails
,
1309 ScAddress::ExternalInfo
* pExtInfo
/* = NULL */ )
1312 if (pExtInfo
|| (ScGlobal::FindUnquoted( rRefString
, SC_COMPILER_FILE_TAB_SEP
) == -1))
1314 ScAddress
aAddr( 0, 0, nDefTab
);
1315 sal_uInt16 nRes
= aAddr
.Parse( rRefString
, pDoc
, rDetails
, pExtInfo
);
1316 if ( nRes
& SCA_VALID
)
1318 rRefAddress
.Set( aAddr
,
1319 ((nRes
& SCA_COL_ABSOLUTE
) == 0),
1320 ((nRes
& SCA_ROW_ABSOLUTE
) == 0),
1321 ((nRes
& SCA_TAB_ABSOLUTE
) == 0));
1328 bool ConvertDoubleRef( ScDocument
* pDoc
, const OUString
& rRefString
, SCTAB nDefTab
,
1329 ScRefAddress
& rStartRefAddress
, ScRefAddress
& rEndRefAddress
,
1330 const ScAddress::Details
& rDetails
,
1331 ScAddress::ExternalInfo
* pExtInfo
/* = NULL */ )
1334 if (pExtInfo
|| (ScGlobal::FindUnquoted( rRefString
, SC_COMPILER_FILE_TAB_SEP
) == -1))
1336 ScRange
aRange( ScAddress( 0, 0, nDefTab
));
1337 sal_uInt16 nRes
= aRange
.Parse( rRefString
, pDoc
, rDetails
, pExtInfo
);
1338 if ( nRes
& SCA_VALID
)
1340 rStartRefAddress
.Set( aRange
.aStart
,
1341 ((nRes
& SCA_COL_ABSOLUTE
) == 0),
1342 ((nRes
& SCA_ROW_ABSOLUTE
) == 0),
1343 ((nRes
& SCA_TAB_ABSOLUTE
) == 0));
1344 rEndRefAddress
.Set( aRange
.aEnd
,
1345 ((nRes
& SCA_COL2_ABSOLUTE
) == 0),
1346 ((nRes
& SCA_ROW2_ABSOLUTE
) == 0),
1347 ((nRes
& SCA_TAB2_ABSOLUTE
) == 0));
1354 sal_uInt16
ScAddress::Parse( const OUString
& r
, ScDocument
* pDoc
,
1355 const Details
& rDetails
,
1356 ExternalInfo
* pExtInfo
,
1357 const uno::Sequence
< const sheet::ExternalLinkInfo
> * pExternalLinks
)
1359 return lcl_ScAddress_Parse( r
.getStr(), pDoc
, *this, rDetails
, pExtInfo
, pExternalLinks
);
1362 bool ScRange::Intersects( const ScRange
& r
) const
1365 std::min( aEnd
.Col(), r
.aEnd
.Col() ) < std::max( aStart
.Col(), r
.aStart
.Col() )
1366 || std::min( aEnd
.Row(), r
.aEnd
.Row() ) < std::max( aStart
.Row(), r
.aStart
.Row() )
1367 || std::min( aEnd
.Tab(), r
.aEnd
.Tab() ) < std::max( aStart
.Tab(), r
.aStart
.Tab() )
1371 void ScRange::PutInOrder()
1373 SCCOL nCol1
= aStart
.Col(), nCol2
= aEnd
.Col();
1374 SCROW nRow1
= aStart
.Row(), nRow2
= aEnd
.Row();
1375 SCTAB nTab1
= aStart
.Tab(), nTab2
= aEnd
.Tab();
1377 ::PutInOrder(nCol1
, nCol2
);
1378 ::PutInOrder(nRow1
, nRow2
);
1379 ::PutInOrder(nTab1
, nTab2
);
1381 aStart
.SetCol(nCol1
);
1382 aStart
.SetRow(nRow1
);
1383 aStart
.SetTab(nTab1
);
1390 void ScRange::Justify()
1393 if ( aEnd
.Col() < (nTempCol
= aStart
.Col()) )
1395 aStart
.SetCol(aEnd
.Col()); aEnd
.SetCol(nTempCol
);
1398 if ( aEnd
.Row() < (nTempRow
= aStart
.Row()) )
1400 aStart
.SetRow(aEnd
.Row()); aEnd
.SetRow(nTempRow
);
1403 if ( aEnd
.Tab() < (nTempTab
= aStart
.Tab()) )
1405 aStart
.SetTab(aEnd
.Tab()); aEnd
.SetTab(nTempTab
);
1409 void ScRange::ExtendTo( const ScRange
& rRange
)
1411 OSL_ENSURE( rRange
.IsValid(), "ScRange::ExtendTo - cannot extend to invalid range" );
1414 aStart
.SetCol( ::std::min( aStart
.Col(), rRange
.aStart
.Col() ) );
1415 aStart
.SetRow( ::std::min( aStart
.Row(), rRange
.aStart
.Row() ) );
1416 aStart
.SetTab( ::std::min( aStart
.Tab(), rRange
.aStart
.Tab() ) );
1417 aEnd
.SetCol( ::std::max( aEnd
.Col(), rRange
.aEnd
.Col() ) );
1418 aEnd
.SetRow( ::std::max( aEnd
.Row(), rRange
.aEnd
.Row() ) );
1419 aEnd
.SetTab( ::std::max( aEnd
.Tab(), rRange
.aEnd
.Tab() ) );
1426 lcl_ScRange_Parse_OOo( ScRange
&aRange
, const OUString
& r
, ScDocument
* pDoc
, ScAddress::ExternalInfo
* pExtInfo
= NULL
)
1428 sal_uInt16 nRes1
= 0, nRes2
= 0;
1429 sal_Int32 nPos
= ScGlobal::FindUnquoted( r
, ':');
1432 OUStringBuffer
aTmp(r
);
1434 const sal_Unicode
* p
= aTmp
.getStr();
1435 if( (nRes1
= lcl_ScAddress_Parse_OOo( p
, pDoc
, aRange
.aStart
, pExtInfo
, NULL
) ) != 0 )
1437 aRange
.aEnd
= aRange
.aStart
; // sheet must be initialized identical to first sheet
1438 if ( (nRes2
= lcl_ScAddress_Parse_OOo( p
+ nPos
+ 1, pDoc
, aRange
.aEnd
, pExtInfo
, &aRange
) ) != 0 )
1440 // PutInOrder / Justify
1441 sal_uInt16 nMask
, nBits1
, nBits2
;
1443 if ( aRange
.aEnd
.Col() < (nTempCol
= aRange
.aStart
.Col()) )
1445 aRange
.aStart
.SetCol(aRange
.aEnd
.Col()); aRange
.aEnd
.SetCol(nTempCol
);
1446 nMask
= (SCA_VALID_COL
| SCA_COL_ABSOLUTE
);
1447 nBits1
= nRes1
& nMask
;
1448 nBits2
= nRes2
& nMask
;
1449 nRes1
= (nRes1
& ~nMask
) | nBits2
;
1450 nRes2
= (nRes2
& ~nMask
) | nBits1
;
1453 if ( aRange
.aEnd
.Row() < (nTempRow
= aRange
.aStart
.Row()) )
1455 aRange
.aStart
.SetRow(aRange
.aEnd
.Row()); aRange
.aEnd
.SetRow(nTempRow
);
1456 nMask
= (SCA_VALID_ROW
| SCA_ROW_ABSOLUTE
);
1457 nBits1
= nRes1
& nMask
;
1458 nBits2
= nRes2
& nMask
;
1459 nRes1
= (nRes1
& ~nMask
) | nBits2
;
1460 nRes2
= (nRes2
& ~nMask
) | nBits1
;
1463 if ( aRange
.aEnd
.Tab() < (nTempTab
= aRange
.aStart
.Tab()) )
1465 aRange
.aStart
.SetTab(aRange
.aEnd
.Tab()); aRange
.aEnd
.SetTab(nTempTab
);
1466 nMask
= (SCA_VALID_TAB
| SCA_TAB_ABSOLUTE
| SCA_TAB_3D
);
1467 nBits1
= nRes1
& nMask
;
1468 nBits2
= nRes2
& nMask
;
1469 nRes1
= (nRes1
& ~nMask
) | nBits2
;
1470 nRes2
= (nRes2
& ~nMask
) | nBits1
;
1472 if ( ((nRes1
& ( SCA_TAB_ABSOLUTE
| SCA_TAB_3D
))
1473 == ( SCA_TAB_ABSOLUTE
| SCA_TAB_3D
))
1474 && !(nRes2
& SCA_TAB_3D
) )
1475 nRes2
|= SCA_TAB_ABSOLUTE
;
1478 nRes1
= 0; // keine Tokens aus halben Sachen
1481 nRes1
= ( ( nRes1
| nRes2
) & SCA_VALID
)
1483 | ( ( nRes2
& SCA_BITS
) << 4 );
1487 sal_uInt16
ScRange::Parse( const OUString
& r
, ScDocument
* pDoc
,
1488 const ScAddress::Details
& rDetails
,
1489 ScAddress::ExternalInfo
* pExtInfo
,
1490 const uno::Sequence
< const sheet::ExternalLinkInfo
> * pExternalLinks
)
1495 switch (rDetails
.eConv
)
1498 case formula::FormulaGrammar::CONV_OOO
:
1499 return lcl_ScRange_Parse_OOo( *this, r
, pDoc
, pExtInfo
);
1501 case formula::FormulaGrammar::CONV_XL_A1
:
1502 case formula::FormulaGrammar::CONV_XL_OOX
:
1503 return lcl_ScRange_Parse_XL_A1( *this, r
.getStr(), pDoc
, false, pExtInfo
,
1504 (rDetails
.eConv
== formula::FormulaGrammar::CONV_XL_OOX
? pExternalLinks
: NULL
) );
1506 case formula::FormulaGrammar::CONV_XL_R1C1
:
1507 return lcl_ScRange_Parse_XL_R1C1( *this, r
.getStr(), pDoc
, rDetails
, false, pExtInfo
);
1511 // Accept a full range, or an address
1512 sal_uInt16
ScRange::ParseAny( const OUString
& r
, ScDocument
* pDoc
,
1513 const ScAddress::Details
& rDetails
)
1515 sal_uInt16 nRet
= Parse( r
, pDoc
, rDetails
);
1516 const sal_uInt16 nValid
= SCA_VALID
| SCA_VALID_COL2
| SCA_VALID_ROW2
|
1519 if ( (nRet
& nValid
) != nValid
)
1521 ScAddress
aAdr(aStart
);//initialize with currentPos as fallback for table number
1522 nRet
= aAdr
.Parse( r
, pDoc
, rDetails
);
1523 if ( nRet
& SCA_VALID
)
1524 aStart
= aEnd
= aAdr
;
1529 // Parse only full row references
1530 sal_uInt16
ScRange::ParseCols( const OUString
& rStr
, ScDocument
* pDoc
,
1531 const ScAddress::Details
& rDetails
)
1536 const sal_Unicode
* p
= rStr
.getStr();
1537 sal_uInt16 nRes
= 0, ignored
= 0;
1539 (void)pDoc
; // make compiler shutup we may need this later
1541 switch (rDetails
.eConv
)
1544 case formula::FormulaGrammar::CONV_OOO
: // No full col refs in OOO yet, assume XL notation
1545 case formula::FormulaGrammar::CONV_XL_A1
:
1546 case formula::FormulaGrammar::CONV_XL_OOX
:
1547 if (NULL
!= (p
= lcl_a1_get_col( p
, &aStart
, &ignored
) ) )
1551 if( NULL
!= (p
= lcl_a1_get_col( p
+1, &aEnd
, &ignored
)))
1553 nRes
= SCA_VALID_COL
;
1559 nRes
= SCA_VALID_COL
;
1564 case formula::FormulaGrammar::CONV_XL_R1C1
:
1565 if ((p
[0] == 'C' || p
[0] != 'c') &&
1566 NULL
!= (p
= lcl_r1c1_get_col( p
, rDetails
, &aStart
, &ignored
)))
1570 if( (p
[1] == 'C' || p
[1] == 'c') &&
1571 NULL
!= (p
= lcl_r1c1_get_col( p
+1, rDetails
, &aEnd
, &ignored
)))
1573 nRes
= SCA_VALID_COL
;
1579 nRes
= SCA_VALID_COL
;
1585 return (p
!= NULL
&& *p
== '\0') ? nRes
: 0;
1588 // Parse only full row references
1589 sal_uInt16
ScRange::ParseRows( const OUString
& rStr
, ScDocument
* pDoc
,
1590 const ScAddress::Details
& rDetails
)
1595 const sal_Unicode
* p
= rStr
.getStr();
1596 sal_uInt16 nRes
= 0, ignored
= 0;
1598 (void)pDoc
; // make compiler shutup we may need this later
1600 switch (rDetails
.eConv
)
1603 case formula::FormulaGrammar::CONV_OOO
: // No full row refs in OOO yet, assume XL notation
1604 case formula::FormulaGrammar::CONV_XL_A1
:
1605 case formula::FormulaGrammar::CONV_XL_OOX
:
1606 if (NULL
!= (p
= lcl_a1_get_row( p
, &aStart
, &ignored
) ) )
1610 if( NULL
!= (p
= lcl_a1_get_row( p
+1, &aEnd
, &ignored
)))
1612 nRes
= SCA_VALID_COL
;
1618 nRes
= SCA_VALID_COL
;
1623 case formula::FormulaGrammar::CONV_XL_R1C1
:
1624 if ((p
[0] == 'R' || p
[0] != 'r') &&
1625 NULL
!= (p
= lcl_r1c1_get_row( p
, rDetails
, &aStart
, &ignored
)))
1629 if( (p
[1] == 'R' || p
[1] == 'r') &&
1630 NULL
!= (p
= lcl_r1c1_get_row( p
+1, rDetails
, &aEnd
, &ignored
)))
1632 nRes
= SCA_VALID_COL
;
1638 nRes
= SCA_VALID_COL
;
1644 return (p
!= NULL
&& *p
== '\0') ? nRes
: 0;
1648 lcl_a1_append_c ( OUString
&r
, int nCol
, bool bIsAbs
)
1652 ScColToAlpha( r
, sal::static_int_cast
<SCCOL
>(nCol
) );
1656 lcl_a1_append_r ( OUString
&r
, int nRow
, bool bIsAbs
)
1660 r
+= OUString::number( nRow
+1 );
1664 lcl_r1c1_append_c ( OUString
&r
, int nCol
, bool bIsAbs
,
1665 const ScAddress::Details
& rDetails
)
1670 r
+= OUString::number( nCol
+ 1 );
1674 nCol
-= rDetails
.nCol
;
1676 r
+= "[" + OUString::number( nCol
) + "]";
1681 lcl_r1c1_append_r ( OUString
&r
, int nRow
, bool bIsAbs
,
1682 const ScAddress::Details
& rDetails
)
1687 r
+= OUString::number( nRow
+ 1 );
1691 nRow
-= rDetails
.nRow
;
1693 r
+= "[" + OUString::number( nRow
) + "]";
1699 getFileNameFromDoc( const ScDocument
* pDoc
)
1701 // TODO : er points at ScGlobal::GetAbsDocName()
1702 // as a better template. Look into it
1704 SfxObjectShell
* pShell
;
1707 NULL
!= (pShell
= pDoc
->GetDocumentShell() ) )
1709 uno::Reference
< frame::XModel
> xModel( pShell
->GetModel(), uno::UNO_QUERY
);
1712 if( !xModel
->getURL().isEmpty() )
1714 INetURLObject
aURL( xModel
->getURL() );
1715 sFileName
= aURL
.GetLastName();
1718 sFileName
= pShell
->GetTitle();
1724 OUString
ScAddress::Format(sal_uInt16 nFlags
, const ScDocument
* pDoc
,
1725 const Details
& rDetails
) const
1728 if( nFlags
& SCA_VALID
)
1729 nFlags
|= ( SCA_VALID_ROW
| SCA_VALID_COL
| SCA_VALID_TAB
);
1730 if( pDoc
&& (nFlags
& SCA_VALID_TAB
) )
1732 if ( nTab
>= pDoc
->GetTableCount() )
1734 return ScGlobal::GetRscString( STR_NOREF_STR
);
1736 if( nFlags
& SCA_TAB_3D
)
1738 OUString aTabName
, aDocName
;
1740 pDoc
->GetName(nTab
, aTmp
);
1741 aTabName
= aTmp
; // TODO: remove use of String here.
1742 // External Reference, same as in ScCompiler::MakeTabStr()
1743 if( aTabName
[0] == '\'' )
1745 sal_Int32 nPos
= ScCompiler::GetDocTabPos( aTabName
);
1748 aDocName
= aTabName
.copy( 0, nPos
+ 1 );
1749 aTabName
= aTabName
.copy( nPos
+ 1 );
1752 else if( nFlags
& SCA_FORCE_DOC
)
1754 // VBA has an 'external' flag that forces the addition of the
1755 // tab name _and_ the doc name. The VBA code would be
1756 // needlessly complicated if it constructed an actual external
1757 // reference so we add this somewhat cheesy kludge to force the
1758 // addition of the document name even for non-external references
1759 aDocName
= getFileNameFromDoc( pDoc
);
1761 ScCompiler::CheckTabQuotes( aTabName
, rDetails
.eConv
);
1763 switch( rDetails
.eConv
)
1766 case formula::FormulaGrammar::CONV_OOO
:
1768 if( nFlags
& SCA_TAB_ABSOLUTE
)
1774 case formula::FormulaGrammar::CONV_XL_A1
:
1775 case formula::FormulaGrammar::CONV_XL_R1C1
:
1776 case formula::FormulaGrammar::CONV_XL_OOX
:
1777 if (!aDocName
.isEmpty())
1779 r
+= "[" + aDocName
+ "]";
1787 switch( rDetails
.eConv
)
1790 case formula::FormulaGrammar::CONV_OOO
:
1791 case formula::FormulaGrammar::CONV_XL_A1
:
1792 case formula::FormulaGrammar::CONV_XL_OOX
:
1793 if( nFlags
& SCA_VALID_COL
)
1794 lcl_a1_append_c ( r
, nCol
, nFlags
& SCA_COL_ABSOLUTE
);
1795 if( nFlags
& SCA_VALID_ROW
)
1796 lcl_a1_append_r ( r
, nRow
, nFlags
& SCA_ROW_ABSOLUTE
);
1799 case formula::FormulaGrammar::CONV_XL_R1C1
:
1800 if( nFlags
& SCA_VALID_ROW
)
1801 lcl_r1c1_append_r ( r
, nRow
, nFlags
& SCA_ROW_ABSOLUTE
, rDetails
);
1802 if( nFlags
& SCA_VALID_COL
)
1803 lcl_r1c1_append_c ( r
, nCol
, nFlags
& SCA_COL_ABSOLUTE
, rDetails
);
1810 lcl_Split_DocTab( const ScDocument
* pDoc
, SCTAB nTab
,
1811 const ScAddress::Details
& rDetails
,
1813 OUString
& rTabName
, OUString
& rDocName
)
1815 pDoc
->GetName(nTab
, rTabName
);
1817 // External reference, same as in ScCompiler::MakeTabStr()
1818 if ( rTabName
[0] == '\'' )
1820 sal_Int32 nPos
= ScCompiler::GetDocTabPos( rTabName
);
1823 rDocName
= rTabName
.copy( 0, nPos
+ 1 );
1824 rTabName
= rTabName
.copy( nPos
+ 1 );
1827 else if( nFlags
& SCA_FORCE_DOC
)
1829 // VBA has an 'external' flag that forces the addition of the
1830 // tab name _and_ the doc name. The VBA code would be
1831 // needlessly complicated if it constructed an actual external
1832 // reference so we add this somewhat cheesy kludge to force the
1833 // addition of the document name even for non-external references
1834 rDocName
= getFileNameFromDoc( pDoc
);
1836 ScCompiler::CheckTabQuotes( rTabName
, rDetails
.eConv
);
1840 lcl_ScRange_Format_XL_Header( OUString
& r
, const ScRange
& rRange
,
1841 sal_uInt16 nFlags
, const ScDocument
* pDoc
,
1842 const ScAddress::Details
& rDetails
)
1844 if( nFlags
& SCA_TAB_3D
)
1846 OUString aTabName
, aDocName
;
1847 lcl_Split_DocTab( pDoc
, rRange
.aStart
.Tab(), rDetails
, nFlags
,
1848 aTabName
, aDocName
);
1849 if( !aDocName
.isEmpty() )
1851 r
+= "[" + aDocName
+ "]";
1855 if( nFlags
& SCA_TAB2_3D
)
1857 lcl_Split_DocTab( pDoc
, rRange
.aEnd
.Tab(), rDetails
, nFlags
,
1858 aTabName
, aDocName
);
1866 OUString
ScRange::Format( sal_uInt16 nFlags
, const ScDocument
* pDoc
,
1867 const ScAddress::Details
& rDetails
) const
1869 if( !( nFlags
& SCA_VALID
) )
1871 return ScGlobal::GetRscString( STR_NOREF_STR
);
1875 #define absrel_differ(nFlags, mask) (((nFlags) & (mask)) ^ (((nFlags) >> 4) & (mask)))
1876 switch( rDetails
.eConv
) {
1878 case formula::FormulaGrammar::CONV_OOO
: {
1879 bool bOneTab
= (aStart
.Tab() == aEnd
.Tab());
1881 nFlags
|= SCA_TAB_3D
;
1882 r
= aStart
.Format(nFlags
, pDoc
, rDetails
);
1883 if( aStart
!= aEnd
||
1884 absrel_differ( nFlags
, SCA_COL_ABSOLUTE
) ||
1885 absrel_differ( nFlags
, SCA_ROW_ABSOLUTE
))
1887 nFlags
= ( nFlags
& SCA_VALID
) | ( ( nFlags
>> 4 ) & 0x070F );
1891 nFlags
|= SCA_TAB_3D
;
1892 OUString
aName(aEnd
.Format(nFlags
, pDoc
, rDetails
));
1899 case formula::FormulaGrammar::CONV_XL_A1
:
1900 case formula::FormulaGrammar::CONV_XL_OOX
:
1901 lcl_ScRange_Format_XL_Header( r
, *this, nFlags
, pDoc
, rDetails
);
1902 if( aStart
.Col() == 0 && aEnd
.Col() >= MAXCOL
)
1904 // Full col refs always require 2 rows (2:2)
1905 lcl_a1_append_r( r
, aStart
.Row(), nFlags
& SCA_ROW_ABSOLUTE
);
1907 lcl_a1_append_r( r
, aEnd
.Row(), nFlags
& SCA_ROW2_ABSOLUTE
);
1909 else if( aStart
.Row() == 0 && aEnd
.Row() >= MAXROW
)
1911 // Full row refs always require 2 cols (A:A)
1912 lcl_a1_append_c( r
, aStart
.Col(), nFlags
& SCA_COL_ABSOLUTE
);
1914 lcl_a1_append_c( r
, aEnd
.Col(), nFlags
& SCA_COL2_ABSOLUTE
);
1918 lcl_a1_append_c ( r
, aStart
.Col(), nFlags
& SCA_COL_ABSOLUTE
);
1919 lcl_a1_append_r ( r
, aStart
.Row(), nFlags
& SCA_ROW_ABSOLUTE
);
1920 if( aStart
.Col() != aEnd
.Col() ||
1921 absrel_differ( nFlags
, SCA_COL_ABSOLUTE
) ||
1922 aStart
.Row() != aEnd
.Row() ||
1923 absrel_differ( nFlags
, SCA_ROW_ABSOLUTE
)) {
1925 lcl_a1_append_c ( r
, aEnd
.Col(), nFlags
& SCA_COL2_ABSOLUTE
);
1926 lcl_a1_append_r ( r
, aEnd
.Row(), nFlags
& SCA_ROW2_ABSOLUTE
);
1931 case formula::FormulaGrammar::CONV_XL_R1C1
:
1932 lcl_ScRange_Format_XL_Header( r
, *this, nFlags
, pDoc
, rDetails
);
1933 if( aStart
.Col() == 0 && aEnd
.Col() >= MAXCOL
)
1935 lcl_r1c1_append_r( r
, aStart
.Row(), nFlags
& SCA_ROW_ABSOLUTE
, rDetails
);
1936 if( aStart
.Row() != aEnd
.Row() ||
1937 absrel_differ( nFlags
, SCA_ROW_ABSOLUTE
)) {
1939 lcl_r1c1_append_r( r
, aEnd
.Row(), nFlags
& SCA_ROW2_ABSOLUTE
, rDetails
);
1942 else if( aStart
.Row() == 0 && aEnd
.Row() >= MAXROW
)
1944 lcl_r1c1_append_c( r
, aStart
.Col(), nFlags
& SCA_COL_ABSOLUTE
, rDetails
);
1945 if( aStart
.Col() != aEnd
.Col() ||
1946 absrel_differ( nFlags
, SCA_COL_ABSOLUTE
)) {
1948 lcl_r1c1_append_c( r
, aEnd
.Col(), nFlags
& SCA_COL2_ABSOLUTE
, rDetails
);
1953 lcl_r1c1_append_r( r
, aStart
.Row(), nFlags
& SCA_ROW_ABSOLUTE
, rDetails
);
1954 lcl_r1c1_append_c( r
, aStart
.Col(), nFlags
& SCA_COL_ABSOLUTE
, rDetails
);
1955 if( aStart
.Col() != aEnd
.Col() ||
1956 absrel_differ( nFlags
, SCA_COL_ABSOLUTE
) ||
1957 aStart
.Row() != aEnd
.Row() ||
1958 absrel_differ( nFlags
, SCA_ROW_ABSOLUTE
)) {
1960 lcl_r1c1_append_r( r
, aEnd
.Row(), nFlags
& SCA_ROW2_ABSOLUTE
, rDetails
);
1961 lcl_r1c1_append_c( r
, aEnd
.Col(), nFlags
& SCA_COL2_ABSOLUTE
, rDetails
);
1965 #undef absrel_differ
1969 bool ScAddress::Move( SCsCOL dx
, SCsROW dy
, SCsTAB dz
, ScDocument
* pDoc
)
1971 SCsTAB nMaxTab
= pDoc
? pDoc
->GetTableCount() : MAXTAB
;
1977 dx
= 0, bValid
= false;
1978 else if( dx
> MAXCOL
)
1979 dx
= MAXCOL
, bValid
=false;
1981 dy
= 0, bValid
= false;
1982 else if( dy
> MAXROW
)
1983 dy
= MAXROW
, bValid
=false;
1985 dz
= 0, bValid
= false;
1986 else if( dz
> nMaxTab
)
1987 dz
= nMaxTab
, bValid
=false;
1992 bool ScRange::Move( SCsCOL dx
, SCsROW dy
, SCsTAB dz
, ScDocument
* pDoc
)
1994 // single & to process both
1995 return aStart
.Move( dx
, dy
, dz
, pDoc
) & aEnd
.Move( dx
, dy
, dz
, pDoc
);
1998 OUString
ScAddress::GetColRowString( bool bAbsolute
,
1999 const Details
& rDetails
) const
2003 switch( rDetails
.eConv
)
2006 case formula::FormulaGrammar::CONV_OOO
:
2007 case formula::FormulaGrammar::CONV_XL_A1
:
2008 case formula::FormulaGrammar::CONV_XL_OOX
:
2012 ScColToAlpha( aString
, nCol
);
2017 aString
+= OUString::number(nRow
+1);
2020 case formula::FormulaGrammar::CONV_XL_R1C1
:
2021 lcl_r1c1_append_r ( aString
, nRow
, bAbsolute
, rDetails
);
2022 lcl_r1c1_append_c ( aString
, nCol
, bAbsolute
, rDetails
);
2029 OUString
ScRefAddress::GetRefString( ScDocument
* pDoc
, SCTAB nActTab
,
2030 const ScAddress::Details
& rDetails
) const
2033 return EMPTY_OUSTRING
;
2034 if ( Tab()+1 > pDoc
->GetTableCount() )
2035 return ScGlobal::GetRscString( STR_NOREF_STR
);
2037 sal_uInt16 nFlags
= SCA_VALID
;
2038 if ( nActTab
!= Tab() )
2040 nFlags
|= SCA_TAB_3D
;
2042 nFlags
|= SCA_TAB_ABSOLUTE
;
2045 nFlags
|= SCA_COL_ABSOLUTE
;
2047 nFlags
|= SCA_ROW_ABSOLUTE
;
2049 return aAdr
.Format(nFlags
, pDoc
, rDetails
);
2052 void ScColToAlpha( OUStringBuffer
& rBuf
, SCCOL nCol
)
2057 rBuf
.append( static_cast<sal_Unicode
>( 'A' +
2058 static_cast<sal_uInt16
>(nCol
)));
2061 rBuf
.append( static_cast<sal_Unicode
>( 'A' +
2062 (static_cast<sal_uInt16
>(nCol
) / 26) - 1));
2063 rBuf
.append( static_cast<sal_Unicode
>( 'A' +
2064 (static_cast<sal_uInt16
>(nCol
) % 26)));
2072 SCCOL nC
= nCol
% 26;
2073 aStr
+= OUString( static_cast<sal_Unicode
>( 'A' +
2074 static_cast<sal_uInt16
>(nC
)));
2075 nCol
= sal::static_int_cast
<SCCOL
>( nCol
- nC
);
2076 nCol
= nCol
/ 26 - 1;
2078 aStr
+= OUString(static_cast<sal_Unicode
>( 'A' +
2079 static_cast<sal_uInt16
>(nCol
)));
2080 rBuf
.append(comphelper::string::reverseString(aStr
));
2084 bool AlphaToCol( SCCOL
& rCol
, const OUString
& rStr
)
2087 sal_Int32 nStop
= rStr
.getLength();
2090 while (nResult
<= MAXCOL
&& nPos
< nStop
&& (c
= rStr
[nPos
]) != 0 &&
2091 rtl::isAsciiAlpha(c
))
2094 nResult
= (nResult
+ 1) * 26;
2095 nResult
+= ScGlobal::ToUpperAlpha(c
) - 'A';
2098 bool bOk
= (ValidCol(nResult
) && nPos
> 0);
2104 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */