1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <o3tl/underlyingenumvalue.hxx>
24 #include <reffind.hxx>
26 #include <compiler.hxx>
27 #include <document.hxx>
32 // Include colon; addresses in range reference are handled individually.
33 const sal_Unicode pDelimiters
[] = {
34 '=','(',')','+','-','*','/','^','&',' ','{','}','<','>',':', 0
37 bool IsText( sal_Unicode c
)
39 bool bFound
= ScGlobal::UnicodeStrChr( pDelimiters
, c
);
41 // This is one of delimiters, therefore not text.
44 // argument separator is configurable.
45 const sal_Unicode sep
= ScCompiler::GetNativeSymbolChar(ocSep
);
49 bool IsText( bool& bQuote
, sal_Unicode c
)
63 * Find first character position that is considered text. A character is
64 * considered a text when it's within the ascii range and when it's not a
67 sal_Int32
FindStartPos(const sal_Unicode
* p
, sal_Int32 nStartPos
, sal_Int32 nEndPos
)
69 while (nStartPos
<= nEndPos
&& !IsText(p
[nStartPos
]))
75 sal_Int32
FindEndPosA1(const sal_Unicode
* p
, sal_Int32 nStartPos
, sal_Int32 nEndPos
)
78 sal_Int32 nNewEnd
= nStartPos
;
79 while (nNewEnd
<= nEndPos
&& IsText(bQuote
, p
[nNewEnd
]))
85 sal_Int32
FindEndPosR1C1(const sal_Unicode
* p
, sal_Int32 nStartPos
, sal_Int32 nEndPos
)
87 sal_Int32 nNewEnd
= nStartPos
;
89 for (; nNewEnd
<= nEndPos
; ++p
, ++nNewEnd
)
93 // Skip until the closing quote.
94 for (++p
, ++nNewEnd
; nNewEnd
<= nEndPos
; ++p
, ++nNewEnd
)
97 if (nNewEnd
> nEndPos
)
102 // Skip until the closing bracket.
103 for (++p
, ++nNewEnd
; nNewEnd
<= nEndPos
; ++p
, ++nNewEnd
)
106 if (nNewEnd
> nEndPos
)
109 else if (!IsText(*p
))
117 * Find last character position that is considered text, from the specified
120 sal_Int32
FindEndPos(const sal_Unicode
* p
, sal_Int32 nStartPos
, sal_Int32 nEndPos
,
121 formula::FormulaGrammar::AddressConvention eConv
)
125 case formula::FormulaGrammar::CONV_XL_R1C1
:
126 return FindEndPosR1C1(p
, nStartPos
, nEndPos
);
127 case formula::FormulaGrammar::CONV_OOO
:
128 case formula::FormulaGrammar::CONV_XL_A1
:
130 return FindEndPosA1(p
, nStartPos
, nEndPos
);
134 void ExpandToTextA1(const sal_Unicode
* p
, sal_Int32 nLen
, sal_Int32
& rStartPos
, sal_Int32
& rEndPos
)
136 bool bQuote
= false; // skip quoted text
137 while (rStartPos
> 0 && IsText(bQuote
, p
[rStartPos
- 1]) )
141 while (rEndPos
+1 < nLen
&& IsText(p
[rEndPos
+ 1]) )
145 void ExpandToTextR1C1(const sal_Unicode
* p
, sal_Int32 nLen
, sal_Int32
& rStartPos
, sal_Int32
& rEndPos
)
147 // move back the start position to the first text character.
150 for (--rStartPos
; rStartPos
> 0; --rStartPos
)
152 sal_Unicode c
= p
[rStartPos
];
155 // Skip until the opening quote.
156 for (--rStartPos
; rStartPos
> 0; --rStartPos
)
167 // Skip until the opening bracket.
168 for (--rStartPos
; rStartPos
> 0; --rStartPos
)
185 // move forward the end position to the last text character.
186 rEndPos
= FindEndPosR1C1(p
, rEndPos
, nLen
-1);
189 void ExpandToText(const sal_Unicode
* p
, sal_Int32 nLen
, sal_Int32
& rStartPos
, sal_Int32
& rEndPos
,
190 formula::FormulaGrammar::AddressConvention eConv
)
194 case formula::FormulaGrammar::CONV_XL_R1C1
:
195 ExpandToTextR1C1(p
, nLen
, rStartPos
, rEndPos
);
197 case formula::FormulaGrammar::CONV_OOO
:
198 case formula::FormulaGrammar::CONV_XL_A1
:
200 ExpandToTextA1(p
, nLen
, rStartPos
, rEndPos
);
206 ScRefFinder::ScRefFinder(
207 OUString aFormula
, const ScAddress
& rPos
,
208 ScDocument
& rDoc
, formula::FormulaGrammar::AddressConvention eConvP
) :
209 maFormula(std::move(aFormula
)),
219 ScRefFinder::~ScRefFinder()
223 static ScRefFlags
lcl_NextFlags( ScRefFlags nOld
)
225 const ScRefFlags Mask_ABS
= ScRefFlags::COL_ABS
| ScRefFlags::ROW_ABS
| ScRefFlags::TAB_ABS
;
226 ScRefFlags nNew
= nOld
& Mask_ABS
;
227 nNew
= ScRefFlags( o3tl::to_underlying(nNew
) - 1 ) & Mask_ABS
; // weiterzaehlen
229 if (!(nOld
& ScRefFlags::TAB_3D
))
230 nNew
&= ~ScRefFlags::TAB_ABS
; // not 3D -> never absolute!
232 return (nOld
& ~Mask_ABS
) | nNew
;
235 void ScRefFinder::ToggleRel( sal_Int32 nStartPos
, sal_Int32 nEndPos
)
237 sal_Int32 nLen
= maFormula
.getLength();
240 const sal_Unicode
* pSource
= maFormula
.getStr(); // for quick access
242 // expand selection, and instead of selection start- and end-index
244 if ( nEndPos
< nStartPos
)
245 ::std::swap(nEndPos
, nStartPos
);
247 ExpandToText(pSource
, nLen
, nStartPos
, nEndPos
, meConv
);
249 OUStringBuffer aResult
;
255 sal_Int32 nLoopStart
= nStartPos
;
256 while ( nLoopStart
<= nEndPos
)
258 // Determine the start and end positions of a text segment. Note that
259 // the end position returned from FindEndPos may be one position after
260 // the last character position in case of the last segment.
261 sal_Int32 nEStart
= FindStartPos(pSource
, nLoopStart
, nEndPos
);
262 sal_Int32 nEEnd
= FindEndPos(pSource
, nEStart
, nEndPos
, meConv
);
264 aSep
= maFormula
.copy(nLoopStart
, nEStart
-nLoopStart
);
265 if (nEEnd
< maFormula
.getLength())
266 aExpr
= maFormula
.copy(nEStart
, nEEnd
-nEStart
);
268 aExpr
= maFormula
.copy(nEStart
);
270 // Check the validity of the expression, and toggle the relative flag.
271 ScAddress::Details
aDetails(meConv
, maPos
.Row(), maPos
.Col());
272 ScAddress::ExternalInfo aExtInfo
;
273 ScRefFlags nResult
= aAddr
.Parse(aExpr
, mrDoc
, aDetails
, &aExtInfo
);
274 if ( nResult
& ScRefFlags::VALID
)
277 if( aExtInfo
.mbExternal
)
278 { // retain external doc name and tab name before toggle relative flag
282 case formula::FormulaGrammar::CONV_XL_A1
:
283 case formula::FormulaGrammar::CONV_XL_OOX
:
284 case formula::FormulaGrammar::CONV_XL_R1C1
:
285 nSep
= aExpr
.lastIndexOf('!');
287 case formula::FormulaGrammar::CONV_OOO
:
289 nSep
= aExpr
.lastIndexOf('.');
294 OUString aRef
= aExpr
.copy(nSep
+1);
295 std::u16string_view aExtDocNameTabName
= aExpr
.subView(0, nSep
+1);
296 nResult
= aAddr
.Parse(aRef
, mrDoc
, aDetails
);
297 aAddr
.SetTab(0); // force to first tab to avoid error on checking
298 nFlags
= lcl_NextFlags( nResult
);
299 aExpr
= aExtDocNameTabName
+ aAddr
.Format(nFlags
, &mrDoc
, aDetails
);
303 assert(!"Invalid syntax according to address convention.");
308 nFlags
= lcl_NextFlags( nResult
);
309 aExpr
= aAddr
.Format(nFlags
, &mrDoc
, aDetails
);
312 sal_Int32 nAbsStart
= nStartPos
+aResult
.getLength()+aSep
.getLength();
314 if (!mnFound
) // first reference ?
315 mnSelStart
= nAbsStart
;
316 mnSelEnd
= nAbsStart
+ aExpr
.getLength(); // selection, no indices
322 aResult
.append(aSep
+ aExpr
);
327 OUString aTotal
= maFormula
.subView(0, nStartPos
) + aResult
;
328 if (nEndPos
< maFormula
.getLength()-1)
329 aTotal
+= maFormula
.subView(nEndPos
+1);
334 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */