1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
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>
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.
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.
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.
45 // Split position coincides with the block border. Nothing to do.
48 sc::formula_block::iterator it
= sc::formula_block::begin(*aPos
.first
->data
);
49 std::advance(it
, aPos
.second
);
50 ScFormulaCell
& rTop
= **it
;
52 // Not a shared formula.
55 if (nRow
== rTop
.GetSharedTopRow())
56 // Already the top cell of a shared group.
59 ScFormulaCellGroupRef xGroup
= rTop
.GetCellGroup();
61 SCROW nLength2
= xGroup
->mpTopCell
->aPos
.Row() + xGroup
->mnLength
- nRow
;
62 ScFormulaCellGroupRef xGroup2
;
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.
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.
86 rPrevTop
.EndListeningTo(*pCxt
);
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);
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
);
117 bool SharedFormulaUtil::splitFormulaCellGroups(const ScDocument
& rDoc
, CellStoreType
& rCells
, std::vector
<SCROW
>& rBounds
)
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();
129 CellStoreType::position_type aPos
= rCells
.position(nRow
);
130 if (aPos
.first
== rCells
.end())
133 bool bSplit
= splitFormulaCellGroup(aPos
, nullptr);
134 std::vector
<SCROW
>::iterator itEnd
= rBounds
.end();
135 for (++it
; it
!= itEnd
; ++it
)
138 if (rDoc
.ValidRow(nRow
))
140 aPos
= rCells
.position(aPos
.first
, nRow
);
141 if (aPos
.first
== rCells
.end())
143 bSplit
|= splitFormulaCellGroup(aPos
, nullptr);
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
);
159 ScFormulaCell::CompareState eState
= rCell1
.CompareByTokenArray(rCell2
);
160 if (eState
== ScFormulaCell::NotEqual
)
163 // Formula tokens equal those of the previous formula cell.
164 ScFormulaCellGroupRef xGroup1
= rCell1
.GetCellGroup();
165 ScFormulaCellGroupRef xGroup2
= rCell2
.GetCellGroup();
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.
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
);
186 // cell 1 is shared but cell 2 is not.
187 rCell2
.SetCellGroup(xGroup1
);
195 // cell 1 is not shared, but cell 2 is already shared.
196 rCell1
.SetCellGroup(xGroup2
);
197 xGroup2
->mpTopCell
= &rCell1
;
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
);
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.
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.
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
;
227 return joinFormulaCells(aPosPrev
, rPrev
, rCell
);
230 void SharedFormulaUtil::unshareFormulaCell(const CellStoreType::position_type
& aPos
, ScFormulaCell
& rCell
)
232 if (!rCell
.IsShared())
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
);
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
;
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
);
272 // Just shorten the shared range length by one.
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();
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
);
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
)
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())
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
)
390 rDoc
.StartListeningCell(rCxt
, aPos
, **pp
);
391 if (pRef
->IsRowRel())
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()));
421 ScFormulaCell
** pp
= ppSharedTop
;
422 ScFormulaCell
** ppEnd
= ppSharedTop
+ xGroup
->mnLength
;
423 for (; pp
!= ppEnd
; ++pp
)
425 ScFormulaCell
& rCell
= **pp
;
426 rCell
.SetNeedsListening(false);
430 ScFormulaCell
** pp
= ppSharedTop
;
431 ScFormulaCell
** ppEnd
= ppSharedTop
+ rTopCell
.GetSharedLength();
432 for (; pp
!= ppEnd
; ++pp
)
434 ScFormulaCell
& rFC
= **pp
;
435 rFC
.StartListeningTo(rCxt
);
442 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */