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>
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.
28 // Split position coincides with the block border. Nothing to do.
31 sc::formula_block::iterator it
= sc::formula_block::begin(*aPos
.first
->data
);
32 std::advance(it
, aPos
.second
);
33 ScFormulaCell
& rTop
= **it
;
35 // Not a shared formula.
38 if (nRow
== rTop
.GetSharedTopRow())
39 // Already the top cell of a shared group.
42 ScFormulaCellGroupRef xGroup
= rTop
.GetCellGroup();
44 SCROW nLength2
= xGroup
->mpTopCell
->aPos
.Row() + xGroup
->mnLength
- nRow
;
45 ScFormulaCellGroupRef xGroup2
;
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.
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.
69 rPrevTop
.EndListeningTo(*pCxt
);
71 rPrevTop
.EndListeningTo( rPrevTop
.GetDocument(), NULL
, ScAddress( ScAddress::UNINITIALIZED
));
72 rPrevTop
.SetNeedsListening(true);
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
;
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
)
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();
113 CellStoreType::position_type aPos
= rCells
.position(nRow
);
114 if (aPos
.first
== rCells
.end())
117 splitFormulaCellGroup(aPos
, nullptr);
118 std::vector
<SCROW
>::iterator itEnd
= rBounds
.end();
119 for (++it
; it
!= itEnd
; ++it
)
122 aPos
= rCells
.position(aPos
.first
, nRow
);
123 if (aPos
.first
== rCells
.end())
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
)
137 // Formula tokens equal those of the previous formula cell.
138 ScFormulaCellGroupRef xGroup1
= rCell1
.GetCellGroup();
139 ScFormulaCellGroupRef xGroup2
= rCell2
.GetCellGroup();
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.
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
);
160 // cell 1 is shared but cell 2 is not.
161 rCell2
.SetCellGroup(xGroup1
);
169 // cell 1 is not shared, but cell 2 is already shared.
170 rCell1
.SetCellGroup(xGroup2
);
171 xGroup2
->mpTopCell
= &rCell1
;
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
);
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.
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.
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
;
201 return joinFormulaCells(aPosPrev
, rPrev
, rCell
);
204 void SharedFormulaUtil::unshareFormulaCell(const CellStoreType::position_type
& aPos
, ScFormulaCell
& rCell
)
206 if (!rCell
.IsShared())
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
;
228 ScFormulaCell
& rNext
= *sc::formula_block::at(*it
->data
, aPos
.second
+1);
229 rNext
.SetCellGroup(xNone
);
233 // Move the top cell to the next formula cell down.
234 ScFormulaCell
& rNext
= *sc::formula_block::at(*it
->data
, aPos
.second
+1);
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
;
254 ScFormulaCell
& rPrev
= *sc::formula_block::at(*it
->data
, aPos
.second
-1);
255 rPrev
.SetCellGroup(xNone
);
259 // Just shortern the shared range length by one.
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
;
280 ScFormulaCell
& rPrev
= *sc::formula_block::at(*it
->data
, aPos
.second
-1);
281 rPrev
.SetCellGroup(xNone
);
284 SCROW nLength2
= nEndRow
- rCell
.aPos
.Row();
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
;
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(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 std::vector
<SCROW
>::const_iterator it
= rRows
.begin(), itEnd
= rRows
.end();
337 for (; it
!= itEnd
; ++it
)
342 aRows2
.push_back(*it
);
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
)
391 rDoc
.StartListeningCell(rCxt
, aPos
, **pp
);
392 if (pRef
->IsRowRel())
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()));
422 ScFormulaCell
** pp
= ppSharedTop
;
423 ScFormulaCell
** ppEnd
= ppSharedTop
+ xGroup
->mnLength
;
424 for (; pp
!= ppEnd
; ++pp
)
426 ScFormulaCell
& rCell
= **pp
;
427 rCell
.SetNeedsListening(false);
431 ScFormulaCell
** pp
= ppSharedTop
;
432 ScFormulaCell
** ppEnd
= ppSharedTop
+ rTopCell
.GetSharedLength();
433 for (; pp
!= ppEnd
; ++pp
)
435 ScFormulaCell
& rFC
= **pp
;
436 rFC
.StartListeningTo(rCxt
);
443 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */