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 0 : void SharedFormulaUtil::splitFormulaCellGroup(const CellStoreType::position_type& aPos)
17 : {
18 0 : SCROW nRow = aPos.first->position + aPos.second;
19 :
20 0 : if (aPos.first->type != sc::element_type_formula)
21 : // Not a formula cell block.
22 0 : return;
23 :
24 0 : if (aPos.second == 0)
25 : // Split position coincides with the block border. Nothing to do.
26 0 : return;
27 :
28 0 : sc::formula_block::iterator it = sc::formula_block::begin(*aPos.first->data);
29 0 : std::advance(it, aPos.second);
30 0 : ScFormulaCell& rTop = **it;
31 0 : if (!rTop.IsShared())
32 : // Not a shared formula.
33 0 : return;
34 :
35 0 : if (nRow == rTop.GetSharedTopRow())
36 : // Already the top cell of a shared group.
37 0 : return;
38 :
39 0 : ScFormulaCellGroupRef xGroup = rTop.GetCellGroup();
40 :
41 0 : SCROW nLength2 = xGroup->mpTopCell->aPos.Row() + xGroup->mnLength - nRow;
42 0 : ScFormulaCellGroupRef xGroup2;
43 0 : if (nLength2 > 1)
44 : {
45 0 : xGroup2.reset(new ScFormulaCellGroup);
46 0 : xGroup2->mbInvariant = xGroup->mbInvariant;
47 0 : xGroup2->mpTopCell = &rTop;
48 0 : xGroup2->mnLength = nLength2;
49 0 : xGroup2->mpCode = xGroup->mpCode->Clone();
50 : }
51 :
52 0 : xGroup->mnLength = nRow - xGroup->mpTopCell->aPos.Row();
53 0 : if (xGroup->mnLength == 1)
54 : {
55 : // The top group consists of only one cell. Ungroup this.
56 0 : ScFormulaCellGroupRef xNone;
57 0 : ScFormulaCell& rPrev = *sc::formula_block::at(*aPos.first->data, aPos.second-1);
58 0 : 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 0 : sc::formula_block::iterator itEnd = it;
71 0 : std::advance(itEnd, nLength2);
72 0 : for (; it != itEnd; ++it)
73 : {
74 0 : ScFormulaCell& rCell = **it;
75 0 : rCell.SetCellGroup(xGroup2);
76 0 : }
77 : }
78 :
79 0 : void SharedFormulaUtil::splitFormulaCellGroups(CellStoreType& rCells, std::vector<SCROW>& rBounds)
80 : {
81 0 : if (rBounds.empty())
82 0 : return;
83 :
84 : // Sort and remove duplicates.
85 0 : std::sort(rBounds.begin(), rBounds.end());
86 0 : std::vector<SCROW>::iterator it = std::unique(rBounds.begin(), rBounds.end());
87 0 : rBounds.erase(it, rBounds.end());
88 :
89 0 : it = rBounds.begin();
90 0 : SCROW nRow = *it;
91 0 : CellStoreType::position_type aPos = rCells.position(nRow);
92 0 : if (aPos.first == rCells.end())
93 0 : return;
94 :
95 0 : splitFormulaCellGroup(aPos);
96 0 : std::vector<SCROW>::iterator itEnd = rBounds.end();
97 0 : for (++it; it != itEnd; ++it)
98 : {
99 0 : nRow = *it;
100 0 : aPos = rCells.position(aPos.first, nRow);
101 0 : if (aPos.first == rCells.end())
102 0 : return;
103 :
104 0 : splitFormulaCellGroup(aPos);
105 : }
106 : }
107 :
108 0 : void SharedFormulaUtil::joinFormulaCells(const CellStoreType::position_type& rPos, ScFormulaCell& rCell1, ScFormulaCell& rCell2)
109 : {
110 0 : ScFormulaCell::CompareState eState = rCell1.CompareByTokenArray(rCell2);
111 0 : if (eState == ScFormulaCell::NotEqual)
112 0 : return;
113 :
114 : // Formula tokens equal those of the previous formula cell.
115 0 : ScFormulaCellGroupRef xGroup1 = rCell1.GetCellGroup();
116 0 : ScFormulaCellGroupRef xGroup2 = rCell2.GetCellGroup();
117 0 : if (xGroup1)
118 : {
119 0 : if (xGroup2)
120 : {
121 : // Both cell 1 and cell 2 are shared. Merge them together.
122 0 : if (xGroup1.get() == xGroup2.get())
123 : // They belong to the same group.
124 0 : return;
125 :
126 : // Set the group object from cell 1 to all cells in group 2.
127 0 : xGroup1->mnLength += xGroup2->mnLength;
128 0 : size_t nOffset = rPos.second + 1; // position of cell 2
129 0 : for (size_t i = 0, n = xGroup2->mnLength; i < n; ++i)
130 : {
131 0 : ScFormulaCell& rCell = *sc::formula_block::at(*rPos.first->data, nOffset+i);
132 0 : rCell.SetCellGroup(xGroup1);
133 : }
134 : }
135 : else
136 : {
137 : // cell 1 is shared but cell 2 is not.
138 0 : rCell2.SetCellGroup(xGroup1);
139 0 : ++xGroup1->mnLength;
140 : }
141 : }
142 : else
143 : {
144 0 : if (xGroup2)
145 : {
146 : // cell 1 is not shared, but cell 2 is already shared.
147 0 : rCell1.SetCellGroup(xGroup2);
148 0 : xGroup2->mpTopCell = &rCell1;
149 0 : ++xGroup2->mnLength;
150 : }
151 : else
152 : {
153 : // neither cells are shared.
154 : assert(rCell1.aPos.Row() == (SCROW)(rPos.first->position + rPos.second));
155 0 : xGroup1 = rCell1.CreateCellGroup(2, eState == ScFormulaCell::EqualInvariant);
156 0 : rCell2.SetCellGroup(xGroup1);
157 : }
158 0 : }
159 : }
160 :
161 0 : void SharedFormulaUtil::joinFormulaCellAbove(const CellStoreType::position_type& aPos)
162 : {
163 0 : if (aPos.first->type != sc::element_type_formula)
164 : // This is not a formula cell.
165 0 : return;
166 :
167 0 : 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 0 : return;
171 :
172 0 : ScFormulaCell& rPrev = *sc::formula_block::at(*aPos.first->data, aPos.second-1);
173 0 : ScFormulaCell& rCell = *sc::formula_block::at(*aPos.first->data, aPos.second);
174 0 : sc::CellStoreType::position_type aPosPrev = aPos;
175 0 : --aPosPrev.second;
176 0 : joinFormulaCells(aPosPrev, rPrev, rCell);
177 : }
178 :
179 0 : void SharedFormulaUtil::unshareFormulaCell(const CellStoreType::position_type& aPos, ScFormulaCell& rCell)
180 : {
181 0 : if (!rCell.IsShared())
182 0 : return;
183 :
184 0 : ScFormulaCellGroupRef xNone;
185 0 : sc::CellStoreType::iterator it = aPos.first;
186 :
187 : // This formula cell is shared. Adjust the shared group.
188 0 : if (rCell.aPos.Row() == rCell.GetSharedTopRow())
189 : {
190 : // Top of the shared range.
191 0 : ScFormulaCellGroupRef xGroup = rCell.GetCellGroup();
192 0 : 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 0 : ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
204 0 : rNext.SetCellGroup(xNone);
205 : }
206 : else
207 : {
208 : // Move the top cell to the next formula cell down.
209 0 : ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
210 0 : --xGroup->mnLength;
211 0 : xGroup->mpTopCell = &rNext;
212 0 : }
213 : }
214 0 : else if (rCell.aPos.Row() == rCell.GetSharedTopRow() + rCell.GetSharedLength() - 1)
215 : {
216 : // Bottom of the shared range.
217 0 : ScFormulaCellGroupRef xGroup = rCell.GetCellGroup();
218 0 : 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 0 : ScFormulaCell& rPrev = *sc::formula_block::at(*it->data, aPos.second-1);
230 0 : rPrev.SetCellGroup(xNone);
231 : }
232 : else
233 : {
234 : // Just shortern the shared range length by one.
235 0 : --xGroup->mnLength;
236 0 : }
237 : }
238 : else
239 : {
240 : // In the middle of the shared range. Split it into two groups.
241 0 : ScFormulaCellGroupRef xGroup = rCell.GetCellGroup();
242 0 : SCROW nEndRow = xGroup->mpTopCell->aPos.Row() + xGroup->mnLength - 1;
243 0 : xGroup->mnLength = rCell.aPos.Row() - xGroup->mpTopCell->aPos.Row(); // Shorten the top group.
244 0 : 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 0 : ScFormulaCell& rPrev = *sc::formula_block::at(*it->data, aPos.second-1);
256 0 : rPrev.SetCellGroup(xNone);
257 : }
258 :
259 0 : SCROW nLength2 = nEndRow - rCell.aPos.Row();
260 0 : if (nLength2 >= 2)
261 : {
262 0 : ScFormulaCellGroupRef xGroup2;
263 0 : xGroup2.reset(new ScFormulaCellGroup);
264 0 : ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
265 0 : xGroup2->mpTopCell = &rNext;
266 0 : xGroup2->mnLength = nLength2;
267 0 : xGroup2->mbInvariant = xGroup->mbInvariant;
268 0 : 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 0 : sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
278 0 : std::advance(itCell, aPos.second+1);
279 0 : sc::formula_block::iterator itCellEnd = itCell;
280 0 : std::advance(itCellEnd, xGroup2->mnLength);
281 0 : for (; itCell != itCellEnd; ++itCell)
282 : {
283 0 : ScFormulaCell& rCell2 = **itCell;
284 0 : rCell2.SetCellGroup(xGroup2);
285 0 : }
286 : }
287 : else
288 : {
289 : // Make the next cell non-shared.
290 0 : sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
291 0 : std::advance(itCell, aPos.second+1);
292 0 : ScFormulaCell& rCell2 = **itCell;
293 0 : rCell2.SetCellGroup(xNone);
294 0 : }
295 : }
296 :
297 0 : rCell.SetCellGroup(xNone);
298 : }
299 :
300 : }
301 :
302 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|