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 <column.hxx>
11 : #include <clipparam.hxx>
12 : #include <cellvalue.hxx>
13 : #include <attarray.hxx>
14 : #include <document.hxx>
15 : #include <cellvalues.hxx>
16 : #include <columnspanset.hxx>
17 : #include <listenercontext.hxx>
18 : #include <tokenstringcontext.hxx>
19 : #include <mtvcellfunc.hxx>
20 : #include <clipcontext.hxx>
21 : #include <attrib.hxx>
22 : #include <patattr.hxx>
23 : #include <docpool.hxx>
24 : #include <conditio.hxx>
25 : #include <formulagroup.hxx>
26 : #include <tokenarray.hxx>
27 : #include <globalnames.hxx>
28 : #include <scitems.hxx>
29 : #include <cellform.hxx>
30 : #include <sharedformula.hxx>
31 :
32 : #include <svl/sharedstringpool.hxx>
33 :
34 : #include <vector>
35 : #include <cassert>
36 :
37 : #include <boost/shared_ptr.hpp>
38 :
39 6 : bool ScColumn::IsMerged( SCROW nRow ) const
40 : {
41 6 : return pAttrArray->IsMerged(nRow);
42 : }
43 :
44 8 : void ScColumn::DeleteBeforeCopyFromClip( sc::CopyFromClipContext& rCxt, const ScColumn& rClipCol )
45 : {
46 8 : sc::CopyFromClipContext::Range aRange = rCxt.getDestRange();
47 8 : if (!ValidRow(aRange.mnRow1) || !ValidRow(aRange.mnRow2))
48 2 : return;
49 :
50 8 : ScRange aClipRange = rCxt.getClipDoc()->GetClipParam().getWholeRange();
51 8 : SCROW nClipRow1 = aClipRange.aStart.Row();
52 8 : SCROW nClipRow2 = aClipRange.aEnd.Row();
53 8 : SCROW nClipRowLen = nClipRow2 - nClipRow1 + 1;
54 :
55 : // Check for non-empty cell ranges in the clip column.
56 8 : sc::SingleColumnSpanSet aSpanSet;
57 8 : aSpanSet.scan(rClipCol, nClipRow1, nClipRow2);
58 14 : sc::SingleColumnSpanSet::SpansType aSpans;
59 8 : aSpanSet.getSpans(aSpans);
60 :
61 8 : if (aSpans.empty())
62 : // All cells in the range in the clip are empty. Nothing to delete.
63 2 : return;
64 :
65 : // Translate the clip column spans into the destination column, and repeat as needed.
66 12 : std::vector<sc::RowSpan> aDestSpans;
67 6 : SCROW nDestOffset = aRange.mnRow1 - nClipRow1;
68 6 : bool bContinue = true;
69 24 : while (bContinue)
70 : {
71 12 : sc::SingleColumnSpanSet::SpansType::const_iterator it = aSpans.begin(), itEnd = aSpans.end();
72 28 : for (; it != itEnd && bContinue; ++it)
73 : {
74 16 : const sc::RowSpan& r = *it;
75 16 : SCROW nDestRow1 = r.mnRow1 + nDestOffset;
76 16 : SCROW nDestRow2 = r.mnRow2 + nDestOffset;
77 :
78 16 : if (nDestRow1 > aRange.mnRow2)
79 : {
80 : // We're done.
81 6 : bContinue = false;
82 6 : continue;
83 : }
84 :
85 10 : if (nDestRow2 > aRange.mnRow2)
86 : {
87 : // Truncate this range, and set it as the last span.
88 0 : nDestRow2 = aRange.mnRow2;
89 0 : bContinue = false;
90 : }
91 :
92 10 : aDestSpans.push_back(sc::RowSpan(nDestRow1, nDestRow2));
93 : }
94 :
95 12 : nDestOffset += nClipRowLen;
96 : }
97 :
98 12 : std::vector<SCROW> aDeletedRows;
99 6 : InsertDeleteFlags nDelFlag = rCxt.getDeleteFlag();
100 6 : sc::ColumnBlockPosition aBlockPos;
101 6 : InitBlockPosition(aBlockPos);
102 :
103 6 : std::vector<sc::RowSpan>::const_iterator it = aDestSpans.begin(), itEnd = aDestSpans.end();
104 16 : for (; it != itEnd; ++it)
105 : {
106 10 : SCROW nRow1 = it->mnRow1;
107 10 : SCROW nRow2 = it->mnRow2;
108 :
109 10 : if (nDelFlag & IDF_CONTENTS)
110 10 : DeleteCells(aBlockPos, nRow1, nRow2, nDelFlag, aDeletedRows);
111 :
112 10 : if (nDelFlag & IDF_NOTE)
113 10 : DeleteCellNotes(aBlockPos, nRow1, nRow2);
114 :
115 10 : if (nDelFlag & IDF_EDITATTR)
116 0 : RemoveEditAttribs(nRow1, nRow2);
117 :
118 : // Delete attributes just now
119 10 : if (nDelFlag & IDF_ATTRIB)
120 : {
121 10 : pAttrArray->DeleteArea(nRow1, nRow2);
122 :
123 10 : if (rCxt.isTableProtected())
124 : {
125 0 : ScPatternAttr aPattern(pDocument->GetPool());
126 0 : aPattern.GetItemSet().Put(ScProtectionAttr(false));
127 0 : ApplyPatternArea(nRow1, nRow2, aPattern);
128 : }
129 :
130 10 : ScConditionalFormatList* pCondList = rCxt.getCondFormatList();
131 10 : if (pCondList)
132 10 : pCondList->DeleteArea(nCol, nRow1, nCol, nRow2);
133 : }
134 0 : else if ((nDelFlag & IDF_HARDATTR) == IDF_HARDATTR)
135 0 : pAttrArray->DeleteHardAttr(nRow1, nRow2);
136 : }
137 :
138 12 : BroadcastCells(aDeletedRows, SC_HINT_DATACHANGED);
139 : }
140 :
141 6 : void ScColumn::CopyOneCellFromClip( sc::CopyFromClipContext& rCxt, SCROW nRow1, SCROW nRow2 )
142 : {
143 : assert(nRow1 <= nRow2);
144 :
145 6 : size_t nDestSize = nRow2 - nRow1 + 1;
146 6 : sc::ColumnBlockPosition* pBlockPos = rCxt.getBlockPosition(nTab, nCol);
147 6 : if (!pBlockPos)
148 6 : return;
149 :
150 6 : ScCellValue& rSrcCell = rCxt.getSingleCell();
151 :
152 6 : InsertDeleteFlags nFlags = rCxt.getInsertFlag();
153 :
154 6 : if ((nFlags & IDF_ATTRIB) != IDF_NONE)
155 : {
156 6 : if (!rCxt.isSkipAttrForEmptyCells() || rSrcCell.meType != CELLTYPE_NONE)
157 : {
158 6 : const ScPatternAttr* pAttr = rCxt.getSingleCellPattern();
159 6 : pAttrArray->SetPatternArea(nRow1, nRow2, pAttr, true);
160 : }
161 : }
162 :
163 6 : if ((nFlags & IDF_CONTENTS) != IDF_NONE)
164 : {
165 6 : std::vector<sc::CellTextAttr> aTextAttrs(nDestSize);
166 :
167 6 : switch (rSrcCell.meType)
168 : {
169 : case CELLTYPE_VALUE:
170 : {
171 0 : std::vector<double> aVals(nDestSize, rSrcCell.mfValue);
172 0 : pBlockPos->miCellPos =
173 0 : maCells.set(pBlockPos->miCellPos, nRow1, aVals.begin(), aVals.end());
174 0 : pBlockPos->miCellTextAttrPos =
175 0 : maCellTextAttrs.set(pBlockPos->miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end());
176 0 : CellStorageModified();
177 : }
178 0 : break;
179 : case CELLTYPE_STRING:
180 : {
181 : // Compare the ScDocumentPool* to determine if we are copying within the
182 : // same document. If not, re-intern shared strings.
183 2 : svl::SharedStringPool* pSharedStringPool = (rCxt.getClipDoc()->GetPool() != pDocument->GetPool()) ?
184 2 : &pDocument->GetSharedStringPool() : NULL;
185 : svl::SharedString aStr = (pSharedStringPool ?
186 : pSharedStringPool->intern( rSrcCell.mpString->getString()) :
187 2 : *rSrcCell.mpString);
188 :
189 4 : std::vector<svl::SharedString> aStrs(nDestSize, aStr);
190 4 : pBlockPos->miCellPos =
191 2 : maCells.set(pBlockPos->miCellPos, nRow1, aStrs.begin(), aStrs.end());
192 4 : pBlockPos->miCellTextAttrPos =
193 2 : maCellTextAttrs.set(pBlockPos->miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end());
194 4 : CellStorageModified();
195 : }
196 2 : break;
197 : case CELLTYPE_EDIT:
198 : {
199 0 : std::vector<EditTextObject*> aStrs;
200 0 : aStrs.reserve(nDestSize);
201 0 : for (size_t i = 0; i < nDestSize; ++i)
202 0 : aStrs.push_back(rSrcCell.mpEditText->Clone());
203 :
204 0 : pBlockPos->miCellPos =
205 0 : maCells.set(pBlockPos->miCellPos, nRow1, aStrs.begin(), aStrs.end());
206 0 : pBlockPos->miCellTextAttrPos =
207 0 : maCellTextAttrs.set(pBlockPos->miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end());
208 0 : CellStorageModified();
209 : }
210 0 : break;
211 : case CELLTYPE_FORMULA:
212 : {
213 2 : std::vector<sc::RowSpan> aRanges;
214 2 : aRanges.reserve(1);
215 2 : aRanges.push_back(sc::RowSpan(nRow1, nRow2));
216 2 : CloneFormulaCell(*rSrcCell.mpFormula, aRanges);
217 : }
218 2 : break;
219 : default:
220 : ;
221 6 : }
222 : }
223 :
224 6 : const ScPostIt* pNote = rCxt.getSingleCellNote();
225 6 : if (pNote && (nFlags & (IDF_NOTE | IDF_ADDNOTES)) != IDF_NONE)
226 : {
227 : // Duplicate the cell note over the whole pasted range.
228 :
229 0 : ScDocument* pClipDoc = rCxt.getClipDoc();
230 0 : const ScAddress& rSrcPos = pClipDoc->GetClipParam().getWholeRange().aStart;
231 0 : std::vector<ScPostIt*> aNotes;
232 0 : ScAddress aDestPos(nCol, nRow1, nTab);
233 0 : aNotes.reserve(nDestSize);
234 0 : for (size_t i = 0; i < nDestSize; ++i)
235 : {
236 0 : bool bCloneCaption = (nFlags & IDF_NOCAPTIONS) == IDF_NONE;
237 0 : aNotes.push_back(pNote->Clone(rSrcPos, *pDocument, aDestPos, bCloneCaption));
238 0 : aDestPos.IncRow();
239 : }
240 :
241 0 : pBlockPos->miCellNotePos =
242 : maCellNotes.set(
243 0 : pBlockPos->miCellNotePos, nRow1, aNotes.begin(), aNotes.end());
244 : }
245 : }
246 :
247 0 : void ScColumn::SetValues( SCROW nRow, const std::vector<double>& rVals )
248 : {
249 0 : if (!ValidRow(nRow))
250 0 : return;
251 :
252 0 : SCROW nLastRow = nRow + rVals.size() - 1;
253 0 : if (nLastRow > MAXROW)
254 : // Out of bound. Do nothing.
255 0 : return;
256 :
257 0 : sc::CellStoreType::position_type aPos = maCells.position(nRow);
258 0 : DetachFormulaCells(aPos, rVals.size());
259 :
260 0 : maCells.set(nRow, rVals.begin(), rVals.end());
261 0 : std::vector<sc::CellTextAttr> aDefaults(rVals.size());
262 0 : maCellTextAttrs.set(nRow, aDefaults.begin(), aDefaults.end());
263 :
264 0 : CellStorageModified();
265 :
266 0 : std::vector<SCROW> aRows;
267 0 : aRows.reserve(rVals.size());
268 0 : for (SCROW i = nRow; i <= nLastRow; ++i)
269 0 : aRows.push_back(i);
270 :
271 0 : BroadcastCells(aRows, SC_HINT_DATACHANGED);
272 : }
273 :
274 0 : void ScColumn::TransferCellValuesTo( SCROW nRow, size_t nLen, sc::CellValues& rDest )
275 : {
276 0 : if (!ValidRow(nRow))
277 0 : return;
278 :
279 0 : SCROW nLastRow = nRow + nLen - 1;
280 0 : if (nLastRow > MAXROW)
281 : // Out of bound. Do nothing.
282 0 : return;
283 :
284 0 : sc::CellStoreType::position_type aPos = maCells.position(nRow);
285 0 : DetachFormulaCells(aPos, nLen);
286 :
287 0 : rDest.transferFrom(*this, nRow, nLen);
288 :
289 0 : CellStorageModified();
290 :
291 0 : std::vector<SCROW> aRows;
292 0 : aRows.reserve(nLen);
293 0 : for (SCROW i = nRow; i <= nLastRow; ++i)
294 0 : aRows.push_back(i);
295 :
296 0 : BroadcastCells(aRows, SC_HINT_DATACHANGED);
297 : }
298 :
299 0 : void ScColumn::CopyCellValuesFrom( SCROW nRow, const sc::CellValues& rSrc )
300 : {
301 0 : if (!ValidRow(nRow))
302 0 : return;
303 :
304 0 : SCROW nLastRow = nRow + rSrc.size() - 1;
305 0 : if (nLastRow > MAXROW)
306 : // Out of bound. Do nothing
307 0 : return;
308 :
309 0 : sc::CellStoreType::position_type aPos = maCells.position(nRow);
310 0 : DetachFormulaCells(aPos, rSrc.size());
311 :
312 0 : rSrc.copyTo(*this, nRow);
313 :
314 0 : CellStorageModified();
315 :
316 0 : std::vector<SCROW> aRows;
317 0 : aRows.reserve(rSrc.size());
318 0 : for (SCROW i = nRow; i <= nLastRow; ++i)
319 0 : aRows.push_back(i);
320 :
321 0 : BroadcastCells(aRows, SC_HINT_DATACHANGED);
322 : }
323 :
324 : namespace {
325 :
326 6 : class ConvertFormulaToValueHandler
327 : {
328 : sc::CellValues maResValues;
329 : bool mbModified;
330 :
331 : public:
332 6 : ConvertFormulaToValueHandler( SCTAB, SCCOL ) :
333 6 : mbModified(false)
334 : {
335 6 : maResValues.reset(MAXROWCOUNT);
336 6 : }
337 :
338 12 : void operator() ( size_t nRow, const ScFormulaCell* pCell )
339 : {
340 12 : sc::FormulaResultValue aRes = pCell->GetResult();
341 12 : switch (aRes.meType)
342 : {
343 : case sc::FormulaResultValue::Value:
344 12 : maResValues.setValue(nRow, aRes.mfValue);
345 12 : break;
346 : case sc::FormulaResultValue::String:
347 0 : maResValues.setValue(nRow, aRes.maString);
348 0 : break;
349 : case sc::FormulaResultValue::Error:
350 : case sc::FormulaResultValue::Invalid:
351 : default:
352 0 : maResValues.setValue(nRow, svl::SharedString::getEmptyString());
353 : }
354 :
355 12 : mbModified = true;
356 12 : }
357 :
358 6 : bool isModified() const { return mbModified; }
359 :
360 6 : sc::CellValues& getResValues() { return maResValues; }
361 : };
362 :
363 : }
364 :
365 6 : void ScColumn::ConvertFormulaToValue(
366 : sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2, sc::TableValues* pUndo )
367 : {
368 6 : if (!ValidRow(nRow1) || !ValidRow(nRow2) || nRow1 > nRow2)
369 0 : return;
370 :
371 6 : std::vector<SCROW> aBounds;
372 6 : aBounds.push_back(nRow1);
373 6 : if (nRow2 < MAXROW-1)
374 6 : aBounds.push_back(nRow2+1);
375 :
376 : // Split formula cell groups at top and bottom boundaries (if applicable).
377 6 : sc::SharedFormulaUtil::splitFormulaCellGroups(maCells, aBounds);
378 :
379 : // Parse all formulas within the range and store their results into temporary storage.
380 12 : ConvertFormulaToValueHandler aFunc(nTab, nCol);
381 6 : sc::ParseFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
382 6 : if (!aFunc.isModified())
383 : // No formula cells encountered.
384 0 : return;
385 :
386 6 : DetachFormulaCells(rCxt, nRow1, nRow2);
387 :
388 : // Undo storage to hold static values which will get swapped to the cell storage later.
389 12 : sc::CellValues aUndoCells;
390 6 : aFunc.getResValues().swap(aUndoCells);
391 6 : aUndoCells.swapNonEmpty(*this);
392 6 : if (pUndo)
393 12 : pUndo->swap(nTab, nCol, aUndoCells);
394 : }
395 :
396 : namespace {
397 :
398 : class StartListeningHandler
399 : {
400 : sc::StartListeningContext& mrCxt;
401 :
402 : public:
403 14 : StartListeningHandler( sc::StartListeningContext& rCxt ) :
404 14 : mrCxt(rCxt) {}
405 :
406 20 : void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
407 : {
408 20 : pCell->StartListeningTo(mrCxt);
409 20 : }
410 : };
411 :
412 : class EndListeningHandler
413 : {
414 : sc::EndListeningContext& mrCxt;
415 :
416 : public:
417 14 : EndListeningHandler( sc::EndListeningContext& rCxt ) :
418 14 : mrCxt(rCxt) {}
419 :
420 8 : void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
421 : {
422 8 : pCell->EndListeningTo(mrCxt);
423 8 : }
424 : };
425 :
426 : }
427 :
428 14 : void ScColumn::SwapNonEmpty(
429 : sc::TableValues& rValues, sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt )
430 : {
431 14 : const ScRange& rRange = rValues.getRange();
432 14 : std::vector<SCROW> aBounds;
433 14 : aBounds.push_back(rRange.aStart.Row());
434 14 : if (rRange.aEnd.Row() < MAXROW-1)
435 14 : aBounds.push_back(rRange.aEnd.Row()+1);
436 :
437 : // Split formula cell groups at top and bottom boundaries (if applicable).
438 14 : sc::SharedFormulaUtil::splitFormulaCellGroups(maCells, aBounds);
439 28 : std::vector<sc::CellValueSpan> aSpans = rValues.getNonEmptySpans(nTab, nCol);
440 :
441 : // Detach formula cells within the spans (if any).
442 14 : EndListeningHandler aEndLisFunc(rEndCxt);
443 14 : std::vector<sc::CellValueSpan>::const_iterator it = aSpans.begin(), itEnd = aSpans.end();
444 14 : sc::CellStoreType::iterator itPos = maCells.begin();
445 30 : for (; it != itEnd; ++it)
446 : {
447 16 : SCROW nRow1 = it->mnRow1;
448 16 : SCROW nRow2 = it->mnRow2;
449 16 : itPos = sc::ProcessFormula(itPos, maCells, nRow1, nRow2, aEndLisFunc);
450 : }
451 :
452 14 : rValues.swapNonEmpty(nTab, nCol, *this);
453 14 : RegroupFormulaCells();
454 :
455 : // Attach formula cells within the spans (if any).
456 14 : StartListeningHandler aStartLisFunc(rStartCxt);
457 14 : it = aSpans.begin();
458 14 : itPos = maCells.begin();
459 30 : for (; it != itEnd; ++it)
460 : {
461 16 : SCROW nRow1 = it->mnRow1;
462 16 : SCROW nRow2 = it->mnRow2;
463 16 : itPos = sc::ProcessFormula(itPos, maCells, nRow1, nRow2, aStartLisFunc);
464 : }
465 :
466 28 : CellStorageModified();
467 14 : }
468 :
469 2 : void ScColumn::DeleteRanges( const std::vector<sc::RowSpan>& rRanges, InsertDeleteFlags nDelFlag, bool bBroadcast )
470 : {
471 2 : std::vector<sc::RowSpan>::const_iterator itSpan = rRanges.begin(), itSpanEnd = rRanges.end();
472 4 : for (; itSpan != itSpanEnd; ++itSpan)
473 2 : DeleteArea(itSpan->mnRow1, itSpan->mnRow2, nDelFlag, bBroadcast);
474 2 : }
475 :
476 4 : void ScColumn::CloneFormulaCell( const ScFormulaCell& rSrc, const std::vector<sc::RowSpan>& rRanges )
477 : {
478 4 : sc::CellStoreType::iterator itPos = maCells.begin();
479 4 : sc::CellTextAttrStoreType::iterator itAttrPos = maCellTextAttrs.begin();
480 4 : sc::StartListeningContext aCxt(*pDocument);
481 :
482 8 : std::vector<ScFormulaCell*> aFormulas;
483 4 : std::vector<sc::RowSpan>::const_iterator itSpan = rRanges.begin(), itSpanEnd = rRanges.end();
484 8 : for (; itSpan != itSpanEnd; ++itSpan)
485 : {
486 4 : SCROW nRow1 = itSpan->mnRow1, nRow2 = itSpan->mnRow2;
487 4 : size_t nLen = nRow2 - nRow1 + 1;
488 : assert(nLen > 0);
489 4 : aFormulas.clear();
490 4 : aFormulas.reserve(nLen);
491 :
492 4 : ScAddress aPos(nCol, nRow1, nTab);
493 :
494 4 : if (nLen == 1)
495 : {
496 : // Single, ungrouped formula cell.
497 2 : ScFormulaCell* pCell = new ScFormulaCell(rSrc, *pDocument, aPos);
498 2 : pCell->StartListeningTo(aCxt);
499 2 : pCell->SetDirty();
500 2 : aFormulas.push_back(pCell);
501 : }
502 : else
503 : {
504 : // Create a group of formula cells.
505 2 : ScFormulaCellGroupRef xGroup(new ScFormulaCellGroup);
506 2 : xGroup->setCode(*rSrc.GetCode());
507 2 : xGroup->compileCode(*pDocument, aPos, pDocument->GetGrammar());
508 22 : for (size_t i = 0; i < nLen; ++i, aPos.IncRow())
509 : {
510 20 : ScFormulaCell* pCell = new ScFormulaCell(pDocument, aPos, xGroup);
511 20 : if (i == 0)
512 : {
513 2 : xGroup->mpTopCell = pCell;
514 2 : xGroup->mnLength = nLen;
515 : }
516 20 : pCell->StartListeningTo(aCxt);
517 20 : pCell->SetDirty();
518 20 : aFormulas.push_back(pCell);
519 2 : }
520 : }
521 :
522 4 : itPos = maCells.set(itPos, nRow1, aFormulas.begin(), aFormulas.end());
523 :
524 : // Join the top and bottom of the pasted formula cells as needed.
525 4 : sc::CellStoreType::position_type aPosObj = maCells.position(itPos, nRow1);
526 :
527 : assert(aPosObj.first->type == sc::element_type_formula);
528 4 : ScFormulaCell* pCell = sc::formula_block::at(*aPosObj.first->data, aPosObj.second);
529 4 : JoinNewFormulaCell(aPosObj, *pCell);
530 :
531 4 : aPosObj = maCells.position(aPosObj.first, nRow2);
532 : assert(aPosObj.first->type == sc::element_type_formula);
533 4 : pCell = sc::formula_block::at(*aPosObj.first->data, aPosObj.second);
534 4 : JoinNewFormulaCell(aPosObj, *pCell);
535 :
536 4 : std::vector<sc::CellTextAttr> aTextAttrs(nLen);
537 4 : itAttrPos = maCellTextAttrs.set(itAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end());
538 4 : }
539 :
540 8 : CellStorageModified();
541 4 : }
542 :
543 38 : ScPostIt* ScColumn::ReleaseNote( SCROW nRow )
544 : {
545 38 : if (!ValidRow(nRow))
546 0 : return NULL;
547 :
548 38 : ScPostIt* p = NULL;
549 38 : maCellNotes.release(nRow, p);
550 38 : return p;
551 : }
552 :
553 2112 : size_t ScColumn::GetNoteCount() const
554 : {
555 2112 : size_t nCount = 0;
556 2112 : sc::CellNoteStoreType::const_iterator it = maCellNotes.begin(), itEnd = maCellNotes.end();
557 4292 : for (; it != itEnd; ++it)
558 : {
559 2180 : if (it->type != sc::element_type_cellnote)
560 2146 : continue;
561 :
562 34 : nCount += it->size;
563 : }
564 :
565 2112 : return nCount;
566 : }
567 :
568 : namespace {
569 :
570 : class NoteCaptionCreator
571 : {
572 : ScAddress maPos;
573 : public:
574 59392 : NoteCaptionCreator( SCTAB nTab, SCCOL nCol ) : maPos(nCol,0,nTab) {}
575 :
576 8 : void operator() ( size_t nRow, ScPostIt* p )
577 : {
578 8 : maPos.SetRow(nRow);
579 8 : p->GetOrCreateCaption(maPos);
580 8 : }
581 : };
582 :
583 : struct NoteCaptionCleaner
584 : {
585 20 : void operator() ( size_t /*nRow*/, ScPostIt* p )
586 : {
587 20 : p->ForgetCaption();
588 20 : }
589 : };
590 :
591 : }
592 :
593 59392 : void ScColumn::CreateAllNoteCaptions()
594 : {
595 59392 : NoteCaptionCreator aFunc(nTab, nCol);
596 59392 : sc::ProcessNote(maCellNotes, aFunc);
597 59392 : }
598 :
599 32 : void ScColumn::ForgetNoteCaptions( SCROW nRow1, SCROW nRow2 )
600 : {
601 32 : if (!ValidRow(nRow1) || !ValidRow(nRow2))
602 32 : return;
603 :
604 : NoteCaptionCleaner aFunc;
605 32 : sc::CellNoteStoreType::iterator it = maCellNotes.begin();
606 32 : sc::ProcessNote(it, maCellNotes, nRow1, nRow2, aFunc);
607 : }
608 :
609 12 : SCROW ScColumn::GetNotePosition( size_t nIndex ) const
610 : {
611 : // Return the row position of the nth note in the column.
612 :
613 12 : sc::CellNoteStoreType::const_iterator it = maCellNotes.begin(), itEnd = maCellNotes.end();
614 :
615 12 : size_t nCount = 0; // Number of notes encountered so far.
616 24 : for (; it != itEnd; ++it)
617 : {
618 24 : if (it->type != sc::element_type_cellnote)
619 : // Skip the empty blocks.
620 12 : continue;
621 :
622 12 : if (nIndex < nCount + it->size)
623 : {
624 : // Index falls within this block.
625 12 : size_t nOffset = nIndex - nCount;
626 12 : return it->position + nOffset;
627 : }
628 :
629 0 : nCount += it->size;
630 : }
631 :
632 0 : return -1;
633 : }
634 :
635 : namespace {
636 :
637 : class NoteEntryCollector
638 : {
639 : std::vector<sc::NoteEntry>& mrNotes;
640 : SCTAB mnTab;
641 : SCCOL mnCol;
642 : SCROW mnStartRow;
643 : SCROW mnEndRow;
644 : public:
645 327680 : NoteEntryCollector( std::vector<sc::NoteEntry>& rNotes, SCTAB nTab, SCCOL nCol,
646 : SCROW nStartRow = 0, SCROW nEndRow = MAXROW) :
647 : mrNotes(rNotes), mnTab(nTab), mnCol(nCol),
648 327680 : mnStartRow(nStartRow), mnEndRow(nEndRow) {}
649 :
650 303234 : void operator() (const sc::CellNoteStoreType::value_type& node) const
651 : {
652 303234 : if (node.type != sc::element_type_cellnote)
653 606400 : return;
654 :
655 68 : size_t nTopRow = node.position;
656 68 : sc::cellnote_block::const_iterator it = sc::cellnote_block::begin(*node.data);
657 68 : sc::cellnote_block::const_iterator itEnd = sc::cellnote_block::end(*node.data);
658 68 : size_t nOffset = 0;
659 68 : if(nTopRow < size_t(mnStartRow))
660 : {
661 0 : std::advance(it, mnStartRow - nTopRow);
662 0 : nOffset = mnStartRow - nTopRow;
663 : }
664 :
665 136 : for (; it != itEnd && nTopRow + nOffset <= size_t(mnEndRow);
666 : ++it, ++nOffset)
667 : {
668 68 : ScAddress aPos(mnCol, nTopRow + nOffset, mnTab);
669 68 : mrNotes.push_back(sc::NoteEntry(aPos, *it));
670 : }
671 : }
672 : };
673 :
674 : }
675 :
676 303104 : void ScColumn::GetAllNoteEntries( std::vector<sc::NoteEntry>& rNotes ) const
677 : {
678 303104 : std::for_each(maCellNotes.begin(), maCellNotes.end(), NoteEntryCollector(rNotes, nTab, nCol));
679 303104 : }
680 :
681 24576 : void ScColumn::GetNotesInRange(SCROW nStartRow, SCROW nEndRow,
682 : std::vector<sc::NoteEntry>& rNotes ) const
683 : {
684 24576 : std::pair<sc::CellNoteStoreType::const_iterator,size_t> aPos = maCellNotes.position(nStartRow);
685 24576 : sc::CellNoteStoreType::const_iterator it = aPos.first;
686 24576 : if (it == maCellNotes.end())
687 : // Invalid row number.
688 24576 : return;
689 :
690 : std::pair<sc::CellNoteStoreType::const_iterator,size_t> aEndPos =
691 24576 : maCellNotes.position(nEndRow);
692 24576 : sc::CellNoteStoreType::const_iterator itEnd = aEndPos.first;
693 :
694 24576 : std::for_each(it, itEnd, NoteEntryCollector(rNotes, nTab, nCol, nStartRow, nEndRow));
695 : }
696 :
697 : namespace {
698 :
699 : class RecompileByOpcodeHandler
700 : {
701 : ScDocument* mpDoc;
702 : const boost::unordered_set<OpCode>& mrOps;
703 : sc::EndListeningContext& mrEndListenCxt;
704 : sc::CompileFormulaContext& mrCompileFormulaCxt;
705 :
706 : public:
707 165888 : RecompileByOpcodeHandler(
708 : ScDocument* pDoc, const boost::unordered_set<OpCode>& rOps,
709 : sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt ) :
710 : mpDoc(pDoc),
711 : mrOps(rOps),
712 : mrEndListenCxt(rEndListenCxt),
713 165888 : mrCompileFormulaCxt(rCompileCxt) {}
714 :
715 28 : void operator() ( sc::FormulaGroupEntry& rEntry )
716 : {
717 : // Perform end listening, remove from formula tree, and set them up
718 : // for re-compilation.
719 :
720 28 : ScFormulaCell* pTop = NULL;
721 :
722 28 : if (rEntry.mbShared)
723 : {
724 : // Only inspect the code from the top cell.
725 10 : pTop = *rEntry.mpCells;
726 : }
727 : else
728 18 : pTop = rEntry.mpCell;
729 :
730 28 : ScTokenArray* pCode = pTop->GetCode();
731 28 : bool bRecompile = pCode->HasOpCodes(mrOps);
732 :
733 28 : if (bRecompile)
734 : {
735 : // Get the formula string.
736 10 : OUString aFormula = pTop->GetFormula(mrCompileFormulaCxt);
737 10 : sal_Int32 n = aFormula.getLength();
738 10 : if (pTop->GetMatrixFlag() != MM_NONE && n > 0)
739 : {
740 0 : if (aFormula[0] == '{' && aFormula[n-1] == '}')
741 0 : aFormula = aFormula.copy(1, n-2);
742 : }
743 :
744 10 : if (rEntry.mbShared)
745 : {
746 8 : ScFormulaCell** pp = rEntry.mpCells;
747 8 : ScFormulaCell** ppEnd = pp + rEntry.mnLength;
748 32 : for (; pp != ppEnd; ++pp)
749 : {
750 24 : ScFormulaCell* p = *pp;
751 24 : p->EndListeningTo(mrEndListenCxt);
752 24 : mpDoc->RemoveFromFormulaTree(p);
753 : }
754 : }
755 : else
756 : {
757 2 : rEntry.mpCell->EndListeningTo(mrEndListenCxt);
758 2 : mpDoc->RemoveFromFormulaTree(rEntry.mpCell);
759 : }
760 :
761 10 : pCode->Clear();
762 10 : pTop->SetHybridFormula(aFormula, mpDoc->GetGrammar());
763 : }
764 28 : }
765 : };
766 :
767 : class CompileHybridFormulaHandler
768 : {
769 : ScDocument* mpDoc;
770 : sc::StartListeningContext& mrStartListenCxt;
771 : sc::CompileFormulaContext& mrCompileFormulaCxt;
772 :
773 : public:
774 165888 : CompileHybridFormulaHandler( ScDocument* pDoc, sc::StartListeningContext& rStartListenCxt, sc::CompileFormulaContext& rCompileCxt ) :
775 : mpDoc(pDoc),
776 : mrStartListenCxt(rStartListenCxt),
777 165888 : mrCompileFormulaCxt(rCompileCxt) {}
778 :
779 28 : void operator() ( sc::FormulaGroupEntry& rEntry )
780 : {
781 28 : if (rEntry.mbShared)
782 : {
783 10 : ScFormulaCell* pTop = *rEntry.mpCells;
784 10 : OUString aFormula = pTop->GetHybridFormula();
785 :
786 10 : if (!aFormula.isEmpty())
787 : {
788 : // Create a new token array from the hybrid formula string, and
789 : // set it to the group.
790 8 : ScCompiler aComp(mrCompileFormulaCxt, pTop->aPos);
791 8 : ScTokenArray* pNewCode = aComp.CompileString(aFormula);
792 16 : ScFormulaCellGroupRef xGroup = pTop->GetCellGroup();
793 : assert(xGroup);
794 8 : xGroup->setCode(pNewCode);
795 8 : xGroup->compileCode(*mpDoc, pTop->aPos, mpDoc->GetGrammar());
796 :
797 : // Propagate the new token array to all formula cells in the group.
798 8 : ScFormulaCell** pp = rEntry.mpCells;
799 8 : ScFormulaCell** ppEnd = pp + rEntry.mnLength;
800 32 : for (; pp != ppEnd; ++pp)
801 : {
802 24 : ScFormulaCell* p = *pp;
803 24 : p->SyncSharedCode();
804 24 : p->StartListeningTo(mrStartListenCxt);
805 24 : p->SetDirty();
806 8 : }
807 10 : }
808 : }
809 : else
810 : {
811 18 : ScFormulaCell* pCell = rEntry.mpCell;
812 18 : OUString aFormula = pCell->GetHybridFormula();
813 :
814 18 : if (!aFormula.isEmpty())
815 : {
816 : // Create token array from formula string.
817 2 : ScCompiler aComp(mrCompileFormulaCxt, pCell->aPos);
818 2 : ScTokenArray* pNewCode = aComp.CompileString(aFormula);
819 :
820 : // Generate RPN tokens.
821 4 : ScCompiler aComp2(mpDoc, pCell->aPos, *pNewCode);
822 2 : aComp2.CompileTokenArray();
823 :
824 2 : pCell->SetCode(pNewCode);
825 2 : pCell->StartListeningTo(mrStartListenCxt);
826 4 : pCell->SetDirty();
827 18 : }
828 : }
829 28 : }
830 : };
831 :
832 : }
833 :
834 143360 : void ScColumn::PreprocessRangeNameUpdate(
835 : sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt )
836 : {
837 : // Collect all formula groups.
838 143360 : std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries();
839 :
840 286720 : boost::unordered_set<OpCode> aOps;
841 143360 : aOps.insert(ocBad);
842 143360 : aOps.insert(ocColRowName);
843 143360 : aOps.insert(ocName);
844 143360 : RecompileByOpcodeHandler aFunc(pDocument, aOps, rEndListenCxt, rCompileCxt);
845 286720 : std::for_each(aGroups.begin(), aGroups.end(), aFunc);
846 143360 : }
847 :
848 22528 : void ScColumn::PreprocessDBDataUpdate(
849 : sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt )
850 : {
851 : // Collect all formula groups.
852 22528 : std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries();
853 :
854 45056 : boost::unordered_set<OpCode> aOps;
855 22528 : aOps.insert(ocBad);
856 22528 : aOps.insert(ocColRowName);
857 22528 : aOps.insert(ocDBArea);
858 22528 : RecompileByOpcodeHandler aFunc(pDocument, aOps, rEndListenCxt, rCompileCxt);
859 45056 : std::for_each(aGroups.begin(), aGroups.end(), aFunc);
860 22528 : }
861 :
862 165888 : void ScColumn::CompileHybridFormula(
863 : sc::StartListeningContext& rStartListenCxt, sc::CompileFormulaContext& rCompileCxt )
864 : {
865 : // Collect all formula groups.
866 165888 : std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries();
867 :
868 165888 : CompileHybridFormulaHandler aFunc(pDocument, rStartListenCxt, rCompileCxt);
869 165888 : std::for_each(aGroups.begin(), aGroups.end(), aFunc);
870 165888 : }
871 :
872 : namespace {
873 :
874 : class ScriptTypeUpdater
875 : {
876 : ScColumn& mrCol;
877 : sc::CellTextAttrStoreType& mrTextAttrs;
878 : sc::CellTextAttrStoreType::iterator miPosAttr;
879 : ScConditionalFormatList* mpCFList;
880 : SvNumberFormatter* mpFormatter;
881 : ScAddress maPos;
882 : bool mbUpdated;
883 :
884 : private:
885 2955 : void updateScriptType( size_t nRow, ScRefCellValue& rCell )
886 : {
887 2955 : sc::CellTextAttrStoreType::position_type aAttrPos = mrTextAttrs.position(miPosAttr, nRow);
888 2955 : miPosAttr = aAttrPos.first;
889 :
890 2955 : if (aAttrPos.first->type != sc::element_type_celltextattr)
891 2139 : return;
892 :
893 2955 : sc::CellTextAttr& rAttr = sc::celltextattr_block::at(*aAttrPos.first->data, aAttrPos.second);
894 2955 : if (rAttr.mnScriptType != SC_SCRIPTTYPE_UNKNOWN)
895 : // Script type already deteremined. Skip it.
896 2139 : return;
897 :
898 816 : const ScPatternAttr* pPat = mrCol.GetPattern(nRow);
899 816 : if (!pPat)
900 : // In theory this should never return NULL. But let's be safe.
901 0 : return;
902 :
903 816 : const SfxItemSet* pCondSet = NULL;
904 816 : if (mpCFList)
905 : {
906 816 : maPos.SetRow(nRow);
907 : const ScCondFormatItem& rItem =
908 816 : static_cast<const ScCondFormatItem&>(pPat->GetItem(ATTR_CONDITIONAL));
909 816 : const std::vector<sal_uInt32>& rData = rItem.GetCondFormatData();
910 816 : pCondSet = mrCol.GetDoc().GetCondResult(rCell, maPos, *mpCFList, rData);
911 : }
912 :
913 816 : OUString aStr;
914 : Color* pColor;
915 816 : sal_uLong nFormat = pPat->GetNumberFormat(mpFormatter, pCondSet);
916 816 : ScCellFormat::GetString(rCell, nFormat, aStr, &pColor, *mpFormatter, &mrCol.GetDoc());
917 :
918 816 : rAttr.mnScriptType = mrCol.GetDoc().GetStringScriptType(aStr);
919 816 : mbUpdated = true;
920 : }
921 :
922 : public:
923 1136778 : ScriptTypeUpdater( ScColumn& rCol ) :
924 : mrCol(rCol),
925 1136778 : mrTextAttrs(rCol.GetCellAttrStore()),
926 : miPosAttr(mrTextAttrs.begin()),
927 1136778 : mpCFList(rCol.GetDoc().GetCondFormList(rCol.GetTab())),
928 1136778 : mpFormatter(rCol.GetDoc().GetFormatTable()),
929 2273556 : maPos(rCol.GetCol(), 0, rCol.GetTab()),
930 6820668 : mbUpdated(false)
931 1136778 : {}
932 :
933 598 : void operator() ( size_t nRow, double fVal )
934 : {
935 598 : ScRefCellValue aCell(fVal);
936 598 : updateScriptType(nRow, aCell);
937 598 : }
938 :
939 2123 : void operator() ( size_t nRow, const svl::SharedString& rStr )
940 : {
941 2123 : ScRefCellValue aCell(&rStr);
942 2123 : updateScriptType(nRow, aCell);
943 2123 : }
944 :
945 0 : void operator() ( size_t nRow, const EditTextObject* pText )
946 : {
947 0 : ScRefCellValue aCell(pText);
948 0 : updateScriptType(nRow, aCell);
949 0 : }
950 :
951 234 : void operator() ( size_t nRow, const ScFormulaCell* pCell )
952 : {
953 234 : ScRefCellValue aCell(const_cast<ScFormulaCell*>(pCell));
954 234 : updateScriptType(nRow, aCell);
955 234 : }
956 :
957 1136778 : bool isUpdated() const { return mbUpdated; }
958 : };
959 :
960 : }
961 :
962 1136778 : void ScColumn::UpdateScriptTypes( SCROW nRow1, SCROW nRow2 )
963 : {
964 1136778 : if (!ValidRow(nRow1) || !ValidRow(nRow2) || nRow1 > nRow2)
965 1136778 : return;
966 :
967 1136778 : ScriptTypeUpdater aFunc(*this);
968 1136778 : sc::ParseAllNonEmpty(maCells.begin(), maCells, nRow1, nRow2, aFunc);
969 1136778 : if (aFunc.isUpdated())
970 366 : CellStorageModified();
971 : }
972 :
973 34 : void ScColumn::Swap( ScColumn& rOther, SCROW nRow1, SCROW nRow2, bool bPattern )
974 : {
975 34 : maCells.swap(nRow1, nRow2, rOther.maCells, nRow1);
976 34 : maCellTextAttrs.swap(nRow1, nRow2, rOther.maCellTextAttrs, nRow1);
977 34 : maCellNotes.swap(nRow1, nRow2, rOther.maCellNotes, nRow1);
978 34 : maBroadcasters.swap(nRow1, nRow2, rOther.maBroadcasters, nRow1);
979 :
980 34 : if (bPattern)
981 : {
982 18874430 : for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
983 : {
984 18874396 : const ScPatternAttr* pPat1 = GetPattern(nRow);
985 18874396 : const ScPatternAttr* pPat2 = rOther.GetPattern(nRow);
986 18874396 : if (pPat1 != pPat2)
987 : {
988 0 : SetPattern(nRow, *pPat2, true);
989 0 : rOther.SetPattern(nRow, *pPat1, true);
990 : }
991 : }
992 : }
993 :
994 34 : CellStorageModified();
995 34 : rOther.CellStorageModified();
996 34 : }
997 :
998 : namespace {
999 :
1000 : class FormulaColPosSetter
1001 : {
1002 : SCCOL mnCol;
1003 : public:
1004 72 : FormulaColPosSetter( SCCOL nCol ) : mnCol(nCol) {}
1005 :
1006 6 : void operator() ( size_t nRow, ScFormulaCell* pCell )
1007 : {
1008 6 : if (!pCell->IsShared() || pCell->IsSharedTop())
1009 : {
1010 : // Ensure that the references still point to the same locations
1011 : // after the position change.
1012 2 : ScAddress aOldPos = pCell->aPos;
1013 2 : pCell->aPos.SetCol(mnCol);
1014 2 : pCell->aPos.SetRow(nRow);
1015 2 : pCell->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos, pCell->aPos);
1016 : }
1017 : else
1018 : {
1019 4 : pCell->aPos.SetCol(mnCol);
1020 4 : pCell->aPos.SetRow(nRow);
1021 : }
1022 6 : }
1023 : };
1024 :
1025 : }
1026 :
1027 72 : void ScColumn::ResetFormulaCellPositions( SCROW nRow1, SCROW nRow2 )
1028 : {
1029 72 : FormulaColPosSetter aFunc(nCol);
1030 72 : sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
1031 72 : }
1032 :
1033 : namespace {
1034 :
1035 72 : class RelativeRefBoundChecker
1036 : {
1037 : std::vector<SCROW> maBounds;
1038 : ScRange maBoundRange;
1039 :
1040 : public:
1041 72 : RelativeRefBoundChecker( const ScRange& rBoundRange ) :
1042 72 : maBoundRange(rBoundRange) {}
1043 :
1044 6 : void operator() ( size_t /*nRow*/, ScFormulaCell* pCell )
1045 : {
1046 6 : if (!pCell->IsSharedTop())
1047 10 : return;
1048 :
1049 : pCell->GetCode()->CheckRelativeReferenceBounds(
1050 2 : pCell->aPos, pCell->GetSharedLength(), maBoundRange, maBounds);
1051 : }
1052 :
1053 72 : void swapBounds( std::vector<SCROW>& rBounds )
1054 : {
1055 72 : rBounds.swap(maBounds);
1056 72 : }
1057 : };
1058 :
1059 : }
1060 :
1061 72 : void ScColumn::SplitFormulaGroupByRelativeRef( const ScRange& rBoundRange )
1062 : {
1063 72 : if (rBoundRange.aStart.Row() >= MAXROW)
1064 : // Nothing to split.
1065 72 : return;
1066 :
1067 72 : std::vector<SCROW> aBounds;
1068 :
1069 : // Cut at row boundaries first.
1070 72 : aBounds.push_back(rBoundRange.aStart.Row());
1071 72 : if (rBoundRange.aEnd.Row() < MAXROW)
1072 42 : aBounds.push_back(rBoundRange.aEnd.Row()+1);
1073 72 : sc::SharedFormulaUtil::splitFormulaCellGroups(maCells, aBounds);
1074 :
1075 144 : RelativeRefBoundChecker aFunc(rBoundRange);
1076 : sc::ProcessFormula(
1077 72 : maCells.begin(), maCells, rBoundRange.aStart.Row(), rBoundRange.aEnd.Row(), aFunc);
1078 72 : aFunc.swapBounds(aBounds);
1079 144 : sc::SharedFormulaUtil::splitFormulaCellGroups(maCells, aBounds);
1080 : }
1081 :
1082 : namespace {
1083 :
1084 : class ListenerCollector
1085 : {
1086 : std::vector<SvtListener*>& mrListeners;
1087 : public:
1088 2280 : ListenerCollector( std::vector<SvtListener*>& rListener ) :
1089 2280 : mrListeners(rListener) {}
1090 :
1091 222 : void operator() ( size_t /*nRow*/, SvtBroadcaster* p )
1092 : {
1093 222 : SvtBroadcaster::ListenersType& rLis = p->GetAllListeners();
1094 222 : std::copy(rLis.begin(), rLis.end(), std::back_inserter(mrListeners));
1095 222 : }
1096 : };
1097 :
1098 : }
1099 :
1100 2280 : void ScColumn::CollectListeners( std::vector<SvtListener*>& rListeners, SCROW nRow1, SCROW nRow2 )
1101 : {
1102 2280 : if (nRow2 < nRow1 || !ValidRow(nRow1) || !ValidRow(nRow2))
1103 2280 : return;
1104 :
1105 2280 : ListenerCollector aFunc(rListeners);
1106 2280 : sc::ProcessBroadcaster(maBroadcasters.begin(), maBroadcasters, nRow1, nRow2, aFunc);
1107 : }
1108 :
1109 : namespace {
1110 :
1111 : struct FindAnyFormula
1112 : {
1113 0 : bool operator() ( size_t /*nRow*/, const ScFormulaCell* /*pCell*/ ) const
1114 : {
1115 0 : return true;
1116 : }
1117 : };
1118 :
1119 : }
1120 :
1121 0 : bool ScColumn::HasFormulaCell( SCROW nRow1, SCROW nRow2 ) const
1122 : {
1123 0 : if (nRow2 < nRow1 || !ValidRow(nRow1) || !ValidRow(nRow2))
1124 0 : return false;
1125 :
1126 : FindAnyFormula aFunc;
1127 : std::pair<sc::CellStoreType::const_iterator, size_t> aRet =
1128 0 : sc::FindFormula(maCells, nRow1, nRow2, aFunc);
1129 :
1130 0 : return aRet.first != maCells.end();
1131 228 : }
1132 :
1133 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|