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 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include <column.hxx> 21 22 #include <scitems.hxx> 23 #include <formulacell.hxx> 24 #include <document.hxx> 25 #include <attarray.hxx> 26 #include <patattr.hxx> 27 #include <cellform.hxx> 28 #include <typedstrdata.hxx> 29 #include <formula/errorcodes.hxx> 30 #include <formula/token.hxx> 31 #include <brdcst.hxx> 32 #include <docoptio.hxx> 33 #include <subtotal.hxx> 34 #include <markdata.hxx> 35 #include <stringutil.hxx> 36 #include <docpool.hxx> 37 #include <cellvalue.hxx> 38 #include <tokenarray.hxx> 39 #include <clipcontext.hxx> 40 #include <columnspanset.hxx> 41 #include <mtvcellfunc.hxx> 42 #include <scopetools.hxx> 43 #include <editutil.hxx> 44 #include <sharedformula.hxx> 45 #include <listenercontext.hxx> 46 #include <filterentries.hxx> 47 #include <conditio.hxx> 48 #include <colorscale.hxx> 49 #include <editeng/brushitem.hxx> 50 #include <editeng/colritem.hxx> 51 52 #include <com/sun/star/i18n/LocaleDataItem2.hpp> 53 #include <com/sun/star/lang/IllegalArgumentException.hpp> 54 55 #include <memory> 56 57 #include <o3tl/deleter.hxx> 58 59 #include <rtl/tencinfo.h> 60 61 #include <svl/numformat.hxx> 62 #include <svl/zforlist.hxx> 63 #include <svl/zformat.hxx> 64 #include <svl/sharedstringpool.hxx> 65 #include <osl/diagnose.h> 66 67 #include <cstdio> 68 #include <refdata.hxx> 69 70 using ::com::sun::star::i18n::LocaleDataItem2; 71 72 using namespace formula; 73 74 void ScColumn::Broadcast( SCROW nRow ) 75 { 76 ScHint aHint(SfxHintId::ScDataChanged, ScAddress(nCol, nRow, nTab)); 77 GetDoc().Broadcast(aHint); 78 } 79 80 void ScColumn::BroadcastCells( const std::vector<SCROW>& rRows, SfxHintId nHint ) 81 { 82 if (rRows.empty()) 83 return; 84 85 // Broadcast the changes. 86 ScDocument& rDocument = GetDoc(); 87 ScHint aHint(nHint, ScAddress(nCol, 0, nTab)); 88 for (const auto& rRow : rRows) 89 { 90 aHint.SetAddressRow(rRow); 91 rDocument.Broadcast(aHint); 92 } 93 } 94 95 void ScColumn::BroadcastRows( SCROW nStartRow, SCROW nEndRow, SfxHintId nHint ) 96 { 97 if( nStartRow > GetLastDataPos()) 98 return; 99 sc::SingleColumnSpanSet aSpanSet(GetDoc().GetSheetLimits()); 100 aSpanSet.scan(*this, nStartRow, nEndRow); 101 std::vector<SCROW> aRows; 102 aSpanSet.getRows(aRows); 103 BroadcastCells(aRows, nHint); 104 } 105 106 namespace { 107 108 class CellInterpreterBase 109 { 110 protected: 111 void Interpret(ScFormulaCell* p) 112 { 113 // Interpret() takes a range in a formula group, so group those together. 114 if( firstCell != nullptr && p->GetCellGroup() == p->GetCellGroup() 115 && p->aPos.Row() == lastPos.Row() + 1 ) 116 { 117 assert( p->aPos.Tab() == lastPos.Tab() && p->aPos.Col() == lastPos.Col()); 118 lastPos = p->aPos; // Extend range. 119 return; 120 } 121 flushPending(); 122 if( !p->GetCellGroup()) 123 { 124 p->Interpret(); 125 return; 126 } 127 firstCell = p; 128 lastPos = p->aPos; 129 130 } 131 ~CellInterpreterBase() 132 { 133 suppress_fun_call_w_exception(flushPending()); 134 } 135 private: 136 void flushPending() 137 { 138 if(firstCell == nullptr) 139 return; 140 SCROW firstRow = firstCell->GetCellGroup()->mpTopCell->aPos.Row(); 141 firstCell->Interpret(firstCell->aPos.Row() - firstRow, lastPos.Row() - firstRow); 142 firstCell = nullptr; 143 } 144 ScFormulaCell* firstCell = nullptr; 145 ScAddress lastPos; 146 }; 147 148 class DirtyCellInterpreter : public CellInterpreterBase 149 { 150 public: 151 void operator() (size_t, ScFormulaCell* p) 152 { 153 if(p->GetDirty()) 154 Interpret(p); 155 } 156 }; 157 158 class NeedsInterpretCellInterpreter : public CellInterpreterBase 159 { 160 public: 161 void operator() (size_t, ScFormulaCell* p) 162 { 163 if(p->NeedsInterpret()) 164 Interpret(p); 165 } 166 }; 167 168 } 169 170 void ScColumn::InterpretDirtyCells( SCROW nRow1, SCROW nRow2 ) 171 { 172 if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2) 173 return; 174 175 DirtyCellInterpreter aFunc; 176 sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc); 177 } 178 179 void ScColumn::InterpretCellsIfNeeded( SCROW nRow1, SCROW nRow2 ) 180 { 181 if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2) 182 return; 183 184 NeedsInterpretCellInterpreter aFunc; 185 sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc); 186 } 187 188 void ScColumn::DeleteContent( SCROW nRow, bool bBroadcast ) 189 { 190 sc::CellStoreType::position_type aPos = maCells.position(nRow); 191 sc::CellStoreType::iterator it = aPos.first; 192 if (it == maCells.end()) 193 return; 194 195 if (it->type == sc::element_type_formula) 196 { 197 ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second); 198 p->EndListeningTo(GetDoc()); 199 sc::SharedFormulaUtil::unshareFormulaCell(aPos, *p); 200 } 201 maCells.set_empty(nRow, nRow); 202 203 if (bBroadcast) 204 { 205 Broadcast(nRow); 206 CellStorageModified(); 207 } 208 } 209 210 void ScColumn::Delete( SCROW nRow ) 211 { 212 DeleteContent(nRow, false); 213 maCellTextAttrs.set_empty(nRow, nRow); 214 maCellNotes.set_empty(nRow, nRow); 215 maSparklines.set_empty(nRow, nRow); 216 217 Broadcast(nRow); 218 CellStorageModified(); 219 } 220 221 void ScColumn::FreeAll() 222 { 223 maCells.event_handler().stop(); 224 225 auto maxRowCount = GetDoc().GetMaxRowCount(); 226 // Keep a logical empty range of 0-rDoc.MaxRow() at all times. 227 maCells.clear(); 228 maCells.resize(maxRowCount); 229 maCellTextAttrs.clear(); 230 maCellTextAttrs.resize(maxRowCount); 231 maCellNotes.clear(); 232 maCellNotes.resize(maxRowCount); 233 maSparklines.clear(); 234 maSparklines.resize(maxRowCount); 235 CellStorageModified(); 236 } 237 238 void ScColumn::FreeNotes() 239 { 240 maCellNotes.clear(); 241 maCellNotes.resize(GetDoc().GetMaxRowCount()); 242 } 243 244 namespace { 245 246 class ShiftFormulaPosHandler 247 { 248 public: 249 250 void operator() (size_t nRow, ScFormulaCell* pCell) 251 { 252 pCell->aPos.SetRow(nRow); 253 } 254 }; 255 256 } 257 258 void ScColumn::DeleteRow( SCROW nStartRow, SCSIZE nSize, std::vector<ScAddress>* pGroupPos ) 259 { 260 pAttrArray->DeleteRow( nStartRow, nSize ); 261 262 SCROW nEndRow = nStartRow + nSize - 1; 263 264 maBroadcasters.erase(nStartRow, nEndRow); 265 maBroadcasters.resize(GetDoc().GetMaxRowCount()); 266 267 CellNotesDeleting(nStartRow, nEndRow, false); 268 maCellNotes.erase(nStartRow, nEndRow); 269 maCellNotes.resize(GetDoc().GetMaxRowCount()); 270 271 // See if we have any cells that would get deleted or shifted by deletion. 272 sc::CellStoreType::position_type aPos = maCells.position(nStartRow); 273 sc::CellStoreType::iterator itCell = aPos.first; 274 if (itCell->type == sc::element_type_empty) 275 { 276 // This is an empty block. If this is the last block, then there is no cells to delete or shift. 277 sc::CellStoreType::iterator itTest = itCell; 278 ++itTest; 279 if (itTest == maCells.end()) 280 { 281 // No cells are affected by this deletion. Bail out. 282 CellStorageModified(); // broadcast array has been modified. 283 return; 284 } 285 } 286 287 // Check if there are any cells below the end row that will get shifted. 288 bool bShiftCells = false; 289 if (nEndRow < GetDoc().MaxRow()) //only makes sense to do this if there *is* a row after the end row 290 { 291 aPos = maCells.position(itCell, nEndRow+1); 292 itCell = aPos.first; 293 if (itCell->type == sc::element_type_empty) 294 { 295 // This block is empty. See if there is any block that follows. 296 sc::CellStoreType::iterator itTest = itCell; 297 ++itTest; 298 if (itTest != maCells.end()) 299 // Non-empty block follows -> cells that will get shifted. 300 bShiftCells = true; 301 } 302 else 303 bShiftCells = true; 304 } 305 306 sc::SingleColumnSpanSet aNonEmptySpans(GetDoc().GetSheetLimits()); 307 if (bShiftCells) 308 { 309 // Mark all non-empty cell positions below the end row. 310 sc::ColumnBlockConstPosition aBlockPos; 311 aBlockPos.miCellPos = itCell; 312 aNonEmptySpans.scan(aBlockPos, *this, nEndRow+1, GetDoc().MaxRow()); 313 } 314 315 sc::AutoCalcSwitch aACSwitch(GetDoc(), false); 316 317 // Remove the cells. 318 maCells.erase(nStartRow, nEndRow); 319 maCells.resize(GetDoc().GetMaxRowCount()); 320 321 // Get the position again after the container change. 322 aPos = maCells.position(nStartRow); 323 324 // Shift the formula cell positions below the start row. 325 ShiftFormulaPosHandler aShiftFormulaFunc; 326 sc::ProcessFormula(aPos.first, maCells, nStartRow, GetDoc().MaxRow(), aShiftFormulaFunc); 327 328 bool bJoined = sc::SharedFormulaUtil::joinFormulaCellAbove(aPos); 329 if (bJoined && pGroupPos) 330 pGroupPos->push_back(ScAddress(nCol, nStartRow, nTab)); 331 332 // Shift the text attribute array too (before the broadcast). 333 maCellTextAttrs.erase(nStartRow, nEndRow); 334 maCellTextAttrs.resize(GetDoc().GetMaxRowCount()); 335 336 CellStorageModified(); 337 } 338 339 sc::CellStoreType::iterator ScColumn::GetPositionToInsert( SCROW nRow, std::vector<SCROW>& rNewSharedRows, 340 bool bInsertFormula ) 341 { 342 return GetPositionToInsert(maCells.begin(), nRow, rNewSharedRows, bInsertFormula); 343 } 344 345 void ScColumn::JoinNewFormulaCell( 346 const sc::CellStoreType::position_type& aPos, ScFormulaCell& rCell ) 347 { 348 // Check the previous row position for possible grouping. 349 if (aPos.first->type == sc::element_type_formula && aPos.second > 0) 350 { 351 ScFormulaCell& rPrev = *sc::formula_block::at(*aPos.first->data, aPos.second-1); 352 sc::CellStoreType::position_type aPosPrev = aPos; 353 --aPosPrev.second; 354 sc::SharedFormulaUtil::joinFormulaCells(aPosPrev, rPrev, rCell); 355 } 356 357 // Check the next row position for possible grouping. 358 if (aPos.first->type == sc::element_type_formula && aPos.second+1 < aPos.first->size) 359 { 360 ScFormulaCell& rNext = *sc::formula_block::at(*aPos.first->data, aPos.second+1); 361 sc::SharedFormulaUtil::joinFormulaCells(aPos, rCell, rNext); 362 } 363 } 364 365 void ScColumn::DetachFormulaCell( 366 const sc::CellStoreType::position_type& aPos, ScFormulaCell& rCell, std::vector<SCROW>& rNewSharedRows ) 367 { 368 if (!GetDoc().IsClipOrUndo()) 369 { 370 #if USE_FORMULA_GROUP_LISTENER 371 if (rCell.IsShared() && rCell.GetSharedLength() > 1) 372 { 373 // Record new spans (shared or remaining single) that will result 374 // from unsharing to reestablish listeners. 375 // Same cases as in unshareFormulaCell(). 376 // XXX NOTE: this is not part of unshareFormulaCell() because that 377 // is called in other contexts as well, for which passing and 378 // determining the rows vector would be superfluous. If that was 379 // needed, move it there. 380 const SCROW nSharedTopRow = rCell.GetSharedTopRow(); 381 const SCROW nSharedLength = rCell.GetSharedLength(); 382 if (rCell.aPos.Row() == nSharedTopRow) 383 { 384 // Top cell. 385 // Next row will be new shared top or single cell. 386 rNewSharedRows.push_back( nSharedTopRow + 1); 387 rNewSharedRows.push_back( nSharedTopRow + nSharedLength - 1); 388 } 389 else if (rCell.aPos.Row() == nSharedTopRow + nSharedLength - 1) 390 { 391 // Bottom cell. 392 // Current shared top row will be new shared top again or 393 // single cell. 394 rNewSharedRows.push_back( nSharedTopRow); 395 rNewSharedRows.push_back( rCell.aPos.Row() - 1); 396 } 397 else 398 { 399 // Some mid cell. 400 // Current shared top row will be new shared top again or 401 // single cell, plus a new shared top below or single cell. 402 rNewSharedRows.push_back( nSharedTopRow); 403 rNewSharedRows.push_back( rCell.aPos.Row() - 1); 404 rNewSharedRows.push_back( rCell.aPos.Row() + 1); 405 rNewSharedRows.push_back( nSharedTopRow + nSharedLength - 1); 406 } 407 } 408 #endif 409 410 // Have the dying formula cell stop listening. 411 // If in a shared formula group this ends the group listening. 412 rCell.EndListeningTo(GetDoc()); 413 } 414 415 sc::SharedFormulaUtil::unshareFormulaCell(aPos, rCell); 416 } 417 418 void ScColumn::StartListeningUnshared( const std::vector<SCROW>& rNewSharedRows ) 419 { 420 assert(rNewSharedRows.empty() || rNewSharedRows.size() == 2 || rNewSharedRows.size() == 4); 421 ScDocument& rDoc = GetDoc(); 422 if (rNewSharedRows.empty() || rDoc.IsDelayedFormulaGrouping()) 423 return; 424 425 auto pPosSet = std::make_shared<sc::ColumnBlockPositionSet>(rDoc); 426 sc::StartListeningContext aStartCxt(rDoc, pPosSet); 427 sc::EndListeningContext aEndCxt(rDoc, pPosSet); 428 if (rNewSharedRows.size() >= 2) 429 { 430 if(!rDoc.CanDelayStartListeningFormulaCells( this, rNewSharedRows[0], rNewSharedRows[1])) 431 StartListeningFormulaCells(aStartCxt, aEndCxt, rNewSharedRows[0], rNewSharedRows[1]); 432 } 433 if (rNewSharedRows.size() >= 4) 434 { 435 if(!rDoc.CanDelayStartListeningFormulaCells( this, rNewSharedRows[2], rNewSharedRows[3])) 436 StartListeningFormulaCells(aStartCxt, aEndCxt, rNewSharedRows[2], rNewSharedRows[3]); 437 } 438 } 439 440 namespace { 441 442 class AttachFormulaCellsHandler 443 { 444 sc::StartListeningContext& mrCxt; 445 446 public: 447 explicit AttachFormulaCellsHandler(sc::StartListeningContext& rCxt) 448 : mrCxt(rCxt) {} 449 450 void operator() (size_t /*nRow*/, ScFormulaCell* pCell) 451 { 452 pCell->StartListeningTo(mrCxt); 453 } 454 }; 455 456 class DetachFormulaCellsHandler 457 { 458 ScDocument& mrDoc; 459 sc::EndListeningContext* mpCxt; 460 461 public: 462 DetachFormulaCellsHandler( ScDocument& rDoc, sc::EndListeningContext* pCxt ) : 463 mrDoc(rDoc), mpCxt(pCxt) {} 464 465 void operator() (size_t /*nRow*/, ScFormulaCell* pCell) 466 { 467 if (mpCxt) 468 pCell->EndListeningTo(*mpCxt); 469 else 470 pCell->EndListeningTo(mrDoc); 471 } 472 }; 473 474 } 475 476 void ScColumn::DetachFormulaCells( 477 const sc::CellStoreType::position_type& aPos, size_t nLength, std::vector<SCROW>* pNewSharedRows ) 478 { 479 const size_t nRow = aPos.first->position + aPos.second; 480 const size_t nNextTopRow = nRow + nLength; // start row of next formula group. 481 482 bool bLowerSplitOff = false; 483 if (pNewSharedRows && !GetDoc().IsClipOrUndo()) 484 { 485 const ScFormulaCell* pFC = sc::SharedFormulaUtil::getSharedTopFormulaCell(aPos); 486 if (pFC) 487 { 488 const SCROW nTopRow = pFC->GetSharedTopRow(); 489 const SCROW nBotRow = nTopRow + pFC->GetSharedLength() - 1; 490 // nTopRow <= nRow <= nBotRow, because otherwise pFC would not exist. 491 if (nTopRow < static_cast<SCROW>(nRow)) 492 { 493 // Upper part will be split off. 494 pNewSharedRows->push_back(nTopRow); 495 pNewSharedRows->push_back(nRow - 1); 496 } 497 if (static_cast<SCROW>(nNextTopRow) <= nBotRow) 498 { 499 // Lower part will be split off. 500 pNewSharedRows->push_back(nNextTopRow); 501 pNewSharedRows->push_back(nBotRow); 502 bLowerSplitOff = true; 503 } 504 } 505 } 506 507 // Split formula grouping at the top and bottom boundaries. 508 sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, nullptr); 509 510 if (nLength > 0 && GetDoc().ValidRow(nNextTopRow)) 511 { 512 if (pNewSharedRows && !bLowerSplitOff && !GetDoc().IsClipOrUndo()) 513 { 514 sc::CellStoreType::position_type aPos2 = maCells.position(aPos.first, nNextTopRow-1); 515 const ScFormulaCell* pFC = sc::SharedFormulaUtil::getSharedTopFormulaCell(aPos2); 516 if (pFC) 517 { 518 const SCROW nTopRow = pFC->GetSharedTopRow(); 519 const SCROW nBotRow = nTopRow + pFC->GetSharedLength() - 1; 520 // nRow < nTopRow < nNextTopRow <= nBotRow 521 if (static_cast<SCROW>(nNextTopRow) <= nBotRow) 522 { 523 // Lower part will be split off. 524 pNewSharedRows->push_back(nNextTopRow); 525 pNewSharedRows->push_back(nBotRow); 526 } 527 } 528 } 529 530 sc::CellStoreType::position_type aPos2 = maCells.position(aPos.first, nNextTopRow); 531 sc::SharedFormulaUtil::splitFormulaCellGroup(aPos2, nullptr); 532 } 533 534 if (GetDoc().IsClipOrUndo()) 535 return; 536 537 DetachFormulaCellsHandler aFunc(GetDoc(), nullptr); 538 sc::ProcessFormula(aPos.first, maCells, nRow, nNextTopRow-1, aFunc); 539 } 540 541 void ScColumn::AttachFormulaCells( sc::StartListeningContext& rCxt, SCROW nRow1, SCROW nRow2 ) 542 { 543 sc::CellStoreType::position_type aPos = maCells.position(nRow1); 544 sc::CellStoreType::iterator it = aPos.first; 545 546 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos); 547 if (GetDoc().ValidRow(nRow2+1)) 548 { 549 aPos = maCells.position(it, nRow2+1); 550 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos); 551 } 552 553 if (GetDoc().IsClipOrUndo()) 554 return; 555 556 AttachFormulaCellsHandler aFunc(rCxt); 557 sc::ProcessFormula(it, maCells, nRow1, nRow2, aFunc); 558 } 559 560 void ScColumn::DetachFormulaCells( sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2 ) 561 { 562 sc::CellStoreType::position_type aPos = maCells.position(nRow1); 563 sc::CellStoreType::iterator it = aPos.first; 564 565 // Split formula grouping at the top and bottom boundaries. 566 sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, &rCxt); 567 if (GetDoc().ValidRow(nRow2+1)) 568 { 569 aPos = maCells.position(it, nRow2+1); 570 sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, &rCxt); 571 } 572 573 if (GetDoc().IsClipOrUndo()) 574 return; 575 576 DetachFormulaCellsHandler aFunc(GetDoc(), &rCxt); 577 sc::ProcessFormula(it, maCells, nRow1, nRow2, aFunc); 578 } 579 580 static void lcl_AddFormulaGroupBoundaries(const sc::CellStoreType::position_type& rPos, 581 std::vector<SCROW>& rNewSharedRows ) 582 { 583 sc::CellStoreType::iterator itRet = rPos.first; 584 if (itRet->type != sc::element_type_formula) 585 return; 586 587 ScFormulaCell& rFC = *sc::formula_block::at(*itRet->data, rPos.second); 588 if ( rFC.IsShared() ) 589 { 590 const SCROW nSharedTopRow = rFC.GetSharedTopRow(); 591 const SCROW nSharedLength = rFC.GetSharedLength(); 592 rNewSharedRows.push_back( nSharedTopRow); 593 rNewSharedRows.push_back( nSharedTopRow + nSharedLength - 1); 594 } 595 else 596 { 597 const SCROW nRow = rFC.aPos.Row(); 598 rNewSharedRows.push_back( nRow); 599 rNewSharedRows.push_back( nRow); 600 } 601 } 602 603 sc::CellStoreType::iterator ScColumn::GetPositionToInsert( const sc::CellStoreType::iterator& it, SCROW nRow, 604 std::vector<SCROW>& rNewSharedRows, bool bInsertFormula ) 605 { 606 // See if we are overwriting an existing formula cell. 607 sc::CellStoreType::position_type aPos = maCells.position(it, nRow); 608 sc::CellStoreType::iterator itRet = aPos.first; 609 610 if (itRet->type == sc::element_type_formula) 611 { 612 ScFormulaCell& rCell = *sc::formula_block::at(*itRet->data, aPos.second); 613 DetachFormulaCell(aPos, rCell, rNewSharedRows); 614 } 615 else if (bInsertFormula && !GetDoc().IsClipOrUndo()) 616 { 617 if (nRow > 0) 618 { 619 sc::CellStoreType::position_type aPosBefore = maCells.position(maCells.begin(), nRow-1); 620 lcl_AddFormulaGroupBoundaries(aPosBefore, rNewSharedRows); 621 } 622 if (nRow < GetDoc().MaxRow()) 623 { 624 sc::CellStoreType::position_type aPosAfter = maCells.position(maCells.begin(), nRow+1); 625 lcl_AddFormulaGroupBoundaries(aPosAfter, rNewSharedRows); 626 } 627 } 628 629 return itRet; 630 } 631 632 void ScColumn::AttachNewFormulaCell( 633 const sc::CellStoreType::iterator& itPos, SCROW nRow, ScFormulaCell& rCell, 634 const std::vector<SCROW>& rNewSharedRows, 635 bool bJoin, sc::StartListeningType eListenType ) 636 { 637 AttachNewFormulaCell(maCells.position(itPos, nRow), rCell, rNewSharedRows, bJoin, eListenType); 638 } 639 640 void ScColumn::AttachNewFormulaCell( 641 const sc::CellStoreType::position_type& aPos, ScFormulaCell& rCell, 642 const std::vector<SCROW>& rNewSharedRows, 643 bool bJoin, sc::StartListeningType eListenType ) 644 { 645 if (bJoin) 646 // See if this new formula cell can join an existing shared formula group. 647 JoinNewFormulaCell(aPos, rCell); 648 649 // When we insert from the Clipboard we still have wrong (old) References! 650 // First they are rewired in CopyBlockFromClip via UpdateReference and the 651 // we call StartListeningFromClip and BroadcastFromClip. 652 // If we insert into the Clipboard/andoDoc, we do not use a Broadcast. 653 // After Import we call CalcAfterLoad and in there Listening. 654 ScDocument& rDocument = GetDoc(); 655 if (rDocument.IsClipOrUndo() || rDocument.IsInsertingFromOtherDoc()) 656 return; 657 658 switch (eListenType) 659 { 660 case sc::ConvertToGroupListening: 661 { 662 auto pPosSet = std::make_shared<sc::ColumnBlockPositionSet>(rDocument); 663 sc::StartListeningContext aStartCxt(rDocument, pPosSet); 664 sc::EndListeningContext aEndCxt(rDocument, pPosSet); 665 SCROW nStartRow, nEndRow; 666 nStartRow = nEndRow = aPos.first->position + aPos.second; 667 for (const SCROW nR : rNewSharedRows) 668 { 669 if (nStartRow > nR) 670 nStartRow = nR; 671 if (nEndRow < nR) 672 nEndRow = nR; 673 } 674 StartListeningFormulaCells(aStartCxt, aEndCxt, nStartRow, nEndRow); 675 } 676 break; 677 case sc::SingleCellListening: 678 rCell.StartListeningTo(rDocument); 679 StartListeningUnshared( rNewSharedRows); 680 break; 681 case sc::NoListening: 682 default: 683 if (!rNewSharedRows.empty()) 684 { 685 assert(rNewSharedRows.size() == 2 || rNewSharedRows.size() == 4); 686 // Calling SetNeedsListeningGroup() with a top row sets it to 687 // all affected formula cells of that group. 688 const ScFormulaCell* pFC = GetFormulaCell( rNewSharedRows[0]); 689 assert(pFC); // that *is* supposed to be a top row 690 if (pFC && !pFC->NeedsListening()) 691 SetNeedsListeningGroup( rNewSharedRows[0]); 692 if (rNewSharedRows.size() > 2) 693 { 694 pFC = GetFormulaCell( rNewSharedRows[2]); 695 assert(pFC); // that *is* supposed to be a top row 696 if (pFC && !pFC->NeedsListening()) 697 SetNeedsListeningGroup( rNewSharedRows[2]); 698 } 699 } 700 break; 701 } 702 703 if (!rDocument.IsCalcingAfterLoad()) 704 rCell.SetDirty(); 705 } 706 707 void ScColumn::AttachNewFormulaCells( const sc::CellStoreType::position_type& aPos, size_t nLength, 708 std::vector<SCROW>& rNewSharedRows ) 709 { 710 // Make sure the whole length consists of formula cells. 711 if (aPos.first->type != sc::element_type_formula) 712 return; 713 714 if (aPos.first->size < aPos.second + nLength) 715 // Block is shorter than specified length. 716 return; 717 718 // Join the top and bottom cells only. 719 ScFormulaCell* pCell1 = sc::formula_block::at(*aPos.first->data, aPos.second); 720 JoinNewFormulaCell(aPos, *pCell1); 721 722 sc::CellStoreType::position_type aPosLast = aPos; 723 aPosLast.second += nLength - 1; 724 ScFormulaCell* pCell2 = sc::formula_block::at(*aPosLast.first->data, aPosLast.second); 725 JoinNewFormulaCell(aPosLast, *pCell2); 726 727 ScDocument& rDocument = GetDoc(); 728 if (rDocument.IsClipOrUndo() || rDocument.IsInsertingFromOtherDoc()) 729 return; 730 731 const bool bShared = pCell1->IsShared() || pCell2->IsShared(); 732 if (bShared) 733 { 734 const SCROW nTopRow = (pCell1->IsShared() ? pCell1->GetSharedTopRow() : pCell1->aPos.Row()); 735 const SCROW nBotRow = (pCell2->IsShared() ? 736 pCell2->GetSharedTopRow() + pCell2->GetSharedLength() - 1 : pCell2->aPos.Row()); 737 if (rNewSharedRows.empty()) 738 { 739 rNewSharedRows.push_back( nTopRow); 740 rNewSharedRows.push_back( nBotRow); 741 } 742 else if (rNewSharedRows.size() == 2) 743 { 744 // Combine into one span. 745 if (rNewSharedRows[0] > nTopRow) 746 rNewSharedRows[0] = nTopRow; 747 if (rNewSharedRows[1] < nBotRow) 748 rNewSharedRows[1] = nBotRow; 749 } 750 else if (rNewSharedRows.size() == 4) 751 { 752 // Merge into one span. 753 // The original two spans are ordered from top to bottom. 754 std::vector<SCROW> aRows { std::min( rNewSharedRows[0], nTopRow), std::max( rNewSharedRows[3], nBotRow) }; 755 rNewSharedRows.swap( aRows); 756 } 757 else 758 { 759 assert(!"rNewSharedRows?"); 760 } 761 } 762 StartListeningUnshared( rNewSharedRows); 763 764 sc::StartListeningContext aCxt(rDocument); 765 ScFormulaCell** pp = &sc::formula_block::at(*aPos.first->data, aPos.second); 766 ScFormulaCell** ppEnd = pp + nLength; 767 for (; pp != ppEnd; ++pp) 768 { 769 if (!bShared) 770 (*pp)->StartListeningTo(aCxt); 771 if (!rDocument.IsCalcingAfterLoad()) 772 (*pp)->SetDirty(); 773 } 774 } 775 776 void ScColumn::BroadcastNewCell( SCROW nRow ) 777 { 778 // When we insert from the Clipboard we still have wrong (old) References! 779 // First they are rewired in CopyBlockFromClip via UpdateReference and the 780 // we call StartListeningFromClip and BroadcastFromClip. 781 // If we insert into the Clipboard/andoDoc, we do not use a Broadcast. 782 // After Import we call CalcAfterLoad and in there Listening. 783 if (GetDoc().IsClipOrUndo() || GetDoc().IsInsertingFromOtherDoc() || GetDoc().IsCalcingAfterLoad()) 784 return; 785 786 Broadcast(nRow); 787 } 788 789 bool ScColumn::UpdateScriptType( sc::CellTextAttr& rAttr, SCROW nRow, sc::CellStoreType::iterator& itr ) 790 { 791 if (rAttr.mnScriptType != SvtScriptType::UNKNOWN) 792 // Already updated. Nothing to do. 793 return false; 794 795 // Script type not yet determined. Determine the real script 796 // type, and store it. 797 const ScPatternAttr* pPattern = GetPattern(nRow); 798 if (!pPattern) 799 return false; 800 801 sc::CellStoreType::position_type pos = maCells.position(itr, nRow); 802 itr = pos.first; 803 size_t nOffset = pos.second; 804 ScRefCellValue aCell = GetCellValue( itr, nOffset ); 805 ScAddress aPos(nCol, nRow, nTab); 806 807 ScDocument& rDocument = GetDoc(); 808 const SfxItemSet* pCondSet = nullptr; 809 ScConditionalFormatList* pCFList = rDocument.GetCondFormList(nTab); 810 if (pCFList) 811 { 812 const ScCondFormatItem& rItem = 813 pPattern->GetItem(ATTR_CONDITIONAL); 814 const ScCondFormatIndexes& rData = rItem.GetCondFormatData(); 815 pCondSet = rDocument.GetCondResult(aCell, aPos, *pCFList, rData); 816 } 817 818 SvNumberFormatter* pFormatter = rDocument.GetFormatTable(); 819 820 const Color* pColor; 821 sal_uInt32 nFormat = pPattern->GetNumberFormat(pFormatter, pCondSet); 822 OUString aStr = ScCellFormat::GetString(aCell, nFormat, &pColor, *pFormatter, rDocument); 823 824 // Store the real script type to the array. 825 rAttr.mnScriptType = rDocument.GetStringScriptType(aStr); 826 return true; 827 } 828 829 namespace { 830 831 class DeleteAreaHandler 832 { 833 ScDocument& mrDoc; 834 std::vector<ScFormulaCell*> maFormulaCells; 835 sc::SingleColumnSpanSet maDeleteRanges; 836 837 bool mbNumeric:1; 838 bool mbString:1; 839 bool mbFormula:1; 840 bool mbDateTime:1; 841 ScColumn& mrCol; 842 843 public: 844 DeleteAreaHandler(ScDocument& rDoc, InsertDeleteFlags nDelFlag, ScColumn& rCol) : 845 mrDoc(rDoc), 846 maDeleteRanges(rDoc.GetSheetLimits()), 847 mbNumeric(nDelFlag & InsertDeleteFlags::VALUE), 848 mbString(nDelFlag & InsertDeleteFlags::STRING), 849 mbFormula(nDelFlag & InsertDeleteFlags::FORMULA), 850 mbDateTime(nDelFlag & InsertDeleteFlags::DATETIME), 851 mrCol(rCol) {} 852 853 void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize) 854 { 855 switch (node.type) 856 { 857 case sc::element_type_numeric: 858 // Numeric type target datetime and number, thus we have a dedicated function 859 if (!mbNumeric && !mbDateTime) 860 return; 861 862 // If numeric and datetime selected, delete full range 863 if (mbNumeric && mbDateTime) 864 break; 865 866 deleteNumeric(node, nOffset, nDataSize); 867 return; 868 case sc::element_type_string: 869 case sc::element_type_edittext: 870 if (!mbString) 871 return; 872 break; 873 case sc::element_type_formula: 874 { 875 if (!mbFormula) 876 return; 877 878 sc::formula_block::iterator it = sc::formula_block::begin(*node.data) + nOffset; 879 sc::formula_block::iterator itEnd = it + nDataSize; 880 maFormulaCells.insert(maFormulaCells.end(), it, itEnd); 881 } 882 break; 883 case sc::element_type_empty: 884 default: 885 return; 886 } 887 888 // Tag these cells for deletion. 889 SCROW nRow1 = node.position + nOffset; 890 SCROW nRow2 = nRow1 + nDataSize - 1; 891 maDeleteRanges.set(nRow1, nRow2, true); 892 } 893 894 void deleteNumeric(const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize) 895 { 896 size_t nStart = node.position + nOffset; 897 size_t nElements = 1; 898 bool bLastTypeDateTime = isDateTime(nStart); // true = datetime, false = numeric 899 size_t nCount = nStart + nDataSize; 900 901 for (size_t i = nStart + 1; i < nCount; i++) 902 { 903 bool bIsDateTime = isDateTime(i); 904 905 // same type as previous 906 if (bIsDateTime == bLastTypeDateTime) 907 { 908 nElements++; 909 } 910 // type switching 911 else 912 { 913 deleteNumberOrDateTime(nStart, nStart + nElements - 1, bLastTypeDateTime); 914 nStart += nElements; 915 nElements = 1; 916 } 917 918 bLastTypeDateTime = bIsDateTime; 919 } 920 921 // delete last cells 922 deleteNumberOrDateTime(nStart, nStart + nElements - 1, bLastTypeDateTime); 923 } 924 925 void deleteNumberOrDateTime(SCROW nRow1, SCROW nRow2, bool dateTime) 926 { 927 if (!dateTime && !mbNumeric) // numeric flag must be selected 928 return; 929 if (dateTime && !mbDateTime) // datetime flag must be selected 930 return; 931 maDeleteRanges.set(nRow1, nRow2, true); 932 } 933 934 bool isDateTime(size_t position) 935 { 936 SvNumFormatType nType = mrDoc.GetFormatTable()->GetType( 937 mrCol.GetAttr(position, ATTR_VALUE_FORMAT).GetValue()); 938 939 return (nType == SvNumFormatType::DATE) || (nType == SvNumFormatType::TIME) || 940 (nType == SvNumFormatType::DATETIME); 941 } 942 943 void endFormulas() 944 { 945 mrDoc.EndListeningFormulaCells(maFormulaCells); 946 } 947 948 sc::SingleColumnSpanSet& getSpans() 949 { 950 return maDeleteRanges; 951 } 952 }; 953 954 class EmptyCells 955 { 956 ScColumn& mrColumn; 957 sc::ColumnBlockPosition& mrPos; 958 959 static void splitFormulaGrouping(const sc::CellStoreType::position_type& rPos) 960 { 961 if (rPos.first->type == sc::element_type_formula) 962 { 963 ScFormulaCell& rCell = *sc::formula_block::at(*rPos.first->data, rPos.second); 964 sc::SharedFormulaUtil::unshareFormulaCell(rPos, rCell); 965 } 966 } 967 968 public: 969 EmptyCells( sc::ColumnBlockPosition& rPos, ScColumn& rColumn ) : 970 mrColumn(rColumn), mrPos(rPos) {} 971 972 void operator() (const sc::RowSpan& rSpan) 973 { 974 sc::CellStoreType& rCells = mrColumn.GetCellStore(); 975 976 // First, split formula grouping at the top and bottom boundaries 977 // before emptying the cells. 978 sc::CellStoreType::position_type aPos = rCells.position(mrPos.miCellPos, rSpan.mnRow1); 979 splitFormulaGrouping(aPos); 980 aPos = rCells.position(aPos.first, rSpan.mnRow2); 981 splitFormulaGrouping(aPos); 982 983 mrPos.miCellPos = rCells.set_empty(mrPos.miCellPos, rSpan.mnRow1, rSpan.mnRow2); 984 mrPos.miCellTextAttrPos = mrColumn.GetCellAttrStore().set_empty(mrPos.miCellTextAttrPos, rSpan.mnRow1, rSpan.mnRow2); 985 } 986 }; 987 988 } 989 990 void ScColumn::DeleteCells( 991 sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2, InsertDeleteFlags nDelFlag, 992 sc::SingleColumnSpanSet& rDeleted ) 993 { 994 // Determine which cells to delete based on the deletion flags. 995 DeleteAreaHandler aFunc(GetDoc(), nDelFlag, *this); 996 sc::CellStoreType::iterator itPos = maCells.position(rBlockPos.miCellPos, nRow1).first; 997 sc::ProcessBlock(itPos, maCells, aFunc, nRow1, nRow2); 998 aFunc.endFormulas(); // Have the formula cells stop listening. 999 1000 // Get the deletion spans. 1001 sc::SingleColumnSpanSet::SpansType aSpans; 1002 aFunc.getSpans().getSpans(aSpans); 1003 1004 // Delete the cells for real. 1005 // tdf#139820: Deleting in reverse order is more efficient. 1006 std::for_each(aSpans.rbegin(), aSpans.rend(), EmptyCells(rBlockPos, *this)); 1007 CellStorageModified(); 1008 1009 aFunc.getSpans().swap(rDeleted); 1010 } 1011 1012 void ScColumn::DeleteArea( 1013 SCROW nStartRow, SCROW nEndRow, InsertDeleteFlags nDelFlag, bool bBroadcast, 1014 sc::ColumnSpanSet* pBroadcastSpans ) 1015 { 1016 InsertDeleteFlags nContMask = InsertDeleteFlags::CONTENTS; 1017 // InsertDeleteFlags::NOCAPTIONS needs to be passed too, if InsertDeleteFlags::NOTE is set 1018 if( nDelFlag & InsertDeleteFlags::NOTE ) 1019 nContMask |= InsertDeleteFlags::NOCAPTIONS; 1020 InsertDeleteFlags nContFlag = nDelFlag & nContMask; 1021 1022 sc::SingleColumnSpanSet aDeletedRows(GetDoc().GetSheetLimits()); 1023 1024 sc::ColumnBlockPosition aBlockPos; 1025 InitBlockPosition(aBlockPos); 1026 1027 if (!IsEmptyData() && nContFlag != InsertDeleteFlags::NONE) 1028 { 1029 DeleteCells(aBlockPos, nStartRow, nEndRow, nDelFlag, aDeletedRows); 1030 if (pBroadcastSpans) 1031 { 1032 sc::SingleColumnSpanSet::SpansType aSpans; 1033 aDeletedRows.getSpans(aSpans); 1034 for (const auto& rSpan : aSpans) 1035 pBroadcastSpans->set(GetDoc(), nTab, nCol, rSpan.mnRow1, rSpan.mnRow2, true); 1036 } 1037 } 1038 1039 if (nDelFlag & InsertDeleteFlags::NOTE) 1040 { 1041 bool bForgetCaptionOwnership = ((nDelFlag & InsertDeleteFlags::FORGETCAPTIONS) != InsertDeleteFlags::NONE); 1042 DeleteCellNotes(aBlockPos, nStartRow, nEndRow, bForgetCaptionOwnership); 1043 } 1044 1045 if (nDelFlag & InsertDeleteFlags::SPARKLINES) 1046 { 1047 DeleteSparklineCells(aBlockPos, nStartRow, nEndRow); 1048 } 1049 1050 if ( nDelFlag & InsertDeleteFlags::EDITATTR ) 1051 { 1052 OSL_ENSURE( nContFlag == InsertDeleteFlags::NONE, "DeleteArea: Wrong Flags" ); 1053 RemoveEditAttribs(aBlockPos, nStartRow, nEndRow); 1054 } 1055 1056 // Delete attributes just now 1057 if ((nDelFlag & InsertDeleteFlags::ATTRIB) == InsertDeleteFlags::ATTRIB) 1058 pAttrArray->DeleteArea( nStartRow, nEndRow ); 1059 else if ((nDelFlag & InsertDeleteFlags::HARDATTR) == InsertDeleteFlags::HARDATTR) 1060 pAttrArray->DeleteHardAttr( nStartRow, nEndRow ); 1061 1062 if (bBroadcast) 1063 { 1064 // Broadcast on only cells that were deleted; no point broadcasting on 1065 // cells that were already empty before the deletion. 1066 std::vector<SCROW> aRows; 1067 aDeletedRows.getRows(aRows); 1068 BroadcastCells(aRows, SfxHintId::ScDataChanged); 1069 } 1070 } 1071 1072 void ScColumn::InitBlockPosition( sc::ColumnBlockPosition& rBlockPos ) 1073 { 1074 rBlockPos.miBroadcasterPos = maBroadcasters.begin(); 1075 rBlockPos.miCellNotePos = maCellNotes.begin(); 1076 rBlockPos.miCellTextAttrPos = maCellTextAttrs.begin(); 1077 rBlockPos.miCellPos = maCells.begin(); 1078 rBlockPos.miSparklinePos = maSparklines.begin(); 1079 } 1080 1081 void ScColumn::InitBlockPosition( sc::ColumnBlockConstPosition& rBlockPos ) const 1082 { 1083 rBlockPos.miCellNotePos = maCellNotes.begin(); 1084 rBlockPos.miCellTextAttrPos = maCellTextAttrs.begin(); 1085 rBlockPos.miCellPos = maCells.begin(); 1086 } 1087 1088 namespace { 1089 1090 class CopyAttrArrayByRange 1091 { 1092 ScAttrArray& mrDestAttrArray; 1093 ScAttrArray& mrSrcAttrArray; 1094 tools::Long mnRowOffset; 1095 public: 1096 CopyAttrArrayByRange(ScAttrArray& rDestAttrArray, ScAttrArray& rSrcAttrArray, tools::Long nRowOffset) : 1097 mrDestAttrArray(rDestAttrArray), mrSrcAttrArray(rSrcAttrArray), mnRowOffset(nRowOffset) {} 1098 1099 void operator() (const sc::RowSpan& rSpan) 1100 { 1101 mrDestAttrArray.CopyAreaSafe( 1102 rSpan.mnRow1+mnRowOffset, rSpan.mnRow2+mnRowOffset, mnRowOffset, mrSrcAttrArray); 1103 } 1104 }; 1105 1106 class CopyCellsFromClipHandler 1107 { 1108 sc::CopyFromClipContext& mrCxt; 1109 ScColumn& mrSrcCol; 1110 ScColumn& mrDestCol; 1111 SCTAB mnTab; 1112 SCCOL mnCol; 1113 SCTAB mnSrcTab; 1114 SCCOL mnSrcCol; 1115 tools::Long mnRowOffset; 1116 sc::ColumnBlockPosition maDestBlockPos; 1117 sc::ColumnBlockPosition* mpDestBlockPos; // to save it for next iteration. 1118 svl::SharedStringPool* mpSharedStringPool; 1119 1120 void insertRefCell(SCROW nSrcRow, SCROW nDestRow) 1121 { 1122 ScAddress aSrcPos(mnSrcCol, nSrcRow, mnSrcTab); 1123 ScAddress aDestPos(mnCol, nDestRow, mnTab); 1124 ScSingleRefData aRef; 1125 aRef.InitAddress(aSrcPos); 1126 aRef.SetFlag3D(true); 1127 1128 ScTokenArray aArr(*mrCxt.getDestDoc()); 1129 aArr.AddSingleReference(aRef); 1130 1131 mrDestCol.SetFormulaCell( 1132 maDestBlockPos, nDestRow, new ScFormulaCell(mrDestCol.GetDoc(), aDestPos, aArr)); 1133 } 1134 1135 void duplicateNotes(SCROW nStartRow, size_t nDataSize, bool bCloneCaption ) 1136 { 1137 mrSrcCol.DuplicateNotes(nStartRow, nDataSize, mrDestCol, maDestBlockPos, bCloneCaption, mnRowOffset); 1138 } 1139 1140 void duplicateSparklines(SCROW nStartRow, size_t nDataSize) 1141 { 1142 mrSrcCol.DuplicateSparklines(nStartRow, nDataSize, mrDestCol, maDestBlockPos, mnRowOffset); 1143 } 1144 1145 public: 1146 CopyCellsFromClipHandler(sc::CopyFromClipContext& rCxt, ScColumn& rSrcCol, ScColumn& rDestCol, SCTAB nDestTab, SCCOL nDestCol, tools::Long nRowOffset, svl::SharedStringPool* pSharedStringPool) : 1147 mrCxt(rCxt), 1148 mrSrcCol(rSrcCol), 1149 mrDestCol(rDestCol), 1150 mnTab(nDestTab), 1151 mnCol(nDestCol), 1152 mnSrcTab(rSrcCol.GetTab()), 1153 mnSrcCol(rSrcCol.GetCol()), 1154 mnRowOffset(nRowOffset), 1155 mpDestBlockPos(mrCxt.getBlockPosition(nDestTab, nDestCol)), 1156 mpSharedStringPool(pSharedStringPool) 1157 { 1158 if (mpDestBlockPos) 1159 { 1160 { 1161 // Re-initialize the broadcaster position hint, which may have 1162 // become invalid by the time it gets here... 1163 sc::ColumnBlockPosition aTempPos; 1164 mrDestCol.InitBlockPosition(aTempPos); 1165 mpDestBlockPos->miBroadcasterPos = aTempPos.miBroadcasterPos; 1166 } 1167 maDestBlockPos = *mpDestBlockPos; 1168 } 1169 else 1170 mrDestCol.InitBlockPosition(maDestBlockPos); 1171 } 1172 1173 ~CopyCellsFromClipHandler() 1174 { 1175 if (mpDestBlockPos) 1176 // Don't forget to save this to the context! 1177 *mpDestBlockPos = maDestBlockPos; 1178 } 1179 1180 void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize) 1181 { 1182 SCROW nSrcRow1 = node.position + nOffset; 1183 bool bCopyCellNotes = mrCxt.isCloneNotes(); 1184 bool bCopySparklines = mrCxt.isCloneSparklines(); 1185 1186 InsertDeleteFlags nFlags = mrCxt.getInsertFlag(); 1187 1188 if (node.type == sc::element_type_empty) 1189 { 1190 if (bCopyCellNotes && !mrCxt.isSkipEmptyCells()) 1191 { 1192 bool bCloneCaption = (nFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE; 1193 duplicateNotes(nSrcRow1, nDataSize, bCloneCaption ); 1194 } 1195 if (bCopySparklines) // If there is a sparkline is it empty? 1196 { 1197 duplicateSparklines(nSrcRow1, nDataSize); 1198 } 1199 return; 1200 } 1201 1202 bool bNumeric = (nFlags & InsertDeleteFlags::VALUE) != InsertDeleteFlags::NONE; 1203 bool bDateTime = (nFlags & InsertDeleteFlags::DATETIME) != InsertDeleteFlags::NONE; 1204 bool bString = (nFlags & InsertDeleteFlags::STRING) != InsertDeleteFlags::NONE; 1205 bool bBoolean = (nFlags & InsertDeleteFlags::SPECIAL_BOOLEAN) != InsertDeleteFlags::NONE; 1206 bool bFormula = (nFlags & InsertDeleteFlags::FORMULA) != InsertDeleteFlags::NONE; 1207 1208 bool bAsLink = mrCxt.isAsLink(); 1209 1210 switch (node.type) 1211 { 1212 case sc::element_type_numeric: 1213 { 1214 // We need to copy numeric cells individually because of date type check. 1215 sc::numeric_block::const_iterator it = sc::numeric_block::begin(*node.data); 1216 std::advance(it, nOffset); 1217 sc::numeric_block::const_iterator itEnd = it; 1218 std::advance(itEnd, nDataSize); 1219 for (SCROW nSrcRow = nSrcRow1; it != itEnd; ++it, ++nSrcRow) 1220 { 1221 bool bCopy = mrCxt.isDateCell(mrSrcCol, nSrcRow) ? bDateTime : bNumeric; 1222 if (!bCopy) 1223 continue; 1224 1225 if (bAsLink) 1226 insertRefCell(nSrcRow, nSrcRow + mnRowOffset); 1227 else 1228 mrDestCol.SetValue(maDestBlockPos, nSrcRow + mnRowOffset, *it); 1229 } 1230 } 1231 break; 1232 case sc::element_type_string: 1233 { 1234 if (!bString) 1235 break; 1236 1237 sc::string_block::const_iterator it = sc::string_block::begin(*node.data); 1238 std::advance(it, nOffset); 1239 sc::string_block::const_iterator itEnd = it; 1240 std::advance(itEnd, nDataSize); 1241 for (SCROW nSrcRow = nSrcRow1; it != itEnd; ++it, ++nSrcRow) 1242 { 1243 if (bAsLink) 1244 insertRefCell(nSrcRow, nSrcRow + mnRowOffset); 1245 else if (mpSharedStringPool) 1246 { 1247 // Re-intern the string if source is a different document. 1248 svl::SharedString aInterned = mpSharedStringPool->intern( (*it).getString()); 1249 mrDestCol.SetRawString(maDestBlockPos, nSrcRow + mnRowOffset, aInterned); 1250 } 1251 else 1252 mrDestCol.SetRawString(maDestBlockPos, nSrcRow + mnRowOffset, *it); 1253 } 1254 } 1255 break; 1256 case sc::element_type_edittext: 1257 { 1258 if (!bString) 1259 break; 1260 1261 sc::edittext_block::const_iterator it = sc::edittext_block::begin(*node.data); 1262 std::advance(it, nOffset); 1263 sc::edittext_block::const_iterator itEnd = it; 1264 std::advance(itEnd, nDataSize); 1265 for (SCROW nSrcRow = nSrcRow1; it != itEnd; ++it, ++nSrcRow) 1266 { 1267 1268 if (bAsLink) 1269 insertRefCell(nSrcRow, nSrcRow + mnRowOffset); 1270 else 1271 mrDestCol.SetEditText(maDestBlockPos, nSrcRow + mnRowOffset, **it); 1272 } 1273 } 1274 break; 1275 case sc::element_type_formula: 1276 { 1277 sc::formula_block::const_iterator it = sc::formula_block::begin(*node.data); 1278 std::advance(it, nOffset); 1279 sc::formula_block::const_iterator itEnd = it; 1280 std::advance(itEnd, nDataSize); 1281 for (SCROW nSrcRow = nSrcRow1; it != itEnd; ++it, ++nSrcRow) 1282 { 1283 ScFormulaCell& rSrcCell = **it; 1284 bool bForceFormula = false; 1285 if (bBoolean) 1286 { 1287 // See if the formula consists of =TRUE() or =FALSE(). 1288 const ScTokenArray* pCode = rSrcCell.GetCode(); 1289 if (pCode && pCode->GetLen() == 1) 1290 { 1291 const formula::FormulaToken* p = pCode->FirstToken(); 1292 if (p->GetOpCode() == ocTrue || p->GetOpCode() == ocFalse) 1293 // This is a boolean formula. 1294 bForceFormula = true; 1295 } 1296 } 1297 1298 ScAddress aDestPos(mnCol, nSrcRow + mnRowOffset, mnTab); 1299 if (bFormula || bForceFormula) 1300 { 1301 if (bAsLink) 1302 insertRefCell(nSrcRow, nSrcRow + mnRowOffset); 1303 else 1304 { 1305 mrDestCol.SetFormulaCell( 1306 maDestBlockPos, nSrcRow + mnRowOffset, 1307 new ScFormulaCell(rSrcCell, mrDestCol.GetDoc(), aDestPos), 1308 sc::SingleCellListening, 1309 rSrcCell.NeedsNumberFormat()); 1310 } 1311 } 1312 else if (bNumeric || bDateTime || bString) 1313 { 1314 // Always just copy the original row to the Undo Document; 1315 // do not create Value/string cells from formulas 1316 1317 FormulaError nErr = rSrcCell.GetErrCode(); 1318 if (nErr != FormulaError::NONE) 1319 { 1320 // error codes are cloned with values 1321 if (bNumeric) 1322 { 1323 if (bAsLink) 1324 insertRefCell(nSrcRow, nSrcRow + mnRowOffset); 1325 else 1326 { 1327 ScFormulaCell* pErrCell = new ScFormulaCell(mrDestCol.GetDoc(), aDestPos); 1328 pErrCell->SetErrCode(nErr); 1329 mrDestCol.SetFormulaCell( 1330 maDestBlockPos, nSrcRow + mnRowOffset, pErrCell); 1331 } 1332 } 1333 } 1334 else if (rSrcCell.IsEmptyDisplayedAsString()) 1335 { 1336 // Empty stays empty and doesn't become 0. 1337 continue; 1338 } 1339 else if (rSrcCell.IsValue()) 1340 { 1341 bool bCopy = mrCxt.isDateCell(mrSrcCol, nSrcRow) ? bDateTime : bNumeric; 1342 if (!bCopy) 1343 continue; 1344 1345 if (bAsLink) 1346 insertRefCell(nSrcRow, nSrcRow + mnRowOffset); 1347 else 1348 mrDestCol.SetValue(maDestBlockPos, nSrcRow + mnRowOffset, rSrcCell.GetValue()); 1349 } 1350 else if (bString) 1351 { 1352 svl::SharedString aStr = rSrcCell.GetString(); 1353 if (aStr.isEmpty()) 1354 // do not clone empty string 1355 continue; 1356 1357 if (bAsLink) 1358 insertRefCell(nSrcRow, nSrcRow + mnRowOffset); 1359 else if (rSrcCell.IsMultilineResult()) 1360 { 1361 // Clone as an edit text object. 1362 ScFieldEditEngine& rEngine = mrDestCol.GetDoc().GetEditEngine(); 1363 rEngine.SetTextCurrentDefaults(aStr.getString()); 1364 mrDestCol.SetEditText(maDestBlockPos, nSrcRow + mnRowOffset, rEngine.CreateTextObject()); 1365 } 1366 else if (mpSharedStringPool) 1367 { 1368 // Re-intern the string if source is a different document. 1369 svl::SharedString aInterned = mpSharedStringPool->intern( aStr.getString()); 1370 mrDestCol.SetRawString(maDestBlockPos, nSrcRow + mnRowOffset, aInterned); 1371 } 1372 else 1373 { 1374 mrDestCol.SetRawString(maDestBlockPos, nSrcRow + mnRowOffset, aStr); 1375 } 1376 } 1377 } 1378 } 1379 } 1380 break; 1381 default: 1382 ; 1383 } 1384 if (bCopyCellNotes) 1385 { 1386 bool bCloneCaption = (nFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE; 1387 duplicateNotes(nSrcRow1, nDataSize, bCloneCaption ); 1388 } 1389 if (bCopySparklines) 1390 { 1391 duplicateSparklines(nSrcRow1, nDataSize); 1392 } 1393 } 1394 }; 1395 1396 class CopyTextAttrsFromClipHandler 1397 { 1398 sc::CellTextAttrStoreType& mrAttrs; 1399 size_t mnDelta; 1400 sc::ColumnBlockPosition maDestBlockPos; 1401 sc::ColumnBlockPosition* mpDestBlockPos; // to save it for next iteration. 1402 1403 public: 1404 CopyTextAttrsFromClipHandler( sc::CopyFromClipContext& rCxt, sc::CellTextAttrStoreType& rAttrs, 1405 ScColumn& rDestCol, SCTAB nDestTab, SCCOL nDestCol, size_t nDelta ) : 1406 mrAttrs(rAttrs), 1407 mnDelta(nDelta), 1408 mpDestBlockPos(rCxt.getBlockPosition(nDestTab, nDestCol)) 1409 { 1410 if (mpDestBlockPos) 1411 maDestBlockPos.miCellTextAttrPos = mpDestBlockPos->miCellTextAttrPos; 1412 else 1413 rDestCol.InitBlockPosition(maDestBlockPos); 1414 } 1415 1416 ~CopyTextAttrsFromClipHandler() 1417 { 1418 if (mpDestBlockPos) 1419 // Don't forget to save this to the context! 1420 mpDestBlockPos->miCellTextAttrPos = maDestBlockPos.miCellTextAttrPos; 1421 } 1422 1423 void operator() ( const sc::CellTextAttrStoreType::value_type& aNode, size_t nOffset, size_t nDataSize ) 1424 { 1425 if (aNode.type != sc::element_type_celltextattr) 1426 return; 1427 1428 sc::celltextattr_block::const_iterator it = sc::celltextattr_block::begin(*aNode.data); 1429 std::advance(it, nOffset); 1430 sc::celltextattr_block::const_iterator itEnd = it; 1431 std::advance(itEnd, nDataSize); 1432 1433 size_t nPos = aNode.position + nOffset + mnDelta; 1434 maDestBlockPos.miCellTextAttrPos = mrAttrs.set(maDestBlockPos.miCellTextAttrPos, nPos, it, itEnd); 1435 } 1436 }; 1437 1438 } 1439 1440 // rColumn = source 1441 // nRow1, nRow2 = target position 1442 1443 void ScColumn::CopyFromClip( 1444 sc::CopyFromClipContext& rCxt, SCROW nRow1, SCROW nRow2, tools::Long nDy, ScColumn& rColumn ) 1445 { 1446 sc::ColumnBlockPosition* pBlockPos = rCxt.getBlockPosition(nTab, nCol); 1447 if (!pBlockPos) 1448 return; 1449 1450 if ((rCxt.getInsertFlag() & InsertDeleteFlags::ATTRIB) != InsertDeleteFlags::NONE) 1451 { 1452 if (rCxt.isSkipEmptyCells()) 1453 { 1454 // copy only attributes for non-empty cells between nRow1-nDy and nRow2-nDy. 1455 sc::SingleColumnSpanSet aSpanSet(GetDoc().GetSheetLimits()); 1456 aSpanSet.scan(rColumn, nRow1-nDy, nRow2-nDy); 1457 sc::SingleColumnSpanSet::SpansType aSpans; 1458 aSpanSet.getSpans(aSpans); 1459 std::for_each( 1460 aSpans.begin(), aSpans.end(), CopyAttrArrayByRange(*rColumn.pAttrArray, *pAttrArray, nDy)); 1461 } 1462 else 1463 rColumn.pAttrArray->CopyAreaSafe( nRow1, nRow2, nDy, *pAttrArray ); 1464 } 1465 if ((rCxt.getInsertFlag() & InsertDeleteFlags::CONTENTS) == InsertDeleteFlags::NONE) 1466 return; 1467 1468 ScDocument& rDocument = GetDoc(); 1469 if (rCxt.isAsLink() && rCxt.getInsertFlag() == InsertDeleteFlags::ALL) 1470 { 1471 // We also reference empty cells for "ALL" 1472 // InsertDeleteFlags::ALL must always contain more flags when compared to "Insert contents" as 1473 // contents can be selected one by one! 1474 1475 ScAddress aDestPos( nCol, 0, nTab ); // Adapt Row 1476 1477 // Create reference (Source Position) 1478 ScSingleRefData aRef; 1479 aRef.InitFlags(); // -> All absolute 1480 aRef.SetAbsCol(rColumn.nCol); 1481 aRef.SetAbsTab(rColumn.nTab); 1482 aRef.SetFlag3D(true); 1483 1484 for (SCROW nDestRow = nRow1; nDestRow <= nRow2; nDestRow++) 1485 { 1486 aRef.SetAbsRow(nDestRow - nDy); // Source row 1487 aDestPos.SetRow( nDestRow ); 1488 1489 ScTokenArray aArr(GetDoc()); 1490 aArr.AddSingleReference( aRef ); 1491 SetFormulaCell(*pBlockPos, nDestRow, new ScFormulaCell(rDocument, aDestPos, aArr)); 1492 } 1493 1494 // Don't forget to copy the cell text attributes. 1495 CopyTextAttrsFromClipHandler aFunc(rCxt, maCellTextAttrs, *this, nTab, nCol, nDy); 1496 sc::ParseBlock(rColumn.maCellTextAttrs.begin(), rColumn.maCellTextAttrs, aFunc, nRow1-nDy, nRow2-nDy); 1497 1498 return; 1499 } 1500 1501 // Compare the ScDocumentPool* to determine if we are copying within the 1502 // same document. If not, re-intern shared strings. 1503 svl::SharedStringPool* pSharedStringPool = (rColumn.GetDoc().GetPool() != rDocument.GetPool()) ? 1504 &rDocument.GetSharedStringPool() : nullptr; 1505 1506 // nRow1 to nRow2 is for destination (this) column. Subtract nDy to get the source range. 1507 // Copy all cells in the source column (rColumn) from nRow1-nDy to nRow2-nDy to this column. 1508 { 1509 CopyCellsFromClipHandler aFunc(rCxt, rColumn, *this, nTab, nCol, nDy, pSharedStringPool); 1510 sc::ParseBlock(rColumn.maCells.begin(), rColumn.maCells, aFunc, nRow1-nDy, nRow2-nDy); 1511 } 1512 1513 { 1514 // Don't forget to copy the cell text attributes. 1515 CopyTextAttrsFromClipHandler aFunc(rCxt, maCellTextAttrs, *this, nTab, nCol, nDy); 1516 sc::ParseBlock(rColumn.maCellTextAttrs.begin(), rColumn.maCellTextAttrs, aFunc, nRow1-nDy, nRow2-nDy); 1517 } 1518 } 1519 1520 void ScColumn::MixMarked( 1521 sc::MixDocContext& rCxt, const ScMarkData& rMark, ScPasteFunc nFunction, 1522 bool bSkipEmpty, const ScColumn& rSrcCol ) 1523 { 1524 SCROW nRow1, nRow2; 1525 1526 if (rMark.IsMultiMarked()) 1527 { 1528 ScMultiSelIter aIter( rMark.GetMultiSelData(), nCol ); 1529 while (aIter.Next( nRow1, nRow2 )) 1530 MixData(rCxt, nRow1, nRow2, nFunction, bSkipEmpty, rSrcCol); 1531 } 1532 } 1533 1534 namespace { 1535 1536 // Result in rVal1 1537 bool lcl_DoFunction( double& rVal1, double nVal2, ScPasteFunc nFunction ) 1538 { 1539 bool bOk = false; 1540 switch (nFunction) 1541 { 1542 case ScPasteFunc::ADD: 1543 bOk = SubTotal::SafePlus( rVal1, nVal2 ); 1544 break; 1545 case ScPasteFunc::SUB: 1546 nVal2 = -nVal2; // FIXME: Can we do this always without error? 1547 bOk = SubTotal::SafePlus( rVal1, nVal2 ); 1548 break; 1549 case ScPasteFunc::MUL: 1550 bOk = SubTotal::SafeMult( rVal1, nVal2 ); 1551 break; 1552 case ScPasteFunc::DIV: 1553 bOk = SubTotal::SafeDiv( rVal1, nVal2 ); 1554 break; 1555 default: break; 1556 } 1557 return bOk; 1558 } 1559 1560 void lcl_AddCode( ScTokenArray& rArr, const ScFormulaCell* pCell ) 1561 { 1562 rArr.AddOpCode(ocOpen); 1563 1564 const ScTokenArray* pCode = pCell->GetCode(); 1565 if (pCode) 1566 { 1567 FormulaTokenArrayPlainIterator aIter(*pCode); 1568 const formula::FormulaToken* pToken = aIter.First(); 1569 while (pToken) 1570 { 1571 rArr.AddToken( *pToken ); 1572 pToken = aIter.Next(); 1573 } 1574 } 1575 1576 rArr.AddOpCode(ocClose); 1577 } 1578 1579 class MixDataHandler 1580 { 1581 ScColumn& mrDestColumn; 1582 sc::ColumnBlockPosition& mrBlockPos; 1583 1584 sc::CellStoreType maNewCells; 1585 sc::CellStoreType::iterator miNewCellsPos; 1586 1587 size_t mnRowOffset; 1588 ScPasteFunc mnFunction; 1589 1590 bool mbSkipEmpty; 1591 1592 void doFunction( size_t nDestRow, double fVal1, double fVal2 ) 1593 { 1594 bool bOk = lcl_DoFunction(fVal1, fVal2, mnFunction); 1595 1596 if (bOk) 1597 miNewCellsPos = maNewCells.set(miNewCellsPos, nDestRow-mnRowOffset, fVal1); 1598 else 1599 { 1600 ScAddress aPos(mrDestColumn.GetCol(), nDestRow, mrDestColumn.GetTab()); 1601 1602 ScFormulaCell* pFC = new ScFormulaCell(mrDestColumn.GetDoc(), aPos); 1603 pFC->SetErrCode(FormulaError::NoValue); 1604 1605 miNewCellsPos = maNewCells.set(miNewCellsPos, nDestRow-mnRowOffset, pFC); 1606 } 1607 } 1608 1609 public: 1610 MixDataHandler( 1611 sc::ColumnBlockPosition& rBlockPos, 1612 ScColumn& rDestColumn, 1613 SCROW nRow1, SCROW nRow2, 1614 ScPasteFunc nFunction, bool bSkipEmpty) : 1615 mrDestColumn(rDestColumn), 1616 mrBlockPos(rBlockPos), 1617 maNewCells(nRow2 - nRow1 + 1), 1618 miNewCellsPos(maNewCells.begin()), 1619 mnRowOffset(nRow1), 1620 mnFunction(nFunction), 1621 mbSkipEmpty(bSkipEmpty) 1622 { 1623 } 1624 1625 void operator() (size_t nRow, double f) 1626 { 1627 sc::CellStoreType::position_type aPos = mrDestColumn.GetCellStore().position(mrBlockPos.miCellPos, nRow); 1628 mrBlockPos.miCellPos = aPos.first; 1629 switch (aPos.first->type) 1630 { 1631 case sc::element_type_empty: 1632 case sc::element_type_numeric: 1633 { 1634 double fSrcVal = 0.0; 1635 if (aPos.first->type == sc::element_type_numeric) 1636 fSrcVal = sc::numeric_block::at(*aPos.first->data, aPos.second); 1637 1638 // Both src and dest are of numeric type. 1639 doFunction(nRow, f, fSrcVal); 1640 } 1641 break; 1642 case sc::element_type_formula: 1643 { 1644 // Combination of value and at least one formula -> Create formula 1645 ScTokenArray aArr(mrDestColumn.GetDoc()); 1646 1647 // First row 1648 aArr.AddDouble(f); 1649 1650 // Operator 1651 OpCode eOp = ocAdd; 1652 switch (mnFunction) 1653 { 1654 case ScPasteFunc::ADD: eOp = ocAdd; break; 1655 case ScPasteFunc::SUB: eOp = ocSub; break; 1656 case ScPasteFunc::MUL: eOp = ocMul; break; 1657 case ScPasteFunc::DIV: eOp = ocDiv; break; 1658 default: break; 1659 } 1660 aArr.AddOpCode(eOp); // Function 1661 1662 // Second row 1663 ScFormulaCell* pDest = sc::formula_block::at(*aPos.first->data, aPos.second); 1664 lcl_AddCode(aArr, pDest); 1665 1666 miNewCellsPos = maNewCells.set( 1667 miNewCellsPos, nRow-mnRowOffset, 1668 new ScFormulaCell( 1669 mrDestColumn.GetDoc(), ScAddress(mrDestColumn.GetCol(), nRow, mrDestColumn.GetTab()), aArr)); 1670 } 1671 break; 1672 case sc::element_type_string: 1673 case sc::element_type_edittext: 1674 { 1675 // Destination cell is not a number. Just take the source cell. 1676 miNewCellsPos = maNewCells.set(miNewCellsPos, nRow-mnRowOffset, f); 1677 } 1678 break; 1679 default: 1680 ; 1681 } 1682 } 1683 1684 void operator() (size_t nRow, const svl::SharedString& rStr) 1685 { 1686 miNewCellsPos = maNewCells.set(miNewCellsPos, nRow-mnRowOffset, rStr); 1687 } 1688 1689 void operator() (size_t nRow, const EditTextObject* p) 1690 { 1691 miNewCellsPos = maNewCells.set(miNewCellsPos, nRow-mnRowOffset, p->Clone().release()); 1692 } 1693 1694 void operator() (size_t nRow, const ScFormulaCell* p) 1695 { 1696 sc::CellStoreType::position_type aPos = mrDestColumn.GetCellStore().position(mrBlockPos.miCellPos, nRow); 1697 mrBlockPos.miCellPos = aPos.first; 1698 switch (aPos.first->type) 1699 { 1700 case sc::element_type_numeric: 1701 { 1702 // Source is formula, and dest is value. 1703 ScTokenArray aArr(mrDestColumn.GetDoc()); 1704 1705 // First row 1706 lcl_AddCode(aArr, p); 1707 1708 // Operator 1709 OpCode eOp = ocAdd; 1710 switch (mnFunction) 1711 { 1712 case ScPasteFunc::ADD: eOp = ocAdd; break; 1713 case ScPasteFunc::SUB: eOp = ocSub; break; 1714 case ScPasteFunc::MUL: eOp = ocMul; break; 1715 case ScPasteFunc::DIV: eOp = ocDiv; break; 1716 default: break; 1717 } 1718 aArr.AddOpCode(eOp); // Function 1719 1720 // Second row 1721 aArr.AddDouble(sc::numeric_block::at(*aPos.first->data, aPos.second)); 1722 1723 miNewCellsPos = maNewCells.set( 1724 miNewCellsPos, nRow-mnRowOffset, 1725 new ScFormulaCell( 1726 mrDestColumn.GetDoc(), ScAddress(mrDestColumn.GetCol(), nRow, mrDestColumn.GetTab()), aArr)); 1727 } 1728 break; 1729 case sc::element_type_formula: 1730 { 1731 // Both are formulas. 1732 ScTokenArray aArr(mrDestColumn.GetDoc()); 1733 1734 // First row 1735 lcl_AddCode(aArr, p); 1736 1737 // Operator 1738 OpCode eOp = ocAdd; 1739 switch (mnFunction) 1740 { 1741 case ScPasteFunc::ADD: eOp = ocAdd; break; 1742 case ScPasteFunc::SUB: eOp = ocSub; break; 1743 case ScPasteFunc::MUL: eOp = ocMul; break; 1744 case ScPasteFunc::DIV: eOp = ocDiv; break; 1745 default: break; 1746 } 1747 aArr.AddOpCode(eOp); // Function 1748 1749 // Second row 1750 ScFormulaCell* pDest = sc::formula_block::at(*aPos.first->data, aPos.second); 1751 lcl_AddCode(aArr, pDest); 1752 1753 miNewCellsPos = maNewCells.set( 1754 miNewCellsPos, nRow-mnRowOffset, 1755 new ScFormulaCell( 1756 mrDestColumn.GetDoc(), ScAddress(mrDestColumn.GetCol(), nRow, mrDestColumn.GetTab()), aArr)); 1757 } 1758 break; 1759 case sc::element_type_string: 1760 case sc::element_type_edittext: 1761 case sc::element_type_empty: 1762 { 1763 // Destination cell is not a number. Just take the source cell. 1764 ScAddress aDestPos(mrDestColumn.GetCol(), nRow, mrDestColumn.GetTab()); 1765 miNewCellsPos = maNewCells.set( 1766 miNewCellsPos, nRow-mnRowOffset, new ScFormulaCell(*p, mrDestColumn.GetDoc(), aDestPos)); 1767 } 1768 break; 1769 default: 1770 ; 1771 } 1772 } 1773 1774 /** 1775 * Empty cell series in the source (clip) document. 1776 */ 1777 void operator() (mdds::mtv::element_t, size_t nTopRow, size_t nDataSize) 1778 { 1779 if (mbSkipEmpty) 1780 return; 1781 1782 // Source cells are empty. Treat them as if they have a value of 0.0. 1783 for (size_t i = 0; i < nDataSize; ++i) 1784 { 1785 size_t nDestRow = nTopRow + i; 1786 sc::CellStoreType::position_type aPos = mrDestColumn.GetCellStore().position(mrBlockPos.miCellPos, nDestRow); 1787 mrBlockPos.miCellPos = aPos.first; 1788 switch (aPos.first->type) 1789 { 1790 case sc::element_type_numeric: 1791 { 1792 double fVal2 = sc::numeric_block::at(*aPos.first->data, aPos.second); 1793 doFunction(nDestRow, 0.0, fVal2); 1794 } 1795 break; 1796 case sc::element_type_string: 1797 { 1798 const svl::SharedString& aVal = sc::string_block::at(*aPos.first->data, aPos.second); 1799 miNewCellsPos = maNewCells.set( 1800 miNewCellsPos, nDestRow-mnRowOffset, aVal); 1801 } 1802 break; 1803 case sc::element_type_edittext: 1804 { 1805 EditTextObject* pObj = sc::edittext_block::at(*aPos.first->data, aPos.second); 1806 miNewCellsPos = maNewCells.set( 1807 miNewCellsPos, nDestRow-mnRowOffset, pObj->Clone().release()); 1808 } 1809 break; 1810 case sc::element_type_formula: 1811 { 1812 ScTokenArray aArr(mrDestColumn.GetDoc()); 1813 1814 // First row 1815 ScFormulaCell* pSrc = sc::formula_block::at(*aPos.first->data, aPos.second); 1816 lcl_AddCode( aArr, pSrc); 1817 1818 // Operator 1819 OpCode eOp = ocAdd; 1820 switch (mnFunction) 1821 { 1822 case ScPasteFunc::ADD: eOp = ocAdd; break; 1823 case ScPasteFunc::SUB: eOp = ocSub; break; 1824 case ScPasteFunc::MUL: eOp = ocMul; break; 1825 case ScPasteFunc::DIV: eOp = ocDiv; break; 1826 default: break; 1827 } 1828 1829 aArr.AddOpCode(eOp); // Function 1830 aArr.AddDouble(0.0); 1831 1832 miNewCellsPos = maNewCells.set( 1833 miNewCellsPos, nDestRow-mnRowOffset, 1834 new ScFormulaCell( 1835 mrDestColumn.GetDoc(), ScAddress(mrDestColumn.GetCol(), nDestRow, mrDestColumn.GetTab()), aArr)); 1836 } 1837 break; 1838 default: 1839 ; 1840 } 1841 } 1842 } 1843 1844 /** 1845 * Set the new cells to the destination (this) column. 1846 */ 1847 void commit() 1848 { 1849 sc::CellStoreType& rDestCells = mrDestColumn.GetCellStore(); 1850 1851 // Stop all formula cells in the destination range first. 1852 sc::CellStoreType::position_type aPos = rDestCells.position(mrBlockPos.miCellPos, mnRowOffset); 1853 mrDestColumn.DetachFormulaCells(aPos, maNewCells.size(), nullptr); 1854 1855 // Move the new cells to the destination range. 1856 sc::CellStoreType::iterator& itDestPos = mrBlockPos.miCellPos; 1857 sc::CellTextAttrStoreType::iterator& itDestAttrPos = mrBlockPos.miCellTextAttrPos; 1858 1859 for (const auto& rNewCell : maNewCells) 1860 { 1861 bool bHasContent = true; 1862 size_t nDestRow = mnRowOffset + rNewCell.position; 1863 1864 switch (rNewCell.type) 1865 { 1866 case sc::element_type_numeric: 1867 { 1868 sc::numeric_block::iterator itData = sc::numeric_block::begin(*rNewCell.data); 1869 sc::numeric_block::iterator itDataEnd = sc::numeric_block::end(*rNewCell.data); 1870 itDestPos = mrDestColumn.GetCellStore().set(itDestPos, nDestRow, itData, itDataEnd); 1871 } 1872 break; 1873 case sc::element_type_string: 1874 { 1875 sc::string_block::iterator itData = sc::string_block::begin(*rNewCell.data); 1876 sc::string_block::iterator itDataEnd = sc::string_block::end(*rNewCell.data); 1877 itDestPos = rDestCells.set(itDestPos, nDestRow, itData, itDataEnd); 1878 } 1879 break; 1880 case sc::element_type_edittext: 1881 { 1882 sc::edittext_block::iterator itData = sc::edittext_block::begin(*rNewCell.data); 1883 sc::edittext_block::iterator itDataEnd = sc::edittext_block::end(*rNewCell.data); 1884 itDestPos = rDestCells.set(itDestPos, nDestRow, itData, itDataEnd); 1885 } 1886 break; 1887 case sc::element_type_formula: 1888 { 1889 sc::formula_block::iterator itData = sc::formula_block::begin(*rNewCell.data); 1890 sc::formula_block::iterator itDataEnd = sc::formula_block::end(*rNewCell.data); 1891 1892 // Group new formula cells before inserting them. 1893 sc::SharedFormulaUtil::groupFormulaCells(itData, itDataEnd); 1894 1895 // Insert the formula cells to the column. 1896 itDestPos = rDestCells.set(itDestPos, nDestRow, itData, itDataEnd); 1897 1898 // Merge with the previous formula group (if any). 1899 aPos = rDestCells.position(itDestPos, nDestRow); 1900 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos); 1901 1902 // Merge with the next formula group (if any). 1903 size_t nNextRow = nDestRow + rNewCell.size; 1904 if (mrDestColumn.GetDoc().ValidRow(nNextRow)) 1905 { 1906 aPos = rDestCells.position(aPos.first, nNextRow); 1907 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos); 1908 } 1909 1910 // Start listening on cells to get them updated by changes of referenced cells 1911 std::vector<SCROW> aNewSharedRows; 1912 aPos = rDestCells.position(itDestPos, nDestRow); 1913 size_t nFormulaCells = std::distance(itData, itDataEnd); 1914 mrDestColumn.AttachNewFormulaCells(aPos, nFormulaCells, aNewSharedRows); 1915 } 1916 break; 1917 case sc::element_type_empty: 1918 { 1919 itDestPos = rDestCells.set_empty(itDestPos, nDestRow, nDestRow+rNewCell.size-1); 1920 bHasContent = false; 1921 } 1922 break; 1923 default: 1924 ; 1925 } 1926 1927 sc::CellTextAttrStoreType& rDestAttrs = mrDestColumn.GetCellAttrStore(); 1928 if (bHasContent) 1929 { 1930 std::vector<sc::CellTextAttr> aAttrs(rNewCell.size, sc::CellTextAttr()); 1931 itDestAttrPos = rDestAttrs.set(itDestAttrPos, nDestRow, aAttrs.begin(), aAttrs.end()); 1932 } 1933 else 1934 itDestAttrPos = rDestAttrs.set_empty(itDestAttrPos, nDestRow, nDestRow+rNewCell.size-1); 1935 } 1936 1937 maNewCells.release(); 1938 } 1939 }; 1940 1941 } 1942 1943 void ScColumn::MixData( 1944 sc::MixDocContext& rCxt, SCROW nRow1, SCROW nRow2, ScPasteFunc nFunction, 1945 bool bSkipEmpty, const ScColumn& rSrcCol ) 1946 { 1947 // destination (this column) block position. 1948 1949 sc::ColumnBlockPosition* p = rCxt.getBlockPosition(nTab, nCol); 1950 if (!p) 1951 return; 1952 1953 MixDataHandler aFunc(*p, *this, nRow1, nRow2, nFunction, bSkipEmpty); 1954 sc::ParseAll(rSrcCol.maCells.begin(), rSrcCol.maCells, nRow1, nRow2, aFunc, aFunc); 1955 1956 aFunc.commit(); 1957 CellStorageModified(); 1958 } 1959 1960 std::unique_ptr<ScAttrIterator> ScColumnData::CreateAttrIterator( SCROW nStartRow, SCROW nEndRow ) const 1961 { 1962 return std::make_unique<ScAttrIterator>( pAttrArray.get(), nStartRow, nEndRow, GetDoc().GetDefPattern() ); 1963 } 1964 1965 namespace { 1966 1967 class StartListenersHandler 1968 { 1969 sc::StartListeningContext* mpCxt; 1970 bool mbAllListeners; 1971 1972 public: 1973 StartListenersHandler( sc::StartListeningContext& rCxt, bool bAllListeners ) : 1974 mpCxt(&rCxt), mbAllListeners(bAllListeners) {} 1975 1976 void operator() ( sc::CellStoreType::value_type& aBlk ) 1977 { 1978 if (aBlk.type != sc::element_type_formula) 1979 return; 1980 1981 ScFormulaCell** pp = &sc::formula_block::at(*aBlk.data, 0); 1982 ScFormulaCell** ppEnd = pp + aBlk.size; 1983 1984 for (; pp != ppEnd; ++pp) 1985 { 1986 ScFormulaCell& rFC = **pp; 1987 if (!mbAllListeners && !rFC.NeedsListening()) 1988 continue; 1989 1990 if (rFC.IsSharedTop()) 1991 { 1992 sc::SharedFormulaUtil::startListeningAsGroup(*mpCxt, pp); 1993 pp += rFC.GetSharedLength() - 1; // Move to the last cell in the group. 1994 } 1995 else 1996 rFC.StartListeningTo(*mpCxt); 1997 } 1998 } 1999 }; 2000 2001 } 2002 2003 void ScColumn::StartListeners( sc::StartListeningContext& rCxt, bool bAll ) 2004 { 2005 std::for_each(maCells.begin(), maCells.end(), StartListenersHandler(rCxt, bAll)); 2006 } 2007 2008 namespace { 2009 2010 void applyTextNumFormat( ScColumn& rCol, SCROW nRow, SvNumberFormatter* pFormatter ) 2011 { 2012 sal_uInt32 nFormat = pFormatter->GetStandardFormat(SvNumFormatType::TEXT); 2013 ScPatternAttr aNewAttrs(rCol.GetDoc().GetPool()); 2014 SfxItemSet& rSet = aNewAttrs.GetItemSet(); 2015 rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat)); 2016 rCol.ApplyPattern(nRow, aNewAttrs); 2017 } 2018 2019 } 2020 2021 bool ScColumn::ParseString( 2022 ScCellValue& rCell, SCROW nRow, SCTAB nTabP, const OUString& rString, 2023 formula::FormulaGrammar::AddressConvention eConv, 2024 const ScSetStringParam* pParam ) 2025 { 2026 if (rString.isEmpty()) 2027 return false; 2028 2029 bool bNumFmtSet = false; 2030 2031 ScSetStringParam aParam; 2032 2033 if (pParam) 2034 aParam = *pParam; 2035 2036 sal_uInt32 nIndex = 0; 2037 sal_uInt32 nOldIndex = 0; 2038 SvNumFormatType eNumFormatType = SvNumFormatType::ALL; 2039 if (!aParam.mpNumFormatter) 2040 aParam.mpNumFormatter = GetDoc().GetFormatTable(); 2041 2042 sal_Unicode cFirstChar = 0; // Text 2043 nIndex = nOldIndex = GetNumberFormat( GetDoc().GetNonThreadedContext(), nRow ); 2044 if ( rString.getLength() > 1 ) 2045 { 2046 eNumFormatType = aParam.mpNumFormatter->GetType(nIndex); 2047 if ( eNumFormatType != SvNumFormatType::TEXT ) 2048 cFirstChar = rString[0]; 2049 } 2050 2051 svl::SharedStringPool& rPool = GetDoc().GetSharedStringPool(); 2052 2053 if ( cFirstChar == '=' ) 2054 { 2055 if ( rString.getLength() == 1 ) // = Text 2056 { 2057 rCell.set(rPool.intern(rString)); 2058 } 2059 else if (aParam.meSetTextNumFormat == ScSetStringParam::Always) 2060 { 2061 // Set the cell format type to Text. 2062 applyTextNumFormat(*this, nRow, aParam.mpNumFormatter); 2063 rCell.set(rPool.intern(rString)); 2064 } 2065 else // = Formula 2066 { 2067 ScFormulaCell* pFormulaCell = new ScFormulaCell( 2068 GetDoc(), ScAddress(nCol, nRow, nTabP), rString, 2069 formula::FormulaGrammar::mergeToGrammar(formula::FormulaGrammar::GRAM_DEFAULT, eConv), 2070 ScMatrixMode::NONE); 2071 if (aParam.mbCheckLinkFormula) 2072 GetDoc().CheckLinkFormulaNeedingCheck( *pFormulaCell->GetCode()); 2073 rCell.set( pFormulaCell); 2074 } 2075 } 2076 else if ( cFirstChar == '\'') // 'Text 2077 { 2078 bool bNumeric = false; 2079 if (aParam.mbHandleApostrophe) 2080 { 2081 // Cell format is not 'Text', and the first char 2082 // is an apostrophe. Check if the input is considered a number. 2083 OUString aTest = rString.copy(1); 2084 double fTest; 2085 bNumeric = aParam.mpNumFormatter->IsNumberFormat(aTest, nIndex, fTest); 2086 if (bNumeric) 2087 // This is a number. Strip out the first char. 2088 rCell.set(rPool.intern(aTest)); 2089 } 2090 if (!bNumeric) 2091 // This is normal text. Take it as-is. 2092 rCell.set(rPool.intern(rString)); 2093 } 2094 else 2095 { 2096 double nVal; 2097 2098 do 2099 { 2100 if (aParam.mbDetectNumberFormat) 2101 { 2102 // Editing a date prefers the format's locale's edit date 2103 // format's date acceptance patterns and YMD order. 2104 /* TODO: this could be determined already far above when 2105 * starting to edit a date "cell" and passed down. A problem 2106 * could also be if a new date was typed over or written by a 2107 * macro assuming the current locale if that conflicts somehow. 2108 * You can't have everything. See tdf#116579 and tdf#125109. */ 2109 if (eNumFormatType == SvNumFormatType::ALL) 2110 eNumFormatType = aParam.mpNumFormatter->GetType(nIndex); 2111 bool bForceFormatDate = (eNumFormatType == SvNumFormatType::DATE 2112 || eNumFormatType == SvNumFormatType::DATETIME); 2113 const SvNumberformat* pOldFormat = nullptr; 2114 NfEvalDateFormat eEvalDateFormat = NF_EVALDATEFORMAT_INTL_FORMAT; 2115 if (bForceFormatDate) 2116 { 2117 ScRefCellValue aCell = GetCellValue(nRow); 2118 if (aCell.getType() == CELLTYPE_VALUE) 2119 { 2120 // Only for an actual date (serial number), not an 2121 // arbitrary string or formula or empty cell. 2122 // Also ensure the edit date format's pattern is used, 2123 // not the display format's. 2124 pOldFormat = aParam.mpNumFormatter->GetEntry( nOldIndex); 2125 if (!pOldFormat) 2126 bForceFormatDate = false; 2127 else 2128 { 2129 nIndex = aParam.mpNumFormatter->GetEditFormat( 2130 aCell.getValue(), nOldIndex, eNumFormatType, pOldFormat); 2131 eEvalDateFormat = aParam.mpNumFormatter->GetEvalDateFormat(); 2132 aParam.mpNumFormatter->SetEvalDateFormat( NF_EVALDATEFORMAT_FORMAT_INTL); 2133 } 2134 } 2135 else 2136 { 2137 bForceFormatDate = false; 2138 } 2139 } 2140 2141 const bool bIsNumberFormat = aParam.mpNumFormatter->IsNumberFormat(rString, nIndex, nVal); 2142 2143 if (bForceFormatDate) 2144 aParam.mpNumFormatter->SetEvalDateFormat( eEvalDateFormat); 2145 2146 if (!bIsNumberFormat) 2147 break; 2148 2149 // If we have bForceFormatDate, the pOldFormat was/is of 2150 // nOldIndex date(+time) type already, if detected type is 2151 // compatible keep the original format. 2152 if (bForceFormatDate && SvNumberFormatter::IsCompatible( 2153 eNumFormatType, aParam.mpNumFormatter->GetType( nIndex))) 2154 { 2155 nIndex = nOldIndex; 2156 } 2157 else 2158 { 2159 // convert back to the original language if a built-in format was detected 2160 if (!pOldFormat) 2161 pOldFormat = aParam.mpNumFormatter->GetEntry( nOldIndex ); 2162 if (pOldFormat) 2163 nIndex = aParam.mpNumFormatter->GetFormatForLanguageIfBuiltIn( 2164 nIndex, pOldFormat->GetLanguage()); 2165 } 2166 2167 rCell.set(nVal); 2168 if ( nIndex != nOldIndex) 2169 { 2170 // #i22345# New behavior: Apply the detected number format only if 2171 // the old one was the default number, date, time or boolean format. 2172 // Exception: If the new format is boolean, always apply it. 2173 2174 bool bOverwrite = false; 2175 if ( pOldFormat ) 2176 { 2177 SvNumFormatType nOldType = pOldFormat->GetMaskedType(); 2178 if ( nOldType == SvNumFormatType::NUMBER || nOldType == SvNumFormatType::DATE || 2179 nOldType == SvNumFormatType::TIME || nOldType == SvNumFormatType::LOGICAL ) 2180 { 2181 if ( nOldIndex == aParam.mpNumFormatter->GetStandardFormat( 2182 nOldType, pOldFormat->GetLanguage() ) ) 2183 { 2184 bOverwrite = true; // default of these types can be overwritten 2185 } 2186 } 2187 } 2188 if ( !bOverwrite && aParam.mpNumFormatter->GetType( nIndex ) == SvNumFormatType::LOGICAL ) 2189 { 2190 bOverwrite = true; // overwrite anything if boolean was detected 2191 } 2192 2193 if ( bOverwrite ) 2194 { 2195 ApplyAttr( nRow, SfxUInt32Item( ATTR_VALUE_FORMAT, 2196 nIndex) ); 2197 bNumFmtSet = true; 2198 } 2199 } 2200 } 2201 else if (aParam.meSetTextNumFormat == ScSetStringParam::Never || 2202 aParam.meSetTextNumFormat == ScSetStringParam::SpecialNumberOnly) 2203 { 2204 // Only check if the string is a regular number. 2205 const LocaleDataWrapper* pLocale = aParam.mpNumFormatter->GetLocaleData(); 2206 if (!pLocale) 2207 break; 2208 2209 const LocaleDataItem2& aLocaleItem = pLocale->getLocaleItem(); 2210 const OUString& rDecSep = aLocaleItem.decimalSeparator; 2211 const OUString& rGroupSep = aLocaleItem.thousandSeparator; 2212 const OUString& rDecSepAlt = aLocaleItem.decimalSeparatorAlternative; 2213 if (rDecSep.getLength() != 1 || rGroupSep.getLength() != 1 || rDecSepAlt.getLength() > 1) 2214 break; 2215 2216 sal_Unicode dsep = rDecSep[0]; 2217 sal_Unicode gsep = rGroupSep[0]; 2218 sal_Unicode dsepa = rDecSepAlt.toChar(); 2219 2220 if (!ScStringUtil::parseSimpleNumber(rString, dsep, gsep, dsepa, nVal)) 2221 break; 2222 2223 rCell.set(nVal); 2224 } 2225 } 2226 while (false); 2227 2228 if (rCell.getType() == CELLTYPE_NONE) 2229 { 2230 // If we reach here with ScSetStringParam::SpecialNumberOnly it 2231 // means a simple number was not detected above, so test for 2232 // special numbers. In any case ScSetStringParam::Always does not 2233 // mean always, but only always for content that could be any 2234 // numeric. 2235 if ((aParam.meSetTextNumFormat == ScSetStringParam::Always || 2236 aParam.meSetTextNumFormat == ScSetStringParam::SpecialNumberOnly) && 2237 aParam.mpNumFormatter->IsNumberFormat(rString, nIndex, nVal)) 2238 { 2239 // Set the cell format type to Text. 2240 applyTextNumFormat(*this, nRow, aParam.mpNumFormatter); 2241 } 2242 2243 rCell.set(rPool.intern(rString)); 2244 } 2245 } 2246 2247 return bNumFmtSet; 2248 } 2249 2250 /** 2251 * Returns true if the cell format was set as well 2252 */ 2253 bool ScColumn::SetString( SCROW nRow, SCTAB nTabP, const OUString& rString, 2254 formula::FormulaGrammar::AddressConvention eConv, 2255 const ScSetStringParam* pParam ) 2256 { 2257 if (!GetDoc().ValidRow(nRow)) 2258 return false; 2259 2260 ScCellValue aNewCell; 2261 bool bNumFmtSet = ParseString(aNewCell, nRow, nTabP, rString, eConv, pParam); 2262 if (pParam) 2263 aNewCell.release(*this, nRow, pParam->meStartListening); 2264 else 2265 aNewCell.release(*this, nRow); 2266 2267 // Do not set Formats and Formulas here anymore! 2268 // These are queried during output 2269 2270 return bNumFmtSet; 2271 } 2272 2273 void ScColumn::SetEditText( SCROW nRow, std::unique_ptr<EditTextObject> pEditText ) 2274 { 2275 pEditText->NormalizeString(GetDoc().GetSharedStringPool()); 2276 std::vector<SCROW> aNewSharedRows; 2277 sc::CellStoreType::iterator it = GetPositionToInsert(nRow, aNewSharedRows, false); 2278 maCells.set(it, nRow, pEditText.release()); 2279 maCellTextAttrs.set(nRow, sc::CellTextAttr()); 2280 CellStorageModified(); 2281 2282 StartListeningUnshared( aNewSharedRows); 2283 2284 BroadcastNewCell(nRow); 2285 } 2286 2287 void ScColumn::SetEditText( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, std::unique_ptr<EditTextObject> pEditText ) 2288 { 2289 pEditText->NormalizeString(GetDoc().GetSharedStringPool()); 2290 std::vector<SCROW> aNewSharedRows; 2291 rBlockPos.miCellPos = GetPositionToInsert(rBlockPos.miCellPos, nRow, aNewSharedRows, false); 2292 rBlockPos.miCellPos = maCells.set(rBlockPos.miCellPos, nRow, pEditText.release()); 2293 rBlockPos.miCellTextAttrPos = maCellTextAttrs.set( 2294 rBlockPos.miCellTextAttrPos, nRow, sc::CellTextAttr()); 2295 2296 CellStorageModified(); 2297 2298 StartListeningUnshared( aNewSharedRows); 2299 2300 BroadcastNewCell(nRow); 2301 } 2302 2303 void ScColumn::SetEditText( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, const EditTextObject& rEditText ) 2304 { 2305 if (GetDoc().GetEditPool() == rEditText.GetPool()) 2306 { 2307 SetEditText(rBlockPos, nRow, rEditText.Clone()); 2308 return; 2309 } 2310 2311 // rats, yet another "spool" 2312 // Sadly there is no other way to change the Pool than to 2313 // "spool" the Object through a corresponding Engine 2314 EditEngine& rEngine = GetDoc().GetEditEngine(); 2315 rEngine.SetText(rEditText); 2316 SetEditText(rBlockPos, nRow, rEngine.CreateTextObject()); 2317 } 2318 2319 void ScColumn::SetEditText( SCROW nRow, const EditTextObject& rEditText, const SfxItemPool* pEditPool ) 2320 { 2321 if (pEditPool && GetDoc().GetEditPool() == pEditPool) 2322 { 2323 SetEditText(nRow, rEditText.Clone()); 2324 return; 2325 } 2326 2327 // rats, yet another "spool" 2328 // Sadly there is no other way to change the Pool than to 2329 // "spool" the Object through a corresponding Engine 2330 EditEngine& rEngine = GetDoc().GetEditEngine(); 2331 rEngine.SetText(rEditText); 2332 SetEditText(nRow, rEngine.CreateTextObject()); 2333 } 2334 2335 void ScColumn::SetFormula( SCROW nRow, const ScTokenArray& rArray, formula::FormulaGrammar::Grammar eGram ) 2336 { 2337 ScAddress aPos(nCol, nRow, nTab); 2338 2339 std::vector<SCROW> aNewSharedRows; 2340 sc::CellStoreType::iterator it = GetPositionToInsert(nRow, aNewSharedRows, true); 2341 ScFormulaCell* pCell = new ScFormulaCell(GetDoc(), aPos, rArray, eGram); 2342 sal_uInt32 nCellFormat = GetNumberFormat(GetDoc().GetNonThreadedContext(), nRow); 2343 if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0) 2344 pCell->SetNeedNumberFormat(true); 2345 it = maCells.set(it, nRow, pCell); 2346 maCellTextAttrs.set(nRow, sc::CellTextAttr()); 2347 2348 CellStorageModified(); 2349 2350 AttachNewFormulaCell(it, nRow, *pCell, aNewSharedRows); 2351 } 2352 2353 void ScColumn::SetFormula( SCROW nRow, const OUString& rFormula, formula::FormulaGrammar::Grammar eGram ) 2354 { 2355 ScAddress aPos(nCol, nRow, nTab); 2356 2357 std::vector<SCROW> aNewSharedRows; 2358 sc::CellStoreType::iterator it = GetPositionToInsert(nRow, aNewSharedRows, true); 2359 ScFormulaCell* pCell = new ScFormulaCell(GetDoc(), aPos, rFormula, eGram); 2360 sal_uInt32 nCellFormat = GetNumberFormat(GetDoc().GetNonThreadedContext(), nRow); 2361 if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0) 2362 pCell->SetNeedNumberFormat(true); 2363 it = maCells.set(it, nRow, pCell); 2364 maCellTextAttrs.set(nRow, sc::CellTextAttr()); 2365 2366 CellStorageModified(); 2367 2368 AttachNewFormulaCell(it, nRow, *pCell, aNewSharedRows); 2369 } 2370 2371 ScFormulaCell* ScColumn::SetFormulaCell( 2372 SCROW nRow, ScFormulaCell* pCell, sc::StartListeningType eListenType, 2373 bool bInheritNumFormatIfNeeded ) 2374 { 2375 std::vector<SCROW> aNewSharedRows; 2376 sc::CellStoreType::iterator it = GetPositionToInsert(nRow, aNewSharedRows, true); 2377 sal_uInt32 nCellFormat = GetNumberFormat(GetDoc().GetNonThreadedContext(), nRow); 2378 if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 && bInheritNumFormatIfNeeded ) 2379 pCell->SetNeedNumberFormat(true); 2380 it = maCells.set(it, nRow, pCell); 2381 maCellTextAttrs.set(nRow, sc::CellTextAttr()); 2382 2383 CellStorageModified(); 2384 2385 AttachNewFormulaCell(it, nRow, *pCell, aNewSharedRows, true, eListenType); 2386 2387 return pCell; 2388 } 2389 2390 void ScColumn::SetFormulaCell( 2391 sc::ColumnBlockPosition& rBlockPos, SCROW nRow, ScFormulaCell* pCell, 2392 sc::StartListeningType eListenType, 2393 bool bInheritNumFormatIfNeeded ) 2394 { 2395 std::vector<SCROW> aNewSharedRows; 2396 rBlockPos.miCellPos = GetPositionToInsert(rBlockPos.miCellPos, nRow, aNewSharedRows, true); 2397 sal_uInt32 nCellFormat = GetNumberFormat(GetDoc().GetNonThreadedContext(), nRow); 2398 if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 && bInheritNumFormatIfNeeded ) 2399 pCell->SetNeedNumberFormat(true); 2400 rBlockPos.miCellPos = maCells.set(rBlockPos.miCellPos, nRow, pCell); 2401 rBlockPos.miCellTextAttrPos = maCellTextAttrs.set( 2402 rBlockPos.miCellTextAttrPos, nRow, sc::CellTextAttr()); 2403 2404 CellStorageModified(); 2405 2406 AttachNewFormulaCell(rBlockPos.miCellPos, nRow, *pCell, aNewSharedRows, true, eListenType); 2407 } 2408 2409 bool ScColumn::SetFormulaCells( SCROW nRow, std::vector<ScFormulaCell*>& rCells ) 2410 { 2411 if (!GetDoc().ValidRow(nRow)) 2412 return false; 2413 2414 SCROW nEndRow = nRow + rCells.size() - 1; 2415 if (!GetDoc().ValidRow(nEndRow)) 2416 return false; 2417 2418 sc::CellStoreType::position_type aPos = maCells.position(nRow); 2419 2420 // Detach all formula cells that will be overwritten. 2421 std::vector<SCROW> aNewSharedRows; 2422 DetachFormulaCells(aPos, rCells.size(), &aNewSharedRows); 2423 2424 if (!GetDoc().IsClipOrUndo()) 2425 { 2426 for (size_t i = 0, n = rCells.size(); i < n; ++i) 2427 { 2428 SCROW nThisRow = nRow + i; 2429 sal_uInt32 nFmt = GetNumberFormat(GetDoc().GetNonThreadedContext(), nThisRow); 2430 if ((nFmt % SV_COUNTRY_LANGUAGE_OFFSET) == 0) 2431 rCells[i]->SetNeedNumberFormat(true); 2432 } 2433 } 2434 2435 std::vector<sc::CellTextAttr> aDefaults(rCells.size(), sc::CellTextAttr()); 2436 maCellTextAttrs.set(nRow, aDefaults.begin(), aDefaults.end()); 2437 2438 maCells.set(aPos.first, nRow, rCells.begin(), rCells.end()); 2439 2440 CellStorageModified(); 2441 2442 // Reget position_type as the type may have changed to formula, block and 2443 // block size changed, ... 2444 aPos = maCells.position(nRow); 2445 AttachNewFormulaCells(aPos, rCells.size(), aNewSharedRows); 2446 2447 return true; 2448 } 2449 2450 svl::SharedString ScColumn::GetSharedString( SCROW nRow ) const 2451 { 2452 sc::CellStoreType::const_position_type aPos = maCells.position(nRow); 2453 switch (aPos.first->type) 2454 { 2455 case sc::element_type_string: 2456 return sc::string_block::at(*aPos.first->data, aPos.second); 2457 case sc::element_type_edittext: 2458 { 2459 const EditTextObject* pObj = sc::edittext_block::at(*aPos.first->data, aPos.second); 2460 std::vector<svl::SharedString> aSSs = pObj->GetSharedStrings(); 2461 if (aSSs.size() != 1) 2462 // We don't handle multiline content for now. 2463 return svl::SharedString(); 2464 2465 return aSSs[0]; 2466 } 2467 break; 2468 default: 2469 ; 2470 } 2471 return svl::SharedString(); 2472 } 2473 2474 namespace { 2475 2476 class FilterEntriesHandler 2477 { 2478 ScColumn& mrColumn; 2479 ScFilterEntries& mrFilterEntries; 2480 2481 void processCell(const ScColumn& rColumn, SCROW nRow, ScRefCellValue& rCell) 2482 { 2483 SvNumberFormatter* pFormatter = mrColumn.GetDoc().GetFormatTable(); 2484 sal_uLong nFormat = mrColumn.GetNumberFormat(mrColumn.GetDoc().GetNonThreadedContext(), nRow); 2485 OUString aStr = ScCellFormat::GetInputString(rCell, nFormat, *pFormatter, mrColumn.GetDoc(), mrColumn.HasFiltering()); 2486 2487 // Colors 2488 ScAddress aPos(rColumn.GetCol(), nRow, rColumn.GetTab()); 2489 2490 Color backgroundColor; 2491 bool bHasConditionalBackgroundColor = false; 2492 2493 Color textColor; 2494 bool bHasConditionalTextColor = false; 2495 // Check text & background color from cond. formatting 2496 const ScPatternAttr* pPattern 2497 = mrColumn.GetDoc().GetPattern(aPos.Col(), aPos.Row(), aPos.Tab()); 2498 if (pPattern) 2499 { 2500 if (!pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData().empty()) 2501 { 2502 const SfxItemSet* pCondSet 2503 = mrColumn.GetDoc().GetCondResult(aPos.Col(), aPos.Row(), aPos.Tab()); 2504 const SvxColorItem* pColor = &pPattern->GetItem(ATTR_FONT_COLOR, pCondSet); 2505 textColor = pColor->GetValue(); 2506 bHasConditionalTextColor = true; 2507 2508 const SvxBrushItem* pBackgroundColor = &pPattern->GetItem(ATTR_BACKGROUND, pCondSet); 2509 backgroundColor = pBackgroundColor->GetColor(); 2510 bHasConditionalBackgroundColor = true; 2511 } 2512 } 2513 2514 if (!bHasConditionalTextColor) 2515 { 2516 const SvxColorItem* pColor = rColumn.GetDoc().GetAttr(aPos, ATTR_FONT_COLOR); 2517 textColor = pColor->GetValue(); 2518 } 2519 mrFilterEntries.addTextColor(textColor); 2520 2521 // Color scale needs a different handling 2522 ScConditionalFormat* pCondFormat 2523 = rColumn.GetDoc().GetCondFormat(aPos.Col(), aPos.Row(), aPos.Tab()); 2524 if (pCondFormat) 2525 { 2526 for (size_t i = 0; i < pCondFormat->size(); i++) 2527 { 2528 auto aEntry = pCondFormat->GetEntry(i); 2529 if (aEntry->GetType() == ScFormatEntry::Type::Colorscale) 2530 { 2531 const ScColorScaleFormat* pColFormat 2532 = static_cast<const ScColorScaleFormat*>(aEntry); 2533 std::optional<Color> oColor = pColFormat->GetColor(aPos); 2534 if (oColor) 2535 { 2536 backgroundColor = *oColor; 2537 bHasConditionalBackgroundColor = true; 2538 } 2539 } 2540 } 2541 } 2542 if (!bHasConditionalBackgroundColor) 2543 { 2544 const SvxBrushItem* pBrush = rColumn.GetDoc().GetAttr(aPos, ATTR_BACKGROUND); 2545 backgroundColor = pBrush->GetColor(); 2546 } 2547 mrFilterEntries.addBackgroundColor(backgroundColor); 2548 2549 if (rCell.hasString()) 2550 { 2551 mrFilterEntries.push_back(ScTypedStrData(std::move(aStr))); 2552 return; 2553 } 2554 2555 double fVal = 0.0; 2556 2557 switch (rCell.getType()) 2558 { 2559 case CELLTYPE_VALUE: 2560 fVal = rCell.getDouble(); 2561 break; 2562 2563 case CELLTYPE_FORMULA: 2564 { 2565 ScFormulaCell* pFC = rCell.getFormula(); 2566 FormulaError nErr = pFC->GetErrCode(); 2567 if (nErr != FormulaError::NONE) 2568 { 2569 // Error cell is evaluated as string (for now). 2570 OUString aErr = ScGlobal::GetErrorString(nErr); 2571 if (!aErr.isEmpty()) 2572 { 2573 mrFilterEntries.push_back(ScTypedStrData(std::move(aErr))); 2574 return; 2575 } 2576 } 2577 else 2578 fVal = pFC->GetValue(); 2579 } 2580 break; 2581 default: 2582 ; 2583 } 2584 2585 SvNumFormatType nType = pFormatter->GetType(nFormat); 2586 bool bDate = false; 2587 if ((nType & SvNumFormatType::DATE) && !(nType & SvNumFormatType::TIME)) 2588 { 2589 // special case for date values. Disregard the time 2590 // element if the number format is of date type. 2591 fVal = rtl::math::approxFloor(fVal); 2592 mrFilterEntries.mbHasDates = true; 2593 bDate = true; 2594 // Convert string representation to ISO 8601 date to eliminate 2595 // locale dependent behaviour later when filtering for dates. 2596 sal_uInt32 nIndex = pFormatter->GetFormatIndex( NF_DATE_DIN_YYYYMMDD); 2597 pFormatter->GetInputLineString( fVal, nIndex, aStr); 2598 } 2599 else if (nType == SvNumFormatType::DATETIME) 2600 { 2601 // special case for datetime values. 2602 // Convert string representation to ISO 8601 (with blank instead of T) datetime 2603 // to eliminate locale dependent behaviour later when filtering for datetimes. 2604 sal_uInt32 nIndex = pFormatter->GetFormatIndex(NF_DATETIME_ISO_YYYYMMDD_HHMMSS); 2605 pFormatter->GetInputLineString(fVal, nIndex, aStr); 2606 } 2607 // store the formatted/rounded value for filtering 2608 if ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0 && !bDate) 2609 mrFilterEntries.push_back(ScTypedStrData(std::move(aStr), fVal, rColumn.GetDoc().RoundValueAsShown(fVal, nFormat), ScTypedStrData::Value, bDate)); 2610 else 2611 mrFilterEntries.push_back(ScTypedStrData(std::move(aStr), fVal, fVal, ScTypedStrData::Value, bDate)); 2612 } 2613 2614 public: 2615 FilterEntriesHandler(ScColumn& rColumn, ScFilterEntries& rFilterEntries) : 2616 mrColumn(rColumn), mrFilterEntries(rFilterEntries) {} 2617 2618 void operator() (size_t nRow, double fVal) 2619 { 2620 ScRefCellValue aCell(fVal); 2621 processCell(mrColumn, nRow, aCell); 2622 } 2623 2624 void operator() (size_t nRow, const svl::SharedString& rStr) 2625 { 2626 ScRefCellValue aCell(&rStr); 2627 processCell(mrColumn, nRow, aCell); 2628 } 2629 2630 void operator() (size_t nRow, const EditTextObject* p) 2631 { 2632 ScRefCellValue aCell(p); 2633 processCell(mrColumn, nRow, aCell); 2634 } 2635 2636 void operator() (size_t nRow, const ScFormulaCell* p) 2637 { 2638 ScRefCellValue aCell(const_cast<ScFormulaCell*>(p)); 2639 processCell(mrColumn, nRow, aCell); 2640 } 2641 2642 void operator() (const int nElemType, size_t nRow, size_t /* nDataSize */) 2643 { 2644 if ( nElemType == sc::element_type_empty ) 2645 { 2646 if (!mrFilterEntries.mbHasEmpties) 2647 { 2648 mrFilterEntries.push_back(ScTypedStrData(OUString())); 2649 mrFilterEntries.mbHasEmpties = true; 2650 } 2651 return; 2652 } 2653 ScRefCellValue aCell = mrColumn.GetCellValue(nRow); 2654 processCell(mrColumn, nRow, aCell); 2655 } 2656 }; 2657 2658 } 2659 2660 void ScColumn::GetFilterEntries( 2661 sc::ColumnBlockConstPosition& rBlockPos, SCROW nStartRow, SCROW nEndRow, 2662 ScFilterEntries& rFilterEntries, bool bFiltering ) 2663 { 2664 mbFiltering = bFiltering; 2665 FilterEntriesHandler aFunc(*this, rFilterEntries); 2666 rBlockPos.miCellPos = 2667 sc::ParseAll(rBlockPos.miCellPos, maCells, nStartRow, nEndRow, aFunc, aFunc); 2668 } 2669 2670 namespace { 2671 2672 /** 2673 * Iterate over only string and edit-text cells. 2674 */ 2675 class StrCellIterator 2676 { 2677 typedef std::pair<sc::CellStoreType::const_iterator,size_t> PosType; 2678 PosType maPos; 2679 sc::CellStoreType::const_iterator miBeg; 2680 sc::CellStoreType::const_iterator miEnd; 2681 const ScDocument* mpDoc; 2682 public: 2683 StrCellIterator(const sc::CellStoreType& rCells, SCROW nStart, const ScDocument* pDoc) : 2684 miBeg(rCells.begin()), miEnd(rCells.end()), mpDoc(pDoc) 2685 { 2686 if (pDoc->ValidRow(nStart)) 2687 maPos = rCells.position(nStart); 2688 else 2689 // Make this iterator invalid. 2690 maPos.first = miEnd; 2691 } 2692 2693 bool valid() const { return (maPos.first != miEnd); } 2694 2695 bool has() const 2696 { 2697 return (maPos.first->type == sc::element_type_string || maPos.first->type == sc::element_type_edittext); 2698 } 2699 2700 bool prev() 2701 { 2702 if (!has()) 2703 { 2704 // Not in a string block. Move back until we hit a string block. 2705 while (!has()) 2706 { 2707 if (maPos.first == miBeg) 2708 return false; 2709 2710 --maPos.first; // move to the preceding block. 2711 maPos.second = maPos.first->size - 1; // last cell in the block. 2712 } 2713 return true; 2714 } 2715 2716 // We are in a string block. 2717 if (maPos.second > 0) 2718 { 2719 // Move back one cell in the same block. 2720 --maPos.second; 2721 } 2722 else 2723 { 2724 // Move back to the preceding string block. 2725 while (true) 2726 { 2727 if (maPos.first == miBeg) 2728 return false; 2729 2730 // Move to the last cell of the previous block. 2731 --maPos.first; 2732 maPos.second = maPos.first->size - 1; 2733 if (has()) 2734 break; 2735 } 2736 } 2737 return true; 2738 } 2739 2740 bool next() 2741 { 2742 if (!has()) 2743 { 2744 // Not in a string block. Move forward until we hit a string block. 2745 while (!has()) 2746 { 2747 ++maPos.first; 2748 if (maPos.first == miEnd) 2749 return false; 2750 2751 maPos.second = 0; // First cell in this block. 2752 } 2753 return true; 2754 } 2755 2756 // We are in a string block. 2757 ++maPos.second; 2758 if (maPos.second >= maPos.first->size) 2759 { 2760 // Move to the next string block. 2761 while (true) 2762 { 2763 ++maPos.first; 2764 if (maPos.first == miEnd) 2765 return false; 2766 2767 maPos.second = 0; 2768 if (has()) 2769 break; 2770 } 2771 } 2772 return true; 2773 } 2774 2775 OUString get() const 2776 { 2777 switch (maPos.first->type) 2778 { 2779 case sc::element_type_string: 2780 return sc::string_block::at(*maPos.first->data, maPos.second).getString(); 2781 case sc::element_type_edittext: 2782 { 2783 const EditTextObject* p = sc::edittext_block::at(*maPos.first->data, maPos.second); 2784 return ScEditUtil::GetString(*p, mpDoc); 2785 } 2786 default: 2787 ; 2788 } 2789 return OUString(); 2790 } 2791 }; 2792 2793 } 2794 2795 // GetDataEntries - Strings from continuous Section around nRow 2796 bool ScColumn::GetDataEntries( 2797 SCROW nStartRow, std::set<ScTypedStrData>& rStrings) const 2798 { 2799 // Start at the specified row position, and collect all string values 2800 // going upward and downward directions in parallel. The start position 2801 // cell must be skipped. 2802 2803 StrCellIterator aItrUp(maCells, nStartRow, &GetDoc()); 2804 StrCellIterator aItrDown(maCells, nStartRow+1, &GetDoc()); 2805 2806 bool bMoveUp = aItrUp.valid(); 2807 if (!bMoveUp) 2808 // Current cell is invalid. 2809 return false; 2810 2811 // Skip the start position cell. 2812 bMoveUp = aItrUp.prev(); // Find the previous string cell position. 2813 2814 bool bMoveDown = aItrDown.valid(); 2815 if (bMoveDown && !aItrDown.has()) 2816 bMoveDown = aItrDown.next(); // Find the next string cell position. 2817 2818 bool bFound = false; 2819 while (bMoveUp) 2820 { 2821 // Get the current string and move up. 2822 OUString aStr = aItrUp.get(); 2823 if (!aStr.isEmpty()) 2824 { 2825 if (rStrings.insert(ScTypedStrData(std::move(aStr))).second) 2826 bFound = true; 2827 } 2828 2829 bMoveUp = aItrUp.prev(); 2830 } 2831 2832 while (bMoveDown) 2833 { 2834 // Get the current string and move down. 2835 OUString aStr = aItrDown.get(); 2836 if (!aStr.isEmpty()) 2837 { 2838 if (rStrings.insert(ScTypedStrData(std::move(aStr))).second) 2839 bFound = true; 2840 } 2841 2842 bMoveDown = aItrDown.next(); 2843 } 2844 2845 return bFound; 2846 } 2847 2848 namespace { 2849 2850 class FormulaToValueHandler 2851 { 2852 struct Entry 2853 { 2854 SCROW mnRow; 2855 ScCellValue maValue; 2856 2857 Entry(SCROW nRow, double f) : mnRow(nRow), maValue(f) {} 2858 Entry(SCROW nRow, const svl::SharedString& rStr) : mnRow(nRow), maValue(rStr) {} 2859 }; 2860 2861 typedef std::vector<Entry> EntriesType; 2862 EntriesType maEntries; 2863 2864 public: 2865 2866 void operator() (size_t nRow, const ScFormulaCell* p) 2867 { 2868 ScFormulaCell* p2 = const_cast<ScFormulaCell*>(p); 2869 if (p2->IsValue()) 2870 maEntries.emplace_back(nRow, p2->GetValue()); 2871 else 2872 maEntries.emplace_back(nRow, p2->GetString()); 2873 } 2874 2875 void commitCells(ScColumn& rColumn) 2876 { 2877 sc::ColumnBlockPosition aBlockPos; 2878 rColumn.InitBlockPosition(aBlockPos); 2879 2880 for (const Entry& r : maEntries) 2881 { 2882 switch (r.maValue.getType()) 2883 { 2884 case CELLTYPE_VALUE: 2885 rColumn.SetValue(aBlockPos, r.mnRow, r.maValue.getDouble(), false); 2886 break; 2887 case CELLTYPE_STRING: 2888 rColumn.SetRawString(aBlockPos, r.mnRow, *r.maValue.getSharedString(), false); 2889 break; 2890 default: 2891 ; 2892 } 2893 } 2894 } 2895 }; 2896 2897 } 2898 2899 void ScColumn::RemoveProtected( SCROW nStartRow, SCROW nEndRow ) 2900 { 2901 FormulaToValueHandler aFunc; 2902 sc::CellStoreType::const_iterator itPos = maCells.begin(); 2903 2904 ScAttrIterator aAttrIter( pAttrArray.get(), nStartRow, nEndRow, GetDoc().GetDefPattern() ); 2905 SCROW nTop = -1; 2906 SCROW nBottom = -1; 2907 const ScPatternAttr* pPattern = aAttrIter.Next( nTop, nBottom ); 2908 while (pPattern) 2909 { 2910 const ScProtectionAttr* pAttr = &pPattern->GetItem(ATTR_PROTECTION); 2911 if ( pAttr->GetHideCell() ) 2912 DeleteArea( nTop, nBottom, InsertDeleteFlags::CONTENTS ); 2913 else if ( pAttr->GetHideFormula() ) 2914 { 2915 // Replace all formula cells between nTop and nBottom with raw value cells. 2916 itPos = sc::ParseFormula(itPos, maCells, nTop, nBottom, aFunc); 2917 } 2918 2919 pPattern = aAttrIter.Next( nTop, nBottom ); 2920 } 2921 2922 aFunc.commitCells(*this); 2923 } 2924 2925 void ScColumn::SetError( SCROW nRow, const FormulaError nError) 2926 { 2927 if (!GetDoc().ValidRow(nRow)) 2928 return; 2929 2930 ScFormulaCell* pCell = new ScFormulaCell(GetDoc(), ScAddress(nCol, nRow, nTab)); 2931 pCell->SetErrCode(nError); 2932 2933 std::vector<SCROW> aNewSharedRows; 2934 sc::CellStoreType::iterator it = GetPositionToInsert(nRow, aNewSharedRows, true); 2935 it = maCells.set(it, nRow, pCell); 2936 maCellTextAttrs.set(nRow, sc::CellTextAttr()); 2937 2938 CellStorageModified(); 2939 2940 AttachNewFormulaCell(it, nRow, *pCell, aNewSharedRows); 2941 } 2942 2943 void ScColumn::SetRawString( SCROW nRow, const OUString& rStr ) 2944 { 2945 if (!GetDoc().ValidRow(nRow)) 2946 return; 2947 2948 svl::SharedString aSS = GetDoc().GetSharedStringPool().intern(rStr); 2949 if (!aSS.getData()) 2950 return; 2951 2952 SetRawString(nRow, aSS); 2953 } 2954 2955 void ScColumn::SetRawString( SCROW nRow, const svl::SharedString& rStr ) 2956 { 2957 if (!GetDoc().ValidRow(nRow)) 2958 return; 2959 2960 std::vector<SCROW> aNewSharedRows; 2961 sc::CellStoreType::iterator it = GetPositionToInsert(nRow, aNewSharedRows, false); 2962 maCells.set(it, nRow, rStr); 2963 maCellTextAttrs.set(nRow, sc::CellTextAttr()); 2964 2965 CellStorageModified(); 2966 2967 StartListeningUnshared( aNewSharedRows); 2968 2969 BroadcastNewCell(nRow); 2970 } 2971 2972 void ScColumn::SetRawString( 2973 sc::ColumnBlockPosition& rBlockPos, SCROW nRow, const svl::SharedString& rStr, bool bBroadcast ) 2974 { 2975 if (!GetDoc().ValidRow(nRow)) 2976 return; 2977 2978 std::vector<SCROW> aNewSharedRows; 2979 rBlockPos.miCellPos = GetPositionToInsert(rBlockPos.miCellPos, nRow, aNewSharedRows, false); 2980 rBlockPos.miCellPos = maCells.set(rBlockPos.miCellPos, nRow, rStr); 2981 rBlockPos.miCellTextAttrPos = maCellTextAttrs.set( 2982 rBlockPos.miCellTextAttrPos, nRow, sc::CellTextAttr()); 2983 2984 CellStorageModified(); 2985 2986 StartListeningUnshared( aNewSharedRows); 2987 2988 if (bBroadcast) 2989 BroadcastNewCell(nRow); 2990 } 2991 2992 void ScColumn::SetValue( SCROW nRow, double fVal ) 2993 { 2994 if (!GetDoc().ValidRow(nRow)) 2995 return; 2996 2997 std::vector<SCROW> aNewSharedRows; 2998 sc::CellStoreType::iterator it = GetPositionToInsert(nRow, aNewSharedRows, false); 2999 maCells.set(it, nRow, fVal); 3000 maCellTextAttrs.set(nRow, sc::CellTextAttr()); 3001 3002 CellStorageModified(); 3003 3004 StartListeningUnshared( aNewSharedRows); 3005 3006 BroadcastNewCell(nRow); 3007 } 3008 3009 void ScColumn::SetValue( 3010 sc::ColumnBlockPosition& rBlockPos, SCROW nRow, double fVal, bool bBroadcast ) 3011 { 3012 if (!GetDoc().ValidRow(nRow)) 3013 return; 3014 3015 std::vector<SCROW> aNewSharedRows; 3016 rBlockPos.miCellPos = GetPositionToInsert(rBlockPos.miCellPos, nRow, aNewSharedRows, false); 3017 rBlockPos.miCellPos = maCells.set(rBlockPos.miCellPos, nRow, fVal); 3018 rBlockPos.miCellTextAttrPos = maCellTextAttrs.set( 3019 rBlockPos.miCellTextAttrPos, nRow, sc::CellTextAttr()); 3020 3021 CellStorageModified(); 3022 3023 StartListeningUnshared( aNewSharedRows); 3024 3025 if (bBroadcast) 3026 BroadcastNewCell(nRow); 3027 } 3028 3029 OUString ScColumn::GetString( const ScRefCellValue& aCell, SCROW nRow, const ScInterpreterContext* pContext ) const 3030 { 3031 // ugly hack for ordering problem with GetNumberFormat and missing inherited formats 3032 if (aCell.getType() == CELLTYPE_FORMULA) 3033 aCell.getFormula()->MaybeInterpret(); 3034 3035 sal_uInt32 nFormat = GetNumberFormat( pContext ? *pContext : GetDoc().GetNonThreadedContext(), nRow); 3036 const Color* pColor = nullptr; 3037 return ScCellFormat::GetString(aCell, nFormat, &pColor, 3038 pContext ? *(pContext->GetFormatTable()) : *(GetDoc().GetFormatTable()), GetDoc()); 3039 } 3040 3041 double* ScColumn::GetValueCell( SCROW nRow ) 3042 { 3043 std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(nRow); 3044 sc::CellStoreType::iterator it = aPos.first; 3045 if (it == maCells.end()) 3046 return nullptr; 3047 3048 if (it->type != sc::element_type_numeric) 3049 return nullptr; 3050 3051 return &sc::numeric_block::at(*it->data, aPos.second); 3052 } 3053 3054 OUString ScColumn::GetInputString( const ScRefCellValue& aCell, SCROW nRow, bool bForceSystemLocale ) const 3055 { 3056 sal_uLong nFormat = GetNumberFormat(GetDoc().GetNonThreadedContext(), nRow); 3057 return ScCellFormat::GetInputString(aCell, nFormat, *(GetDoc().GetFormatTable()), GetDoc(), nullptr, false, bForceSystemLocale); 3058 } 3059 3060 double ScColumn::GetValue( SCROW nRow ) const 3061 { 3062 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow); 3063 sc::CellStoreType::const_iterator it = aPos.first; 3064 switch (it->type) 3065 { 3066 case sc::element_type_numeric: 3067 return sc::numeric_block::at(*it->data, aPos.second); 3068 case sc::element_type_formula: 3069 { 3070 const ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second); 3071 ScFormulaCell* p2 = const_cast<ScFormulaCell*>(p); 3072 return p2->IsValue() ? p2->GetValue() : 0.0; 3073 } 3074 default: 3075 ; 3076 } 3077 3078 return 0.0; 3079 } 3080 3081 const EditTextObject* ScColumn::GetEditText( SCROW nRow ) const 3082 { 3083 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow); 3084 sc::CellStoreType::const_iterator it = aPos.first; 3085 if (it == maCells.end()) 3086 return nullptr; 3087 3088 if (it->type != sc::element_type_edittext) 3089 return nullptr; 3090 3091 return sc::edittext_block::at(*it->data, aPos.second); 3092 } 3093 3094 void ScColumn::RemoveEditTextCharAttribs( SCROW nRow, const ScPatternAttr& rAttr ) 3095 { 3096 std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(nRow); 3097 sc::CellStoreType::iterator it = aPos.first; 3098 if (it == maCells.end()) 3099 return; 3100 3101 if (it->type != sc::element_type_edittext) 3102 return; 3103 3104 EditTextObject* p = sc::edittext_block::at(*it->data, aPos.second); 3105 ScEditUtil::RemoveCharAttribs(*p, rAttr); 3106 } 3107 3108 OUString ScColumn::GetFormula( SCROW nRow ) const 3109 { 3110 const ScFormulaCell* p = FetchFormulaCell(nRow); 3111 if (p) 3112 return p->GetFormula(); 3113 return OUString(); 3114 } 3115 3116 const ScFormulaCell* ScColumn::GetFormulaCell( SCROW nRow ) const 3117 { 3118 return FetchFormulaCell(nRow); 3119 } 3120 3121 ScFormulaCell* ScColumn::GetFormulaCell( SCROW nRow ) 3122 { 3123 return const_cast<ScFormulaCell*>(FetchFormulaCell(nRow)); 3124 } 3125 3126 CellType ScColumn::GetCellType( SCROW nRow ) const 3127 { 3128 switch (maCells.get_type(nRow)) 3129 { 3130 case sc::element_type_numeric: 3131 return CELLTYPE_VALUE; 3132 case sc::element_type_string: 3133 return CELLTYPE_STRING; 3134 case sc::element_type_edittext: 3135 return CELLTYPE_EDIT; 3136 case sc::element_type_formula: 3137 return CELLTYPE_FORMULA; 3138 default: 3139 ; 3140 } 3141 return CELLTYPE_NONE; 3142 } 3143 3144 namespace { 3145 3146 /** 3147 * Count the number of all non-empty cells. 3148 */ 3149 class CellCounter 3150 { 3151 size_t mnCount; 3152 public: 3153 CellCounter() : mnCount(0) {} 3154 3155 void operator() (const sc::CellStoreType::value_type& node) 3156 { 3157 if (node.type == sc::element_type_empty) 3158 return; 3159 3160 mnCount += node.size; 3161 } 3162 3163 size_t getCount() const { return mnCount; } 3164 }; 3165 3166 } 3167 3168 SCSIZE ScColumn::GetCellCount() const 3169 { 3170 CellCounter aFunc; 3171 aFunc = std::for_each(maCells.begin(), maCells.end(), aFunc); 3172 return aFunc.getCount(); 3173 } 3174 3175 FormulaError ScColumn::GetErrCode( SCROW nRow ) const 3176 { 3177 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow); 3178 sc::CellStoreType::const_iterator it = aPos.first; 3179 if (it == maCells.end()) 3180 return FormulaError::NONE; 3181 3182 if (it->type != sc::element_type_formula) 3183 return FormulaError::NONE; 3184 3185 const ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second); 3186 return const_cast<ScFormulaCell*>(p)->GetErrCode(); 3187 } 3188 3189 bool ScColumn::HasStringData( SCROW nRow ) const 3190 { 3191 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow); 3192 switch (aPos.first->type) 3193 { 3194 case sc::element_type_string: 3195 case sc::element_type_edittext: 3196 return true; 3197 case sc::element_type_formula: 3198 { 3199 const ScFormulaCell* p = sc::formula_block::at(*aPos.first->data, aPos.second); 3200 return !const_cast<ScFormulaCell*>(p)->IsValue(); 3201 } 3202 default: 3203 ; 3204 } 3205 3206 return false; 3207 } 3208 3209 bool ScColumn::HasValueData( SCROW nRow ) const 3210 { 3211 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow); 3212 switch (aPos.first->type) 3213 { 3214 case sc::element_type_numeric: 3215 return true; 3216 case sc::element_type_formula: 3217 { 3218 const ScFormulaCell* p = sc::formula_block::at(*aPos.first->data, aPos.second); 3219 return const_cast<ScFormulaCell*>(p)->IsValue(); 3220 } 3221 default: 3222 ; 3223 } 3224 3225 return false; 3226 } 3227 3228 /** 3229 * Return true if there is a string or editcell in the range 3230 */ 3231 bool ScColumn::HasStringCells( SCROW nStartRow, SCROW nEndRow ) const 3232 { 3233 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow); 3234 sc::CellStoreType::const_iterator it = aPos.first; 3235 size_t nOffset = aPos.second; 3236 SCROW nRow = nStartRow; 3237 for (; it != maCells.end() && nRow <= nEndRow; ++it) 3238 { 3239 if (it->type == sc::element_type_string || it->type == sc::element_type_edittext) 3240 return true; 3241 3242 nRow += it->size - nOffset; 3243 nOffset = 0; 3244 } 3245 3246 return false; 3247 } 3248 3249 namespace { 3250 3251 class MaxStringLenHandler 3252 { 3253 sal_Int32 mnMaxLen; 3254 const ScColumn& mrColumn; 3255 SvNumberFormatter* mpFormatter; 3256 rtl_TextEncoding meCharSet; 3257 bool mbOctetEncoding; 3258 3259 void processCell(size_t nRow, const ScRefCellValue& rCell) 3260 { 3261 const Color* pColor; 3262 sal_uInt32 nFormat = mrColumn.GetAttr(nRow, ATTR_VALUE_FORMAT).GetValue(); 3263 OUString aString = ScCellFormat::GetString(rCell, nFormat, &pColor, *mpFormatter, mrColumn.GetDoc()); 3264 sal_Int32 nLen = 0; 3265 if (mbOctetEncoding) 3266 { 3267 OString aOString; 3268 if (!aString.convertToString(&aOString, meCharSet, 3269 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | 3270 RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)) 3271 { 3272 // TODO: anything? this is used by the dBase export filter 3273 // that throws an error anyway, but in case of another 3274 // context we might want to indicate a conversion error 3275 // early. 3276 } 3277 nLen = aOString.getLength(); 3278 } 3279 else 3280 nLen = aString.getLength() * sizeof(sal_Unicode); 3281 3282 if (mnMaxLen < nLen) 3283 mnMaxLen = nLen; 3284 } 3285 3286 public: 3287 MaxStringLenHandler(const ScColumn& rColumn, rtl_TextEncoding eCharSet) : 3288 mnMaxLen(0), 3289 mrColumn(rColumn), 3290 mpFormatter(rColumn.GetDoc().GetFormatTable()), 3291 meCharSet(eCharSet), 3292 mbOctetEncoding(rtl_isOctetTextEncoding(eCharSet)) 3293 { 3294 } 3295 3296 void operator() (size_t nRow, double fVal) 3297 { 3298 ScRefCellValue aCell(fVal); 3299 processCell(nRow, aCell); 3300 } 3301 3302 void operator() (size_t nRow, const svl::SharedString& rStr) 3303 { 3304 ScRefCellValue aCell(&rStr); 3305 processCell(nRow, aCell); 3306 } 3307 3308 void operator() (size_t nRow, const EditTextObject* p) 3309 { 3310 ScRefCellValue aCell(p); 3311 processCell(nRow, aCell); 3312 } 3313 3314 void operator() (size_t nRow, const ScFormulaCell* p) 3315 { 3316 ScRefCellValue aCell(const_cast<ScFormulaCell*>(p)); 3317 processCell(nRow, aCell); 3318 } 3319 3320 sal_Int32 getMaxLen() const { return mnMaxLen; } 3321 }; 3322 3323 } 3324 3325 sal_Int32 ScColumn::GetMaxStringLen( SCROW nRowStart, SCROW nRowEnd, rtl_TextEncoding eCharSet ) const 3326 { 3327 MaxStringLenHandler aFunc(*this, eCharSet); 3328 sc::ParseAllNonEmpty(maCells.begin(), maCells, nRowStart, nRowEnd, aFunc); 3329 return aFunc.getMaxLen(); 3330 } 3331 3332 namespace { 3333 3334 class MaxNumStringLenHandler 3335 { 3336 const ScColumn& mrColumn; 3337 SvNumberFormatter* mpFormatter; 3338 sal_Int32 mnMaxLen; 3339 sal_uInt16 mnPrecision; 3340 sal_uInt16 mnMaxGeneralPrecision; 3341 bool mbHaveSigned; 3342 3343 void processCell(size_t nRow, ScRefCellValue& rCell) 3344 { 3345 sal_uInt16 nCellPrecision = mnMaxGeneralPrecision; 3346 if (rCell.getType() == CELLTYPE_FORMULA) 3347 { 3348 if (!rCell.getFormula()->IsValue()) 3349 return; 3350 3351 // Limit unformatted formula cell precision to precision 3352 // encountered so far, if any, otherwise we'd end up with 15 just 3353 // because of =1/3 ... If no precision yet then arbitrarily limit 3354 // to a maximum of 4 unless a maximum general precision is set. 3355 if (mnPrecision) 3356 nCellPrecision = mnPrecision; 3357 else 3358 nCellPrecision = (mnMaxGeneralPrecision >= 15) ? 4 : mnMaxGeneralPrecision; 3359 } 3360 3361 double fVal = rCell.getValue(); 3362 if (!mbHaveSigned && fVal < 0.0) 3363 mbHaveSigned = true; 3364 3365 OUString aString; 3366 OUString aSep; 3367 sal_uInt16 nPrec; 3368 sal_uInt32 nFormat = 3369 mrColumn.GetAttr(nRow, ATTR_VALUE_FORMAT).GetValue(); 3370 if (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) 3371 { 3372 aSep = mpFormatter->GetFormatDecimalSep(nFormat); 3373 aString = ScCellFormat::GetInputString(rCell, nFormat, *mpFormatter, mrColumn.GetDoc()); 3374 const SvNumberformat* pEntry = mpFormatter->GetEntry(nFormat); 3375 if (pEntry) 3376 { 3377 bool bThousand, bNegRed; 3378 sal_uInt16 nLeading; 3379 pEntry->GetFormatSpecialInfo(bThousand, bNegRed, nPrec, nLeading); 3380 } 3381 else 3382 nPrec = mpFormatter->GetFormatPrecision(nFormat); 3383 } 3384 else 3385 { 3386 if (mnPrecision >= mnMaxGeneralPrecision) 3387 return; // early bail out for nothing changes here 3388 3389 if (!fVal) 3390 { 3391 // 0 doesn't change precision, but set a maximum length if none yet. 3392 if (!mnMaxLen) 3393 mnMaxLen = 1; 3394 return; 3395 } 3396 3397 // Simple number string with at most 15 decimals and trailing 3398 // decimal zeros eliminated. 3399 aSep = "."; 3400 aString = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_F, nCellPrecision, '.', true); 3401 nPrec = SvNumberFormatter::UNLIMITED_PRECISION; 3402 } 3403 3404 sal_Int32 nLen = aString.getLength(); 3405 if (nLen <= 0) 3406 // Ignore empty string. 3407 return; 3408 3409 if (nPrec == SvNumberFormatter::UNLIMITED_PRECISION && mnPrecision < mnMaxGeneralPrecision) 3410 { 3411 if (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) 3412 { 3413 // For some reason we couldn't obtain a precision from the 3414 // format, retry with simple number string. 3415 aSep = "."; 3416 aString = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_F, nCellPrecision, '.', true); 3417 nLen = aString.getLength(); 3418 } 3419 sal_Int32 nSep = aString.indexOf( aSep); 3420 if (nSep != -1) 3421 nPrec = aString.getLength() - nSep - 1; 3422 3423 } 3424 3425 if (nPrec != SvNumberFormatter::UNLIMITED_PRECISION && nPrec > mnPrecision) 3426 mnPrecision = nPrec; 3427 3428 if (mnPrecision) 3429 { // less than mnPrecision in string => widen it 3430 // more => shorten it 3431 sal_Int32 nTmp = aString.indexOf(aSep); 3432 if ( nTmp == -1 ) 3433 nLen += mnPrecision + aSep.getLength(); 3434 else 3435 { 3436 nTmp = aString.getLength() - (nTmp + aSep.getLength()); 3437 if (nTmp != mnPrecision) 3438 nLen += mnPrecision - nTmp; 3439 // nPrecision > nTmp : nLen + Diff 3440 // nPrecision < nTmp : nLen - Diff 3441 } 3442 } 3443 3444 // Enlarge for sign if necessary. Bear in mind that 3445 // GetMaxNumberStringLen() is for determining dBase decimal field width 3446 // and precision where the overall field width must include the sign. 3447 // Fitting -1 into "#.##" (width 4, 2 decimals) does not work. 3448 if (mbHaveSigned && fVal >= 0.0) 3449 ++nLen; 3450 3451 if (mnMaxLen < nLen) 3452 mnMaxLen = nLen; 3453 } 3454 3455 public: 3456 MaxNumStringLenHandler(const ScColumn& rColumn, sal_uInt16 nMaxGeneralPrecision) : 3457 mrColumn(rColumn), mpFormatter(rColumn.GetDoc().GetFormatTable()), 3458 mnMaxLen(0), mnPrecision(0), mnMaxGeneralPrecision(nMaxGeneralPrecision), 3459 mbHaveSigned(false) 3460 { 3461 // Limit the decimals passed to doubleToUString(). 3462 // Also, the dBaseIII maximum precision is 15. 3463 if (mnMaxGeneralPrecision > 15) 3464 mnMaxGeneralPrecision = 15; 3465 } 3466 3467 void operator() (size_t nRow, double fVal) 3468 { 3469 ScRefCellValue aCell(fVal); 3470 processCell(nRow, aCell); 3471 } 3472 3473 void operator() (size_t nRow, const ScFormulaCell* p) 3474 { 3475 ScRefCellValue aCell(const_cast<ScFormulaCell*>(p)); 3476 processCell(nRow, aCell); 3477 } 3478 3479 sal_Int32 getMaxLen() const { return mnMaxLen; } 3480 3481 sal_uInt16 getPrecision() const { return mnPrecision; } 3482 }; 3483 3484 } 3485 3486 sal_Int32 ScColumn::GetMaxNumberStringLen( 3487 sal_uInt16& nPrecision, SCROW nRowStart, SCROW nRowEnd ) const 3488 { 3489 sal_uInt16 nMaxGeneralPrecision = GetDoc().GetDocOptions().GetStdPrecision(); 3490 MaxNumStringLenHandler aFunc(*this, nMaxGeneralPrecision); 3491 sc::ParseFormulaNumeric(maCells.begin(), maCells, nRowStart, nRowEnd, aFunc); 3492 nPrecision = aFunc.getPrecision(); 3493 return aFunc.getMaxLen(); 3494 } 3495 3496 namespace { 3497 3498 class GroupFormulaCells 3499 { 3500 std::vector<ScAddress>* mpGroupPos; 3501 3502 public: 3503 explicit GroupFormulaCells(std::vector<ScAddress>* pGroupPos) 3504 : mpGroupPos(pGroupPos) {} 3505 3506 void operator() (sc::CellStoreType::value_type& node) 3507 { 3508 if (node.type != sc::element_type_formula) 3509 // We are only interested in formula cells. 3510 return; 3511 3512 size_t nRow = node.position; // start row position. 3513 3514 sc::formula_block::iterator it = sc::formula_block::begin(*node.data); 3515 sc::formula_block::iterator itEnd = sc::formula_block::end(*node.data); 3516 3517 // This block should never be empty. 3518 3519 ScFormulaCell* pPrev = *it; 3520 ScFormulaCellGroupRef xPrevGrp = pPrev->GetCellGroup(); 3521 if (xPrevGrp) 3522 { 3523 // Move to the cell after the last cell of the current group. 3524 std::advance(it, xPrevGrp->mnLength); 3525 nRow += xPrevGrp->mnLength; 3526 } 3527 else 3528 { 3529 ++it; 3530 ++nRow; 3531 } 3532 3533 ScFormulaCell* pCur = nullptr; 3534 ScFormulaCellGroupRef xCurGrp; 3535 for (; it != itEnd; pPrev = pCur, xPrevGrp = xCurGrp) 3536 { 3537 pCur = *it; 3538 xCurGrp = pCur->GetCellGroup(); 3539 3540 ScFormulaCell::CompareState eCompState = pPrev->CompareByTokenArray(*pCur); 3541 if (eCompState == ScFormulaCell::NotEqual) 3542 { 3543 // different formula tokens. 3544 if (xCurGrp) 3545 { 3546 // Move to the cell after the last cell of the current group. 3547 if (xCurGrp->mnLength > std::distance(it, itEnd)) 3548 throw css::lang::IllegalArgumentException(); 3549 std::advance(it, xCurGrp->mnLength); 3550 nRow += xCurGrp->mnLength; 3551 } 3552 else 3553 { 3554 ++it; 3555 ++nRow; 3556 } 3557 3558 continue; 3559 } 3560 3561 // Formula tokens equal those of the previous formula cell or cell group. 3562 if (xPrevGrp) 3563 { 3564 // Previous cell is a group. 3565 if (xCurGrp) 3566 { 3567 // The current cell is a group. Merge these two groups. 3568 xPrevGrp->mnLength += xCurGrp->mnLength; 3569 pCur->SetCellGroup(xPrevGrp); 3570 sc::formula_block::iterator itGrpEnd = it; 3571 if (xCurGrp->mnLength > std::distance(itGrpEnd, itEnd)) 3572 throw css::lang::IllegalArgumentException(); 3573 std::advance(itGrpEnd, xCurGrp->mnLength); 3574 for (++it; it != itGrpEnd; ++it) 3575 { 3576 ScFormulaCell* pCell = *it; 3577 pCell->SetCellGroup(xPrevGrp); 3578 } 3579 nRow += xCurGrp->mnLength; 3580 } 3581 else 3582 { 3583 // Add this cell to the previous group. 3584 pCur->SetCellGroup(xPrevGrp); 3585 ++xPrevGrp->mnLength; 3586 ++nRow; 3587 ++it; 3588 } 3589 3590 } 3591 else if (xCurGrp) 3592 { 3593 // Previous cell is a regular cell and current cell is a group. 3594 nRow += xCurGrp->mnLength; 3595 if (xCurGrp->mnLength > std::distance(it, itEnd)) 3596 throw css::lang::IllegalArgumentException(); 3597 std::advance(it, xCurGrp->mnLength); 3598 pPrev->SetCellGroup(xCurGrp); 3599 xCurGrp->mpTopCell = pPrev; 3600 ++xCurGrp->mnLength; 3601 xPrevGrp = xCurGrp; 3602 } 3603 else 3604 { 3605 // Both previous and current cells are regular cells. 3606 assert(pPrev->aPos.Row() == static_cast<SCROW>(nRow - 1)); 3607 xPrevGrp = pPrev->CreateCellGroup(2, eCompState == ScFormulaCell::EqualInvariant); 3608 pCur->SetCellGroup(xPrevGrp); 3609 ++nRow; 3610 ++it; 3611 } 3612 3613 if (mpGroupPos) 3614 mpGroupPos->push_back(pCur->aPos); 3615 3616 pCur = pPrev; 3617 xCurGrp = xPrevGrp; 3618 } 3619 } 3620 }; 3621 3622 } 3623 3624 void ScColumn::RegroupFormulaCells( std::vector<ScAddress>* pGroupPos ) 3625 { 3626 // re-build formula groups. 3627 std::for_each(maCells.begin(), maCells.end(), GroupFormulaCells(pGroupPos)); 3628 } 3629 3630 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 3631
