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 <string.h>
21 :
22 : #include "reffind.hxx"
23 : #include "global.hxx"
24 : #include "compiler.hxx"
25 : #include "document.hxx"
26 :
27 : // STATIC DATA -----------------------------------------------------------
28 :
29 : namespace {
30 :
31 : // include colon -> duplicate referenced are handled individual
32 : const sal_Unicode pDelimiters[] = {
33 : '=','(',')','+','-','*','/','^','&',' ','{','}','<','>',':', 0
34 : };
35 :
36 : // =======================================================================
37 :
38 52 : inline bool IsText( sal_Unicode c )
39 : {
40 52 : bool bFound = ScGlobal::UnicodeStrChr( pDelimiters, c );
41 52 : if (bFound)
42 : // This is one of delimiters, therefore not text.
43 8 : return false;
44 :
45 : // argument separator is configurable.
46 44 : const sal_Unicode sep = ScCompiler::GetNativeSymbol(ocSep).GetChar(0);
47 44 : return c != sep;
48 : }
49 :
50 20 : inline bool IsText( bool& bQuote, sal_Unicode c )
51 : {
52 20 : if (c == '\'')
53 : {
54 0 : bQuote = !bQuote;
55 0 : return true;
56 : }
57 20 : if (bQuote)
58 0 : return true;
59 :
60 20 : return IsText(c);
61 : }
62 :
63 : /**
64 : * Find first character position that is considered text. A character is
65 : * considered a text when it's within the ascii range and when it's not a
66 : * delimiter.
67 : */
68 8 : xub_StrLen FindStartPos(const sal_Unicode* p, xub_StrLen nStartPos, xub_StrLen nEndPos)
69 : {
70 24 : while (nStartPos <= nEndPos && !IsText(p[nStartPos]))
71 8 : ++nStartPos;
72 :
73 8 : return nStartPos;
74 : }
75 :
76 4 : xub_StrLen FindEndPosA1(const sal_Unicode* p, xub_StrLen nStartPos, xub_StrLen nEndPos)
77 : {
78 4 : bool bQuote = false;
79 4 : xub_StrLen nNewEnd = nStartPos;
80 28 : while (nNewEnd <= nEndPos && IsText(bQuote, p[nNewEnd]))
81 20 : ++nNewEnd;
82 :
83 4 : return nNewEnd;
84 : }
85 :
86 8 : xub_StrLen FindEndPosR1C1(const sal_Unicode* p, xub_StrLen nStartPos, xub_StrLen nEndPos)
87 : {
88 8 : xub_StrLen nNewEnd = nStartPos;
89 8 : p = &p[nStartPos];
90 28 : for (; nNewEnd <= nEndPos; ++p, ++nNewEnd)
91 : {
92 20 : if (*p == '\'')
93 : {
94 : // Skip until the closing quote.
95 0 : for (; nNewEnd <= nEndPos; ++p, ++nNewEnd)
96 0 : if (*p == '\'')
97 0 : break;
98 : }
99 20 : else if (*p == '[')
100 : {
101 : // Skip until the closing braket.
102 16 : for (; nNewEnd <= nEndPos; ++p, ++nNewEnd)
103 16 : if (*p == ']')
104 4 : break;
105 : }
106 16 : else if (!IsText(*p))
107 0 : break;
108 : }
109 :
110 8 : return nNewEnd;
111 : }
112 :
113 : /**
114 : * Find last character position that is considred text, from the specified
115 : * start position.
116 : */
117 8 : xub_StrLen FindEndPos(const sal_Unicode* p, xub_StrLen nStartPos, xub_StrLen nEndPos,
118 : formula::FormulaGrammar::AddressConvention eConv)
119 : {
120 8 : switch (eConv)
121 : {
122 : case formula::FormulaGrammar::CONV_XL_R1C1:
123 4 : return FindEndPosR1C1(p, nStartPos, nEndPos);
124 : case formula::FormulaGrammar::CONV_OOO:
125 : case formula::FormulaGrammar::CONV_XL_A1:
126 : default:
127 4 : return FindEndPosA1(p, nStartPos, nEndPos);
128 : }
129 : }
130 :
131 4 : void ExpandToTextA1(const sal_Unicode* p, xub_StrLen nLen, xub_StrLen& rStartPos, xub_StrLen& rEndPos)
132 : {
133 8 : while (rStartPos > 0 && IsText(p[rStartPos - 1]) )
134 0 : --rStartPos;
135 4 : if (rEndPos)
136 4 : --rEndPos;
137 8 : while (rEndPos+1 < nLen && IsText(p[rEndPos + 1]) )
138 0 : ++rEndPos;
139 4 : }
140 :
141 4 : void ExpandToTextR1C1(const sal_Unicode* p, xub_StrLen nLen, xub_StrLen& rStartPos, xub_StrLen& rEndPos)
142 : {
143 : // move back the start position to the first text character.
144 4 : if (rStartPos > 0)
145 : {
146 0 : for (--rStartPos; rStartPos > 0; --rStartPos)
147 : {
148 0 : sal_Unicode c = p[rStartPos];
149 0 : 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 : }
159 0 : else if (c == ']')
160 : {
161 : // Skip until the opening braket.
162 0 : for (--rStartPos; rStartPos > 0; --rStartPos)
163 : {
164 0 : if (c == '[')
165 0 : break;
166 : }
167 : }
168 0 : else if (!IsText(c))
169 : {
170 0 : ++rStartPos;
171 0 : break;
172 : }
173 : }
174 : }
175 :
176 : // move forward the end position to the last text character.
177 4 : rEndPos = FindEndPosR1C1(p, rEndPos, nLen-1);
178 4 : }
179 :
180 8 : void ExpandToText(const sal_Unicode* p, xub_StrLen nLen, xub_StrLen& rStartPos, xub_StrLen& rEndPos,
181 : formula::FormulaGrammar::AddressConvention eConv)
182 : {
183 8 : switch (eConv)
184 : {
185 : case formula::FormulaGrammar::CONV_XL_R1C1:
186 4 : ExpandToTextR1C1(p, nLen, rStartPos, rEndPos);
187 4 : break;
188 : case formula::FormulaGrammar::CONV_OOO:
189 : case formula::FormulaGrammar::CONV_XL_A1:
190 : default:
191 4 : ExpandToTextA1(p, nLen, rStartPos, rEndPos);
192 : }
193 8 : }
194 :
195 : }
196 :
197 2 : ScRefFinder::ScRefFinder(
198 : const String& rFormula, const ScAddress& rPos,
199 : ScDocument* pDocument, formula::FormulaGrammar::AddressConvention eConvP) :
200 : aFormula( rFormula ),
201 : eConv( eConvP ),
202 : pDoc( pDocument ),
203 2 : maPos(rPos)
204 : {
205 2 : nSelStart = nSelEnd = nFound = 0;
206 2 : }
207 :
208 2 : ScRefFinder::~ScRefFinder()
209 : {
210 2 : }
211 :
212 8 : static sal_uInt16 lcl_NextFlags( sal_uInt16 nOld )
213 : {
214 8 : sal_uInt16 nNew = nOld & 7; // die drei Abs-Flags
215 8 : nNew = ( nNew - 1 ) & 7; // weiterzaehlen
216 :
217 8 : if (!(nOld & SCA_TAB_3D))
218 8 : nNew &= ~SCA_TAB_ABSOLUTE; // not 3D -> never absolute!
219 :
220 8 : return ( nOld & 0xfff8 ) | nNew;
221 : }
222 :
223 8 : void ScRefFinder::ToggleRel( xub_StrLen nStartPos, xub_StrLen nEndPos )
224 : {
225 8 : xub_StrLen nLen = aFormula.Len();
226 8 : if (!nLen)
227 8 : return;
228 8 : const sal_Unicode* pSource = aFormula.GetBuffer(); // for quick access
229 :
230 : // expand selection, and instead of selection start- and end-index
231 :
232 8 : if ( nEndPos < nStartPos )
233 0 : ::std::swap(nEndPos, nStartPos);
234 :
235 8 : ExpandToText(pSource, nLen, nStartPos, nEndPos, eConv);
236 :
237 8 : String aResult;
238 8 : String aExpr;
239 8 : String aSep;
240 8 : ScAddress aAddr;
241 8 : nFound = 0;
242 :
243 8 : xub_StrLen nLoopStart = nStartPos;
244 24 : while ( nLoopStart <= nEndPos )
245 : {
246 : // Determine the stard and end positions of a text segment.
247 8 : xub_StrLen nEStart = FindStartPos(pSource, nLoopStart, nEndPos);
248 8 : xub_StrLen nEEnd = FindEndPos(pSource, nEStart, nEndPos, eConv);
249 :
250 8 : aSep = aFormula.Copy( nLoopStart, nEStart-nLoopStart );
251 8 : aExpr = aFormula.Copy( nEStart, nEEnd-nEStart );
252 :
253 : // Check the validity of the expression, and toggle the relative flag.
254 8 : ScAddress::Details aDetails(eConv, maPos.Row(), maPos.Col());
255 8 : sal_uInt16 nResult = aAddr.Parse(aExpr, pDoc, aDetails);
256 8 : if ( nResult & SCA_VALID )
257 : {
258 8 : sal_uInt16 nFlags = lcl_NextFlags( nResult );
259 8 : aAddr.Format(aExpr, nFlags, pDoc, aDetails);
260 :
261 8 : xub_StrLen nAbsStart = nStartPos+aResult.Len()+aSep.Len();
262 :
263 8 : if (!nFound) // first reference ?
264 8 : nSelStart = nAbsStart;
265 8 : nSelEnd = nAbsStart+aExpr.Len(); // selection, no indizes
266 8 : ++nFound;
267 : }
268 :
269 : // assemble
270 :
271 8 : aResult += aSep;
272 8 : aResult += aExpr;
273 :
274 8 : nLoopStart = nEEnd;
275 : }
276 :
277 8 : String aTotal = aFormula.Copy( 0, nStartPos );
278 8 : aTotal += aResult;
279 8 : aTotal += aFormula.Copy( nEndPos+1 );
280 :
281 8 : aFormula = aTotal;
282 : }
283 :
284 :
285 :
286 :
287 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|