LibreOffice Module sc (master) 1
sharedformula.cxx
Go to the documentation of this file.
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#include <refdata.hxx>
17
18namespace sc {
19
20const ScFormulaCell* SharedFormulaUtil::getSharedTopFormulaCell(const CellStoreType::position_type& aPos)
21{
22 if (aPos.first->type != sc::element_type_formula)
23 // Not a formula cell block.
24 return nullptr;
25
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;
32
33 return pCell->GetCellGroup()->mpTopCell;
34}
35
36bool SharedFormulaUtil::splitFormulaCellGroup(const CellStoreType::position_type& aPos, sc::EndListeningContext* pCxt)
37{
38 SCROW nRow = aPos.first->position + aPos.second;
39
40 if (aPos.first->type != sc::element_type_formula)
41 // Not a formula cell block.
42 return false;
43
44 if (aPos.second == 0)
45 // Split position coincides with the block border. Nothing to do.
46 return false;
47
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;
54
55 if (nRow == rTop.GetSharedTopRow())
56 // Already the top cell of a shared group.
57 return false;
58
59 ScFormulaCellGroupRef xGroup = rTop.GetCellGroup();
60
61 SCROW nLength2 = xGroup->mpTopCell->aPos.Row() + xGroup->mnLength - nRow;
63 if (nLength2 > 1)
64 {
65 xGroup2.reset(new ScFormulaCellGroup);
66 xGroup2->mbInvariant = xGroup->mbInvariant;
67 xGroup2->mpTopCell = &rTop;
68 xGroup2->mnLength = nLength2;
69 xGroup2->mpCode = xGroup->mpCode->CloneValue();
70 }
71
72 xGroup->mnLength = nRow - xGroup->mpTopCell->aPos.Row();
73 ScFormulaCell& rPrevTop = *sc::formula_block::at(*aPos.first->data, aPos.second - xGroup->mnLength);
74
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)
80 {
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);
90
91 // The new group or remaining single cell needs a new listening.
92 rTop.SetNeedsListening(true);
93 }
94#endif
95
96 if (xGroup->mnLength == 1)
97 {
98 // The top group consists of only one cell. Ungroup this.
100 rPrevTop.SetCellGroup(xNone);
101 }
102
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)
109 {
110 ScFormulaCell& rCell = **it;
111 rCell.SetCellGroup(xGroup2);
112 }
113
114 return true;
115}
116
117bool SharedFormulaUtil::splitFormulaCellGroups(const ScDocument& rDoc, CellStoreType& rCells, std::vector<SCROW>& rBounds)
118{
119 if (rBounds.empty())
120 return false;
121
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());
126
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;
132
133 bool bSplit = splitFormulaCellGroup(aPos, nullptr);
134 std::vector<SCROW>::iterator itEnd = rBounds.end();
135 for (++it; it != itEnd; ++it)
136 {
137 nRow = *it;
138 if (rDoc.ValidRow(nRow))
139 {
140 aPos = rCells.position(aPos.first, nRow);
141 if (aPos.first == rCells.end())
142 return bSplit;
143 bSplit |= splitFormulaCellGroup(aPos, nullptr);
144 }
145 }
146 return bSplit;
147}
148
150 const CellStoreType::position_type& rPos, ScFormulaCell& rCell1, ScFormulaCell& rCell2 )
151{
153 {
154 rCell1.GetDocument().AddDelayedFormulaGroupingCell( &rCell1 );
155 rCell1.GetDocument().AddDelayedFormulaGroupingCell( &rCell2 );
156 return false;
157 }
158
159 ScFormulaCell::CompareState eState = rCell1.CompareByTokenArray(rCell2);
160 if (eState == ScFormulaCell::NotEqual)
161 return false;
162
163 // Formula tokens equal those of the previous formula cell.
164 ScFormulaCellGroupRef xGroup1 = rCell1.GetCellGroup();
165 ScFormulaCellGroupRef xGroup2 = rCell2.GetCellGroup();
166 if (xGroup1)
167 {
168 if (xGroup2)
169 {
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;
174
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)
179 {
180 ScFormulaCell& rCell = *sc::formula_block::at(*rPos.first->data, nOffset+i);
181 rCell.SetCellGroup(xGroup1);
182 }
183 }
184 else
185 {
186 // cell 1 is shared but cell 2 is not.
187 rCell2.SetCellGroup(xGroup1);
188 ++xGroup1->mnLength;
189 }
190 }
191 else
192 {
193 if (xGroup2)
194 {
195 // cell 1 is not shared, but cell 2 is already shared.
196 rCell1.SetCellGroup(xGroup2);
197 xGroup2->mpTopCell = &rCell1;
198 ++xGroup2->mnLength;
199 }
200 else
201 {
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);
206 }
207 }
208
209 return true;
210}
211
212bool SharedFormulaUtil::joinFormulaCellAbove( const CellStoreType::position_type& aPos )
213{
214 if (aPos.first->type != sc::element_type_formula)
215 // This is not a formula cell.
216 return false;
217
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;
222
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);
228}
229
230void SharedFormulaUtil::unshareFormulaCell(const CellStoreType::position_type& aPos, ScFormulaCell& rCell)
231{
232 if (!rCell.IsShared())
233 return;
234
236 sc::CellStoreType::iterator it = aPos.first;
237
238 // This formula cell is shared. Adjust the shared group.
239 if (rCell.aPos.Row() == rCell.GetSharedTopRow())
240 {
241 // Top of the shared range.
242 const ScFormulaCellGroupRef& xGroup = rCell.GetCellGroup();
243 if (xGroup->mnLength == 2)
244 {
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);
250 }
251 else
252 {
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;
256 }
257 --xGroup->mnLength;
258 }
259 else if (rCell.aPos.Row() == rCell.GetSharedTopRow() + rCell.GetSharedLength() - 1)
260 {
261 // Bottom of the shared range.
262 const ScFormulaCellGroupRef& xGroup = rCell.GetCellGroup();
263 if (xGroup->mnLength == 2)
264 {
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);
269 }
270 else
271 {
272 // Just shorten the shared range length by one.
273 --xGroup->mnLength;
274 }
275 }
276 else
277 {
278 // In the middle of the shared range. Split it into two groups.
279 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)
283 {
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);
288 }
289
290 SCROW nLength2 = nEndRow - rCell.aPos.Row();
291 if (nLength2 >= 2)
292 {
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)
307 {
308 ScFormulaCell& rCell2 = **itCell;
309 rCell2.SetCellGroup(xGroup2);
310 }
311 }
312 else
313 {
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);
319 }
320 }
321
322 rCell.SetCellGroup(xNone);
323}
324
325void SharedFormulaUtil::unshareFormulaCells(const ScDocument& rDoc, CellStoreType& rCells, std::vector<SCROW>& rRows)
326{
327 if (rRows.empty())
328 return;
329
330 // Sort and remove duplicates.
331 std::sort(rRows.begin(), rRows.end());
332 rRows.erase(std::unique(rRows.begin(), rRows.end()), rRows.end());
333
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)
337 {
338 if (rRow > rDoc.MaxRow())
339 break;
340
341 aRows2.push_back(rRow);
342
343 if (rRow < rDoc.MaxRow())
344 aRows2.push_back(rRow+1);
345 }
346
347 // Remove duplicates again (the vector should still be sorted).
348 aRows2.erase(std::unique(aRows2.begin(), aRows2.end()), aRows2.end());
349
350 splitFormulaCellGroups(rDoc, rCells, aRows2);
351}
352
354{
355 ScFormulaCell& rTopCell = **ppSharedTop;
356 assert(rTopCell.IsSharedTop());
357
358#if USE_FORMULA_GROUP_LISTENER
359 ScDocument& rDoc = rCxt.getDoc();
360 rDoc.SetDetectiveDirty(true);
361
362 ScFormulaCellGroupRef xGroup = rTopCell.GetCellGroup();
363 const ScTokenArray& rCode = *xGroup->mpCode;
364 assert(&rCode == rTopCell.GetCode());
365 if (rCode.IsRecalcModeAlways())
366 {
368 BCA_LISTEN_ALWAYS, false,
369 xGroup->getAreaListener(ppSharedTop, BCA_LISTEN_ALWAYS, true, true));
370 }
371
372 formula::FormulaToken** p = rCode.GetCode();
373 formula::FormulaToken** pEnd = p + rCode.GetCodeLen();
374 for (; p != pEnd; ++p)
375 {
376 const formula::FormulaToken* t = *p;
377 switch (t->GetType())
378 {
380 {
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)
386 {
387 if (!aPos.IsValid())
388 break;
389
390 rDoc.StartListeningCell(rCxt, aPos, **pp);
391 if (pRef->IsRowRel())
392 aPos.IncRow();
393 }
394 }
395 break;
397 {
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);
402
403 ScRange aOrigRange(aPos1, aPos2);
404 ScRange aListenedRange = aOrigRange;
405 if (rRef2.IsRowRel())
406 aListenedRange.aEnd.IncRow(xGroup->mnLength-1);
407
408 if (aPos1.IsValid() && aPos2.IsValid())
409 {
411 aListenedRange, true,
412 xGroup->getAreaListener(ppSharedTop, aOrigRange, !rRef1.IsRowRel(), !rRef2.IsRowRel()));
413 }
414 }
415 break;
416 default:
417 ;
418 }
419 }
420
421 ScFormulaCell** pp = ppSharedTop;
422 ScFormulaCell** ppEnd = ppSharedTop + xGroup->mnLength;
423 for (; pp != ppEnd; ++pp)
424 {
425 ScFormulaCell& rCell = **pp;
426 rCell.SetNeedsListening(false);
427 }
428
429#else
430 ScFormulaCell** pp = ppSharedTop;
431 ScFormulaCell** ppEnd = ppSharedTop + rTopCell.GetSharedLength();
432 for (; pp != ppEnd; ++pp)
433 {
434 ScFormulaCell& rFC = **pp;
435 rFC.StartListeningTo(rCxt);
436 }
437#endif
438}
439
440}
441
442/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
XPropertyListType t
#define BCA_LISTEN_ALWAYS
Definition: address.hxx:945
@ UNINITIALIZED
Definition: address.hxx:220
bool IsValid() const
Definition: address.hxx:305
SCROW Row() const
Definition: address.hxx:274
void IncRow(SCROW nDelta=1)
Definition: address.hxx:312
bool ValidRow(SCROW nRow) const
Definition: document.hxx:899
void SetDetectiveDirty(bool bSet)
Definition: document.hxx:2206
SC_DLLPUBLIC SCROW MaxRow() const
Definition: document.hxx:892
bool IsDelayedFormulaGrouping() const
Definition: document.hxx:1423
void AddDelayedFormulaGroupingCell(const ScFormulaCell *cell)
To be used only by SharedFormulaUtil::joinFormulaCells().
Definition: document10.cxx:393
void StartListeningArea(const ScRange &rRange, bool bGroupListening, SvtListener *pListener)
Definition: documen7.cxx:35
void StartListeningCell(const ScAddress &rAddress, SvtListener *pListener)
Definition: documen7.cxx:221
bool IsShared() const
SCROW GetSharedLength() const
void SetCellGroup(const ScFormulaCellGroupRef &xRef)
const ScFormulaCellGroupRef & GetCellGroup() const
SCROW GetSharedTopRow() const
void SetNeedsListening(bool bVar)
bool IsSharedTop() const
void EndListeningTo(ScDocument &rDoc, ScTokenArray *pArr=nullptr, ScAddress aPos=ScAddress())
void StartListeningTo(ScDocument &rDoc)
ScFormulaCellGroupRef CreateCellGroup(SCROW nLen, bool bInvariant)
Turn a non-grouped cell into the top of a grouped cell.
ScDocument & GetDocument() const
CompareState CompareByTokenArray(const ScFormulaCell &rOther) const
ScTokenArray * GetCode()
ScAddress aPos
ScAddress aEnd
Definition: address.hxx:498
bool IsRecalcModeAlways() const
sal_uInt16 GetCodeLen() const
FormulaToken ** GetCode() const
static void startListeningAsGroup(StartListeningContext &rCxt, ScFormulaCell **ppSharedTop)
Have all formula cells belonging to a group start listening to their references.
static void unshareFormulaCells(const ScDocument &rDoc, CellStoreType &rCells, std::vector< SCROW > &rRows)
Make specified formula cells non-shared ones, and split them off from their respective adjacent formu...
static bool joinFormulaCells(const CellStoreType::position_type &rPos, ScFormulaCell &rCell1, ScFormulaCell &rCell2)
See if two specified adjacent formula cells can be merged, and if they can, merge them into the same ...
static void unshareFormulaCell(const CellStoreType::position_type &aPos, ScFormulaCell &rCell)
Turn a shared formula cell into a non-shared one, and split it off from the adjacent formula cell gro...
static bool joinFormulaCellAbove(const CellStoreType::position_type &aPos)
Merge with an existing formula group (if any) located immediately above if the cell at specified posi...
static bool splitFormulaCellGroup(const CellStoreType::position_type &aPos, sc::EndListeningContext *pCxt)
Split existing shared formula range at specified position.
static bool splitFormulaCellGroups(const ScDocument &rDoc, CellStoreType &rCells, std::vector< SCROW > &rBounds)
Split existing shared formula ranges at specified row positions.
static const ScFormulaCell * getSharedTopFormulaCell(const CellStoreType::position_type &aPos)
Get shared formula top cell from position, if any, else nullptr.
void * p
sal_Int64 n
int i
CAUTION! The following defines must be in the same namespace as the respective type.
const mdds::mtv::element_t element_type_formula
Definition: mtvelements.hxx:50
mdds::mtv::soa::multi_type_vector< CellFunc, CellStoreTrait > CellStoreType
Single reference (one address) into the sheet.
Definition: refdata.hxx:30
bool IsRowRel() const
Definition: refdata.hxx:67
ScAddress toAbs(const ScSheetLimits &rLimits, const ScAddress &rPos) const
Definition: refdata.cxx:193
::boost::intrusive_ptr< ScFormulaCellGroup > ScFormulaCellGroupRef
Definition: types.hxx:43
sal_Int32 SCROW
Definition: types.hxx:17