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 <algorithm> 21 #include <memory> 22 #include <table.hxx> 23 #include <patattr.hxx> 24 #include <docpool.hxx> 25 #include <formulacell.hxx> 26 #include <document.hxx> 27 #include <drwlayer.hxx> 28 #include <olinetab.hxx> 29 #include <stlpool.hxx> 30 #include <attarray.hxx> 31 #include <markdata.hxx> 32 #include <dociter.hxx> 33 #include <conditio.hxx> 34 #include <chartlis.hxx> 35 #include <fillinfo.hxx> 36 #include <bcaslot.hxx> 37 #include <postit.hxx> 38 #include <sheetevents.hxx> 39 #include <segmenttree.hxx> 40 #include <dbdata.hxx> 41 #include <tokenarray.hxx> 42 #include <clipcontext.hxx> 43 #include <types.hxx> 44 #include <editutil.hxx> 45 #include <mtvcellfunc.hxx> 46 #include <refupdatecontext.hxx> 47 #include <scopetools.hxx> 48 #include <tabprotection.hxx> 49 #include <columnspanset.hxx> 50 #include <rowheightcontext.hxx> 51 #include <listenercontext.hxx> 52 #include <compressedarray.hxx> 53 #include <brdcst.hxx> 54 #include <refdata.hxx> 55 #include <docsh.hxx> 56 57 #include <scitems.hxx> 58 #include <editeng/boxitem.hxx> 59 #include <editeng/editobj.hxx> 60 #include <o3tl/safeint.hxx> 61 #include <svl/poolcach.hxx> 62 #include <unotools/charclass.hxx> 63 #include <math.h> 64 65 namespace { 66 67 class ColumnRegroupFormulaCells 68 { 69 ScColContainer& mrCols; 70 std::vector<ScAddress>* mpGroupPos; 71 72 public: 73 ColumnRegroupFormulaCells( ScColContainer& rCols, std::vector<ScAddress>* pGroupPos ) : 74 mrCols(rCols), mpGroupPos(pGroupPos) {} 75 76 void operator() (SCCOL nCol) 77 { 78 mrCols[nCol].RegroupFormulaCells(mpGroupPos); 79 } 80 }; 81 82 } 83 84 sal_uInt16 ScTable::GetTextWidth(SCCOL nCol, SCROW nRow) const 85 { 86 return aCol[nCol].GetTextWidth(nRow); 87 } 88 89 bool ScTable::SetOutlineTable( const ScOutlineTable* pNewOutline ) 90 { 91 sal_uInt16 nOldSizeX = 0; 92 sal_uInt16 nOldSizeY = 0; 93 sal_uInt16 nNewSizeX = 0; 94 sal_uInt16 nNewSizeY = 0; 95 96 if (pOutlineTable) 97 { 98 nOldSizeX = pOutlineTable->GetColArray().GetDepth(); 99 nOldSizeY = pOutlineTable->GetRowArray().GetDepth(); 100 pOutlineTable.reset(); 101 } 102 103 if (pNewOutline) 104 { 105 pOutlineTable.reset(new ScOutlineTable( *pNewOutline )); 106 nNewSizeX = pOutlineTable->GetColArray().GetDepth(); 107 nNewSizeY = pOutlineTable->GetRowArray().GetDepth(); 108 } 109 110 return ( nNewSizeX != nOldSizeX || nNewSizeY != nOldSizeY ); // changed size? 111 } 112 113 void ScTable::StartOutlineTable() 114 { 115 if (!pOutlineTable) 116 pOutlineTable.reset(new ScOutlineTable); 117 } 118 119 void ScTable::SetSheetEvents( std::unique_ptr<ScSheetEvents> pNew ) 120 { 121 pSheetEvents = std::move(pNew); 122 123 SetCalcNotification( false ); // discard notifications before the events were set 124 125 SetStreamValid(false); 126 } 127 128 void ScTable::SetCalcNotification( bool bSet ) 129 { 130 bCalcNotification = bSet; 131 } 132 133 bool ScTable::TestInsertRow( SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCSIZE nSize ) const 134 { 135 bool bTest = true; 136 137 if ( nStartCol==0 && nEndCol==pDocument->MaxCol() && pOutlineTable ) 138 bTest = pOutlineTable->TestInsertRow(nSize); 139 140 for (SCCOL i=nStartCol; (i<=nEndCol) && bTest; i++) 141 bTest = CreateColumnIfNotExists(i).TestInsertRow(nStartRow, nSize); 142 143 return bTest; 144 } 145 146 void ScTable::InsertRow( SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCSIZE nSize ) 147 { 148 if (nStartCol==0 && nEndCol==pDocument->MaxCol()) 149 { 150 if (mpRowHeights && pRowFlags) 151 { 152 mpRowHeights->insertSegment(nStartRow, nSize); 153 CRFlags nNewFlags = pRowFlags->Insert( nStartRow, nSize); 154 // only copy manual size flag, clear all others 155 if (nNewFlags != CRFlags::NONE && (nNewFlags != CRFlags::ManualSize)) 156 pRowFlags->SetValue( nStartRow, nStartRow + nSize - 1, 157 nNewFlags & CRFlags::ManualSize); 158 } 159 160 if (pOutlineTable) 161 pOutlineTable->InsertRow( nStartRow, nSize ); 162 163 mpFilteredRows->insertSegment(nStartRow, nSize); 164 mpHiddenRows->insertSegment(nStartRow, nSize); 165 166 if (!maRowManualBreaks.empty()) 167 { 168 // Copy all breaks up to nStartRow (non-inclusive). 169 ::std::set<SCROW>::iterator itr1 = maRowManualBreaks.lower_bound(nStartRow); 170 ::std::set<SCROW> aNewBreaks(maRowManualBreaks.begin(), itr1); 171 172 // Copy all breaks from nStartRow (inclusive) to the last element, 173 // but add nSize to each value. 174 ::std::set<SCROW>::iterator itr2 = maRowManualBreaks.end(); 175 for (; itr1 != itr2; ++itr1) 176 aNewBreaks.insert(static_cast<SCROW>(*itr1 + nSize)); 177 178 maRowManualBreaks.swap(aNewBreaks); 179 } 180 } 181 182 for (SCCOL j=nStartCol; j<=nEndCol; j++) 183 aCol[j].InsertRow( nStartRow, nSize ); 184 185 mpCondFormatList->InsertRow(nTab, nStartCol, nEndCol, nStartRow, nSize); 186 187 InvalidatePageBreaks(); 188 189 // TODO: In the future we may want to check if the table has been 190 // really modified before setting the stream invalid. 191 SetStreamValid(false); 192 } 193 194 void ScTable::DeleteRow( 195 const sc::ColumnSet& rRegroupCols, SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCSIZE nSize, 196 bool* pUndoOutline, std::vector<ScAddress>* pGroupPos ) 197 { 198 if (nStartCol==0 && nEndCol==pDocument->MaxCol()) 199 { 200 if (pRowFlags) 201 pRowFlags->Remove( nStartRow, nSize); 202 203 if (mpRowHeights) 204 mpRowHeights->removeSegment(nStartRow, nStartRow+nSize); 205 206 if (pOutlineTable) 207 if (pOutlineTable->DeleteRow( nStartRow, nSize )) 208 if (pUndoOutline) 209 *pUndoOutline = true; 210 211 mpFilteredRows->removeSegment(nStartRow, nStartRow+nSize); 212 mpHiddenRows->removeSegment(nStartRow, nStartRow+nSize); 213 214 if (!maRowManualBreaks.empty()) 215 { 216 // Erase all manual breaks between nStartRow and nStartRow + nSize - 1 (inclusive). 217 std::set<SCROW>::iterator itr1 = maRowManualBreaks.lower_bound(nStartRow); 218 std::set<SCROW>::iterator itr2 = maRowManualBreaks.upper_bound(static_cast<SCROW>(nStartRow + nSize - 1)); 219 maRowManualBreaks.erase(itr1, itr2); 220 221 // Copy all breaks from the 1st element up to nStartRow to the new container. 222 itr1 = maRowManualBreaks.lower_bound(nStartRow); 223 ::std::set<SCROW> aNewBreaks(maRowManualBreaks.begin(), itr1); 224 225 // Copy all breaks from nStartRow to the last element, but subtract each value by nSize. 226 itr2 = maRowManualBreaks.end(); 227 for (; itr1 != itr2; ++itr1) 228 aNewBreaks.insert(static_cast<SCROW>(*itr1 - nSize)); 229 230 maRowManualBreaks.swap(aNewBreaks); 231 } 232 } 233 234 { // scope for bulk broadcast 235 ScBulkBroadcast aBulkBroadcast( pDocument->GetBASM(), SfxHintId::ScDataChanged); 236 for (SCCOL j=nStartCol; j<=nEndCol; j++) 237 aCol[j].DeleteRow(nStartRow, nSize, pGroupPos); 238 } 239 240 std::vector<SCCOL> aRegroupCols; 241 rRegroupCols.getColumns(nTab, aRegroupCols); 242 std::for_each( 243 aRegroupCols.begin(), aRegroupCols.end(), ColumnRegroupFormulaCells(aCol, pGroupPos)); 244 245 InvalidatePageBreaks(); 246 247 // TODO: In the future we may want to check if the table has been 248 // really modified before setting the stream invalid. 249 SetStreamValid(false); 250 } 251 252 bool ScTable::TestInsertCol( SCROW nStartRow, SCROW nEndRow, SCSIZE nSize ) const 253 { 254 if ( nSize > o3tl::make_unsigned(pDocument->MaxCol()) ) 255 return false; 256 257 if ( nStartRow==0 && nEndRow==pDocument->MaxRow() && pOutlineTable 258 && ! pOutlineTable->TestInsertCol(nSize) ) 259 return false; 260 261 auto range = GetColumnsRange( pDocument->MaxCol() - static_cast<SCCOL>(nSize) + 1, pDocument->MaxCol() ); 262 for (auto it = range.rbegin(); it != range.rend(); ++it ) 263 if (! aCol[*it].TestInsertCol(nStartRow, nEndRow)) 264 return false; 265 266 return true; 267 } 268 269 void ScTable::InsertCol( 270 const sc::ColumnSet& rRegroupCols, SCCOL nStartCol, SCROW nStartRow, SCROW nEndRow, SCSIZE nSize ) 271 { 272 if (nStartRow==0 && nEndRow==pDocument->MaxRow()) 273 { 274 if (mpColWidth && mpColFlags) 275 { 276 mpColWidth->InsertPreservingSize(nStartCol, nSize, STD_COL_WIDTH); 277 // The inserted columns have the same widths as the columns, which were selected for insert. 278 for (SCSIZE i=0; i < std::min(pDocument->MaxCol()-nSize-nStartCol, nSize); ++i) 279 mpColWidth->SetValue(nStartCol + i, mpColWidth->GetValue(nStartCol+i+nSize)); 280 mpColFlags->InsertPreservingSize(nStartCol, nSize, CRFlags::NONE); 281 } 282 if (pOutlineTable) 283 pOutlineTable->InsertCol( nStartCol, nSize ); 284 285 mpHiddenCols->insertSegment(nStartCol, static_cast<SCCOL>(nSize)); 286 mpFilteredCols->insertSegment(nStartCol, static_cast<SCCOL>(nSize)); 287 288 if (!maColManualBreaks.empty()) 289 { 290 // Copy all breaks up to nStartCol (non-inclusive). 291 ::std::set<SCCOL>::iterator itr1 = maColManualBreaks.lower_bound(nStartCol); 292 ::std::set<SCCOL> aNewBreaks(maColManualBreaks.begin(), itr1); 293 294 // Copy all breaks from nStartCol (inclusive) to the last element, 295 // but add nSize to each value. 296 ::std::set<SCCOL>::iterator itr2 = maColManualBreaks.end(); 297 for (; itr1 != itr2; ++itr1) 298 aNewBreaks.insert(static_cast<SCCOL>(*itr1 + nSize)); 299 300 maColManualBreaks.swap(aNewBreaks); 301 } 302 } 303 304 if ((nStartRow == 0) && (nEndRow == pDocument->MaxRow())) 305 { 306 for (SCSIZE i=0; i < nSize; i++) 307 for (SCCOL nCol = aCol.size() - 1; nCol > nStartCol; nCol--) 308 aCol[nCol].SwapCol(aCol[nCol-1]); 309 } 310 else 311 { 312 for (SCSIZE i=0; static_cast<SCCOL>(i+nSize)+nStartCol < aCol.size(); i++) 313 aCol[aCol.size() - 1 - nSize - i].MoveTo(nStartRow, nEndRow, aCol[aCol.size() - 1 - i]); 314 } 315 316 std::vector<SCCOL> aRegroupCols; 317 rRegroupCols.getColumns(nTab, aRegroupCols); 318 std::for_each(aRegroupCols.begin(), aRegroupCols.end(), ColumnRegroupFormulaCells(aCol, nullptr)); 319 320 if (nStartCol>0) // copy old attributes 321 { 322 sal_uInt16 nWhichArray[2]; 323 nWhichArray[0] = ATTR_MERGE; 324 nWhichArray[1] = 0; 325 326 sc::CopyToDocContext aCxt(*pDocument); 327 for (SCSIZE i=0; i<nSize; i++) 328 { 329 aCol[nStartCol-1].CopyToColumn(aCxt, nStartRow, nEndRow, InsertDeleteFlags::ATTRIB, 330 false, aCol[nStartCol+i] ); 331 aCol[nStartCol+i].RemoveFlags( nStartRow, nEndRow, 332 ScMF::Hor | ScMF::Ver | ScMF::Auto ); 333 aCol[nStartCol+i].ClearItems( nStartRow, nEndRow, nWhichArray ); 334 } 335 } 336 337 mpCondFormatList->InsertCol(nTab, nStartRow, nEndRow, nStartCol, nSize); 338 339 InvalidatePageBreaks(); 340 341 // TODO: In the future we may want to check if the table has been 342 // really modified before setting the stream invalid. 343 SetStreamValid(false); 344 } 345 346 void ScTable::DeleteCol( 347 const sc::ColumnSet& rRegroupCols, SCCOL nStartCol, SCROW nStartRow, SCROW nEndRow, SCSIZE nSize, bool* pUndoOutline ) 348 { 349 if (nStartRow==0 && nEndRow==pDocument->MaxRow()) 350 { 351 if (mpColWidth && mpColFlags) 352 { 353 assert( nStartCol + nSize <= o3tl::make_unsigned(pDocument->MaxCol()+1) ); // moving 0 if ==pDocument->MaxCol()+1 is correct 354 mpColWidth->RemovePreservingSize(nStartCol, nSize, STD_COL_WIDTH); 355 mpColFlags->RemovePreservingSize(nStartCol, nSize, CRFlags::NONE); 356 } 357 if (pOutlineTable) 358 if (pOutlineTable->DeleteCol( nStartCol, nSize )) 359 if (pUndoOutline) 360 *pUndoOutline = true; 361 362 SCCOL nRmSize = nStartCol + static_cast<SCCOL>(nSize); 363 mpHiddenCols->removeSegment(nStartCol, nRmSize); 364 mpFilteredCols->removeSegment(nStartCol, nRmSize); 365 366 if (!maColManualBreaks.empty()) 367 { 368 // Erase all manual breaks between nStartCol and nStartCol + nSize - 1 (inclusive). 369 std::set<SCCOL>::iterator itr1 = maColManualBreaks.lower_bound(nStartCol); 370 std::set<SCCOL>::iterator itr2 = maColManualBreaks.upper_bound(static_cast<SCCOL>(nStartCol + nSize - 1)); 371 maColManualBreaks.erase(itr1, itr2); 372 373 // Copy all breaks from the 1st element up to nStartCol to the new container. 374 itr1 = maColManualBreaks.lower_bound(nStartCol); 375 ::std::set<SCCOL> aNewBreaks(maColManualBreaks.begin(), itr1); 376 377 // Copy all breaks from nStartCol to the last element, but subtract each value by nSize. 378 itr2 = maColManualBreaks.end(); 379 for (; itr1 != itr2; ++itr1) 380 aNewBreaks.insert(static_cast<SCCOL>(*itr1 - nSize)); 381 382 maColManualBreaks.swap(aNewBreaks); 383 } 384 } 385 386 for (SCSIZE i = 0; i < nSize; i++) 387 aCol[nStartCol + i].DeleteArea(nStartRow, nEndRow, InsertDeleteFlags::ALL, false); 388 389 if ((nStartRow == 0) && (nEndRow == pDocument->MaxRow())) 390 { 391 for (SCSIZE i=0; i < nSize; i++) 392 for (SCCOL nCol = nStartCol; nCol < aCol.size() - 1; nCol++) 393 aCol[nCol].SwapCol(aCol[nCol+1]); 394 } 395 else 396 { 397 for (SCSIZE i=0; static_cast<SCCOL>(i+nSize)+nStartCol < aCol.size(); i++) 398 aCol[nStartCol + nSize + i].MoveTo(nStartRow, nEndRow, aCol[nStartCol + i]); 399 } 400 401 std::vector<SCCOL> aRegroupCols; 402 rRegroupCols.getColumns(nTab, aRegroupCols); 403 std::for_each(aRegroupCols.begin(), aRegroupCols.end(), ColumnRegroupFormulaCells(aCol, nullptr)); 404 405 InvalidatePageBreaks(); 406 407 // TODO: In the future we may want to check if the table has been 408 // really modified before setting the stream invalid. 409 SetStreamValid(false); 410 } 411 412 void ScTable::DeleteArea( 413 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, InsertDeleteFlags nDelFlag, 414 bool bBroadcast, sc::ColumnSpanSet* pBroadcastSpans ) 415 { 416 if ( nCol2 >= aCol.size() ) nCol2 = aCol.size() - 1; 417 if (nRow2 > pDocument->MaxRow()) nRow2 = pDocument->MaxRow(); 418 if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2)) 419 { 420 { // scope for bulk broadcast 421 ScBulkBroadcast aBulkBroadcast( pDocument->GetBASM(), SfxHintId::ScDataChanged); 422 for (SCCOL i = nCol1; i <= nCol2; i++) 423 aCol[i].DeleteArea(nRow1, nRow2, nDelFlag, bBroadcast, pBroadcastSpans); 424 } 425 426 // Do not set protected cell in a protected table 427 428 if ( IsProtected() && (nDelFlag & InsertDeleteFlags::ATTRIB) ) 429 { 430 ScPatternAttr aPattern(pDocument->GetPool()); 431 aPattern.GetItemSet().Put( ScProtectionAttr( false ) ); 432 ApplyPatternArea( nCol1, nRow1, nCol2, nRow2, aPattern ); 433 } 434 435 if( nDelFlag & InsertDeleteFlags::ATTRIB ) 436 mpCondFormatList->DeleteArea( nCol1, nRow1, nCol2, nRow2 ); 437 } 438 439 // TODO: In the future we may want to check if the table has been 440 // really modified before setting the stream invalid. 441 SetStreamValid(false); 442 } 443 444 void ScTable::DeleteSelection( InsertDeleteFlags nDelFlag, const ScMarkData& rMark, bool bBroadcast ) 445 { 446 { // scope for bulk broadcast 447 ScBulkBroadcast aBulkBroadcast( pDocument->GetBASM(), SfxHintId::ScDataChanged); 448 for (SCCOL i=0; i < aCol.size(); i++) 449 aCol[i].DeleteSelection(nDelFlag, rMark, bBroadcast); 450 } 451 452 ScRangeList aRangeList; 453 rMark.FillRangeListWithMarks(&aRangeList, false); 454 455 for (size_t i = 0; i < aRangeList.size(); ++i) 456 { 457 const ScRange & rRange = aRangeList[i]; 458 459 if((nDelFlag & InsertDeleteFlags::ATTRIB) && rRange.aStart.Tab() == nTab) 460 mpCondFormatList->DeleteArea( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row() ); 461 } 462 463 // Do not set protected cell in a protected sheet 464 465 if ( IsProtected() && (nDelFlag & InsertDeleteFlags::ATTRIB) ) 466 { 467 ScDocumentPool* pPool = pDocument->GetPool(); 468 SfxItemSet aSet( *pPool, svl::Items<ATTR_PATTERN_START, ATTR_PATTERN_END>{} ); 469 aSet.Put( ScProtectionAttr( false ) ); 470 SfxItemPoolCache aCache( pPool, &aSet ); 471 ApplySelectionCache( &aCache, rMark ); 472 } 473 474 // TODO: In the future we may want to check if the table has been 475 // really modified before setting the stream invalid. 476 SetStreamValid(false); 477 } 478 479 // pTable = Clipboard 480 void ScTable::CopyToClip( 481 sc::CopyToClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, 482 ScTable* pTable ) 483 { 484 if (!ValidColRow(nCol1, nRow1) || !ValidColRow(nCol2, nRow2)) 485 return; 486 487 // copy content 488 //local range names need to be copied first for formula cells 489 if (!pTable->mpRangeName && mpRangeName) 490 pTable->mpRangeName.reset( new ScRangeName(*mpRangeName) ); 491 492 nCol2 = ClampToAllocatedColumns(nCol2); 493 494 for ( SCCOL i = nCol1; i <= nCol2; i++) 495 aCol[i].CopyToClip(rCxt, nRow1, nRow2, pTable->CreateColumnIfNotExists(i)); // notes are handled at column level 496 497 // copy widths/heights, and only "hidden", "filtered" and "manual" flags 498 // also for all preceding columns/rows, to have valid positions for drawing objects 499 500 if (mpColWidth && pTable->mpColWidth) 501 pTable->mpColWidth->CopyFrom(*mpColWidth, 0, nCol2); 502 503 pTable->CopyColHidden(*this, 0, nCol2); 504 pTable->CopyColFiltered(*this, 0, nCol2); 505 if (pDBDataNoName) 506 pTable->SetAnonymousDBData(std::unique_ptr<ScDBData>(new ScDBData(*pDBDataNoName))); 507 508 if (pRowFlags && pTable->pRowFlags && mpRowHeights && pTable->mpRowHeights) 509 { 510 pTable->pRowFlags->CopyFromAnded( *pRowFlags, 0, nRow2, CRFlags::ManualSize); 511 pTable->CopyRowHeight(*this, 0, nRow2, 0); 512 } 513 514 pTable->CopyRowHidden(*this, 0, nRow2); 515 pTable->CopyRowFiltered(*this, 0, nRow2); 516 517 // If necessary replace formulas with values 518 519 if ( IsProtected() ) 520 for (SCCOL i = nCol1; i <= nCol2; i++) 521 pTable->aCol[i].RemoveProtected(nRow1, nRow2); 522 523 pTable->mpCondFormatList.reset(new ScConditionalFormatList(pTable->pDocument, *mpCondFormatList)); 524 } 525 526 void ScTable::CopyToClip( 527 sc::CopyToClipContext& rCxt, const ScRangeList& rRanges, ScTable* pTable ) 528 { 529 for ( size_t i = 0, nListSize = rRanges.size(); i < nListSize; ++i ) 530 { 531 const ScRange & r = rRanges[ i ]; 532 CopyToClip( rCxt, r.aStart.Col(), r.aStart.Row(), r.aEnd.Col(), r.aEnd.Row(), pTable); 533 } 534 } 535 536 void ScTable::CopyStaticToDocument( 537 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const SvNumberFormatterMergeMap& rMap, ScTable* pDestTab ) 538 { 539 if (nCol1 > nCol2 || nRow1 > nRow2) 540 return; 541 542 const SCCOL nFirstUnallocated = std::clamp<SCCOL>(GetAllocatedColumnsCount(), nCol1, nCol2 + 1); 543 if (nFirstUnallocated > nCol1) 544 pDestTab->CreateColumnIfNotExists(nFirstUnallocated - 1); 545 546 for (SCCOL i = nCol1; i < nFirstUnallocated; ++i) 547 { 548 ScColumn& rSrcCol = aCol[i]; 549 ScColumn& rDestCol = pDestTab->aCol[i]; 550 rSrcCol.CopyStaticToDocument(nRow1, nRow2, rMap, rDestCol); 551 } 552 553 // Maybe copy this table's default attrs to dest not limiting to already allocated in dest? 554 const SCCOL nLastInDest = std::min<SCCOL>(pDestTab->GetAllocatedColumnsCount() - 1, nCol2); 555 for (SCCOL i = nFirstUnallocated; i <= nLastInDest; ++i) 556 { 557 ScColumn& rDestCol = pDestTab->aCol[i]; 558 rDestCol.maCellTextAttrs.set_empty(nRow1, nRow2); 559 rDestCol.maCells.set_empty(nRow1, nRow2); 560 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) 561 { 562 sal_uInt32 nNumFmt = aDefaultColAttrArray.GetPattern(nRow)->GetNumberFormat( 563 pDocument->GetNonThreadedContext().GetFormatTable()); 564 SvNumberFormatterMergeMap::const_iterator itNum = rMap.find(nNumFmt); 565 if (itNum != rMap.end()) 566 nNumFmt = itNum->second; 567 568 rDestCol.SetNumberFormat(nRow, nNumFmt); 569 } 570 rDestCol.CellStorageModified(); 571 } 572 } 573 574 void ScTable::CopyCellToDocument(SCCOL nSrcCol, SCROW nSrcRow, SCCOL nDestCol, SCROW nDestRow, ScTable& rDestTab ) 575 { 576 if (!ValidColRow(nSrcCol, nSrcRow) || !ValidColRow(nDestCol, nDestRow)) 577 return; 578 579 if (nSrcCol >= GetAllocatedColumnsCount()) 580 { 581 if (nDestCol < rDestTab.GetAllocatedColumnsCount()) 582 { 583 ScColumn& rDestCol = rDestTab.aCol[nDestCol]; 584 rDestCol.maCells.set_empty(nDestRow, nDestRow); 585 rDestCol.maCellTextAttrs.set_empty(nDestRow, nDestRow); 586 rDestCol.maCellNotes.set_empty(nDestRow, nDestRow); 587 rDestCol.CellStorageModified(); 588 } 589 return; 590 } 591 592 ScColumn& rSrcCol = aCol[nSrcCol]; 593 ScColumn& rDestCol = rDestTab.CreateColumnIfNotExists(nDestCol); 594 rSrcCol.CopyCellToDocument(nSrcRow, nDestRow, rDestCol); 595 } 596 597 namespace { 598 599 bool CheckAndDeduplicateCondFormat(ScDocument* pDocument, ScConditionalFormat* pOldFormat, const ScConditionalFormat* pNewFormat, SCTAB nTab) 600 { 601 if (!pOldFormat) 602 return false; 603 604 if (pOldFormat->EqualEntries(*pNewFormat, true)) 605 { 606 const ScRangeList& rNewRangeList = pNewFormat->GetRange(); 607 ScRangeList& rDstRangeList = pOldFormat->GetRangeList(); 608 for (size_t i = 0; i < rNewRangeList.size(); ++i) 609 { 610 rDstRangeList.Join(rNewRangeList[i]); 611 } 612 pDocument->AddCondFormatData(rNewRangeList, nTab, pOldFormat->GetKey()); 613 return true; 614 } 615 616 return false; 617 } 618 619 } 620 621 void ScTable::CopyConditionalFormat( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, 622 SCCOL nDx, SCROW nDy, const ScTable* pTable) 623 { 624 ScRange aOldRange( nCol1 - nDx, nRow1 - nDy, pTable->nTab, nCol2 - nDx, nRow2 - nDy, pTable->nTab); 625 ScRange aNewRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab ); 626 bool bSameDoc = pDocument->GetStyleSheetPool() == pTable->pDocument->GetStyleSheetPool(); 627 628 for(const auto& rxCondFormat : *pTable->mpCondFormatList) 629 { 630 const ScRangeList& rCondFormatRange = rxCondFormat->GetRange(); 631 if(!rCondFormatRange.Intersects( aOldRange )) 632 continue; 633 634 ScRangeList aIntersectedRange = rCondFormatRange.GetIntersectedRange(aOldRange); 635 std::unique_ptr<ScConditionalFormat> pNewFormat = rxCondFormat->Clone(pDocument); 636 637 pNewFormat->SetRange(aIntersectedRange); 638 sc::RefUpdateContext aRefCxt(*pDocument); 639 aRefCxt.meMode = URM_COPY; 640 aRefCxt.maRange = aNewRange; 641 aRefCxt.mnColDelta = nDx; 642 aRefCxt.mnRowDelta = nDy; 643 aRefCxt.mnTabDelta = nTab - pTable->nTab; 644 pNewFormat->UpdateReference(aRefCxt, true); 645 646 if (bSameDoc && pTable->nTab == nTab && CheckAndDeduplicateCondFormat(pDocument, mpCondFormatList->GetFormat(rxCondFormat->GetKey()), pNewFormat.get(), nTab)) 647 { 648 continue; 649 } 650 sal_uLong nMax = 0; 651 bool bDuplicate = false; 652 for(const auto& rxCond : *mpCondFormatList) 653 { 654 // Check if there is the same format in the destination 655 // If there is, then simply expand its range 656 if (CheckAndDeduplicateCondFormat(pDocument, rxCond.get(), pNewFormat.get(), nTab)) 657 { 658 bDuplicate = true; 659 break; 660 } 661 662 if (rxCond->GetKey() > nMax) 663 nMax = rxCond->GetKey(); 664 } 665 // Do not add duplicate entries 666 if (bDuplicate) 667 { 668 continue; 669 } 670 671 pNewFormat->SetKey(nMax + 1); 672 auto pNewFormatTmp = pNewFormat.get(); 673 mpCondFormatList->InsertNew(std::move(pNewFormat)); 674 675 if(!bSameDoc) 676 { 677 for(size_t i = 0, n = pNewFormatTmp->size(); 678 i < n; ++i) 679 { 680 OUString aStyleName; 681 const ScFormatEntry* pEntry = pNewFormatTmp->GetEntry(i); 682 if(pEntry->GetType() == ScFormatEntry::Type::Condition || 683 pEntry->GetType() == ScFormatEntry::Type::ExtCondition) 684 aStyleName = static_cast<const ScCondFormatEntry*>(pEntry)->GetStyle(); 685 else if(pEntry->GetType() == ScFormatEntry::Type::Date) 686 aStyleName = static_cast<const ScCondDateFormatEntry*>(pEntry)->GetStyleName(); 687 688 if(!aStyleName.isEmpty()) 689 { 690 if(pDocument->GetStyleSheetPool()->Find(aStyleName, SfxStyleFamily::Para)) 691 continue; 692 693 pDocument->GetStyleSheetPool()->CopyStyleFrom( 694 pTable->pDocument->GetStyleSheetPool(), aStyleName, SfxStyleFamily::Para ); 695 } 696 } 697 } 698 699 pDocument->AddCondFormatData( pNewFormatTmp->GetRange(), nTab, pNewFormatTmp->GetKey() ); 700 } 701 } 702 703 bool ScTable::InitColumnBlockPosition( sc::ColumnBlockPosition& rBlockPos, SCCOL nCol ) 704 { 705 if (!ValidCol(nCol)) 706 return false; 707 708 CreateColumnIfNotExists(nCol).InitBlockPosition(rBlockPos); 709 return true; 710 } 711 712 // pTable is source 713 714 void ScTable::CopyFromClip( 715 sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, 716 SCCOL nDx, SCROW nDy, ScTable* pTable ) 717 { 718 if (nCol2 > pDocument->MaxCol()) 719 nCol2 = pDocument->MaxCol(); 720 if (nRow2 > pDocument->MaxRow()) 721 nRow2 = pDocument->MaxRow(); 722 723 if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2)) 724 { 725 CreateColumnIfNotExists(nCol2); 726 for ( SCCOL i = nCol1; i <= nCol2; i++) 727 { 728 pTable->CreateColumnIfNotExists(i - nDx); 729 aCol[i].CopyFromClip(rCxt, nRow1, nRow2, nDy, pTable->aCol[i - nDx]); // notes are handles at column level 730 } 731 732 if (rCxt.getInsertFlag() & InsertDeleteFlags::ATTRIB) 733 { 734 // make sure that there are no old references to the cond formats 735 sal_uInt16 nWhichArray[2]; 736 nWhichArray[0] = ATTR_CONDITIONAL; 737 nWhichArray[1] = 0; 738 for ( SCCOL i = nCol1; i <= nCol2; ++i) 739 aCol[i].ClearItems(nRow1, nRow2, nWhichArray); 740 } 741 742 if ((rCxt.getInsertFlag() & InsertDeleteFlags::ATTRIB) != InsertDeleteFlags::NONE) 743 { 744 if (nRow1==0 && nRow2==pDocument->MaxRow() && mpColWidth && pTable->mpColWidth) 745 mpColWidth->CopyFrom(*pTable->mpColWidth, nCol1, nCol2, nCol1 - nDx); 746 747 if (nCol1==0 && nCol2==pDocument->MaxCol() && mpRowHeights && pTable->mpRowHeights && 748 pRowFlags && pTable->pRowFlags) 749 { 750 CopyRowHeight(*pTable, nRow1, nRow2, -nDy); 751 // Must copy CRFlags::ManualSize bit too, otherwise pRowHeight doesn't make sense 752 for (SCROW j=nRow1; j<=nRow2; j++) 753 { 754 if ( pTable->pRowFlags->GetValue(j-nDy) & CRFlags::ManualSize ) 755 pRowFlags->OrValue( j, CRFlags::ManualSize); 756 else 757 pRowFlags->AndValue( j, ~CRFlags::ManualSize); 758 } 759 } 760 761 // Do not set protected cell in a protected sheet 762 if (IsProtected() && (rCxt.getInsertFlag() & InsertDeleteFlags::ATTRIB)) 763 { 764 ScPatternAttr aPattern(pDocument->GetPool()); 765 aPattern.GetItemSet().Put( ScProtectionAttr( false ) ); 766 ApplyPatternArea( nCol1, nRow1, nCol2, nRow2, aPattern ); 767 } 768 769 // create deep copies for conditional formatting 770 CopyConditionalFormat( nCol1, nRow1, nCol2, nRow2, nDx, nDy, pTable); 771 } 772 } 773 } 774 775 void ScTable::MixData( 776 sc::MixDocContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, 777 ScPasteFunc nFunction, bool bSkipEmpty, const ScTable* pSrcTab ) 778 { 779 for (SCCOL i=nCol1; i<=nCol2; i++) 780 aCol[i].MixData(rCxt, nRow1, nRow2, nFunction, bSkipEmpty, pSrcTab->aCol[i]); 781 } 782 783 // Selection form this document 784 void ScTable::MixMarked( 785 sc::MixDocContext& rCxt, const ScMarkData& rMark, ScPasteFunc nFunction, 786 bool bSkipEmpty, const ScTable* pSrcTab ) 787 { 788 for (SCCOL i=0; i < aCol.size(); i++) 789 aCol[i].MixMarked(rCxt, rMark, nFunction, bSkipEmpty, pSrcTab->aCol[i]); 790 } 791 792 namespace { 793 794 class TransClipHandler 795 { 796 ScTable& mrClipTab; 797 SCTAB mnSrcTab; 798 SCCOL mnSrcCol; 799 size_t mnTopRow; 800 SCROW mnTransRow; 801 bool mbAsLink; 802 bool mbWasCut; 803 804 ScAddress getDestPos(size_t nRow) const 805 { 806 return ScAddress(static_cast<SCCOL>(nRow-mnTopRow), mnTransRow, mrClipTab.GetTab()); 807 } 808 809 ScFormulaCell* createRefCell(size_t nSrcRow, const ScAddress& rDestPos) const 810 { 811 ScAddress aSrcPos(mnSrcCol, nSrcRow, mnSrcTab); 812 ScSingleRefData aRef; 813 aRef.InitAddress(aSrcPos); // Absolute reference. 814 aRef.SetFlag3D(true); 815 816 ScTokenArray aArr(&mrClipTab.GetDoc()); 817 aArr.AddSingleReference(aRef); 818 return new ScFormulaCell(&mrClipTab.GetDoc(), rDestPos, aArr); 819 } 820 821 void setLink(size_t nRow) 822 { 823 SCCOL nTransCol = nRow - mnTopRow; 824 mrClipTab.SetFormulaCell( 825 nTransCol, mnTransRow, createRefCell(nRow, getDestPos(nRow))); 826 } 827 828 public: 829 TransClipHandler(ScTable& rClipTab, SCTAB nSrcTab, SCCOL nSrcCol, size_t nTopRow, SCROW nTransRow, bool bAsLink, bool bWasCut) : 830 mrClipTab(rClipTab), mnSrcTab(nSrcTab), mnSrcCol(nSrcCol), 831 mnTopRow(nTopRow), mnTransRow(nTransRow), mbAsLink(bAsLink), mbWasCut(bWasCut) {} 832 833 void operator() (size_t nRow, double fVal) 834 { 835 if (mbAsLink) 836 { 837 setLink(nRow); 838 return; 839 } 840 841 SCCOL nTransCol = nRow - mnTopRow; 842 mrClipTab.SetValue(nTransCol, mnTransRow, fVal); 843 } 844 845 void operator() (size_t nRow, const svl::SharedString& rStr) 846 { 847 if (mbAsLink) 848 { 849 setLink(nRow); 850 return; 851 } 852 853 SCCOL nTransCol = nRow - mnTopRow; 854 mrClipTab.SetRawString(nTransCol, mnTransRow, rStr); 855 } 856 857 void operator() (size_t nRow, const EditTextObject* p) 858 { 859 if (mbAsLink) 860 { 861 setLink(nRow); 862 return; 863 } 864 865 SCCOL nTransCol = nRow - mnTopRow; 866 mrClipTab.SetEditText(nTransCol, mnTransRow, ScEditUtil::Clone(*p, mrClipTab.GetDoc())); 867 } 868 869 void operator() (size_t nRow, const ScFormulaCell* p) 870 { 871 if (mbAsLink) 872 { 873 setLink(nRow); 874 return; 875 } 876 877 ScFormulaCell* pNew = new ScFormulaCell( 878 *p, mrClipTab.GetDoc(), getDestPos(nRow), ScCloneFlags::StartListening); 879 880 // rotate reference 881 // for Cut, the references are later adjusted through UpdateTranspose 882 883 if (!mbWasCut) 884 pNew->TransposeReference(); 885 886 SCCOL nTransCol = nRow - mnTopRow; 887 mrClipTab.SetFormulaCell(nTransCol, mnTransRow, pNew); 888 } 889 }; 890 891 } 892 893 void ScTable::TransposeClip( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, 894 ScTable* pTransClip, InsertDeleteFlags nFlags, bool bAsLink ) 895 { 896 bool bWasCut = pDocument->IsCutMode(); 897 898 ScDocument* pDestDoc = pTransClip->pDocument; 899 900 for (SCCOL nCol=nCol1; nCol<=nCol2; nCol++) 901 { 902 SCROW nRow; 903 if ( bAsLink && nFlags == InsertDeleteFlags::ALL ) 904 { 905 // with InsertDeleteFlags::ALL, also create links (formulas) for empty cells 906 907 for ( nRow=nRow1; nRow<=nRow2; nRow++ ) 908 { 909 // create simple formula, as in ScColumn::CreateRefCell 910 911 ScAddress aDestPos( static_cast<SCCOL>(nRow-nRow1), static_cast<SCROW>(nCol-nCol1), pTransClip->nTab ); 912 ScSingleRefData aRef; 913 aRef.InitAddress(ScAddress(nCol,nRow,nTab)); 914 aRef.SetFlag3D(true); 915 ScTokenArray aArr(pDestDoc); 916 aArr.AddSingleReference( aRef ); 917 918 pTransClip->SetFormulaCell( 919 static_cast<SCCOL>(nRow-nRow1), static_cast<SCROW>(nCol-nCol1), 920 new ScFormulaCell(pDestDoc, aDestPos, aArr)); 921 } 922 } 923 else 924 { 925 TransClipHandler aFunc(*pTransClip, nTab, nCol, nRow1, static_cast<SCROW>(nCol-nCol1), bAsLink, bWasCut); 926 const sc::CellStoreType& rCells = aCol[nCol].maCells; 927 sc::ParseAllNonEmpty(rCells.begin(), rCells, nRow1, nRow2, aFunc); 928 } 929 930 // Attribute 931 932 SCROW nAttrRow1 = {}; // spurious -Werror=maybe-uninitialized 933 SCROW nAttrRow2 = {}; // spurious -Werror=maybe-uninitialized 934 const ScPatternAttr* pPattern; 935 std::unique_ptr<ScAttrIterator> pAttrIter(aCol[nCol].CreateAttrIterator( nRow1, nRow2 )); 936 while ( (pPattern = pAttrIter->Next( nAttrRow1, nAttrRow2 )) != nullptr ) 937 { 938 if ( !IsDefaultItem( pPattern ) ) 939 { 940 const SfxItemSet& rSet = pPattern->GetItemSet(); 941 if ( rSet.GetItemState( ATTR_MERGE, false ) == SfxItemState::DEFAULT && 942 rSet.GetItemState( ATTR_MERGE_FLAG, false ) == SfxItemState::DEFAULT && 943 rSet.GetItemState( ATTR_BORDER, false ) == SfxItemState::DEFAULT ) 944 { 945 // no borders or merge items involved - use pattern as-is 946 for (nRow = nAttrRow1; nRow<=nAttrRow2; nRow++) 947 pTransClip->SetPattern( static_cast<SCCOL>(nRow-nRow1), static_cast<SCROW>(nCol-nCol1), *pPattern ); 948 } 949 else 950 { 951 // transpose borders and merge values, remove merge flags (refreshed after pasting) 952 ScPatternAttr aNewPattern( *pPattern ); 953 SfxItemSet& rNewSet = aNewPattern.GetItemSet(); 954 955 const SvxBoxItem& rOldBox = rSet.Get(ATTR_BORDER); 956 if ( rOldBox.GetTop() || rOldBox.GetBottom() || rOldBox.GetLeft() || rOldBox.GetRight() ) 957 { 958 SvxBoxItem aNew( ATTR_BORDER ); 959 aNew.SetLine( rOldBox.GetLine( SvxBoxItemLine::TOP ), SvxBoxItemLine::LEFT ); 960 aNew.SetLine( rOldBox.GetLine( SvxBoxItemLine::LEFT ), SvxBoxItemLine::TOP ); 961 aNew.SetLine( rOldBox.GetLine( SvxBoxItemLine::BOTTOM ), SvxBoxItemLine::RIGHT ); 962 aNew.SetLine( rOldBox.GetLine( SvxBoxItemLine::RIGHT ), SvxBoxItemLine::BOTTOM ); 963 aNew.SetDistance( rOldBox.GetDistance( SvxBoxItemLine::TOP ), SvxBoxItemLine::LEFT ); 964 aNew.SetDistance( rOldBox.GetDistance( SvxBoxItemLine::LEFT ), SvxBoxItemLine::TOP ); 965 aNew.SetDistance( rOldBox.GetDistance( SvxBoxItemLine::BOTTOM ), SvxBoxItemLine::RIGHT ); 966 aNew.SetDistance( rOldBox.GetDistance( SvxBoxItemLine::RIGHT ), SvxBoxItemLine::BOTTOM ); 967 rNewSet.Put( aNew ); 968 } 969 970 const ScMergeAttr& rOldMerge = rSet.Get(ATTR_MERGE); 971 if (rOldMerge.IsMerged()) 972 rNewSet.Put( ScMergeAttr( std::min( 973 static_cast<SCCOL>(rOldMerge.GetRowMerge()), 974 static_cast<SCCOL>(pDocument->MaxCol()+1 - (nAttrRow2-nRow1))), 975 std::min( 976 static_cast<SCROW>(rOldMerge.GetColMerge()), 977 static_cast<SCROW>(pDocument->MaxRow()+1 - (nCol-nCol1))))); 978 const ScMergeFlagAttr& rOldFlag = rSet.Get(ATTR_MERGE_FLAG); 979 if (rOldFlag.IsOverlapped()) 980 { 981 ScMF nNewFlags = rOldFlag.GetValue() & ~ScMF( ScMF::Hor | ScMF::Ver ); 982 if ( nNewFlags != ScMF::NONE ) 983 rNewSet.Put( ScMergeFlagAttr( nNewFlags ) ); 984 else 985 rNewSet.ClearItem( ATTR_MERGE_FLAG ); 986 } 987 988 for (nRow = nAttrRow1; nRow<=nAttrRow2; nRow++) 989 pTransClip->SetPattern( static_cast<SCCOL>(nRow-nRow1), 990 static_cast<SCROW>(nCol-nCol1), aNewPattern); 991 } 992 } 993 } 994 995 // Cell Notes - fdo#68381 paste cell notes on Transpose 996 if ( pDocument->HasColNotes(nCol, nTab) ) 997 TransposeColNotes(pTransClip, nCol1, nCol, nRow1, nRow2); 998 } 999 } 1000 1001 void ScTable::TransposeColNotes(ScTable* pTransClip, SCCOL nCol1, SCCOL nCol, SCROW nRow1, SCROW nRow2) 1002 { 1003 sc::CellNoteStoreType::const_iterator itBlk = aCol[nCol].maCellNotes.begin(), itBlkEnd = aCol[nCol].maCellNotes.end(); 1004 1005 // Locate the top row position. 1006 size_t nOffsetInBlock = 0; 1007 size_t nBlockStart = 0, nBlockEnd = 0, nRowPos = static_cast<size_t>(nRow1); 1008 for (; itBlk != itBlkEnd; ++itBlk, nBlockStart = nBlockEnd) 1009 { 1010 nBlockEnd = nBlockStart + itBlk->size; 1011 if (nBlockStart <= nRowPos && nRowPos < nBlockEnd) 1012 { 1013 // Found. 1014 nOffsetInBlock = nRowPos - nBlockStart; 1015 break; 1016 } 1017 } 1018 1019 if (itBlk != itBlkEnd) 1020 // Specified range found 1021 { 1022 nRowPos = static_cast<size_t>(nRow2); // End row position. 1023 1024 // Keep processing until we hit the end row position. 1025 sc::cellnote_block::const_iterator itData, itDataEnd; 1026 for (; itBlk != itBlkEnd; ++itBlk, nBlockStart = nBlockEnd, nOffsetInBlock = 0) 1027 { 1028 nBlockEnd = nBlockStart + itBlk->size; 1029 1030 if (itBlk->data) 1031 { 1032 itData = sc::cellnote_block::begin(*itBlk->data); 1033 std::advance(itData, nOffsetInBlock); 1034 1035 if (nBlockStart <= nRowPos && nRowPos < nBlockEnd) 1036 { 1037 // This block contains the end row. Only process partially. 1038 size_t nOffsetEnd = nRowPos - nBlockStart + 1; 1039 itDataEnd = sc::cellnote_block::begin(*itBlk->data); 1040 std::advance(itDataEnd, nOffsetEnd); 1041 size_t curRow = nBlockStart + nOffsetInBlock; 1042 for (; itData != itDataEnd; ++itData, ++curRow) 1043 { 1044 ScAddress aDestPos( static_cast<SCCOL>(curRow-nRow1), static_cast<SCROW>(nCol-nCol1), pTransClip->nTab ); 1045 pTransClip->pDocument->ReleaseNote(aDestPos); 1046 ScPostIt* pNote = *itData; 1047 if (pNote) 1048 { 1049 std::unique_ptr<ScPostIt> pClonedNote = pNote->Clone( ScAddress(nCol, curRow, nTab), *pTransClip->pDocument, aDestPos, true ); 1050 pTransClip->pDocument->SetNote(aDestPos, std::move(pClonedNote)); 1051 } 1052 } 1053 break; // we reached the last valid block 1054 } 1055 else 1056 { 1057 itDataEnd = sc::cellnote_block::end(*itBlk->data); 1058 size_t curRow = nBlockStart + nOffsetInBlock; 1059 for (; itData != itDataEnd; ++itData, ++curRow) 1060 { 1061 ScAddress aDestPos( static_cast<SCCOL>(curRow-nRow1), static_cast<SCROW>(nCol-nCol1), pTransClip->nTab ); 1062 pTransClip->pDocument->ReleaseNote(aDestPos); 1063 ScPostIt* pNote = *itData; 1064 if (pNote) 1065 { 1066 std::unique_ptr<ScPostIt> pClonedNote = pNote->Clone( ScAddress(nCol, curRow, nTab), *pTransClip->pDocument, aDestPos, true ); 1067 pTransClip->pDocument->SetNote(aDestPos, std::move(pClonedNote)); 1068 } 1069 } 1070 } 1071 } 1072 else 1073 { 1074 size_t curRow; 1075 for ( curRow = nBlockStart + nOffsetInBlock; curRow <= nBlockEnd && curRow <= nRowPos; ++curRow) 1076 { 1077 ScAddress aDestPos( static_cast<SCCOL>(curRow-nRow1), static_cast<SCROW>(nCol-nCol1), pTransClip->nTab ); 1078 pTransClip->pDocument->ReleaseNote(aDestPos); 1079 } 1080 if (curRow == nRowPos) 1081 break; 1082 } 1083 } 1084 } 1085 } 1086 1087 ScColumn* ScTable::FetchColumn( SCCOL nCol ) 1088 { 1089 if (!ValidCol(nCol)) 1090 return nullptr; 1091 1092 return &CreateColumnIfNotExists(nCol); 1093 } 1094 1095 const ScColumn* ScTable::FetchColumn( SCCOL nCol ) const 1096 { 1097 if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount()) 1098 return nullptr; 1099 1100 return &aCol[nCol]; 1101 } 1102 1103 void ScTable::StartListeners( sc::StartListeningContext& rCxt, bool bAll ) 1104 { 1105 std::shared_ptr<const sc::ColumnSet> pColSet = rCxt.getColumnSet(); 1106 if (!pColSet) 1107 { 1108 for (SCCOL i=0; i < aCol.size(); i++) 1109 aCol[i].StartListeners(rCxt, bAll); 1110 } 1111 else if (pColSet->hasTab( nTab)) 1112 { 1113 std::vector<SCCOL> aColumns; 1114 pColSet->getColumns( nTab, aColumns); 1115 for (auto i : aColumns) 1116 { 1117 if (0 <= i && i < aCol.size()) 1118 aCol[i].StartListeners(rCxt, bAll); 1119 } 1120 } 1121 } 1122 1123 void ScTable::AttachFormulaCells( 1124 sc::StartListeningContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) 1125 { 1126 nCol2 = ClampToAllocatedColumns(nCol2); 1127 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) 1128 aCol[nCol].AttachFormulaCells(rCxt, nRow1, nRow2); 1129 } 1130 1131 void ScTable::DetachFormulaCells( 1132 sc::EndListeningContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) 1133 { 1134 nCol2 = ClampToAllocatedColumns(nCol2); 1135 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) 1136 aCol[nCol].DetachFormulaCells(rCxt, nRow1, nRow2, nullptr); 1137 } 1138 1139 void ScTable::SetDirtyFromClip( 1140 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, sc::ColumnSpanSet& rBroadcastSpans ) 1141 { 1142 if ( nCol2 >= aCol.size() ) nCol2 = aCol.size() - 1; 1143 if (nCol2 > pDocument->MaxCol()) nCol2 = pDocument->MaxCol(); 1144 if (nRow2 > pDocument->MaxRow()) nRow2 = pDocument->MaxRow(); 1145 if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2)) 1146 for (SCCOL i = nCol1; i <= nCol2; i++) 1147 aCol[i].SetDirtyFromClip(nRow1, nRow2, rBroadcastSpans); 1148 } 1149 1150 void ScTable::StartListeningFormulaCells( 1151 sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt, 1152 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) 1153 { 1154 if ( nCol2 >= aCol.size() ) nCol2 = aCol.size() - 1; 1155 if (nCol2 > pDocument->MaxCol()) nCol2 = pDocument->MaxCol(); 1156 if (nRow2 > pDocument->MaxRow()) nRow2 = pDocument->MaxRow(); 1157 if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2)) 1158 for (SCCOL i = nCol1; i <= nCol2; i++) 1159 aCol[i].StartListeningFormulaCells(rStartCxt, rEndCxt, nRow1, nRow2); 1160 } 1161 1162 void ScTable::CopyToTable( 1163 sc::CopyToDocContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, 1164 InsertDeleteFlags nFlags, bool bMarked, ScTable* pDestTab, const ScMarkData* pMarkData, 1165 bool bAsLink, bool bColRowFlags, bool bGlobalNamesToLocal, bool bCopyCaptions ) 1166 { 1167 if (!ValidColRow(nCol1, nRow1) || !ValidColRow(nCol2, nRow2)) 1168 return; 1169 1170 bool bIsUndoDoc = pDestTab->pDocument->IsUndo(); 1171 1172 if (bIsUndoDoc && (nFlags & InsertDeleteFlags::CONTENTS)) 1173 { 1174 // Copying formulas may create sheet-local named expressions on the 1175 // destination sheet. Add existing to Undo first. 1176 pDestTab->SetRangeName( std::unique_ptr<ScRangeName>( new ScRangeName( *GetRangeName()))); 1177 } 1178 1179 if (nFlags != InsertDeleteFlags::NONE) 1180 { 1181 InsertDeleteFlags nTempFlags( nFlags & 1182 ~InsertDeleteFlags( InsertDeleteFlags::NOTE | InsertDeleteFlags::ADDNOTES)); 1183 // tdf#102364 - in some pathological cases CopyToTable() replacing cells with new cells 1184 // can lead to repetitive splitting and rejoining of the same formula group, which can get 1185 // quadratically expensive with large groups. So do the grouping just once at the end. 1186 sc::DelayFormulaGroupingSwitch delayGrouping( *pDestTab->pDocument, true ); 1187 for (SCCOL i = nCol1; i <= ClampToAllocatedColumns(nCol2); i++) 1188 aCol[i].CopyToColumn(rCxt, nRow1, nRow2, bIsUndoDoc ? nFlags : nTempFlags, bMarked, 1189 pDestTab->CreateColumnIfNotExists(i), pMarkData, bAsLink, bGlobalNamesToLocal); 1190 } 1191 1192 if (!bColRowFlags) // Column widths/Row heights/Flags 1193 return; 1194 1195 if(bIsUndoDoc && (nFlags & InsertDeleteFlags::ATTRIB)) 1196 { 1197 pDestTab->mpCondFormatList.reset(new ScConditionalFormatList(pDestTab->pDocument, *mpCondFormatList)); 1198 } 1199 1200 if (pDBDataNoName) 1201 { 1202 std::unique_ptr<ScDBData> pNewDBData(new ScDBData(*pDBDataNoName)); 1203 SCCOL aCol1, aCol2; 1204 SCROW aRow1, aRow2; 1205 SCTAB aTab; 1206 pNewDBData->GetArea(aTab, aCol1, aRow1, aCol2, aRow2); 1207 pNewDBData->MoveTo(pDestTab->nTab, aCol1, aRow1, aCol2, aRow2); 1208 pDestTab->SetAnonymousDBData(std::move(pNewDBData)); 1209 } 1210 // Charts have to be adjusted when hide/show 1211 ScChartListenerCollection* pCharts = pDestTab->pDocument->GetChartListenerCollection(); 1212 1213 bool bFlagChange = false; 1214 1215 bool bWidth = (nRow1==0 && nRow2==pDocument->MaxRow() && mpColWidth && pDestTab->mpColWidth); 1216 bool bHeight = (nCol1==0 && nCol2==pDocument->MaxCol() && mpRowHeights && pDestTab->mpRowHeights); 1217 1218 if (bWidth || bHeight) 1219 { 1220 if (bWidth) 1221 { 1222 auto destTabColWidthIt = pDestTab->mpColWidth->begin() + nCol1; 1223 auto thisTabColWidthIt = mpColWidth->begin() + nCol1; 1224 pDestTab->mpColWidth->CopyFrom(*mpColWidth, nCol1, nCol2); 1225 pDestTab->mpColFlags->CopyFrom(*mpColFlags, nCol1, nCol2); 1226 for (SCCOL i = nCol1; i <= nCol2; ++i) 1227 { 1228 bool bThisHidden = ColHidden(i); 1229 bool bHiddenChange = (pDestTab->ColHidden(i) != bThisHidden); 1230 bool bChange = bHiddenChange || (*destTabColWidthIt != *thisTabColWidthIt); 1231 pDestTab->SetColHidden(i, i, bThisHidden); 1232 //TODO: collect changes? 1233 if (bHiddenChange && pCharts) 1234 pCharts->SetRangeDirty(ScRange( i, 0, nTab, i, pDocument->MaxRow(), nTab )); 1235 1236 if (bChange) 1237 bFlagChange = true; 1238 1239 ++destTabColWidthIt; 1240 ++thisTabColWidthIt; 1241 } 1242 pDestTab->SetColManualBreaks( maColManualBreaks); 1243 } 1244 1245 if (bHeight) 1246 { 1247 bool bChange = pDestTab->GetRowHeight(nRow1, nRow2) != GetRowHeight(nRow1, nRow2); 1248 1249 if (bChange) 1250 bFlagChange = true; 1251 1252 pDestTab->CopyRowHeight(*this, nRow1, nRow2, 0); 1253 pDestTab->pRowFlags->CopyFrom(*pRowFlags, nRow1, nRow2); 1254 1255 // Hidden flags. 1256 for (SCROW i = nRow1; i <= nRow2; ++i) 1257 { 1258 SCROW nLastRow; 1259 bool bHidden = RowHidden(i, nullptr, &nLastRow); 1260 if (nLastRow >= nRow2) 1261 // the last row shouldn't exceed the upper bound the caller specified. 1262 nLastRow = nRow2; 1263 1264 bool bHiddenChanged = pDestTab->SetRowHidden(i, nLastRow, bHidden); 1265 if (bHiddenChanged && pCharts) 1266 // Hidden flags differ. 1267 pCharts->SetRangeDirty(ScRange(0, i, nTab, pDocument->MaxCol(), nLastRow, nTab)); 1268 1269 if (bHiddenChanged) 1270 bFlagChange = true; 1271 1272 // Jump to the last row of the identical flag segment. 1273 i = nLastRow; 1274 } 1275 1276 // Filtered flags. 1277 for (SCROW i = nRow1; i <= nRow2; ++i) 1278 { 1279 SCROW nLastRow; 1280 bool bFiltered = RowFiltered(i, nullptr, &nLastRow); 1281 if (nLastRow >= nRow2) 1282 // the last row shouldn't exceed the upper bound the caller specified. 1283 nLastRow = nRow2; 1284 pDestTab->SetRowFiltered(i, nLastRow, bFiltered); 1285 i = nLastRow; 1286 } 1287 pDestTab->SetRowManualBreaks( maRowManualBreaks); 1288 } 1289 } 1290 1291 if (bFlagChange) 1292 pDestTab->InvalidatePageBreaks(); 1293 1294 if(nFlags & InsertDeleteFlags::ATTRIB) 1295 { 1296 pDestTab->mpCondFormatList->DeleteArea(nCol1, nRow1, nCol2, nRow2); 1297 pDestTab->CopyConditionalFormat(nCol1, nRow1, nCol2, nRow2, 0, 0, this); 1298 } 1299 1300 if(nFlags & InsertDeleteFlags::OUTLINE) // also only when bColRowFlags 1301 pDestTab->SetOutlineTable( pOutlineTable.get() ); 1302 1303 if (!bIsUndoDoc && bCopyCaptions && (nFlags & (InsertDeleteFlags::NOTE | InsertDeleteFlags::ADDNOTES))) 1304 { 1305 bool bCloneCaption = (nFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE; 1306 CopyCaptionsToTable( nCol1, nRow1, nCol2, nRow2, pDestTab, bCloneCaption); 1307 } 1308 } 1309 1310 void ScTable::CopyCaptionsToTable( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScTable* pDestTab, 1311 bool bCloneCaption ) 1312 { 1313 if (!ValidColRow(nCol1, nRow1) || !ValidColRow(nCol2, nRow2)) 1314 return; 1315 1316 nCol2 = ClampToAllocatedColumns(nCol2); 1317 for (SCCOL i = nCol1; i <= nCol2; i++) 1318 { 1319 aCol[i].CopyCellNotesToDocument(nRow1, nRow2, pDestTab->CreateColumnIfNotExists(i), bCloneCaption); 1320 pDestTab->aCol[i].UpdateNoteCaptions(nRow1, nRow2); 1321 } 1322 } 1323 1324 void ScTable::UndoToTable( 1325 sc::CopyToDocContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, 1326 InsertDeleteFlags nFlags, bool bMarked, ScTable* pDestTab ) 1327 { 1328 if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2)) 1329 { 1330 bool bWidth = (nRow1==0 && nRow2==pDocument->MaxRow() && mpColWidth && pDestTab->mpColWidth); 1331 bool bHeight = (nCol1==0 && nCol2==pDocument->MaxCol() && mpRowHeights && pDestTab->mpRowHeights); 1332 1333 if ((nFlags & InsertDeleteFlags::CONTENTS) && mpRangeName) 1334 { 1335 // Undo sheet-local named expressions created during copying 1336 // formulas. If mpRangeName is not set then the Undo wasn't even 1337 // set to an empty ScRangeName map so don't "undo" that. 1338 pDestTab->SetRangeName( std::unique_ptr<ScRangeName>( new ScRangeName( *GetRangeName()))); 1339 if (!pDestTab->pDocument->IsClipOrUndo()) 1340 { 1341 ScDocShell* pDocSh = static_cast<ScDocShell*>(pDestTab->pDocument->GetDocumentShell()); 1342 if (pDocSh) 1343 pDocSh->SetAreasChangedNeedBroadcast(); 1344 } 1345 1346 } 1347 1348 for ( SCCOL i = 0; i < aCol.size(); i++) 1349 { 1350 if ( i >= nCol1 && i <= nCol2 ) 1351 aCol[i].UndoToColumn(rCxt, nRow1, nRow2, nFlags, bMarked, pDestTab->aCol[i]); 1352 else 1353 aCol[i].CopyToColumn(rCxt, 0, pDocument->MaxRow(), InsertDeleteFlags::FORMULA, false, pDestTab->aCol[i]); 1354 } 1355 1356 if (nFlags & InsertDeleteFlags::ATTRIB) 1357 pDestTab->mpCondFormatList.reset(new ScConditionalFormatList(pDestTab->pDocument, *mpCondFormatList)); 1358 1359 if (bWidth||bHeight) 1360 { 1361 if (bWidth) 1362 { 1363 pDestTab->mpColWidth->CopyFrom(*mpColWidth, nCol1, nCol2); 1364 pDestTab->SetColManualBreaks( maColManualBreaks); 1365 } 1366 if (bHeight) 1367 { 1368 pDestTab->CopyRowHeight(*this, nRow1, nRow2, 0); 1369 pDestTab->SetRowManualBreaks( maRowManualBreaks); 1370 } 1371 } 1372 } 1373 } 1374 1375 void ScTable::CopyUpdated( const ScTable* pPosTab, ScTable* pDestTab ) const 1376 { 1377 pPosTab->CreateColumnIfNotExists(aCol.size()-1); 1378 pDestTab->CreateColumnIfNotExists(aCol.size()-1); 1379 for (SCCOL i=0; i < aCol.size(); i++) 1380 aCol[i].CopyUpdated( pPosTab->aCol[i], pDestTab->aCol[i] ); 1381 } 1382 1383 void ScTable::InvalidateTableArea() 1384 { 1385 bTableAreaValid = false; 1386 } 1387 1388 void ScTable::InvalidatePageBreaks() 1389 { 1390 mbPageBreaksValid = false; 1391 } 1392 1393 void ScTable::CopyScenarioTo( const ScTable* pDestTab ) const 1394 { 1395 OSL_ENSURE( bScenario, "bScenario == FALSE" ); 1396 1397 for (SCCOL i=0; i < aCol.size(); i++) 1398 aCol[i].CopyScenarioTo( pDestTab->CreateColumnIfNotExists(i) ); 1399 } 1400 1401 void ScTable::CopyScenarioFrom( const ScTable* pSrcTab ) 1402 { 1403 OSL_ENSURE( bScenario, "bScenario == FALSE" ); 1404 1405 SCCOL nEndCol = pSrcTab->aCol.size(); 1406 CreateColumnIfNotExists(nEndCol); 1407 for (SCCOL i=0; i < nEndCol; i++) 1408 aCol[i].CopyScenarioFrom( pSrcTab->aCol[i] ); 1409 } 1410 1411 void ScTable::MarkScenarioIn( ScMarkData& rDestMark, ScScenarioFlags nNeededBits ) const 1412 { 1413 OSL_ENSURE( bScenario, "bScenario == FALSE" ); 1414 1415 if ( ( nScenarioFlags & nNeededBits ) != nNeededBits ) // Are all Bits set? 1416 return; 1417 1418 for (SCCOL i=0; i < aCol.size(); i++) 1419 aCol[i].MarkScenarioIn( rDestMark ); 1420 } 1421 1422 bool ScTable::HasScenarioRange( const ScRange& rRange ) const 1423 { 1424 OSL_ENSURE( bScenario, "bScenario == FALSE" ); 1425 1426 ScRange aTabRange = rRange; 1427 aTabRange.aStart.SetTab( nTab ); 1428 aTabRange.aEnd.SetTab( nTab ); 1429 1430 const ScRangeList* pList = GetScenarioRanges(); 1431 1432 if (pList) 1433 { 1434 for ( size_t j = 0, n = pList->size(); j < n; j++ ) 1435 { 1436 const ScRange & rR = (*pList)[j]; 1437 if ( rR.Intersects( aTabRange ) ) 1438 return true; 1439 } 1440 } 1441 1442 return false; 1443 } 1444 1445 void ScTable::InvalidateScenarioRanges() 1446 { 1447 pScenarioRanges.reset(); 1448 } 1449 1450 const ScRangeList* ScTable::GetScenarioRanges() const 1451 { 1452 OSL_ENSURE( bScenario, "bScenario == FALSE" ); 1453 1454 if (!pScenarioRanges) 1455 { 1456 const_cast<ScTable*>(this)->pScenarioRanges.reset(new ScRangeList); 1457 ScMarkData aMark(pDocument->GetSheetLimits()); 1458 MarkScenarioIn( aMark, ScScenarioFlags::NONE ); // always 1459 aMark.FillRangeListWithMarks( pScenarioRanges.get(), false ); 1460 } 1461 return pScenarioRanges.get(); 1462 } 1463 1464 bool ScTable::TestCopyScenarioTo( const ScTable* pDestTab ) const 1465 { 1466 OSL_ENSURE( bScenario, "bScenario == FALSE" ); 1467 1468 if (!pDestTab->IsProtected()) 1469 return true; 1470 1471 bool bOk = true; 1472 for (SCCOL i=0; i < aCol.size() && bOk; i++) 1473 bOk = aCol[i].TestCopyScenarioTo( pDestTab->aCol[i] ); 1474 return bOk; 1475 } 1476 1477 bool ScTable::SetString( SCCOL nCol, SCROW nRow, SCTAB nTabP, const OUString& rString, 1478 const ScSetStringParam * pParam ) 1479 { 1480 if (!ValidColRow(nCol,nRow)) 1481 { 1482 return false; 1483 } 1484 1485 return CreateColumnIfNotExists(nCol).SetString( 1486 nRow, nTabP, rString, pDocument->GetAddressConvention(), pParam); 1487 } 1488 1489 bool ScTable::SetEditText( SCCOL nCol, SCROW nRow, std::unique_ptr<EditTextObject> pEditText ) 1490 { 1491 if (!ValidColRow(nCol, nRow)) 1492 { 1493 return false; 1494 } 1495 1496 CreateColumnIfNotExists(nCol).SetEditText(nRow, std::move(pEditText)); 1497 return true; 1498 } 1499 1500 void ScTable::SetEditText( SCCOL nCol, SCROW nRow, const EditTextObject& rEditText, const SfxItemPool* pEditPool ) 1501 { 1502 if (!ValidColRow(nCol, nRow)) 1503 return; 1504 1505 CreateColumnIfNotExists(nCol).SetEditText(nRow, rEditText, pEditPool); 1506 } 1507 1508 SCROW ScTable::GetFirstEditTextRow( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) const 1509 { 1510 if (!ValidCol(nCol1) || !ValidCol(nCol2) || nCol2 < nCol1) 1511 return -1; 1512 1513 if (!ValidRow(nRow1) || !ValidRow(nRow2) || nRow2 < nRow1) 1514 return -1; 1515 1516 nCol2 = ClampToAllocatedColumns(nCol2); 1517 SCROW nFirst = pDocument->MaxRow()+1; 1518 for (SCCOL i = nCol1; i <= nCol2; ++i) 1519 { 1520 const ScColumn& rCol = aCol[i]; 1521 SCROW nThisFirst = -1; 1522 if (const_cast<ScColumn&>(rCol).HasEditCells(nRow1, nRow2, nThisFirst)) 1523 { 1524 if (nThisFirst == nRow1) 1525 return nRow1; 1526 1527 if (nThisFirst < nFirst) 1528 nFirst = nThisFirst; 1529 } 1530 } 1531 1532 return nFirst == (pDocument->MaxRow()+1) ? -1 : nFirst; 1533 } 1534 1535 void ScTable::SetEmptyCell( SCCOL nCol, SCROW nRow ) 1536 { 1537 if (!ValidColRow(nCol, nRow) || nCol >= GetAllocatedColumnsCount()) 1538 return; 1539 1540 aCol[nCol].Delete(nRow); 1541 } 1542 1543 void ScTable::SetFormula( 1544 SCCOL nCol, SCROW nRow, const ScTokenArray& rArray, formula::FormulaGrammar::Grammar eGram ) 1545 { 1546 if (!ValidColRow(nCol, nRow)) 1547 return; 1548 1549 CreateColumnIfNotExists(nCol).SetFormula(nRow, rArray, eGram); 1550 } 1551 1552 void ScTable::SetFormula( 1553 SCCOL nCol, SCROW nRow, const OUString& rFormula, formula::FormulaGrammar::Grammar eGram ) 1554 { 1555 if (!ValidColRow(nCol, nRow)) 1556 return; 1557 1558 CreateColumnIfNotExists(nCol).SetFormula(nRow, rFormula, eGram); 1559 } 1560 1561 ScFormulaCell* ScTable::SetFormulaCell( SCCOL nCol, SCROW nRow, ScFormulaCell* pCell ) 1562 { 1563 if (!ValidColRow(nCol, nRow)) 1564 { 1565 delete pCell; 1566 return nullptr; 1567 } 1568 1569 return CreateColumnIfNotExists(nCol).SetFormulaCell(nRow, pCell, sc::ConvertToGroupListening); 1570 } 1571 1572 bool ScTable::SetFormulaCells( SCCOL nCol, SCROW nRow, std::vector<ScFormulaCell*>& rCells ) 1573 { 1574 if (!ValidCol(nCol)) 1575 return false; 1576 1577 return CreateColumnIfNotExists(nCol).SetFormulaCells(nRow, rCells); 1578 } 1579 1580 svl::SharedString ScTable::GetSharedString( SCCOL nCol, SCROW nRow ) const 1581 { 1582 if (!ValidColRow(nCol, nRow) || nCol >= GetAllocatedColumnsCount()) 1583 return svl::SharedString(); 1584 1585 return aCol[nCol].GetSharedString(nRow); 1586 } 1587 1588 void ScTable::SetValue( SCCOL nCol, SCROW nRow, const double& rVal ) 1589 { 1590 if (ValidColRow(nCol, nRow)) 1591 CreateColumnIfNotExists(nCol).SetValue(nRow, rVal); 1592 } 1593 1594 void ScTable::SetRawString( SCCOL nCol, SCROW nRow, const svl::SharedString& rStr ) 1595 { 1596 if (ValidColRow(nCol, nRow)) 1597 CreateColumnIfNotExists(nCol).SetRawString(nRow, rStr); 1598 } 1599 1600 void ScTable::GetString( SCCOL nCol, SCROW nRow, OUString& rString, const ScInterpreterContext* pContext ) const 1601 { 1602 if (ValidColRow(nCol,nRow) && nCol < GetAllocatedColumnsCount()) 1603 aCol[nCol].GetString( nRow, rString, pContext ); 1604 else 1605 rString.clear(); 1606 } 1607 1608 double* ScTable::GetValueCell( SCCOL nCol, SCROW nRow ) 1609 { 1610 if (!ValidColRow(nCol, nRow)) 1611 return nullptr; 1612 1613 return CreateColumnIfNotExists(nCol).GetValueCell(nRow); 1614 } 1615 1616 void ScTable::GetInputString( SCCOL nCol, SCROW nRow, OUString& rString ) const 1617 { 1618 if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount()) 1619 aCol[nCol].GetInputString( nRow, rString ); 1620 else 1621 rString.clear(); 1622 } 1623 1624 double ScTable::GetValue( SCCOL nCol, SCROW nRow ) const 1625 { 1626 if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount()) 1627 return aCol[nCol].GetValue( nRow ); 1628 return 0.0; 1629 } 1630 1631 const EditTextObject* ScTable::GetEditText( SCCOL nCol, SCROW nRow ) const 1632 { 1633 if (!ValidColRow(nCol, nRow) || nCol >= GetAllocatedColumnsCount()) 1634 return nullptr; 1635 1636 return aCol[nCol].GetEditText(nRow); 1637 } 1638 1639 void ScTable::RemoveEditTextCharAttribs( SCCOL nCol, SCROW nRow, const ScPatternAttr& rAttr ) 1640 { 1641 if (!ValidColRow(nCol, nRow) || nCol >= GetAllocatedColumnsCount()) 1642 return; 1643 1644 return aCol[nCol].RemoveEditTextCharAttribs(nRow, rAttr); 1645 } 1646 1647 void ScTable::GetFormula( SCCOL nCol, SCROW nRow, OUString& rFormula ) const 1648 { 1649 if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount()) 1650 aCol[nCol].GetFormula( nRow, rFormula ); 1651 else 1652 rFormula.clear(); 1653 } 1654 1655 const ScFormulaCell* ScTable::GetFormulaCell( SCCOL nCol, SCROW nRow ) const 1656 { 1657 if (!ValidColRow(nCol, nRow) || nCol >= GetAllocatedColumnsCount()) 1658 return nullptr; 1659 1660 return aCol[nCol].GetFormulaCell(nRow); 1661 } 1662 1663 ScFormulaCell* ScTable::GetFormulaCell( SCCOL nCol, SCROW nRow ) 1664 { 1665 if (!ValidColRow(nCol, nRow)) 1666 return nullptr; 1667 return CreateColumnIfNotExists(nCol).GetFormulaCell(nRow); 1668 } 1669 1670 std::unique_ptr<ScPostIt> ScTable::ReleaseNote( SCCOL nCol, SCROW nRow ) 1671 { 1672 if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount()) 1673 return nullptr; 1674 1675 return aCol[nCol].ReleaseNote(nRow); 1676 } 1677 1678 size_t ScTable::GetNoteCount( SCCOL nCol ) const 1679 { 1680 if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount()) 1681 return 0; 1682 1683 return aCol[nCol].GetNoteCount(); 1684 } 1685 1686 SCROW ScTable::GetNotePosition( SCCOL nCol, size_t nIndex ) const 1687 { 1688 if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount()) 1689 return -1; 1690 1691 return aCol[nCol].GetNotePosition(nIndex); 1692 } 1693 1694 void ScTable::CreateAllNoteCaptions() 1695 { 1696 for (SCCOL i = 0; i < aCol.size(); ++i) 1697 aCol[i].CreateAllNoteCaptions(); 1698 } 1699 1700 void ScTable::ForgetNoteCaptions( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, bool bPreserveData ) 1701 { 1702 if (!ValidCol(nCol1) || !ValidCol(nCol2)) 1703 return; 1704 if ( nCol2 >= aCol.size() ) nCol2 = aCol.size() - 1; 1705 for (SCCOL i = nCol1; i <= nCol2; ++i) 1706 aCol[i].ForgetNoteCaptions(nRow1, nRow2, bPreserveData); 1707 } 1708 1709 void ScTable::GetAllNoteEntries( std::vector<sc::NoteEntry>& rNotes ) const 1710 { 1711 for (SCCOL nCol = 0; nCol < aCol.size(); ++nCol) 1712 aCol[nCol].GetAllNoteEntries(rNotes); 1713 } 1714 1715 void ScTable::GetNotesInRange( const ScRange& rRange, std::vector<sc::NoteEntry>& rNotes ) const 1716 { 1717 SCROW nStartRow = rRange.aStart.Row(); 1718 SCROW nEndRow = rRange.aEnd.Row(); 1719 SCCOL nEndCol = ClampToAllocatedColumns(rRange.aEnd.Col()); 1720 for (SCCOL nCol = rRange.aStart.Col(); nCol <= nEndCol; ++nCol) 1721 { 1722 aCol[nCol].GetNotesInRange(nStartRow, nEndRow, rNotes); 1723 } 1724 } 1725 1726 CommentCaptionState ScTable::GetAllNoteCaptionsState(const ScRange& rRange, std::vector<sc::NoteEntry>& rNotes ) 1727 { 1728 SCROW nStartRow = rRange.aStart.Row(); 1729 SCROW nEndRow = rRange.aEnd.Row(); 1730 bool bIsFirstNoteShownState = true; // because of error: -Werror=maybe-uninitialized 1731 bool bFirstControl = true; 1732 1733 ScTable* pTab = pDocument->FetchTable(nTab); 1734 assert(pTab); 1735 const SCCOL nEndCol = pTab->ClampToAllocatedColumns(rRange.aEnd.Col()); 1736 for (SCCOL nCol = rRange.aStart.Col(); nCol <= nEndCol; ++nCol) 1737 { 1738 if (bFirstControl && pDocument->HasColNotes(nCol, nTab)) // detect status of first note caption 1739 { 1740 aCol[nCol].GetNotesInRange(nStartRow, nEndRow, rNotes); 1741 bIsFirstNoteShownState = rNotes.begin()->mpNote->IsCaptionShown(); 1742 bFirstControl = false; 1743 } 1744 1745 if (pDocument->HasColNotes(nCol, nTab)) 1746 { 1747 aCol[nCol].GetNotesInRange(nStartRow, nEndRow, rNotes); 1748 1749 bool bIsMixedState = std::any_of(rNotes.begin(), rNotes.end(), [bIsFirstNoteShownState](const sc::NoteEntry& rNote) { 1750 // compare the first note caption with others 1751 return bIsFirstNoteShownState != rNote.mpNote->IsCaptionShown(); }); 1752 if (bIsMixedState) 1753 return CommentCaptionState::MIXED; 1754 } 1755 } 1756 return bIsFirstNoteShownState ? CommentCaptionState::ALLSHOWN : CommentCaptionState::ALLHIDDEN; 1757 } 1758 1759 void ScTable::GetUnprotectedCells( ScRangeList& rRangeList ) const 1760 { 1761 for (auto const & pCol : aCol) 1762 pCol->GetUnprotectedCells(0, pDocument->MaxRow(), rRangeList); 1763 } 1764 1765 bool ScTable::ContainsNotesInRange( const ScRange& rRange ) const 1766 { 1767 SCROW nStartRow = rRange.aStart.Row(); 1768 SCROW nEndRow = rRange.aEnd.Row(); 1769 SCCOL nEndCol = ClampToAllocatedColumns(rRange.aEnd.Col()); 1770 for (SCCOL nCol = rRange.aStart.Col(); nCol <= nEndCol; ++nCol) 1771 { 1772 bool bContainsNote = !aCol[nCol].IsNotesEmptyBlock(nStartRow, nEndRow); 1773 if(bContainsNote) 1774 return true; 1775 } 1776 1777 return false; 1778 } 1779 1780 CellType ScTable::GetCellType( SCCOL nCol, SCROW nRow ) const 1781 { 1782 if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount()) 1783 return aCol[nCol].GetCellType( nRow ); 1784 return CELLTYPE_NONE; 1785 } 1786 1787 ScRefCellValue ScTable::GetCellValue( SCCOL nCol, SCROW nRow ) const 1788 { 1789 if (!ValidColRow(nCol, nRow) || nCol >= GetAllocatedColumnsCount()) 1790 return ScRefCellValue(); 1791 1792 return aCol[nCol].GetCellValue(nRow); 1793 } 1794 1795 void ScTable::GetFirstDataPos(SCCOL& rCol, SCROW& rRow) const 1796 { 1797 rCol = 0; 1798 rRow = pDocument->MaxRow()+1; 1799 while (rCol < (aCol.size() - 1) && aCol[rCol].IsEmptyData() ) 1800 ++rCol; 1801 SCCOL nCol = rCol; 1802 while (nCol < aCol.size() && rRow > 0) 1803 { 1804 if (!aCol[nCol].IsEmptyData()) 1805 rRow = ::std::min( rRow, aCol[nCol].GetFirstDataPos()); 1806 ++nCol; 1807 } 1808 } 1809 1810 void ScTable::GetLastDataPos(SCCOL& rCol, SCROW& rRow) const 1811 { 1812 rCol = aCol.size() - 1; 1813 rRow = 0; 1814 while (aCol[rCol].IsEmptyData() && (rCol > 0)) 1815 rCol--; 1816 SCCOL nCol = rCol; 1817 while (nCol >= 0 && rRow < pDocument->MaxRow()) 1818 rRow = ::std::max( rRow, aCol[nCol--].GetLastDataPos()); 1819 } 1820 1821 bool ScTable::HasData( SCCOL nCol, SCROW nRow ) const 1822 { 1823 if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount()) 1824 return aCol[nCol].HasDataAt( nRow ); 1825 else 1826 return false; 1827 } 1828 1829 bool ScTable::HasStringData( SCCOL nCol, SCROW nRow ) const 1830 { 1831 if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount()) 1832 return aCol[nCol].HasStringData( nRow ); 1833 else 1834 return false; 1835 } 1836 1837 bool ScTable::HasValueData( SCCOL nCol, SCROW nRow ) const 1838 { 1839 if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount()) 1840 return aCol[nCol].HasValueData( nRow ); 1841 else 1842 return false; 1843 } 1844 1845 bool ScTable::HasStringCells( SCCOL nStartCol, SCROW nStartRow, 1846 SCCOL nEndCol, SCROW nEndRow ) const 1847 { 1848 if (ValidCol(nEndCol)) 1849 { 1850 nEndCol = ClampToAllocatedColumns(nEndCol); 1851 for (SCCOL nCol = nStartCol; nCol <= nEndCol; nCol++) 1852 if (aCol[nCol].HasStringCells(nStartRow, nEndRow)) 1853 return true; 1854 } 1855 1856 return false; 1857 } 1858 1859 void ScTable::SetDirtyVar() 1860 { 1861 for (SCCOL i=0; i < aCol.size(); i++) 1862 aCol[i].SetDirtyVar(); 1863 } 1864 1865 void ScTable::CheckVectorizationState() 1866 { 1867 sc::AutoCalcSwitch aACSwitch(*pDocument, false); 1868 1869 for (SCCOL i = 0; i < aCol.size(); i++) 1870 aCol[i].CheckVectorizationState(); 1871 } 1872 1873 void ScTable::SetAllFormulasDirty( const sc::SetFormulaDirtyContext& rCxt ) 1874 { 1875 sc::AutoCalcSwitch aACSwitch(*pDocument, false); 1876 1877 for (SCCOL i=0; i < aCol.size(); i++) 1878 aCol[i].SetAllFormulasDirty(rCxt); 1879 } 1880 1881 void ScTable::SetDirty( const ScRange& rRange, ScColumn::BroadcastMode eMode ) 1882 { 1883 bool bOldAutoCalc = pDocument->GetAutoCalc(); 1884 pDocument->SetAutoCalc( false ); // avoid multiple recalculations 1885 SCCOL nCol2 = rRange.aEnd.Col(); 1886 nCol2 = ClampToAllocatedColumns(nCol2); 1887 for (SCCOL i=rRange.aStart.Col(); i<=nCol2; i++) 1888 aCol[i].SetDirty(rRange.aStart.Row(), rRange.aEnd.Row(), eMode); 1889 pDocument->SetAutoCalc( bOldAutoCalc ); 1890 } 1891 1892 void ScTable::SetTableOpDirty( const ScRange& rRange ) 1893 { 1894 bool bOldAutoCalc = pDocument->GetAutoCalc(); 1895 pDocument->SetAutoCalc( false ); // no multiple recalculation 1896 const SCCOL nCol2 = ClampToAllocatedColumns(rRange.aEnd.Col()); 1897 for (SCCOL i=rRange.aStart.Col(); i<=nCol2; i++) 1898 aCol[i].SetTableOpDirty( rRange ); 1899 pDocument->SetAutoCalc( bOldAutoCalc ); 1900 } 1901 1902 void ScTable::SetDirtyAfterLoad() 1903 { 1904 bool bOldAutoCalc = pDocument->GetAutoCalc(); 1905 pDocument->SetAutoCalc( false ); // avoid multiple recalculations 1906 for (SCCOL i=0; i < aCol.size(); i++) 1907 aCol[i].SetDirtyAfterLoad(); 1908 pDocument->SetAutoCalc( bOldAutoCalc ); 1909 } 1910 1911 void ScTable::SetDirtyIfPostponed() 1912 { 1913 bool bOldAutoCalc = pDocument->GetAutoCalc(); 1914 pDocument->SetAutoCalc( false ); // avoid multiple recalculations 1915 for (SCCOL i=0; i < aCol.size(); i++) 1916 aCol[i].SetDirtyIfPostponed(); 1917 pDocument->SetAutoCalc( bOldAutoCalc ); 1918 } 1919 1920 void ScTable::BroadcastRecalcOnRefMove() 1921 { 1922 sc::AutoCalcSwitch aSwitch(*pDocument, false); 1923 for (SCCOL i = 0; i < aCol.size(); ++i) 1924 aCol[i].BroadcastRecalcOnRefMove(); 1925 } 1926 1927 bool ScTable::BroadcastBroadcasters( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScHint& rHint ) 1928 { 1929 bool bBroadcasted = false; 1930 sc::AutoCalcSwitch aSwitch(*pDocument, false); 1931 rHint.GetAddress().SetTab(nTab); 1932 nCol2 = ClampToAllocatedColumns(nCol2); 1933 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) 1934 bBroadcasted |= aCol[nCol].BroadcastBroadcasters( nRow1, nRow2, rHint); 1935 return bBroadcasted; 1936 } 1937 1938 void ScTable::SetLoadingMedium(bool bLoading) 1939 { 1940 mpRowHeights->enableTreeSearch(!bLoading); 1941 } 1942 1943 void ScTable::CalcAll() 1944 { 1945 for (SCCOL i=0; i < aCol.size(); i++) 1946 aCol[i].CalcAll(); 1947 1948 mpCondFormatList->CalcAll(); 1949 } 1950 1951 void ScTable::CompileAll( sc::CompileFormulaContext& rCxt ) 1952 { 1953 for (SCCOL i = 0; i < aCol.size(); ++i) 1954 aCol[i].CompileAll(rCxt); 1955 1956 if(mpCondFormatList) 1957 mpCondFormatList->CompileAll(); 1958 } 1959 1960 void ScTable::CompileXML( sc::CompileFormulaContext& rCxt, ScProgress& rProgress ) 1961 { 1962 if (mpRangeName) 1963 mpRangeName->CompileUnresolvedXML(rCxt); 1964 1965 for (SCCOL i=0; i < aCol.size(); i++) 1966 { 1967 aCol[i].CompileXML(rCxt, rProgress); 1968 } 1969 1970 if(mpCondFormatList) 1971 mpCondFormatList->CompileXML(); 1972 } 1973 1974 bool ScTable::CompileErrorCells( sc::CompileFormulaContext& rCxt, FormulaError nErrCode ) 1975 { 1976 bool bCompiled = false; 1977 for (SCCOL i = 0; i < aCol.size(); ++i) 1978 { 1979 if (aCol[i].CompileErrorCells(rCxt, nErrCode)) 1980 bCompiled = true; 1981 } 1982 1983 return bCompiled; 1984 } 1985 1986 void ScTable::CalcAfterLoad( sc::CompileFormulaContext& rCxt, bool bStartListening ) 1987 { 1988 for (SCCOL i = 0; i < aCol.size(); ++i) 1989 aCol[i].CalcAfterLoad(rCxt, bStartListening); 1990 } 1991 1992 void ScTable::ResetChanged( const ScRange& rRange ) 1993 { 1994 SCCOL nStartCol = rRange.aStart.Col(); 1995 SCROW nStartRow = rRange.aStart.Row(); 1996 SCCOL nEndCol = ClampToAllocatedColumns(rRange.aEnd.Col()); 1997 SCROW nEndRow = rRange.aEnd.Row(); 1998 1999 for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++) 2000 aCol[nCol].ResetChanged(nStartRow, nEndRow); 2001 } 2002 2003 // Attribute 2004 2005 const SfxPoolItem* ScTable::GetAttr( SCCOL nCol, SCROW nRow, sal_uInt16 nWhich ) const 2006 { 2007 if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount()) 2008 return &aCol[nCol].GetAttr( nRow, nWhich ); 2009 else 2010 return nullptr; 2011 } 2012 2013 sal_uInt32 ScTable::GetNumberFormat( const ScInterpreterContext& rContext, const ScAddress& rPos ) const 2014 { 2015 if (ValidColRow(rPos.Col(), rPos.Row())) 2016 { 2017 if (rPos.Col() < GetAllocatedColumnsCount()) 2018 return aCol[rPos.Col()].GetNumberFormat(rContext, rPos.Row()); 2019 return aDefaultColAttrArray.GetPattern(rPos.Row()) 2020 ->GetNumberFormat(rContext.GetFormatTable()); 2021 } 2022 return 0; 2023 } 2024 2025 sal_uInt32 ScTable::GetNumberFormat( SCCOL nCol, SCROW nRow ) const 2026 { 2027 if (ValidColRow(nCol,nRow)) 2028 return CreateColumnIfNotExists(nCol).GetNumberFormat(pDocument->GetNonThreadedContext(), nRow); 2029 else 2030 return 0; 2031 } 2032 2033 sal_uInt32 ScTable::GetNumberFormat( SCCOL nCol, SCROW nStartRow, SCROW nEndRow ) const 2034 { 2035 if (!ValidCol(nCol) || !ValidRow(nStartRow) || !ValidRow(nEndRow)) 2036 return 0; 2037 2038 return CreateColumnIfNotExists(nCol).GetNumberFormat(nStartRow, nEndRow); 2039 } 2040 2041 void ScTable::SetNumberFormat( SCCOL nCol, SCROW nRow, sal_uInt32 nNumberFormat ) 2042 { 2043 if (!ValidColRow(nCol, nRow)) 2044 return; 2045 2046 CreateColumnIfNotExists(nCol).SetNumberFormat(nRow, nNumberFormat); 2047 } 2048 2049 const ScPatternAttr* ScTable::GetPattern( SCCOL nCol, SCROW nRow ) const 2050 { 2051 if (ValidColRow(nCol,nRow)) 2052 return CreateColumnIfNotExists(nCol).GetPattern( nRow ); 2053 else 2054 { 2055 OSL_FAIL("wrong column or row"); 2056 return pDocument->GetDefPattern(); // for safety 2057 } 2058 } 2059 2060 const ScPatternAttr* ScTable::GetMostUsedPattern( SCCOL nCol, SCROW nStartRow, SCROW nEndRow ) const 2061 { 2062 if ( ValidColRow( nCol, nStartRow ) && ValidRow( nEndRow ) && (nStartRow <= nEndRow) 2063 && nCol < GetAllocatedColumnsCount()) 2064 return aCol[nCol].GetMostUsedPattern( nStartRow, nEndRow ); 2065 else 2066 return nullptr; 2067 } 2068 2069 bool ScTable::HasAttrib( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, HasAttrFlags nMask ) const 2070 { 2071 if ( nCol1 >= aCol.size() ) 2072 return false; 2073 if ( nCol2 >= aCol.size() ) 2074 nCol2 = aCol.size() - 1; // Rows above range, doesn't contains flags 2075 2076 bool bFound = false; 2077 for (SCCOL i=nCol1; i<=nCol2 && !bFound; i++) 2078 bFound |= aCol[i].HasAttrib( nRow1, nRow2, nMask ); 2079 return bFound; 2080 } 2081 2082 bool ScTable::HasAttribSelection( const ScMarkData& rMark, HasAttrFlags nMask ) const 2083 { 2084 std::vector<sc::ColRowSpan> aSpans = rMark.GetMarkedColSpans(); 2085 2086 for (const sc::ColRowSpan & aSpan : aSpans) 2087 { 2088 for (SCCOLROW j = aSpan.mnStart; j <= aSpan.mnEnd; ++j) 2089 { 2090 if (aCol[j].HasAttribSelection(rMark, nMask)) 2091 return true; 2092 } 2093 } 2094 return false; 2095 } 2096 2097 bool ScTable::ExtendMerge( SCCOL nStartCol, SCROW nStartRow, 2098 SCCOL& rEndCol, SCROW& rEndRow, 2099 bool bRefresh ) 2100 { 2101 if (!(ValidCol(nStartCol) && ValidCol(rEndCol))) 2102 { 2103 OSL_FAIL("ScTable::ExtendMerge: invalid column number"); 2104 return false; 2105 } 2106 if ( nStartCol >= aCol.size() ) 2107 { 2108 OSL_FAIL("ScTable::ExtendMerge: invalid nStartCol"); 2109 return false; 2110 } 2111 bool bFound = false; 2112 SCCOL nOldEndX = std::min( rEndCol, static_cast<SCCOL>(aCol.size()-1) ); 2113 SCROW nOldEndY = rEndRow; 2114 for (SCCOL i=nStartCol; i<=nOldEndX; i++) 2115 bFound |= aCol[i].ExtendMerge( i, nStartRow, nOldEndY, rEndCol, rEndRow, bRefresh ); 2116 return bFound; 2117 } 2118 2119 void ScTable::SetMergedCells( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) 2120 { 2121 ScMergeAttr aAttr(nCol2-nCol1+1, nRow2-nRow1+1); 2122 ApplyAttr(nCol1, nRow1, aAttr); 2123 2124 if (nCol1 < nCol2) 2125 ApplyFlags(nCol1+1, nRow1, nCol2, nRow2, ScMF::Hor); 2126 2127 if (nRow1 < nRow2) 2128 ApplyFlags(nCol1, nRow1+1, nCol1, nRow2, ScMF::Ver); 2129 2130 if (nCol1 < nCol2 && nRow1 < nRow2) 2131 ApplyFlags(nCol1+1, nRow1+1, nCol2, nRow2, ScMF::Hor | ScMF::Ver); 2132 } 2133 2134 bool ScTable::IsBlockEmpty( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, bool bIgnoreNotes ) const 2135 { 2136 if (!(ValidCol(nCol1) && ValidCol(nCol2))) 2137 { 2138 OSL_FAIL("ScTable::IsBlockEmpty: invalid column number"); 2139 return false; 2140 } 2141 nCol2 = ClampToAllocatedColumns(nCol2); 2142 bool bEmpty = true; 2143 for (SCCOL i=nCol1; i<=nCol2 && bEmpty; i++) 2144 { 2145 bEmpty = aCol[i].IsEmptyBlock( nRow1, nRow2 ); 2146 if (!bIgnoreNotes && bEmpty) 2147 { 2148 bEmpty = aCol[i].IsNotesEmptyBlock(nRow1, nRow2); 2149 } 2150 } 2151 return bEmpty; 2152 } 2153 2154 SCSIZE ScTable::FillMaxRot( RowInfo* pRowInfo, SCSIZE nArrCount, SCCOL nX1, SCCOL nX2, 2155 SCCOL nCol, SCROW nAttrRow1, SCROW nAttrRow2, SCSIZE nArrY, 2156 const ScPatternAttr* pPattern, const SfxItemSet* pCondSet ) 2157 { 2158 // Return value = new nArrY 2159 2160 ScRotateDir nRotDir = pPattern->GetRotateDir( pCondSet ); 2161 if ( nRotDir != ScRotateDir::NONE ) 2162 { 2163 bool bHit = true; 2164 if ( nCol+1 < nX1 ) // column to the left 2165 bHit = ( nRotDir != ScRotateDir::Left ); 2166 else if ( nCol > nX2+1 ) // column to the right 2167 bHit = ( nRotDir != ScRotateDir::Right ); // ScRotateDir::Standard may now also be extended to the left 2168 2169 if ( bHit ) 2170 { 2171 double nFactor = 0.0; 2172 if ( nCol > nX2+1 ) 2173 { 2174 long nRotVal = pPattern-> 2175 GetItem( ATTR_ROTATE_VALUE, pCondSet ).GetValue(); 2176 double nRealOrient = nRotVal * F_PI18000; // 1/100 degree 2177 double nCos = cos( nRealOrient ); 2178 double nSin = sin( nRealOrient ); 2179 //TODO: limit !!! 2180 //TODO: additional factor for varying PPT X/Y !!! 2181 2182 // for ScRotateDir::Left this gives a negative value, 2183 // if the mode is considered 2184 nFactor = -fabs( nCos / nSin ); 2185 } 2186 2187 for ( SCROW nRow = nAttrRow1; nRow <= nAttrRow2; nRow++ ) 2188 { 2189 if (!RowHidden(nRow)) 2190 { 2191 bool bHitOne = true; 2192 if ( nCol > nX2+1 ) 2193 { 2194 // Does the rotated cell extend into the visible range? 2195 2196 SCCOL nTouchedCol = nCol; 2197 long nWidth = static_cast<long>(mpRowHeights->getValue(nRow) * nFactor); 2198 OSL_ENSURE(nWidth <= 0, "Wrong direction"); 2199 while ( nWidth < 0 && nTouchedCol > 0 ) 2200 { 2201 --nTouchedCol; 2202 nWidth += GetColWidth( nTouchedCol ); 2203 } 2204 if ( nTouchedCol > nX2 ) 2205 bHitOne = false; 2206 } 2207 2208 if (bHitOne) 2209 { 2210 while ( nArrY<nArrCount && pRowInfo[nArrY].nRowNo < nRow ) 2211 ++nArrY; 2212 if ( nArrY<nArrCount && pRowInfo[nArrY].nRowNo == nRow ) 2213 pRowInfo[nArrY].nRotMaxCol = nCol; 2214 } 2215 } 2216 } 2217 } 2218 } 2219 2220 return nArrY; 2221 } 2222 2223 void ScTable::FindMaxRotCol( RowInfo* pRowInfo, SCSIZE nArrCount, SCCOL nX1, SCCOL nX2 ) 2224 { 2225 if ( !mpColWidth || !mpRowHeights || !mpColFlags || !pRowFlags ) 2226 { 2227 OSL_FAIL( "Row/column info missing" ); 2228 return; 2229 } 2230 2231 // nRotMaxCol is initialized to SC_ROTMAX_NONE, nRowNo is already set 2232 2233 SCROW nY1 = pRowInfo[0].nRowNo; 2234 SCROW nY2 = pRowInfo[nArrCount-1].nRowNo; 2235 2236 for (SCCOL nCol : GetColumnsRange(0, pDocument->MaxCol())) 2237 { 2238 if (!ColHidden(nCol)) 2239 { 2240 SCSIZE nArrY = 0; 2241 ScDocAttrIterator aIter( pDocument, nTab, nCol, nY1, nCol, nY2 ); 2242 SCCOL nAttrCol; 2243 SCROW nAttrRow1, nAttrRow2; 2244 const ScPatternAttr* pPattern = aIter.GetNext( nAttrCol, nAttrRow1, nAttrRow2 ); 2245 while ( pPattern ) 2246 { 2247 const SfxPoolItem* pCondItem; 2248 if ( pPattern->GetItemSet().GetItemState( ATTR_CONDITIONAL, true, &pCondItem ) 2249 == SfxItemState::SET ) 2250 { 2251 // Run through all formats, so that each cell does not have to be 2252 // handled individually 2253 2254 const ScCondFormatIndexes& rCondFormatData = static_cast<const ScCondFormatItem*>(pCondItem)->GetCondFormatData(); 2255 ScStyleSheetPool* pStylePool = pDocument->GetStyleSheetPool(); 2256 if (mpCondFormatList && pStylePool && !rCondFormatData.empty()) 2257 { 2258 for(const auto& rItem : rCondFormatData) 2259 { 2260 const ScConditionalFormat* pFormat = mpCondFormatList->GetFormat(rItem); 2261 if ( pFormat ) 2262 { 2263 size_t nEntryCount = pFormat->size(); 2264 for (size_t nEntry=0; nEntry<nEntryCount; nEntry++) 2265 { 2266 const ScFormatEntry* pEntry = pFormat->GetEntry(nEntry); 2267 if(pEntry->GetType() != ScFormatEntry::Type::Condition && 2268 pEntry->GetType() != ScFormatEntry::Type::ExtCondition) 2269 continue; 2270 2271 OUString aStyleName = static_cast<const ScCondFormatEntry*>(pEntry)->GetStyle(); 2272 if (!aStyleName.isEmpty()) 2273 { 2274 SfxStyleSheetBase* pStyleSheet = 2275 pStylePool->Find( aStyleName, SfxStyleFamily::Para ); 2276 if ( pStyleSheet ) 2277 { 2278 FillMaxRot( pRowInfo, nArrCount, nX1, nX2, 2279 nCol, nAttrRow1, nAttrRow2, 2280 nArrY, pPattern, &pStyleSheet->GetItemSet() ); 2281 // not changing nArrY 2282 } 2283 } 2284 } 2285 } 2286 } 2287 } 2288 } 2289 2290 nArrY = FillMaxRot( pRowInfo, nArrCount, nX1, nX2, 2291 nCol, nAttrRow1, nAttrRow2, 2292 nArrY, pPattern, nullptr ); 2293 2294 pPattern = aIter.GetNext( nAttrCol, nAttrRow1, nAttrRow2 ); 2295 } 2296 } 2297 } 2298 } 2299 2300 bool ScTable::HasBlockMatrixFragment( const SCCOL nCol1, SCROW nRow1, const SCCOL nCol2, SCROW nRow2, 2301 bool bNoMatrixAtAll ) const 2302 { 2303 using namespace sc; 2304 2305 if ( !IsColValid( nCol1 ) ) 2306 return false; 2307 2308 const SCCOL nMaxCol2 = std::min<SCCOL>( nCol2, aCol.size() - 1 ); 2309 2310 MatrixEdge nEdges = MatrixEdge::Nothing; 2311 2312 if ( nCol1 == nMaxCol2 ) 2313 { // left and right column 2314 const MatrixEdge n = MatrixEdge::Left | MatrixEdge::Right; 2315 nEdges = aCol[nCol1].GetBlockMatrixEdges( nRow1, nRow2, n, bNoMatrixAtAll ); 2316 if ((nEdges != MatrixEdge::Nothing) && (((nEdges & n)!=n) || (nEdges & (MatrixEdge::Inside|MatrixEdge::Open)))) 2317 return true; // left or right edge is missing or open 2318 } 2319 else 2320 { // left column 2321 nEdges = aCol[nCol1].GetBlockMatrixEdges(nRow1, nRow2, MatrixEdge::Left, bNoMatrixAtAll); 2322 if ((nEdges != MatrixEdge::Nothing) && ((!(nEdges & MatrixEdge::Left)) || (nEdges & (MatrixEdge::Inside|MatrixEdge::Open)))) 2323 return true; // left edge missing or open 2324 // right column 2325 nEdges = aCol[nMaxCol2].GetBlockMatrixEdges(nRow1, nRow2, MatrixEdge::Right, bNoMatrixAtAll); 2326 if ((nEdges != MatrixEdge::Nothing) && ((!(nEdges & MatrixEdge::Right)) || (nEdges & (MatrixEdge::Inside|MatrixEdge::Open)))) 2327 return true; // right edge is missing or open 2328 } 2329 2330 if (bNoMatrixAtAll) 2331 { 2332 for (SCCOL i=nCol1; i<=nMaxCol2; i++) 2333 { 2334 nEdges = aCol[i].GetBlockMatrixEdges( nRow1, nRow2, MatrixEdge::Nothing, bNoMatrixAtAll); 2335 if (nEdges != MatrixEdge::Nothing 2336 && (nEdges != (MatrixEdge::Top | MatrixEdge::Left | MatrixEdge::Bottom | MatrixEdge::Right))) 2337 return true; 2338 } 2339 } 2340 else if ( nRow1 == nRow2 ) 2341 { // Row on top and on bottom 2342 bool bOpen = false; 2343 const MatrixEdge n = MatrixEdge::Bottom | MatrixEdge::Top; 2344 for ( SCCOL i=nCol1; i<=nMaxCol2; i++) 2345 { 2346 nEdges = aCol[i].GetBlockMatrixEdges( nRow1, nRow1, n, bNoMatrixAtAll ); 2347 if (nEdges != MatrixEdge::Nothing) 2348 { 2349 if ( (nEdges & n) != n ) 2350 return true; // Top or bottom edge missing 2351 if (nEdges & MatrixEdge::Left) 2352 bOpen = true; // left edge open, continue 2353 else if ( !bOpen ) 2354 return true; // Something exist that has not been opened 2355 if (nEdges & MatrixEdge::Right) 2356 bOpen = false; // Close right edge 2357 } 2358 } 2359 if ( bOpen ) 2360 return true; 2361 } 2362 else 2363 { 2364 int j; 2365 MatrixEdge n; 2366 SCROW nR; 2367 // first top row, then bottom row 2368 for ( j=0, n = MatrixEdge::Top, nR=nRow1; j<2; 2369 j++, n = MatrixEdge::Bottom, nR=nRow2) 2370 { 2371 bool bOpen = false; 2372 for ( SCCOL i=nCol1; i<=nMaxCol2; i++) 2373 { 2374 nEdges = aCol[i].GetBlockMatrixEdges( nR, nR, n, bNoMatrixAtAll ); 2375 if ( nEdges != MatrixEdge::Nothing) 2376 { 2377 // in top row no top edge respectively 2378 // in bottom row no bottom edge 2379 if ( (nEdges & n) != n ) 2380 return true; 2381 if (nEdges & MatrixEdge::Left) 2382 bOpen = true; // open left edge, continue 2383 else if ( !bOpen ) 2384 return true; // Something exist that has not been opened 2385 if (nEdges & MatrixEdge::Right) 2386 bOpen = false; // Close right edge 2387 } 2388 } 2389 if ( bOpen ) 2390 return true; 2391 } 2392 } 2393 return false; 2394 } 2395 2396 bool ScTable::HasSelectionMatrixFragment( const ScMarkData& rMark ) const 2397 { 2398 std::vector<sc::ColRowSpan> aSpans = rMark.GetMarkedColSpans(); 2399 2400 for (const sc::ColRowSpan & aSpan : aSpans) 2401 { 2402 SCCOL nEndCol = ClampToAllocatedColumns(aSpan.mnEnd); 2403 for ( SCCOLROW j=aSpan.mnStart; j<=nEndCol; j++ ) 2404 { 2405 if ( aCol[j].HasSelectionMatrixFragment(rMark) ) 2406 return true; 2407 } 2408 } 2409 return false; 2410 } 2411 2412 bool ScTable::IsBlockEditable( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, 2413 SCROW nRow2, bool* pOnlyNotBecauseOfMatrix /* = NULL */, 2414 bool bNoMatrixAtAll ) const 2415 { 2416 if ( !ValidColRow( nCol2, nRow2 ) ) 2417 { 2418 SAL_WARN("sc", "IsBlockEditable: invalid column or row " << nCol2 << " " << nRow2); 2419 if (pOnlyNotBecauseOfMatrix) 2420 *pOnlyNotBecauseOfMatrix = false; 2421 return false; 2422 } 2423 nCol1 = ClampToAllocatedColumns(nCol1); 2424 nCol2 = ClampToAllocatedColumns(nCol2); 2425 2426 bool bIsEditable = true; 2427 if ( nLockCount ) 2428 bIsEditable = false; 2429 else if ( IsProtected() && !pDocument->IsScenario(nTab) ) 2430 { 2431 bIsEditable = !HasAttrib( nCol1, nRow1, nCol2, nRow2, HasAttrFlags::Protected ); 2432 if (!bIsEditable) 2433 { 2434 // An enhanced protection permission may override the attribute. 2435 if (pTabProtection) 2436 bIsEditable = pTabProtection->isBlockEditable( ScRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab)); 2437 } 2438 if (bIsEditable) 2439 { 2440 // If Sheet is protected and cells are not protected then 2441 // check the active scenario protect flag if this range is 2442 // on the active scenario range. Note the 'copy back' must also 2443 // be set to apply protection. 2444 sal_uInt16 nScenTab = nTab+1; 2445 while(pDocument->IsScenario(nScenTab)) 2446 { 2447 ScRange aEditRange(nCol1, nRow1, nScenTab, nCol2, nRow2, nScenTab); 2448 if(pDocument->IsActiveScenario(nScenTab) && pDocument->HasScenarioRange(nScenTab, aEditRange)) 2449 { 2450 ScScenarioFlags nFlags; 2451 pDocument->GetScenarioFlags(nScenTab,nFlags); 2452 bIsEditable = !((nFlags & ScScenarioFlags::Protected) && (nFlags & ScScenarioFlags::TwoWay)); 2453 break; 2454 } 2455 nScenTab++; 2456 } 2457 } 2458 } 2459 else if (pDocument->IsScenario(nTab)) 2460 { 2461 // Determine if the preceding sheet is protected 2462 SCTAB nActualTab = nTab; 2463 do 2464 { 2465 nActualTab--; 2466 } 2467 while(pDocument->IsScenario(nActualTab)); 2468 2469 if(pDocument->IsTabProtected(nActualTab)) 2470 { 2471 ScRange aEditRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab); 2472 if(pDocument->HasScenarioRange(nTab, aEditRange)) 2473 { 2474 ScScenarioFlags nFlags; 2475 pDocument->GetScenarioFlags(nTab,nFlags); 2476 bIsEditable = !(nFlags & ScScenarioFlags::Protected); 2477 } 2478 } 2479 } 2480 if ( bIsEditable ) 2481 { 2482 if (HasBlockMatrixFragment( nCol1, nRow1, nCol2, nRow2, bNoMatrixAtAll)) 2483 { 2484 bIsEditable = false; 2485 if ( pOnlyNotBecauseOfMatrix ) 2486 *pOnlyNotBecauseOfMatrix = true; 2487 } 2488 else if ( pOnlyNotBecauseOfMatrix ) 2489 *pOnlyNotBecauseOfMatrix = false; 2490 } 2491 else if ( pOnlyNotBecauseOfMatrix ) 2492 *pOnlyNotBecauseOfMatrix = false; 2493 return bIsEditable; 2494 } 2495 2496 bool ScTable::IsSelectionEditable( const ScMarkData& rMark, 2497 bool* pOnlyNotBecauseOfMatrix /* = NULL */ ) const 2498 { 2499 bool bIsEditable = true; 2500 if ( nLockCount ) 2501 bIsEditable = false; 2502 else if ( IsProtected() && !pDocument->IsScenario(nTab) ) 2503 { 2504 ScRangeList aRanges; 2505 rMark.FillRangeListWithMarks( &aRanges, false ); 2506 bIsEditable = !HasAttribSelection( rMark, HasAttrFlags::Protected ); 2507 if (!bIsEditable) 2508 { 2509 // An enhanced protection permission may override the attribute. 2510 if (pTabProtection) 2511 bIsEditable = pTabProtection->isSelectionEditable( aRanges); 2512 } 2513 if (bIsEditable) 2514 { 2515 // If Sheet is protected and cells are not protected then 2516 // check the active scenario protect flag if this area is 2517 // in the active scenario range. 2518 SCTAB nScenTab = nTab+1; 2519 while(pDocument->IsScenario(nScenTab) && bIsEditable) 2520 { 2521 if(pDocument->IsActiveScenario(nScenTab)) 2522 { 2523 for (size_t i=0, nRange = aRanges.size(); (i < nRange) && bIsEditable; i++ ) 2524 { 2525 const ScRange & rRange = aRanges[ i ]; 2526 if(pDocument->HasScenarioRange(nScenTab, rRange)) 2527 { 2528 ScScenarioFlags nFlags; 2529 pDocument->GetScenarioFlags(nScenTab,nFlags); 2530 bIsEditable = !((nFlags & ScScenarioFlags::Protected) && (nFlags & ScScenarioFlags::TwoWay)); 2531 } 2532 } 2533 } 2534 nScenTab++; 2535 } 2536 } 2537 } 2538 else if (pDocument->IsScenario(nTab)) 2539 { 2540 // Determine if the preceding sheet is protected 2541 SCTAB nActualTab = nTab; 2542 do 2543 { 2544 nActualTab--; 2545 } 2546 while(pDocument->IsScenario(nActualTab)); 2547 2548 if(pDocument->IsTabProtected(nActualTab)) 2549 { 2550 ScRangeList aRanges; 2551 rMark.FillRangeListWithMarks( &aRanges, false ); 2552 for (size_t i = 0, nRange = aRanges.size(); (i < nRange) && bIsEditable; i++) 2553 { 2554 const ScRange & rRange = aRanges[ i ]; 2555 if(pDocument->HasScenarioRange(nTab, rRange)) 2556 { 2557 ScScenarioFlags nFlags; 2558 pDocument->GetScenarioFlags(nTab,nFlags); 2559 bIsEditable = !(nFlags & ScScenarioFlags::Protected); 2560 } 2561 } 2562 } 2563 } 2564 if ( bIsEditable ) 2565 { 2566 if ( HasSelectionMatrixFragment( rMark ) ) 2567 { 2568 bIsEditable = false; 2569 if ( pOnlyNotBecauseOfMatrix ) 2570 *pOnlyNotBecauseOfMatrix = true; 2571 } 2572 else if ( pOnlyNotBecauseOfMatrix ) 2573 *pOnlyNotBecauseOfMatrix = false; 2574 } 2575 else if ( pOnlyNotBecauseOfMatrix ) 2576 *pOnlyNotBecauseOfMatrix = false; 2577 return bIsEditable; 2578 } 2579 2580 void ScTable::LockTable() 2581 { 2582 ++nLockCount; 2583 } 2584 2585 void ScTable::UnlockTable() 2586 { 2587 if (nLockCount) 2588 --nLockCount; 2589 else 2590 { 2591 OSL_FAIL("UnlockTable without LockTable"); 2592 } 2593 } 2594 2595 void ScTable::MergeSelectionPattern( ScMergePatternState& rState, const ScMarkData& rMark, bool bDeep ) const 2596 { 2597 std::vector<sc::ColRowSpan> aSpans = rMark.GetMarkedColSpans(); 2598 2599 for (const sc::ColRowSpan & rSpan : aSpans) 2600 { 2601 SCCOL nEnd = ClampToAllocatedColumns(rSpan.mnEnd); 2602 for (SCCOLROW i = rSpan.mnStart; i <= nEnd; ++i) 2603 { 2604 aCol[i].MergeSelectionPattern( rState, rMark, bDeep ); 2605 } 2606 } 2607 } 2608 2609 void ScTable::MergePatternArea( ScMergePatternState& rState, SCCOL nCol1, SCROW nRow1, 2610 SCCOL nCol2, SCROW nRow2, bool bDeep ) const 2611 { 2612 nCol2 = ClampToAllocatedColumns(nCol2); 2613 for (SCCOL i=nCol1; i<=nCol2; i++) 2614 aCol[i].MergePatternArea( rState, nRow1, nRow2, bDeep ); 2615 } 2616 2617 void ScTable::MergeBlockFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner, ScLineFlags& rFlags, 2618 SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) const 2619 { 2620 if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow)) 2621 { 2622 PutInOrder(nStartCol, nEndCol); 2623 PutInOrder(nStartRow, nEndRow); 2624 nEndCol = ClampToAllocatedColumns(nEndCol); 2625 for (SCCOL i=nStartCol; i<=nEndCol; i++) 2626 aCol[i].MergeBlockFrame( pLineOuter, pLineInner, rFlags, 2627 nStartRow, nEndRow, (i==nStartCol), nEndCol-i ); 2628 } 2629 } 2630 2631 void ScTable::ApplyBlockFrame(const SvxBoxItem& rLineOuter, const SvxBoxInfoItem* pLineInner, 2632 SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow) 2633 { 2634 if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow)) 2635 { 2636 PutInOrder(nStartCol, nEndCol); 2637 PutInOrder(nStartRow, nEndRow); 2638 nEndCol = ClampToAllocatedColumns(nEndCol); 2639 for (SCCOL i=nStartCol; i<=nEndCol; i++) 2640 aCol[i].ApplyBlockFrame(rLineOuter, pLineInner, 2641 nStartRow, nEndRow, (i==nStartCol), nEndCol-i); 2642 } 2643 } 2644 2645 void ScTable::ApplyPattern( SCCOL nCol, SCROW nRow, const ScPatternAttr& rAttr ) 2646 { 2647 if (ValidColRow(nCol,nRow)) 2648 CreateColumnIfNotExists(nCol).ApplyPattern( nRow, rAttr ); 2649 } 2650 2651 void ScTable::ApplyPatternArea( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, 2652 const ScPatternAttr& rAttr, ScEditDataArray* pDataArray, 2653 bool* const pIsChanged ) 2654 { 2655 if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow)) 2656 { 2657 PutInOrder(nStartCol, nEndCol); 2658 PutInOrder(nStartRow, nEndRow); 2659 for (SCCOL i = nStartCol; i <= nEndCol; i++) 2660 CreateColumnIfNotExists(i).ApplyPatternArea(nStartRow, nEndRow, rAttr, pDataArray, pIsChanged); 2661 } 2662 } 2663 2664 void ScTable::ApplyPatternIfNumberformatIncompatible( const ScRange& rRange, 2665 const ScPatternAttr& rPattern, SvNumFormatType nNewType ) 2666 { 2667 SCCOL nEndCol = rRange.aEnd.Col(); 2668 for ( SCCOL nCol = rRange.aStart.Col(); nCol <= nEndCol; nCol++ ) 2669 { 2670 aCol[nCol].ApplyPatternIfNumberformatIncompatible( rRange, rPattern, nNewType ); 2671 } 2672 } 2673 2674 void ScTable::AddCondFormatData( const ScRangeList& rRangeList, sal_uInt32 nIndex ) 2675 { 2676 size_t n = rRangeList.size(); 2677 for(size_t i = 0; i < n; ++i) 2678 { 2679 const ScRange & rRange = rRangeList[i]; 2680 SCCOL nColStart = rRange.aStart.Col(); 2681 SCCOL nColEnd = rRange.aEnd.Col(); 2682 SCROW nRowStart = rRange.aStart.Row(); 2683 SCROW nRowEnd = rRange.aEnd.Row(); 2684 for(SCCOL nCol = nColStart; nCol <= nColEnd; ++nCol) 2685 { 2686 CreateColumnIfNotExists(nCol).AddCondFormat(nRowStart, nRowEnd, nIndex); 2687 } 2688 } 2689 } 2690 2691 void ScTable::RemoveCondFormatData( const ScRangeList& rRangeList, sal_uInt32 nIndex ) 2692 { 2693 size_t n = rRangeList.size(); 2694 for(size_t i = 0; i < n; ++i) 2695 { 2696 const ScRange & rRange = rRangeList[i]; 2697 SCCOL nColStart = rRange.aStart.Col(); 2698 SCCOL nColEnd = ClampToAllocatedColumns(rRange.aEnd.Col()); 2699 SCROW nRowStart = rRange.aStart.Row(); 2700 SCROW nRowEnd = rRange.aEnd.Row(); 2701 for(SCCOL nCol = nColStart; nCol <= nColEnd; ++nCol) 2702 { 2703 aCol[nCol].RemoveCondFormat(nRowStart, nRowEnd, nIndex); 2704 } 2705 } 2706 } 2707 2708 void ScTable::SetPatternAreaCondFormat( SCCOL nCol, SCROW nStartRow, SCROW nEndRow, 2709 const ScPatternAttr& rAttr, const ScCondFormatIndexes& rCondFormatIndexes ) 2710 { 2711 aCol[nCol].SetPatternArea( nStartRow, nEndRow, rAttr); 2712 2713 for (const auto& rIndex : rCondFormatIndexes) 2714 { 2715 ScConditionalFormat* pCondFormat = mpCondFormatList->GetFormat(rIndex); 2716 if (pCondFormat) 2717 { 2718 ScRangeList aRange = pCondFormat->GetRange(); 2719 aRange.Join( ScRange( nCol, nStartRow, nTab, nCol, nEndRow, nTab)); 2720 pCondFormat->SetRange(aRange); 2721 } 2722 } 2723 } 2724 2725 void ScTable::ApplyStyle( SCCOL nCol, SCROW nRow, const ScStyleSheet* rStyle ) 2726 { 2727 if (ValidColRow(nCol,nRow)) 2728 // If column not exists then we need to create it 2729 CreateColumnIfNotExists( nCol ).ApplyStyle( nRow, rStyle ); 2730 } 2731 2732 void ScTable::ApplyStyleArea( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, const ScStyleSheet& rStyle ) 2733 { 2734 if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow)) 2735 { 2736 PutInOrder(nStartCol, nEndCol); 2737 PutInOrder(nStartRow, nEndRow); 2738 if ( nEndCol == pDocument->MaxCol() ) 2739 { 2740 if ( nStartCol < aCol.size() ) 2741 { 2742 // If we would like set all columns to specific style, then change only default style for not existing columns 2743 nEndCol = aCol.size() - 1; 2744 for (SCCOL i = nStartCol; i <= nEndCol; i++) 2745 aCol[i].ApplyStyleArea(nStartRow, nEndRow, rStyle); 2746 aDefaultColAttrArray.ApplyStyleArea(nStartRow, nEndRow, rStyle ); 2747 } 2748 else 2749 { 2750 CreateColumnIfNotExists( nStartCol - 1 ); 2751 aDefaultColAttrArray.ApplyStyleArea(nStartRow, nEndRow, rStyle ); 2752 } 2753 } 2754 else 2755 { 2756 CreateColumnIfNotExists( nEndCol ); 2757 for (SCCOL i = nStartCol; i <= nEndCol; i++) 2758 aCol[i].ApplyStyleArea(nStartRow, nEndRow, rStyle); 2759 } 2760 } 2761 } 2762 2763 void ScTable::ApplySelectionStyle(const ScStyleSheet& rStyle, const ScMarkData& rMark) 2764 { 2765 for (SCCOL i=0; i < aCol.size(); i++) 2766 aCol[i].ApplySelectionStyle( rStyle, rMark ); 2767 } 2768 2769 void ScTable::ApplySelectionLineStyle( const ScMarkData& rMark, 2770 const ::editeng::SvxBorderLine* pLine, bool bColorOnly ) 2771 { 2772 if ( bColorOnly && !pLine ) 2773 return; 2774 2775 for (SCCOL i=0; i < aCol.size(); i++) 2776 aCol[i].ApplySelectionLineStyle( rMark, pLine, bColorOnly ); 2777 } 2778 2779 const ScStyleSheet* ScTable::GetStyle( SCCOL nCol, SCROW nRow ) const 2780 { 2781 if ( !ValidColRow( nCol, nRow ) ) 2782 return nullptr; 2783 if ( nCol < aCol.size() ) 2784 return aCol[nCol].GetStyle( nRow ); 2785 else 2786 return aDefaultColAttrArray.GetPattern( nRow )->GetStyleSheet(); 2787 } 2788 2789 const ScStyleSheet* ScTable::GetSelectionStyle( const ScMarkData& rMark, bool& rFound ) const 2790 { 2791 rFound = false; 2792 2793 bool bEqual = true; 2794 bool bColFound; 2795 2796 const ScStyleSheet* pStyle = nullptr; 2797 const ScStyleSheet* pNewStyle; 2798 2799 for (SCCOL i=0; i < aCol.size() && bEqual; i++) 2800 if (rMark.HasMultiMarks(i)) 2801 { 2802 pNewStyle = aCol[i].GetSelectionStyle( rMark, bColFound ); 2803 if (bColFound) 2804 { 2805 rFound = true; 2806 if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) ) 2807 bEqual = false; 2808 pStyle = pNewStyle; 2809 } 2810 } 2811 2812 return bEqual ? pStyle : nullptr; 2813 } 2814 2815 const ScStyleSheet* ScTable::GetAreaStyle( bool& rFound, SCCOL nCol1, SCROW nRow1, 2816 SCCOL nCol2, SCROW nRow2 ) const 2817 { 2818 rFound = false; 2819 2820 bool bEqual = true; 2821 bool bColFound; 2822 2823 const ScStyleSheet* pStyle = nullptr; 2824 const ScStyleSheet* pNewStyle; 2825 nCol2 = ClampToAllocatedColumns(nCol2); 2826 for (SCCOL i=nCol1; i<=nCol2 && bEqual; i++) 2827 { 2828 pNewStyle = aCol[i].GetAreaStyle(bColFound, nRow1, nRow2); 2829 if (bColFound) 2830 { 2831 rFound = true; 2832 if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) ) 2833 bEqual = false; 2834 pStyle = pNewStyle; 2835 } 2836 } 2837 2838 return bEqual ? pStyle : nullptr; 2839 } 2840 2841 bool ScTable::IsStyleSheetUsed( const ScStyleSheet& rStyle ) const 2842 { 2843 bool bIsUsed = false; 2844 2845 for ( SCCOL i=0; i < aCol.size(); i++ ) 2846 { 2847 if ( aCol[i].IsStyleSheetUsed( rStyle ) ) 2848 { 2849 bIsUsed = true; 2850 } 2851 } 2852 2853 return bIsUsed; 2854 } 2855 2856 void ScTable::StyleSheetChanged( const SfxStyleSheetBase* pStyleSheet, bool bRemoved, 2857 OutputDevice* pDev, 2858 double nPPTX, double nPPTY, 2859 const Fraction& rZoomX, const Fraction& rZoomY ) 2860 { 2861 ScFlatBoolRowSegments aUsedRows(pDocument->MaxRow()); 2862 for (SCCOL i = 0; i < aCol.size(); ++i) 2863 aCol[i].FindStyleSheet(pStyleSheet, aUsedRows, bRemoved); 2864 2865 sc::RowHeightContext aCxt(pDocument->MaxRow(), nPPTX, nPPTY, rZoomX, rZoomY, pDev); 2866 SCROW nRow = 0; 2867 while (nRow <= pDocument->MaxRow()) 2868 { 2869 ScFlatBoolRowSegments::RangeData aData; 2870 if (!aUsedRows.getRangeData(nRow, aData)) 2871 // search failed! 2872 return; 2873 2874 SCROW nEndRow = aData.mnRow2; 2875 if (aData.mbValue) 2876 SetOptimalHeight(aCxt, nRow, nEndRow); 2877 2878 nRow = nEndRow + 1; 2879 } 2880 } 2881 2882 bool ScTable::ApplyFlags( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, 2883 ScMF nFlags ) 2884 { 2885 bool bChanged = false; 2886 if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow)) 2887 for (SCCOL i = nStartCol; i <= nEndCol; i++) 2888 bChanged |= CreateColumnIfNotExists(i).ApplyFlags(nStartRow, nEndRow, nFlags); 2889 return bChanged; 2890 } 2891 2892 bool ScTable::RemoveFlags( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, 2893 ScMF nFlags ) 2894 { 2895 if (!ValidColRow(nStartCol, nStartRow) || !ValidColRow(nEndCol, nEndRow)) 2896 return false; 2897 bool bChanged = false; 2898 nEndCol = ClampToAllocatedColumns(nEndCol); 2899 for (SCCOL i = nStartCol; i <= nEndCol; i++) 2900 bChanged |= aCol[i].RemoveFlags(nStartRow, nEndRow, nFlags); 2901 return bChanged; 2902 } 2903 2904 void ScTable::SetPattern( const ScAddress& rPos, const ScPatternAttr& rAttr ) 2905 { 2906 if (ValidColRow(rPos.Col(),rPos.Row())) 2907 aCol[rPos.Col()].SetPattern( rPos.Row(), rAttr ); 2908 } 2909 2910 const ScPatternAttr* ScTable::SetPattern( SCCOL nCol, SCROW nRow, std::unique_ptr<ScPatternAttr> pAttr ) 2911 { 2912 if (ValidColRow(nCol,nRow)) 2913 return aCol[nCol].SetPattern( nRow, std::move(pAttr) ); 2914 return nullptr; 2915 } 2916 2917 void ScTable::SetPattern( SCCOL nCol, SCROW nRow, const ScPatternAttr& rAttr ) 2918 { 2919 if (ValidColRow(nCol,nRow)) 2920 aCol[nCol].SetPattern( nRow, rAttr ); 2921 } 2922 2923 void ScTable::ApplyAttr( SCCOL nCol, SCROW nRow, const SfxPoolItem& rAttr ) 2924 { 2925 if (ValidColRow(nCol,nRow)) 2926 CreateColumnIfNotExists(nCol).ApplyAttr( nRow, rAttr ); 2927 } 2928 2929 void ScTable::ApplySelectionCache( SfxItemPoolCache* pCache, const ScMarkData& rMark, 2930 ScEditDataArray* pDataArray, bool* const pIsChanged ) 2931 { 2932 for (SCCOL i=0; i < aCol.size(); i++) 2933 aCol[i].ApplySelectionCache( pCache, rMark, pDataArray, pIsChanged ); 2934 } 2935 2936 void ScTable::ChangeSelectionIndent( bool bIncrement, const ScMarkData& rMark ) 2937 { 2938 for (SCCOL i=0; i < aCol.size(); i++) 2939 aCol[i].ChangeSelectionIndent( bIncrement, rMark ); 2940 } 2941 2942 void ScTable::ClearSelectionItems( const sal_uInt16* pWhich, const ScMarkData& rMark ) 2943 { 2944 for (SCCOL i=0; i < aCol.size(); i++) 2945 aCol[i].ClearSelectionItems( pWhich, rMark ); 2946 } 2947 2948 // Column widths / Row heights 2949 2950 void ScTable::SetColWidth( SCCOL nCol, sal_uInt16 nNewWidth ) 2951 { 2952 if (ValidCol(nCol) && mpColWidth) 2953 { 2954 if (!nNewWidth) 2955 { 2956 nNewWidth = STD_COL_WIDTH; 2957 } 2958 2959 if ( nNewWidth != mpColWidth->GetValue(nCol) ) 2960 { 2961 mpColWidth->SetValue(nCol, nNewWidth); 2962 InvalidatePageBreaks(); 2963 } 2964 } 2965 else 2966 { 2967 OSL_FAIL("Invalid column number or no widths"); 2968 } 2969 } 2970 2971 void ScTable::SetColWidthOnly( SCCOL nCol, sal_uInt16 nNewWidth ) 2972 { 2973 if (!ValidCol(nCol) || !mpColWidth) 2974 return; 2975 2976 if (!nNewWidth) 2977 nNewWidth = STD_COL_WIDTH; 2978 2979 if (nNewWidth != mpColWidth->GetValue(nCol)) 2980 mpColWidth->SetValue(nCol, nNewWidth); 2981 } 2982 2983 void ScTable::SetRowHeight( SCROW nRow, sal_uInt16 nNewHeight ) 2984 { 2985 if (ValidRow(nRow) && mpRowHeights) 2986 { 2987 if (!nNewHeight) 2988 { 2989 OSL_FAIL("SetRowHeight: Row height zero"); 2990 nNewHeight = ScGlobal::nStdRowHeight; 2991 } 2992 2993 sal_uInt16 nOldHeight = mpRowHeights->getValue(nRow); 2994 if ( nNewHeight != nOldHeight ) 2995 { 2996 mpRowHeights->setValue(nRow, nRow, nNewHeight); 2997 InvalidatePageBreaks(); 2998 } 2999 } 3000 else 3001 { 3002 OSL_FAIL("Invalid row number or no heights"); 3003 } 3004 } 3005 3006 namespace { 3007 3008 /** 3009 * Check if the new pixel size is different from the old size between 3010 * specified ranges. 3011 */ 3012 bool lcl_pixelSizeChanged( 3013 ScFlatUInt16RowSegments& rRowHeights, SCROW nStartRow, SCROW nEndRow, 3014 sal_uInt16 nNewHeight, double nPPTY) 3015 { 3016 long nNewPix = static_cast<long>(nNewHeight * nPPTY); 3017 3018 ScFlatUInt16RowSegments::ForwardIterator aFwdIter(rRowHeights); 3019 for (SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow) 3020 { 3021 sal_uInt16 nHeight; 3022 if (!aFwdIter.getValue(nRow, nHeight)) 3023 break; 3024 3025 if (nHeight != nNewHeight) 3026 { 3027 bool bChanged = (nNewPix != static_cast<long>(nHeight * nPPTY)); 3028 if (bChanged) 3029 return true; 3030 } 3031 3032 // Skip ahead to the last position of the current range. 3033 nRow = aFwdIter.getLastPos(); 3034 } 3035 return false; 3036 } 3037 3038 } 3039 3040 bool ScTable::SetRowHeightRange( SCROW nStartRow, SCROW nEndRow, sal_uInt16 nNewHeight, 3041 double nPPTY ) 3042 { 3043 bool bChanged = false; 3044 if (ValidRow(nStartRow) && ValidRow(nEndRow) && mpRowHeights) 3045 { 3046 if (!nNewHeight) 3047 { 3048 OSL_FAIL("SetRowHeight: Row height zero"); 3049 nNewHeight = ScGlobal::nStdRowHeight; 3050 } 3051 3052 bool bSingle = false; // true = process every row for its own 3053 ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer(); 3054 if (pDrawLayer) 3055 if (pDrawLayer->HasObjectsInRows( nTab, nStartRow, nEndRow )) 3056 bSingle = true; 3057 3058 if (bSingle) 3059 { 3060 ScFlatUInt16RowSegments::RangeData aData; 3061 if (mpRowHeights->getRangeData(nStartRow, aData) && 3062 nNewHeight == aData.mnValue && nEndRow <= aData.mnRow2) 3063 { 3064 bSingle = false; // no difference in this range 3065 } 3066 } 3067 3068 if (!bSingle || nEndRow - nStartRow < 20) 3069 { 3070 bChanged = lcl_pixelSizeChanged(*mpRowHeights, nStartRow, nEndRow, nNewHeight, nPPTY); 3071 mpRowHeights->setValue(nStartRow, nEndRow, nNewHeight); 3072 } 3073 else 3074 { 3075 SCROW nMid = (nStartRow + nEndRow) / 2; 3076 if (SetRowHeightRange(nStartRow, nMid, nNewHeight, 1.0)) 3077 bChanged = true; 3078 if (SetRowHeightRange(nMid + 1, nEndRow, nNewHeight, 1.0)) 3079 bChanged = true; 3080 } 3081 3082 if (bChanged) 3083 InvalidatePageBreaks(); 3084 } 3085 else 3086 { 3087 OSL_FAIL("Invalid row number or no heights"); 3088 } 3089 3090 return bChanged; 3091 } 3092 3093 void ScTable::SetRowHeightOnly( SCROW nStartRow, SCROW nEndRow, sal_uInt16 nNewHeight ) 3094 { 3095 if (!ValidRow(nStartRow) || !ValidRow(nEndRow) || !mpRowHeights) 3096 return; 3097 3098 if (!nNewHeight) 3099 nNewHeight = ScGlobal::nStdRowHeight; 3100 3101 mpRowHeights->setValue(nStartRow, nEndRow, nNewHeight); 3102 } 3103 3104 void ScTable::SetManualHeight( SCROW nStartRow, SCROW nEndRow, bool bManual ) 3105 { 3106 if (ValidRow(nStartRow) && ValidRow(nEndRow) && pRowFlags) 3107 { 3108 if (bManual) 3109 pRowFlags->OrValue( nStartRow, nEndRow, CRFlags::ManualSize); 3110 else 3111 pRowFlags->AndValue( nStartRow, nEndRow, ~CRFlags::ManualSize); 3112 } 3113 else 3114 { 3115 OSL_FAIL("Invalid row number or no column flags"); 3116 } 3117 } 3118 3119 sal_uInt16 ScTable::GetColWidth( SCCOL nCol, bool bHiddenAsZero ) const 3120 { 3121 OSL_ENSURE(ValidCol(nCol),"wrong column number"); 3122 3123 if (ValidCol(nCol) && mpColFlags && mpColWidth) 3124 { 3125 if (bHiddenAsZero && ColHidden(nCol)) 3126 return 0; 3127 else 3128 return mpColWidth->GetValue(nCol); 3129 } 3130 else 3131 return sal_uInt16(STD_COL_WIDTH); 3132 } 3133 3134 sal_uLong ScTable::GetColWidth( SCCOL nStartCol, SCCOL nEndCol ) const 3135 { 3136 if (!ValidCol(nStartCol) || !ValidCol(nEndCol) || nStartCol > nEndCol) 3137 return 0; 3138 3139 sal_uLong nW = 0; 3140 bool bHidden = false; 3141 SCCOL nLastHiddenCol = -1; 3142 auto colWidthIt = mpColWidth->begin() + nStartCol; 3143 for (SCCOL nCol = nStartCol; nCol <= nEndCol; (++nCol <= nEndCol) ? ++colWidthIt : (void)false) 3144 { 3145 if (nCol > nLastHiddenCol) 3146 bHidden = ColHidden(nCol, nullptr, &nLastHiddenCol); 3147 3148 if (bHidden) 3149 continue; 3150 3151 nW += *colWidthIt; 3152 } 3153 return nW; 3154 } 3155 3156 sal_uInt16 ScTable::GetOriginalWidth( SCCOL nCol ) const // always the set value 3157 { 3158 OSL_ENSURE(ValidCol(nCol),"wrong column number"); 3159 3160 if (ValidCol(nCol) && mpColWidth) 3161 return mpColWidth->GetValue(nCol); 3162 else 3163 return sal_uInt16(STD_COL_WIDTH); 3164 } 3165 3166 sal_uInt16 ScTable::GetCommonWidth( SCCOL nEndCol ) const 3167 { 3168 // get the width that is used in the largest continuous column range (up to nEndCol) 3169 3170 if ( !ValidCol(nEndCol) ) 3171 { 3172 OSL_FAIL("wrong column"); 3173 nEndCol = pDocument->MaxCol(); 3174 } 3175 3176 sal_uInt16 nMaxWidth = 0; 3177 sal_uInt16 nMaxCount = 0; 3178 SCCOL nRangeStart = 0; 3179 while ( nRangeStart <= nEndCol ) 3180 { 3181 // skip hidden columns 3182 while ( nRangeStart <= nEndCol && ColHidden(nRangeStart) ) 3183 ++nRangeStart; 3184 if ( nRangeStart <= nEndCol ) 3185 { 3186 sal_uInt16 nThisCount = 0; 3187 auto colWidthIt = mpColWidth->begin() + nRangeStart; 3188 sal_uInt16 nThisWidth = *colWidthIt; 3189 SCCOL nRangeEnd = nRangeStart; 3190 while ( nRangeEnd <= nEndCol && *colWidthIt == nThisWidth ) 3191 { 3192 ++nThisCount; 3193 ++nRangeEnd; 3194 ++colWidthIt; 3195 3196 // skip hidden columns 3197 while ( nRangeEnd <= nEndCol && ColHidden(nRangeEnd) ) 3198 { 3199 ++nRangeEnd; 3200 ++colWidthIt; 3201 } 3202 } 3203 3204 if ( nThisCount > nMaxCount ) 3205 { 3206 nMaxCount = nThisCount; 3207 nMaxWidth = nThisWidth; 3208 } 3209 3210 nRangeStart = nRangeEnd; // next range 3211 } 3212 } 3213 3214 return nMaxWidth; 3215 } 3216 3217 sal_uInt16 ScTable::GetRowHeight( SCROW nRow, SCROW* pStartRow, SCROW* pEndRow, bool bHiddenAsZero ) const 3218 { 3219 SAL_WARN_IF(!ValidRow(nRow), "sc", "Invalid row number " << nRow); 3220 3221 if (ValidRow(nRow) && mpRowHeights) 3222 { 3223 if (bHiddenAsZero && RowHidden( nRow, pStartRow, pEndRow)) 3224 return 0; 3225 else 3226 { 3227 ScFlatUInt16RowSegments::RangeData aData; 3228 if (!mpRowHeights->getRangeData(nRow, aData)) 3229 { 3230 if (pStartRow) 3231 *pStartRow = nRow; 3232 if (pEndRow) 3233 *pEndRow = nRow; 3234 // TODO: What should we return in case the search fails? 3235 return 0; 3236 } 3237 3238 // If bHiddenAsZero, pStartRow and pEndRow were initialized to 3239 // boundaries of a non-hidden segment. Assume that the previous and 3240 // next segment are hidden then and limit the current height 3241 // segment. 3242 if (pStartRow) 3243 *pStartRow = (bHiddenAsZero ? std::max( *pStartRow, aData.mnRow1) : aData.mnRow1); 3244 if (pEndRow) 3245 *pEndRow = (bHiddenAsZero ? std::min( *pEndRow, aData.mnRow2) : aData.mnRow2); 3246 return aData.mnValue; 3247 } 3248 } 3249 else 3250 { 3251 if (pStartRow) 3252 *pStartRow = nRow; 3253 if (pEndRow) 3254 *pEndRow = nRow; 3255 return ScGlobal::nStdRowHeight; 3256 } 3257 } 3258 3259 sal_uLong ScTable::GetRowHeight( SCROW nStartRow, SCROW nEndRow, bool bHiddenAsZero ) const 3260 { 3261 OSL_ENSURE(ValidRow(nStartRow) && ValidRow(nEndRow),"wrong row number"); 3262 3263 if (ValidRow(nStartRow) && ValidRow(nEndRow) && mpRowHeights) 3264 { 3265 sal_uLong nHeight = 0; 3266 SCROW nRow = nStartRow; 3267 while (nRow <= nEndRow) 3268 { 3269 SCROW nLastRow = -1; 3270 if (!( ( RowHidden(nRow, nullptr, &nLastRow) ) && bHiddenAsZero ) ) 3271 { 3272 if (nLastRow > nEndRow) 3273 nLastRow = nEndRow; 3274 nHeight += mpRowHeights->getSumValue(nRow, nLastRow); 3275 } 3276 nRow = nLastRow + 1; 3277 } 3278 return nHeight; 3279 } 3280 else 3281 return (nEndRow - nStartRow + 1) * static_cast<sal_uLong>(ScGlobal::nStdRowHeight); 3282 } 3283 3284 sal_uLong ScTable::GetScaledRowHeight( SCROW nStartRow, SCROW nEndRow, double fScale, const sal_uLong* pnMaxHeight ) const 3285 { 3286 OSL_ENSURE(ValidRow(nStartRow) && ValidRow(nEndRow),"wrong row number"); 3287 3288 if (ValidRow(nStartRow) && ValidRow(nEndRow) && mpRowHeights) 3289 { 3290 sal_uLong nHeight = 0; 3291 SCROW nRow = nStartRow; 3292 while (nRow <= nEndRow) 3293 { 3294 SCROW nLastRow = -1; 3295 if (!RowHidden(nRow, nullptr, &nLastRow)) 3296 { 3297 if (nLastRow > nEndRow) 3298 nLastRow = nEndRow; 3299 3300 // #i117315# can't use getSumValue, because individual values must be rounded 3301 while (nRow <= nLastRow) 3302 { 3303 ScFlatUInt16RowSegments::RangeData aData; 3304 if (!mpRowHeights->getRangeData(nRow, aData)) 3305 return nHeight; // shouldn't happen 3306 3307 SCROW nSegmentEnd = std::min( nLastRow, aData.mnRow2 ); 3308 3309 // round-down a single height value, multiply resulting (pixel) values 3310 const sal_uLong nOneHeight = static_cast<sal_uLong>( aData.mnValue * fScale ); 3311 // sometimes scaling results in zero height 3312 if (nOneHeight) 3313 { 3314 SCROW nRowsInSegment = nSegmentEnd + 1 - nRow; 3315 if (pnMaxHeight) 3316 { 3317 nRowsInSegment = std::min(nRowsInSegment, static_cast<SCROW>(*pnMaxHeight / nOneHeight + 1)); 3318 nHeight += nOneHeight * nRowsInSegment; 3319 if (nHeight > *pnMaxHeight) 3320 return nHeight; 3321 } 3322 else 3323 nHeight += nOneHeight * nRowsInSegment; 3324 } 3325 3326 nRow = nSegmentEnd + 1; 3327 } 3328 } 3329 nRow = nLastRow + 1; 3330 } 3331 return nHeight; 3332 } 3333 else 3334 { 3335 const sal_uLong nOneHeight = static_cast<sal_uLong>(ScGlobal::nStdRowHeight * fScale); 3336 SCROW nRowsInSegment = nEndRow - nStartRow + 1; 3337 if (pnMaxHeight) 3338 { 3339 nRowsInSegment = std::min(nRowsInSegment, static_cast<SCROW>(*pnMaxHeight / nOneHeight + 1)); 3340 return nOneHeight * nRowsInSegment; 3341 } 3342 else 3343 return static_cast<sal_uLong>(nRowsInSegment * nOneHeight); 3344 } 3345 } 3346 3347 sal_uInt16 ScTable::GetOriginalHeight( SCROW nRow ) const // non-0 even if hidden 3348 { 3349 OSL_ENSURE(ValidRow(nRow),"wrong row number"); 3350 3351 if (ValidRow(nRow) && mpRowHeights) 3352 return mpRowHeights->getValue(nRow); 3353 else 3354 return ScGlobal::nStdRowHeight; 3355 } 3356 3357 // Column/Row -Flags 3358 3359 SCROW ScTable::GetHiddenRowCount( SCROW nRow ) const 3360 { 3361 if (!ValidRow(nRow)) 3362 return 0; 3363 3364 SCROW nLastRow = -1; 3365 if (!RowHidden(nRow, nullptr, &nLastRow) || !ValidRow(nLastRow)) 3366 return 0; 3367 3368 return nLastRow - nRow + 1; 3369 } 3370 3371 //TODO: combine ShowRows / DBShowRows 3372 3373 void ScTable::ShowCol(SCCOL nCol, bool bShow) 3374 { 3375 if (ValidCol(nCol)) 3376 { 3377 bool bWasVis = !ColHidden(nCol); 3378 if (bWasVis != bShow) 3379 { 3380 SetColHidden(nCol, nCol, !bShow); 3381 3382 ScChartListenerCollection* pCharts = pDocument->GetChartListenerCollection(); 3383 if ( pCharts ) 3384 pCharts->SetRangeDirty(ScRange( nCol, 0, nTab, nCol, pDocument->MaxRow(), nTab )); 3385 } 3386 } 3387 else 3388 { 3389 OSL_FAIL("Invalid column number or no flags"); 3390 } 3391 } 3392 3393 void ScTable::ShowRow(SCROW nRow, bool bShow) 3394 { 3395 if (ValidRow(nRow) && pRowFlags) 3396 { 3397 bool bWasVis = !RowHidden(nRow); 3398 if (bWasVis != bShow) 3399 { 3400 SetRowHidden(nRow, nRow, !bShow); 3401 if (bShow) 3402 SetRowFiltered(nRow, nRow, false); 3403 ScChartListenerCollection* pCharts = pDocument->GetChartListenerCollection(); 3404 if ( pCharts ) 3405 pCharts->SetRangeDirty(ScRange( 0, nRow, nTab, pDocument->MaxCol(), nRow, nTab )); 3406 3407 InvalidatePageBreaks(); 3408 } 3409 } 3410 else 3411 { 3412 OSL_FAIL("Invalid row number or no flags"); 3413 } 3414 } 3415 3416 void ScTable::DBShowRow(SCROW nRow, bool bShow) 3417 { 3418 if (ValidRow(nRow) && pRowFlags) 3419 { 3420 // Always set filter flag; unchanged when Hidden 3421 bool bChanged = SetRowHidden(nRow, nRow, !bShow); 3422 SetRowFiltered(nRow, nRow, !bShow); 3423 3424 if (bChanged) 3425 { 3426 ScChartListenerCollection* pCharts = pDocument->GetChartListenerCollection(); 3427 if ( pCharts ) 3428 pCharts->SetRangeDirty(ScRange( 0, nRow, nTab, pDocument->MaxCol(), nRow, nTab )); 3429 3430 if (pOutlineTable) 3431 UpdateOutlineRow( nRow, nRow, bShow ); 3432 3433 InvalidatePageBreaks(); 3434 } 3435 } 3436 else 3437 { 3438 OSL_FAIL("Invalid row number or no flags"); 3439 } 3440 } 3441 3442 void ScTable::DBShowRows(SCROW nRow1, SCROW nRow2, bool bShow) 3443 { 3444 SCROW nStartRow = nRow1; 3445 while (nStartRow <= nRow2) 3446 { 3447 SCROW nEndRow = -1; 3448 bool bWasVis = !RowHiddenLeaf(nStartRow, nullptr, &nEndRow); 3449 if (nEndRow > nRow2) 3450 nEndRow = nRow2; 3451 3452 bool bChanged = ( bWasVis != bShow ); 3453 3454 SetRowHidden(nStartRow, nEndRow, !bShow); 3455 SetRowFiltered(nStartRow, nEndRow, !bShow); 3456 3457 if ( bChanged ) 3458 { 3459 ScChartListenerCollection* pCharts = pDocument->GetChartListenerCollection(); 3460 if ( pCharts ) 3461 pCharts->SetRangeDirty(ScRange( 0, nStartRow, nTab, pDocument->MaxCol(), nEndRow, nTab )); 3462 } 3463 3464 nStartRow = nEndRow + 1; 3465 } 3466 3467 // #i12341# For Show/Hide rows, the outlines are updated separately from the outside. 3468 // For filtering, the changes aren't visible to the caller, so UpdateOutlineRow has 3469 // to be done here. 3470 if (pOutlineTable) 3471 UpdateOutlineRow( nRow1, nRow2, bShow ); 3472 } 3473 3474 void ScTable::ShowRows(SCROW nRow1, SCROW nRow2, bool bShow) 3475 { 3476 SCROW nStartRow = nRow1; 3477 3478 // #i116164# if there are no drawing objects within the row range, a single HeightChanged call is enough 3479 ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer(); 3480 bool bHasObjects = pDrawLayer && pDrawLayer->HasObjectsInRows( nTab, nRow1, nRow2 ); 3481 3482 while (nStartRow <= nRow2) 3483 { 3484 SCROW nEndRow = -1; 3485 bool bWasVis = !RowHiddenLeaf(nStartRow, nullptr, &nEndRow); 3486 if (nEndRow > nRow2) 3487 nEndRow = nRow2; 3488 3489 bool bChanged = ( bWasVis != bShow ); 3490 3491 SetRowHidden(nStartRow, nEndRow, !bShow); 3492 if (bShow) 3493 SetRowFiltered(nStartRow, nEndRow, false); 3494 3495 if ( bChanged ) 3496 { 3497 ScChartListenerCollection* pCharts = pDocument->GetChartListenerCollection(); 3498 if ( pCharts ) 3499 pCharts->SetRangeDirty(ScRange( 0, nStartRow, nTab, pDocument->MaxCol(), nEndRow, nTab )); 3500 3501 InvalidatePageBreaks(); 3502 } 3503 3504 nStartRow = nEndRow + 1; 3505 } 3506 3507 if ( !bHasObjects ) 3508 { 3509 // #i116164# set the flags for the whole range at once 3510 SetRowHidden(nRow1, nRow2, !bShow); 3511 if (bShow) 3512 SetRowFiltered(nRow1, nRow2, false); 3513 } 3514 } 3515 3516 bool ScTable::IsDataFiltered(SCCOL nColStart, SCROW nRowStart, SCCOL nColEnd, SCROW nRowEnd) const 3517 { 3518 for (SCROW i = nRowStart; i <= nRowEnd; ++i) 3519 { 3520 if (RowHidden(i)) 3521 return true; 3522 } 3523 for (SCCOL i = nColStart; i <= nColEnd; ++i) 3524 { 3525 if (ColHidden(i)) 3526 return true; 3527 } 3528 return false; 3529 } 3530 3531 bool ScTable::IsDataFiltered(const ScRange& rRange) const 3532 { 3533 return IsDataFiltered(rRange.aStart.Col(), rRange.aStart.Row(), 3534 rRange.aEnd.Col(), rRange.aEnd.Row()); 3535 } 3536 3537 void ScTable::SetRowFlags( SCROW nRow, CRFlags nNewFlags ) 3538 { 3539 if (ValidRow(nRow) && pRowFlags) 3540 pRowFlags->SetValue( nRow, nNewFlags); 3541 else 3542 { 3543 OSL_FAIL("Invalid row number or no flags"); 3544 } 3545 } 3546 3547 void ScTable::SetRowFlags( SCROW nStartRow, SCROW nEndRow, CRFlags nNewFlags ) 3548 { 3549 if (ValidRow(nStartRow) && ValidRow(nEndRow) && pRowFlags) 3550 pRowFlags->SetValue( nStartRow, nEndRow, nNewFlags); 3551 else 3552 { 3553 OSL_FAIL("Invalid row number(s) or no flags"); 3554 } 3555 } 3556 3557 CRFlags ScTable::GetColFlags( SCCOL nCol ) const 3558 { 3559 if (ValidCol(nCol) && mpColFlags) 3560 return mpColFlags->GetValue(nCol); 3561 else 3562 return CRFlags::NONE; 3563 } 3564 3565 CRFlags ScTable::GetRowFlags( SCROW nRow ) const 3566 { 3567 if (ValidRow(nRow) && pRowFlags) 3568 return pRowFlags->GetValue(nRow); 3569 else 3570 return CRFlags::NONE; 3571 } 3572 3573 SCROW ScTable::GetLastFlaggedRow() const 3574 { 3575 SCROW nLastFound = 0; 3576 if (pRowFlags) 3577 { 3578 SCROW nRow = pRowFlags->GetLastAnyBitAccess( CRFlags::All ); 3579 if (ValidRow(nRow)) 3580 nLastFound = nRow; 3581 } 3582 3583 if (!maRowManualBreaks.empty()) 3584 nLastFound = ::std::max(nLastFound, *maRowManualBreaks.rbegin()); 3585 3586 if (mpHiddenRows) 3587 { 3588 SCROW nRow = mpHiddenRows->findLastTrue(); 3589 if (ValidRow(nRow)) 3590 nLastFound = ::std::max(nLastFound, nRow); 3591 } 3592 3593 if (mpFilteredRows) 3594 { 3595 SCROW nRow = mpFilteredRows->findLastTrue(); 3596 if (ValidRow(nRow)) 3597 nLastFound = ::std::max(nLastFound, nRow); 3598 } 3599 3600 return nLastFound; 3601 } 3602 3603 SCCOL ScTable::GetLastChangedCol() const 3604 { 3605 if ( !mpColFlags ) 3606 return 0; 3607 3608 SCCOL nLastFound = 0; 3609 const auto nColSize = aCol.size(); 3610 auto colWidthIt = mpColWidth->begin() + 1; 3611 for (SCCOL nCol = 1; nCol < nColSize; (++nCol < nColSize) ? ++colWidthIt : (void)false) 3612 if ((mpColFlags->GetValue(nCol) & CRFlags::All) || (*colWidthIt != STD_COL_WIDTH)) 3613 nLastFound = nCol; 3614 3615 return nLastFound; 3616 } 3617 3618 SCROW ScTable::GetLastChangedRow() const 3619 { 3620 if ( !pRowFlags ) 3621 return 0; 3622 3623 SCROW nLastFlags = GetLastFlaggedRow(); 3624 3625 // Find the last row position where the height is NOT the standard row 3626 // height. 3627 // KOHEI: Test this to make sure it does what it's supposed to. 3628 SCROW nLastHeight = mpRowHeights->findLastTrue(ScGlobal::nStdRowHeight); 3629 if (!ValidRow(nLastHeight)) 3630 nLastHeight = 0; 3631 3632 return std::max( nLastFlags, nLastHeight); 3633 } 3634 3635 bool ScTable::UpdateOutlineCol( SCCOL nStartCol, SCCOL nEndCol, bool bShow ) 3636 { 3637 if (pOutlineTable && mpColFlags) 3638 { 3639 return pOutlineTable->GetColArray().ManualAction( nStartCol, nEndCol, bShow, *this, true ); 3640 } 3641 else 3642 return false; 3643 } 3644 3645 bool ScTable::UpdateOutlineRow( SCROW nStartRow, SCROW nEndRow, bool bShow ) 3646 { 3647 if (pOutlineTable && pRowFlags) 3648 return pOutlineTable->GetRowArray().ManualAction( nStartRow, nEndRow, bShow, *this, false ); 3649 else 3650 return false; 3651 } 3652 3653 void ScTable::ExtendHidden( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2 ) 3654 { 3655 // Column-wise expansion 3656 3657 while (rX1 > 0 && ColHidden(rX1-1)) 3658 --rX1; 3659 3660 while (rX2 < pDocument->MaxCol() && ColHidden(rX2+1)) 3661 ++rX2; 3662 3663 // Row-wise expansion 3664 3665 if (rY1 > 0) 3666 { 3667 ScFlatBoolRowSegments::RangeData aData; 3668 if (mpHiddenRows->getRangeData(rY1-1, aData) && aData.mbValue) 3669 { 3670 SCROW nStartRow = aData.mnRow1; 3671 if (ValidRow(nStartRow)) 3672 rY1 = nStartRow; 3673 } 3674 } 3675 if (rY2 < pDocument->MaxRow()) 3676 { 3677 SCROW nEndRow = -1; 3678 if (RowHidden(rY2+1, nullptr, &nEndRow) && ValidRow(nEndRow)) 3679 rY2 = nEndRow; 3680 } 3681 } 3682 3683 void ScTable::StripHidden( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2 ) 3684 { 3685 while ( rX2>rX1 && ColHidden(rX2) ) 3686 --rX2; 3687 while ( rX2>rX1 && ColHidden(rX1) ) 3688 ++rX1; 3689 3690 if (rY1 < rY2) 3691 { 3692 ScFlatBoolRowSegments::RangeData aData; 3693 if (mpHiddenRows->getRangeData(rY2, aData) && aData.mbValue) 3694 { 3695 SCROW nStartRow = aData.mnRow1; 3696 if (ValidRow(nStartRow) && nStartRow >= rY1) 3697 rY2 = nStartRow; 3698 } 3699 } 3700 3701 if (rY1 < rY2) 3702 { 3703 SCROW nEndRow = -1; 3704 if (RowHidden(rY1, nullptr, &nEndRow) && ValidRow(nEndRow) && nEndRow <= rY2) 3705 rY1 = nEndRow; 3706 } 3707 } 3708 3709 // Auto-Outline 3710 3711 template< typename T > 3712 static short DiffSign( T a, T b ) 3713 { 3714 return (a<b) ? -1 : 3715 (a>b) ? 1 : 0; 3716 } 3717 3718 namespace { 3719 3720 class OutlineArrayFinder 3721 { 3722 ScRange maRef; 3723 SCCOL mnCol; 3724 SCTAB mnTab; 3725 ScOutlineArray* mpArray; 3726 bool mbSizeChanged; 3727 3728 public: 3729 OutlineArrayFinder(const ScRange& rRef, SCCOL nCol, SCTAB nTab, ScOutlineArray* pArray, bool bSizeChanged) : 3730 maRef(rRef), mnCol(nCol), mnTab(nTab), mpArray(pArray), 3731 mbSizeChanged(bSizeChanged) {} 3732 3733 bool operator() (size_t nRow, const ScFormulaCell* pCell) 3734 { 3735 SCROW nRow2 = static_cast<SCROW>(nRow); 3736 3737 if (!pCell->HasRefListExpressibleAsOneReference(maRef)) 3738 return false; 3739 3740 if (maRef.aStart.Row() != nRow2 || maRef.aEnd.Row() != nRow2 || 3741 maRef.aStart.Tab() != mnTab || maRef.aEnd.Tab() != mnTab) 3742 return false; 3743 3744 if (DiffSign(maRef.aStart.Col(), mnCol) != DiffSign(maRef.aEnd.Col(), mnCol)) 3745 return false; 3746 3747 return mpArray->Insert(maRef.aStart.Col(), maRef.aEnd.Col(), mbSizeChanged); 3748 } 3749 }; 3750 3751 } 3752 3753 void ScTable::DoAutoOutline( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) 3754 { 3755 typedef mdds::flat_segment_tree<SCROW, bool> UsedRowsType; 3756 3757 bool bSizeChanged = false; 3758 3759 SCCOL nCol; 3760 SCROW nRow; 3761 bool bFound; 3762 ScRange aRef; 3763 3764 nEndCol = ClampToAllocatedColumns(nEndCol); 3765 3766 StartOutlineTable(); 3767 3768 // Rows 3769 3770 UsedRowsType aUsed(0, pDocument->MaxRow()+1, false); 3771 for (nCol=nStartCol; nCol<=nEndCol; nCol++) 3772 aCol[nCol].FindUsed(nStartRow, nEndRow, aUsed); 3773 aUsed.build_tree(); 3774 3775 ScOutlineArray& rRowArray = pOutlineTable->GetRowArray(); 3776 for (nRow=nStartRow; nRow<=nEndRow; nRow++) 3777 { 3778 bool bUsed = false; 3779 SCROW nLastRow = nRow; 3780 aUsed.search_tree(nRow, bUsed, nullptr, &nLastRow); 3781 if (!bUsed) 3782 { 3783 nRow = nLastRow; 3784 continue; 3785 } 3786 3787 bFound = false; 3788 for (nCol=nStartCol; nCol<=nEndCol && !bFound; nCol++) 3789 { 3790 ScRefCellValue aCell = aCol[nCol].GetCellValue(nRow); 3791 3792 if (aCell.meType != CELLTYPE_FORMULA) 3793 continue; 3794 3795 if (!aCell.mpFormula->HasRefListExpressibleAsOneReference(aRef)) 3796 continue; 3797 3798 if ( aRef.aStart.Col() == nCol && aRef.aEnd.Col() == nCol && 3799 aRef.aStart.Tab() == nTab && aRef.aEnd.Tab() == nTab && 3800 DiffSign( aRef.aStart.Row(), nRow ) == 3801 DiffSign( aRef.aEnd.Row(), nRow ) ) 3802 { 3803 if (rRowArray.Insert( aRef.aStart.Row(), aRef.aEnd.Row(), bSizeChanged )) 3804 { 3805 bFound = true; 3806 } 3807 } 3808 } 3809 } 3810 3811 // Column 3812 ScOutlineArray& rColArray = pOutlineTable->GetColArray(); 3813 for (nCol=nStartCol; nCol<=nEndCol; nCol++) 3814 { 3815 if (aCol[nCol].IsEmptyData()) 3816 continue; 3817 3818 OutlineArrayFinder aFunc(aRef, nCol, nTab, &rColArray, bSizeChanged); 3819 sc::FindFormula(aCol[nCol].maCells, nStartRow, nEndRow, aFunc); 3820 } 3821 } 3822 3823 // CopyData - for Query in other range 3824 3825 void ScTable::CopyData( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, 3826 SCCOL nDestCol, SCROW nDestRow, SCTAB nDestTab ) 3827 { 3828 //TODO: if used for multiple rows, optimize after columns! 3829 3830 ScAddress aSrc( nStartCol, nStartRow, nTab ); 3831 ScAddress aDest( nDestCol, nDestRow, nDestTab ); 3832 ScRange aRange( aSrc, aDest ); 3833 bool bThisTab = ( nDestTab == nTab ); 3834 SCROW nDestY = nDestRow; 3835 for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++) 3836 { 3837 aSrc.SetRow( nRow ); 3838 aDest.SetRow( nDestY ); 3839 SCCOL nDestX = nDestCol; 3840 for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++) 3841 { 3842 aSrc.SetCol( nCol ); 3843 aDest.SetCol( nDestX ); 3844 ScCellValue aCell; 3845 aCell.assign(*pDocument, ScAddress(nCol, nRow, nTab)); 3846 3847 if (aCell.meType == CELLTYPE_FORMULA) 3848 { 3849 sc::RefUpdateContext aCxt(*pDocument); 3850 aCxt.meMode = URM_COPY; 3851 aCxt.maRange = aRange; 3852 aCxt.mnColDelta = nDestCol - nStartCol; 3853 aCxt.mnRowDelta = nDestRow - nStartRow; 3854 aCxt.mnTabDelta = nDestTab - nTab; 3855 aCell.mpFormula->UpdateReference(aCxt); 3856 aCell.mpFormula->aPos = aDest; 3857 } 3858 3859 if (bThisTab) 3860 { 3861 aCell.release(aCol[nDestX], nDestY); 3862 SetPattern( nDestX, nDestY, *GetPattern( nCol, nRow ) ); 3863 } 3864 else 3865 { 3866 aCell.release(*pDocument, aDest); 3867 pDocument->SetPattern( aDest, *GetPattern( nCol, nRow ) ); 3868 } 3869 3870 ++nDestX; 3871 } 3872 ++nDestY; 3873 } 3874 } 3875 3876 bool ScTable::RefVisible(const ScFormulaCell* pCell) 3877 { 3878 ScRange aRef; 3879 3880 if (pCell->HasOneReference(aRef)) 3881 { 3882 if (aRef.aStart.Col()==aRef.aEnd.Col() && aRef.aStart.Tab()==aRef.aEnd.Tab()) 3883 { 3884 SCROW nEndRow; 3885 if (!RowFiltered(aRef.aStart.Row(), nullptr, &nEndRow)) 3886 // row not filtered. 3887 nEndRow = ::std::numeric_limits<SCROW>::max(); 3888 3889 if (!ValidRow(nEndRow) || nEndRow < aRef.aEnd.Row()) 3890 return true; // at least partly visible 3891 return false; // completely invisible 3892 } 3893 } 3894 3895 return true; // somehow different 3896 } 3897 3898 void ScTable::GetUpperCellString(SCCOL nCol, SCROW nRow, OUString& rStr) 3899 { 3900 GetInputString(nCol, nRow, rStr); 3901 rStr = ScGlobal::getCharClassPtr()->uppercase(rStr.trim()); 3902 } 3903 3904 // Calculate the size of the sheet and set the size on DrawPage 3905 3906 void ScTable::SetDrawPageSize(bool bResetStreamValid, bool bUpdateNoteCaptionPos) 3907 { 3908 ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer(); 3909 if( pDrawLayer ) 3910 { 3911 double fValX = GetColOffset( pDocument->MaxCol() + 1 ) * HMM_PER_TWIPS; 3912 double fValY = GetRowOffset( pDocument->MaxRow() + 1 ) * HMM_PER_TWIPS; 3913 const long nMax = ::std::numeric_limits<long>::max(); 3914 // #i113884# Avoid int32 overflow with possible negative results than can cause bad effects. 3915 // If the draw page size is smaller than all rows, only the bottom of the sheet is affected. 3916 long x = ( fValX > static_cast<double>(nMax) ) ? nMax : static_cast<long>(fValX); 3917 long y = ( fValY > static_cast<double>(nMax) ) ? nMax : static_cast<long>(fValY); 3918 3919 if ( IsLayoutRTL() ) // IsNegativePage 3920 x = -x; 3921 3922 pDrawLayer->SetPageSize( static_cast<sal_uInt16>(nTab), Size( x, y ), bUpdateNoteCaptionPos ); 3923 } 3924 3925 // #i102616# actions that modify the draw page size count as sheet modification 3926 // (exception: InitDrawLayer) 3927 if (bResetStreamValid) 3928 SetStreamValid(false); 3929 } 3930 3931 void ScTable::SetRangeName(std::unique_ptr<ScRangeName> pNew) 3932 { 3933 mpRangeName = std::move(pNew); 3934 3935 //fdo#39792: mark stream as invalid, otherwise new ScRangeName will not be written to file 3936 SetStreamValid(false); 3937 } 3938 3939 ScRangeName* ScTable::GetRangeName() const 3940 { 3941 if (!mpRangeName) 3942 mpRangeName.reset(new ScRangeName); 3943 return mpRangeName.get(); 3944 } 3945 3946 sal_uLong ScTable::GetRowOffset( SCROW nRow, bool bHiddenAsZero ) const 3947 { 3948 sal_uLong n = 0; 3949 if ( mpHiddenRows && mpRowHeights ) 3950 { 3951 if (nRow == 0) 3952 return 0; 3953 else if (nRow == 1) 3954 return GetRowHeight(0, nullptr, nullptr, bHiddenAsZero ); 3955 3956 n = GetTotalRowHeight(0, nRow-1, bHiddenAsZero); 3957 #if OSL_DEBUG_LEVEL > 0 3958 if (n == ::std::numeric_limits<unsigned long>::max()) 3959 OSL_FAIL("ScTable::GetRowOffset: row heights overflow"); 3960 #endif 3961 } 3962 else 3963 { 3964 OSL_FAIL("GetRowOffset: Data missing"); 3965 } 3966 return n; 3967 } 3968 3969 SCROW ScTable::GetRowForHeight(sal_uLong nHeight) const 3970 { 3971 sal_uLong nSum = 0; 3972 3973 ScFlatBoolRowSegments::RangeData aData; 3974 3975 ScFlatUInt16RowSegments::RangeData aRowHeightRange; 3976 aRowHeightRange.mnRow2 = -1; 3977 aRowHeightRange.mnValue = 0; // silence MSVC C4701 3978 3979 for (SCROW nRow = 0; nRow <= pDocument->MaxRow(); ++nRow) 3980 { 3981 if (!mpHiddenRows->getRangeData(nRow, aData)) 3982 // Failed to fetch the range data for whatever reason. 3983 break; 3984 3985 if (aData.mbValue) 3986 { 3987 // This row is hidden. Skip ahead all hidden rows. 3988 nRow = aData.mnRow2; 3989 continue; 3990 } 3991 3992 if (aRowHeightRange.mnRow2 < nRow) 3993 { 3994 if (!mpRowHeights->getRangeData(nRow, aRowHeightRange)) 3995 // Failed to fetch the range data for whatever reason. 3996 break; 3997 } 3998 3999 nSum += aRowHeightRange.mnValue; 4000 4001 if (nSum > nHeight) 4002 { 4003 if (nRow >= pDocument->MaxRow()) 4004 return pDocument->MaxRow(); 4005 4006 // Find the next visible row. 4007 ++nRow; 4008 4009 if (!mpHiddenRows->getRangeData(nRow, aData)) 4010 // Failed to fetch the range data for whatever reason. 4011 break; 4012 4013 if (aData.mbValue) 4014 // These rows are hidden. 4015 nRow = aData.mnRow2 + 1; 4016 4017 return nRow <= pDocument->MaxRow() ? nRow : pDocument->MaxRow(); 4018 } 4019 } 4020 return -1; 4021 } 4022 4023 sal_uLong ScTable::GetColOffset( SCCOL nCol, bool bHiddenAsZero ) const 4024 { 4025 sal_uLong n = 0; 4026 if ( mpColWidth ) 4027 { 4028 auto colWidthIt = mpColWidth->begin(); 4029 for (SCCOL i = 0; i < nCol; (++i < nCol) ? ++colWidthIt : (void)false) 4030 if (!( bHiddenAsZero && ColHidden(i) )) 4031 n += *colWidthIt; 4032 } 4033 else 4034 { 4035 OSL_FAIL("GetColumnOffset: Data missing"); 4036 } 4037 return n; 4038 } 4039 4040 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 4041
