/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * 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 .
*/
const sal_Unicode* parseQuotedNameWithBuffer( const sal_Unicode* pStart, const sal_Unicode* p, OUString& rName )
{ // The current character must be on the 2nd quote.
// Push all the characters up to the current, but skip the very first // character which is the opening quote.
OUStringBuffer aBuf(std::u16string_view(pStart+1, p-pStart-1));
++p; // Skip the 2nd quote.
sal_Unicode cPrev = 0; for (; *p; ++p)
{ if (*p == '\'')
{ if (cPrev == '\'')
{ // double single-quote equals one single quote.
aBuf.append(*p);
cPrev = 0; continue;
}
} elseif (cPrev == '\'')
{ // We are past the closing quote. We're done!
rName = aBuf.makeStringAndClear(); return p;
} else
aBuf.append(*p);
cPrev = *p;
}
return pStart;
}
/** * 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.
*/ const sal_Unicode* parseQuotedName( const sal_Unicode* p, OUString& rName )
{ 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. return parseQuotedNameWithBuffer(pStart, p, rName);
}
} elseif (cPrev == '\'')
{ // We are past the closing quote. We're done! Skip the opening // and closing quotes.
rName = OUString(pStart+1, p - pStart-2); return p;
}
while (rtl::isAsciiDigit( *p ))
{ int val = *p - '0'; if (accum < cutoff || (accum == cutoff && val > cutlim))
{
*pEnd = nullptr; return 0;
}
accum = accum * 10 - val;
p++;
}
*pEnd = p; return is_neg ? accum : -accum;
}
staticconst sal_Unicode* lcl_eatWhiteSpace( const sal_Unicode* p )
{ if ( p )
{ while( *p == ' ' )
++p;
} return p;
}
// Compare ignore case ASCII. staticbool lcl_isString( const sal_Unicode* p1, const OUString& rStr )
{ const size_t n = rStr.getLength(); if (!n) returnfalse; const sal_Unicode* p2 = rStr.getStr(); for (size_t i=0; i<n; ++i)
{ if (!p1[i]) returnfalse; if (p1[i] != p2[i])
{
sal_Unicode c1 = p1[i]; if ('A' <= c1 && c1 <= 'Z')
c1 += 0x20; if (c1 < 'a' || 'z' < c1) returnfalse; // not a letter
sal_Unicode c2 = p2[i]; if ('A' <= c2 && c2 <= 'Z')
c2 += 0x20; if (c2 < 'a' || 'z' < c2) returnfalse; // not a letter to match
if (c1 != c2) returnfalse; // lower case doesn't match either
}
} returntrue;
}
/** 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/>.
*/ staticbool lcl_ScRange_External_TabSpan(
ScRange & rRange,
ScRefFlags & rFlags,
ScAddress::ExternalInfo* pExtInfo, const OUString & rExternDocName, const OUString & rStartTabName, const OUString & rEndTabName, const ScDocument& rDoc )
{ if (rExternDocName.isEmpty()) return !pExtInfo || !pExtInfo->mbExternal;
ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager(); if (pRefMgr->isOwnDocument( rExternDocName))
{ // This is an internal document. Get the sheet positions from the // ScDocument instance. if (!rStartTabName.isEmpty())
{
SCTAB nTab; if (rDoc.GetTable(rStartTabName, nTab))
rRange.aStart.SetTab(nTab);
}
if (!rEndTabName.isEmpty())
{
SCTAB nTab; if (rDoc.GetTable(rEndTabName, nTab))
rRange.aEnd.SetTab(nTab);
} return !pExtInfo || !pExtInfo->mbExternal;
}
/** 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
*/ staticconst sal_Unicode * lcl_XL_ParseSheetRef( const sal_Unicode* start,
OUString& rExternTabName, bool bAllow3D, const sal_Unicode* pMsoxlQuoteStop, const OUString* pErrRef )
{
OUString 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 += std::u16string_view( pCurrentStart,
sal::static_int_cast<sal_Int32>( p - pCurrentStart));
pCurrentStart = ++p;
}
} elseif (*p == ':')
{ break; // while
} else
++p;
} if (pCurrentStart < p)
aTabName += std::u16string_view( pCurrentStart, sal::static_int_cast<sal_Int32>( p - pCurrentStart)); if (aTabName.isEmpty()) return nullptr; if (p == pMsoxlQuoteStop && *pMsoxlQuoteStop == '\'')
++p; // position on ! of ...'!... if( *p != '!' && ( !bAllow3D || *p != ':' ) ) return (!bAllow3D && *p == ':') ? p : start;
} elseif( *p == '\'')
{
p = parseQuotedName(p, aTabName); if (aTabName.isEmpty()) return nullptr;
} elseif (pErrRef && lcl_isString( p, *pErrRef) && p[pErrRef->getLength()] == '!')
{
p += pErrRef->getLength(); // position after "#REF!" on '!' // XXX NOTE: caller has to check the name and that it wasn't quoted.
aTabName = *pErrRef;
} else
{ bool only_digits = 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( true )
{ const sal_Unicode uc = *p; if( rtl::isAsciiAlpha( uc ) || uc == '_' )
{ if( only_digits && p != start &&
(uc == 'e' || uc == 'E' ) )
{
p = start; break;
}
only_digits = false;
p++;
} elseif( rtl::isAsciiDigit( uc ))
{
p++;
} elseif( uc == '.' )
{ if( only_digits ) // Valid, except after only digits.
{
p = start; break;
}
p++;
} elseif (uc > 127)
{ // non ASCII character is allowed.
++p;
} else break;
}
/** Tries to obtain the external document index and replace by actual document name.
@param ppErrRet Contains the default pointer the caller would return if this method returns FALSE, may be replaced by NULL for type or data errors.
@returns FALSE only if the input name is numeric and not within the index sequence, or the link type cannot be determined or data mismatch. Returns TRUE in all other cases, also when there is no index sequence or the input name is not numeric.
*/ staticbool lcl_XL_getExternalDoc( const sal_Unicode** ppErrRet, OUString& rExternDocName, const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
{ // 1-based, sequence starts with an empty element. 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()) returnfalse; // with default *ppErrRet const sheet::ExternalLinkInfo & rInfo = (*pExternalLinks)[i]; switch (rInfo.Type)
{ case sheet::ExternalLinkType::DOCUMENT :
{
OUString aStr; if (!(rInfo.Data >>= aStr))
{
SAL_INFO( "sc.core", "Data type mismatch for ExternalLinkInfo "
<< i);
*ppErrRet = nullptr; returnfalse;
}
rExternDocName = aStr;
} break; case sheet::ExternalLinkType::SELF : returnfalse; // ??? case sheet::ExternalLinkType::SPECIAL : // silently return nothing (do not assert), caller has to handle this
*ppErrRet = nullptr; returnfalse; default:
SAL_INFO( "sc.core", "unhandled ExternalLinkType " << rInfo.Type
<< " for index " << i);
*ppErrRet = nullptr; returnfalse;
}
}
} returntrue;
}
rExternDocName = ScGlobal::GetAbsDocName(rExternDocName, rDoc.GetDocumentShell());
} elseif (*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! // Then again, 'Sheet1':'Sheet2'!C4 would be logical and Calc wrote // that to OOXML but Excel instead does 'Sheet1:Sheet2'!C4 which is // the worse you can get. // Excel does not allow [ and ] and : characters in sheet names though. // But, more sickness comes with MOOXML as there may be // '[1]Sheet 4'!$A$1 where [1] is the external doc's index.
p = parseQuotedName(p, rExternDocName); if (*p == ':')
{ // The incorrect 'Sheet1':'Sheet2' case. Just fall through.
} elseif (*p != '!')
{
rExternDocName.clear(); return start;
} if (!rExternDocName.isEmpty())
{
sal_Int32 nOpen = rExternDocName.indexOf( '['); if (nOpen == -1)
{
rExternDocName.clear(); // Look for 'Sheet1:Sheet2'! if (*p == '!')
{ const sal_Unicode* pQ = start + 1; do
{ if (*pQ == ':')
{
pMsoxlQuoteStop = pQ;
pQuoted3DStop = p - 1; break;
}
} while (++pQ < p);
}
} else
{
sal_Int32 nClose = rExternDocName.indexOf( ']', nOpen+1); if (nClose == -1)
rExternDocName.clear(); else
{
rExternDocName = rExternDocName.copy(0, nClose);
rExternDocName = rExternDocName.replaceAt( nOpen, 1, u"");
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;
// Handle '[1]Sheet 4'!$A$1 if (nOpen == 0)
{ const sal_Unicode* pErrRet = start; if (!lcl_XL_getExternalDoc( &pErrRet, rExternDocName, pExternalLinks)) return pErrRet;
}
}
}
} if (rExternDocName.isEmpty())
p = (pQuoted3DStop ? start + 1 : start);
}
startTabs = p;
p = lcl_XL_ParseSheetRef( p, rStartTabName, !bOnlyAcceptSingle, pMsoxlQuoteStop, pErrRef); if( nullptr == p ) return start; // invalid tab if (bOnlyAcceptSingle && *p == ':') return nullptr; // 3D const sal_Unicode* startEndTabs = nullptr; if( p != startTabs )
{
nFlags |= ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D | ScRefFlags::TAB_ABS; if( *p == ':' ) // 3d ref
{
startEndTabs = p + 1;
p = lcl_XL_ParseSheetRef( startEndTabs, rEndTabName, false,
(pQuoted3DStop ? pQuoted3DStop : pMsoxlQuoteStop), pErrRef); if( p == nullptr )
{
nFlags = nSaveFlags; return start; // invalid tab
}
nFlags |= ScRefFlags::TAB2_VALID | ScRefFlags::TAB2_3D | ScRefFlags::TAB2_ABS;
} else
{ // If only one sheet is given, the full reference is still valid, // only the second 3D flag is not set.
nFlags |= ScRefFlags::TAB2_VALID | ScRefFlags::TAB2_ABS;
aEnd.SetTab( aStart.Tab() );
}
if( *p++ != '!' )
{
nFlags = nSaveFlags; return start; // syntax error
} else
p = lcl_eatWhiteSpace( p );
} else
{
nFlags |= ScRefFlags::TAB_VALID | ScRefFlags::TAB2_VALID; // Use the current tab, it needs to be passed in. : aEnd.SetTab( .. );
}
if (!rExternDocName.isEmpty())
{
ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
pRefMgr->convertToAbsName(rExternDocName);
} else
{ // Internal reference. if (rStartTabName.isEmpty())
{
nFlags = nSaveFlags; return start;
}
if( *p != 'C' && *p != 'c' ) // full row R#
{ if( p[0] != ':' || (p[1] != 'R' && p[1] != 'r' ) ||
nullptr == (pTmp = lcl_r1c1_get_row( rDoc.GetSheetLimits(), 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
applyStartToEndFlags(nFlags);
r.aEnd.SetRow( r.aStart.Row() );
} else// pTmp != nullptr
{ // Full row range successfully parsed.
applyStartToEndFlags(nFlags, nFlags2);
p = pTmp;
}
if (p[0] != 0)
{ // any trailing invalid character must invalidate the whole address.
nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID); return nFlags;
}
ScRefFlags nBailOutFlags = ScRefFlags::ZERO; if (pSheetEndPos && pStart < p && (nFlags & ScRefFlags::TAB_VALID) && (nFlags & ScRefFlags::TAB_3D))
{
*pSheetEndPos = p - pStart;
nBailOutFlags = ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D;
}
if (!aExternDocName.isEmpty())
lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName,
aStartTabName, aEndTabName, rDoc);
if( nullptr == p ) return nBailOutFlags;
tmp1 = lcl_a1_get_col( rDoc, p, &r.aStart, &nFlags, pErrRef); if( tmp1 == nullptr ) // Is it a row only reference 3:5
{ if( bOnlyAcceptSingle ) // by definition full row refs are ranges return nBailOutFlags;
tmp2 = lcl_a1_get_row( rDoc, tmp1, &r.aStart, &nFlags, pErrRef); if( tmp2 == nullptr ) // check for col only reference F:H
{ if( bOnlyAcceptSingle ) // by definition full col refs are ranges return nBailOutFlags;
tmp1 = lcl_eatWhiteSpace( tmp1 ); if( *tmp1++ != ':' ) // Even a singleton requires ':' (eg F:F) return nBailOutFlags;
tmp1 = lcl_eatWhiteSpace( tmp1 );
tmp2 = lcl_a1_get_col( rDoc, tmp1, &r.aEnd, &nFlags2, pErrRef); if( !tmp2 || *tmp2 != 0 ) // Must have fully parsed a singleton. return nBailOutFlags;
// 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 &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID); 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 & ScRefFlags::TAB2_3D))
{ if (nFlags & ScRefFlags::COL_ABS)
nFlags |= ScRefFlags::COL2_ABS; if (nFlags & ScRefFlags::ROW_ABS)
nFlags |= ScRefFlags::ROW2_ABS;
} else
nFlags &= ~ScRefFlags(ScRefFlags::VALID |
ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID); return nFlags;
}
p = lcl_eatWhiteSpace( tmp2+1 ); // after ':'
tmp1 = lcl_a1_get_col( rDoc, p, &r.aEnd, &nFlags2, pErrRef); if( !tmp1 && aEndTabName.isEmpty() ) // Probably the aEndTabName was specified after the first range
{
p = lcl_XL_ParseSheetRef( p, aEndTabName, false, nullptr, pErrRef); if( p )
{
SCTAB nTab = 0; if( !aEndTabName.isEmpty() && rDoc.GetTable( aEndTabName, nTab ) )
{
r.aEnd.SetTab( nTab );
nFlags |= ScRefFlags::TAB2_VALID | ScRefFlags::TAB2_3D | ScRefFlags::TAB2_ABS;
} if (*p == '!' || *p == ':')
p = lcl_eatWhiteSpace( p+1 );
tmp1 = lcl_a1_get_col( rDoc, p, &r.aEnd, &nFlags2, pErrRef);
}
} if( !tmp1 ) // strange, but maybe valid singleton return isValidSingleton( nFlags, nFlags2) ? nFlags : (nFlags & ~ScRefFlags::VALID);
/** @param p pointer to null-terminated sal_Unicode string @param rRawRes returns ScRefFlags::... flags without the final check for full validity that is applied to the return value, with which two addresses that form a column or row singleton range, e.g. A:A or 1:1, can be detected. Used in lcl_ScRange_Parse_OOo(). @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 ScRefFlags lcl_ScAddress_Parse_OOo( const sal_Unicode* p, const ScDocument& rDoc, ScAddress& rAddr,
ScRefFlags& rRawRes,
ScAddress::ExternalInfo* pExtInfo,
ScRange* pRange,
sal_Int32* pSheetEndPos, const OUString* pErrRef )
{ const sal_Unicode* const pStart = p; if (pSheetEndPos)
*pSheetEndPos = 0;
ScRefFlags nRes = ScRefFlags::ZERO;
rRawRes = ScRefFlags::ZERO;
OUString aDocName; // the pure Document Name
OUString aTab; bool bExtDoc = false; bool bExtDocInherited = false;
// Let's see if this is a reference to something in an external file. A // document name is always quoted and has a trailing #. if (*p == '\'')
{
OUString aTmp;
p = parseQuotedName(p, aTmp);
aDocName = aTmp; if (*p++ == SC_COMPILER_FILE_TAB_SEP)
bExtDoc = true; else // This is not a document name. Perhaps a quoted relative table // name.
p = pStart;
} elseif (pExtInfo && pExtInfo->mbExternal)
{ // This is an external reference.
bExtDoc = bExtDocInherited = true;
}
if (pErrRef && lcl_isString( p, *pErrRef) && p[pErrRef->getLength()] == '.')
{ // #REF! particle of an invalidated reference plus sheet separator.
p += pErrRef->getLength() + 1;
nRes &= ~ScRefFlags::TAB_VALID;
nTab = -1;
} else
{ 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 = parseQuotedName(p, aTab);
} else
{
OUStringBuffer aTabAcc; while (*p)
{ if( *p == '.') break;
q = p; if (*p)
{
nBits = ScRefFlags::ROW_VALID; if (*p == '$')
{
nBits |= ScRefFlags::ROW_ABS;
p++;
}
if (pErrRef && lcl_isString( p, *pErrRef))
{ // #REF! particle of an invalidated reference.
p += pErrRef->getLength(); // Clearing the ROW_VALID bit here is not possible because of the // check at the end whether only a valid column was detected in // which case all bits are cleared because it could be any other // name. Instead, set to an absolute invalid row value. This will // display a $#REF! instead of #REF! if the error value was // relative, but live with it.
nBits |= ScRefFlags::ROW_ABS;
nRow = -1;
} else
{ if( !rtl::isAsciiDigit( *p ) )
{
nBits = ScRefFlags::ZERO;
nRow = -1;
} else
{
sal_Int64 n = rtl_ustr_toInt32( p, 10 ) - 1; while (rtl::isAsciiDigit( *p ))
p++; const SCROW nMaxRow = rDoc.MaxRow(); if( n < 0 || n > nMaxRow )
nBits = ScRefFlags::ZERO;
nRow = sal::static_int_cast<SCROW>(n);
} if( nBits == ScRefFlags::ZERO )
p = q;
}
nRes |= nBits;
}
rAddr.Set( nCol, nRow, nTab );
if (!*p && bExtDoc)
{
ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
// Need document name if inherited. if (bExtDocInherited)
{ // The FileId was created using the original file name, so // obtain that. Otherwise lcl_ScRange_External_TabSpan() would // retrieve a FileId for the real name and bail out if that // differed from pExtInfo->mnFileId, as is the case when // loading documents that refer external files relative to the // current own document but were saved from a different path // than loaded. const OUString* pFileName = pRefMgr->getExternalFileName( pExtInfo->mnFileId, true); if (pFileName)
aDocName = *pFileName; else
nRes = ScRefFlags::ZERO;
}
pRefMgr->convertToAbsName(aDocName);
if ((!pExtInfo || !pExtInfo->mbExternal) && pRefMgr->isOwnDocument(aDocName))
{ if (!rDoc.GetTable( aTab, nTab ))
nRes = ScRefFlags::ZERO; else
{
rAddr.SetTab( nTab);
nRes |= ScRefFlags::TAB_VALID;
}
} else
{ if (!pExtInfo)
nRes = ScRefFlags::ZERO; else
{ if (!pExtInfo->mbExternal)
{
sal_uInt16 nFileId = pRefMgr->getExternalFileId(aDocName);
if (pRefMgr->getSingleRefToken(nFileId, aTab,
ScAddress(nCol, nRow, 0), nullptr,
&nTab))
{
rAddr.SetTab( nTab);
nRes |= ScRefFlags::TAB_VALID;
} else
nRes = ScRefFlags::ZERO;
} else
{ // This is a call for the second part of the reference, // we must have the range to adapt tab span. if (!pRange)
nRes = ScRefFlags::ZERO; else
{
ScRefFlags nFlags = nRes | ScRefFlags::TAB2_VALID; if (!lcl_ScRange_External_TabSpan( *pRange, nFlags,
pExtInfo, aDocName,
pExtInfo->maTabName, aTab, rDoc))
nRes &= ~ScRefFlags::TAB_VALID; else
{ if (nFlags & ScRefFlags::TAB2_VALID)
{
rAddr.SetTab( pRange->aEnd.Tab());
nRes |= ScRefFlags::TAB_VALID;
} else
nRes &= ~ScRefFlags::TAB_VALID;
}
}
}
}
}
} elseif (bExtDoc && pExtInfo && !bExtDocInherited && !pExtInfo->mbExternal && pSheetEndPos)
{ // Pass partial info up to caller, there may be an external name // following, and if after *pSheetEndPos it's external sheet-local. // For global names aTab is empty and *pSheetEndPos==0.
pExtInfo->mbExternal = true;
pExtInfo->maTabName = aTab;
pExtInfo->mnFileId = rDoc.GetExternalRefManager()->getExternalFileId(aDocName);
}
rRawRes |= nRes;
if ( !(nRes & ScRefFlags::ROW_VALID) && (nRes & ScRefFlags::COL_VALID)
&& !( (nRes & ScRefFlags::TAB_3D) && (nRes & ScRefFlags::TAB_VALID)) )
{ // no Row, no Tab, but Col => DM (...), B (...) et al
nRes = ScRefFlags::ZERO;
} if( !*p )
{
ScRefFlags nMask = nRes & ( ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID ); if( nMask == ( ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID ) )
nRes |= ScRefFlags::VALID;
} else
nRes = rRawRes = nBailOutFlags; return nRes;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.