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 
18 namespace sc {
19 
20 const 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 
36 bool 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;
62  ScFormulaCellGroupRef xGroup2;
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->Clone();
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 #if DEBUG_COLUMN_STORAGE
105  if (xGroup2->mpTopCell->aPos.Row() + size_t(xGroup2->mnLength) > aPos.first->position + aPos.first->size)
106  {
107  cerr << "ScColumn::SplitFormulaCellGroup: Shared formula region goes beyond the formula block. Not good." << endl;
108  cerr.flush();
109  abort();
110  }
111 #endif
112  sc::formula_block::iterator itEnd = it;
113  std::advance(itEnd, nLength2);
114  for (; it != itEnd; ++it)
115  {
116  ScFormulaCell& rCell = **it;
117  rCell.SetCellGroup(xGroup2);
118  }
119 
120  return true;
121 }
122 
123 bool SharedFormulaUtil::splitFormulaCellGroups(const ScDocument* pDoc, CellStoreType& rCells, std::vector<SCROW>& rBounds)
124 {
125  if (rBounds.empty())
126  return false;
127 
128  // Sort and remove duplicates.
129  std::sort(rBounds.begin(), rBounds.end());
130  std::vector<SCROW>::iterator it = std::unique(rBounds.begin(), rBounds.end());
131  rBounds.erase(it, rBounds.end());
132 
133  it = rBounds.begin();
134  SCROW nRow = *it;
135  CellStoreType::position_type aPos = rCells.position(nRow);
136  if (aPos.first == rCells.end())
137  return false;
138 
139  bool bSplit = splitFormulaCellGroup(aPos, nullptr);
140  std::vector<SCROW>::iterator itEnd = rBounds.end();
141  for (++it; it != itEnd; ++it)
142  {
143  nRow = *it;
144  if (pDoc->ValidRow(nRow))
145  {
146  aPos = rCells.position(aPos.first, nRow);
147  if (aPos.first == rCells.end())
148  return bSplit;
149  bSplit |= splitFormulaCellGroup(aPos, nullptr);
150  }
151  }
152  return bSplit;
153 }
154 
156  const CellStoreType::position_type& rPos, ScFormulaCell& rCell1, ScFormulaCell& rCell2 )
157 {
158  if( rCell1.GetDocument()->IsDelayedFormulaGrouping())
159  {
160  rCell1.GetDocument()->AddDelayedFormulaGroupingCell( &rCell1 );
161  rCell1.GetDocument()->AddDelayedFormulaGroupingCell( &rCell2 );
162  return false;
163  }
164 
165  ScFormulaCell::CompareState eState = rCell1.CompareByTokenArray(rCell2);
166  if (eState == ScFormulaCell::NotEqual)
167  return false;
168 
169  // Formula tokens equal those of the previous formula cell.
170  ScFormulaCellGroupRef xGroup1 = rCell1.GetCellGroup();
171  ScFormulaCellGroupRef xGroup2 = rCell2.GetCellGroup();
172  if (xGroup1)
173  {
174  if (xGroup2)
175  {
176  // Both cell 1 and cell 2 are shared. Merge them together.
177  if (xGroup1.get() == xGroup2.get())
178  // They belong to the same group.
179  return false;
180 
181  // Set the group object from cell 1 to all cells in group 2.
182  xGroup1->mnLength += xGroup2->mnLength;
183  size_t nOffset = rPos.second + 1; // position of cell 2
184  for (size_t i = 0, n = xGroup2->mnLength; i < n; ++i)
185  {
186  ScFormulaCell& rCell = *sc::formula_block::at(*rPos.first->data, nOffset+i);
187  rCell.SetCellGroup(xGroup1);
188  }
189  }
190  else
191  {
192  // cell 1 is shared but cell 2 is not.
193  rCell2.SetCellGroup(xGroup1);
194  ++xGroup1->mnLength;
195  }
196  }
197  else
198  {
199  if (xGroup2)
200  {
201  // cell 1 is not shared, but cell 2 is already shared.
202  rCell1.SetCellGroup(xGroup2);
203  xGroup2->mpTopCell = &rCell1;
204  ++xGroup2->mnLength;
205  }
206  else
207  {
208  // neither cells are shared.
209  assert(rCell1.aPos.Row() == static_cast<SCROW>(rPos.first->position + rPos.second));
210  xGroup1 = rCell1.CreateCellGroup(2, eState == ScFormulaCell::EqualInvariant);
211  rCell2.SetCellGroup(xGroup1);
212  }
213  }
214 
215  return true;
216 }
217 
218 bool SharedFormulaUtil::joinFormulaCellAbove( const CellStoreType::position_type& aPos )
219 {
220  if (aPos.first->type != sc::element_type_formula)
221  // This is not a formula cell.
222  return false;
223 
224  if (aPos.second == 0)
225  // This cell is already the top cell in a formula block; the previous
226  // cell is not a formula cell.
227  return false;
228 
229  ScFormulaCell& rPrev = *sc::formula_block::at(*aPos.first->data, aPos.second-1);
230  ScFormulaCell& rCell = *sc::formula_block::at(*aPos.first->data, aPos.second);
231  sc::CellStoreType::position_type aPosPrev = aPos;
232  --aPosPrev.second;
233  return joinFormulaCells(aPosPrev, rPrev, rCell);
234 }
235 
236 void SharedFormulaUtil::unshareFormulaCell(const CellStoreType::position_type& aPos, ScFormulaCell& rCell)
237 {
238  if (!rCell.IsShared())
239  return;
240 
241  ScFormulaCellGroupRef xNone;
242  sc::CellStoreType::iterator it = aPos.first;
243 
244  // This formula cell is shared. Adjust the shared group.
245  if (rCell.aPos.Row() == rCell.GetSharedTopRow())
246  {
247  // Top of the shared range.
248  const ScFormulaCellGroupRef& xGroup = rCell.GetCellGroup();
249  if (xGroup->mnLength == 2)
250  {
251  // Group consists of only two cells. Mark the second one non-shared.
252 #if DEBUG_COLUMN_STORAGE
253  if (aPos.second+1 >= aPos.first->size)
254  {
255  cerr << "ScColumn::UnshareFormulaCell: There is no next formula cell but there should be!" << endl;
256  cerr.flush();
257  abort();
258  }
259 #endif
260  ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
261  rNext.SetCellGroup(xNone);
262  }
263  else
264  {
265  // Move the top cell to the next formula cell down.
266  ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
267  xGroup->mpTopCell = &rNext;
268  }
269  --xGroup->mnLength;
270  }
271  else if (rCell.aPos.Row() == rCell.GetSharedTopRow() + rCell.GetSharedLength() - 1)
272  {
273  // Bottom of the shared range.
274  const ScFormulaCellGroupRef& xGroup = rCell.GetCellGroup();
275  if (xGroup->mnLength == 2)
276  {
277  // Mark the top cell non-shared.
278 #if DEBUG_COLUMN_STORAGE
279  if (aPos.second == 0)
280  {
281  cerr << "ScColumn::UnshareFormulaCell: There is no previous formula cell but there should be!" << endl;
282  cerr.flush();
283  abort();
284  }
285 #endif
286  ScFormulaCell& rPrev = *sc::formula_block::at(*it->data, aPos.second-1);
287  rPrev.SetCellGroup(xNone);
288  }
289  else
290  {
291  // Just shorten the shared range length by one.
292  --xGroup->mnLength;
293  }
294  }
295  else
296  {
297  // In the middle of the shared range. Split it into two groups.
298  ScFormulaCellGroupRef xGroup = rCell.GetCellGroup();
299  SCROW nEndRow = xGroup->mpTopCell->aPos.Row() + xGroup->mnLength - 1;
300  xGroup->mnLength = rCell.aPos.Row() - xGroup->mpTopCell->aPos.Row(); // Shorten the top group.
301  if (xGroup->mnLength == 1)
302  {
303  // Make the top cell non-shared.
304 #if DEBUG_COLUMN_STORAGE
305  if (aPos.second == 0)
306  {
307  cerr << "ScColumn::UnshareFormulaCell: There is no previous formula cell but there should be!" << endl;
308  cerr.flush();
309  abort();
310  }
311 #endif
312  ScFormulaCell& rPrev = *sc::formula_block::at(*it->data, aPos.second-1);
313  rPrev.SetCellGroup(xNone);
314  }
315 
316  SCROW nLength2 = nEndRow - rCell.aPos.Row();
317  if (nLength2 >= 2)
318  {
319  ScFormulaCellGroupRef xGroup2;
320  xGroup2.reset(new ScFormulaCellGroup);
321  ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
322  xGroup2->mpTopCell = &rNext;
323  xGroup2->mnLength = nLength2;
324  xGroup2->mbInvariant = xGroup->mbInvariant;
325  xGroup2->mpCode = xGroup->mpCode->Clone();
326 #if DEBUG_COLUMN_STORAGE
327  if (xGroup2->mpTopCell->aPos.Row() + size_t(xGroup2->mnLength) > it->position + it->size)
328  {
329  cerr << "ScColumn::UnshareFormulaCell: Shared formula region goes beyond the formula block. Not good." << endl;
330  cerr.flush();
331  abort();
332  }
333 #endif
334  sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
335  std::advance(itCell, aPos.second+1);
336  sc::formula_block::iterator itCellEnd = itCell;
337  std::advance(itCellEnd, xGroup2->mnLength);
338  for (; itCell != itCellEnd; ++itCell)
339  {
340  ScFormulaCell& rCell2 = **itCell;
341  rCell2.SetCellGroup(xGroup2);
342  }
343  }
344  else
345  {
346  // Make the next cell non-shared.
347  sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
348  std::advance(itCell, aPos.second+1);
349  ScFormulaCell& rCell2 = **itCell;
350  rCell2.SetCellGroup(xNone);
351  }
352  }
353 
354  rCell.SetCellGroup(xNone);
355 }
356 
357 void SharedFormulaUtil::unshareFormulaCells(const ScDocument* pDoc, CellStoreType& rCells, std::vector<SCROW>& rRows)
358 {
359  if (rRows.empty())
360  return;
361 
362  // Sort and remove duplicates.
363  std::sort(rRows.begin(), rRows.end());
364  rRows.erase(std::unique(rRows.begin(), rRows.end()), rRows.end());
365 
366  // Add next cell positions to the list (to ensure that each position becomes a single cell).
367  std::vector<SCROW> aRows2;
368  for (const auto& rRow : rRows)
369  {
370  if (rRow > pDoc->MaxRow())
371  break;
372 
373  aRows2.push_back(rRow);
374 
375  if (rRow < pDoc->MaxRow())
376  aRows2.push_back(rRow+1);
377  }
378 
379  // Remove duplicates again (the vector should still be sorted).
380  aRows2.erase(std::unique(aRows2.begin(), aRows2.end()), aRows2.end());
381 
382  splitFormulaCellGroups(pDoc, rCells, aRows2);
383 }
384 
386 {
387  ScFormulaCell& rTopCell = **ppSharedTop;
388  assert(rTopCell.IsSharedTop());
389 
390 #if USE_FORMULA_GROUP_LISTENER
391  ScDocument& rDoc = rCxt.getDoc();
392  rDoc.SetDetectiveDirty(true);
393 
394  ScFormulaCellGroupRef xGroup = rTopCell.GetCellGroup();
395  const ScTokenArray* pCode = xGroup->mpCode.get();
396  assert(pCode == rTopCell.GetCode());
397  if (pCode->IsRecalcModeAlways())
398  {
399  rDoc.StartListeningArea(
400  BCA_LISTEN_ALWAYS, false,
401  xGroup->getAreaListener(ppSharedTop, BCA_LISTEN_ALWAYS, true, true));
402  }
403 
404  formula::FormulaToken** p = pCode->GetCode();
405  formula::FormulaToken** pEnd = p + pCode->GetCodeLen();
406  for (; p != pEnd; ++p)
407  {
408  const formula::FormulaToken* t = *p;
409  switch (t->GetType())
410  {
412  {
413  const ScSingleRefData* pRef = t->GetSingleRef();
414  ScAddress aPos = pRef->toAbs(&rDoc, rTopCell.aPos);
415  ScFormulaCell** pp = ppSharedTop;
416  ScFormulaCell** ppEnd = ppSharedTop + xGroup->mnLength;
417  for (; pp != ppEnd; ++pp)
418  {
419  if (!aPos.IsValid())
420  break;
421 
422  rDoc.StartListeningCell(rCxt, aPos, **pp);
423  if (pRef->IsRowRel())
424  aPos.IncRow();
425  }
426  }
427  break;
429  {
430  const ScSingleRefData& rRef1 = *t->GetSingleRef();
431  const ScSingleRefData& rRef2 = *t->GetSingleRef2();
432  ScAddress aPos1 = rRef1.toAbs(&rDoc, rTopCell.aPos);
433  ScAddress aPos2 = rRef2.toAbs(&rDoc, rTopCell.aPos);
434 
435  ScRange aOrigRange(aPos1, aPos2);
436  ScRange aListenedRange = aOrigRange;
437  if (rRef2.IsRowRel())
438  aListenedRange.aEnd.IncRow(xGroup->mnLength-1);
439 
440  if (aPos1.IsValid() && aPos2.IsValid())
441  {
442  rDoc.StartListeningArea(
443  aListenedRange, true,
444  xGroup->getAreaListener(ppSharedTop, aOrigRange, !rRef1.IsRowRel(), !rRef2.IsRowRel()));
445  }
446  }
447  break;
448  default:
449  ;
450  }
451  }
452 
453  ScFormulaCell** pp = ppSharedTop;
454  ScFormulaCell** ppEnd = ppSharedTop + xGroup->mnLength;
455  for (; pp != ppEnd; ++pp)
456  {
457  ScFormulaCell& rCell = **pp;
458  rCell.SetNeedsListening(false);
459  }
460 
461 #else
462  ScFormulaCell** pp = ppSharedTop;
463  ScFormulaCell** ppEnd = ppSharedTop + rTopCell.GetSharedLength();
464  for (; pp != ppEnd; ++pp)
465  {
466  ScFormulaCell& rFC = **pp;
467  rFC.StartListeningTo(rCxt);
468  }
469 #endif
470 }
471 
472 }
473 
474 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void AddDelayedFormulaGroupingCell(const ScFormulaCell *cell)
To be used only by SharedFormulaUtil::joinFormulaCells().
Definition: document10.cxx:392
::boost::intrusive_ptr< ScFormulaCellGroup > ScFormulaCellGroupRef
Definition: types.hxx:44
#define BCA_LISTEN_ALWAYS
Definition: address.hxx:952
SCROW Row() const
Definition: address.hxx:262
Single reference (one address) into the sheet.
Definition: refdata.hxx:30
bool IsRecalcModeAlways() const
ScDocument * GetDocument() const
SCROW GetSharedTopRow() const
ScFormulaCell * Clone() const
ScTokenArray * GetCode()
sal_Int64 n
sal_uInt16 GetCodeLen() const
ScAddress aEnd
Definition: address.hxx:501
static void startListeningAsGroup(StartListeningContext &rCxt, ScFormulaCell **ppSharedTop)
Have all formula cells belonging to a group start listening to their references.
static const ScFormulaCell * getSharedTopFormulaCell(const CellStoreType::position_type &aPos)
Get shared formula top cell from position, if any, else nullptr.
mdds::multi_type_vector< CellFunc, CellStoreEvent > CellStoreType
const ScFormulaCellGroupRef & GetCellGroup() const
const mdds::mtv::element_t element_type_formula
Definition: mtvelements.hxx:50
SC_DLLPUBLIC SCROW MaxRow() const
Definition: document.hxx:873
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
virtual const ScSingleRefData * GetSingleRef() const
static bool splitFormulaCellGroup(const CellStoreType::position_type &aPos, sc::EndListeningContext *pCxt)
Split existing shared formula range at specified position.
static bool splitFormulaCellGroups(const ScDocument *pDoc, CellStoreType &rCells, std::vector< SCROW > &rBounds)
Split existing shared formula ranges at specified row positions.
ScAddress aPos
void SetNeedsListening(bool bVar)
bool IsValid() const
Definition: address.hxx:293
SCROW GetSharedLength() const
bool IsShared() const
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...
int i
void SetCellGroup(const ScFormulaCellGroupRef &xRef)
void IncRow(SCROW nDelta=1)
Definition: address.hxx:300
bool IsSharedTop() const
bool IsRowRel() const
Definition: refdata.hxx:68
XPropertyListType t
bool IsDelayedFormulaGrouping() const
Definition: document.hxx:1372
ScAddress toAbs(ScSheetLimits &rLimits, const ScAddress &rPos) const
Definition: refdata.cxx:193
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...
sal_Int32 SCROW
Definition: types.hxx:18
void EndListeningTo(ScDocument *pDoc, ScTokenArray *pArr=nullptr, ScAddress aPos=ScAddress())
void StartListeningTo(ScDocument *pDoc)
bool ValidRow(SCROW nRow) const
Definition: document.hxx:876
static void unshareFormulaCells(const ScDocument *pDoc, CellStoreType &rCells, std::vector< SCROW > &rRows)
Make specified formula cells non-shared ones, and split them off from their respective adjacent formu...
virtual const ScSingleRefData * GetSingleRef2() const
SvStream & endl(SvStream &rStr)
FormulaToken ** GetCode() const
void SetDetectiveDirty(bool bSet)
Definition: document.hxx:2121
void * p
CompareState CompareByTokenArray(const ScFormulaCell &rOther) const
ScFormulaCellGroupRef CreateCellGroup(SCROW nLen, bool bInvariant)
Turn a non-grouped cell into the top of a grouped cell.
StackVar GetType() const
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 ...