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 :
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 :
17 : namespace sc {
18 :
19 57928 : void SharedFormulaUtil::splitFormulaCellGroup(const CellStoreType::position_type& aPos)
20 : {
21 57928 : SCROW nRow = aPos.first->position + aPos.second;
22 :
23 57928 : if (aPos.first->type != sc::element_type_formula)
24 : // Not a formula cell block.
25 115604 : return;
26 :
27 205 : if (aPos.second == 0)
28 : // Split position coincides with the block border. Nothing to do.
29 140 : return;
30 :
31 65 : sc::formula_block::iterator it = sc::formula_block::begin(*aPos.first->data);
32 65 : std::advance(it, aPos.second);
33 65 : ScFormulaCell& rTop = **it;
34 65 : if (!rTop.IsShared())
35 : // Not a shared formula.
36 13 : return;
37 :
38 52 : if (nRow == rTop.GetSharedTopRow())
39 : // Already the top cell of a shared group.
40 5 : return;
41 :
42 47 : ScFormulaCellGroupRef xGroup = rTop.GetCellGroup();
43 :
44 47 : SCROW nLength2 = xGroup->mpTopCell->aPos.Row() + xGroup->mnLength - nRow;
45 94 : ScFormulaCellGroupRef xGroup2;
46 47 : if (nLength2 > 1)
47 : {
48 37 : xGroup2.reset(new ScFormulaCellGroup);
49 37 : xGroup2->mbInvariant = xGroup->mbInvariant;
50 37 : xGroup2->mpTopCell = &rTop;
51 37 : xGroup2->mnLength = nLength2;
52 37 : xGroup2->mpCode = xGroup->mpCode->Clone();
53 : }
54 :
55 47 : xGroup->mnLength = nRow - xGroup->mpTopCell->aPos.Row();
56 47 : ScFormulaCell& rPrevTop = *sc::formula_block::at(*aPos.first->data, aPos.second - xGroup->mnLength);
57 :
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 47 : if (nLength2)
63 : {
64 47 : rPrevTop.EndListeningTo( rPrevTop.GetDocument(), NULL, ScAddress( ScAddress::UNINITIALIZED));
65 47 : rPrevTop.SetNeedsListening(true);
66 : }
67 : #endif
68 :
69 47 : if (xGroup->mnLength == 1)
70 : {
71 : // The top group consists of only one cell. Ungroup this.
72 34 : ScFormulaCellGroupRef xNone;
73 34 : rPrevTop.SetCellGroup(xNone);
74 : }
75 :
76 : // Apply the lower group object to the lower cells.
77 : #if DEBUG_COLUMN_STORAGE
78 : if (xGroup2->mpTopCell->aPos.Row() + xGroup2->mnLength > aPos.first->position + aPos.first->size)
79 : {
80 : cerr << "ScColumn::SplitFormulaCellGroup: Shared formula region goes beyond the formula block. Not good." << endl;
81 : cerr.flush();
82 : abort();
83 : }
84 : #endif
85 47 : sc::formula_block::iterator itEnd = it;
86 47 : std::advance(itEnd, nLength2);
87 197 : for (; it != itEnd; ++it)
88 : {
89 150 : ScFormulaCell& rCell = **it;
90 150 : rCell.SetCellGroup(xGroup2);
91 47 : }
92 : }
93 :
94 611 : void SharedFormulaUtil::splitFormulaCellGroups(CellStoreType& rCells, std::vector<SCROW>& rBounds)
95 : {
96 611 : if (rBounds.empty())
97 368 : return;
98 :
99 : // Sort and remove duplicates.
100 427 : std::sort(rBounds.begin(), rBounds.end());
101 427 : std::vector<SCROW>::iterator it = std::unique(rBounds.begin(), rBounds.end());
102 427 : rBounds.erase(it, rBounds.end());
103 :
104 427 : it = rBounds.begin();
105 427 : SCROW nRow = *it;
106 427 : CellStoreType::position_type aPos = rCells.position(nRow);
107 427 : if (aPos.first == rCells.end())
108 0 : return;
109 :
110 427 : splitFormulaCellGroup(aPos);
111 427 : std::vector<SCROW>::iterator itEnd = rBounds.end();
112 734 : for (++it; it != itEnd; ++it)
113 : {
114 307 : nRow = *it;
115 307 : aPos = rCells.position(aPos.first, nRow);
116 307 : if (aPos.first == rCells.end())
117 0 : return;
118 :
119 307 : splitFormulaCellGroup(aPos);
120 : }
121 : }
122 :
123 1075 : bool SharedFormulaUtil::joinFormulaCells(
124 : const CellStoreType::position_type& rPos, ScFormulaCell& rCell1, ScFormulaCell& rCell2 )
125 : {
126 1075 : ScFormulaCell::CompareState eState = rCell1.CompareByTokenArray(rCell2);
127 1075 : if (eState == ScFormulaCell::NotEqual)
128 380 : return false;
129 :
130 : // Formula tokens equal those of the previous formula cell.
131 695 : ScFormulaCellGroupRef xGroup1 = rCell1.GetCellGroup();
132 1390 : ScFormulaCellGroupRef xGroup2 = rCell2.GetCellGroup();
133 695 : if (xGroup1)
134 : {
135 543 : if (xGroup2)
136 : {
137 : // Both cell 1 and cell 2 are shared. Merge them together.
138 25 : if (xGroup1.get() == xGroup2.get())
139 : // They belong to the same group.
140 6 : return false;
141 :
142 : // Set the group object from cell 1 to all cells in group 2.
143 19 : xGroup1->mnLength += xGroup2->mnLength;
144 19 : size_t nOffset = rPos.second + 1; // position of cell 2
145 90 : for (size_t i = 0, n = xGroup2->mnLength; i < n; ++i)
146 : {
147 71 : ScFormulaCell& rCell = *sc::formula_block::at(*rPos.first->data, nOffset+i);
148 71 : rCell.SetCellGroup(xGroup1);
149 : }
150 : }
151 : else
152 : {
153 : // cell 1 is shared but cell 2 is not.
154 518 : rCell2.SetCellGroup(xGroup1);
155 518 : ++xGroup1->mnLength;
156 : }
157 : }
158 : else
159 : {
160 152 : if (xGroup2)
161 : {
162 : // cell 1 is not shared, but cell 2 is already shared.
163 5 : rCell1.SetCellGroup(xGroup2);
164 5 : xGroup2->mpTopCell = &rCell1;
165 5 : ++xGroup2->mnLength;
166 : }
167 : else
168 : {
169 : // neither cells are shared.
170 : assert(rCell1.aPos.Row() == (SCROW)(rPos.first->position + rPos.second));
171 147 : xGroup1 = rCell1.CreateCellGroup(2, eState == ScFormulaCell::EqualInvariant);
172 147 : rCell2.SetCellGroup(xGroup1);
173 : }
174 : }
175 :
176 1384 : return true;
177 : }
178 :
179 47457 : bool SharedFormulaUtil::joinFormulaCellAbove( const CellStoreType::position_type& aPos )
180 : {
181 47457 : if (aPos.first->type != sc::element_type_formula)
182 : // This is not a formula cell.
183 47339 : return false;
184 :
185 118 : if (aPos.second == 0)
186 : // This cell is already the top cell in a formula block; the previous
187 : // cell is not a formula cell.
188 110 : return false;
189 :
190 8 : ScFormulaCell& rPrev = *sc::formula_block::at(*aPos.first->data, aPos.second-1);
191 8 : ScFormulaCell& rCell = *sc::formula_block::at(*aPos.first->data, aPos.second);
192 8 : sc::CellStoreType::position_type aPosPrev = aPos;
193 8 : --aPosPrev.second;
194 8 : return joinFormulaCells(aPosPrev, rPrev, rCell);
195 : }
196 :
197 330 : void SharedFormulaUtil::unshareFormulaCell(const CellStoreType::position_type& aPos, ScFormulaCell& rCell)
198 : {
199 330 : if (!rCell.IsShared())
200 559 : return;
201 :
202 101 : ScFormulaCellGroupRef xNone;
203 101 : sc::CellStoreType::iterator it = aPos.first;
204 :
205 : // This formula cell is shared. Adjust the shared group.
206 101 : if (rCell.aPos.Row() == rCell.GetSharedTopRow())
207 : {
208 : // Top of the shared range.
209 50 : ScFormulaCellGroupRef xGroup = rCell.GetCellGroup();
210 50 : if (xGroup->mnLength == 2)
211 : {
212 : // Group consists only only two cells. Mark the second one non-shared.
213 : #if DEBUG_COLUMN_STORAGE
214 : if (aPos.second+1 >= aPos.first->size)
215 : {
216 : cerr << "ScColumn::UnshareFormulaCell: There is no next formula cell but there should be!" << endl;
217 : cerr.flush();
218 : abort();
219 : }
220 : #endif
221 14 : ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
222 14 : rNext.SetCellGroup(xNone);
223 : }
224 : else
225 : {
226 : // Move the top cell to the next formula cell down.
227 36 : ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
228 36 : --xGroup->mnLength;
229 36 : xGroup->mpTopCell = &rNext;
230 50 : }
231 : }
232 51 : else if (rCell.aPos.Row() == rCell.GetSharedTopRow() + rCell.GetSharedLength() - 1)
233 : {
234 : // Bottom of the shared range.
235 25 : ScFormulaCellGroupRef xGroup = rCell.GetCellGroup();
236 25 : if (xGroup->mnLength == 2)
237 : {
238 : // Mark the top cell non-shared.
239 : #if DEBUG_COLUMN_STORAGE
240 : if (aPos.second == 0)
241 : {
242 : cerr << "ScColumn::UnshareFormulaCell: There is no previous formula cell but there should be!" << endl;
243 : cerr.flush();
244 : abort();
245 : }
246 : #endif
247 9 : ScFormulaCell& rPrev = *sc::formula_block::at(*it->data, aPos.second-1);
248 9 : rPrev.SetCellGroup(xNone);
249 : }
250 : else
251 : {
252 : // Just shortern the shared range length by one.
253 16 : --xGroup->mnLength;
254 25 : }
255 : }
256 : else
257 : {
258 : // In the middle of the shared range. Split it into two groups.
259 26 : ScFormulaCellGroupRef xGroup = rCell.GetCellGroup();
260 26 : SCROW nEndRow = xGroup->mpTopCell->aPos.Row() + xGroup->mnLength - 1;
261 26 : xGroup->mnLength = rCell.aPos.Row() - xGroup->mpTopCell->aPos.Row(); // Shorten the top group.
262 26 : if (xGroup->mnLength == 1)
263 : {
264 : // Make the top cell non-shared.
265 : #if DEBUG_COLUMN_STORAGE
266 : if (aPos.second == 0)
267 : {
268 : cerr << "ScColumn::UnshareFormulaCell: There is no previous formula cell but there should be!" << endl;
269 : cerr.flush();
270 : abort();
271 : }
272 : #endif
273 7 : ScFormulaCell& rPrev = *sc::formula_block::at(*it->data, aPos.second-1);
274 7 : rPrev.SetCellGroup(xNone);
275 : }
276 :
277 26 : SCROW nLength2 = nEndRow - rCell.aPos.Row();
278 26 : if (nLength2 >= 2)
279 : {
280 19 : ScFormulaCellGroupRef xGroup2;
281 19 : xGroup2.reset(new ScFormulaCellGroup);
282 19 : ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
283 19 : xGroup2->mpTopCell = &rNext;
284 19 : xGroup2->mnLength = nLength2;
285 19 : xGroup2->mbInvariant = xGroup->mbInvariant;
286 19 : xGroup2->mpCode = xGroup->mpCode->Clone();
287 : #if DEBUG_COLUMN_STORAGE
288 : if (xGroup2->mpTopCell->aPos.Row() + xGroup2->mnLength > it->position + it->size)
289 : {
290 : cerr << "ScColumn::UnshareFormulaCell: Shared formula region goes beyond the formula block. Not good." << endl;
291 : cerr.flush();
292 : abort();
293 : }
294 : #endif
295 19 : sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
296 19 : std::advance(itCell, aPos.second+1);
297 19 : sc::formula_block::iterator itCellEnd = itCell;
298 19 : std::advance(itCellEnd, xGroup2->mnLength);
299 115 : for (; itCell != itCellEnd; ++itCell)
300 : {
301 96 : ScFormulaCell& rCell2 = **itCell;
302 96 : rCell2.SetCellGroup(xGroup2);
303 19 : }
304 : }
305 : else
306 : {
307 : // Make the next cell non-shared.
308 7 : sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
309 7 : std::advance(itCell, aPos.second+1);
310 7 : ScFormulaCell& rCell2 = **itCell;
311 7 : rCell2.SetCellGroup(xNone);
312 26 : }
313 : }
314 :
315 101 : rCell.SetCellGroup(xNone);
316 : }
317 :
318 5 : void SharedFormulaUtil::unshareFormulaCells(CellStoreType& rCells, std::vector<SCROW>& rRows)
319 : {
320 5 : if (rRows.empty())
321 5 : return;
322 :
323 : // Sort and remove duplicates.
324 5 : std::sort(rRows.begin(), rRows.end());
325 5 : rRows.erase(std::unique(rRows.begin(), rRows.end()), rRows.end());
326 :
327 : // Add next cell positions to the list (to ensure that each position becomes a single cell).
328 5 : std::vector<SCROW> aRows2;
329 5 : std::vector<SCROW>::const_iterator it = rRows.begin(), itEnd = rRows.end();
330 33 : for (; it != itEnd; ++it)
331 : {
332 28 : if (*it > MAXROW)
333 0 : break;
334 :
335 28 : aRows2.push_back(*it);
336 :
337 28 : if (*it < MAXROW)
338 28 : aRows2.push_back(*it+1);
339 : }
340 :
341 : // Remove duplicates again (the vector should still be sorted).
342 5 : aRows2.erase(std::unique(aRows2.begin(), aRows2.end()), aRows2.end());
343 :
344 5 : splitFormulaCellGroups(rCells, aRows2);
345 : }
346 :
347 1044 : void SharedFormulaUtil::startListeningAsGroup( sc::StartListeningContext& rCxt, ScFormulaCell** ppSharedTop )
348 : {
349 1044 : ScFormulaCell& rTopCell = **ppSharedTop;
350 : assert(rTopCell.IsSharedTop());
351 :
352 : #if USE_FORMULA_GROUP_LISTENER
353 1044 : ScDocument& rDoc = rCxt.getDoc();
354 1044 : rDoc.SetDetectiveDirty(true);
355 :
356 1044 : ScFormulaCellGroupRef xGroup = rTopCell.GetCellGroup();
357 1044 : const ScTokenArray* pCode = xGroup->mpCode;
358 : assert(pCode == rTopCell.GetCode());
359 1044 : if (pCode->IsRecalcModeAlways())
360 : {
361 : rDoc.StartListeningArea(
362 : BCA_LISTEN_ALWAYS, false,
363 1 : xGroup->getAreaListener(ppSharedTop, BCA_LISTEN_ALWAYS, true, true));
364 : }
365 :
366 1044 : formula::FormulaToken** p = pCode->GetCode();
367 1044 : formula::FormulaToken** pEnd = p + pCode->GetCodeLen();
368 6333 : for (; p != pEnd; ++p)
369 : {
370 5289 : const formula::FormulaToken* t = *p;
371 5289 : switch (t->GetType())
372 : {
373 : case formula::svSingleRef:
374 : {
375 1769 : const ScSingleRefData* pRef = t->GetSingleRef();
376 1769 : ScAddress aPos = pRef->toAbs(rTopCell.aPos);
377 1769 : ScFormulaCell** pp = ppSharedTop;
378 1769 : ScFormulaCell** ppEnd = ppSharedTop + xGroup->mnLength;
379 11054 : for (; pp != ppEnd; ++pp)
380 : {
381 9285 : if (!aPos.IsValid())
382 0 : break;
383 :
384 9285 : rDoc.StartListeningCell(rCxt, aPos, **pp);
385 9285 : if (pRef->IsRowRel())
386 7797 : aPos.IncRow();
387 : }
388 : }
389 1769 : break;
390 : case formula::svDoubleRef:
391 : {
392 478 : const ScSingleRefData& rRef1 = *t->GetSingleRef();
393 478 : const ScSingleRefData& rRef2 = *t->GetSingleRef2();
394 478 : ScAddress aPos1 = rRef1.toAbs(rTopCell.aPos);
395 478 : ScAddress aPos2 = rRef2.toAbs(rTopCell.aPos);
396 :
397 478 : ScRange aOrigRange = ScRange(aPos1, aPos2);
398 478 : ScRange aListenedRange = aOrigRange;
399 478 : if (rRef2.IsRowRel())
400 258 : aListenedRange.aEnd.IncRow(xGroup->mnLength-1);
401 :
402 478 : if (aPos1.IsValid() && aPos2.IsValid())
403 : {
404 : rDoc.StartListeningArea(
405 : aListenedRange, true,
406 478 : xGroup->getAreaListener(ppSharedTop, aOrigRange, !rRef1.IsRowRel(), !rRef2.IsRowRel()));
407 : }
408 : }
409 478 : break;
410 : default:
411 : ;
412 : }
413 : }
414 :
415 1044 : ScFormulaCell** pp = ppSharedTop;
416 1044 : ScFormulaCell** ppEnd = ppSharedTop + xGroup->mnLength;
417 6352 : for (; pp != ppEnd; ++pp)
418 : {
419 5308 : ScFormulaCell& rCell = **pp;
420 5308 : rCell.SetNeedsListening(false);
421 1044 : }
422 :
423 : #else
424 : ScFormulaCell** pp = ppSharedTop;
425 : ScFormulaCell** ppEnd = ppSharedTop + rTopCell.GetSharedLength();
426 : for (; pp != ppEnd; ++pp)
427 : {
428 : ScFormulaCell& rFC = **pp;
429 : rFC.StartListeningTo(rCxt);
430 : }
431 : #endif
432 1044 : }
433 :
434 156 : }
435 :
436 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|