summaryrefslogtreecommitdiff
path: root/sc/source/core/tool/address.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/core/tool/address.cxx')
-rw-r--r--sc/source/core/tool/address.cxx2039
1 files changed, 2039 insertions, 0 deletions
diff --git a/sc/source/core/tool/address.cxx b/sc/source/core/tool/address.cxx
new file mode 100644
index 000000000000..16b821a28738
--- /dev/null
+++ b/sc/source/core/tool/address.cxx
@@ -0,0 +1,2039 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 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;
+using ::rtl::OUString;
+
+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() )
+{
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+
+#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 = 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;
+
+ // 1-based, sequence starts with an empty element.
+ if (pExternalLinks && pExternalLinks->getLength() > 1)
+ {
+ // 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))
+ {
+ OSL_TRACE( "ScRange::Parse_XL_Header: Data type mismatch for ExternalLinkInfo %d", i);
+ return NULL;
+ }
+ rExternDocName = aStr;
+ }
+ break;
+ default:
+ OSL_TRACE( "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;
+
+ 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;
+
+ 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 && !aEndTabName.Len() ) // Probably the aEndTabName was specified after the first range
+ {
+ p = lcl_XL_ParseSheetRef( p, aEndTabName, false, NULL );
+ if( p )
+ {
+ SCTAB nTab = 0;
+ if( aEndTabName.Len() && pDoc->GetTable( aEndTabName, nTab ) )
+ {
+ r.aEnd.SetTab( nTab );
+ nFlags |= SCA_VALID_TAB2 | SCA_TAB2_3D | SCA_TAB2_ABSOLUTE;
+ }
+ 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 )))
+ {
+ // Specified table name is not found in this document. Assume this is an external document.
+ bExtDoc = true;
+ aDocName = aTab;
+ xub_StrLen n = aTab.SearchBackward('.');
+ if (n != STRING_NOTFOUND && n > 0)
+ // Extension found. Strip it.
+ aTab.Erase(n);
+ else
+ // No extension found. This is probably not an external document.
+ 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; // keine Tokens aus halben Sachen
+ }
+ }
+ nRes1 = ( ( nRes1 | nRes2 ) & SCA_VALID )
+ | nRes1
+ | ( ( nRes2 & SCA_BITS ) << 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(aStart);//initialize with currentPos as fallback for table number
+ 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;
+
+ (void)pDoc; // 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;
+
+ (void)pDoc; // 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();
+ }
+ }
+ return sFileName;
+}
+
+void ScAddress::Format( OUString& r, sal_uInt16 nFlags, ScDocument* pDoc,
+ const Details& rDetails) const
+{
+ String aStr;
+ Format(aStr, nFlags, pDoc, rDetails);
+ r = aStr;
+}
+
+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_3D )
+ {
+ String aTabName, aDocName;
+ pDoc->GetName( nTab, aTabName );
+ // External Reference, same as in ScCompiler::MakeTabStr()
+ if( aTabName.GetChar(0) == '\'' )
+ { // "'Doc'#Tab"
+ xub_StrLen nPos = ScGlobal::FindUnquoted( aTabName, SC_COMPILER_FILE_TAB_SEP);
+ if (nPos != STRING_NOTFOUND && nPos > 0 && aTabName.GetChar(nPos-1) == '\'')
+ {
+ 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();
+ // External reference, same as in ScCompiler::MakeTabStr()
+ if ( rTabName.GetChar(0) == '\'' )
+ { // "'Doc'#Tab"
+ xub_StrLen nPos = ScGlobal::FindUnquoted( rTabName, SC_COMPILER_FILE_TAB_SEP);
+ if (nPos != STRING_NOTFOUND && nPos > 0 && rTabName.GetChar(nPos-1) == '\'')
+ {
+ 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
+}
+
+void ScRange::Format( OUString& r, sal_uInt16 nFlags, ScDocument* pDoc,
+ const ScAddress::Details& rDetails ) const
+{
+ String aStr;
+ Format(aStr, nFlags, pDoc, rDetails);
+ r = aStr;
+}
+
+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 = false;
+ else if( dx > MAXCOL )
+ dx = MAXCOL, bValid =false;
+ if( dy < 0 )
+ dy = 0, bValid = false;
+ else if( dy > MAXROW )
+ dy = MAXROW, bValid =false;
+ if( dz < 0 )
+ dz = 0, bValid = false;
+ else if( dz >= nMaxTab )
+ dz = nMaxTab-1, bValid =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;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */