fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / sc / source / core / tool / sharedformula.cxx
blob8e84ac0f0135a29b7ca1f4fe5473ad078c4058ae
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>
17 namespace sc {
19 void SharedFormulaUtil::splitFormulaCellGroup(const CellStoreType::position_type& aPos, sc::EndListeningContext* pCxt)
21 SCROW nRow = aPos.first->position + aPos.second;
23 if (aPos.first->type != sc::element_type_formula)
24 // Not a formula cell block.
25 return;
27 if (aPos.second == 0)
28 // Split position coincides with the block border. Nothing to do.
29 return;
31 sc::formula_block::iterator it = sc::formula_block::begin(*aPos.first->data);
32 std::advance(it, aPos.second);
33 ScFormulaCell& rTop = **it;
34 if (!rTop.IsShared())
35 // Not a shared formula.
36 return;
38 if (nRow == rTop.GetSharedTopRow())
39 // Already the top cell of a shared group.
40 return;
42 ScFormulaCellGroupRef xGroup = rTop.GetCellGroup();
44 SCROW nLength2 = xGroup->mpTopCell->aPos.Row() + xGroup->mnLength - nRow;
45 ScFormulaCellGroupRef xGroup2;
46 if (nLength2 > 1)
48 xGroup2.reset(new ScFormulaCellGroup);
49 xGroup2->mbInvariant = xGroup->mbInvariant;
50 xGroup2->mpTopCell = &rTop;
51 xGroup2->mnLength = nLength2;
52 xGroup2->mpCode = xGroup->mpCode->Clone();
55 xGroup->mnLength = nRow - xGroup->mpTopCell->aPos.Row();
56 ScFormulaCell& rPrevTop = *sc::formula_block::at(*aPos.first->data, aPos.second - xGroup->mnLength);
58 #if USE_FORMULA_GROUP_LISTENER
59 // At least group area listeners will have to be adapted. As long as
60 // there's no update mechanism and no separated handling of group area and
61 // other listeners, all listeners of this group's top cell are to be reset.
62 if (nLength2)
64 // If a context exists it has to be used to not interfere with
65 // ScColumn::maBroadcasters iterators, which the EndListeningTo()
66 // without context would do when removing a broadcaster that had its
67 // last listener removed.
68 if (pCxt)
69 rPrevTop.EndListeningTo(*pCxt);
70 else
71 rPrevTop.EndListeningTo( rPrevTop.GetDocument(), NULL, ScAddress( ScAddress::UNINITIALIZED));
72 rPrevTop.SetNeedsListening(true);
74 #endif
76 if (xGroup->mnLength == 1)
78 // The top group consists of only one cell. Ungroup this.
79 ScFormulaCellGroupRef xNone;
80 rPrevTop.SetCellGroup(xNone);
83 // Apply the lower group object to the lower cells.
84 #if DEBUG_COLUMN_STORAGE
85 if (xGroup2->mpTopCell->aPos.Row() + xGroup2->mnLength > aPos.first->position + aPos.first->size)
87 cerr << "ScColumn::SplitFormulaCellGroup: Shared formula region goes beyond the formula block. Not good." << endl;
88 cerr.flush();
89 abort();
91 #endif
92 sc::formula_block::iterator itEnd = it;
93 std::advance(itEnd, nLength2);
94 for (; it != itEnd; ++it)
96 ScFormulaCell& rCell = **it;
97 rCell.SetCellGroup(xGroup2);
101 void SharedFormulaUtil::splitFormulaCellGroups(CellStoreType& rCells, std::vector<SCROW>& rBounds)
103 if (rBounds.empty())
104 return;
106 // Sort and remove duplicates.
107 std::sort(rBounds.begin(), rBounds.end());
108 std::vector<SCROW>::iterator it = std::unique(rBounds.begin(), rBounds.end());
109 rBounds.erase(it, rBounds.end());
111 it = rBounds.begin();
112 SCROW nRow = *it;
113 CellStoreType::position_type aPos = rCells.position(nRow);
114 if (aPos.first == rCells.end())
115 return;
117 splitFormulaCellGroup(aPos, nullptr);
118 std::vector<SCROW>::iterator itEnd = rBounds.end();
119 for (++it; it != itEnd; ++it)
121 nRow = *it;
122 aPos = rCells.position(aPos.first, nRow);
123 if (aPos.first == rCells.end())
124 return;
126 splitFormulaCellGroup(aPos, nullptr);
130 bool SharedFormulaUtil::joinFormulaCells(
131 const CellStoreType::position_type& rPos, ScFormulaCell& rCell1, ScFormulaCell& rCell2 )
133 ScFormulaCell::CompareState eState = rCell1.CompareByTokenArray(rCell2);
134 if (eState == ScFormulaCell::NotEqual)
135 return false;
137 // Formula tokens equal those of the previous formula cell.
138 ScFormulaCellGroupRef xGroup1 = rCell1.GetCellGroup();
139 ScFormulaCellGroupRef xGroup2 = rCell2.GetCellGroup();
140 if (xGroup1)
142 if (xGroup2)
144 // Both cell 1 and cell 2 are shared. Merge them together.
145 if (xGroup1.get() == xGroup2.get())
146 // They belong to the same group.
147 return false;
149 // Set the group object from cell 1 to all cells in group 2.
150 xGroup1->mnLength += xGroup2->mnLength;
151 size_t nOffset = rPos.second + 1; // position of cell 2
152 for (size_t i = 0, n = xGroup2->mnLength; i < n; ++i)
154 ScFormulaCell& rCell = *sc::formula_block::at(*rPos.first->data, nOffset+i);
155 rCell.SetCellGroup(xGroup1);
158 else
160 // cell 1 is shared but cell 2 is not.
161 rCell2.SetCellGroup(xGroup1);
162 ++xGroup1->mnLength;
165 else
167 if (xGroup2)
169 // cell 1 is not shared, but cell 2 is already shared.
170 rCell1.SetCellGroup(xGroup2);
171 xGroup2->mpTopCell = &rCell1;
172 ++xGroup2->mnLength;
174 else
176 // neither cells are shared.
177 assert(rCell1.aPos.Row() == (SCROW)(rPos.first->position + rPos.second));
178 xGroup1 = rCell1.CreateCellGroup(2, eState == ScFormulaCell::EqualInvariant);
179 rCell2.SetCellGroup(xGroup1);
183 return true;
186 bool SharedFormulaUtil::joinFormulaCellAbove( const CellStoreType::position_type& aPos )
188 if (aPos.first->type != sc::element_type_formula)
189 // This is not a formula cell.
190 return false;
192 if (aPos.second == 0)
193 // This cell is already the top cell in a formula block; the previous
194 // cell is not a formula cell.
195 return false;
197 ScFormulaCell& rPrev = *sc::formula_block::at(*aPos.first->data, aPos.second-1);
198 ScFormulaCell& rCell = *sc::formula_block::at(*aPos.first->data, aPos.second);
199 sc::CellStoreType::position_type aPosPrev = aPos;
200 --aPosPrev.second;
201 return joinFormulaCells(aPosPrev, rPrev, rCell);
204 void SharedFormulaUtil::unshareFormulaCell(const CellStoreType::position_type& aPos, ScFormulaCell& rCell)
206 if (!rCell.IsShared())
207 return;
209 ScFormulaCellGroupRef xNone;
210 sc::CellStoreType::iterator it = aPos.first;
212 // This formula cell is shared. Adjust the shared group.
213 if (rCell.aPos.Row() == rCell.GetSharedTopRow())
215 // Top of the shared range.
216 ScFormulaCellGroupRef xGroup = rCell.GetCellGroup();
217 if (xGroup->mnLength == 2)
219 // Group consists only only two cells. Mark the second one non-shared.
220 #if DEBUG_COLUMN_STORAGE
221 if (aPos.second+1 >= aPos.first->size)
223 cerr << "ScColumn::UnshareFormulaCell: There is no next formula cell but there should be!" << endl;
224 cerr.flush();
225 abort();
227 #endif
228 ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
229 rNext.SetCellGroup(xNone);
231 else
233 // Move the top cell to the next formula cell down.
234 ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
235 --xGroup->mnLength;
236 xGroup->mpTopCell = &rNext;
239 else if (rCell.aPos.Row() == rCell.GetSharedTopRow() + rCell.GetSharedLength() - 1)
241 // Bottom of the shared range.
242 ScFormulaCellGroupRef xGroup = rCell.GetCellGroup();
243 if (xGroup->mnLength == 2)
245 // Mark the top cell non-shared.
246 #if DEBUG_COLUMN_STORAGE
247 if (aPos.second == 0)
249 cerr << "ScColumn::UnshareFormulaCell: There is no previous formula cell but there should be!" << endl;
250 cerr.flush();
251 abort();
253 #endif
254 ScFormulaCell& rPrev = *sc::formula_block::at(*it->data, aPos.second-1);
255 rPrev.SetCellGroup(xNone);
257 else
259 // Just shortern the shared range length by one.
260 --xGroup->mnLength;
263 else
265 // In the middle of the shared range. Split it into two groups.
266 ScFormulaCellGroupRef xGroup = rCell.GetCellGroup();
267 SCROW nEndRow = xGroup->mpTopCell->aPos.Row() + xGroup->mnLength - 1;
268 xGroup->mnLength = rCell.aPos.Row() - xGroup->mpTopCell->aPos.Row(); // Shorten the top group.
269 if (xGroup->mnLength == 1)
271 // Make the top cell non-shared.
272 #if DEBUG_COLUMN_STORAGE
273 if (aPos.second == 0)
275 cerr << "ScColumn::UnshareFormulaCell: There is no previous formula cell but there should be!" << endl;
276 cerr.flush();
277 abort();
279 #endif
280 ScFormulaCell& rPrev = *sc::formula_block::at(*it->data, aPos.second-1);
281 rPrev.SetCellGroup(xNone);
284 SCROW nLength2 = nEndRow - rCell.aPos.Row();
285 if (nLength2 >= 2)
287 ScFormulaCellGroupRef xGroup2;
288 xGroup2.reset(new ScFormulaCellGroup);
289 ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
290 xGroup2->mpTopCell = &rNext;
291 xGroup2->mnLength = nLength2;
292 xGroup2->mbInvariant = xGroup->mbInvariant;
293 xGroup2->mpCode = xGroup->mpCode->Clone();
294 #if DEBUG_COLUMN_STORAGE
295 if (xGroup2->mpTopCell->aPos.Row() + xGroup2->mnLength > it->position + it->size)
297 cerr << "ScColumn::UnshareFormulaCell: Shared formula region goes beyond the formula block. Not good." << endl;
298 cerr.flush();
299 abort();
301 #endif
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(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 std::vector<SCROW>::const_iterator it = rRows.begin(), itEnd = rRows.end();
337 for (; it != itEnd; ++it)
339 if (*it > MAXROW)
340 break;
342 aRows2.push_back(*it);
344 if (*it < MAXROW)
345 aRows2.push_back(*it+1);
348 // Remove duplicates again (the vector should still be sorted).
349 aRows2.erase(std::unique(aRows2.begin(), aRows2.end()), aRows2.end());
351 splitFormulaCellGroups(rCells, aRows2);
354 void SharedFormulaUtil::startListeningAsGroup( sc::StartListeningContext& rCxt, ScFormulaCell** ppSharedTop )
356 ScFormulaCell& rTopCell = **ppSharedTop;
357 assert(rTopCell.IsSharedTop());
359 #if USE_FORMULA_GROUP_LISTENER
360 ScDocument& rDoc = rCxt.getDoc();
361 rDoc.SetDetectiveDirty(true);
363 ScFormulaCellGroupRef xGroup = rTopCell.GetCellGroup();
364 const ScTokenArray* pCode = xGroup->mpCode;
365 assert(pCode == rTopCell.GetCode());
366 if (pCode->IsRecalcModeAlways())
368 rDoc.StartListeningArea(
369 BCA_LISTEN_ALWAYS, false,
370 xGroup->getAreaListener(ppSharedTop, BCA_LISTEN_ALWAYS, true, true));
373 formula::FormulaToken** p = pCode->GetCode();
374 formula::FormulaToken** pEnd = p + pCode->GetCodeLen();
375 for (; p != pEnd; ++p)
377 const formula::FormulaToken* t = *p;
378 switch (t->GetType())
380 case formula::svSingleRef:
382 const ScSingleRefData* pRef = t->GetSingleRef();
383 ScAddress aPos = pRef->toAbs(rTopCell.aPos);
384 ScFormulaCell** pp = ppSharedTop;
385 ScFormulaCell** ppEnd = ppSharedTop + xGroup->mnLength;
386 for (; pp != ppEnd; ++pp)
388 if (!aPos.IsValid())
389 break;
391 rDoc.StartListeningCell(rCxt, aPos, **pp);
392 if (pRef->IsRowRel())
393 aPos.IncRow();
396 break;
397 case formula::svDoubleRef:
399 const ScSingleRefData& rRef1 = *t->GetSingleRef();
400 const ScSingleRefData& rRef2 = *t->GetSingleRef2();
401 ScAddress aPos1 = rRef1.toAbs(rTopCell.aPos);
402 ScAddress aPos2 = rRef2.toAbs(rTopCell.aPos);
404 ScRange aOrigRange = ScRange(aPos1, aPos2);
405 ScRange aListenedRange = aOrigRange;
406 if (rRef2.IsRowRel())
407 aListenedRange.aEnd.IncRow(xGroup->mnLength-1);
409 if (aPos1.IsValid() && aPos2.IsValid())
411 rDoc.StartListeningArea(
412 aListenedRange, true,
413 xGroup->getAreaListener(ppSharedTop, aOrigRange, !rRef1.IsRowRel(), !rRef2.IsRowRel()));
416 break;
417 default:
422 ScFormulaCell** pp = ppSharedTop;
423 ScFormulaCell** ppEnd = ppSharedTop + xGroup->mnLength;
424 for (; pp != ppEnd; ++pp)
426 ScFormulaCell& rCell = **pp;
427 rCell.SetNeedsListening(false);
430 #else
431 ScFormulaCell** pp = ppSharedTop;
432 ScFormulaCell** ppEnd = ppSharedTop + rTopCell.GetSharedLength();
433 for (; pp != ppEnd; ++pp)
435 ScFormulaCell& rFC = **pp;
436 rFC.StartListeningTo(rCxt);
438 #endif
443 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */