| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_sc.hxx" |
| |
| #include "address.hxx" |
| #include "global.hxx" |
| #include "compiler.hxx" |
| #include "document.hxx" |
| #include "externalrefmgr.hxx" |
| |
| #include "globstr.hrc" |
| #include <sal/alloca.h> |
| |
| #include <com/sun/star/frame/XModel.hpp> |
| #include <com/sun/star/beans/XPropertySet.hpp> |
| #include <com/sun/star/sheet/ExternalLinkInfo.hpp> |
| #include <com/sun/star/sheet/ExternalLinkType.hpp> |
| #include <sfx2/objsh.hxx> |
| #include <tools/urlobj.hxx> |
| using namespace ::com::sun::star; |
| |
| //////////////////////////////////////////////////////////////////////////// |
| const ScAddress::Details ScAddress::detailsOOOa1( formula::FormulaGrammar::CONV_OOO, 0, 0 ); |
| |
| ScAddress::Details::Details ( const ScDocument* pDoc, |
| const ScAddress & rAddr ) : |
| eConv( pDoc->GetAddressConvention() ), |
| nRow( rAddr.Row() ), |
| nCol( rAddr.Col() ) |
| { |
| } |
| |
| //UNUSED2009-05 void ScAddress::Details::SetPos ( const ScDocument* pDoc, |
| //UNUSED2009-05 const ScAddress & rAddr ) |
| //UNUSED2009-05 { |
| //UNUSED2009-05 nRow = rAddr.Row(); |
| //UNUSED2009-05 nCol = rAddr.Col(); |
| //UNUSED2009-05 eConv = pDoc->GetAddressConvention(); |
| //UNUSED2009-05 } |
| |
| //////////////////////////////////////////////////////////////////////////// |
| |
| #include <iostream> |
| |
| /** |
| * Parse from the opening single quote to the closing single quote. Inside |
| * the quotes, a single quote character is encoded by double single-quote |
| * characters. |
| * |
| * @param p pointer to the first character to begin parsing. |
| * @param rName (reference) parsed name within the quotes. If the name is |
| * empty, either the parsing failed or it's an empty quote. |
| * |
| * @return pointer to the character immediately after the closing single |
| * quote. |
| */ |
| static const sal_Unicode* lcl_ParseQuotedName( const sal_Unicode* p, String& rName ) |
| { |
| rName.Erase(); |
| if (*p != '\'') |
| return p; |
| |
| const sal_Unicode* pStart = p; |
| sal_Unicode cPrev = 0; |
| for (++p; *p; ++p) |
| { |
| if (*p == '\'') |
| { |
| if (cPrev == '\'') |
| { |
| // double single-quote equals one single quote. |
| rName += *p; |
| cPrev = 0; |
| continue; |
| } |
| } |
| else if (cPrev == '\'') |
| // We are past the closing quote. We're done! |
| return p; |
| else |
| rName += *p; |
| cPrev = *p; |
| } |
| rName.Erase(); |
| return pStart; |
| } |
| |
| static long int |
| sal_Unicode_strtol ( const sal_Unicode* p, |
| const sal_Unicode** pEnd ) |
| { |
| long int accum = 0, prev = 0; |
| bool is_neg = false; |
| |
| if( *p == '-' ) |
| { |
| is_neg = true; |
| p++; |
| } |
| else if( *p == '+' ) |
| p++; |
| |
| while (CharClass::isAsciiDigit( *p )) |
| { |
| accum = accum * 10 + *p - '0'; |
| if( accum < prev ) |
| { |
| *pEnd = NULL; |
| return 0; |
| } |
| prev = accum; |
| p++; |
| } |
| |
| *pEnd = p; |
| return is_neg ? -accum : accum; |
| } |
| |
| const sal_Unicode* lcl_eatWhiteSpace( const sal_Unicode* p ) |
| { |
| if ( p ) |
| { |
| while( *p == ' ' ) |
| ++p; |
| } |
| return p; |
| } |
| |
| /** Determines the number of sheets an external reference spans and sets |
| rRange.aEnd.nTab accordingly. If a sheet is not found, the corresponding |
| bits in rFlags are cleared. pExtInfo is filled if it wasn't already. If in |
| cached order rStartTabName comes after rEndTabName, pExtInfo->maTabName |
| is set to rEndTabName. |
| @returns <FALSE/> if pExtInfo is already filled and rExternDocName does not |
| result in the identical file ID. Else <TRUE/>. |
| */ |
| static bool lcl_ScRange_External_TabSpan( |
| ScRange & rRange, |
| sal_uInt16 & rFlags, |
| ScAddress::ExternalInfo* pExtInfo, |
| const String & rExternDocName, |
| const String & rStartTabName, |
| const String & rEndTabName, |
| ScDocument* pDoc ) |
| { |
| if (!rExternDocName.Len()) |
| return !pExtInfo || !pExtInfo->mbExternal; |
| |
| ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); |
| if (pRefMgr->isOwnDocument( rExternDocName)) |
| return !pExtInfo || !pExtInfo->mbExternal; |
| |
| sal_uInt16 nFileId = pRefMgr->getExternalFileId( rExternDocName); |
| |
| if (pExtInfo) |
| { |
| if (pExtInfo->mbExternal) |
| { |
| if (pExtInfo->mnFileId != nFileId) |
| return false; |
| } |
| else |
| { |
| pExtInfo->mbExternal = true; |
| pExtInfo->maTabName = rStartTabName; |
| pExtInfo->mnFileId = nFileId; |
| } |
| } |
| |
| if (!rEndTabName.Len() || rStartTabName == rEndTabName) |
| { |
| rRange.aEnd.SetTab( rRange.aStart.Tab()); |
| return true; |
| } |
| |
| SCsTAB nSpan = pRefMgr->getCachedTabSpan( nFileId, rStartTabName, rEndTabName); |
| if (nSpan == -1) |
| rFlags &= ~(SCA_VALID_TAB | SCA_VALID_TAB2); |
| else if (nSpan == 0) |
| rFlags &= ~SCA_VALID_TAB2; |
| else if (nSpan >= 1) |
| rRange.aEnd.SetTab( rRange.aStart.Tab() + nSpan - 1); |
| else // (nSpan < -1) |
| { |
| rRange.aEnd.SetTab( rRange.aStart.Tab() - nSpan - 1); |
| if (pExtInfo) |
| pExtInfo->maTabName = rEndTabName; |
| } |
| return true; |
| } |
| |
| /** Returns NULL if the string should be a sheet name, but is invalid. |
| Returns a pointer to the first character after the sheet name, if there was |
| any, else pointer to start. |
| @param pMsoxlQuoteStop |
| Starting _within_ a quoted name, but still may be 3D; quoted name stops |
| at pMsoxlQuoteStop |
| */ |
| static const sal_Unicode * |
| lcl_XL_ParseSheetRef( const sal_Unicode* start, |
| String& rExternTabName, |
| bool allow_3d, |
| const sal_Unicode* pMsoxlQuoteStop ) |
| { |
| String aTabName; |
| const sal_Unicode *p = start; |
| |
| // XL only seems to use single quotes for sheet names. |
| if (pMsoxlQuoteStop) |
| { |
| const sal_Unicode* pCurrentStart = p; |
| while (p < pMsoxlQuoteStop) |
| { |
| if (*p == '\'') |
| { |
| // We pre-analyzed the quoting, no checks needed here. |
| if (*++p == '\'') |
| { |
| aTabName.Append( pCurrentStart, |
| sal::static_int_cast<xub_StrLen>( p - pCurrentStart)); |
| pCurrentStart = ++p; |
| } |
| } |
| else if (*p == ':') |
| { |
| break; // while |
| } |
| else |
| ++p; |
| } |
| if (pCurrentStart < p) |
| aTabName.Append( pCurrentStart, sal::static_int_cast<xub_StrLen>( p - pCurrentStart)); |
| if (!aTabName.Len()) |
| return NULL; |
| if (p == pMsoxlQuoteStop) |
| ++p; // position on ! of ...'!... |
| if( *p != '!' && ( !allow_3d || *p != ':' ) ) |
| return (!allow_3d && *p == ':') ? p : start; |
| } |
| else if( *p == '\'') |
| { |
| p = lcl_ParseQuotedName(p, aTabName); |
| if (!aTabName.Len()) |
| return NULL; |
| } |
| else |
| { |
| bool only_digits = sal_True; |
| |
| /* |
| * Valid: Normal!a1 |
| * Valid: x.y!a1 |
| * Invalid: .y!a1 |
| * |
| * Some names starting with digits are actually valid, but |
| * unparse quoted. Things are quite tricky: most sheet names |
| * starting with a digit are ok, but not those starting with |
| * "[0-9]*\." or "[0-9]+[eE]". |
| * |
| * Valid: 42!a1 |
| * Valid: 4x!a1 |
| * Invalid: 1.!a1 |
| * Invalid: 1e!a1 |
| */ |
| while( 1 ) |
| { |
| const sal_Unicode uc = *p; |
| if( CharClass::isAsciiAlpha( uc ) || uc == '_' ) |
| { |
| if( only_digits && p != start && |
| (uc == 'e' || uc == 'E' ) ) |
| { |
| p = start; |
| break; |
| } |
| only_digits = sal_False; |
| p++; |
| } |
| else if( CharClass::isAsciiDigit( uc )) |
| { |
| p++; |
| } |
| else if( uc == '.' ) |
| { |
| if( only_digits ) // Valid, except after only digits. |
| { |
| p = start; |
| break; |
| } |
| p++; |
| } |
| else if (uc > 127) |
| { |
| // non ASCII character is allowed. |
| ++p; |
| } |
| else |
| break; |
| } |
| |
| if( *p != '!' && ( !allow_3d || *p != ':' ) ) |
| return (!allow_3d && *p == ':') ? p : start; |
| |
| aTabName.Append( start, sal::static_int_cast<xub_StrLen>( p - start ) ); |
| } |
| |
| rExternTabName = aTabName; |
| return p; |
| } |
| |
| |
| const sal_Unicode* ScRange::Parse_XL_Header( |
| const sal_Unicode* p, |
| const ScDocument* pDoc, |
| String& rExternDocName, |
| String& rStartTabName, |
| String& rEndTabName, |
| sal_uInt16& nFlags, |
| bool bOnlyAcceptSingle, |
| const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks ) |
| { |
| const sal_Unicode* startTabs, *start = p; |
| sal_uInt16 nSaveFlags = nFlags; |
| |
| // Is this an external reference ? |
| rStartTabName.Erase(); |
| rEndTabName.Erase(); |
| rExternDocName.Erase(); |
| const sal_Unicode* pMsoxlQuoteStop = NULL; |
| if (*p == '[') |
| { |
| ++p; |
| // Only single quotes are correct, and a double single quote escapes a |
| // single quote text inside the quoted text. |
| if (*p == '\'') |
| { |
| p = lcl_ParseQuotedName(p, rExternDocName); |
| if (!*p || *p != ']' || !rExternDocName.Len()) |
| { |
| rExternDocName.Erase(); |
| return start; |
| } |
| } |
| else |
| { |
| // non-quoted file name. |
| p = ScGlobal::UnicodeStrChr( start+1, ']' ); |
| if( p == NULL ) |
| return start; |
| rExternDocName.Append( start+1, sal::static_int_cast<xub_StrLen>( p-(start+1) ) ); |
| } |
| ++p; |
| |
| if (pExternalLinks && pExternalLinks->hasElements()) |
| { |
| // A numeric "document name" is an index into the sequence. |
| if (CharClass::isAsciiNumeric( rExternDocName)) |
| { |
| sal_Int32 i = rExternDocName.ToInt32(); |
| if (i < 0 || i >= pExternalLinks->getLength()) |
| return start; |
| const sheet::ExternalLinkInfo & rInfo = (*pExternalLinks)[i]; |
| switch (rInfo.Type) |
| { |
| case sheet::ExternalLinkType::DOCUMENT : |
| { |
| rtl::OUString aStr; |
| if (!(rInfo.Data >>= aStr)) |
| { |
| DBG_ERROR1( "ScRange::Parse_XL_Header: Data type mismatch for ExternalLinkInfo %d", i); |
| return NULL; |
| } |
| rExternDocName = aStr; |
| } |
| break; |
| case sheet::ExternalLinkType::SELF : |
| return start; // ??? |
| case sheet::ExternalLinkType::SPECIAL : |
| // silently return nothing (do not assert), caller has to handle this |
| return NULL; |
| default: |
| DBG_ERROR2( "ScRange::Parse_XL_Header: unhandled ExternalLinkType %d for index %d", |
| rInfo.Type, i); |
| return NULL; |
| } |
| } |
| } |
| rExternDocName = ScGlobal::GetAbsDocName(rExternDocName, pDoc->GetDocumentShell()); |
| } |
| else if (*p == '\'') |
| { |
| // Sickness in Excel's ODF msoxl namespace: |
| // 'E:\[EXTDATA8.XLS]Sheet1'!$A$7 or |
| // 'E:\[EXTDATA12B.XLSB]Sheet1:Sheet3'!$A$11 |
| // But, 'Sheet1'!B3 would also be a valid! |
| // Excel does not allow [ and ] characters in sheet names though. |
| p = lcl_ParseQuotedName(p, rExternDocName); |
| if (!*p || *p != '!') |
| { |
| rExternDocName.Erase(); |
| return start; |
| } |
| if (rExternDocName.Len()) |
| { |
| xub_StrLen nOpen = rExternDocName.Search( '['); |
| if (nOpen == STRING_NOTFOUND) |
| rExternDocName.Erase(); |
| else |
| { |
| xub_StrLen nClose = rExternDocName.Search( ']', nOpen+1); |
| if (nClose == STRING_NOTFOUND) |
| rExternDocName.Erase(); |
| else |
| { |
| rExternDocName.Erase( nClose); |
| rExternDocName.Erase( nOpen, 1); |
| pMsoxlQuoteStop = p - 1; // the ' quote char |
| // There may be embedded escaped quotes, just matching the |
| // doc name's length may not work. |
| for (p = start; *p != '['; ++p) |
| ; |
| for ( ; *p != ']'; ++p) |
| ; |
| ++p; |
| } |
| } |
| } |
| if (!rExternDocName.Len()) |
| p = start; |
| } |
| |
| startTabs = p; |
| p = lcl_XL_ParseSheetRef( p, rStartTabName, !bOnlyAcceptSingle, pMsoxlQuoteStop); |
| if( NULL == p ) |
| return start; // invalid tab |
| if (bOnlyAcceptSingle && *p == ':') |
| return NULL; // 3D |
| if( p != startTabs ) |
| { |
| nFlags |= SCA_VALID_TAB | SCA_TAB_3D | SCA_TAB_ABSOLUTE; |
| if( *p == ':' ) // 3d ref |
| { |
| p = lcl_XL_ParseSheetRef( p+1, rEndTabName, false, pMsoxlQuoteStop); |
| if( p == NULL ) |
| { |
| nFlags = nSaveFlags; |
| return start; // invalid tab |
| } |
| nFlags |= SCA_VALID_TAB2 | SCA_TAB2_3D | SCA_TAB2_ABSOLUTE; |
| } |
| else |
| { |
| // If only one sheet is given, the full reference is still valid, |
| // only the second 3D flag is not set. |
| nFlags |= SCA_VALID_TAB2 | SCA_TAB2_ABSOLUTE; |
| aEnd.SetTab( aStart.Tab() ); |
| } |
| |
| if( *p++ != '!' ) |
| { |
| nFlags = nSaveFlags; |
| return start; // syntax error |
| } |
| else |
| p = lcl_eatWhiteSpace( p ); |
| } |
| else |
| { |
| nFlags |= SCA_VALID_TAB | SCA_VALID_TAB2; |
| // Use the current tab, it needs to be passed in. : aEnd.SetTab( .. ); |
| } |
| |
| if (rExternDocName.Len()) |
| { |
| ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); |
| pRefMgr->convertToAbsName( rExternDocName); |
| } |
| else |
| { |
| // Internal reference. |
| if (!rStartTabName.Len()) |
| { |
| nFlags = nSaveFlags; |
| return start; |
| } |
| |
| SCTAB nTab; |
| if (!pDoc->GetTable(rStartTabName, nTab)) |
| { |
| // invalid table name. |
| nFlags &= ~SCA_VALID_TAB; |
| nTab = -1; |
| } |
| |
| aStart.SetTab(nTab); |
| aEnd.SetTab(nTab); |
| |
| if (rEndTabName.Len()) |
| { |
| if (!pDoc->GetTable(rEndTabName, nTab)) |
| { |
| // invalid table name. |
| nFlags &= ~SCA_VALID_TAB2; |
| nTab = -1; |
| } |
| |
| aEnd.SetTab(nTab); |
| } |
| } |
| return p; |
| } |
| |
| |
| static const sal_Unicode* |
| lcl_r1c1_get_col( const sal_Unicode* p, |
| const ScAddress::Details& rDetails, |
| ScAddress* pAddr, sal_uInt16* nFlags ) |
| { |
| const sal_Unicode *pEnd; |
| long int n; |
| bool isRelative; |
| |
| if( p[0] == '\0' ) |
| return NULL; |
| |
| p++; |
| if( (isRelative = (*p == '[') ) != false ) |
| p++; |
| n = sal_Unicode_strtol( p, &pEnd ); |
| if( NULL == pEnd ) |
| return NULL; |
| |
| if( p == pEnd ) // C is a relative ref with offset 0 |
| { |
| if( isRelative ) |
| return NULL; |
| n = rDetails.nCol; |
| } |
| else if( isRelative ) |
| { |
| if( *pEnd != ']' ) |
| return NULL; |
| n += rDetails.nCol; |
| pEnd++; |
| } |
| else |
| { |
| *nFlags |= SCA_COL_ABSOLUTE; |
| n--; |
| } |
| |
| if( n < 0 || n >= MAXCOLCOUNT ) |
| return NULL; |
| pAddr->SetCol( static_cast<SCCOL>( n ) ); |
| *nFlags |= SCA_VALID_COL; |
| |
| return pEnd; |
| } |
| static inline const sal_Unicode* |
| lcl_r1c1_get_row( const sal_Unicode* p, |
| const ScAddress::Details& rDetails, |
| ScAddress* pAddr, sal_uInt16* nFlags ) |
| { |
| const sal_Unicode *pEnd; |
| long int n; |
| bool isRelative; |
| |
| if( p[0] == '\0' ) |
| return NULL; |
| |
| p++; |
| if( (isRelative = (*p == '[') ) != false ) |
| p++; |
| n = sal_Unicode_strtol( p, &pEnd ); |
| if( NULL == pEnd ) |
| return NULL; |
| |
| if( p == pEnd ) // R is a relative ref with offset 0 |
| { |
| if( isRelative ) |
| return NULL; |
| n = rDetails.nRow; |
| } |
| else if( isRelative ) |
| { |
| if( *pEnd != ']' ) |
| return NULL; |
| n += rDetails.nRow; |
| pEnd++; |
| } |
| else |
| { |
| *nFlags |= SCA_ROW_ABSOLUTE; |
| n--; |
| } |
| |
| if( n < 0 || n >= MAXROWCOUNT ) |
| return NULL; |
| pAddr->SetRow( static_cast<SCROW>( n ) ); |
| *nFlags |= SCA_VALID_ROW; |
| |
| return pEnd; |
| } |
| |
| static sal_uInt16 |
| lcl_ScRange_Parse_XL_R1C1( ScRange& r, |
| const sal_Unicode* p, |
| ScDocument* pDoc, |
| const ScAddress::Details& rDetails, |
| bool bOnlyAcceptSingle, |
| ScAddress::ExternalInfo* pExtInfo ) |
| { |
| const sal_Unicode* pTmp = NULL; |
| String aExternDocName, aStartTabName, aEndTabName; |
| sal_uInt16 nFlags = SCA_VALID | SCA_VALID_TAB; |
| // Keep in mind that nFlags2 gets left-shifted by 4 bits before being merged. |
| sal_uInt16 nFlags2 = SCA_VALID_TAB; |
| |
| #if 0 |
| { |
| ByteString aStr(p, RTL_TEXTENCODING_UTF8); |
| aStr.Append(static_cast< char >(0)); |
| std::cerr << "parse::XL::R1C1 \'" << aStr.GetBuffer() << '\'' << std::endl; |
| } |
| #endif |
| p = r.Parse_XL_Header( p, pDoc, aExternDocName, aStartTabName, |
| aEndTabName, nFlags, bOnlyAcceptSingle, NULL ); |
| |
| if (aExternDocName.Len() > 0) |
| lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName, |
| aStartTabName, aEndTabName, pDoc); |
| |
| if( NULL == p ) |
| return 0; |
| |
| if( *p == 'R' || *p == 'r' ) |
| { |
| if( NULL == (p = lcl_r1c1_get_row( p, rDetails, &r.aStart, &nFlags )) ) |
| goto failed; |
| |
| if( *p != 'C' && *p != 'c' ) // full row R# |
| { |
| if( p[0] != ':' || (p[1] != 'R' && p[1] != 'r' ) || |
| NULL == (pTmp = lcl_r1c1_get_row( p+1, rDetails, &r.aEnd, &nFlags2 ))) |
| { |
| // Only the initial row number is given, or the second row |
| // number is invalid. Fallback to just the initial R |
| nFlags |= (nFlags << 4); |
| r.aEnd.SetRow( r.aStart.Row() ); |
| } |
| else |
| { |
| // Full row range successfully parsed. |
| nFlags |= (nFlags2 << 4); |
| p = pTmp; |
| } |
| |
| if (p && p[0] != 0) |
| { |
| // any trailing invalid character must invalidate the whole address. |
| nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB | |
| SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2); |
| return nFlags; |
| } |
| |
| nFlags |= |
| SCA_VALID_COL | SCA_VALID_COL2 | |
| SCA_COL_ABSOLUTE | SCA_COL2_ABSOLUTE; |
| r.aStart.SetCol( 0 ); |
| r.aEnd.SetCol( MAXCOL ); |
| |
| return bOnlyAcceptSingle ? 0 : nFlags; |
| } |
| else if( NULL == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, &nFlags ))) |
| goto failed; |
| |
| if( p[0] != ':' || |
| (p[1] != 'R' && p[1] != 'r') || |
| NULL == (pTmp = lcl_r1c1_get_row( p+1, rDetails, &r.aEnd, &nFlags2 )) || |
| (*pTmp != 'C' && *pTmp != 'c') || |
| NULL == (pTmp = lcl_r1c1_get_col( pTmp, rDetails, &r.aEnd, &nFlags2 ))) |
| { |
| // single cell reference |
| |
| if (p && p[0] != 0) |
| { |
| // any trailing invalid character must invalidate the whole address. |
| nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB); |
| return nFlags; |
| } |
| |
| return bOnlyAcceptSingle ? nFlags : 0; |
| } |
| p = pTmp; |
| |
| // double reference |
| |
| if (p && p[0] != 0) |
| { |
| // any trailing invalid character must invalidate the whole range. |
| nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB | |
| SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2); |
| return nFlags; |
| } |
| |
| nFlags |= (nFlags2 << 4); |
| return bOnlyAcceptSingle ? 0 : nFlags; |
| } |
| else if( *p == 'C' || *p == 'c' ) // full col C# |
| { |
| if( NULL == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, &nFlags ))) |
| goto failed; |
| |
| if( p[0] != ':' || (p[1] != 'C' && p[1] != 'c') || |
| NULL == (pTmp = lcl_r1c1_get_col( p+1, rDetails, &r.aEnd, &nFlags2 ))) |
| { // Fallback to just the initial C |
| nFlags |= (nFlags << 4); |
| r.aEnd.SetCol( r.aStart.Col() ); |
| } |
| else |
| { |
| nFlags |= (nFlags2 << 4); |
| p = pTmp; |
| } |
| |
| if (p && p[0] != 0) |
| { |
| // any trailing invalid character must invalidate the whole address. |
| nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB | |
| SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2); |
| return nFlags; |
| } |
| |
| nFlags |= |
| SCA_VALID_ROW | SCA_VALID_ROW2 | |
| SCA_ROW_ABSOLUTE | SCA_ROW2_ABSOLUTE; |
| r.aStart.SetRow( 0 ); |
| r.aEnd.SetRow( MAXROW ); |
| |
| return bOnlyAcceptSingle ? 0 : nFlags; |
| } |
| |
| failed : |
| return 0; |
| } |
| |
| static inline const sal_Unicode* |
| lcl_a1_get_col( const sal_Unicode* p, ScAddress* pAddr, sal_uInt16* nFlags ) |
| { |
| SCCOL nCol; |
| |
| if( *p == '$' ) |
| *nFlags |= SCA_COL_ABSOLUTE, p++; |
| |
| if( !CharClass::isAsciiAlpha( *p ) ) |
| return NULL; |
| |
| nCol = sal::static_int_cast<SCCOL>( toupper( char(*p++) ) - 'A' ); |
| while (nCol <= MAXCOL && CharClass::isAsciiAlpha(*p)) |
| nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + toupper( char(*p++) ) - 'A' ); |
| if( nCol > MAXCOL || CharClass::isAsciiAlpha( *p ) ) |
| return NULL; |
| |
| *nFlags |= SCA_VALID_COL; |
| pAddr->SetCol( nCol ); |
| |
| return p; |
| } |
| |
| static inline const sal_Unicode* |
| lcl_a1_get_row( const sal_Unicode* p, ScAddress* pAddr, sal_uInt16* nFlags ) |
| { |
| const sal_Unicode *pEnd; |
| long int n; |
| |
| if( *p == '$' ) |
| *nFlags |= SCA_ROW_ABSOLUTE, p++; |
| |
| n = sal_Unicode_strtol( p, &pEnd ) - 1; |
| if( NULL == pEnd || p == pEnd || n < 0 || n > MAXROW ) |
| return NULL; |
| |
| *nFlags |= SCA_VALID_ROW; |
| pAddr->SetRow( static_cast<SCROW>(n) ); |
| |
| return pEnd; |
| } |
| |
| static sal_uInt16 |
| lcl_ScRange_Parse_XL_A1( ScRange& r, |
| const sal_Unicode* p, |
| ScDocument* pDoc, |
| bool bOnlyAcceptSingle, |
| ScAddress::ExternalInfo* pExtInfo, |
| const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks ) |
| { |
| const sal_Unicode* tmp1, *tmp2; |
| String aExternDocName, aStartTabName, aEndTabName; // for external link table |
| sal_uInt16 nFlags = SCA_VALID | SCA_VALID_TAB, nFlags2 = SCA_VALID_TAB; |
| |
| #if 0 |
| { |
| ByteString aStr(p, RTL_TEXTENCODING_UTF8); |
| aStr.Append(static_cast< char >(0)); |
| std::cerr << "parse::XL::A1 \'" << aStr.GetBuffer() << '\'' << std::endl; |
| } |
| #endif |
| p = r.Parse_XL_Header( p, pDoc, aExternDocName, aStartTabName, |
| aEndTabName, nFlags, bOnlyAcceptSingle, pExternalLinks ); |
| |
| if (aExternDocName.Len() > 0) |
| lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName, |
| aStartTabName, aEndTabName, pDoc); |
| |
| if( NULL == p ) |
| return 0; |
| |
| tmp1 = lcl_a1_get_col( p, &r.aStart, &nFlags ); |
| if( tmp1 == NULL ) // Is it a row only reference 3:5 |
| { |
| if( bOnlyAcceptSingle ) // by definition full row refs are ranges |
| return 0; |
| |
| tmp1 = lcl_a1_get_row( p, &r.aStart, &nFlags ); |
| |
| tmp1 = lcl_eatWhiteSpace( tmp1 ); |
| if( !tmp1 || *tmp1++ != ':' ) // Even a singleton requires ':' (eg 2:2) |
| return 0; |
| |
| tmp1 = lcl_eatWhiteSpace( tmp1 ); |
| tmp2 = lcl_a1_get_row( tmp1, &r.aEnd, &nFlags2 ); |
| if( !tmp2 ) |
| return 0; |
| |
| r.aStart.SetCol( 0 ); r.aEnd.SetCol( MAXCOL ); |
| nFlags |= |
| SCA_VALID_COL | SCA_VALID_COL2 | |
| SCA_COL_ABSOLUTE | SCA_COL2_ABSOLUTE; |
| nFlags |= (nFlags2 << 4); |
| return nFlags; |
| } |
| |
| tmp2 = lcl_a1_get_row( tmp1, &r.aStart, &nFlags ); |
| if( tmp2 == NULL ) // check for col only reference F:H |
| { |
| if( bOnlyAcceptSingle ) // by definition full col refs are ranges |
| return 0; |
| |
| tmp1 = lcl_eatWhiteSpace( tmp1 ); |
| if( *tmp1++ != ':' ) // Even a singleton requires ':' (eg F:F) |
| return 0; |
| |
| tmp1 = lcl_eatWhiteSpace( tmp1 ); |
| tmp2 = lcl_a1_get_col( tmp1, &r.aEnd, &nFlags2 ); |
| if( !tmp2 ) |
| return 0; |
| |
| r.aStart.SetRow( 0 ); r.aEnd.SetRow( MAXROW ); |
| nFlags |= |
| SCA_VALID_ROW | SCA_VALID_ROW2 | |
| SCA_ROW_ABSOLUTE | SCA_ROW2_ABSOLUTE; |
| nFlags |= (nFlags2 << 4); |
| return nFlags; |
| } |
| |
| // prepare as if it's a singleton, in case we want to fall back */ |
| r.aEnd.SetCol( r.aStart.Col() ); |
| r.aEnd.SetRow( r.aStart.Row() ); // don't overwrite sheet number as parsed in Parse_XL_Header() |
| |
| if ( bOnlyAcceptSingle ) |
| { |
| if ( *tmp2 == 0 ) |
| return nFlags; |
| else |
| { |
| // any trailing invalid character must invalidate the address. |
| nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB); |
| return nFlags; |
| } |
| } |
| |
| tmp2 = lcl_eatWhiteSpace( tmp2 ); |
| if( *tmp2 != ':' ) |
| { |
| // Sheet1:Sheet2!C4 is a valid range, without a second sheet it is |
| // not. Any trailing invalid character invalidates the range. |
| if (*tmp2 == 0 && (nFlags & SCA_TAB2_3D)) |
| { |
| if (nFlags & SCA_COL_ABSOLUTE) |
| nFlags |= SCA_COL2_ABSOLUTE; |
| if (nFlags & SCA_ROW_ABSOLUTE) |
| nFlags |= SCA_ROW2_ABSOLUTE; |
| } |
| else |
| nFlags &= ~(SCA_VALID | |
| SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB | |
| SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2); |
| return nFlags; |
| } |
| |
| p = tmp2; |
| p = lcl_eatWhiteSpace( p+1 ); |
| tmp1 = lcl_a1_get_col( p, &r.aEnd, &nFlags2 ); |
| if( !tmp1 ) // strange, but valid singleton |
| return nFlags; |
| |
| tmp2 = lcl_a1_get_row( tmp1, &r.aEnd, &nFlags2 ); |
| if( !tmp2 ) // strange, but valid singleton |
| return nFlags; |
| |
| if ( *tmp2 != 0 ) |
| { |
| // any trailing invalid character must invalidate the range. |
| nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB | |
| SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2); |
| return nFlags; |
| } |
| |
| nFlags |= (nFlags2 << 4); |
| return nFlags; |
| } |
| |
| /** |
| @param pRange pointer to range where rAddr effectively is *pRange->aEnd, |
| used in conjunction with pExtInfo to determine the tab span |
| of a 3D reference. |
| */ |
| static sal_uInt16 |
| lcl_ScAddress_Parse_OOo( const sal_Unicode* p, ScDocument* pDoc, ScAddress& rAddr, |
| ScAddress::ExternalInfo* pExtInfo = NULL, ScRange* pRange = NULL ) |
| { |
| sal_uInt16 nRes = 0; |
| String aDocName; // der pure Dokumentenname |
| String aTab; |
| bool bExtDoc = false; |
| bool bExtDocInherited = false; |
| const ScAddress aCurPos(rAddr); |
| |
| // Lets see if this is a reference to something in an external file. A |
| // document name is always quoted and has a trailing #. |
| if (*p == '\'') |
| { |
| const sal_Unicode* pStart = p; |
| p = lcl_ParseQuotedName(p, aDocName); |
| if (*p++ == SC_COMPILER_FILE_TAB_SEP) |
| bExtDoc = true; |
| else |
| // This is not a document name. Perhaps a quoted relative table |
| // name. |
| p = pStart; |
| } |
| else if (pExtInfo && pExtInfo->mbExternal) |
| { |
| // This is an external reference. |
| bExtDoc = bExtDocInherited = true; |
| } |
| |
| SCCOL nCol = 0; |
| SCROW nRow = 0; |
| SCTAB nTab = 0; |
| sal_uInt16 nBits = SCA_VALID_TAB; |
| const sal_Unicode* q; |
| if ( ScGlobal::FindUnquoted( p, '.') ) |
| { |
| nRes |= SCA_TAB_3D; |
| if ( bExtDoc ) |
| nRes |= SCA_TAB_ABSOLUTE; |
| if (*p == '$') |
| nRes |= SCA_TAB_ABSOLUTE, p++; |
| |
| if (*p == '\'') |
| { |
| // Tokens that start at ' can have anything in them until a final |
| // ' but '' marks an escaped '. We've earlier guaranteed that a |
| // string containing '' will be surrounded by '. |
| p = lcl_ParseQuotedName(p, aTab); |
| } |
| else |
| { |
| while (*p) |
| { |
| if( *p == '.') |
| break; |
| |
| if( *p == '\'' ) |
| { |
| p++; break; |
| } |
| aTab += *p++; |
| } |
| } |
| if( *p++ != '.' ) |
| nBits = 0; |
| |
| if (!bExtDoc && (!pDoc || !pDoc->GetTable( aTab, nTab ))) |
| nBits = 0; |
| } |
| else |
| { |
| if (bExtDoc && !bExtDocInherited) |
| return nRes; // After a document a sheet must follow. |
| nTab = rAddr.Tab(); |
| } |
| nRes |= nBits; |
| |
| q = p; |
| if (*p) |
| { |
| nBits = SCA_VALID_COL; |
| if (*p == '$') |
| nBits |= SCA_COL_ABSOLUTE, p++; |
| |
| if (CharClass::isAsciiAlpha( *p )) |
| { |
| nCol = sal::static_int_cast<SCCOL>( toupper( char(*p++) ) - 'A' ); |
| while (nCol < MAXCOL && CharClass::isAsciiAlpha(*p)) |
| nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + toupper( char(*p++) ) - 'A' ); |
| } |
| else |
| nBits = 0; |
| |
| if( nCol > MAXCOL || CharClass::isAsciiAlpha( *p ) ) |
| nBits = 0; |
| nRes |= nBits; |
| if( !nBits ) |
| p = q; |
| } |
| |
| q = p; |
| if (*p) |
| { |
| nBits = SCA_VALID_ROW; |
| if (*p == '$') |
| nBits |= SCA_ROW_ABSOLUTE, p++; |
| if( !CharClass::isAsciiDigit( *p ) ) |
| { |
| nBits = 0; |
| nRow = SCROW(-1); |
| } |
| else |
| { |
| String aTmp( p ); |
| long n = aTmp.ToInt32() - 1; |
| while (CharClass::isAsciiDigit( *p )) |
| p++; |
| if( n < 0 || n > MAXROW ) |
| nBits = 0; |
| nRow = static_cast<SCROW>(n); |
| } |
| nRes |= nBits; |
| if( !nBits ) |
| p = q; |
| } |
| |
| rAddr.Set( nCol, nRow, nTab ); |
| |
| if (!*p && bExtDoc) |
| { |
| if (!pDoc) |
| nRes = 0; |
| else |
| { |
| ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); |
| |
| // Need document name if inherited. |
| if (bExtDocInherited) |
| { |
| const String* pFileName = pRefMgr->getExternalFileName( pExtInfo->mnFileId); |
| if (pFileName) |
| aDocName = *pFileName; |
| else |
| nRes = 0; |
| } |
| pRefMgr->convertToAbsName(aDocName); |
| |
| if ((!pExtInfo || !pExtInfo->mbExternal) && pRefMgr->isOwnDocument(aDocName)) |
| { |
| if (!pDoc->GetTable( aTab, nTab )) |
| nRes = 0; |
| else |
| { |
| rAddr.SetTab( nTab); |
| nRes |= SCA_VALID_TAB; |
| } |
| } |
| else |
| { |
| if (!pExtInfo) |
| nRes = 0; |
| else |
| { |
| if (!pExtInfo->mbExternal) |
| { |
| sal_uInt16 nFileId = pRefMgr->getExternalFileId(aDocName); |
| |
| pExtInfo->mbExternal = true; |
| pExtInfo->maTabName = aTab; |
| pExtInfo->mnFileId = nFileId; |
| |
| if (pRefMgr->getSingleRefToken(nFileId, aTab, |
| ScAddress(nCol, nRow, 0), NULL, |
| &nTab).get()) |
| { |
| rAddr.SetTab( nTab); |
| nRes |= SCA_VALID_TAB; |
| } |
| else |
| nRes = 0; |
| } |
| else |
| { |
| // This is a call for the second part of the reference, |
| // we must have the range to adapt tab span. |
| if (!pRange) |
| nRes = 0; |
| else |
| { |
| sal_uInt16 nFlags = nRes | SCA_VALID_TAB2; |
| if (!lcl_ScRange_External_TabSpan( *pRange, nFlags, |
| pExtInfo, aDocName, |
| pExtInfo->maTabName, aTab, pDoc)) |
| nRes &= ~SCA_VALID_TAB; |
| else |
| { |
| if (nFlags & SCA_VALID_TAB2) |
| { |
| rAddr.SetTab( pRange->aEnd.Tab()); |
| nRes |= SCA_VALID_TAB; |
| } |
| else |
| nRes &= ~SCA_VALID_TAB; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if ( !(nRes & SCA_VALID_ROW) && (nRes & SCA_VALID_COL) |
| && !( (nRes & SCA_TAB_3D) && (nRes & SCA_VALID_TAB)) ) |
| { // no Row, no Tab, but Col => DM (...), B (...) et al |
| nRes = 0; |
| } |
| if( !*p ) |
| { |
| sal_uInt16 nMask = nRes & ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB ); |
| if( nMask == ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB ) ) |
| nRes |= SCA_VALID; |
| } |
| else |
| nRes = 0; |
| return nRes; |
| } |
| |
| static sal_uInt16 |
| lcl_ScAddress_Parse ( const sal_Unicode* p, ScDocument* pDoc, ScAddress& rAddr, |
| const ScAddress::Details& rDetails, |
| ScAddress::ExternalInfo* pExtInfo = NULL, |
| const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks = NULL ) |
| { |
| if( !*p ) |
| return 0; |
| |
| switch (rDetails.eConv) |
| { |
| default : |
| case formula::FormulaGrammar::CONV_OOO: |
| { |
| return lcl_ScAddress_Parse_OOo( p, pDoc, rAddr, pExtInfo, NULL ); |
| } |
| |
| case formula::FormulaGrammar::CONV_XL_A1: |
| case formula::FormulaGrammar::CONV_XL_OOX: |
| { |
| ScRange r = rAddr; |
| sal_uInt16 nFlags = lcl_ScRange_Parse_XL_A1( r, p, pDoc, true, pExtInfo, |
| (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : NULL) ); |
| rAddr = r.aStart; |
| return nFlags; |
| } |
| case formula::FormulaGrammar::CONV_XL_R1C1: |
| { |
| ScRange r = rAddr; |
| sal_uInt16 nFlags = lcl_ScRange_Parse_XL_R1C1( r, p, pDoc, rDetails, true, pExtInfo ); |
| rAddr = r.aStart; |
| return nFlags; |
| } |
| } |
| } |
| |
| |
| bool ConvertSingleRef( ScDocument* pDoc, const String& rRefString, |
| SCTAB nDefTab, ScRefAddress& rRefAddress, |
| const ScAddress::Details& rDetails, |
| ScAddress::ExternalInfo* pExtInfo /* = NULL */ ) |
| { |
| bool bRet = false; |
| if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == STRING_NOTFOUND)) |
| { |
| ScAddress aAddr( 0, 0, nDefTab ); |
| sal_uInt16 nRes = aAddr.Parse( rRefString, pDoc, rDetails, pExtInfo); |
| if ( nRes & SCA_VALID ) |
| { |
| rRefAddress.Set( aAddr, |
| ((nRes & SCA_COL_ABSOLUTE) == 0), |
| ((nRes & SCA_ROW_ABSOLUTE) == 0), |
| ((nRes & SCA_TAB_ABSOLUTE) == 0)); |
| bRet = true; |
| } |
| } |
| return bRet; |
| } |
| |
| |
| bool ConvertDoubleRef( ScDocument* pDoc, const String& rRefString, SCTAB nDefTab, |
| ScRefAddress& rStartRefAddress, ScRefAddress& rEndRefAddress, |
| const ScAddress::Details& rDetails, |
| ScAddress::ExternalInfo* pExtInfo /* = NULL */ ) |
| { |
| bool bRet = false; |
| if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == STRING_NOTFOUND)) |
| { |
| ScRange aRange( ScAddress( 0, 0, nDefTab)); |
| sal_uInt16 nRes = aRange.Parse( rRefString, pDoc, rDetails, pExtInfo); |
| if ( nRes & SCA_VALID ) |
| { |
| rStartRefAddress.Set( aRange.aStart, |
| ((nRes & SCA_COL_ABSOLUTE) == 0), |
| ((nRes & SCA_ROW_ABSOLUTE) == 0), |
| ((nRes & SCA_TAB_ABSOLUTE) == 0)); |
| rEndRefAddress.Set( aRange.aEnd, |
| ((nRes & SCA_COL2_ABSOLUTE) == 0), |
| ((nRes & SCA_ROW2_ABSOLUTE) == 0), |
| ((nRes & SCA_TAB2_ABSOLUTE) == 0)); |
| bRet = true; |
| } |
| } |
| return bRet; |
| } |
| |
| |
| sal_uInt16 ScAddress::Parse( const String& r, ScDocument* pDoc, |
| const Details& rDetails, |
| ExternalInfo* pExtInfo, |
| const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks ) |
| { |
| return lcl_ScAddress_Parse( r.GetBuffer(), pDoc, *this, rDetails, pExtInfo, pExternalLinks ); |
| } |
| |
| |
| bool ScRange::Intersects( const ScRange& r ) const |
| { |
| return !( |
| Min( aEnd.Col(), r.aEnd.Col() ) < Max( aStart.Col(), r.aStart.Col() ) |
| || Min( aEnd.Row(), r.aEnd.Row() ) < Max( aStart.Row(), r.aStart.Row() ) |
| || Min( aEnd.Tab(), r.aEnd.Tab() ) < Max( aStart.Tab(), r.aStart.Tab() ) |
| ); |
| } |
| |
| |
| void ScRange::Justify() |
| { |
| SCCOL nTempCol; |
| if ( aEnd.Col() < (nTempCol = aStart.Col()) ) |
| { |
| aStart.SetCol(aEnd.Col()); aEnd.SetCol(nTempCol); |
| } |
| SCROW nTempRow; |
| if ( aEnd.Row() < (nTempRow = aStart.Row()) ) |
| { |
| aStart.SetRow(aEnd.Row()); aEnd.SetRow(nTempRow); |
| } |
| SCTAB nTempTab; |
| if ( aEnd.Tab() < (nTempTab = aStart.Tab()) ) |
| { |
| aStart.SetTab(aEnd.Tab()); aEnd.SetTab(nTempTab); |
| } |
| } |
| |
| void ScRange::ExtendTo( const ScRange& rRange ) |
| { |
| DBG_ASSERT( rRange.IsValid(), "ScRange::ExtendTo - cannot extend to invalid range" ); |
| if( IsValid() ) |
| { |
| aStart.SetCol( ::std::min( aStart.Col(), rRange.aStart.Col() ) ); |
| aStart.SetRow( ::std::min( aStart.Row(), rRange.aStart.Row() ) ); |
| aStart.SetTab( ::std::min( aStart.Tab(), rRange.aStart.Tab() ) ); |
| aEnd.SetCol( ::std::max( aEnd.Col(), rRange.aEnd.Col() ) ); |
| aEnd.SetRow( ::std::max( aEnd.Row(), rRange.aEnd.Row() ) ); |
| aEnd.SetTab( ::std::max( aEnd.Tab(), rRange.aEnd.Tab() ) ); |
| } |
| else |
| *this = rRange; |
| } |
| |
| static sal_uInt16 |
| lcl_ScRange_Parse_OOo( ScRange &aRange, const String& r, ScDocument* pDoc, ScAddress::ExternalInfo* pExtInfo = NULL ) |
| { |
| sal_uInt16 nRes1 = 0, nRes2 = 0; |
| xub_StrLen nPos = ScGlobal::FindUnquoted( r, ':'); |
| if (nPos != STRING_NOTFOUND) |
| { |
| String aTmp( r ); |
| sal_Unicode* p = aTmp.GetBufferAccess(); |
| p[ nPos ] = 0; |
| if( (nRes1 = lcl_ScAddress_Parse_OOo( p, pDoc, aRange.aStart, pExtInfo, NULL ) ) != 0 ) |
| { |
| aRange.aEnd = aRange.aStart; // sheet must be initialized identical to first sheet |
| if ( (nRes2 = lcl_ScAddress_Parse_OOo( p + nPos+ 1, pDoc, aRange.aEnd, pExtInfo, &aRange ) ) != 0 ) |
| { |
| // PutInOrder / Justify |
| sal_uInt16 nMask, nBits1, nBits2; |
| SCCOL nTempCol; |
| if ( aRange.aEnd.Col() < (nTempCol = aRange.aStart.Col()) ) |
| { |
| aRange.aStart.SetCol(aRange.aEnd.Col()); aRange.aEnd.SetCol(nTempCol); |
| nMask = (SCA_VALID_COL | SCA_COL_ABSOLUTE); |
| nBits1 = nRes1 & nMask; |
| nBits2 = nRes2 & nMask; |
| nRes1 = (nRes1 & ~nMask) | nBits2; |
| nRes2 = (nRes2 & ~nMask) | nBits1; |
| } |
| SCROW nTempRow; |
| if ( aRange.aEnd.Row() < (nTempRow = aRange.aStart.Row()) ) |
| { |
| aRange.aStart.SetRow(aRange.aEnd.Row()); aRange.aEnd.SetRow(nTempRow); |
| nMask = (SCA_VALID_ROW | SCA_ROW_ABSOLUTE); |
| nBits1 = nRes1 & nMask; |
| nBits2 = nRes2 & nMask; |
| nRes1 = (nRes1 & ~nMask) | nBits2; |
| nRes2 = (nRes2 & ~nMask) | nBits1; |
| } |
| SCTAB nTempTab; |
| if ( aRange.aEnd.Tab() < (nTempTab = aRange.aStart.Tab()) ) |
| { |
| aRange.aStart.SetTab(aRange.aEnd.Tab()); aRange.aEnd.SetTab(nTempTab); |
| nMask = (SCA_VALID_TAB | SCA_TAB_ABSOLUTE | SCA_TAB_3D); |
| nBits1 = nRes1 & nMask; |
| nBits2 = nRes2 & nMask; |
| nRes1 = (nRes1 & ~nMask) | nBits2; |
| nRes2 = (nRes2 & ~nMask) | nBits1; |
| } |
| if ( ((nRes1 & ( SCA_TAB_ABSOLUTE | SCA_TAB_3D )) |
| == ( SCA_TAB_ABSOLUTE | SCA_TAB_3D )) |
| && !(nRes2 & SCA_TAB_3D) ) |
| nRes2 |= SCA_TAB_ABSOLUTE; |
| } |
| else |
| nRes1 = 0; // #38840# keine Tokens aus halben Sachen |
| } |
| } |
| nRes1 = ( ( nRes1 | nRes2 ) & SCA_VALID ) |
| | nRes1 |
| | ( ( nRes2 & 0x070F ) << 4 ); |
| return nRes1; |
| } |
| |
| sal_uInt16 ScRange::Parse( const String& r, ScDocument* pDoc, |
| const ScAddress::Details& rDetails, |
| ScAddress::ExternalInfo* pExtInfo, |
| const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks ) |
| { |
| if ( r.Len() <= 0 ) |
| return 0; |
| |
| switch (rDetails.eConv) |
| { |
| default : |
| case formula::FormulaGrammar::CONV_OOO: |
| return lcl_ScRange_Parse_OOo( *this, r, pDoc, pExtInfo ); |
| |
| case formula::FormulaGrammar::CONV_XL_A1: |
| case formula::FormulaGrammar::CONV_XL_OOX: |
| return lcl_ScRange_Parse_XL_A1( *this, r.GetBuffer(), pDoc, false, pExtInfo, |
| (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : NULL) ); |
| |
| case formula::FormulaGrammar::CONV_XL_R1C1: |
| return lcl_ScRange_Parse_XL_R1C1( *this, r.GetBuffer(), pDoc, rDetails, false, pExtInfo ); |
| } |
| } |
| |
| |
| // Accept a full range, or an address |
| sal_uInt16 ScRange::ParseAny( const String& r, ScDocument* pDoc, |
| const ScAddress::Details& rDetails ) |
| { |
| sal_uInt16 nRet = Parse( r, pDoc, rDetails ); |
| const sal_uInt16 nValid = SCA_VALID | SCA_VALID_COL2 | SCA_VALID_ROW2 | |
| SCA_VALID_TAB2; |
| |
| if ( (nRet & nValid) != nValid ) |
| { |
| ScAddress aAdr; |
| nRet = aAdr.Parse( r, pDoc, rDetails ); |
| if ( nRet & SCA_VALID ) |
| aStart = aEnd = aAdr; |
| } |
| return nRet; |
| } |
| |
| // Parse only full row references |
| sal_uInt16 ScRange::ParseCols( const String& rStr, ScDocument* pDoc, |
| const ScAddress::Details& rDetails ) |
| { |
| const sal_Unicode* p = rStr.GetBuffer(); |
| sal_uInt16 nRes = 0, ignored = 0; |
| |
| if( NULL == p ) |
| return 0; |
| |
| pDoc = NULL; // make compiler shutup we may need this later |
| |
| switch (rDetails.eConv) |
| { |
| default : |
| case formula::FormulaGrammar::CONV_OOO: // No full col refs in OOO yet, assume XL notation |
| case formula::FormulaGrammar::CONV_XL_A1: |
| case formula::FormulaGrammar::CONV_XL_OOX: |
| if (NULL != (p = lcl_a1_get_col( p, &aStart, &ignored ) ) ) |
| { |
| if( p[0] == ':') |
| { |
| if( NULL != (p = lcl_a1_get_col( p+1, &aEnd, &ignored ))) |
| { |
| nRes = SCA_VALID_COL; |
| } |
| } |
| else |
| { |
| aEnd = aStart; |
| nRes = SCA_VALID_COL; |
| } |
| } |
| break; |
| |
| case formula::FormulaGrammar::CONV_XL_R1C1: |
| if ((p[0] == 'C' || p[0] != 'c') && |
| NULL != (p = lcl_r1c1_get_col( p, rDetails, &aStart, &ignored ))) |
| { |
| if( p[0] == ':') |
| { |
| if( (p[1] == 'C' || p[1] == 'c') && |
| NULL != (p = lcl_r1c1_get_col( p+1, rDetails, &aEnd, &ignored ))) |
| { |
| nRes = SCA_VALID_COL; |
| } |
| } |
| else |
| { |
| aEnd = aStart; |
| nRes = SCA_VALID_COL; |
| } |
| } |
| break; |
| } |
| |
| return (p != NULL && *p == '\0') ? nRes : 0; |
| } |
| |
| // Parse only full row references |
| sal_uInt16 ScRange::ParseRows( const String& rStr, ScDocument* pDoc, |
| const ScAddress::Details& rDetails ) |
| { |
| const sal_Unicode* p = rStr.GetBuffer(); |
| sal_uInt16 nRes = 0, ignored = 0; |
| |
| if( NULL == p ) |
| return 0; |
| |
| pDoc = NULL; // make compiler shutup we may need this later |
| |
| switch (rDetails.eConv) |
| { |
| default : |
| case formula::FormulaGrammar::CONV_OOO: // No full row refs in OOO yet, assume XL notation |
| case formula::FormulaGrammar::CONV_XL_A1: |
| case formula::FormulaGrammar::CONV_XL_OOX: |
| if (NULL != (p = lcl_a1_get_row( p, &aStart, &ignored ) ) ) |
| { |
| if( p[0] == ':') |
| { |
| if( NULL != (p = lcl_a1_get_row( p+1, &aEnd, &ignored ))) |
| { |
| nRes = SCA_VALID_COL; |
| } |
| } |
| else |
| { |
| aEnd = aStart; |
| nRes = SCA_VALID_COL; |
| } |
| } |
| break; |
| |
| case formula::FormulaGrammar::CONV_XL_R1C1: |
| if ((p[0] == 'R' || p[0] != 'r') && |
| NULL != (p = lcl_r1c1_get_row( p, rDetails, &aStart, &ignored ))) |
| { |
| if( p[0] == ':') |
| { |
| if( (p[1] == 'R' || p[1] == 'r') && |
| NULL != (p = lcl_r1c1_get_row( p+1, rDetails, &aEnd, &ignored ))) |
| { |
| nRes = SCA_VALID_COL; |
| } |
| } |
| else |
| { |
| aEnd = aStart; |
| nRes = SCA_VALID_COL; |
| } |
| } |
| break; |
| } |
| |
| return (p != NULL && *p == '\0') ? nRes : 0; |
| } |
| |
| static inline void |
| lcl_a1_append_c ( String &r, int nCol, bool bIsAbs ) |
| { |
| if( bIsAbs ) |
| r += '$'; |
| ScColToAlpha( r, sal::static_int_cast<SCCOL>(nCol) ); |
| } |
| |
| static inline void |
| lcl_a1_append_r ( String &r, int nRow, bool bIsAbs ) |
| { |
| if ( bIsAbs ) |
| r += '$'; |
| r += String::CreateFromInt32( nRow+1 ); |
| } |
| |
| static inline void |
| lcl_r1c1_append_c ( String &r, int nCol, bool bIsAbs, |
| const ScAddress::Details& rDetails ) |
| { |
| r += 'C'; |
| if (bIsAbs) |
| { |
| r += String::CreateFromInt32( nCol + 1 ); |
| } |
| else |
| { |
| nCol -= rDetails.nCol; |
| if (nCol != 0) { |
| r += '['; |
| r += String::CreateFromInt32( nCol ); |
| r += ']'; |
| } |
| } |
| } |
| static inline void |
| lcl_r1c1_append_r ( String &r, int nRow, bool bIsAbs, |
| const ScAddress::Details& rDetails ) |
| { |
| r += 'R'; |
| if (bIsAbs) |
| { |
| r += String::CreateFromInt32( nRow + 1 ); |
| } |
| else |
| { |
| nRow -= rDetails.nRow; |
| if (nRow != 0) { |
| r += '['; |
| r += String::CreateFromInt32( nRow ); |
| r += ']'; |
| } |
| } |
| } |
| |
| static String |
| getFileNameFromDoc( const ScDocument* pDoc ) |
| { |
| // TODO : er points at ScGlobal::GetAbsDocName() |
| // as a better template. Look into it |
| String sFileName; |
| SfxObjectShell* pShell; |
| |
| if( NULL != pDoc && |
| NULL != (pShell = pDoc->GetDocumentShell() ) ) |
| { |
| uno::Reference< frame::XModel > xModel( pShell->GetModel(), uno::UNO_QUERY ); |
| if( xModel.is() ) |
| { |
| if( xModel->getURL().getLength() ) |
| { |
| INetURLObject aURL( xModel->getURL() ); |
| sFileName = aURL.GetLastName(); |
| } |
| else |
| sFileName = pShell->GetTitle(); |
| } |
| } |
| #if 0 |
| { |
| ByteString aStr( sFileName, RTL_TEXTENCODING_UTF8 ); |
| aStr.Append(static_cast< char >(0)); |
| std::cerr << "docname \'" << aStr.GetBuffer() << '\'' << std::endl; |
| } |
| #endif |
| return sFileName; |
| } |
| |
| void ScAddress::Format( String& r, sal_uInt16 nFlags, ScDocument* pDoc, |
| const Details& rDetails) const |
| { |
| r.Erase(); |
| if( nFlags & SCA_VALID ) |
| nFlags |= ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB ); |
| if( pDoc && (nFlags & SCA_VALID_TAB ) ) |
| { |
| if ( nTab >= pDoc->GetTableCount() ) |
| { |
| r = ScGlobal::GetRscString( STR_NOREF_STR ); |
| return; |
| } |
| // if( nFlags & ( SCA_TAB_ABSOLUTE | SCA_TAB_3D ) ) |
| if( nFlags & SCA_TAB_3D ) |
| { |
| String aTabName, aDocName; |
| pDoc->GetName( nTab, aTabName ); |
| // External Reference, same as in ScCompiler::MakeTabStr() |
| if( aTabName.GetChar(0) == '\'' ) |
| { // "'Doc'#Tab" |
| xub_StrLen nPos = ScCompiler::GetDocTabPos( aTabName); |
| if (nPos != STRING_NOTFOUND) |
| { |
| aDocName = aTabName.Copy( 0, nPos + 1 ); |
| aTabName.Erase( 0, nPos + 1 ); |
| } |
| } |
| else if( nFlags & SCA_FORCE_DOC ) |
| { |
| // VBA has an 'external' flag that forces the addition of the |
| // tab name _and_ the doc name. The VBA code would be |
| // needlessly complicated if it constructed an actual external |
| // reference so we add this somewhat cheesy kludge to force the |
| // addition of the document name even for non-external references |
| aDocName = getFileNameFromDoc( pDoc ); |
| } |
| ScCompiler::CheckTabQuotes( aTabName, rDetails.eConv); |
| |
| switch( rDetails.eConv ) |
| { |
| default : |
| case formula::FormulaGrammar::CONV_OOO: |
| r += aDocName; |
| if( nFlags & SCA_TAB_ABSOLUTE ) |
| r += '$'; |
| r += aTabName; |
| r += '.'; |
| break; |
| |
| case formula::FormulaGrammar::CONV_XL_A1: |
| case formula::FormulaGrammar::CONV_XL_R1C1: |
| case formula::FormulaGrammar::CONV_XL_OOX: |
| if (aDocName.Len() > 0) |
| { |
| r += '['; |
| r += aDocName; |
| r += ']'; |
| } |
| r += aTabName; |
| r += '!'; |
| break; |
| } |
| } |
| } |
| switch( rDetails.eConv ) |
| { |
| default : |
| case formula::FormulaGrammar::CONV_OOO: |
| case formula::FormulaGrammar::CONV_XL_A1: |
| case formula::FormulaGrammar::CONV_XL_OOX: |
| if( nFlags & SCA_VALID_COL ) |
| lcl_a1_append_c ( r, nCol, nFlags & SCA_COL_ABSOLUTE ); |
| if( nFlags & SCA_VALID_ROW ) |
| lcl_a1_append_r ( r, nRow, nFlags & SCA_ROW_ABSOLUTE ); |
| break; |
| |
| case formula::FormulaGrammar::CONV_XL_R1C1: |
| if( nFlags & SCA_VALID_ROW ) |
| lcl_r1c1_append_r ( r, nRow, nFlags & SCA_ROW_ABSOLUTE, rDetails ); |
| if( nFlags & SCA_VALID_COL ) |
| lcl_r1c1_append_c ( r, nCol, nFlags & SCA_COL_ABSOLUTE, rDetails ); |
| break; |
| } |
| } |
| |
| static void |
| lcl_Split_DocTab( const ScDocument* pDoc, SCTAB nTab, |
| const ScAddress::Details& rDetails, |
| sal_uInt16 nFlags, |
| String& rTabName, String& rDocName ) |
| { |
| pDoc->GetName( nTab, rTabName ); |
| rDocName.Erase(); |
| #if 0 |
| { |
| ByteString aStr(rTabName, RTL_TEXTENCODING_UTF8); |
| aStr.Append(static_cast< char >(0)); |
| std::cerr << "tabname \'" << aStr.GetBuffer() << '\'' << std::endl; |
| } |
| #endif |
| // External reference, same as in ScCompiler::MakeTabStr() |
| if ( rTabName.GetChar(0) == '\'' ) |
| { // "'Doc'#Tab" |
| xub_StrLen nPos = ScCompiler::GetDocTabPos( rTabName); |
| if (nPos != STRING_NOTFOUND) |
| { |
| rDocName = rTabName.Copy( 0, nPos + 1 ); |
| rTabName.Erase( 0, nPos + 1 ); |
| } |
| } |
| else if( nFlags & SCA_FORCE_DOC ) |
| { |
| // VBA has an 'external' flag that forces the addition of the |
| // tab name _and_ the doc name. The VBA code would be |
| // needlessly complicated if it constructed an actual external |
| // reference so we add this somewhat cheesy kludge to force the |
| // addition of the document name even for non-external references |
| rDocName = getFileNameFromDoc( pDoc ); |
| } |
| ScCompiler::CheckTabQuotes( rTabName, rDetails.eConv); |
| } |
| |
| static void |
| lcl_ScRange_Format_XL_Header( String& r, const ScRange& rRange, |
| sal_uInt16 nFlags, ScDocument* pDoc, |
| const ScAddress::Details& rDetails ) |
| { |
| if( nFlags & SCA_TAB_3D ) |
| { |
| String aTabName, aDocName; |
| lcl_Split_DocTab( pDoc, rRange.aStart.Tab(), rDetails, nFlags, |
| aTabName, aDocName ); |
| if( aDocName.Len() > 0 ) |
| { |
| r += '['; |
| r += aDocName; |
| r += ']'; |
| } |
| r += aTabName; |
| |
| if( nFlags & SCA_TAB2_3D ) |
| { |
| lcl_Split_DocTab( pDoc, rRange.aEnd.Tab(), rDetails, nFlags, |
| aTabName, aDocName ); |
| r += ':'; |
| r += aTabName; |
| } |
| r += '!'; |
| } |
| } |
| |
| void ScRange::Format( String& r, sal_uInt16 nFlags, ScDocument* pDoc, |
| const ScAddress::Details& rDetails ) const |
| { |
| r.Erase(); |
| if( !( nFlags & SCA_VALID ) ) |
| { |
| r = ScGlobal::GetRscString( STR_NOREF_STR ); |
| return; |
| } |
| |
| #define absrel_differ(nFlags, mask) (((nFlags) & (mask)) ^ (((nFlags) >> 4) & (mask))) |
| switch( rDetails.eConv ) { |
| default : |
| case formula::FormulaGrammar::CONV_OOO: { |
| sal_Bool bOneTab = (aStart.Tab() == aEnd.Tab()); |
| if ( !bOneTab ) |
| nFlags |= SCA_TAB_3D; |
| aStart.Format( r, nFlags, pDoc, rDetails ); |
| if( aStart != aEnd || |
| absrel_differ( nFlags, SCA_COL_ABSOLUTE ) || |
| absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) |
| { |
| String aName; |
| nFlags = ( nFlags & SCA_VALID ) | ( ( nFlags >> 4 ) & 0x070F ); |
| if ( bOneTab ) |
| pDoc = NULL; |
| else |
| nFlags |= SCA_TAB_3D; |
| aEnd.Format( aName, nFlags, pDoc, rDetails ); |
| r += ':'; |
| r += aName; |
| } |
| } |
| break; |
| |
| case formula::FormulaGrammar::CONV_XL_A1: |
| case formula::FormulaGrammar::CONV_XL_OOX: |
| lcl_ScRange_Format_XL_Header( r, *this, nFlags, pDoc, rDetails ); |
| if( aStart.Col() == 0 && aEnd.Col() >= MAXCOL ) |
| { |
| // Full col refs always require 2 rows (2:2) |
| lcl_a1_append_r( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE ); |
| r += ':'; |
| lcl_a1_append_r( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE ); |
| } |
| else if( aStart.Row() == 0 && aEnd.Row() >= MAXROW ) |
| { |
| // Full row refs always require 2 cols (A:A) |
| lcl_a1_append_c( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE ); |
| r += ':'; |
| lcl_a1_append_c( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE ); |
| } |
| else |
| { |
| lcl_a1_append_c ( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE ); |
| lcl_a1_append_r ( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE ); |
| if( aStart.Col() != aEnd.Col() || |
| absrel_differ( nFlags, SCA_COL_ABSOLUTE ) || |
| aStart.Row() != aEnd.Row() || |
| absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) { |
| r += ':'; |
| lcl_a1_append_c ( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE ); |
| lcl_a1_append_r ( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE ); |
| } |
| } |
| break; |
| |
| case formula::FormulaGrammar::CONV_XL_R1C1: |
| lcl_ScRange_Format_XL_Header( r, *this, nFlags, pDoc, rDetails ); |
| if( aStart.Col() == 0 && aEnd.Col() >= MAXCOL ) |
| { |
| lcl_r1c1_append_r( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE, rDetails ); |
| if( aStart.Row() != aEnd.Row() || |
| absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) { |
| r += ':'; |
| lcl_r1c1_append_r( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE, rDetails ); |
| } |
| } |
| else if( aStart.Row() == 0 && aEnd.Row() >= MAXROW ) |
| { |
| lcl_r1c1_append_c( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE, rDetails ); |
| if( aStart.Col() != aEnd.Col() || |
| absrel_differ( nFlags, SCA_COL_ABSOLUTE )) { |
| r += ':'; |
| lcl_r1c1_append_c( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE, rDetails ); |
| } |
| } |
| else |
| { |
| lcl_r1c1_append_r( r, aStart.Row(), nFlags & SCA_ROW_ABSOLUTE, rDetails ); |
| lcl_r1c1_append_c( r, aStart.Col(), nFlags & SCA_COL_ABSOLUTE, rDetails ); |
| if( aStart.Col() != aEnd.Col() || |
| absrel_differ( nFlags, SCA_COL_ABSOLUTE ) || |
| aStart.Row() != aEnd.Row() || |
| absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) { |
| r += ':'; |
| lcl_r1c1_append_r( r, aEnd.Row(), nFlags & SCA_ROW2_ABSOLUTE, rDetails ); |
| lcl_r1c1_append_c( r, aEnd.Col(), nFlags & SCA_COL2_ABSOLUTE, rDetails ); |
| } |
| } |
| } |
| #undef absrel_differ |
| } |
| |
| bool ScAddress::Move( SCsCOL dx, SCsROW dy, SCsTAB dz, ScDocument* pDoc ) |
| { |
| SCsTAB nMaxTab = pDoc ? pDoc->GetTableCount() : MAXTAB+1; |
| dx = Col() + dx; |
| dy = Row() + dy; |
| dz = Tab() + dz; |
| sal_Bool bValid = sal_True; |
| if( dx < 0 ) |
| dx = 0, bValid = sal_False; |
| else if( dx > MAXCOL ) |
| dx = MAXCOL, bValid =sal_False; |
| if( dy < 0 ) |
| dy = 0, bValid = sal_False; |
| else if( dy > MAXROW ) |
| dy = MAXROW, bValid =sal_False; |
| if( dz < 0 ) |
| dz = 0, bValid = sal_False; |
| else if( dz >= nMaxTab ) |
| dz = nMaxTab-1, bValid =sal_False; |
| Set( dx, dy, dz ); |
| return bValid; |
| } |
| |
| |
| bool ScRange::Move( SCsCOL dx, SCsROW dy, SCsTAB dz, ScDocument* pDoc ) |
| { |
| // Einfahces &, damit beides ausgefuehrt wird!! |
| return aStart.Move( dx, dy, dz, pDoc ) & aEnd.Move( dx, dy, dz, pDoc ); |
| } |
| |
| |
| String ScAddress::GetColRowString( bool bAbsolute, |
| const Details& rDetails ) const |
| { |
| String aString; |
| |
| switch( rDetails.eConv ) |
| { |
| default : |
| case formula::FormulaGrammar::CONV_OOO: |
| case formula::FormulaGrammar::CONV_XL_A1: |
| case formula::FormulaGrammar::CONV_XL_OOX: |
| if (bAbsolute) |
| aString.Append( '$' ); |
| |
| ScColToAlpha( aString, nCol); |
| |
| if ( bAbsolute ) |
| aString.Append( '$' ); |
| |
| aString += String::CreateFromInt32(nRow+1); |
| break; |
| |
| case formula::FormulaGrammar::CONV_XL_R1C1: |
| lcl_r1c1_append_r ( aString, nRow, bAbsolute, rDetails ); |
| lcl_r1c1_append_c ( aString, nCol, bAbsolute, rDetails ); |
| break; |
| } |
| |
| return aString; |
| } |
| |
| |
| String ScRefAddress::GetRefString( ScDocument* pDoc, SCTAB nActTab, |
| const ScAddress::Details& rDetails ) const |
| { |
| if ( !pDoc ) |
| return EMPTY_STRING; |
| if ( Tab()+1 > pDoc->GetTableCount() ) |
| return ScGlobal::GetRscString( STR_NOREF_STR ); |
| |
| String aString; |
| sal_uInt16 nFlags = SCA_VALID; |
| if ( nActTab != Tab() ) |
| { |
| nFlags |= SCA_TAB_3D; |
| if ( !bRelTab ) |
| nFlags |= SCA_TAB_ABSOLUTE; |
| } |
| if ( !bRelCol ) |
| nFlags |= SCA_COL_ABSOLUTE; |
| if ( !bRelRow ) |
| nFlags |= SCA_ROW_ABSOLUTE; |
| |
| aAdr.Format( aString, nFlags, pDoc, rDetails ); |
| |
| return aString; |
| } |
| |
| //------------------------------------------------------------------------ |
| |
| void ScColToAlpha( rtl::OUStringBuffer& rBuf, SCCOL nCol ) |
| { |
| if (nCol < 26*26) |
| { |
| if (nCol < 26) |
| rBuf.append( static_cast<sal_Unicode>( 'A' + |
| static_cast<sal_uInt16>(nCol))); |
| else |
| { |
| rBuf.append( static_cast<sal_Unicode>( 'A' + |
| (static_cast<sal_uInt16>(nCol) / 26) - 1)); |
| rBuf.append( static_cast<sal_Unicode>( 'A' + |
| (static_cast<sal_uInt16>(nCol) % 26))); |
| } |
| } |
| else |
| { |
| String aStr; |
| while (nCol >= 26) |
| { |
| SCCOL nC = nCol % 26; |
| aStr += static_cast<sal_Unicode>( 'A' + |
| static_cast<sal_uInt16>(nC)); |
| nCol = sal::static_int_cast<SCCOL>( nCol - nC ); |
| nCol = nCol / 26 - 1; |
| } |
| aStr += static_cast<sal_Unicode>( 'A' + |
| static_cast<sal_uInt16>(nCol)); |
| aStr.Reverse(); |
| rBuf.append( aStr); |
| } |
| } |
| |
| |
| bool AlphaToCol( SCCOL& rCol, const String& rStr) |
| { |
| SCCOL nResult = 0; |
| xub_StrLen nStop = rStr.Len(); |
| xub_StrLen nPos = 0; |
| sal_Unicode c; |
| while (nResult <= MAXCOL && nPos < nStop && (c = rStr.GetChar( nPos)) != 0 && |
| CharClass::isAsciiAlpha(c)) |
| { |
| if (nPos > 0) |
| nResult = (nResult + 1) * 26; |
| nResult += ScGlobal::ToUpperAlpha(c) - 'A'; |
| ++nPos; |
| } |
| bool bOk = (ValidCol(nResult) && nPos > 0); |
| if (bOk) |
| rCol = nResult; |
| return bOk; |
| } |