Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include "reffind.hxx"
21 : #include "global.hxx"
22 : #include "compiler.hxx"
23 : #include "document.hxx"
24 :
25 : // STATIC DATA
26 : namespace {
27 :
28 : // Include colon; addresses in range reference are handled individually.
29 : const sal_Unicode pDelimiters[] = {
30 : '=','(',')','+','-','*','/','^','&',' ','{','}','<','>',':', 0
31 : };
32 :
33 124 : inline bool IsText( sal_Unicode c )
34 : {
35 124 : bool bFound = ScGlobal::UnicodeStrChr( pDelimiters, c );
36 124 : if (bFound)
37 : // This is one of delimiters, therefore not text.
38 20 : return false;
39 :
40 : // argument separator is configurable.
41 104 : const sal_Unicode sep = ScCompiler::GetNativeSymbolChar(ocSep);
42 104 : return c != sep;
43 : }
44 :
45 40 : inline bool IsText( bool& bQuote, sal_Unicode c )
46 : {
47 40 : if (c == '\'')
48 : {
49 0 : bQuote = !bQuote;
50 0 : return true;
51 : }
52 40 : if (bQuote)
53 0 : return true;
54 :
55 40 : return IsText(c);
56 : }
57 :
58 : /**
59 : * Find first character position that is considered text. A character is
60 : * considered a text when it's within the ascii range and when it's not a
61 : * delimiter.
62 : */
63 16 : sal_Int32 FindStartPos(const sal_Unicode* p, sal_Int32 nStartPos, sal_Int32 nEndPos)
64 : {
65 44 : while (nStartPos <= nEndPos && !IsText(p[nStartPos]))
66 12 : ++nStartPos;
67 :
68 16 : return nStartPos;
69 : }
70 :
71 8 : sal_Int32 FindEndPosA1(const sal_Unicode* p, sal_Int32 nStartPos, sal_Int32 nEndPos)
72 : {
73 8 : bool bQuote = false;
74 8 : sal_Int32 nNewEnd = nStartPos;
75 48 : while (nNewEnd <= nEndPos && IsText(bQuote, p[nNewEnd]))
76 32 : ++nNewEnd;
77 :
78 8 : return nNewEnd;
79 : }
80 :
81 16 : sal_Int32 FindEndPosR1C1(const sal_Unicode* p, sal_Int32 nStartPos, sal_Int32 nEndPos)
82 : {
83 16 : sal_Int32 nNewEnd = nStartPos;
84 16 : p = &p[nStartPos];
85 56 : for (; nNewEnd <= nEndPos; ++p, ++nNewEnd)
86 : {
87 40 : if (*p == '\'')
88 : {
89 : // Skip until the closing quote.
90 0 : for (; nNewEnd <= nEndPos; ++p, ++nNewEnd)
91 0 : if (*p == '\'')
92 0 : break;
93 0 : if (nNewEnd > nEndPos)
94 0 : break;
95 : }
96 40 : else if (*p == '[')
97 : {
98 : // Skip until the closing braket.
99 32 : for (; nNewEnd <= nEndPos; ++p, ++nNewEnd)
100 32 : if (*p == ']')
101 8 : break;
102 8 : if (nNewEnd > nEndPos)
103 0 : break;
104 : }
105 32 : else if (!IsText(*p))
106 0 : break;
107 : }
108 :
109 16 : return nNewEnd;
110 : }
111 :
112 : /**
113 : * Find last character position that is considred text, from the specified
114 : * start position.
115 : */
116 16 : sal_Int32 FindEndPos(const sal_Unicode* p, sal_Int32 nStartPos, sal_Int32 nEndPos,
117 : formula::FormulaGrammar::AddressConvention eConv)
118 : {
119 16 : switch (eConv)
120 : {
121 : case formula::FormulaGrammar::CONV_XL_R1C1:
122 8 : return FindEndPosR1C1(p, nStartPos, nEndPos);
123 : case formula::FormulaGrammar::CONV_OOO:
124 : case formula::FormulaGrammar::CONV_XL_A1:
125 : default:
126 8 : return FindEndPosA1(p, nStartPos, nEndPos);
127 : }
128 : }
129 :
130 8 : void ExpandToTextA1(const sal_Unicode* p, sal_Int32 nLen, sal_Int32& rStartPos, sal_Int32& rEndPos)
131 : {
132 8 : bool bQuote = false; // skip quoted text
133 20 : while (rStartPos > 0 && IsText(bQuote, p[rStartPos - 1]) )
134 4 : --rStartPos;
135 8 : if (rEndPos)
136 8 : --rEndPos;
137 24 : while (rEndPos+1 < nLen && IsText(p[rEndPos + 1]) )
138 8 : ++rEndPos;
139 8 : }
140 :
141 8 : void ExpandToTextR1C1(const sal_Unicode* p, sal_Int32 nLen, sal_Int32& rStartPos, sal_Int32& rEndPos)
142 : {
143 : // move back the start position to the first text character.
144 8 : if (rStartPos > 0)
145 : {
146 20 : for (--rStartPos; rStartPos > 0; --rStartPos)
147 : {
148 16 : sal_Unicode c = p[rStartPos];
149 16 : if (c == '\'')
150 : {
151 : // Skip until the opening quote.
152 0 : for (--rStartPos; rStartPos > 0; --rStartPos)
153 : {
154 0 : c = p[rStartPos];
155 0 : if (c == '\'')
156 0 : break;
157 : }
158 0 : if (rStartPos == 0)
159 0 : break;
160 : }
161 16 : else if (c == ']')
162 : {
163 : // Skip until the opening braket.
164 12 : for (--rStartPos; rStartPos > 0; --rStartPos)
165 : {
166 12 : c = p[rStartPos];
167 12 : if (c == '[')
168 4 : break;
169 : }
170 4 : if (rStartPos == 0)
171 0 : break;
172 : }
173 12 : else if (!IsText(c))
174 : {
175 0 : ++rStartPos;
176 0 : break;
177 : }
178 : }
179 : }
180 :
181 : // move forward the end position to the last text character.
182 8 : rEndPos = FindEndPosR1C1(p, rEndPos, nLen-1);
183 8 : }
184 :
185 16 : void ExpandToText(const sal_Unicode* p, sal_Int32 nLen, sal_Int32& rStartPos, sal_Int32& rEndPos,
186 : formula::FormulaGrammar::AddressConvention eConv)
187 : {
188 16 : switch (eConv)
189 : {
190 : case formula::FormulaGrammar::CONV_XL_R1C1:
191 8 : ExpandToTextR1C1(p, nLen, rStartPos, rEndPos);
192 8 : break;
193 : case formula::FormulaGrammar::CONV_OOO:
194 : case formula::FormulaGrammar::CONV_XL_A1:
195 : default:
196 8 : ExpandToTextA1(p, nLen, rStartPos, rEndPos);
197 : }
198 16 : }
199 :
200 : }
201 :
202 4 : ScRefFinder::ScRefFinder(
203 : const OUString& rFormula, const ScAddress& rPos,
204 : ScDocument* pDoc, formula::FormulaGrammar::AddressConvention eConvP) :
205 : maFormula(rFormula),
206 : meConv(eConvP),
207 : mpDoc(pDoc),
208 : maPos(rPos),
209 : mnFound(0),
210 : mnSelStart(0),
211 4 : mnSelEnd(0)
212 : {
213 4 : }
214 :
215 4 : ScRefFinder::~ScRefFinder()
216 : {
217 4 : }
218 :
219 16 : static sal_uInt16 lcl_NextFlags( sal_uInt16 nOld )
220 : {
221 16 : sal_uInt16 nNew = nOld & 7; // die drei Abs-Flags
222 16 : nNew = ( nNew - 1 ) & 7; // weiterzaehlen
223 :
224 16 : if (!(nOld & SCA_TAB_3D))
225 16 : nNew &= ~SCA_TAB_ABSOLUTE; // not 3D -> never absolute!
226 :
227 16 : return ( nOld & 0xfff8 ) | nNew;
228 : }
229 :
230 16 : void ScRefFinder::ToggleRel( sal_Int32 nStartPos, sal_Int32 nEndPos )
231 : {
232 16 : sal_Int32 nLen = maFormula.getLength();
233 16 : if (nLen <= 0)
234 16 : return;
235 16 : const sal_Unicode* pSource = maFormula.getStr(); // for quick access
236 :
237 : // expand selection, and instead of selection start- and end-index
238 :
239 16 : if ( nEndPos < nStartPos )
240 0 : ::std::swap(nEndPos, nStartPos);
241 :
242 16 : ExpandToText(pSource, nLen, nStartPos, nEndPos, meConv);
243 :
244 16 : OUString aResult;
245 32 : OUString aExpr;
246 32 : OUString aSep;
247 16 : ScAddress aAddr;
248 16 : mnFound = 0;
249 :
250 16 : sal_Int32 nLoopStart = nStartPos;
251 48 : while ( nLoopStart <= nEndPos )
252 : {
253 : // Determine the start and end positions of a text segment. Note that
254 : // the end position returned from FindEndPos may be one position after
255 : // the last character position in case of the last segment.
256 16 : sal_Int32 nEStart = FindStartPos(pSource, nLoopStart, nEndPos);
257 16 : sal_Int32 nEEnd = FindEndPos(pSource, nEStart, nEndPos, meConv);
258 :
259 16 : aSep = maFormula.copy(nLoopStart, nEStart-nLoopStart);
260 16 : if (nEEnd < maFormula.getLength())
261 4 : aExpr = maFormula.copy(nEStart, nEEnd-nEStart);
262 : else
263 12 : aExpr = maFormula.copy(nEStart);
264 :
265 : // Check the validity of the expression, and toggle the relative flag.
266 16 : ScAddress::Details aDetails(meConv, maPos.Row(), maPos.Col());
267 16 : ScAddress::ExternalInfo aExtInfo;
268 16 : sal_uInt16 nResult = aAddr.Parse(aExpr, mpDoc, aDetails, &aExtInfo);
269 16 : if ( nResult & SCA_VALID )
270 : {
271 16 : sal_uInt16 nFlags = lcl_NextFlags( nResult );
272 16 : if( aExtInfo.mbExternal )
273 : { // retain external doc name and tab name before toggle relative flag
274 : sal_Int32 nSep;
275 0 : switch(meConv)
276 : {
277 : case formula::FormulaGrammar::CONV_XL_A1 :
278 : case formula::FormulaGrammar::CONV_XL_OOX :
279 : case formula::FormulaGrammar::CONV_XL_R1C1 :
280 0 : nSep = aExpr.lastIndexOf('!');
281 0 : break;
282 : case formula::FormulaGrammar::CONV_OOO :
283 : default:
284 0 : nSep = aExpr.lastIndexOf('.');
285 0 : break;
286 : }
287 0 : if (nSep < 0)
288 : {
289 : assert(!"Invalid syntax according to address convention.");
290 : }
291 : else
292 : {
293 0 : OUString aRef = aExpr.copy(nSep+1);
294 0 : OUString aExtDocNameTabName = aExpr.copy(0, nSep+1);
295 0 : nResult = aAddr.Parse(aRef, mpDoc, aDetails);
296 0 : aAddr.SetTab(0); // force to first tab to avoid error on checking
297 0 : nFlags = lcl_NextFlags( nResult );
298 0 : aExpr = aExtDocNameTabName + aAddr.Format(nFlags, mpDoc, aDetails);
299 : }
300 : }
301 : else
302 : {
303 16 : aExpr = aAddr.Format(nFlags, mpDoc, aDetails);
304 : }
305 :
306 16 : sal_Int32 nAbsStart = nStartPos+aResult.getLength()+aSep.getLength();
307 :
308 16 : if (!mnFound) // first reference ?
309 16 : mnSelStart = nAbsStart;
310 16 : mnSelEnd = nAbsStart + aExpr.getLength(); // selection, no indices
311 16 : ++mnFound;
312 : }
313 :
314 : // assemble
315 :
316 16 : aResult += aSep;
317 16 : aResult += aExpr;
318 :
319 16 : nLoopStart = nEEnd;
320 16 : }
321 :
322 16 : OUString aTotal = maFormula.copy(0, nStartPos);
323 16 : aTotal += aResult;
324 16 : if (nEndPos < maFormula.getLength()-1)
325 4 : aTotal += maFormula.copy(nEndPos+1);
326 :
327 32 : maFormula = aTotal;
328 156 : }
329 :
330 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|