tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / core / tool / reftokenhelper.cxx
bloba4992485ee66e8a0d09bc6cc62f0933d1f089f33
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 <memory>
32 using namespace formula;
34 using ::std::vector;
36 void ScRefTokenHelper::compileRangeRepresentation(
37 vector<ScTokenRef>& rRefTokens, const OUString& rRangeStr, ScDocument& rDoc,
38 const sal_Unicode cSep, FormulaGrammar::Grammar eGrammar, bool bOnly3DRef)
40 // #i107275# ignore parentheses
41 OUString aRangeStr = rRangeStr;
42 while( (aRangeStr.getLength() >= 2) && (aRangeStr[ 0 ] == '(') && (aRangeStr[ aRangeStr.getLength() - 1 ] == ')') )
43 aRangeStr = aRangeStr.copy( 1, aRangeStr.getLength() - 2 );
45 bool bFailure = false;
46 sal_Int32 nOffset = 0;
47 while (nOffset >= 0 && !bFailure)
49 OUString aToken;
50 ScRangeStringConverter::GetTokenByOffset(aToken, aRangeStr, nOffset, cSep);
51 if (nOffset < 0)
52 break;
54 ScCompiler aCompiler(rDoc, ScAddress(0,0,0), eGrammar);
55 std::unique_ptr<ScTokenArray> pArray(aCompiler.CompileString(aToken));
57 // There MUST be exactly one reference per range token and nothing
58 // else, and it MUST be a valid reference, not some #REF!
59 sal_uInt16 nLen = pArray->GetLen();
60 if (!nLen)
61 continue; // Should a missing range really be allowed?
62 if (nLen != 1)
64 bFailure = true;
65 break;
68 const FormulaToken* p = pArray->FirstToken();
69 if (!p)
71 bFailure = true;
72 break;
75 switch (p->GetType())
77 case svSingleRef:
79 const ScSingleRefData& rRef = *p->GetSingleRef();
80 if (!rRef.Valid(rDoc))
81 bFailure = true;
82 else if (bOnly3DRef && !rRef.IsFlag3D())
83 bFailure = true;
85 break;
86 case svDoubleRef:
88 const ScComplexRefData& rRef = *p->GetDoubleRef();
89 if (!rRef.Valid(rDoc))
90 bFailure = true;
91 else if (bOnly3DRef && !rRef.Ref1.IsFlag3D())
92 bFailure = true;
94 break;
95 case svExternalSingleRef:
97 if (!p->GetSingleRef()->ValidExternal(rDoc))
98 bFailure = true;
100 break;
101 case svExternalDoubleRef:
103 if (!p->GetDoubleRef()->ValidExternal(rDoc))
104 bFailure = true;
106 break;
107 case svString:
108 if (p->GetString().isEmpty())
109 bFailure = true;
110 break;
111 case svIndex:
113 if (p->GetOpCode() == ocName)
115 ScRangeData* pNameRange = rDoc.FindRangeNameBySheetAndIndex(p->GetSheet(), p->GetIndex());
116 if (!pNameRange->HasReferences())
117 bFailure = true;
120 break;
121 default:
122 bFailure = true;
123 break;
125 if (!bFailure)
126 rRefTokens.emplace_back(p->Clone());
129 if (bFailure)
130 rRefTokens.clear();
133 bool ScRefTokenHelper::getRangeFromToken(
134 const ScDocument* pDoc,
135 ScRange& rRange, const ScTokenRef& pToken, const ScAddress& rPos, bool bExternal)
137 StackVar eType = pToken->GetType();
138 switch (pToken->GetType())
140 case svSingleRef:
141 case svExternalSingleRef:
143 if ((eType == svExternalSingleRef && !bExternal) ||
144 (eType == svSingleRef && bExternal))
145 return false;
147 const ScSingleRefData& rRefData = *pToken->GetSingleRef();
148 rRange.aStart = rRefData.toAbs(*pDoc, rPos);
149 rRange.aEnd = rRange.aStart;
150 return true;
152 case svDoubleRef:
153 case svExternalDoubleRef:
155 if ((eType == svExternalDoubleRef && !bExternal) ||
156 (eType == svDoubleRef && bExternal))
157 return false;
159 const ScComplexRefData& rRefData = *pToken->GetDoubleRef();
160 rRange = rRefData.toAbs(*pDoc, rPos);
161 return true;
163 case svIndex:
165 if (pToken->GetOpCode() == ocName)
167 ScRangeData* pNameRange = pDoc->FindRangeNameBySheetAndIndex(pToken->GetSheet(), pToken->GetIndex());
168 if (pNameRange->IsReference(rRange, rPos))
169 return true;
171 return false;
173 default:
174 ; // do nothing
176 return false;
179 void ScRefTokenHelper::getRangeListFromTokens(
180 const ScDocument* pDoc, ScRangeList& rRangeList, const vector<ScTokenRef>& rTokens, const ScAddress& rPos)
182 for (const auto& rToken : rTokens)
184 ScRange aRange;
185 getRangeFromToken(pDoc, aRange, rToken, rPos);
186 rRangeList.push_back(aRange);
190 void ScRefTokenHelper::getTokenFromRange(const ScDocument* pDoc, ScTokenRef& pToken, const ScRange& rRange)
192 ScComplexRefData aData;
193 aData.InitRange(rRange);
194 aData.Ref1.SetFlag3D(true);
196 // Display sheet name on 2nd reference only when the 1st and 2nd refs are on
197 // different sheets.
198 aData.Ref2.SetFlag3D(rRange.aStart.Tab() != rRange.aEnd.Tab());
200 pToken.reset(new ScDoubleRefToken(pDoc->GetSheetLimits(), aData));
203 void ScRefTokenHelper::getTokensFromRangeList(const ScDocument* pDoc, vector<ScTokenRef>& pTokens, const ScRangeList& rRanges)
205 vector<ScTokenRef> aTokens;
206 size_t nCount = rRanges.size();
207 aTokens.reserve(nCount);
208 for (size_t i = 0; i < nCount; ++i)
210 const ScRange & rRange = rRanges[i];
211 ScTokenRef pToken;
212 ScRefTokenHelper::getTokenFromRange(pDoc, pToken, rRange);
213 aTokens.push_back(pToken);
215 pTokens.swap(aTokens);
218 bool ScRefTokenHelper::isRef(const ScTokenRef& pToken)
220 switch (pToken->GetType())
222 case svSingleRef:
223 case svDoubleRef:
224 case svExternalSingleRef:
225 case svExternalDoubleRef:
226 return true;
227 default:
230 return false;
233 bool ScRefTokenHelper::isExternalRef(const ScTokenRef& pToken)
235 switch (pToken->GetType())
237 case svExternalSingleRef:
238 case svExternalDoubleRef:
239 return true;
240 default:
243 return false;
246 bool ScRefTokenHelper::intersects(
247 const ScDocument* pDoc,
248 const vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
250 if (!isRef(pToken))
251 return false;
253 bool bExternal = isExternalRef(pToken);
254 sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
256 ScRange aRange;
257 getRangeFromToken(pDoc, aRange, pToken, rPos, bExternal);
259 for (const ScTokenRef& p : rTokens)
261 if (!isRef(p))
262 continue;
264 if (bExternal != isExternalRef(p))
265 continue;
267 ScRange aRange2;
268 getRangeFromToken(pDoc, aRange2, p, rPos, bExternal);
270 if (bExternal && nFileId != p->GetIndex())
271 // different external file
272 continue;
274 if (aRange.Intersects(aRange2))
275 return true;
277 return false;
280 namespace {
282 class JoinRefTokenRanges
284 public:
286 * Insert a new reference token into the existing list of reference tokens,
287 * but in that process, try to join as many adjacent ranges as possible.
289 * @param rTokens existing list of reference tokens
290 * @param rToken new token
292 void operator() (const ScDocument* pDoc, vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
294 join(pDoc, rTokens, pToken, rPos);
297 private:
300 * Check two 1-dimensional ranges to see if they overlap each other.
302 * @param nMin1 min value of range 1
303 * @param nMax1 max value of range 1
304 * @param nMin2 min value of range 2
305 * @param nMax2 max value of range 2
306 * @param rNewMin min value of new range in case they overlap
307 * @param rNewMax max value of new range in case they overlap
309 template<typename T>
310 static bool overlaps(T nMin1, T nMax1, T nMin2, T nMax2, T& rNewMin, T& rNewMax)
312 bool bDisjoint1 = (nMin1 > nMax2) && (nMin1 - nMax2 > 1);
313 bool bDisjoint2 = (nMin2 > nMax1) && (nMin2 - nMax1 > 1);
314 if (bDisjoint1 || bDisjoint2)
315 // These two ranges cannot be joined. Move on.
316 return false;
318 T nMin = std::min(nMin1, nMin2);
319 T nMax = std::max(nMax1, nMax2);
321 rNewMin = nMin;
322 rNewMax = nMax;
324 return true;
327 void join(const ScDocument* pDoc, vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
329 // Normalize the token to a double reference.
330 ScComplexRefData aData;
331 if (!ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken))
332 return;
334 // Get the information of the new token.
335 bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
336 sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
337 svl::SharedString aTabName = bExternal ? pToken->GetString() : svl::SharedString::getEmptyString();
339 bool bJoined = false;
340 for (ScTokenRef& pOldToken : rTokens)
342 if (!ScRefTokenHelper::isRef(pOldToken))
343 // A non-ref token should not have been added here in the first
344 // place!
345 continue;
347 if (bExternal != ScRefTokenHelper::isExternalRef(pOldToken))
348 // External and internal refs don't mix.
349 continue;
351 if (bExternal)
353 if (nFileId != pOldToken->GetIndex())
354 // Different external files.
355 continue;
357 if (aTabName != pOldToken->GetString())
358 // Different table names.
359 continue;
362 ScComplexRefData aOldData;
363 if (!ScRefTokenHelper::getDoubleRefDataFromToken(aOldData, pOldToken))
364 continue;
366 ScRange aOld = aOldData.toAbs(*pDoc, rPos), aNew = aData.toAbs(*pDoc, rPos);
368 if (aNew.aStart.Tab() != aOld.aStart.Tab() || aNew.aEnd.Tab() != aOld.aEnd.Tab())
369 // Sheet ranges differ.
370 continue;
372 if (aOld.Contains(aNew))
373 // This new range is part of an existing range. Skip it.
374 return;
376 bool bSameRows = (aNew.aStart.Row() == aOld.aStart.Row()) && (aNew.aEnd.Row() == aOld.aEnd.Row());
377 bool bSameCols = (aNew.aStart.Col() == aOld.aStart.Col()) && (aNew.aEnd.Col() == aOld.aEnd.Col());
378 ScComplexRefData aNewData = aOldData;
379 bool bJoinRanges = false;
380 if (bSameRows)
382 SCCOL nNewMin, nNewMax;
383 bJoinRanges = overlaps(
384 aNew.aStart.Col(), aNew.aEnd.Col(), aOld.aStart.Col(), aOld.aEnd.Col(),
385 nNewMin, nNewMax);
387 if (bJoinRanges)
389 aNew.aStart.SetCol(nNewMin);
390 aNew.aEnd.SetCol(nNewMax);
391 aNewData.SetRange(pDoc->GetSheetLimits(), aNew, rPos);
394 else if (bSameCols)
396 SCROW nNewMin, nNewMax;
397 bJoinRanges = overlaps(
398 aNew.aStart.Row(), aNew.aEnd.Row(), aOld.aStart.Row(), aOld.aEnd.Row(),
399 nNewMin, nNewMax);
401 if (bJoinRanges)
403 aNew.aStart.SetRow(nNewMin);
404 aNew.aEnd.SetRow(nNewMax);
405 aNewData.SetRange(pDoc->GetSheetLimits(), aNew, rPos);
409 if (bJoinRanges)
411 if (bExternal)
412 pOldToken.reset(new ScExternalDoubleRefToken(nFileId, aTabName, aNewData));
413 else
414 pOldToken.reset(new ScDoubleRefToken(pDoc->GetSheetLimits(), aNewData));
416 bJoined = true;
417 break;
421 if (bJoined)
423 if (rTokens.size() == 1)
424 // There is only one left. No need to do more joining.
425 return;
427 // Pop the last token from the list, and keep joining recursively.
428 ScTokenRef p = rTokens.back();
429 rTokens.pop_back();
430 join(pDoc, rTokens, p, rPos);
432 else
433 rTokens.push_back(pToken);
439 void ScRefTokenHelper::join(const ScDocument* pDoc, vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
441 JoinRefTokenRanges join;
442 join(pDoc, rTokens, pToken, rPos);
445 bool ScRefTokenHelper::getDoubleRefDataFromToken(ScComplexRefData& rData, const ScTokenRef& pToken)
447 switch (pToken->GetType())
449 case svSingleRef:
450 case svExternalSingleRef:
452 const ScSingleRefData& r = *pToken->GetSingleRef();
453 rData.Ref1 = r;
454 rData.Ref1.SetFlag3D(true);
455 rData.Ref2 = r;
456 rData.Ref2.SetFlag3D(false); // Don't display sheet name on second reference.
458 break;
459 case svDoubleRef:
460 case svExternalDoubleRef:
461 rData = *pToken->GetDoubleRef();
462 break;
463 default:
464 // Not a reference token. Bail out.
465 return false;
467 return true;
470 ScTokenRef ScRefTokenHelper::createRefToken(const ScDocument& rDoc, const ScAddress& rAddr)
472 ScSingleRefData aRefData;
473 aRefData.InitAddress(rAddr);
474 ScTokenRef pRef(new ScSingleRefToken(rDoc.GetSheetLimits(), aRefData));
475 return pRef;
478 ScTokenRef ScRefTokenHelper::createRefToken(const ScDocument& rDoc, const ScRange& rRange)
480 ScComplexRefData aRefData;
481 aRefData.InitRange(rRange);
482 ScTokenRef pRef(new ScDoubleRefToken(rDoc.GetSheetLimits(), aRefData));
483 return pRef;
486 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */