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 <columniterator.hxx> 18 #include <mtvcellfunc.hxx> 19 #include <clipcontext.hxx> 20 #include <attrib.hxx> 21 #include <patattr.hxx> 22 #include <docpool.hxx> 23 #include <conditio.hxx> 24 #include <formulagroup.hxx> 25 #include <tokenarray.hxx> 26 #include <scitems.hxx> 27 #include <cellform.hxx> 28 #include <sharedformula.hxx> 29 #include <drwlayer.hxx> 30 #include <compiler.hxx> 31 #include <recursionhelper.hxx> 32 #include <docsh.hxx> 33 34 #include <SparklineGroup.hxx> 35 36 #include <o3tl/safeint.hxx> 37 #include <svl/sharedstringpool.hxx> 38 #include <sal/log.hxx> 39 #include <tools/stream.hxx> 40 41 #include <numeric> 42 #include <vector> 43 #include <cassert> 44 45 sc::MultiDataCellState::StateType ScColumn::HasDataCellsInRange( 46 SCROW nRow1, SCROW nRow2, SCROW* pRow1 ) const 47 { 48 sc::CellStoreType::const_position_type aPos = maCells.position(nRow1); 49 sc::CellStoreType::const_iterator it = aPos.first; 50 size_t nOffset = aPos.second; 51 SCROW nRow = nRow1; 52 bool bHasOne = false; // whether or not we have found a non-empty block of size one. 53 54 for (; it != maCells.end() && nRow <= nRow2; ++it) 55 { 56 if (it->type != sc::element_type_empty) 57 { 58 // non-empty block found. 59 assert(it->size > 0); // mtv should never contain a block of zero length. 60 size_t nSize = it->size - nOffset; 61 62 SCROW nLastRow = nRow + nSize - 1; 63 if (nLastRow > nRow2) 64 // shrink the size to avoid exceeding the specified last row position. 65 nSize -= nLastRow - nRow2; 66 67 if (nSize == 1) 68 { 69 // this block is of size one. 70 if (bHasOne) 71 return sc::MultiDataCellState::HasMultipleCells; 72 73 bHasOne = true; 74 if (pRow1) 75 *pRow1 = nRow; 76 } 77 else 78 { 79 // size of this block is greater than one. 80 if (pRow1) 81 *pRow1 = nRow; 82 return sc::MultiDataCellState::HasMultipleCells; 83 } 84 } 85 86 nRow += it->size - nOffset; 87 nOffset = 0; 88 } 89 90 return bHasOne ? sc::MultiDataCellState::HasOneCell : sc::MultiDataCellState::Empty; 91 } 92 93 void ScColumn::DeleteBeforeCopyFromClip( 94 sc::CopyFromClipContext& rCxt, const ScColumn& rClipCol, sc::ColumnSpanSet& rBroadcastSpans ) 95 { 96 ScDocument& rDocument = GetDoc(); 97 sc::CopyFromClipContext::Range aRange = rCxt.getDestRange(); 98 if (!rDocument.ValidRow(aRange.mnRow1) || !rDocument.ValidRow(aRange.mnRow2)) 99 return; 100 101 sc::ColumnBlockPosition* pBlockPos = rCxt.getBlockPosition(nTab, nCol); 102 if (!pBlockPos) 103 return; 104 105 InsertDeleteFlags nDelFlag = rCxt.getDeleteFlag(); 106 107 if (!rCxt.isSkipEmptyCells()) 108 { 109 // Delete the whole destination range. 110 111 if (nDelFlag & InsertDeleteFlags::CONTENTS) 112 { 113 sc::SingleColumnSpanSet aDeletedRows(GetDoc().GetSheetLimits()); 114 DeleteCells(*pBlockPos, aRange.mnRow1, aRange.mnRow2, nDelFlag, aDeletedRows); 115 rBroadcastSpans.set(GetDoc(), nTab, nCol, aDeletedRows, true); 116 } 117 118 if (nDelFlag & InsertDeleteFlags::NOTE) 119 DeleteCellNotes(*pBlockPos, aRange.mnRow1, aRange.mnRow2, false); 120 121 if (nDelFlag & InsertDeleteFlags::SPARKLINES) 122 DeleteSparklineCells(*pBlockPos, aRange.mnRow1, aRange.mnRow2); 123 124 if (nDelFlag & InsertDeleteFlags::EDITATTR) 125 RemoveEditAttribs(*pBlockPos, aRange.mnRow1, aRange.mnRow2); 126 127 if (nDelFlag & InsertDeleteFlags::ATTRIB) 128 { 129 pAttrArray->DeleteArea(aRange.mnRow1, aRange.mnRow2); 130 131 if (rCxt.isTableProtected()) 132 { 133 ScPatternAttr aPattern(rDocument.GetPool()); 134 aPattern.GetItemSet().Put(ScProtectionAttr(false)); 135 ApplyPatternArea(aRange.mnRow1, aRange.mnRow2, aPattern); 136 } 137 138 ScConditionalFormatList* pCondList = rCxt.getCondFormatList(); 139 if (pCondList) 140 pCondList->DeleteArea(nCol, aRange.mnRow1, nCol, aRange.mnRow2); 141 } 142 else if ((nDelFlag & InsertDeleteFlags::HARDATTR) == InsertDeleteFlags::HARDATTR) 143 pAttrArray->DeleteHardAttr(aRange.mnRow1, aRange.mnRow2); 144 145 return; 146 } 147 148 ScRange aClipRange = rCxt.getClipDoc()->GetClipParam().getWholeRange(); 149 SCROW nClipRow1 = aClipRange.aStart.Row(); 150 SCROW nClipRow2 = aClipRange.aEnd.Row(); 151 SCROW nClipRowLen = nClipRow2 - nClipRow1 + 1; 152 153 // Check for non-empty cell ranges in the clip column. 154 sc::SingleColumnSpanSet aSpanSet(GetDoc().GetSheetLimits()); 155 aSpanSet.scan(rClipCol, nClipRow1, nClipRow2); 156 sc::SingleColumnSpanSet::SpansType aSpans; 157 aSpanSet.getSpans(aSpans); 158 159 if (aSpans.empty()) 160 // All cells in the range in the clip are empty. Nothing to delete. 161 return; 162 163 // Translate the clip column spans into the destination column, and repeat as needed. 164 std::vector<sc::RowSpan> aDestSpans; 165 SCROW nDestOffset = aRange.mnRow1 - nClipRow1; 166 bool bContinue = true; 167 while (bContinue) 168 { 169 for (const sc::RowSpan& r : aSpans) 170 { 171 SCROW nDestRow1 = r.mnRow1 + nDestOffset; 172 SCROW nDestRow2 = r.mnRow2 + nDestOffset; 173 174 if (nDestRow1 > aRange.mnRow2) 175 { 176 // We're done. 177 bContinue = false; 178 break; 179 } 180 181 if (nDestRow2 > aRange.mnRow2) 182 { 183 // Truncate this range, and set it as the last span. 184 nDestRow2 = aRange.mnRow2; 185 bContinue = false; 186 } 187 188 aDestSpans.emplace_back(nDestRow1, nDestRow2); 189 190 if (!bContinue) 191 break; 192 } 193 194 nDestOffset += nClipRowLen; 195 } 196 197 for (const auto& rDestSpan : aDestSpans) 198 { 199 SCROW nRow1 = rDestSpan.mnRow1; 200 SCROW nRow2 = rDestSpan.mnRow2; 201 202 if (nDelFlag & InsertDeleteFlags::CONTENTS) 203 { 204 sc::SingleColumnSpanSet aDeletedRows(GetDoc().GetSheetLimits()); 205 DeleteCells(*pBlockPos, nRow1, nRow2, nDelFlag, aDeletedRows); 206 rBroadcastSpans.set(GetDoc(), nTab, nCol, aDeletedRows, true); 207 } 208 209 if (nDelFlag & InsertDeleteFlags::NOTE) 210 DeleteCellNotes(*pBlockPos, nRow1, nRow2, false); 211 212 if (nDelFlag & InsertDeleteFlags::SPARKLINES) 213 DeleteSparklineCells(*pBlockPos, nRow1, nRow2); 214 215 if (nDelFlag & InsertDeleteFlags::EDITATTR) 216 RemoveEditAttribs(*pBlockPos, nRow1, nRow2); 217 218 // Delete attributes just now 219 if (nDelFlag & InsertDeleteFlags::ATTRIB) 220 { 221 pAttrArray->DeleteArea(nRow1, nRow2); 222 223 if (rCxt.isTableProtected()) 224 { 225 ScPatternAttr aPattern(rDocument.GetPool()); 226 aPattern.GetItemSet().Put(ScProtectionAttr(false)); 227 ApplyPatternArea(nRow1, nRow2, aPattern); 228 } 229 230 ScConditionalFormatList* pCondList = rCxt.getCondFormatList(); 231 if (pCondList) 232 pCondList->DeleteArea(nCol, nRow1, nCol, nRow2); 233 } 234 else if ((nDelFlag & InsertDeleteFlags::HARDATTR) == InsertDeleteFlags::HARDATTR) 235 pAttrArray->DeleteHardAttr(nRow1, nRow2); 236 } 237 } 238 239 void ScColumn::CopyOneCellFromClip( sc::CopyFromClipContext& rCxt, SCROW nRow1, SCROW nRow2, size_t nColOffset ) 240 { 241 assert(nRow1 <= nRow2); 242 243 size_t nDestSize = nRow2 - nRow1 + 1; 244 sc::ColumnBlockPosition* pBlockPos = rCxt.getBlockPosition(nTab, nCol); 245 if (!pBlockPos) 246 return; 247 248 ScDocument& rDocument = GetDoc(); 249 bool bSameDocPool = (rCxt.getClipDoc()->GetPool() == rDocument.GetPool()); 250 251 ScCellValue& rSrcCell = rCxt.getSingleCell(nColOffset); 252 sc::CellTextAttr& rSrcAttr = rCxt.getSingleCellAttr(nColOffset); 253 254 InsertDeleteFlags nFlags = rCxt.getInsertFlag(); 255 256 if ((nFlags & InsertDeleteFlags::ATTRIB) != InsertDeleteFlags::NONE) 257 { 258 if (!rCxt.isSkipEmptyCells() || rSrcCell.getType() != CELLTYPE_NONE) 259 { 260 const ScPatternAttr* pAttr = (bSameDocPool ? rCxt.getSingleCellPattern(nColOffset) : 261 rCxt.getSingleCellPattern(nColOffset)->PutInPool( &rDocument, rCxt.getClipDoc())); 262 263 auto pNewPattern = std::make_unique<ScPatternAttr>(*pAttr); 264 sal_uInt16 pItems[2]; 265 pItems[0] = ATTR_CONDITIONAL; 266 pItems[1] = 0; 267 pNewPattern->ClearItems(pItems); 268 pAttrArray->SetPatternArea(nRow1, nRow2, std::move(pNewPattern), true); 269 } 270 } 271 272 if ((nFlags & InsertDeleteFlags::CONTENTS) != InsertDeleteFlags::NONE) 273 { 274 std::vector<sc::CellTextAttr> aTextAttrs(nDestSize, rSrcAttr); 275 276 switch (rSrcCell.getType()) 277 { 278 case CELLTYPE_VALUE: 279 { 280 std::vector<double> aVals(nDestSize, rSrcCell.getDouble()); 281 pBlockPos->miCellPos = 282 maCells.set(pBlockPos->miCellPos, nRow1, aVals.begin(), aVals.end()); 283 pBlockPos->miCellTextAttrPos = 284 maCellTextAttrs.set(pBlockPos->miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end()); 285 CellStorageModified(); 286 } 287 break; 288 case CELLTYPE_STRING: 289 { 290 // Compare the ScDocumentPool* to determine if we are copying within the 291 // same document. If not, re-intern shared strings. 292 svl::SharedStringPool* pSharedStringPool = (bSameDocPool ? nullptr : &rDocument.GetSharedStringPool()); 293 svl::SharedString aStr = (pSharedStringPool ? 294 pSharedStringPool->intern( rSrcCell.getSharedString()->getString()) : 295 *rSrcCell.getSharedString()); 296 297 std::vector<svl::SharedString> aStrs(nDestSize, aStr); 298 pBlockPos->miCellPos = 299 maCells.set(pBlockPos->miCellPos, nRow1, aStrs.begin(), aStrs.end()); 300 pBlockPos->miCellTextAttrPos = 301 maCellTextAttrs.set(pBlockPos->miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end()); 302 CellStorageModified(); 303 } 304 break; 305 case CELLTYPE_EDIT: 306 { 307 std::vector<EditTextObject*> aStrs; 308 aStrs.reserve(nDestSize); 309 for (size_t i = 0; i < nDestSize; ++i) 310 aStrs.push_back(rSrcCell.getEditText()->Clone().release()); 311 312 pBlockPos->miCellPos = 313 maCells.set(pBlockPos->miCellPos, nRow1, aStrs.begin(), aStrs.end()); 314 pBlockPos->miCellTextAttrPos = 315 maCellTextAttrs.set(pBlockPos->miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end()); 316 CellStorageModified(); 317 } 318 break; 319 case CELLTYPE_FORMULA: 320 { 321 std::vector<sc::RowSpan> aRanges; 322 aRanges.reserve(1); 323 aRanges.emplace_back(nRow1, nRow2); 324 CloneFormulaCell(*pBlockPos, *rSrcCell.getFormula(), rSrcAttr, aRanges); 325 } 326 break; 327 default: 328 ; 329 } 330 } 331 332 ScAddress aDestPosition(nCol, nRow1, nTab); 333 334 duplicateSparkline(rCxt, pBlockPos, nColOffset, nDestSize, aDestPosition); 335 336 // Notes 337 const ScPostIt* pNote = rCxt.getSingleCellNote(nColOffset); 338 if (!(pNote && (nFlags & (InsertDeleteFlags::NOTE | InsertDeleteFlags::ADDNOTES)) != InsertDeleteFlags::NONE)) 339 return; 340 341 // Duplicate the cell note over the whole pasted range. 342 343 ScDocument* pClipDoc = rCxt.getClipDoc(); 344 const ScAddress aSrcPos = pClipDoc->GetClipParam().getWholeRange().aStart; 345 std::vector<ScPostIt*> aNotes; 346 aNotes.reserve(nDestSize); 347 for (size_t i = 0; i < nDestSize; ++i) 348 { 349 bool bCloneCaption = (nFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE; 350 aNotes.push_back(pNote->Clone(aSrcPos, rDocument, aDestPosition, bCloneCaption).release()); 351 aDestPosition.IncRow(); 352 } 353 354 pBlockPos->miCellNotePos = 355 maCellNotes.set( 356 pBlockPos->miCellNotePos, nRow1, aNotes.begin(), aNotes.end()); 357 358 // Notify our LOK clients. 359 aDestPosition.SetRow(nRow1); 360 for (size_t i = 0; i < nDestSize; ++i) 361 { 362 ScDocShell::LOKCommentNotify(LOKCommentNotificationType::Add, &rDocument, aDestPosition, aNotes[i]); 363 aDestPosition.IncRow(); 364 } 365 } 366 367 void ScColumn::duplicateSparkline(sc::CopyFromClipContext& rContext, sc::ColumnBlockPosition* pBlockPos, 368 size_t nColOffset, size_t nDestSize, ScAddress aDestPosition) 369 { 370 if ((rContext.getInsertFlag() & InsertDeleteFlags::SPARKLINES) == InsertDeleteFlags::NONE) 371 return; 372 373 auto pSparkline = rContext.getSingleSparkline(nColOffset); 374 if (pSparkline) 375 { 376 auto const& pSparklineGroup = pSparkline->getSparklineGroup(); 377 378 auto pDuplicatedGroup = GetDoc().SearchSparklineGroup(pSparklineGroup->getID()); 379 if (!pDuplicatedGroup) 380 pDuplicatedGroup = std::make_shared<sc::SparklineGroup>(*pSparklineGroup); 381 382 std::vector<sc::SparklineCell*> aSparklines(nDestSize, nullptr); 383 ScAddress aCurrentPosition = aDestPosition; 384 for (size_t i = 0; i < nDestSize; ++i) 385 { 386 auto pNewSparkline = std::make_shared<sc::Sparkline>(aCurrentPosition.Col(), aCurrentPosition.Row(), pDuplicatedGroup); 387 pNewSparkline->setInputRange(pSparkline->getInputRange()); 388 aSparklines[i] = new sc::SparklineCell(pNewSparkline); 389 aCurrentPosition.IncRow(); 390 } 391 392 pBlockPos->miSparklinePos = maSparklines.set(pBlockPos->miSparklinePos, aDestPosition.Row(), aSparklines.begin(), aSparklines.end()); 393 } 394 } 395 396 void ScColumn::SetValues( const SCROW nRow, const std::vector<double>& rVals ) 397 { 398 if (!GetDoc().ValidRow(nRow)) 399 return; 400 401 SCROW nLastRow = nRow + rVals.size() - 1; 402 if (nLastRow > GetDoc().MaxRow()) 403 // Out of bound. Do nothing. 404 return; 405 406 sc::CellStoreType::position_type aPos = maCells.position(nRow); 407 std::vector<SCROW> aNewSharedRows; 408 DetachFormulaCells(aPos, rVals.size(), &aNewSharedRows); 409 410 maCells.set(nRow, rVals.begin(), rVals.end()); 411 std::vector<sc::CellTextAttr> aDefaults(rVals.size()); 412 maCellTextAttrs.set(nRow, aDefaults.begin(), aDefaults.end()); 413 414 CellStorageModified(); 415 416 StartListeningUnshared( aNewSharedRows); 417 418 std::vector<SCROW> aRows; 419 aRows.reserve(rVals.size()); 420 for (SCROW i = nRow; i <= nLastRow; ++i) 421 aRows.push_back(i); 422 423 BroadcastCells(aRows, SfxHintId::ScDataChanged); 424 } 425 426 void ScColumn::TransferCellValuesTo( SCROW nRow, size_t nLen, sc::CellValues& rDest ) 427 { 428 if (!GetDoc().ValidRow(nRow)) 429 return; 430 431 SCROW nLastRow = nRow + nLen - 1; 432 if (nLastRow > GetDoc().MaxRow()) 433 // Out of bound. Do nothing. 434 return; 435 436 sc::CellStoreType::position_type aPos = maCells.position(nRow); 437 DetachFormulaCells(aPos, nLen, nullptr); 438 439 rDest.transferFrom(*this, nRow, nLen); 440 441 CellStorageModified(); 442 443 std::vector<SCROW> aRows; 444 aRows.reserve(nLen); 445 for (SCROW i = nRow; i <= nLastRow; ++i) 446 aRows.push_back(i); 447 448 BroadcastCells(aRows, SfxHintId::ScDataChanged); 449 } 450 451 void ScColumn::CopyCellValuesFrom( SCROW nRow, const sc::CellValues& rSrc ) 452 { 453 if (!GetDoc().ValidRow(nRow)) 454 return; 455 456 SCROW nLastRow = nRow + rSrc.size() - 1; 457 if (nLastRow > GetDoc().MaxRow()) 458 // Out of bound. Do nothing 459 return; 460 461 sc::CellStoreType::position_type aPos = maCells.position(nRow); 462 DetachFormulaCells(aPos, rSrc.size(), nullptr); 463 464 rSrc.copyTo(*this, nRow); 465 466 CellStorageModified(); 467 468 std::vector<SCROW> aRows; 469 aRows.reserve(rSrc.size()); 470 for (SCROW i = nRow; i <= nLastRow; ++i) 471 aRows.push_back(i); 472 473 BroadcastCells(aRows, SfxHintId::ScDataChanged); 474 } 475 476 namespace { 477 478 class ConvertFormulaToValueHandler 479 { 480 sc::CellValues maResValues; 481 bool mbModified; 482 483 public: 484 ConvertFormulaToValueHandler(ScSheetLimits const & rSheetLimits) : 485 mbModified(false) 486 { 487 maResValues.reset(rSheetLimits.GetMaxRowCount()); 488 } 489 490 void operator() ( size_t nRow, const ScFormulaCell* pCell ) 491 { 492 sc::FormulaResultValue aRes = pCell->GetResult(); 493 switch (aRes.meType) 494 { 495 case sc::FormulaResultValue::Value: 496 maResValues.setValue(nRow, aRes.mfValue); 497 break; 498 case sc::FormulaResultValue::String: 499 maResValues.setValue(nRow, aRes.maString); 500 break; 501 case sc::FormulaResultValue::Error: 502 case sc::FormulaResultValue::Invalid: 503 default: 504 maResValues.setValue(nRow, svl::SharedString::getEmptyString()); 505 } 506 507 mbModified = true; 508 } 509 510 bool isModified() const { return mbModified; } 511 512 sc::CellValues& getResValues() { return maResValues; } 513 }; 514 515 } 516 517 void ScColumn::ConvertFormulaToValue( 518 sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2, sc::TableValues* pUndo ) 519 { 520 if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2) 521 return; 522 523 std::vector<SCROW> aBounds { nRow1 }; 524 if (nRow2 < GetDoc().MaxRow()-1) 525 aBounds.push_back(nRow2+1); 526 527 // Split formula cell groups at top and bottom boundaries (if applicable). 528 sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells, aBounds); 529 530 // Parse all formulas within the range and store their results into temporary storage. 531 ConvertFormulaToValueHandler aFunc(GetDoc().GetSheetLimits()); 532 sc::ParseFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc); 533 if (!aFunc.isModified()) 534 // No formula cells encountered. 535 return; 536 537 DetachFormulaCells(rCxt, nRow1, nRow2); 538 539 // Undo storage to hold static values which will get swapped to the cell storage later. 540 sc::CellValues aUndoCells; 541 aFunc.getResValues().swap(aUndoCells); 542 aUndoCells.swapNonEmpty(*this); 543 if (pUndo) 544 pUndo->swap(nTab, nCol, aUndoCells); 545 } 546 547 namespace { 548 549 class StartListeningHandler 550 { 551 sc::StartListeningContext& mrCxt; 552 553 public: 554 explicit StartListeningHandler( sc::StartListeningContext& rCxt ) : 555 mrCxt(rCxt) {} 556 557 void operator() (size_t /*nRow*/, ScFormulaCell* pCell) 558 { 559 pCell->StartListeningTo(mrCxt); 560 } 561 }; 562 563 class EndListeningHandler 564 { 565 sc::EndListeningContext& mrCxt; 566 567 public: 568 explicit EndListeningHandler( sc::EndListeningContext& rCxt ) : 569 mrCxt(rCxt) {} 570 571 void operator() (size_t /*nRow*/, ScFormulaCell* pCell) 572 { 573 pCell->EndListeningTo(mrCxt); 574 } 575 }; 576 577 } 578 579 void ScColumn::SwapNonEmpty( 580 sc::TableValues& rValues, sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt ) 581 { 582 const ScRange& rRange = rValues.getRange(); 583 std::vector<SCROW> aBounds { rRange.aStart.Row() }; 584 if (rRange.aEnd.Row() < GetDoc().MaxRow()-1) 585 aBounds.push_back(rRange.aEnd.Row()+1); 586 587 // Split formula cell groups at top and bottom boundaries (if applicable). 588 sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells, aBounds); 589 std::vector<sc::CellValueSpan> aSpans = rValues.getNonEmptySpans(nTab, nCol); 590 591 // Detach formula cells within the spans (if any). 592 EndListeningHandler aEndLisFunc(rEndCxt); 593 sc::CellStoreType::iterator itPos = maCells.begin(); 594 for (const auto& rSpan : aSpans) 595 { 596 SCROW nRow1 = rSpan.mnRow1; 597 SCROW nRow2 = rSpan.mnRow2; 598 itPos = sc::ProcessFormula(itPos, maCells, nRow1, nRow2, aEndLisFunc); 599 } 600 601 rValues.swapNonEmpty(nTab, nCol, *this); 602 RegroupFormulaCells(); 603 604 // Attach formula cells within the spans (if any). 605 StartListeningHandler aStartLisFunc(rStartCxt); 606 itPos = maCells.begin(); 607 for (const auto& rSpan : aSpans) 608 { 609 SCROW nRow1 = rSpan.mnRow1; 610 SCROW nRow2 = rSpan.mnRow2; 611 itPos = sc::ProcessFormula(itPos, maCells, nRow1, nRow2, aStartLisFunc); 612 } 613 614 CellStorageModified(); 615 } 616 617 void ScColumn::DeleteRanges( const std::vector<sc::RowSpan>& rRanges, InsertDeleteFlags nDelFlag ) 618 { 619 for (const auto& rSpan : rRanges) 620 DeleteArea(rSpan.mnRow1, rSpan.mnRow2, nDelFlag, false/*bBroadcast*/); 621 } 622 623 void ScColumn::CloneFormulaCell( 624 sc::ColumnBlockPosition& rBlockPos, 625 const ScFormulaCell& rSrc, const sc::CellTextAttr& rAttr, 626 const std::vector<sc::RowSpan>& rRanges ) 627 { 628 SCCOL nMatrixCols = 0; 629 SCROW nMatrixRows = 0; 630 ScMatrixMode nMatrixFlag = rSrc.GetMatrixFlag(); 631 if (nMatrixFlag == ScMatrixMode::Formula) 632 { 633 rSrc.GetMatColsRows( nMatrixCols, nMatrixRows); 634 SAL_WARN_IF( nMatrixCols != 1 || nMatrixRows != 1, "sc.core", 635 "ScColumn::CloneFormulaCell - cloning array/matrix with not exactly one column or row as single cell"); 636 } 637 638 ScDocument& rDocument = GetDoc(); 639 std::vector<ScFormulaCell*> aFormulas; 640 for (const auto& rSpan : rRanges) 641 { 642 SCROW nRow1 = rSpan.mnRow1, nRow2 = rSpan.mnRow2; 643 size_t nLen = nRow2 - nRow1 + 1; 644 assert(nLen > 0); 645 aFormulas.clear(); 646 aFormulas.reserve(nLen); 647 648 ScAddress aPos(nCol, nRow1, nTab); 649 650 if (nLen == 1 || !rSrc.GetCode()->IsShareable()) 651 { 652 // Single, ungrouped formula cell, or create copies for 653 // non-shareable token arrays. 654 for (size_t i = 0; i < nLen; ++i, aPos.IncRow()) 655 { 656 ScFormulaCell* pCell = new ScFormulaCell(rSrc, rDocument, aPos); 657 aFormulas.push_back(pCell); 658 } 659 } 660 else 661 { 662 // Create a group of formula cells. 663 ScFormulaCellGroupRef xGroup(new ScFormulaCellGroup); 664 xGroup->setCode(*rSrc.GetCode()); 665 xGroup->compileCode(rDocument, aPos, rDocument.GetGrammar()); 666 for (size_t i = 0; i < nLen; ++i, aPos.IncRow()) 667 { 668 ScFormulaCell* pCell = new ScFormulaCell(rDocument, aPos, xGroup, rDocument.GetGrammar(), nMatrixFlag); 669 if (nMatrixFlag == ScMatrixMode::Formula) 670 pCell->SetMatColsRows( nMatrixCols, nMatrixRows); 671 if (i == 0) 672 { 673 xGroup->mpTopCell = pCell; 674 xGroup->mnLength = nLen; 675 } 676 aFormulas.push_back(pCell); 677 } 678 } 679 680 rBlockPos.miCellPos = maCells.set(rBlockPos.miCellPos, nRow1, aFormulas.begin(), aFormulas.end()); 681 682 // Join the top and bottom of the pasted formula cells as needed. 683 sc::CellStoreType::position_type aPosObj = maCells.position(rBlockPos.miCellPos, nRow1); 684 685 assert(aPosObj.first->type == sc::element_type_formula); 686 ScFormulaCell* pCell = sc::formula_block::at(*aPosObj.first->data, aPosObj.second); 687 JoinNewFormulaCell(aPosObj, *pCell); 688 689 aPosObj = maCells.position(aPosObj.first, nRow2); 690 assert(aPosObj.first->type == sc::element_type_formula); 691 pCell = sc::formula_block::at(*aPosObj.first->data, aPosObj.second); 692 JoinNewFormulaCell(aPosObj, *pCell); 693 694 std::vector<sc::CellTextAttr> aTextAttrs(nLen, rAttr); 695 rBlockPos.miCellTextAttrPos = maCellTextAttrs.set( 696 rBlockPos.miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end()); 697 } 698 699 CellStorageModified(); 700 } 701 702 void ScColumn::CloneFormulaCell( 703 const ScFormulaCell& rSrc, const sc::CellTextAttr& rAttr, 704 const std::vector<sc::RowSpan>& rRanges ) 705 { 706 sc::ColumnBlockPosition aBlockPos; 707 InitBlockPosition(aBlockPos); 708 CloneFormulaCell(aBlockPos, rSrc, rAttr, rRanges); 709 } 710 711 std::unique_ptr<ScPostIt> ScColumn::ReleaseNote( SCROW nRow ) 712 { 713 if (!GetDoc().ValidRow(nRow)) 714 return nullptr; 715 716 ScPostIt* p = nullptr; 717 maCellNotes.release(nRow, p); 718 return std::unique_ptr<ScPostIt>(p); 719 } 720 721 size_t ScColumn::GetNoteCount() const 722 { 723 return std::accumulate(maCellNotes.begin(), maCellNotes.end(), size_t(0), 724 [](const size_t& rCount, const auto& rCellNote) { 725 if (rCellNote.type != sc::element_type_cellnote) 726 return rCount; 727 return rCount + rCellNote.size; 728 }); 729 } 730 731 namespace { 732 733 class NoteCaptionCreator 734 { 735 ScAddress maPos; 736 public: 737 NoteCaptionCreator( SCTAB nTab, SCCOL nCol ) : maPos(nCol,0,nTab) {} 738 739 void operator() ( size_t nRow, const ScPostIt* p ) 740 { 741 maPos.SetRow(nRow); 742 p->GetOrCreateCaption(maPos); 743 } 744 }; 745 746 class NoteCaptionCleaner 747 { 748 bool mbPreserveData; 749 public: 750 explicit NoteCaptionCleaner( bool bPreserveData ) : mbPreserveData(bPreserveData) {} 751 752 void operator() ( size_t /*nRow*/, ScPostIt* p ) 753 { 754 p->ForgetCaption(mbPreserveData); 755 } 756 }; 757 758 } 759 760 void ScColumn::CreateAllNoteCaptions() 761 { 762 NoteCaptionCreator aFunc(nTab, nCol); 763 sc::ProcessNote(maCellNotes, aFunc); 764 } 765 766 void ScColumn::ForgetNoteCaptions( SCROW nRow1, SCROW nRow2, bool bPreserveData ) 767 { 768 if (maCellNotes.empty()) 769 return; 770 771 if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2)) 772 return; 773 774 NoteCaptionCleaner aFunc(bPreserveData); 775 sc::CellNoteStoreType::iterator it = maCellNotes.begin(); 776 sc::ProcessNote(it, maCellNotes, nRow1, nRow2, aFunc); 777 } 778 779 SCROW ScColumn::GetNotePosition( size_t nIndex ) const 780 { 781 // Return the row position of the nth note in the column. 782 783 size_t nCount = 0; // Number of notes encountered so far. 784 for (const auto& rCellNote : maCellNotes) 785 { 786 if (rCellNote.type != sc::element_type_cellnote) 787 // Skip the empty blocks. 788 continue; 789 790 if (nIndex < nCount + rCellNote.size) 791 { 792 // Index falls within this block. 793 size_t nOffset = nIndex - nCount; 794 return rCellNote.position + nOffset; 795 } 796 797 nCount += rCellNote.size; 798 } 799 800 return -1; 801 } 802 803 namespace { 804 805 class NoteEntryCollector 806 { 807 std::vector<sc::NoteEntry>& mrNotes; 808 SCTAB mnTab; 809 SCCOL mnCol; 810 SCROW mnStartRow; 811 SCROW mnEndRow; 812 public: 813 NoteEntryCollector( std::vector<sc::NoteEntry>& rNotes, SCTAB nTab, SCCOL nCol, 814 SCROW nStartRow, SCROW nEndRow) : 815 mrNotes(rNotes), mnTab(nTab), mnCol(nCol), 816 mnStartRow(nStartRow), mnEndRow(nEndRow) {} 817 818 void operator() (const sc::CellNoteStoreType::value_type& node) const 819 { 820 if (node.type != sc::element_type_cellnote) 821 return; 822 823 size_t nTopRow = node.position; 824 sc::cellnote_block::const_iterator it = sc::cellnote_block::begin(*node.data); 825 sc::cellnote_block::const_iterator itEnd = sc::cellnote_block::end(*node.data); 826 size_t nOffset = 0; 827 if(nTopRow < o3tl::make_unsigned(mnStartRow)) 828 { 829 std::advance(it, mnStartRow - nTopRow); 830 nOffset = mnStartRow - nTopRow; 831 } 832 833 for (; it != itEnd && nTopRow + nOffset <= o3tl::make_unsigned(mnEndRow); 834 ++it, ++nOffset) 835 { 836 ScAddress aPos(mnCol, nTopRow + nOffset, mnTab); 837 mrNotes.emplace_back(aPos, *it); 838 } 839 } 840 }; 841 842 } 843 844 void ScColumn::GetAllNoteEntries( std::vector<sc::NoteEntry>& rNotes ) const 845 { 846 std::for_each(maCellNotes.begin(), maCellNotes.end(), NoteEntryCollector(rNotes, nTab, nCol, 0, GetDoc().MaxRow())); 847 } 848 849 void ScColumn::GetNotesInRange(SCROW nStartRow, SCROW nEndRow, 850 std::vector<sc::NoteEntry>& rNotes ) const 851 { 852 std::pair<sc::CellNoteStoreType::const_iterator,size_t> aPos = maCellNotes.position(nStartRow); 853 sc::CellNoteStoreType::const_iterator it = aPos.first; 854 if (it == maCellNotes.end()) 855 // Invalid row number. 856 return; 857 858 std::pair<sc::CellNoteStoreType::const_iterator,size_t> aEndPos = 859 maCellNotes.position(nEndRow); 860 sc::CellNoteStoreType::const_iterator itEnd = aEndPos.first; 861 862 std::for_each(it, ++itEnd, NoteEntryCollector(rNotes, nTab, nCol, nStartRow, nEndRow)); 863 } 864 865 bool ScColumn::HasCellNote(SCROW nStartRow, SCROW nEndRow) const 866 { 867 std::pair<sc::CellNoteStoreType::const_iterator,size_t> aStartPos = 868 maCellNotes.position(nStartRow); 869 if (aStartPos.first == maCellNotes.end()) 870 // Invalid row number. 871 return false; 872 873 std::pair<sc::CellNoteStoreType::const_iterator,size_t> aEndPos = 874 maCellNotes.position(nEndRow); 875 876 for (sc::CellNoteStoreType::const_iterator it = aStartPos.first; it != aEndPos.first; ++it) 877 { 878 if (it->type != sc::element_type_cellnote) 879 continue; 880 size_t nTopRow = it->position; 881 sc::cellnote_block::const_iterator blockIt = sc::cellnote_block::begin(*(it->data)); 882 sc::cellnote_block::const_iterator blockItEnd = sc::cellnote_block::end(*(it->data)); 883 size_t nOffset = 0; 884 if(nTopRow < o3tl::make_unsigned(nStartRow)) 885 { 886 std::advance(blockIt, nStartRow - nTopRow); 887 nOffset = nStartRow - nTopRow; 888 } 889 890 if (blockIt != blockItEnd && nTopRow + nOffset <= o3tl::make_unsigned(nEndRow)) 891 return true; 892 } 893 894 return false; 895 } 896 897 void ScColumn::GetUnprotectedCells( SCROW nStartRow, SCROW nEndRow, ScRangeList& rRangeList ) const 898 { 899 SCROW nTmpStartRow = nStartRow, nTmpEndRow = nEndRow; 900 const ScPatternAttr* pPattern = pAttrArray->GetPatternRange(nTmpStartRow, nTmpEndRow, nStartRow); 901 bool bProtection = pPattern->GetItem(ATTR_PROTECTION).GetProtection(); 902 if (!bProtection) 903 { 904 // Limit the span to the range in question. 905 if (nTmpStartRow < nStartRow) 906 nTmpStartRow = nStartRow; 907 if (nTmpEndRow > nEndRow) 908 nTmpEndRow = nEndRow; 909 rRangeList.Join( ScRange( nCol, nTmpStartRow, nTab, nCol, nTmpEndRow, nTab)); 910 } 911 while (nEndRow > nTmpEndRow) 912 { 913 nStartRow = nTmpEndRow + 1; 914 pPattern = pAttrArray->GetPatternRange(nTmpStartRow, nTmpEndRow, nStartRow); 915 bool bTmpProtection = pPattern->GetItem(ATTR_PROTECTION).GetProtection(); 916 if (!bTmpProtection) 917 { 918 // Limit the span to the range in question. 919 // Only end row needs to be checked as we enter here only for spans 920 // below the original nStartRow. 921 if (nTmpEndRow > nEndRow) 922 nTmpEndRow = nEndRow; 923 rRangeList.Join( ScRange( nCol, nTmpStartRow, nTab, nCol, nTmpEndRow, nTab)); 924 } 925 } 926 } 927 928 namespace { 929 930 class RecompileByOpcodeHandler 931 { 932 ScDocument* mpDoc; 933 const formula::unordered_opcode_set& mrOps; 934 sc::EndListeningContext& mrEndListenCxt; 935 sc::CompileFormulaContext& mrCompileFormulaCxt; 936 937 public: 938 RecompileByOpcodeHandler( 939 ScDocument* pDoc, const formula::unordered_opcode_set& rOps, 940 sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt ) : 941 mpDoc(pDoc), 942 mrOps(rOps), 943 mrEndListenCxt(rEndListenCxt), 944 mrCompileFormulaCxt(rCompileCxt) {} 945 946 void operator() ( sc::FormulaGroupEntry& rEntry ) 947 { 948 // Perform end listening, remove from formula tree, and set them up 949 // for re-compilation. 950 951 ScFormulaCell* pTop = nullptr; 952 953 if (rEntry.mbShared) 954 { 955 // Only inspect the code from the top cell. 956 pTop = *rEntry.mpCells; 957 } 958 else 959 pTop = rEntry.mpCell; 960 961 ScTokenArray* pCode = pTop->GetCode(); 962 bool bRecompile = pCode->HasOpCodes(mrOps); 963 964 if (!bRecompile) 965 return; 966 967 // Get the formula string. 968 OUString aFormula = pTop->GetFormula(mrCompileFormulaCxt); 969 sal_Int32 n = aFormula.getLength(); 970 if (pTop->GetMatrixFlag() != ScMatrixMode::NONE && n > 0) 971 { 972 if (aFormula[0] == '{' && aFormula[n-1] == '}') 973 aFormula = aFormula.copy(1, n-2); 974 } 975 976 if (rEntry.mbShared) 977 { 978 ScFormulaCell** pp = rEntry.mpCells; 979 ScFormulaCell** ppEnd = pp + rEntry.mnLength; 980 for (; pp != ppEnd; ++pp) 981 { 982 ScFormulaCell* p = *pp; 983 p->EndListeningTo(mrEndListenCxt); 984 mpDoc->RemoveFromFormulaTree(p); 985 } 986 } 987 else 988 { 989 rEntry.mpCell->EndListeningTo(mrEndListenCxt); 990 mpDoc->RemoveFromFormulaTree(rEntry.mpCell); 991 } 992 993 pCode->Clear(); 994 pTop->SetHybridFormula(aFormula, mpDoc->GetGrammar()); 995 } 996 }; 997 998 class CompileHybridFormulaHandler 999 { 1000 ScDocument& mrDoc; 1001 sc::StartListeningContext& mrStartListenCxt; 1002 sc::CompileFormulaContext& mrCompileFormulaCxt; 1003 1004 public: 1005 CompileHybridFormulaHandler(ScDocument& rDoc, sc::StartListeningContext& rStartListenCxt, sc::CompileFormulaContext& rCompileCxt ) : 1006 mrDoc(rDoc), 1007 mrStartListenCxt(rStartListenCxt), 1008 mrCompileFormulaCxt(rCompileCxt) {} 1009 1010 void operator() ( sc::FormulaGroupEntry& rEntry ) 1011 { 1012 if (rEntry.mbShared) 1013 { 1014 ScFormulaCell* pTop = *rEntry.mpCells; 1015 OUString aFormula = pTop->GetHybridFormula(); 1016 1017 if (!aFormula.isEmpty()) 1018 { 1019 // Create a new token array from the hybrid formula string, and 1020 // set it to the group. 1021 ScCompiler aComp(mrCompileFormulaCxt, pTop->aPos); 1022 std::unique_ptr<ScTokenArray> pNewCode = aComp.CompileString(aFormula); 1023 ScFormulaCellGroupRef xGroup = pTop->GetCellGroup(); 1024 assert(xGroup); 1025 xGroup->setCode(std::move(*pNewCode)); 1026 xGroup->compileCode(mrDoc, pTop->aPos, mrDoc.GetGrammar()); 1027 1028 // Propagate the new token array to all formula cells in the group. 1029 ScFormulaCell** pp = rEntry.mpCells; 1030 ScFormulaCell** ppEnd = pp + rEntry.mnLength; 1031 for (; pp != ppEnd; ++pp) 1032 { 1033 ScFormulaCell* p = *pp; 1034 p->SyncSharedCode(); 1035 p->StartListeningTo(mrStartListenCxt); 1036 p->SetDirty(); 1037 } 1038 } 1039 } 1040 else 1041 { 1042 ScFormulaCell* pCell = rEntry.mpCell; 1043 OUString aFormula = pCell->GetHybridFormula(); 1044 1045 if (!aFormula.isEmpty()) 1046 { 1047 // Create token array from formula string. 1048 ScCompiler aComp(mrCompileFormulaCxt, pCell->aPos); 1049 std::unique_ptr<ScTokenArray> pNewCode = aComp.CompileString(aFormula); 1050 1051 // Generate RPN tokens. 1052 ScCompiler aComp2(mrDoc, pCell->aPos, *pNewCode, formula::FormulaGrammar::GRAM_UNSPECIFIED, 1053 true, pCell->GetMatrixFlag() != ScMatrixMode::NONE); 1054 aComp2.CompileTokenArray(); 1055 1056 pCell->SetCode(std::move(pNewCode)); 1057 pCell->StartListeningTo(mrStartListenCxt); 1058 pCell->SetDirty(); 1059 } 1060 } 1061 } 1062 }; 1063 1064 } 1065 1066 void ScColumn::PreprocessRangeNameUpdate( 1067 sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt ) 1068 { 1069 // Collect all formula groups. 1070 std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries(); 1071 1072 formula::unordered_opcode_set aOps; 1073 aOps.insert(ocBad); 1074 aOps.insert(ocColRowName); 1075 aOps.insert(ocName); 1076 RecompileByOpcodeHandler aFunc(&GetDoc(), aOps, rEndListenCxt, rCompileCxt); 1077 std::for_each(aGroups.begin(), aGroups.end(), aFunc); 1078 } 1079 1080 void ScColumn::PreprocessDBDataUpdate( 1081 sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt ) 1082 { 1083 // Collect all formula groups. 1084 std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries(); 1085 1086 formula::unordered_opcode_set aOps; 1087 aOps.insert(ocBad); 1088 aOps.insert(ocColRowName); 1089 aOps.insert(ocDBArea); 1090 aOps.insert(ocTableRef); 1091 RecompileByOpcodeHandler aFunc(&GetDoc(), aOps, rEndListenCxt, rCompileCxt); 1092 std::for_each(aGroups.begin(), aGroups.end(), aFunc); 1093 } 1094 1095 void ScColumn::CompileHybridFormula( 1096 sc::StartListeningContext& rStartListenCxt, sc::CompileFormulaContext& rCompileCxt ) 1097 { 1098 // Collect all formula groups. 1099 std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries(); 1100 1101 CompileHybridFormulaHandler aFunc(GetDoc(), rStartListenCxt, rCompileCxt); 1102 std::for_each(aGroups.begin(), aGroups.end(), aFunc); 1103 } 1104 1105 namespace { 1106 1107 class ScriptTypeUpdater 1108 { 1109 ScColumn& mrCol; 1110 sc::CellTextAttrStoreType& mrTextAttrs; 1111 sc::CellTextAttrStoreType::iterator miPosAttr; 1112 ScConditionalFormatList* mpCFList; 1113 SvNumberFormatter* mpFormatter; 1114 ScAddress maPos; 1115 bool mbUpdated; 1116 1117 private: 1118 void updateScriptType( size_t nRow, ScRefCellValue& rCell ) 1119 { 1120 sc::CellTextAttrStoreType::position_type aAttrPos = mrTextAttrs.position(miPosAttr, nRow); 1121 miPosAttr = aAttrPos.first; 1122 1123 if (aAttrPos.first->type != sc::element_type_celltextattr) 1124 return; 1125 1126 sc::CellTextAttr& rAttr = sc::celltextattr_block::at(*aAttrPos.first->data, aAttrPos.second); 1127 if (rAttr.mnScriptType != SvtScriptType::UNKNOWN) 1128 // Script type already determined. Skip it. 1129 return; 1130 1131 const ScPatternAttr* pPat = mrCol.GetPattern(nRow); 1132 if (!pPat) 1133 // In theory this should never return NULL. But let's be safe. 1134 return; 1135 1136 const SfxItemSet* pCondSet = nullptr; 1137 if (mpCFList) 1138 { 1139 maPos.SetRow(nRow); 1140 const ScCondFormatItem& rItem = pPat->GetItem(ATTR_CONDITIONAL); 1141 const ScCondFormatIndexes& rData = rItem.GetCondFormatData(); 1142 pCondSet = mrCol.GetDoc().GetCondResult(rCell, maPos, *mpCFList, rData); 1143 } 1144 1145 const Color* pColor; 1146 sal_uInt32 nFormat = pPat->GetNumberFormat(mpFormatter, pCondSet); 1147 OUString aStr = ScCellFormat::GetString(rCell, nFormat, &pColor, *mpFormatter, mrCol.GetDoc()); 1148 1149 rAttr.mnScriptType = mrCol.GetDoc().GetStringScriptType(aStr); 1150 mbUpdated = true; 1151 } 1152 1153 public: 1154 explicit ScriptTypeUpdater( ScColumn& rCol ) : 1155 mrCol(rCol), 1156 mrTextAttrs(rCol.GetCellAttrStore()), 1157 miPosAttr(mrTextAttrs.begin()), 1158 mpCFList(rCol.GetDoc().GetCondFormList(rCol.GetTab())), 1159 mpFormatter(rCol.GetDoc().GetFormatTable()), 1160 maPos(rCol.GetCol(), 0, rCol.GetTab()), 1161 mbUpdated(false) 1162 {} 1163 1164 void operator() ( size_t nRow, double fVal ) 1165 { 1166 ScRefCellValue aCell(fVal); 1167 updateScriptType(nRow, aCell); 1168 } 1169 1170 void operator() ( size_t nRow, const svl::SharedString& rStr ) 1171 { 1172 ScRefCellValue aCell(&rStr); 1173 updateScriptType(nRow, aCell); 1174 } 1175 1176 void operator() ( size_t nRow, const EditTextObject* pText ) 1177 { 1178 ScRefCellValue aCell(pText); 1179 updateScriptType(nRow, aCell); 1180 } 1181 1182 void operator() ( size_t nRow, const ScFormulaCell* pCell ) 1183 { 1184 ScRefCellValue aCell(const_cast<ScFormulaCell*>(pCell)); 1185 updateScriptType(nRow, aCell); 1186 } 1187 1188 bool isUpdated() const { return mbUpdated; } 1189 }; 1190 1191 } 1192 1193 void ScColumn::UpdateScriptTypes( SCROW nRow1, SCROW nRow2 ) 1194 { 1195 if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2) 1196 return; 1197 1198 ScriptTypeUpdater aFunc(*this); 1199 sc::ParseAllNonEmpty(maCells.begin(), maCells, nRow1, nRow2, aFunc); 1200 if (aFunc.isUpdated()) 1201 CellStorageModified(); 1202 } 1203 1204 void ScColumn::Swap( ScColumn& rOther, SCROW nRow1, SCROW nRow2, bool bPattern ) 1205 { 1206 maCells.swap(nRow1, nRow2, rOther.maCells, nRow1); 1207 maCellTextAttrs.swap(nRow1, nRow2, rOther.maCellTextAttrs, nRow1); 1208 maCellNotes.swap(nRow1, nRow2, rOther.maCellNotes, nRow1); 1209 maBroadcasters.swap(nRow1, nRow2, rOther.maBroadcasters, nRow1); 1210 1211 // Update draw object anchors 1212 ScDrawLayer* pDrawLayer = GetDoc().GetDrawLayer(); 1213 if (pDrawLayer) 1214 { 1215 std::map<SCROW, std::vector<SdrObject*>> aThisColRowDrawObjects 1216 = pDrawLayer->GetObjectsAnchoredToRange(GetTab(), GetCol(), nRow1, nRow2); 1217 std::map<SCROW, std::vector<SdrObject*>> aOtherColRowDrawObjects 1218 = pDrawLayer->GetObjectsAnchoredToRange(GetTab(), rOther.GetCol(), nRow1, nRow2); 1219 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) 1220 { 1221 std::vector<SdrObject*>& rThisCellDrawObjects = aThisColRowDrawObjects[nRow]; 1222 if (!rThisCellDrawObjects.empty()) 1223 UpdateDrawObjectsForRow(rThisCellDrawObjects, rOther.GetCol(), nRow); 1224 std::vector<SdrObject*>& rOtherCellDrawObjects = aOtherColRowDrawObjects[nRow]; 1225 if (!rOtherCellDrawObjects.empty()) 1226 rOther.UpdateDrawObjectsForRow(rOtherCellDrawObjects, GetCol(), nRow); 1227 } 1228 } 1229 1230 if (bPattern) 1231 { 1232 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) 1233 { 1234 const ScPatternAttr* pPat1 = GetPattern(nRow); 1235 const ScPatternAttr* pPat2 = rOther.GetPattern(nRow); 1236 if (pPat1 != pPat2) 1237 { 1238 if (pPat1->GetRefCount() == 1) 1239 pPat1 = &rOther.GetDoc().GetPool()->Put(*pPat1); 1240 SetPattern(nRow, *pPat2); 1241 rOther.SetPattern(nRow, *pPat1); 1242 } 1243 } 1244 } 1245 1246 CellStorageModified(); 1247 rOther.CellStorageModified(); 1248 } 1249 1250 namespace { 1251 1252 class FormulaColPosSetter 1253 { 1254 SCCOL mnCol; 1255 bool mbUpdateRefs; 1256 public: 1257 FormulaColPosSetter( SCCOL nCol, bool bUpdateRefs ) : mnCol(nCol), mbUpdateRefs(bUpdateRefs) {} 1258 1259 void operator() ( size_t nRow, ScFormulaCell* pCell ) 1260 { 1261 if (!pCell->IsShared() || pCell->IsSharedTop()) 1262 { 1263 // Ensure that the references still point to the same locations 1264 // after the position change. 1265 ScAddress aOldPos = pCell->aPos; 1266 pCell->aPos.SetCol(mnCol); 1267 pCell->aPos.SetRow(nRow); 1268 if (mbUpdateRefs) 1269 pCell->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos, pCell->aPos); 1270 else 1271 pCell->GetCode()->AdjustReferenceOnMovedOriginIfOtherSheet(aOldPos, pCell->aPos); 1272 } 1273 else 1274 { 1275 pCell->aPos.SetCol(mnCol); 1276 pCell->aPos.SetRow(nRow); 1277 } 1278 } 1279 }; 1280 1281 } 1282 1283 void ScColumn::ResetFormulaCellPositions( SCROW nRow1, SCROW nRow2, bool bUpdateRefs ) 1284 { 1285 FormulaColPosSetter aFunc(nCol, bUpdateRefs); 1286 sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc); 1287 } 1288 1289 namespace { 1290 1291 class RelativeRefBoundChecker 1292 { 1293 std::vector<SCROW> maBounds; 1294 ScRange maBoundRange; 1295 1296 public: 1297 explicit RelativeRefBoundChecker( const ScRange& rBoundRange ) : 1298 maBoundRange(rBoundRange) {} 1299 1300 void operator() ( size_t /*nRow*/, ScFormulaCell* pCell ) 1301 { 1302 if (!pCell->IsSharedTop()) 1303 return; 1304 1305 pCell->GetCode()->CheckRelativeReferenceBounds( 1306 pCell->aPos, pCell->GetSharedLength(), maBoundRange, maBounds); 1307 } 1308 1309 void swapBounds( std::vector<SCROW>& rBounds ) 1310 { 1311 rBounds.swap(maBounds); 1312 } 1313 }; 1314 1315 } 1316 1317 void ScColumn::SplitFormulaGroupByRelativeRef( const ScRange& rBoundRange ) 1318 { 1319 if (rBoundRange.aStart.Row() >= GetDoc().MaxRow()) 1320 // Nothing to split. 1321 return; 1322 1323 std::vector<SCROW> aBounds; 1324 1325 // Cut at row boundaries first. 1326 aBounds.push_back(rBoundRange.aStart.Row()); 1327 if (rBoundRange.aEnd.Row() < GetDoc().MaxRow()) 1328 aBounds.push_back(rBoundRange.aEnd.Row()+1); 1329 sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells, aBounds); 1330 1331 RelativeRefBoundChecker aFunc(rBoundRange); 1332 sc::ProcessFormula( 1333 maCells.begin(), maCells, rBoundRange.aStart.Row(), rBoundRange.aEnd.Row(), aFunc); 1334 aFunc.swapBounds(aBounds); 1335 sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells, aBounds); 1336 } 1337 1338 namespace { 1339 1340 class ListenerCollector 1341 { 1342 std::vector<SvtListener*>& mrListeners; 1343 public: 1344 explicit ListenerCollector( std::vector<SvtListener*>& rListener ) : 1345 mrListeners(rListener) {} 1346 1347 void operator() ( size_t /*nRow*/, SvtBroadcaster* p ) 1348 { 1349 SvtBroadcaster::ListenersType& rLis = p->GetAllListeners(); 1350 mrListeners.insert(mrListeners.end(), rLis.begin(), rLis.end()); 1351 } 1352 }; 1353 1354 class FormulaCellCollector 1355 { 1356 std::vector<ScFormulaCell*>& mrCells; 1357 public: 1358 explicit FormulaCellCollector( std::vector<ScFormulaCell*>& rCells ) : mrCells(rCells) {} 1359 1360 void operator() ( size_t /*nRow*/, ScFormulaCell* p ) 1361 { 1362 mrCells.push_back(p); 1363 } 1364 }; 1365 1366 } 1367 1368 void ScColumn::CollectListeners( std::vector<SvtListener*>& rListeners, SCROW nRow1, SCROW nRow2 ) 1369 { 1370 if (nRow2 < nRow1 || !GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2)) 1371 return; 1372 1373 ListenerCollector aFunc(rListeners); 1374 sc::ProcessBroadcaster(maBroadcasters.begin(), maBroadcasters, nRow1, nRow2, aFunc); 1375 } 1376 1377 void ScColumn::CollectFormulaCells( std::vector<ScFormulaCell*>& rCells, SCROW nRow1, SCROW nRow2 ) 1378 { 1379 if (nRow2 < nRow1 || !GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2)) 1380 return; 1381 1382 FormulaCellCollector aFunc(rCells); 1383 sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc); 1384 } 1385 1386 bool ScColumn::HasFormulaCell() const 1387 { 1388 return mnBlkCountFormula != 0; 1389 } 1390 1391 namespace { 1392 1393 struct FindAnyFormula 1394 { 1395 bool operator() ( size_t /*nRow*/, const ScFormulaCell* /*pCell*/ ) const 1396 { 1397 return true; 1398 } 1399 }; 1400 1401 } 1402 1403 bool ScColumn::HasFormulaCell( SCROW nRow1, SCROW nRow2 ) const 1404 { 1405 if (!mnBlkCountFormula) 1406 return false; 1407 1408 if (nRow2 < nRow1 || !GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2)) 1409 return false; 1410 1411 if (nRow1 == 0 && nRow2 == GetDoc().MaxRow()) 1412 return HasFormulaCell(); 1413 1414 FindAnyFormula aFunc; 1415 std::pair<sc::CellStoreType::const_iterator, size_t> aRet = 1416 sc::FindFormula(maCells, nRow1, nRow2, aFunc); 1417 1418 return aRet.first != maCells.end(); 1419 } 1420 1421 namespace { 1422 1423 void endListening( sc::EndListeningContext& rCxt, ScFormulaCell** pp, ScFormulaCell** ppEnd ) 1424 { 1425 for (; pp != ppEnd; ++pp) 1426 { 1427 ScFormulaCell& rFC = **pp; 1428 rFC.EndListeningTo(rCxt); 1429 } 1430 } 1431 1432 class StartListeningFormulaCellsHandler 1433 { 1434 sc::StartListeningContext& mrStartCxt; 1435 sc::EndListeningContext& mrEndCxt; 1436 SCROW mnStartRow; 1437 1438 public: 1439 StartListeningFormulaCellsHandler( sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt ) : 1440 mrStartCxt(rStartCxt), mrEndCxt(rEndCxt), mnStartRow(-1) {} 1441 1442 void operator() ( const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize ) 1443 { 1444 if (node.type != sc::element_type_formula) 1445 // We are only interested in formulas. 1446 return; 1447 1448 mnStartRow = node.position + nOffset; 1449 1450 ScFormulaCell** ppBeg = &sc::formula_block::at(*node.data, nOffset); 1451 ScFormulaCell** ppEnd = ppBeg + nDataSize; 1452 1453 ScFormulaCell** pp = ppBeg; 1454 1455 // If the first formula cell belongs to a group and it's not the top 1456 // cell, move up to the top cell of the group, and have all the extra 1457 // formula cells stop listening. 1458 1459 ScFormulaCell* pFC = *pp; 1460 if (pFC->IsShared() && !pFC->IsSharedTop()) 1461 { 1462 SCROW nBackTrackSize = pFC->aPos.Row() - pFC->GetSharedTopRow(); 1463 if (nBackTrackSize > 0) 1464 { 1465 assert(o3tl::make_unsigned(nBackTrackSize) <= nOffset); 1466 for (SCROW i = 0; i < nBackTrackSize; ++i) 1467 --pp; 1468 endListening(mrEndCxt, pp, ppBeg); 1469 mnStartRow -= nBackTrackSize; 1470 } 1471 } 1472 1473 for (; pp != ppEnd; ++pp) 1474 { 1475 pFC = *pp; 1476 1477 if (!pFC->IsSharedTop()) 1478 { 1479 assert(!pFC->IsShared()); 1480 pFC->StartListeningTo(mrStartCxt); 1481 continue; 1482 } 1483 1484 // If This is the last group in the range, see if the group 1485 // extends beyond the range, in which case have the excess 1486 // formula cells stop listening. 1487 size_t nEndGroupPos = (pp - ppBeg) + pFC->GetSharedLength(); 1488 if (nEndGroupPos > nDataSize) 1489 { 1490 size_t nExcessSize = nEndGroupPos - nDataSize; 1491 ScFormulaCell** ppGrpEnd = pp + pFC->GetSharedLength(); 1492 ScFormulaCell** ppGrp = ppGrpEnd - nExcessSize; 1493 endListening(mrEndCxt, ppGrp, ppGrpEnd); 1494 1495 // Register formula cells as a group. 1496 sc::SharedFormulaUtil::startListeningAsGroup(mrStartCxt, pp); 1497 pp = ppEnd - 1; // Move to the one before the end position. 1498 } 1499 else 1500 { 1501 // Register formula cells as a group. 1502 sc::SharedFormulaUtil::startListeningAsGroup(mrStartCxt, pp); 1503 pp += pFC->GetSharedLength() - 1; // Move to the last one in the group. 1504 } 1505 } 1506 } 1507 1508 }; 1509 1510 class EndListeningFormulaCellsHandler 1511 { 1512 sc::EndListeningContext& mrEndCxt; 1513 SCROW mnStartRow; 1514 SCROW mnEndRow; 1515 1516 public: 1517 explicit EndListeningFormulaCellsHandler( sc::EndListeningContext& rEndCxt ) : 1518 mrEndCxt(rEndCxt), mnStartRow(-1), mnEndRow(-1) {} 1519 1520 void operator() ( const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize ) 1521 { 1522 if (node.type != sc::element_type_formula) 1523 // We are only interested in formulas. 1524 return; 1525 1526 mnStartRow = node.position + nOffset; 1527 1528 ScFormulaCell** ppBeg = &sc::formula_block::at(*node.data, nOffset); 1529 ScFormulaCell** ppEnd = ppBeg + nDataSize; 1530 1531 ScFormulaCell** pp = ppBeg; 1532 1533 // If the first formula cell belongs to a group and it's not the top 1534 // cell, move up to the top cell of the group. 1535 1536 ScFormulaCell* pFC = *pp; 1537 if (pFC->IsShared() && !pFC->IsSharedTop()) 1538 { 1539 SCROW nBackTrackSize = pFC->aPos.Row() - pFC->GetSharedTopRow(); 1540 if (nBackTrackSize > 0) 1541 { 1542 assert(o3tl::make_unsigned(nBackTrackSize) <= nOffset); 1543 for (SCROW i = 0; i < nBackTrackSize; ++i) 1544 --pp; 1545 mnStartRow -= nBackTrackSize; 1546 } 1547 } 1548 1549 for (; pp != ppEnd; ++pp) 1550 { 1551 pFC = *pp; 1552 1553 if (!pFC->IsSharedTop()) 1554 { 1555 assert(!pFC->IsShared()); 1556 pFC->EndListeningTo(mrEndCxt); 1557 continue; 1558 } 1559 1560 size_t nEndGroupPos = (pp - ppBeg) + pFC->GetSharedLength(); 1561 mnEndRow = node.position + nOffset + nEndGroupPos - 1; // absolute row position of the last one in the group. 1562 1563 ScFormulaCell** ppGrpEnd = pp + pFC->GetSharedLength(); 1564 endListening(mrEndCxt, pp, ppGrpEnd); 1565 1566 if (nEndGroupPos > nDataSize) 1567 { 1568 // The group goes beyond the specified end row. Move to the 1569 // one before the end position to finish the loop. 1570 pp = ppEnd - 1; 1571 } 1572 else 1573 { 1574 // Move to the last one in the group. 1575 pp += pFC->GetSharedLength() - 1; 1576 } 1577 } 1578 } 1579 1580 SCROW getStartRow() const 1581 { 1582 return mnStartRow; 1583 } 1584 1585 SCROW getEndRow() const 1586 { 1587 return mnEndRow; 1588 } 1589 }; 1590 1591 } 1592 1593 void ScColumn::StartListeningFormulaCells( 1594 sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt, 1595 SCROW nRow1, SCROW nRow2 ) 1596 { 1597 if (!HasFormulaCell()) 1598 return; 1599 1600 StartListeningFormulaCellsHandler aFunc(rStartCxt, rEndCxt); 1601 sc::ProcessBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2); 1602 } 1603 1604 void ScColumn::EndListeningFormulaCells( 1605 sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2, 1606 SCROW* pStartRow, SCROW* pEndRow ) 1607 { 1608 if (!HasFormulaCell()) 1609 return; 1610 1611 EndListeningFormulaCellsHandler aFunc(rCxt); 1612 sc::ProcessBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2); 1613 1614 if (pStartRow) 1615 *pStartRow = aFunc.getStartRow(); 1616 1617 if (pEndRow) 1618 *pEndRow = aFunc.getEndRow(); 1619 } 1620 1621 void ScColumn::EndListeningIntersectedGroup( 1622 sc::EndListeningContext& rCxt, SCROW nRow, std::vector<ScAddress>* pGroupPos ) 1623 { 1624 if (!GetDoc().ValidRow(nRow)) 1625 return; 1626 1627 sc::CellStoreType::position_type aPos = maCells.position(nRow); 1628 sc::CellStoreType::iterator it = aPos.first; 1629 if (it->type != sc::element_type_formula) 1630 // Only interested in a formula block. 1631 return; 1632 1633 ScFormulaCell* pFC = sc::formula_block::at(*it->data, aPos.second); 1634 ScFormulaCellGroupRef xGroup = pFC->GetCellGroup(); 1635 if (!xGroup) 1636 // Not a formula group. 1637 return; 1638 1639 // End listening. 1640 pFC->EndListeningTo(rCxt); 1641 1642 if (pGroupPos) 1643 { 1644 if (!pFC->IsSharedTop()) 1645 // Record the position of the top cell of the group. 1646 pGroupPos->push_back(xGroup->mpTopCell->aPos); 1647 1648 SCROW nGrpLastRow = pFC->GetSharedTopRow() + pFC->GetSharedLength() - 1; 1649 if (nRow < nGrpLastRow) 1650 // Record the last position of the group. 1651 pGroupPos->push_back(ScAddress(nCol, nGrpLastRow, nTab)); 1652 } 1653 } 1654 1655 void ScColumn::EndListeningIntersectedGroups( 1656 sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2, std::vector<ScAddress>* pGroupPos ) 1657 { 1658 // Only end the intersected group. 1659 sc::CellStoreType::position_type aPos = maCells.position(nRow1); 1660 sc::CellStoreType::iterator it = aPos.first; 1661 if (it->type == sc::element_type_formula) 1662 { 1663 ScFormulaCell* pFC = sc::formula_block::at(*it->data, aPos.second); 1664 ScFormulaCellGroupRef xGroup = pFC->GetCellGroup(); 1665 if (xGroup) 1666 { 1667 if (!pFC->IsSharedTop()) 1668 // End listening. 1669 pFC->EndListeningTo(rCxt); 1670 1671 if (pGroupPos) 1672 // Record the position of the top cell of the group. 1673 pGroupPos->push_back(xGroup->mpTopCell->aPos); 1674 } 1675 } 1676 1677 aPos = maCells.position(it, nRow2); 1678 it = aPos.first; 1679 if (it->type != sc::element_type_formula) 1680 return; 1681 1682 ScFormulaCell* pFC = sc::formula_block::at(*it->data, aPos.second); 1683 ScFormulaCellGroupRef xGroup = pFC->GetCellGroup(); 1684 if (!xGroup) 1685 return; 1686 1687 if (!pFC->IsSharedTop()) 1688 // End listening. 1689 pFC->EndListeningTo(rCxt); 1690 1691 if (pGroupPos) 1692 { 1693 // Record the position of the bottom cell of the group. 1694 ScAddress aPosLast = xGroup->mpTopCell->aPos; 1695 aPosLast.IncRow(xGroup->mnLength-1); 1696 pGroupPos->push_back(aPosLast); 1697 } 1698 } 1699 1700 void ScColumn::EndListeningGroup( sc::EndListeningContext& rCxt, SCROW nRow ) 1701 { 1702 sc::CellStoreType::position_type aPos = maCells.position(nRow); 1703 if (aPos.first->type != sc::element_type_formula) 1704 // not a formula cell. 1705 return; 1706 1707 ScFormulaCell** pp = &sc::formula_block::at(*aPos.first->data, aPos.second); 1708 1709 ScFormulaCellGroupRef xGroup = (*pp)->GetCellGroup(); 1710 if (!xGroup) 1711 { 1712 // not a formula group. 1713 (*pp)->EndListeningTo(rCxt); 1714 return; 1715 } 1716 1717 // Move back to the top cell. 1718 SCROW nTopDelta = (*pp)->aPos.Row() - xGroup->mpTopCell->aPos.Row(); 1719 assert(nTopDelta >= 0); 1720 if (nTopDelta > 0) 1721 pp -= nTopDelta; 1722 1723 // Set the needs listening flag to all cells in the group. 1724 assert(*pp == xGroup->mpTopCell); 1725 ScFormulaCell** ppEnd = pp + xGroup->mnLength; 1726 for (; pp != ppEnd; ++pp) 1727 (*pp)->EndListeningTo(rCxt); 1728 } 1729 1730 void ScColumn::SetNeedsListeningGroup( SCROW nRow ) 1731 { 1732 sc::CellStoreType::position_type aPos = maCells.position(nRow); 1733 if (aPos.first->type != sc::element_type_formula) 1734 // not a formula cell. 1735 return; 1736 1737 ScFormulaCell** pp = &sc::formula_block::at(*aPos.first->data, aPos.second); 1738 1739 ScFormulaCellGroupRef xGroup = (*pp)->GetCellGroup(); 1740 if (!xGroup) 1741 { 1742 // not a formula group. 1743 (*pp)->SetNeedsListening(true); 1744 return; 1745 } 1746 1747 // Move back to the top cell. 1748 SCROW nTopDelta = (*pp)->aPos.Row() - xGroup->mpTopCell->aPos.Row(); 1749 assert(nTopDelta >= 0); 1750 if (nTopDelta > 0) 1751 pp -= nTopDelta; 1752 1753 // Set the needs listening flag to all cells in the group. 1754 assert(*pp == xGroup->mpTopCell); 1755 ScFormulaCell** ppEnd = pp + xGroup->mnLength; 1756 for (; pp != ppEnd; ++pp) 1757 (*pp)->SetNeedsListening(true); 1758 } 1759 1760 std::optional<sc::ColumnIterator> ScColumn::GetColumnIterator( SCROW nRow1, SCROW nRow2 ) const 1761 { 1762 if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2) 1763 return {}; 1764 1765 return sc::ColumnIterator(maCells, nRow1, nRow2); 1766 } 1767 1768 static bool lcl_InterpretSpan(sc::formula_block::const_iterator& rSpanIter, SCROW nStartOffset, SCROW nEndOffset, 1769 const ScFormulaCellGroupRef& mxParentGroup, bool& bAllowThreading, ScDocument& rDoc) 1770 { 1771 bAllowThreading = true; 1772 ScFormulaCell* pCellStart = nullptr; 1773 SCROW nSpanStart = -1; 1774 SCROW nSpanEnd = -1; 1775 sc::formula_block::const_iterator itSpanStart; 1776 bool bAnyDirty = false; 1777 for (SCROW nFGOffset = nStartOffset; nFGOffset <= nEndOffset; ++rSpanIter, ++nFGOffset) 1778 { 1779 bool bThisDirty = (*rSpanIter)->NeedsInterpret(); 1780 if (!pCellStart && bThisDirty) 1781 { 1782 pCellStart = *rSpanIter; 1783 itSpanStart = rSpanIter; 1784 nSpanStart = nFGOffset; 1785 bAnyDirty = true; 1786 } 1787 1788 if (pCellStart && (!bThisDirty || nFGOffset == nEndOffset)) 1789 { 1790 nSpanEnd = bThisDirty ? nFGOffset : nFGOffset - 1; 1791 assert(nSpanStart >= nStartOffset && nSpanStart <= nSpanEnd && nSpanEnd <= nEndOffset); 1792 1793 // Found a completely dirty sub span [nSpanStart, nSpanEnd] inside the required span [nStartOffset, nEndOffset] 1794 bool bGroupInterpreted = pCellStart->Interpret(nSpanStart, nSpanEnd); 1795 1796 if (bGroupInterpreted) 1797 for (SCROW nIdx = nSpanStart; nIdx <= nSpanEnd; ++nIdx, ++itSpanStart) 1798 assert(!(*itSpanStart)->NeedsInterpret()); 1799 1800 ScRecursionHelper& rRecursionHelper = rDoc.GetRecursionHelper(); 1801 // child cell's Interpret could result in calling dependency calc 1802 // and that could detect a cycle involving mxGroup 1803 // and do early exit in that case. 1804 // OR 1805 // this call resulted from a dependency calculation for a multi-formula-group-threading and 1806 // if intergroup dependency is found, return early. 1807 if ((mxParentGroup && mxParentGroup->mbPartOfCycle) || !rRecursionHelper.AreGroupsIndependent()) 1808 { 1809 bAllowThreading = false; 1810 return bAnyDirty; 1811 } 1812 1813 if (!bGroupInterpreted) 1814 { 1815 // Evaluate from second cell in non-grouped style (no point in trying group-interpret again). 1816 ++itSpanStart; 1817 for (SCROW nIdx = nSpanStart+1; nIdx <= nSpanEnd; ++nIdx, ++itSpanStart) 1818 { 1819 (*itSpanStart)->Interpret(); // We know for sure that this cell is dirty so directly call Interpret(). 1820 if ((*itSpanStart)->NeedsInterpret()) 1821 { 1822 SAL_WARN("sc.core.formulagroup", "Internal error, cell " << (*itSpanStart)->aPos 1823 << " failed running Interpret(), not allowing threading"); 1824 bAllowThreading = false; 1825 return bAnyDirty; 1826 } 1827 1828 // Allow early exit like above. 1829 if ((mxParentGroup && mxParentGroup->mbPartOfCycle) || !rRecursionHelper.AreGroupsIndependent()) 1830 { 1831 // Set this cell as dirty as this may be interpreted in InterpretTail() 1832 pCellStart->SetDirtyVar(); 1833 bAllowThreading = false; 1834 return bAnyDirty; 1835 } 1836 } 1837 } 1838 1839 pCellStart = nullptr; // For next sub span start detection. 1840 } 1841 } 1842 1843 return bAnyDirty; 1844 } 1845 1846 static void lcl_EvalDirty(sc::CellStoreType& rCells, SCROW nRow1, SCROW nRow2, ScDocument& rDoc, 1847 const ScFormulaCellGroupRef& mxGroup, bool bThreadingDepEval, bool bSkipRunning, 1848 bool& bIsDirty, bool& bAllowThreading) 1849 { 1850 ScRecursionHelper& rRecursionHelper = rDoc.GetRecursionHelper(); 1851 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = rCells.position(nRow1); 1852 sc::CellStoreType::const_iterator it = aPos.first; 1853 size_t nOffset = aPos.second; 1854 SCROW nRow = nRow1; 1855 1856 bIsDirty = false; 1857 1858 for (;it != rCells.end() && nRow <= nRow2; ++it, nOffset = 0) 1859 { 1860 switch( it->type ) 1861 { 1862 case sc::element_type_edittext: 1863 // These require EditEngine (in ScEditUtils::GetString()), which is probably 1864 // too complex for use in threads. 1865 if (bThreadingDepEval) 1866 { 1867 bAllowThreading = false; 1868 return; 1869 } 1870 break; 1871 case sc::element_type_formula: 1872 { 1873 size_t nRowsToRead = nRow2 - nRow + 1; 1874 const size_t nEnd = std::min(it->size, nOffset+nRowsToRead); // last row + 1 1875 sc::formula_block::const_iterator itCell = sc::formula_block::begin(*it->data); 1876 std::advance(itCell, nOffset); 1877 1878 // Loop inside the formula block. 1879 size_t nCellIdx = nOffset; 1880 while (nCellIdx < nEnd) 1881 { 1882 const ScFormulaCellGroupRef& mxGroupChild = (*itCell)->GetCellGroup(); 1883 ScFormulaCell* pChildTopCell = mxGroupChild ? mxGroupChild->mpTopCell : *itCell; 1884 1885 // Check if itCell is already in path. 1886 // If yes use a cycle guard to mark all elements of the cycle 1887 // and return false 1888 if (bThreadingDepEval && pChildTopCell->GetSeenInPath()) 1889 { 1890 ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, pChildTopCell); 1891 bAllowThreading = false; 1892 return; 1893 } 1894 1895 if (bSkipRunning && (*itCell)->IsRunning()) 1896 { 1897 ++itCell; 1898 nCellIdx += 1; 1899 nRow += 1; 1900 nRowsToRead -= 1; 1901 continue; 1902 } 1903 1904 if (mxGroupChild) 1905 { 1906 // It is a Formula-group, evaluate the necessary parts of it (spans). 1907 const SCROW nFGStartOffset = (*itCell)->aPos.Row() - pChildTopCell->aPos.Row(); 1908 const SCROW nFGEndOffset = std::min(nFGStartOffset + static_cast<SCROW>(nRowsToRead) - 1, mxGroupChild->mnLength - 1); 1909 assert(nFGEndOffset >= nFGStartOffset); 1910 const SCROW nSpanLen = nFGEndOffset - nFGStartOffset + 1; 1911 // The (main) span required to be evaluated is [nFGStartOffset, nFGEndOffset], but this span may contain 1912 // non-dirty cells, so split this into sets of completely-dirty spans and try evaluate each of them in grouped-style. 1913 1914 bool bAnyDirtyInSpan = lcl_InterpretSpan(itCell, nFGStartOffset, nFGEndOffset, mxGroup, bAllowThreading, rDoc); 1915 if (!bAllowThreading) 1916 return; 1917 // itCell will now point to cell just after the end of span [nFGStartOffset, nFGEndOffset]. 1918 bIsDirty = bIsDirty || bAnyDirtyInSpan; 1919 1920 // update the counters by nSpanLen. 1921 // itCell already got updated. 1922 nCellIdx += nSpanLen; 1923 nRow += nSpanLen; 1924 nRowsToRead -= nSpanLen; 1925 } 1926 else 1927 { 1928 // No formula-group here. 1929 bool bDirtyFlag = false; 1930 if( (*itCell)->NeedsInterpret()) 1931 { 1932 bDirtyFlag = true; 1933 (*itCell)->Interpret(); 1934 if ((*itCell)->NeedsInterpret()) 1935 { 1936 SAL_WARN("sc.core.formulagroup", "Internal error, cell " << (*itCell)->aPos 1937 << " failed running Interpret(), not allowing threading"); 1938 bAllowThreading = false; 1939 return; 1940 } 1941 } 1942 bIsDirty = bIsDirty || bDirtyFlag; 1943 1944 // child cell's Interpret could result in calling dependency calc 1945 // and that could detect a cycle involving mxGroup 1946 // and do early exit in that case. 1947 // OR 1948 // we are trying multi-formula-group-threading, but found intergroup dependency. 1949 if (bThreadingDepEval && mxGroup && 1950 (mxGroup->mbPartOfCycle || !rRecursionHelper.AreGroupsIndependent())) 1951 { 1952 // Set itCell as dirty as itCell may be interpreted in InterpretTail() 1953 (*itCell)->SetDirtyVar(); 1954 bAllowThreading = false; 1955 return; 1956 } 1957 1958 // update the counters by 1. 1959 nCellIdx += 1; 1960 nRow += 1; 1961 nRowsToRead -= 1; 1962 ++itCell; 1963 } 1964 } 1965 break; 1966 } 1967 default: 1968 // Skip this block. 1969 nRow += it->size - nOffset; 1970 continue; 1971 } 1972 } 1973 1974 if (bThreadingDepEval) 1975 bAllowThreading = true; 1976 1977 } 1978 1979 // Returns true if at least one FC is dirty. 1980 bool ScColumn::EnsureFormulaCellResults( SCROW nRow1, SCROW nRow2, bool bSkipRunning ) 1981 { 1982 if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2) 1983 return false; 1984 1985 if (!HasFormulaCell(nRow1, nRow2)) 1986 return false; 1987 1988 bool bAnyDirty = false, bTmp = false; 1989 lcl_EvalDirty(maCells, nRow1, nRow2, GetDoc(), nullptr, false, bSkipRunning, bAnyDirty, bTmp); 1990 return bAnyDirty; 1991 } 1992 1993 bool ScColumn::HandleRefArrayForParallelism( SCROW nRow1, SCROW nRow2, const ScFormulaCellGroupRef& mxGroup ) 1994 { 1995 if (nRow1 > nRow2) 1996 return false; 1997 1998 bool bAllowThreading = true, bTmp = false; 1999 lcl_EvalDirty(maCells, nRow1, nRow2, GetDoc(), mxGroup, true, false, bTmp, bAllowThreading); 2000 2001 return bAllowThreading; 2002 } 2003 2004 namespace { 2005 2006 class StoreToCacheFunc 2007 { 2008 SvStream& mrStrm; 2009 public: 2010 2011 StoreToCacheFunc(SvStream& rStrm): 2012 mrStrm(rStrm) 2013 { 2014 } 2015 2016 void operator() ( const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize ) 2017 { 2018 SCROW nStartRow = node.position + nOffset; 2019 mrStrm.WriteUInt64(nStartRow); 2020 mrStrm.WriteUInt64(nDataSize); 2021 switch (node.type) 2022 { 2023 case sc::element_type_empty: 2024 { 2025 mrStrm.WriteUChar(0); 2026 } 2027 break; 2028 case sc::element_type_numeric: 2029 { 2030 mrStrm.WriteUChar(1); 2031 sc::numeric_block::const_iterator it = sc::numeric_block::begin(*node.data); 2032 std::advance(it, nOffset); 2033 sc::numeric_block::const_iterator itEnd = it; 2034 std::advance(itEnd, nDataSize); 2035 2036 for (; it != itEnd; ++it) 2037 { 2038 mrStrm.WriteDouble(*it); 2039 } 2040 } 2041 break; 2042 case sc::element_type_string: 2043 { 2044 mrStrm.WriteUChar(2); 2045 sc::string_block::const_iterator it = sc::string_block::begin(*node.data); 2046 std::advance(it, nOffset); 2047 sc::string_block::const_iterator itEnd = it; 2048 std::advance(itEnd, nDataSize); 2049 2050 for (; it != itEnd; ++it) 2051 { 2052 OString aStr = OUStringToOString(it->getString(), RTL_TEXTENCODING_UTF8); 2053 sal_Int32 nStrLength = aStr.getLength(); 2054 mrStrm.WriteInt32(nStrLength); 2055 mrStrm.WriteOString(aStr); 2056 } 2057 } 2058 break; 2059 case sc::element_type_formula: 2060 { 2061 mrStrm.WriteUChar(3); 2062 sc::formula_block::const_iterator it = sc::formula_block::begin(*node.data); 2063 std::advance(it, nOffset); 2064 sc::formula_block::const_iterator itEnd = it; 2065 std::advance(itEnd, nDataSize); 2066 2067 for (; it != itEnd; /* incrementing through std::advance*/) 2068 { 2069 const ScFormulaCell* pCell = *it; 2070 OUString aFormula = pCell->GetFormula(formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1); 2071 const auto& xCellGroup = pCell->GetCellGroup(); 2072 sal_uInt64 nGroupLength = 0; 2073 if (xCellGroup) 2074 { 2075 nGroupLength = xCellGroup->mnLength; 2076 } 2077 else 2078 { 2079 nGroupLength = 1; 2080 } 2081 mrStrm.WriteUInt64(nGroupLength); 2082 mrStrm.WriteInt32(aFormula.getLength()); 2083 mrStrm.WriteOString(OUStringToOString(aFormula, RTL_TEXTENCODING_UTF8)); 2084 2085 // incrementing the iterator 2086 std::advance(it, nGroupLength); 2087 } 2088 } 2089 break; 2090 } 2091 } 2092 }; 2093 2094 } 2095 2096 void ScColumn::StoreToCache(SvStream& rStrm) const 2097 { 2098 rStrm.WriteUInt64(nCol); 2099 SCROW nLastRow = GetLastDataPos(); 2100 rStrm.WriteUInt64(nLastRow + 1); // the rows are zero based 2101 2102 StoreToCacheFunc aFunc(rStrm); 2103 sc::ParseBlock(maCells.begin(), maCells, aFunc, SCROW(0), nLastRow); 2104 } 2105 2106 void ScColumn::RestoreFromCache(SvStream& rStrm) 2107 { 2108 sal_uInt64 nStoredCol = 0; 2109 rStrm.ReadUInt64(nStoredCol); 2110 if (nStoredCol != static_cast<sal_uInt64>(nCol)) 2111 throw std::exception(); 2112 2113 sal_uInt64 nLastRow = 0; 2114 rStrm.ReadUInt64(nLastRow); 2115 sal_uInt64 nReadRow = 0; 2116 ScDocument& rDocument = GetDoc(); 2117 while (nReadRow < nLastRow) 2118 { 2119 sal_uInt64 nStartRow = 0; 2120 sal_uInt64 nDataSize = 0; 2121 rStrm.ReadUInt64(nStartRow); 2122 rStrm.ReadUInt64(nDataSize); 2123 sal_uInt8 nType = 0; 2124 rStrm.ReadUChar(nType); 2125 switch (nType) 2126 { 2127 case 0: 2128 // nothing to do 2129 maCells.set_empty(nStartRow, nDataSize); 2130 break; 2131 case 1: 2132 { 2133 // nDataSize double values 2134 std::vector<double> aValues(nDataSize); 2135 for (auto& rValue : aValues) 2136 { 2137 rStrm.ReadDouble(rValue); 2138 } 2139 maCells.set(nStartRow, aValues.begin(), aValues.end()); 2140 } 2141 break; 2142 case 2: 2143 { 2144 std::vector<svl::SharedString> aStrings(nDataSize); 2145 svl::SharedStringPool& rPool = rDocument.GetSharedStringPool(); 2146 for (auto& rString : aStrings) 2147 { 2148 sal_Int32 nStrLength = 0; 2149 rStrm.ReadInt32(nStrLength); 2150 std::unique_ptr<char[]> pStr(new char[nStrLength]); 2151 rStrm.ReadBytes(pStr.get(), nStrLength); 2152 OString aOStr(pStr.get(), nStrLength); 2153 OUString aStr = OStringToOUString(aOStr, RTL_TEXTENCODING_UTF8); 2154 rString = rPool.intern(aStr); 2155 } 2156 maCells.set(nStartRow, aStrings.begin(), aStrings.end()); 2157 2158 } 2159 break; 2160 case 3: 2161 { 2162 std::vector<ScFormulaCell*> aFormulaCells(nDataSize); 2163 2164 ScAddress aAddr(nCol, nStartRow, nTab); 2165 const formula::FormulaGrammar::Grammar eGrammar = formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1; 2166 for (SCROW nRow = 0; nRow < static_cast<SCROW>(nDataSize);) 2167 { 2168 sal_uInt64 nFormulaGroupSize = 0; 2169 rStrm.ReadUInt64(nFormulaGroupSize); 2170 sal_Int32 nStrLength = 0; 2171 rStrm.ReadInt32(nStrLength); 2172 std::unique_ptr<char[]> pStr(new char[nStrLength]); 2173 rStrm.ReadBytes(pStr.get(), nStrLength); 2174 OString aOStr(pStr.get(), nStrLength); 2175 OUString aStr = OStringToOUString(aOStr, RTL_TEXTENCODING_UTF8); 2176 for (sal_uInt64 i = 0; i < nFormulaGroupSize; ++i) 2177 { 2178 aFormulaCells[nRow + i] = new ScFormulaCell(rDocument, aAddr, aStr, eGrammar); 2179 aAddr.IncRow(); 2180 } 2181 2182 nRow += nFormulaGroupSize; 2183 } 2184 2185 maCells.set(nStartRow, aFormulaCells.begin(), aFormulaCells.end()); 2186 } 2187 break; 2188 } 2189 2190 nReadRow += nDataSize; 2191 } 2192 } 2193 2194 void ScColumn::CheckIntegrity() const 2195 { 2196 const ScColumn* pColTest = maCells.event_handler().getColumn(); 2197 2198 if (pColTest != this) 2199 { 2200 std::ostringstream os; 2201 os << "cell store's event handler references wrong column instance (this=" << this 2202 << "; stored=" << pColTest << ")"; 2203 throw std::runtime_error(os.str()); 2204 } 2205 2206 size_t nCount = std::count_if(maCells.cbegin(), maCells.cend(), 2207 [](const auto& blk) { return blk.type == sc::element_type_formula; } 2208 ); 2209 2210 if (mnBlkCountFormula != nCount) 2211 { 2212 std::ostringstream os; 2213 os << "incorrect cached formula block count (expected=" << nCount << "; actual=" 2214 << mnBlkCountFormula << ")"; 2215 throw std::runtime_error(os.str()); 2216 } 2217 } 2218 2219 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 2220
