summaryrefslogtreecommitdiffstats
path: root/sc/source/core/tool/reffind.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/core/tool/reffind.cxx')
-rw-r--r--sc/source/core/tool/reffind.cxx334
1 files changed, 334 insertions, 0 deletions
diff --git a/sc/source/core/tool/reffind.cxx b/sc/source/core/tool/reffind.cxx
new file mode 100644
index 000000000..1d930dadf
--- /dev/null
+++ b/sc/source/core/tool/reffind.cxx
@@ -0,0 +1,334 @@
+/* -*- 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 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/underlyingenumvalue.hxx>
+
+#include <reffind.hxx>
+#include <global.hxx>
+#include <compiler.hxx>
+#include <document.hxx>
+
+namespace {
+
+// Include colon; addresses in range reference are handled individually.
+const sal_Unicode pDelimiters[] = {
+ '=','(',')','+','-','*','/','^','&',' ','{','}','<','>',':', 0
+};
+
+bool IsText( sal_Unicode c )
+{
+ 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;
+}
+
+bool IsText( bool& bQuote, sal_Unicode c )
+{
+ if (c == '\'')
+ {
+ 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 bracket.
+ 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);
+ }
+}
+
+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 bracket.
+ 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& rDoc, formula::FormulaGrammar::AddressConvention eConvP) :
+ maFormula(rFormula),
+ meConv(eConvP),
+ mrDoc(rDoc),
+ maPos(rPos),
+ mnFound(0),
+ mnSelStart(0),
+ mnSelEnd(0)
+{
+}
+
+ScRefFinder::~ScRefFinder()
+{
+}
+
+static ScRefFlags lcl_NextFlags( ScRefFlags nOld )
+{
+ const ScRefFlags Mask_ABS = ScRefFlags::COL_ABS | ScRefFlags::ROW_ABS | ScRefFlags::TAB_ABS;
+ ScRefFlags nNew = nOld & Mask_ABS;
+ nNew = ScRefFlags( o3tl::to_underlying(nNew) - 1 ) & Mask_ABS; // weiterzaehlen
+
+ if (!(nOld & ScRefFlags::TAB_3D))
+ nNew &= ~ScRefFlags::TAB_ABS; // not 3D -> never absolute!
+
+ return (nOld & ~Mask_ABS) | nNew;
+}
+
+void ScRefFinder::ToggleRel( sal_Int32 nStartPos, sal_Int32 nEndPos )
+{
+ sal_Int32 nLen = maFormula.getLength();
+ if (nLen <= 0)
+ return;
+ const sal_Unicode* pSource = maFormula.getStr(); // for quick access
+
+ // expand selection, and instead of selection start- and end-index
+
+ if ( nEndPos < nStartPos )
+ ::std::swap(nEndPos, nStartPos);
+
+ ExpandToText(pSource, nLen, nStartPos, nEndPos, meConv);
+
+ OUStringBuffer aResult;
+ OUString aExpr;
+ OUString aSep;
+ ScAddress aAddr;
+ mnFound = 0;
+
+ sal_Int32 nLoopStart = nStartPos;
+ 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);
+
+ // 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, mrDoc, aDetails, &aExtInfo);
+ if ( nResult & ScRefFlags::VALID )
+ {
+ 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);
+ std::u16string_view aExtDocNameTabName = aExpr.subView(0, nSep+1);
+ nResult = aAddr.Parse(aRef, mrDoc, aDetails);
+ aAddr.SetTab(0); // force to first tab to avoid error on checking
+ nFlags = lcl_NextFlags( nResult );
+ aExpr = aExtDocNameTabName + aAddr.Format(nFlags, &mrDoc, aDetails);
+ }
+ else
+ {
+ assert(!"Invalid syntax according to address convention.");
+ }
+ }
+ else
+ {
+ nFlags = lcl_NextFlags( nResult );
+ aExpr = aAddr.Format(nFlags, &mrDoc, aDetails);
+ }
+
+ sal_Int32 nAbsStart = nStartPos+aResult.getLength()+aSep.getLength();
+
+ if (!mnFound) // first reference ?
+ mnSelStart = nAbsStart;
+ mnSelEnd = nAbsStart + aExpr.getLength(); // selection, no indices
+ ++mnFound;
+ }
+
+ // assemble
+
+ aResult.append(aSep);
+ aResult.append(aExpr);
+
+ nLoopStart = nEEnd;
+ }
+
+ OUString aTotal = maFormula.subView(0, nStartPos) + aResult;
+ if (nEndPos < maFormula.getLength()-1)
+ aTotal += maFormula.subView(nEndPos+1);
+
+ maFormula = aTotal;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */