tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / core / tool / sharedformula.cxx
blob98fa783c07ae2e41a3b168926c6a78ebe3b107a1
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/.
8 */
10 #include <sharedformula.hxx>
11 #include <calcmacros.hxx>
12 #include <tokenarray.hxx>
13 #include <listenercontext.hxx>
14 #include <document.hxx>
15 #include <grouparealistener.hxx>
16 #include <refdata.hxx>
18 namespace sc {
20 const ScFormulaCell* SharedFormulaUtil::getSharedTopFormulaCell(const CellStoreType::position_type& aPos)
22 if (aPos.first->type != sc::element_type_formula)
23 // Not a formula cell block.
24 return nullptr;
26 sc::formula_block::iterator it = sc::formula_block::begin(*aPos.first->data);
27 std::advance(it, aPos.second);
28 const ScFormulaCell* pCell = *it;
29 if (!pCell->IsShared())
30 // Not a shared formula.
31 return nullptr;
33 return pCell->GetCellGroup()->mpTopCell;
36 bool SharedFormulaUtil::splitFormulaCellGroup(const CellStoreType::position_type& aPos, sc::EndListeningContext* pCxt)
38 SCROW nRow = aPos.first->position + aPos.second;
40 if (aPos.first->type != sc::element_type_formula)
41 // Not a formula cell block.
42 return false;
44 if (aPos.second == 0)
45 // Split position coincides with the block border. Nothing to do.
46 return false;
48 sc::formula_block::iterator it = sc::formula_block::begin(*aPos.first->data);
49 std::advance(it, aPos.second);
50 ScFormulaCell& rTop = **it;
51 if (!rTop.IsShared())
52 // Not a shared formula.
53 return false;
55 if (nRow == rTop.GetSharedTopRow())
56 // Already the top cell of a shared group.
57 return false;
59 ScFormulaCellGroupRef xGroup = rTop.GetCellGroup();
61 SCROW nLength2 = xGroup->mpTopCell->aPos.Row() + xGroup->mnLength - nRow;
62 ScFormulaCellGroupRef xGroup2;
63 if (nLength2 > 1)
65 xGroup2.reset(new ScFormulaCellGroup);
66 xGroup2->mbInvariant = xGroup->mbInvariant;
67 xGroup2->mpTopCell = &rTop;
68 xGroup2->mnLength = nLength2;
69 xGroup2->mpCode = xGroup->mpCode->CloneValue();
72 xGroup->mnLength = nRow - xGroup->mpTopCell->aPos.Row();
73 ScFormulaCell& rPrevTop = *sc::formula_block::at(*aPos.first->data, aPos.second - xGroup->mnLength);
75 #if USE_FORMULA_GROUP_LISTENER
76 // At least group area listeners will have to be adapted. As long as
77 // there's no update mechanism and no separated handling of group area and
78 // other listeners, all listeners of this group's top cell are to be reset.
79 if (nLength2)
81 // If a context exists it has to be used to not interfere with
82 // ScColumn::maBroadcasters iterators, which the EndListeningTo()
83 // without context would do when removing a broadcaster that had its
84 // last listener removed.
85 if (pCxt)
86 rPrevTop.EndListeningTo(*pCxt);
87 else
88 rPrevTop.EndListeningTo( rPrevTop.GetDocument(), nullptr, ScAddress( ScAddress::UNINITIALIZED));
89 rPrevTop.SetNeedsListening(true);
91 // The new group or remaining single cell needs a new listening.
92 rTop.SetNeedsListening(true);
94 #endif
96 if (xGroup->mnLength == 1)
98 // The top group consists of only one cell. Ungroup this.
99 ScFormulaCellGroupRef xNone;
100 rPrevTop.SetCellGroup(xNone);
103 // Apply the lower group object to the lower cells.
104 assert ((xGroup2 == nullptr || xGroup2->mpTopCell->aPos.Row() + size_t(xGroup2->mnLength) <= aPos.first->position + aPos.first->size)
105 && "Shared formula region goes beyond the formula block.");
106 sc::formula_block::iterator itEnd = it;
107 std::advance(itEnd, nLength2);
108 for (; it != itEnd; ++it)
110 ScFormulaCell& rCell = **it;
111 rCell.SetCellGroup(xGroup2);
114 return true;
117 bool SharedFormulaUtil::splitFormulaCellGroups(const ScDocument& rDoc, CellStoreType& rCells, std::vector<SCROW>& rBounds)
119 if (rBounds.empty())
120 return false;
122 // Sort and remove duplicates.
123 std::sort(rBounds.begin(), rBounds.end());
124 std::vector<SCROW>::iterator it = std::unique(rBounds.begin(), rBounds.end());
125 rBounds.erase(it, rBounds.end());
127 it = rBounds.begin();
128 SCROW nRow = *it;
129 CellStoreType::position_type aPos = rCells.position(nRow);
130 if (aPos.first == rCells.end())
131 return false;
133 bool bSplit = splitFormulaCellGroup(aPos, nullptr);
134 std::vector<SCROW>::iterator itEnd = rBounds.end();
135 for (++it; it != itEnd; ++it)
137 nRow = *it;
138 if (rDoc.ValidRow(nRow))
140 aPos = rCells.position(aPos.first, nRow);
141 if (aPos.first == rCells.end())
142 return bSplit;
143 bSplit |= splitFormulaCellGroup(aPos, nullptr);
146 return bSplit;
149 bool SharedFormulaUtil::joinFormulaCells(
150 const CellStoreType::position_type& rPos, ScFormulaCell& rCell1, ScFormulaCell& rCell2 )
152 if( rCell1.GetDocument().IsDelayedFormulaGrouping())
154 rCell1.GetDocument().AddDelayedFormulaGroupingCell( &rCell1 );
155 rCell1.GetDocument().AddDelayedFormulaGroupingCell( &rCell2 );
156 return false;
159 ScFormulaCell::CompareState eState = rCell1.CompareByTokenArray(rCell2);
160 if (eState == ScFormulaCell::NotEqual)
161 return false;
163 // Formula tokens equal those of the previous formula cell.
164 ScFormulaCellGroupRef xGroup1 = rCell1.GetCellGroup();
165 ScFormulaCellGroupRef xGroup2 = rCell2.GetCellGroup();
166 if (xGroup1)
168 if (xGroup2)
170 // Both cell 1 and cell 2 are shared. Merge them together.
171 if (xGroup1.get() == xGroup2.get())
172 // They belong to the same group.
173 return false;
175 // Set the group object from cell 1 to all cells in group 2.
176 xGroup1->mnLength += xGroup2->mnLength;
177 size_t nOffset = rPos.second + 1; // position of cell 2
178 for (size_t i = 0, n = xGroup2->mnLength; i < n; ++i)
180 ScFormulaCell& rCell = *sc::formula_block::at(*rPos.first->data, nOffset+i);
181 rCell.SetCellGroup(xGroup1);
184 else
186 // cell 1 is shared but cell 2 is not.
187 rCell2.SetCellGroup(xGroup1);
188 ++xGroup1->mnLength;
191 else
193 if (xGroup2)
195 // cell 1 is not shared, but cell 2 is already shared.
196 rCell1.SetCellGroup(xGroup2);
197 xGroup2->mpTopCell = &rCell1;
198 ++xGroup2->mnLength;
200 else
202 // neither cells are shared.
203 assert(rCell1.aPos.Row() == static_cast<SCROW>(rPos.first->position + rPos.second));
204 xGroup1 = rCell1.CreateCellGroup(2, eState == ScFormulaCell::EqualInvariant);
205 rCell2.SetCellGroup(xGroup1);
209 return true;
212 bool SharedFormulaUtil::joinFormulaCellAbove( const CellStoreType::position_type& aPos )
214 if (aPos.first->type != sc::element_type_formula)
215 // This is not a formula cell.
216 return false;
218 if (aPos.second == 0)
219 // This cell is already the top cell in a formula block; the previous
220 // cell is not a formula cell.
221 return false;
223 ScFormulaCell& rPrev = *sc::formula_block::at(*aPos.first->data, aPos.second-1);
224 ScFormulaCell& rCell = *sc::formula_block::at(*aPos.first->data, aPos.second);
225 sc::CellStoreType::position_type aPosPrev = aPos;
226 --aPosPrev.second;
227 return joinFormulaCells(aPosPrev, rPrev, rCell);
230 void SharedFormulaUtil::unshareFormulaCell(const CellStoreType::position_type& aPos, ScFormulaCell& rCell)
232 if (!rCell.IsShared())
233 return;
235 ScFormulaCellGroupRef xNone;
236 sc::CellStoreType::iterator it = aPos.first;
238 // This formula cell is shared. Adjust the shared group.
239 if (rCell.aPos.Row() == rCell.GetSharedTopRow())
241 // Top of the shared range.
242 const ScFormulaCellGroupRef& xGroup = rCell.GetCellGroup();
243 if (xGroup->mnLength == 2)
245 // Group consists of only two cells. Mark the second one non-shared.
246 assert (aPos.second+1 < aPos.first->size
247 && "There is no next formula cell but there should be!");
248 ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
249 rNext.SetCellGroup(xNone);
251 else
253 // Move the top cell to the next formula cell down.
254 ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
255 xGroup->mpTopCell = &rNext;
257 --xGroup->mnLength;
259 else if (rCell.aPos.Row() == rCell.GetSharedTopRow() + rCell.GetSharedLength() - 1)
261 // Bottom of the shared range.
262 const ScFormulaCellGroupRef& xGroup = rCell.GetCellGroup();
263 if (xGroup->mnLength == 2)
265 // Mark the top cell non-shared.
266 assert(aPos.second != 0 && "There is no previous formula cell but there should be!");
267 ScFormulaCell& rPrev = *sc::formula_block::at(*it->data, aPos.second-1);
268 rPrev.SetCellGroup(xNone);
270 else
272 // Just shorten the shared range length by one.
273 --xGroup->mnLength;
276 else
278 // In the middle of the shared range. Split it into two groups.
279 const ScFormulaCellGroupRef& xGroup = rCell.GetCellGroup();
280 SCROW nEndRow = xGroup->mpTopCell->aPos.Row() + xGroup->mnLength - 1;
281 xGroup->mnLength = rCell.aPos.Row() - xGroup->mpTopCell->aPos.Row(); // Shorten the top group.
282 if (xGroup->mnLength == 1)
284 // Make the top cell non-shared.
285 assert(aPos.second != 0 && "There is no previous formula cell but there should be!");
286 ScFormulaCell& rPrev = *sc::formula_block::at(*it->data, aPos.second-1);
287 rPrev.SetCellGroup(xNone);
290 SCROW nLength2 = nEndRow - rCell.aPos.Row();
291 if (nLength2 >= 2)
293 ScFormulaCellGroupRef xGroup2;
294 xGroup2.reset(new ScFormulaCellGroup);
295 ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
296 xGroup2->mpTopCell = &rNext;
297 xGroup2->mnLength = nLength2;
298 xGroup2->mbInvariant = xGroup->mbInvariant;
299 xGroup2->mpCode = xGroup->mpCode->CloneValue();
300 assert(xGroup2->mpTopCell->aPos.Row() + size_t(xGroup2->mnLength) <= it->position + it->size
301 && "Shared formula region goes beyond the formula block.");
302 sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
303 std::advance(itCell, aPos.second+1);
304 sc::formula_block::iterator itCellEnd = itCell;
305 std::advance(itCellEnd, xGroup2->mnLength);
306 for (; itCell != itCellEnd; ++itCell)
308 ScFormulaCell& rCell2 = **itCell;
309 rCell2.SetCellGroup(xGroup2);
312 else
314 // Make the next cell non-shared.
315 sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
316 std::advance(itCell, aPos.second+1);
317 ScFormulaCell& rCell2 = **itCell;
318 rCell2.SetCellGroup(xNone);
322 rCell.SetCellGroup(xNone);
325 void SharedFormulaUtil::unshareFormulaCells(const ScDocument& rDoc, CellStoreType& rCells, std::vector<SCROW>& rRows)
327 if (rRows.empty())
328 return;
330 // Sort and remove duplicates.
331 std::sort(rRows.begin(), rRows.end());
332 rRows.erase(std::unique(rRows.begin(), rRows.end()), rRows.end());
334 // Add next cell positions to the list (to ensure that each position becomes a single cell).
335 std::vector<SCROW> aRows2;
336 for (const auto& rRow : rRows)
338 if (rRow > rDoc.MaxRow())
339 break;
341 aRows2.push_back(rRow);
343 if (rRow < rDoc.MaxRow())
344 aRows2.push_back(rRow+1);
347 // Remove duplicates again (the vector should still be sorted).
348 aRows2.erase(std::unique(aRows2.begin(), aRows2.end()), aRows2.end());
350 splitFormulaCellGroups(rDoc, rCells, aRows2);
353 void SharedFormulaUtil::startListeningAsGroup( sc::StartListeningContext& rCxt, ScFormulaCell** ppSharedTop )
355 ScFormulaCell& rTopCell = **ppSharedTop;
356 assert(rTopCell.IsSharedTop());
358 #if USE_FORMULA_GROUP_LISTENER
359 ScDocument& rDoc = rCxt.getDoc();
360 rDoc.SetDetectiveDirty(true);
362 ScFormulaCellGroupRef xGroup = rTopCell.GetCellGroup();
363 const ScTokenArray& rCode = *xGroup->mpCode;
364 assert(&rCode == rTopCell.GetCode());
365 if (rCode.IsRecalcModeAlways())
367 rDoc.StartListeningArea(
368 BCA_LISTEN_ALWAYS, false,
369 xGroup->getAreaListener(ppSharedTop, BCA_LISTEN_ALWAYS, true, true));
372 formula::FormulaToken** p = rCode.GetCode();
373 formula::FormulaToken** pEnd = p + rCode.GetCodeLen();
374 for (; p != pEnd; ++p)
376 const formula::FormulaToken* t = *p;
377 switch (t->GetType())
379 case formula::svSingleRef:
381 const ScSingleRefData* pRef = t->GetSingleRef();
382 ScAddress aPos = pRef->toAbs(rDoc, rTopCell.aPos);
383 ScFormulaCell** pp = ppSharedTop;
384 ScFormulaCell** ppEnd = ppSharedTop + xGroup->mnLength;
385 for (; pp != ppEnd; ++pp)
387 if (!aPos.IsValid())
388 break;
390 rDoc.StartListeningCell(rCxt, aPos, **pp);
391 if (pRef->IsRowRel())
392 aPos.IncRow();
395 break;
396 case formula::svDoubleRef:
398 const ScSingleRefData& rRef1 = *t->GetSingleRef();
399 const ScSingleRefData& rRef2 = *t->GetSingleRef2();
400 ScAddress aPos1 = rRef1.toAbs(rDoc, rTopCell.aPos);
401 ScAddress aPos2 = rRef2.toAbs(rDoc, rTopCell.aPos);
403 ScRange aOrigRange(aPos1, aPos2);
404 ScRange aListenedRange = aOrigRange;
405 if (rRef2.IsRowRel())
406 aListenedRange.aEnd.IncRow(xGroup->mnLength-1);
408 if (aPos1.IsValid() && aPos2.IsValid())
410 rDoc.StartListeningArea(
411 aListenedRange, true,
412 xGroup->getAreaListener(ppSharedTop, aOrigRange, !rRef1.IsRowRel(), !rRef2.IsRowRel()));
415 break;
416 default:
421 ScFormulaCell** pp = ppSharedTop;
422 ScFormulaCell** ppEnd = ppSharedTop + xGroup->mnLength;
423 for (; pp != ppEnd; ++pp)
425 ScFormulaCell& rCell = **pp;
426 rCell.SetNeedsListening(false);
429 #else
430 ScFormulaCell** pp = ppSharedTop;
431 ScFormulaCell** ppEnd = ppSharedTop + rTopCell.GetSharedLength();
432 for (; pp != ppEnd; ++pp)
434 ScFormulaCell& rFC = **pp;
435 rFC.StartListeningTo(rCxt);
437 #endif
442 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */