Files
libreoffice/sc/source/core/tool/reffind.cxx

332 lines
10 KiB
C++
Raw Normal View History

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
re-base on ALv2 code. Includes: Patches contributed by Herbert Duerr i#118735 prevent endless loop if vlookup/hlookup doesn't find anything http://svn.apache.org/viewvc?view=revision&revision=1239673 Patches contributed by Andre Fischer remove lp_solver http://svn.apache.org/viewvc?view=revision&revision=1199180 i#118160: Added external CoinMP library. http://svn.apache.org/viewvc?view=revision&revision=1233909 Patches contributed by Armin Le-Grand i#118485 - Styles for OLEs are not saved. http://svn.apache.org/viewvc?view=revision&revision=1182166 i#118524: apply patch, followup fixes to 118485 http://svn.apache.org/viewvc?view=revision&revision=1186077 Patches contributed by lihuiibm i#108860 - Fix range validation. http://svn.apache.org/viewvc?view=revision&revision=1242846 i#118954 Chart data will lost after copy to different file http://svn.apache.org/viewvc?view=revision&revision=1301345 Patches contributed by Ariel Constenla-Haile Fix Linux build breaker: extra qualification on member http://svn.apache.org/viewvc?view=revision&revision=1301591 i#118696 - i#118697 - Fix some Sheet Tab Color API issues http://svn.apache.org/viewvc?view=revision&revision=1225428 i#118697 - Fix uninitialized variable http://svn.apache.org/viewvc?view=revision&revision=1225859 i#118771 - ScUndoImportTab should preserve tab background color http://svn.apache.org/viewvc?view=revision&revision=1230356 i#118921 - Repaint linked sheet tab background color after updating link http://svn.apache.org/viewvc?view=revision&revision=1245177 i#118927 - Undo/Redo "Update Link" does not reset sheet tab color http://svn.apache.org/viewvc?view=revision&revision=1245241 i#118747 - Copy tab color when transferring sheets across documents http://svn.apache.org/viewvc?view=revision&revision=1230355 Patch contributed by Oliver Rainer-Wittman i#118012 - methods <ScBroadcastAreaSlot::AreaBroadcast(..)> and <ScBroadcastAreaSlot::AreaBroadcastInRange(..)> adapt stl-container iteration in order to avoid destroyed iterators during iteration. http://svn.apache.org/viewvc?view=revision&revision=1297916 Patches contributed by Mathias Bauer gnumake4 work variously http://svn.apache.org/viewvc?view=revision&revision=1394707 http://svn.apache.org/viewvc?view=revision&revision=1394326 http://svn.apache.org/viewvc?view=revision&revision=1396797 http://svn.apache.org/viewvc?view=revision&revision=1397315 Patch contributed by Daniel Rentz calc69: #i116936# fix VBA symbol Cells http://svn.apache.org/viewvc?view=revision&revision=1172135 Patches contributed by leiw: i#118546 CPU 100% on switched off AutoCalculate with Conditional Formatting on date values http://svn.apache.org/viewvc?view=revision&revision=1301380 Re-add new function documentation. Many various cleanups. Add missing calc66: #o11817313# also look at formula result number format, remove redundant binaries.
2012-11-30 12:23:25 +00:00
/*
* 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 .
*/
2000-09-18 23:16:46 +00:00
#include "reffind.hxx"
#include "global.hxx"
#include "compiler.hxx"
#include "document.hxx"
2000-09-18 23:16:46 +00:00
namespace {
// Include colon; addresses in range reference are handled individually.
const sal_Unicode pDelimiters[] = {
'=','(',')','+','-','*','/','^','&',' ','{','}','<','>',':', 0
2000-09-18 23:16:46 +00:00
};
inline bool IsText( sal_Unicode c )
2000-09-18 23:16:46 +00:00
{
bool bFound = ScGlobal::UnicodeStrChr( pDelimiters, c );
if (bFound)
// This is one of delimiters, therefore not text.
return false;
// argument separator is configurable.
const sal_Unicode sep = ScCompiler::GetNativeSymbolChar(ocSep);
return c != sep;
2000-09-18 23:16:46 +00:00
}
inline bool IsText( bool& bQuote, sal_Unicode c )
2000-09-18 23:16:46 +00:00
{
if (c == '\'')
2000-09-18 23:16:46 +00:00
{
bQuote = !bQuote;
return true;
}
if (bQuote)
return true;
return IsText(c);
}
/**
* Find first character position that is considered text. A character is
* considered a text when it's within the ascii range and when it's not a
* delimiter.
*/
sal_Int32 FindStartPos(const sal_Unicode* p, sal_Int32 nStartPos, sal_Int32 nEndPos)
{
while (nStartPos <= nEndPos && !IsText(p[nStartPos]))
++nStartPos;
return nStartPos;
}
sal_Int32 FindEndPosA1(const sal_Unicode* p, sal_Int32 nStartPos, sal_Int32 nEndPos)
{
bool bQuote = false;
sal_Int32 nNewEnd = nStartPos;
while (nNewEnd <= nEndPos && IsText(bQuote, p[nNewEnd]))
++nNewEnd;
return nNewEnd;
}
sal_Int32 FindEndPosR1C1(const sal_Unicode* p, sal_Int32 nStartPos, sal_Int32 nEndPos)
{
sal_Int32 nNewEnd = nStartPos;
p = &p[nStartPos];
for (; nNewEnd <= nEndPos; ++p, ++nNewEnd)
{
if (*p == '\'')
{
// Skip until the closing quote.
for (; nNewEnd <= nEndPos; ++p, ++nNewEnd)
if (*p == '\'')
break;
if (nNewEnd > nEndPos)
break;
}
else if (*p == '[')
{
// Skip until the closing braket.
for (; nNewEnd <= nEndPos; ++p, ++nNewEnd)
if (*p == ']')
break;
if (nNewEnd > nEndPos)
break;
}
else if (!IsText(*p))
break;
}
return nNewEnd;
}
/**
* Find last character position that is considered text, from the specified
* start position.
*/
sal_Int32 FindEndPos(const sal_Unicode* p, sal_Int32 nStartPos, sal_Int32 nEndPos,
formula::FormulaGrammar::AddressConvention eConv)
{
switch (eConv)
{
case formula::FormulaGrammar::CONV_XL_R1C1:
return FindEndPosR1C1(p, nStartPos, nEndPos);
case formula::FormulaGrammar::CONV_OOO:
case formula::FormulaGrammar::CONV_XL_A1:
default:
return FindEndPosA1(p, nStartPos, nEndPos);
2000-09-18 23:16:46 +00:00
}
}
void ExpandToTextA1(const sal_Unicode* p, sal_Int32 nLen, sal_Int32& rStartPos, sal_Int32& rEndPos)
{
bool bQuote = false; // skip quoted text
while (rStartPos > 0 && IsText(bQuote, p[rStartPos - 1]) )
--rStartPos;
if (rEndPos)
--rEndPos;
while (rEndPos+1 < nLen && IsText(p[rEndPos + 1]) )
++rEndPos;
}
void ExpandToTextR1C1(const sal_Unicode* p, sal_Int32 nLen, sal_Int32& rStartPos, sal_Int32& rEndPos)
{
// move back the start position to the first text character.
if (rStartPos > 0)
{
for (--rStartPos; rStartPos > 0; --rStartPos)
{
sal_Unicode c = p[rStartPos];
if (c == '\'')
{
// Skip until the opening quote.
for (--rStartPos; rStartPos > 0; --rStartPos)
{
c = p[rStartPos];
if (c == '\'')
break;
}
if (rStartPos == 0)
break;
}
else if (c == ']')
{
// Skip until the opening braket.
for (--rStartPos; rStartPos > 0; --rStartPos)
{
c = p[rStartPos];
if (c == '[')
break;
}
if (rStartPos == 0)
break;
}
else if (!IsText(c))
{
++rStartPos;
break;
}
}
}
// move forward the end position to the last text character.
rEndPos = FindEndPosR1C1(p, rEndPos, nLen-1);
}
void ExpandToText(const sal_Unicode* p, sal_Int32 nLen, sal_Int32& rStartPos, sal_Int32& rEndPos,
formula::FormulaGrammar::AddressConvention eConv)
{
switch (eConv)
{
case formula::FormulaGrammar::CONV_XL_R1C1:
ExpandToTextR1C1(p, nLen, rStartPos, rEndPos);
break;
case formula::FormulaGrammar::CONV_OOO:
case formula::FormulaGrammar::CONV_XL_A1:
default:
ExpandToTextA1(p, nLen, rStartPos, rEndPos);
}
}
}
ScRefFinder::ScRefFinder(
const OUString& rFormula, const ScAddress& rPos,
ScDocument* pDoc, formula::FormulaGrammar::AddressConvention eConvP) :
maFormula(rFormula),
meConv(eConvP),
mpDoc(pDoc),
maPos(rPos),
mnFound(0),
mnSelStart(0),
mnSelEnd(0)
2000-09-18 23:16:46 +00:00
{
}
ScRefFinder::~ScRefFinder()
{
}
static ScRefFlags lcl_NextFlags( ScRefFlags nOld )
2000-09-18 23:16:46 +00:00
{
const ScRefFlags Mask_ABS = (ScRefFlags::COL_ABS | ScRefFlags::ROW_ABS | ScRefFlags::TAB_ABS);
ScRefFlags nNew = nOld & Mask_ABS;
nNew = ScRefFlags( (std::underlying_type<ScRefFlags>::type)(nNew) - 1 ) & Mask_ABS; // weiterzaehlen
2000-09-18 23:16:46 +00:00
if (!(nOld & ScRefFlags::TAB_3D))
nNew &= ~ScRefFlags::TAB_ABS; // not 3D -> never absolute!
2000-09-18 23:16:46 +00:00
return (nOld & ~Mask_ABS) | nNew;
2000-09-18 23:16:46 +00:00
}
void ScRefFinder::ToggleRel( sal_Int32 nStartPos, sal_Int32 nEndPos )
2000-09-18 23:16:46 +00:00
{
sal_Int32 nLen = maFormula.getLength();
if (nLen <= 0)
2000-09-18 23:16:46 +00:00
return;
const sal_Unicode* pSource = maFormula.getStr(); // for quick access
2000-09-18 23:16:46 +00:00
// expand selection, and instead of selection start- and end-index
2000-09-18 23:16:46 +00:00
if ( nEndPos < nStartPos )
::std::swap(nEndPos, nStartPos);
ExpandToText(pSource, nLen, nStartPos, nEndPos, meConv);
2000-09-18 23:16:46 +00:00
OUString aResult;
OUString aExpr;
OUString aSep;
2000-09-18 23:16:46 +00:00
ScAddress aAddr;
mnFound = 0;
2000-09-18 23:16:46 +00:00
sal_Int32 nLoopStart = nStartPos;
2000-09-18 23:16:46 +00:00
while ( nLoopStart <= nEndPos )
{
// Determine the start and end positions of a text segment. Note that
// the end position returned from FindEndPos may be one position after
// the last character position in case of the last segment.
sal_Int32 nEStart = FindStartPos(pSource, nLoopStart, nEndPos);
sal_Int32 nEEnd = FindEndPos(pSource, nEStart, nEndPos, meConv);
aSep = maFormula.copy(nLoopStart, nEStart-nLoopStart);
if (nEEnd < maFormula.getLength())
aExpr = maFormula.copy(nEStart, nEEnd-nEStart);
else
aExpr = maFormula.copy(nEStart);
2000-09-18 23:16:46 +00:00
// Check the validity of the expression, and toggle the relative flag.
ScAddress::Details aDetails(meConv, maPos.Row(), maPos.Col());
ScAddress::ExternalInfo aExtInfo;
ScRefFlags nResult = aAddr.Parse(aExpr, mpDoc, aDetails, &aExtInfo);
if ( nResult & ScRefFlags::VALID )
2000-09-18 23:16:46 +00:00
{
ScRefFlags nFlags;
if( aExtInfo.mbExternal )
{ // retain external doc name and tab name before toggle relative flag
sal_Int32 nSep;
switch(meConv)
{
case formula::FormulaGrammar::CONV_XL_A1 :
case formula::FormulaGrammar::CONV_XL_OOX :
case formula::FormulaGrammar::CONV_XL_R1C1 :
nSep = aExpr.lastIndexOf('!');
break;
case formula::FormulaGrammar::CONV_OOO :
default:
nSep = aExpr.lastIndexOf('.');
break;
}
if (nSep >= 0)
{
OUString aRef = aExpr.copy(nSep+1);
OUString aExtDocNameTabName = aExpr.copy(0, nSep+1);
nResult = aAddr.Parse(aRef, mpDoc, aDetails);
aAddr.SetTab(0); // force to first tab to avoid error on checking
nFlags = lcl_NextFlags( nResult );
aExpr = aExtDocNameTabName + aAddr.Format(nFlags, mpDoc, aDetails);
}
else
{
assert(!"Invalid syntax according to address convention.");
}
}
else
{
nFlags = lcl_NextFlags( nResult );
aExpr = aAddr.Format(nFlags, mpDoc, aDetails);
}
2000-09-18 23:16:46 +00:00
sal_Int32 nAbsStart = nStartPos+aResult.getLength()+aSep.getLength();
2000-09-18 23:16:46 +00:00
if (!mnFound) // first reference ?
mnSelStart = nAbsStart;
mnSelEnd = nAbsStart + aExpr.getLength(); // selection, no indices
++mnFound;
2000-09-18 23:16:46 +00:00
}
// assemble
2000-09-18 23:16:46 +00:00
aResult += aSep;
aResult += aExpr;
nLoopStart = nEEnd;
}
OUString aTotal = maFormula.copy(0, nStartPos);
2000-09-18 23:16:46 +00:00
aTotal += aResult;
if (nEndPos < maFormula.getLength()-1)
aTotal += maFormula.copy(nEndPos+1);
2000-09-18 23:16:46 +00:00
maFormula = aTotal;
2000-09-18 23:16:46 +00:00
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */