fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / sc / source / core / tool / reftokenhelper.cxx
blob04070f29225f217a480665be1015fa7655ce9583
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 "reftokenhelper.hxx"
21 #include "document.hxx"
22 #include "rangeutl.hxx"
23 #include "compiler.hxx"
24 #include "tokenarray.hxx"
26 #include <rtl/ustring.hxx>
27 #include <formula/grammar.hxx>
28 #include <formula/token.hxx>
30 #include <boost/scoped_ptr.hpp>
32 using namespace formula;
34 using ::std::vector;
36 void ScRefTokenHelper::compileRangeRepresentation(
37 vector<ScTokenRef>& rRefTokens, const OUString& rRangeStr, ScDocument* pDoc,
38 const sal_Unicode cSep, FormulaGrammar::Grammar eGrammar, bool bOnly3DRef)
40 const sal_Unicode cQuote = '\'';
42 // #i107275# ignore parentheses
43 OUString aRangeStr = rRangeStr;
44 while( (aRangeStr.getLength() >= 2) && (aRangeStr[ 0 ] == '(') && (aRangeStr[ aRangeStr.getLength() - 1 ] == ')') )
45 aRangeStr = aRangeStr.copy( 1, aRangeStr.getLength() - 2 );
47 bool bFailure = false;
48 sal_Int32 nOffset = 0;
49 while (nOffset >= 0 && !bFailure)
51 OUString aToken;
52 ScRangeStringConverter::GetTokenByOffset(aToken, aRangeStr, nOffset, cSep, cQuote);
53 if (nOffset < 0)
54 break;
56 ScCompiler aCompiler(pDoc, ScAddress(0,0,0));
57 aCompiler.SetGrammar(eGrammar);
58 boost::scoped_ptr<ScTokenArray> pArray(aCompiler.CompileString(aToken));
60 // There MUST be exactly one reference per range token and nothing
61 // else, and it MUST be a valid reference, not some #REF!
62 sal_uInt16 nLen = pArray->GetLen();
63 if (!nLen)
64 continue; // Should a missing range really be allowed?
65 if (nLen != 1)
67 bFailure = true;
68 break;
71 pArray->Reset();
72 const FormulaToken* p = pArray->Next();
73 if (!p)
75 bFailure = true;
76 break;
79 switch (p->GetType())
81 case svSingleRef:
83 const ScSingleRefData& rRef = *p->GetSingleRef();
84 if (!rRef.Valid())
85 bFailure = true;
86 else if (bOnly3DRef && !rRef.IsFlag3D())
87 bFailure = true;
89 break;
90 case svDoubleRef:
92 const ScComplexRefData& rRef = *p->GetDoubleRef();
93 if (!rRef.Valid())
94 bFailure = true;
95 else if (bOnly3DRef && !rRef.Ref1.IsFlag3D())
96 bFailure = true;
98 break;
99 case svExternalSingleRef:
101 if (!p->GetSingleRef()->ValidExternal())
102 bFailure = true;
104 break;
105 case svExternalDoubleRef:
107 if (!p->GetDoubleRef()->ValidExternal())
108 bFailure = true;
110 break;
111 case svString:
112 if (p->GetString().isEmpty())
113 bFailure = true;
114 break;
115 default:
116 bFailure = true;
117 break;
119 if (!bFailure)
120 rRefTokens.push_back(ScTokenRef(p->Clone()));
123 if (bFailure)
124 rRefTokens.clear();
127 bool ScRefTokenHelper::getRangeFromToken(
128 ScRange& rRange, const ScTokenRef& pToken, const ScAddress& rPos, bool bExternal)
130 StackVar eType = pToken->GetType();
131 switch (pToken->GetType())
133 case svSingleRef:
134 case svExternalSingleRef:
136 if ((eType == svExternalSingleRef && !bExternal) ||
137 (eType == svSingleRef && bExternal))
138 return false;
140 const ScSingleRefData& rRefData = *pToken->GetSingleRef();
141 rRange.aStart = rRefData.toAbs(rPos);
142 rRange.aEnd = rRange.aStart;
143 return true;
145 case svDoubleRef:
146 case svExternalDoubleRef:
148 if ((eType == svExternalDoubleRef && !bExternal) ||
149 (eType == svDoubleRef && bExternal))
150 return false;
152 const ScComplexRefData& rRefData = *pToken->GetDoubleRef();
153 rRange = rRefData.toAbs(rPos);
154 return true;
156 default:
157 ; // do nothing
159 return false;
162 void ScRefTokenHelper::getRangeListFromTokens(
163 ScRangeList& rRangeList, const vector<ScTokenRef>& rTokens, const ScAddress& rPos)
165 vector<ScTokenRef>::const_iterator itr = rTokens.begin(), itrEnd = rTokens.end();
166 for (; itr != itrEnd; ++itr)
168 ScRange aRange;
169 getRangeFromToken(aRange, *itr, rPos);
170 rRangeList.Append(aRange);
174 void ScRefTokenHelper::getTokenFromRange(ScTokenRef& pToken, const ScRange& rRange)
176 ScComplexRefData aData;
177 aData.InitRange(rRange);
178 aData.Ref1.SetFlag3D(true);
180 // Display sheet name on 2nd reference only when the 1st and 2nd refs are on
181 // different sheets.
182 aData.Ref2.SetFlag3D(rRange.aStart.Tab() != rRange.aEnd.Tab());
184 pToken.reset(new ScDoubleRefToken(aData));
187 void ScRefTokenHelper::getTokensFromRangeList(vector<ScTokenRef>& pTokens, const ScRangeList& rRanges)
189 vector<ScTokenRef> aTokens;
190 size_t nCount = rRanges.size();
191 aTokens.reserve(nCount);
192 for (size_t i = 0; i < nCount; ++i)
194 const ScRange* pRange = rRanges[i];
195 if (!pRange)
196 // failed.
197 return;
199 ScTokenRef pToken;
200 ScRefTokenHelper::getTokenFromRange(pToken,* pRange);
201 aTokens.push_back(pToken);
203 pTokens.swap(aTokens);
206 bool ScRefTokenHelper::isRef(const ScTokenRef& pToken)
208 switch (pToken->GetType())
210 case svSingleRef:
211 case svDoubleRef:
212 case svExternalSingleRef:
213 case svExternalDoubleRef:
214 return true;
215 default:
218 return false;
221 bool ScRefTokenHelper::isExternalRef(const ScTokenRef& pToken)
223 switch (pToken->GetType())
225 case svExternalSingleRef:
226 case svExternalDoubleRef:
227 return true;
228 default:
231 return false;
234 bool ScRefTokenHelper::intersects(
235 const vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
237 if (!isRef(pToken))
238 return false;
240 bool bExternal = isExternalRef(pToken);
241 sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
243 ScRange aRange;
244 getRangeFromToken(aRange, pToken, rPos, bExternal);
246 vector<ScTokenRef>::const_iterator itr = rTokens.begin(), itrEnd = rTokens.end();
247 for (; itr != itrEnd; ++itr)
249 const ScTokenRef& p = *itr;
250 if (!isRef(p))
251 continue;
253 if (bExternal != isExternalRef(p))
254 continue;
256 ScRange aRange2;
257 getRangeFromToken(aRange2, p, rPos, bExternal);
259 if (bExternal && nFileId != p->GetIndex())
260 // different external file
261 continue;
263 if (aRange.Intersects(aRange2))
264 return true;
266 return false;
269 namespace {
271 class JoinRefTokenRanges
273 public:
275 * Insert a new reference token into the existing list of reference tokens,
276 * but in that process, try to join as many adjacent ranges as possible.
278 * @param rTokens existing list of reference tokens
279 * @param rToken new token
281 void operator() (vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
283 join(rTokens, pToken, rPos);
286 private:
289 * Check two 1-dimensional ranges to see if they overlap each other.
291 * @param nMin1 min value of range 1
292 * @param nMax1 max value of range 1
293 * @param nMin2 min value of range 2
294 * @param nMax2 max value of range 2
295 * @param rNewMin min value of new range in case they overlap
296 * @param rNewMax max value of new range in case they overlap
298 template<typename T>
299 static bool overlaps(T nMin1, T nMax1, T nMin2, T nMax2, T& rNewMin, T& rNewMax)
301 bool bDisjoint1 = (nMin1 > nMax2) && (nMin1 - nMax2 > 1);
302 bool bDisjoint2 = (nMin2 > nMax1) && (nMin2 - nMax1 > 1);
303 if (bDisjoint1 || bDisjoint2)
304 // These two ranges cannot be joined. Move on.
305 return false;
307 T nMin = nMin1 < nMin2 ? nMin1 : nMin2;
308 T nMax = nMax1 > nMax2 ? nMax1 : nMax2;
310 rNewMin = nMin;
311 rNewMax = nMax;
313 return true;
316 void join(vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
318 // Normalize the token to a double reference.
319 ScComplexRefData aData;
320 if (!ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken))
321 return;
323 // Get the information of the new token.
324 bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
325 sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
326 OUString aTabName = bExternal ? pToken->GetString().getString() : OUString();
328 bool bJoined = false;
329 vector<ScTokenRef>::iterator itr = rTokens.begin(), itrEnd = rTokens.end();
330 for (; itr != itrEnd; ++itr)
332 ScTokenRef& pOldToken = *itr;
334 if (!ScRefTokenHelper::isRef(pOldToken))
335 // A non-ref token should not have been added here in the first
336 // place!
337 continue;
339 if (bExternal != ScRefTokenHelper::isExternalRef(pOldToken))
340 // External and internal refs don't mix.
341 continue;
343 if (bExternal)
345 if (nFileId != pOldToken->GetIndex())
346 // Different external files.
347 continue;
349 if (aTabName != pOldToken->GetString().getString())
350 // Different table names.
351 continue;
354 ScComplexRefData aOldData;
355 if (!ScRefTokenHelper::getDoubleRefDataFromToken(aOldData, pOldToken))
356 continue;
358 ScRange aOld = aOldData.toAbs(rPos), aNew = aData.toAbs(rPos);
360 if (aNew.aStart.Tab() != aOld.aStart.Tab() || aNew.aEnd.Tab() != aOld.aEnd.Tab())
361 // Sheet ranges differ.
362 continue;
364 if (aOld.In(aNew))
365 // This new range is part of an existing range. Skip it.
366 return;
368 bool bSameRows = (aNew.aStart.Row() == aOld.aStart.Row()) && (aNew.aEnd.Row() == aOld.aEnd.Row());
369 bool bSameCols = (aNew.aStart.Col() == aOld.aStart.Col()) && (aNew.aEnd.Col() == aOld.aEnd.Col());
370 ScComplexRefData aNewData = aOldData;
371 bool bJoinRanges = false;
372 if (bSameRows)
374 SCCOL nNewMin, nNewMax;
375 bJoinRanges = overlaps(
376 aNew.aStart.Col(), aNew.aEnd.Col(), aOld.aStart.Col(), aOld.aEnd.Col(),
377 nNewMin, nNewMax);
379 if (bJoinRanges)
381 aNew.aStart.SetCol(nNewMin);
382 aNew.aEnd.SetCol(nNewMax);
383 aNewData.SetRange(aNew, rPos);
386 else if (bSameCols)
388 SCROW nNewMin, nNewMax;
389 bJoinRanges = overlaps(
390 aNew.aStart.Row(), aNew.aEnd.Row(), aOld.aStart.Row(), aOld.aEnd.Row(),
391 nNewMin, nNewMax);
393 if (bJoinRanges)
395 aNew.aStart.SetRow(nNewMin);
396 aNew.aEnd.SetRow(nNewMax);
397 aNewData.SetRange(aNew, rPos);
401 if (bJoinRanges)
403 if (bExternal)
404 pOldToken.reset(new ScExternalDoubleRefToken(nFileId, aTabName, aNewData));
405 else
406 pOldToken.reset(new ScDoubleRefToken(aNewData));
408 bJoined = true;
409 break;
413 if (bJoined)
415 if (rTokens.size() == 1)
416 // There is only one left. No need to do more joining.
417 return;
419 // Pop the last token from the list, and keep joining recursively.
420 ScTokenRef p = rTokens.back();
421 rTokens.pop_back();
422 join(rTokens, p, rPos);
424 else
425 rTokens.push_back(pToken);
431 void ScRefTokenHelper::join(vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
433 JoinRefTokenRanges join;
434 join(rTokens, pToken, rPos);
437 bool ScRefTokenHelper::getDoubleRefDataFromToken(ScComplexRefData& rData, const ScTokenRef& pToken)
439 switch (pToken->GetType())
441 case svSingleRef:
442 case svExternalSingleRef:
444 const ScSingleRefData& r = *pToken->GetSingleRef();
445 rData.Ref1 = r;
446 rData.Ref1.SetFlag3D(true);
447 rData.Ref2 = r;
448 rData.Ref2.SetFlag3D(false); // Don't display sheet name on second reference.
450 break;
451 case svDoubleRef:
452 case svExternalDoubleRef:
453 rData = *pToken->GetDoubleRef();
454 break;
455 default:
456 // Not a reference token. Bail out.
457 return false;
459 return true;
462 ScTokenRef ScRefTokenHelper::createRefToken(const ScAddress& rAddr)
464 ScSingleRefData aRefData;
465 aRefData.InitAddress(rAddr);
466 ScTokenRef pRef(new ScSingleRefToken(aRefData));
467 return pRef;
470 ScTokenRef ScRefTokenHelper::createRefToken(const ScRange& rRange)
472 ScComplexRefData aRefData;
473 aRefData.InitRange(rRange);
474 ScTokenRef pRef(new ScDoubleRefToken(aRefData));
475 return pRef;
478 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */