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 :
14 : namespace sc {
15 :
16 111392 : void SharedFormulaUtil::splitFormulaCellGroup(const CellStoreType::position_type& aPos)
17 : {
18 111392 : SCROW nRow = aPos.first->position + aPos.second;
19 :
20 111392 : if (aPos.first->type != sc::element_type_formula)
21 : // Not a formula cell block.
22 222410 : return;
23 :
24 284 : if (aPos.second == 0)
25 : // Split position coincides with the block border. Nothing to do.
26 158 : return;
27 :
28 126 : sc::formula_block::iterator it = sc::formula_block::begin(*aPos.first->data);
29 126 : std::advance(it, aPos.second);
30 126 : ScFormulaCell& rTop = **it;
31 126 : if (!rTop.IsShared())
32 : // Not a shared formula.
33 26 : return;
34 :
35 100 : if (nRow == rTop.GetSharedTopRow())
36 : // Already the top cell of a shared group.
37 10 : return;
38 :
39 90 : ScFormulaCellGroupRef xGroup = rTop.GetCellGroup();
40 :
41 90 : SCROW nLength2 = xGroup->mpTopCell->aPos.Row() + xGroup->mnLength - nRow;
42 180 : ScFormulaCellGroupRef xGroup2;
43 90 : if (nLength2 > 1)
44 : {
45 72 : xGroup2.reset(new ScFormulaCellGroup);
46 72 : xGroup2->mbInvariant = xGroup->mbInvariant;
47 72 : xGroup2->mpTopCell = &rTop;
48 72 : xGroup2->mnLength = nLength2;
49 72 : xGroup2->mpCode = xGroup->mpCode->Clone();
50 : }
51 :
52 90 : xGroup->mnLength = nRow - xGroup->mpTopCell->aPos.Row();
53 90 : if (xGroup->mnLength == 1)
54 : {
55 : // The top group consists of only one cell. Ungroup this.
56 66 : ScFormulaCellGroupRef xNone;
57 66 : ScFormulaCell& rPrev = *sc::formula_block::at(*aPos.first->data, aPos.second-1);
58 66 : rPrev.SetCellGroup(xNone);
59 : }
60 :
61 : // Apply the lower group object to the lower cells.
62 : #if DEBUG_COLUMN_STORAGE
63 : if (xGroup2->mpTopCell->aPos.Row() + xGroup2->mnLength > aPos.first->position + aPos.first->size)
64 : {
65 : cerr << "ScColumn::SplitFormulaCellGroup: Shared formula region goes beyond the formula block. Not good." << endl;
66 : cerr.flush();
67 : abort();
68 : }
69 : #endif
70 90 : sc::formula_block::iterator itEnd = it;
71 90 : std::advance(itEnd, nLength2);
72 384 : for (; it != itEnd; ++it)
73 : {
74 294 : ScFormulaCell& rCell = **it;
75 294 : rCell.SetCellGroup(xGroup2);
76 90 : }
77 : }
78 :
79 3178 : void SharedFormulaUtil::splitFormulaCellGroups(CellStoreType& rCells, std::vector<SCROW>& rBounds)
80 : {
81 3178 : if (rBounds.empty())
82 732 : return;
83 :
84 : // Sort and remove duplicates.
85 2812 : std::sort(rBounds.begin(), rBounds.end());
86 2812 : std::vector<SCROW>::iterator it = std::unique(rBounds.begin(), rBounds.end());
87 2812 : rBounds.erase(it, rBounds.end());
88 :
89 2812 : it = rBounds.begin();
90 2812 : SCROW nRow = *it;
91 2812 : CellStoreType::position_type aPos = rCells.position(nRow);
92 2812 : if (aPos.first == rCells.end())
93 0 : return;
94 :
95 2812 : splitFormulaCellGroup(aPos);
96 2812 : std::vector<SCROW>::iterator itEnd = rBounds.end();
97 5364 : for (++it; it != itEnd; ++it)
98 : {
99 2552 : nRow = *it;
100 2552 : aPos = rCells.position(aPos.first, nRow);
101 2552 : if (aPos.first == rCells.end())
102 0 : return;
103 :
104 2552 : splitFormulaCellGroup(aPos);
105 : }
106 : }
107 :
108 1982 : void SharedFormulaUtil::joinFormulaCells(const CellStoreType::position_type& rPos, ScFormulaCell& rCell1, ScFormulaCell& rCell2)
109 : {
110 1982 : ScFormulaCell::CompareState eState = rCell1.CompareByTokenArray(rCell2);
111 1982 : if (eState == ScFormulaCell::NotEqual)
112 1388 : return;
113 :
114 : // Formula tokens equal those of the previous formula cell.
115 1292 : ScFormulaCellGroupRef xGroup1 = rCell1.GetCellGroup();
116 2576 : ScFormulaCellGroupRef xGroup2 = rCell2.GetCellGroup();
117 1292 : if (xGroup1)
118 : {
119 1026 : if (xGroup2)
120 : {
121 : // Both cell 1 and cell 2 are shared. Merge them together.
122 44 : if (xGroup1.get() == xGroup2.get())
123 : // They belong to the same group.
124 8 : return;
125 :
126 : // Set the group object from cell 1 to all cells in group 2.
127 36 : xGroup1->mnLength += xGroup2->mnLength;
128 36 : size_t nOffset = rPos.second + 1; // position of cell 2
129 174 : for (size_t i = 0, n = xGroup2->mnLength; i < n; ++i)
130 : {
131 138 : ScFormulaCell& rCell = *sc::formula_block::at(*rPos.first->data, nOffset+i);
132 138 : rCell.SetCellGroup(xGroup1);
133 : }
134 : }
135 : else
136 : {
137 : // cell 1 is shared but cell 2 is not.
138 982 : rCell2.SetCellGroup(xGroup1);
139 982 : ++xGroup1->mnLength;
140 : }
141 : }
142 : else
143 : {
144 266 : if (xGroup2)
145 : {
146 : // cell 1 is not shared, but cell 2 is already shared.
147 8 : rCell1.SetCellGroup(xGroup2);
148 8 : xGroup2->mpTopCell = &rCell1;
149 8 : ++xGroup2->mnLength;
150 : }
151 : else
152 : {
153 : // neither cells are shared.
154 : assert(rCell1.aPos.Row() == (SCROW)(rPos.first->position + rPos.second));
155 258 : xGroup1 = rCell1.CreateCellGroup(2, eState == ScFormulaCell::EqualInvariant);
156 258 : rCell2.SetCellGroup(xGroup1);
157 : }
158 1284 : }
159 : }
160 :
161 88576 : void SharedFormulaUtil::joinFormulaCellAbove(const CellStoreType::position_type& aPos)
162 : {
163 88576 : if (aPos.first->type != sc::element_type_formula)
164 : // This is not a formula cell.
165 176960 : return;
166 :
167 178 : if (aPos.second == 0)
168 : // This cell is already the top cell in a formula block; the previous
169 : // cell is not a formula cell.
170 164 : return;
171 :
172 14 : ScFormulaCell& rPrev = *sc::formula_block::at(*aPos.first->data, aPos.second-1);
173 14 : ScFormulaCell& rCell = *sc::formula_block::at(*aPos.first->data, aPos.second);
174 14 : sc::CellStoreType::position_type aPosPrev = aPos;
175 14 : --aPosPrev.second;
176 14 : joinFormulaCells(aPosPrev, rPrev, rCell);
177 : }
178 :
179 640 : void SharedFormulaUtil::unshareFormulaCell(const CellStoreType::position_type& aPos, ScFormulaCell& rCell)
180 : {
181 640 : if (!rCell.IsShared())
182 1088 : return;
183 :
184 192 : ScFormulaCellGroupRef xNone;
185 192 : sc::CellStoreType::iterator it = aPos.first;
186 :
187 : // This formula cell is shared. Adjust the shared group.
188 192 : if (rCell.aPos.Row() == rCell.GetSharedTopRow())
189 : {
190 : // Top of the shared range.
191 98 : ScFormulaCellGroupRef xGroup = rCell.GetCellGroup();
192 98 : if (xGroup->mnLength == 2)
193 : {
194 : // Group consists only only two cells. Mark the second one non-shared.
195 : #if DEBUG_COLUMN_STORAGE
196 : if (aPos.second+1 >= aPos.first->size)
197 : {
198 : cerr << "ScColumn::UnshareFormulaCell: There is no next formula cell but there should be!" << endl;
199 : cerr.flush();
200 : abort();
201 : }
202 : #endif
203 26 : ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
204 26 : rNext.SetCellGroup(xNone);
205 : }
206 : else
207 : {
208 : // Move the top cell to the next formula cell down.
209 72 : ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
210 72 : --xGroup->mnLength;
211 72 : xGroup->mpTopCell = &rNext;
212 98 : }
213 : }
214 94 : else if (rCell.aPos.Row() == rCell.GetSharedTopRow() + rCell.GetSharedLength() - 1)
215 : {
216 : // Bottom of the shared range.
217 46 : ScFormulaCellGroupRef xGroup = rCell.GetCellGroup();
218 46 : if (xGroup->mnLength == 2)
219 : {
220 : // Mark the top cell non-shared.
221 : #if DEBUG_COLUMN_STORAGE
222 : if (aPos.second == 0)
223 : {
224 : cerr << "ScColumn::UnshareFormulaCell: There is no previous formula cell but there should be!" << endl;
225 : cerr.flush();
226 : abort();
227 : }
228 : #endif
229 16 : ScFormulaCell& rPrev = *sc::formula_block::at(*it->data, aPos.second-1);
230 16 : rPrev.SetCellGroup(xNone);
231 : }
232 : else
233 : {
234 : // Just shortern the shared range length by one.
235 30 : --xGroup->mnLength;
236 46 : }
237 : }
238 : else
239 : {
240 : // In the middle of the shared range. Split it into two groups.
241 48 : ScFormulaCellGroupRef xGroup = rCell.GetCellGroup();
242 48 : SCROW nEndRow = xGroup->mpTopCell->aPos.Row() + xGroup->mnLength - 1;
243 48 : xGroup->mnLength = rCell.aPos.Row() - xGroup->mpTopCell->aPos.Row(); // Shorten the top group.
244 48 : if (xGroup->mnLength == 1)
245 : {
246 : // Make the top cell non-shared.
247 : #if DEBUG_COLUMN_STORAGE
248 : if (aPos.second == 0)
249 : {
250 : cerr << "ScColumn::UnshareFormulaCell: There is no previous formula cell but there should be!" << endl;
251 : cerr.flush();
252 : abort();
253 : }
254 : #endif
255 10 : ScFormulaCell& rPrev = *sc::formula_block::at(*it->data, aPos.second-1);
256 10 : rPrev.SetCellGroup(xNone);
257 : }
258 :
259 48 : SCROW nLength2 = nEndRow - rCell.aPos.Row();
260 48 : if (nLength2 >= 2)
261 : {
262 38 : ScFormulaCellGroupRef xGroup2;
263 38 : xGroup2.reset(new ScFormulaCellGroup);
264 38 : ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
265 38 : xGroup2->mpTopCell = &rNext;
266 38 : xGroup2->mnLength = nLength2;
267 38 : xGroup2->mbInvariant = xGroup->mbInvariant;
268 38 : xGroup2->mpCode = xGroup->mpCode->Clone();
269 : #if DEBUG_COLUMN_STORAGE
270 : if (xGroup2->mpTopCell->aPos.Row() + xGroup2->mnLength > it->position + it->size)
271 : {
272 : cerr << "ScColumn::UnshareFormulaCell: Shared formula region goes beyond the formula block. Not good." << endl;
273 : cerr.flush();
274 : abort();
275 : }
276 : #endif
277 38 : sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
278 38 : std::advance(itCell, aPos.second+1);
279 38 : sc::formula_block::iterator itCellEnd = itCell;
280 38 : std::advance(itCellEnd, xGroup2->mnLength);
281 230 : for (; itCell != itCellEnd; ++itCell)
282 : {
283 192 : ScFormulaCell& rCell2 = **itCell;
284 192 : rCell2.SetCellGroup(xGroup2);
285 38 : }
286 : }
287 : else
288 : {
289 : // Make the next cell non-shared.
290 10 : sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
291 10 : std::advance(itCell, aPos.second+1);
292 10 : ScFormulaCell& rCell2 = **itCell;
293 10 : rCell2.SetCellGroup(xNone);
294 48 : }
295 : }
296 :
297 192 : rCell.SetCellGroup(xNone);
298 : }
299 :
300 10 : void SharedFormulaUtil::unshareFormulaCells(CellStoreType& rCells, std::vector<SCROW>& rRows)
301 : {
302 10 : if (rRows.empty())
303 10 : return;
304 :
305 : // Sort and remove duplicates.
306 10 : std::sort(rRows.begin(), rRows.end());
307 10 : rRows.erase(std::unique(rRows.begin(), rRows.end()), rRows.end());
308 :
309 : // Add next cell positions to the list (to ensure that each position becomes a single cell).
310 10 : std::vector<SCROW> aRows2;
311 10 : std::vector<SCROW>::const_iterator it = rRows.begin(), itEnd = rRows.end();
312 62 : for (; it != itEnd; ++it)
313 : {
314 52 : if (*it > MAXROW)
315 0 : break;
316 :
317 52 : aRows2.push_back(*it);
318 :
319 52 : if (*it < MAXROW)
320 52 : aRows2.push_back(*it+1);
321 : }
322 :
323 : // Remove duplicates again (the vector should still be sorted).
324 10 : aRows2.erase(std::unique(aRows2.begin(), aRows2.end()), aRows2.end());
325 :
326 10 : splitFormulaCellGroups(rCells, aRows2);
327 : }
328 :
329 228 : }
330 :
331 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|