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 <comphelper/processfactory.hxx> 21 #include <comphelper/random.hxx> 22 #include <svl/numformat.hxx> 23 #include <svl/zforlist.hxx> 24 #include <svl/zformat.hxx> 25 #include <unotools/collatorwrapper.hxx> 26 #include <stdlib.h> 27 #include <com/sun/star/i18n/KParseTokens.hpp> 28 #include <com/sun/star/i18n/KParseType.hpp> 29 #include <sal/log.hxx> 30 #include <osl/diagnose.h> 31 32 #include <refdata.hxx> 33 #include <table.hxx> 34 #include <scitems.hxx> 35 #include <formulacell.hxx> 36 #include <document.hxx> 37 #include <globstr.hrc> 38 #include <scresid.hxx> 39 #include <global.hxx> 40 #include <stlpool.hxx> 41 #include <patattr.hxx> 42 #include <subtotal.hxx> 43 #include <markdata.hxx> 44 #include <rangelst.hxx> 45 #include <userlist.hxx> 46 #include <progress.hxx> 47 #include <queryparam.hxx> 48 #include <queryentry.hxx> 49 #include <subtotalparam.hxx> 50 #include <docpool.hxx> 51 #include <cellvalue.hxx> 52 #include <tokenarray.hxx> 53 #include <mtvcellfunc.hxx> 54 #include <columnspanset.hxx> 55 #include <fstalgorithm.hxx> 56 #include <listenercontext.hxx> 57 #include <sharedformula.hxx> 58 #include <stlsheet.hxx> 59 #include <refhint.hxx> 60 #include <listenerquery.hxx> 61 #include <bcaslot.hxx> 62 #include <reordermap.hxx> 63 #include <drwlayer.hxx> 64 #include <queryevaluator.hxx> 65 #include <scopetools.hxx> 66 67 #include <svl/sharedstringpool.hxx> 68 69 #include <memory> 70 #include <set> 71 #include <unordered_set> 72 #include <vector> 73 #include <mdds/flat_segment_tree.hpp> 74 75 using namespace ::com::sun::star; 76 77 namespace naturalsort { 78 79 using namespace ::com::sun::star::i18n; 80 81 /** Splits a given string into three parts: the prefix, number string, and 82 the suffix. 83 84 @param sWhole 85 Original string to be split into pieces 86 87 @param sPrefix 88 Prefix string that consists of the part before the first number token. 89 If no number was found, sPrefix is unchanged. 90 91 @param sSuffix 92 String after the last number token. This may still contain number strings. 93 If no number was found, sSuffix is unchanged. 94 95 @param fNum 96 Number converted from the middle number string 97 If no number was found, fNum is unchanged. 98 99 @return Returns TRUE if a numeral element is found in a given string, or 100 FALSE if no numeral element is found. 101 */ 102 static bool SplitString( const OUString &sWhole, 103 OUString &sPrefix, OUString &sSuffix, double &fNum ) 104 { 105 // Get prefix element, search for any digit and stop. 106 sal_Int32 nPos = 0; 107 while (nPos < sWhole.getLength()) 108 { 109 const sal_uInt16 nType = ScGlobal::getCharClass().getCharacterType( sWhole, nPos); 110 if (nType & KCharacterType::DIGIT) 111 break; 112 sWhole.iterateCodePoints( &nPos ); 113 } 114 115 // Return FALSE if no numeral element is found 116 if ( nPos == sWhole.getLength() ) 117 return false; 118 119 // Get numeral element 120 const OUString& sUser = ScGlobal::getLocaleData().getNumDecimalSep(); 121 ParseResult aPRNum = ScGlobal::getCharClass().parsePredefinedToken( 122 KParseType::ANY_NUMBER, sWhole, nPos, 123 KParseTokens::ANY_NUMBER, "", KParseTokens::ANY_NUMBER, sUser ); 124 125 if ( aPRNum.EndPos == nPos ) 126 { 127 SAL_WARN("sc.core","naturalsort::SplitString - digit found but no number parsed, pos " << 128 nPos << " : " << sWhole); 129 return false; 130 } 131 132 sPrefix = sWhole.copy( 0, nPos ); 133 fNum = aPRNum.Value; 134 sSuffix = sWhole.copy( aPRNum.EndPos ); 135 136 return true; 137 } 138 139 /** Naturally compares two given strings. 140 141 This is the main function that should be called externally. It returns 142 either 1, 0, or -1 depending on the comparison result of given two strings. 143 144 @param sInput1 145 Input string 1 146 147 @param sInput2 148 Input string 2 149 150 @param bCaseSens 151 Boolean value for case sensitivity 152 153 @param pData 154 Pointer to user defined sort list 155 156 @param pCW 157 Pointer to collator wrapper for normal string comparison 158 159 @return Returns 1 if sInput1 is greater, 0 if sInput1 == sInput2, and -1 if 160 sInput2 is greater. 161 */ 162 static short Compare( const OUString &sInput1, const OUString &sInput2, 163 const bool bCaseSens, const ScUserListData* pData, const CollatorWrapper *pCW ) 164 { 165 OUString sStr1( sInput1 ), sStr2( sInput2 ), sPre1, sSuf1, sPre2, sSuf2; 166 167 do 168 { 169 double nNum1, nNum2; 170 bool bNumFound1 = SplitString( sStr1, sPre1, sSuf1, nNum1 ); 171 bool bNumFound2 = SplitString( sStr2, sPre2, sSuf2, nNum2 ); 172 173 short nPreRes; // Prefix comparison result 174 if ( pData ) 175 { 176 if ( bCaseSens ) 177 { 178 if ( !bNumFound1 || !bNumFound2 ) 179 return static_cast<short>(pData->Compare( sStr1, sStr2 )); 180 else 181 nPreRes = pData->Compare( sPre1, sPre2 ); 182 } 183 else 184 { 185 if ( !bNumFound1 || !bNumFound2 ) 186 return static_cast<short>(pData->ICompare( sStr1, sStr2 )); 187 else 188 nPreRes = pData->ICompare( sPre1, sPre2 ); 189 } 190 } 191 else 192 { 193 if ( !bNumFound1 || !bNumFound2 ) 194 return static_cast<short>(pCW->compareString( sStr1, sStr2 )); 195 else 196 nPreRes = static_cast<short>(pCW->compareString( sPre1, sPre2 )); 197 } 198 199 // Prefix strings differ. Return immediately. 200 if ( nPreRes != 0 ) return nPreRes; 201 202 if ( nNum1 != nNum2 ) 203 { 204 if ( nNum1 < nNum2 ) return -1; 205 return (nNum1 > nNum2) ? 1 : 0; 206 } 207 208 // The prefix and the first numerical elements are equal, but the suffix 209 // strings may still differ. Stay in the loop. 210 211 sStr1 = sSuf1; 212 sStr2 = sSuf2; 213 214 } while (true); 215 216 return 0; 217 } 218 219 } 220 221 namespace { 222 223 struct ScSortInfo final 224 { 225 ScRefCellValue maCell; 226 SCCOLROW nOrg; 227 }; 228 229 } 230 231 class ScSortInfoArray 232 { 233 public: 234 235 struct Cell 236 { 237 ScRefCellValue maCell; 238 const sc::CellTextAttr* mpAttr; 239 const ScPostIt* mpNote; 240 std::vector<SdrObject*> maDrawObjects; 241 const ScPatternAttr* mpPattern; 242 243 Cell() : mpAttr(nullptr), mpNote(nullptr), mpPattern(nullptr) {} 244 }; 245 246 struct Row 247 { 248 std::vector<Cell> maCells; 249 250 bool mbHidden:1; 251 bool mbFiltered:1; 252 253 explicit Row( size_t nColSize ) : maCells(nColSize, Cell()), mbHidden(false), mbFiltered(false) {} 254 }; 255 256 typedef std::vector<Row> RowsType; 257 258 private: 259 std::unique_ptr<RowsType> mpRows; /// row-wise data table for sort by row operation. 260 261 std::vector<std::unique_ptr<ScSortInfo[]>> mvppInfo; 262 SCCOLROW nStart; 263 SCCOLROW mnLastIndex; /// index of last non-empty cell position. 264 265 std::vector<SCCOLROW> maOrderIndices; 266 bool mbKeepQuery; 267 bool mbUpdateRefs; 268 269 public: 270 ScSortInfoArray(const ScSortInfoArray&) = delete; 271 const ScSortInfoArray& operator=(const ScSortInfoArray&) = delete; 272 273 ScSortInfoArray( sal_uInt16 nSorts, SCCOLROW nInd1, SCCOLROW nInd2 ) : 274 mvppInfo(nSorts), 275 nStart( nInd1 ), 276 mnLastIndex(nInd2), 277 mbKeepQuery(false), 278 mbUpdateRefs(false) 279 { 280 SCSIZE nCount( nInd2 - nInd1 + 1 ); 281 if (nSorts) 282 { 283 for ( sal_uInt16 nSort = 0; nSort < nSorts; nSort++ ) 284 { 285 mvppInfo[nSort].reset(new ScSortInfo[nCount]); 286 } 287 } 288 289 for (size_t i = 0; i < nCount; ++i) 290 maOrderIndices.push_back(i+nStart); 291 } 292 293 void SetKeepQuery( bool b ) { mbKeepQuery = b; } 294 295 bool IsKeepQuery() const { return mbKeepQuery; } 296 297 void SetUpdateRefs( bool b ) { mbUpdateRefs = b; } 298 299 bool IsUpdateRefs() const { return mbUpdateRefs; } 300 301 /** 302 * Call this only during normal sorting, not from reordering. 303 */ 304 std::unique_ptr<ScSortInfo[]> const & GetFirstArray() const 305 { 306 return mvppInfo[0]; 307 } 308 309 /** 310 * Call this only during normal sorting, not from reordering. 311 */ 312 ScSortInfo & Get( sal_uInt16 nSort, SCCOLROW nInd ) 313 { 314 return mvppInfo[nSort][ nInd - nStart ]; 315 } 316 317 /** 318 * Call this only during normal sorting, not from reordering. 319 */ 320 void Swap( SCCOLROW nInd1, SCCOLROW nInd2 ) 321 { 322 if (nInd1 == nInd2) // avoid self-move-assign 323 return; 324 SCSIZE n1 = static_cast<SCSIZE>(nInd1 - nStart); 325 SCSIZE n2 = static_cast<SCSIZE>(nInd2 - nStart); 326 for ( sal_uInt16 nSort = 0; nSort < static_cast<sal_uInt16>(mvppInfo.size()); nSort++ ) 327 { 328 auto & ppInfo = mvppInfo[nSort]; 329 std::swap(ppInfo[n1], ppInfo[n2]); 330 } 331 332 std::swap(maOrderIndices[n1], maOrderIndices[n2]); 333 334 if (mpRows) 335 { 336 // Swap rows in data table. 337 RowsType& rRows = *mpRows; 338 std::swap(rRows[n1], rRows[n2]); 339 } 340 } 341 342 void SetOrderIndices( std::vector<SCCOLROW>&& rIndices ) 343 { 344 maOrderIndices = std::move(rIndices); 345 } 346 347 /** 348 * @param rIndices indices are actual row positions on the sheet, not an 349 * offset from the top row. 350 */ 351 void ReorderByRow( const std::vector<SCCOLROW>& rIndices ) 352 { 353 if (!mpRows) 354 return; 355 356 RowsType& rRows = *mpRows; 357 358 std::vector<SCCOLROW> aOrderIndices2; 359 aOrderIndices2.reserve(rIndices.size()); 360 361 RowsType aRows2; 362 aRows2.reserve(rRows.size()); 363 364 for (const auto& rIndex : rIndices) 365 { 366 size_t nPos = rIndex - nStart; // switch to an offset to top row. 367 aRows2.push_back(rRows[nPos]); 368 aOrderIndices2.push_back(maOrderIndices[nPos]); 369 } 370 371 rRows.swap(aRows2); 372 maOrderIndices.swap(aOrderIndices2); 373 } 374 375 sal_uInt16 GetUsedSorts() const { return mvppInfo.size(); } 376 377 SCCOLROW GetStart() const { return nStart; } 378 SCCOLROW GetLast() const { return mnLastIndex; } 379 380 const std::vector<SCCOLROW>& GetOrderIndices() const { return maOrderIndices; } 381 382 RowsType& InitDataRows( size_t nRowSize, size_t nColSize ) 383 { 384 mpRows.reset(new RowsType); 385 mpRows->resize(nRowSize, Row(nColSize)); 386 return *mpRows; 387 } 388 389 RowsType* GetDataRows() 390 { 391 return mpRows.get(); 392 } 393 }; 394 395 // Assume that we can handle 512MB, which with a ~100 bytes 396 // ScSortInfoArray::Cell element for 500MB are about 5 million cells plus 397 // overhead in one chunk. 398 constexpr sal_Int32 kSortCellsChunk = 500 * 1024 * 1024 / sizeof(ScSortInfoArray::Cell); 399 400 namespace { 401 402 void initDataRows( 403 ScSortInfoArray& rArray, ScTable& rTab, ScColContainer& rCols, 404 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, 405 bool bHiddenFiltered, bool bPattern, bool bCellNotes, bool bCellDrawObjects, bool bOnlyDataAreaExtras ) 406 { 407 // Fill row-wise data table. 408 ScSortInfoArray::RowsType& rRows = rArray.InitDataRows(nRow2-nRow1+1, nCol2-nCol1+1); 409 410 const std::vector<SCCOLROW>& rOrderIndices = rArray.GetOrderIndices(); 411 assert(!bOnlyDataAreaExtras || (rOrderIndices.size() == static_cast<size_t>(nRow2 - nRow1 + 1) 412 && nRow1 == rArray.GetStart())); 413 414 ScDrawLayer* pDrawLayer = (bCellDrawObjects ? rTab.GetDoc().GetDrawLayer() : nullptr); 415 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) 416 { 417 ScColumn& rCol = rCols[nCol]; 418 419 // Skip reordering of cell formats if the whole span is on the same pattern entry. 420 bool bUniformPattern = rCol.GetPatternCount(nRow1, nRow2) < 2u; 421 422 sc::ColumnBlockConstPosition aBlockPos; 423 rCol.InitBlockPosition(aBlockPos); 424 std::map<SCROW, std::vector<SdrObject*>> aRowDrawObjects; 425 if (pDrawLayer) 426 aRowDrawObjects = pDrawLayer->GetObjectsAnchoredToRange(rTab.GetTab(), nCol, nRow1, nRow2); 427 428 for (SCROW nR = nRow1; nR <= nRow2; ++nR) 429 { 430 const SCROW nRow = (bOnlyDataAreaExtras ? rOrderIndices[nR - rArray.GetStart()] : nR); 431 ScSortInfoArray::Row& rRow = rRows[nR-nRow1]; 432 ScSortInfoArray::Cell& rCell = rRow.maCells[nCol-nCol1]; 433 if (!bOnlyDataAreaExtras) 434 { 435 rCell.maCell = rCol.GetCellValue(aBlockPos, nRow); 436 rCell.mpAttr = rCol.GetCellTextAttr(aBlockPos, nRow); 437 } 438 if (bCellNotes) 439 rCell.mpNote = rCol.GetCellNote(aBlockPos, nRow); 440 if (pDrawLayer) 441 rCell.maDrawObjects = aRowDrawObjects[nRow]; 442 443 if (!bUniformPattern && bPattern) 444 rCell.mpPattern = rCol.GetPattern(nRow); 445 } 446 } 447 448 if (!bOnlyDataAreaExtras && bHiddenFiltered) 449 { 450 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) 451 { 452 ScSortInfoArray::Row& rRow = rRows[nRow-nRow1]; 453 rRow.mbHidden = rTab.RowHidden(nRow); 454 rRow.mbFiltered = rTab.RowFiltered(nRow); 455 } 456 } 457 } 458 459 } 460 461 std::unique_ptr<ScSortInfoArray> ScTable::CreateSortInfoArray( const sc::ReorderParam& rParam ) 462 { 463 std::unique_ptr<ScSortInfoArray> pArray; 464 465 if (rParam.mbByRow) 466 { 467 // Create a sort info array with just the data table. 468 SCROW nRow1 = rParam.maSortRange.aStart.Row(); 469 SCROW nRow2 = rParam.maSortRange.aEnd.Row(); 470 SCCOL nCol1 = rParam.maSortRange.aStart.Col(); 471 SCCOL nCol2 = rParam.maSortRange.aEnd.Col(); 472 473 pArray.reset(new ScSortInfoArray(0, nRow1, nRow2)); 474 pArray->SetKeepQuery(rParam.mbHiddenFiltered); 475 pArray->SetUpdateRefs(rParam.mbUpdateRefs); 476 477 CreateColumnIfNotExists(nCol2); 478 initDataRows( *pArray, *this, aCol, nCol1, nRow1, nCol2, nRow2, rParam.mbHiddenFiltered, 479 rParam.maDataAreaExtras.mbCellFormats, true, true, false); 480 } 481 else 482 { 483 SCCOLROW nCol1 = rParam.maSortRange.aStart.Col(); 484 SCCOLROW nCol2 = rParam.maSortRange.aEnd.Col(); 485 486 pArray.reset(new ScSortInfoArray(0, nCol1, nCol2)); 487 pArray->SetKeepQuery(rParam.mbHiddenFiltered); 488 pArray->SetUpdateRefs(rParam.mbUpdateRefs); 489 } 490 491 return pArray; 492 } 493 494 std::unique_ptr<ScSortInfoArray> ScTable::CreateSortInfoArray( 495 const ScSortParam& rSortParam, SCCOLROW nInd1, SCCOLROW nInd2, 496 bool bKeepQuery, bool bUpdateRefs ) 497 { 498 sal_uInt16 nUsedSorts = 1; 499 while ( nUsedSorts < rSortParam.GetSortKeyCount() && rSortParam.maKeyState[nUsedSorts].bDoSort ) 500 nUsedSorts++; 501 std::unique_ptr<ScSortInfoArray> pArray(new ScSortInfoArray( nUsedSorts, nInd1, nInd2 )); 502 pArray->SetKeepQuery(bKeepQuery); 503 pArray->SetUpdateRefs(bUpdateRefs); 504 505 if ( rSortParam.bByRow ) 506 { 507 for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ ) 508 { 509 SCCOL nCol = static_cast<SCCOL>(rSortParam.maKeyState[nSort].nField); 510 ScColumn* pCol = &aCol[nCol]; 511 sc::ColumnBlockConstPosition aBlockPos; 512 pCol->InitBlockPosition(aBlockPos); 513 for ( SCROW nRow = nInd1; nRow <= nInd2; nRow++ ) 514 { 515 ScSortInfo & rInfo = pArray->Get( nSort, nRow ); 516 rInfo.maCell = pCol->GetCellValue(aBlockPos, nRow); 517 rInfo.nOrg = nRow; 518 } 519 } 520 521 CreateColumnIfNotExists(rSortParam.nCol2); 522 initDataRows( *pArray, *this, aCol, rSortParam.nCol1, nInd1, rSortParam.nCol2, nInd2, bKeepQuery, 523 rSortParam.aDataAreaExtras.mbCellFormats, true, true, false); 524 } 525 else 526 { 527 for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ ) 528 { 529 SCROW nRow = rSortParam.maKeyState[nSort].nField; 530 for ( SCCOL nCol = static_cast<SCCOL>(nInd1); 531 nCol <= static_cast<SCCOL>(nInd2); nCol++ ) 532 { 533 ScSortInfo & rInfo = pArray->Get( nSort, nCol ); 534 rInfo.maCell = GetCellValue(nCol, nRow); 535 rInfo.nOrg = nCol; 536 } 537 } 538 } 539 return pArray; 540 } 541 542 namespace { 543 544 struct SortedColumn 545 { 546 typedef mdds::flat_segment_tree<SCROW, const ScPatternAttr*> PatRangeType; 547 548 sc::CellStoreType maCells; 549 sc::CellTextAttrStoreType maCellTextAttrs; 550 sc::BroadcasterStoreType maBroadcasters; 551 sc::CellNoteStoreType maCellNotes; 552 std::vector<std::vector<SdrObject*>> maCellDrawObjects; 553 554 PatRangeType maPatterns; 555 PatRangeType::const_iterator miPatternPos; 556 557 SortedColumn(const SortedColumn&) = delete; 558 const SortedColumn operator=(const SortedColumn&) = delete; 559 560 explicit SortedColumn( size_t nTopEmptyRows, const ScSheetLimits& rSheetLimits ) : 561 maCells(nTopEmptyRows), 562 maCellTextAttrs(nTopEmptyRows), 563 maBroadcasters(nTopEmptyRows), 564 maCellNotes(nTopEmptyRows), 565 maPatterns(0, rSheetLimits.GetMaxRowCount(), nullptr), 566 miPatternPos(maPatterns.begin()) {} 567 568 void setPattern( SCROW nRow, const ScPatternAttr* pPat ) 569 { 570 miPatternPos = maPatterns.insert(miPatternPos, nRow, nRow+1, pPat).first; 571 } 572 }; 573 574 struct SortedRowFlags 575 { 576 typedef mdds::flat_segment_tree<SCROW,bool> FlagsType; 577 578 FlagsType maRowsHidden; 579 FlagsType maRowsFiltered; 580 FlagsType::const_iterator miPosHidden; 581 FlagsType::const_iterator miPosFiltered; 582 583 SortedRowFlags(const ScSheetLimits& rSheetLimits) : 584 maRowsHidden(0, rSheetLimits.GetMaxRowCount(), false), 585 maRowsFiltered(0, rSheetLimits.GetMaxRowCount(), false), 586 miPosHidden(maRowsHidden.begin()), 587 miPosFiltered(maRowsFiltered.begin()) {} 588 589 void setRowHidden( SCROW nRow, bool b ) 590 { 591 miPosHidden = maRowsHidden.insert(miPosHidden, nRow, nRow+1, b).first; 592 } 593 594 void setRowFiltered( SCROW nRow, bool b ) 595 { 596 miPosFiltered = maRowsFiltered.insert(miPosFiltered, nRow, nRow+1, b).first; 597 } 598 599 void swap( SortedRowFlags& r ) 600 { 601 maRowsHidden.swap(r.maRowsHidden); 602 maRowsFiltered.swap(r.maRowsFiltered); 603 604 // Just reset the position hints. 605 miPosHidden = maRowsHidden.begin(); 606 miPosFiltered = maRowsFiltered.begin(); 607 } 608 }; 609 610 struct PatternSpan 611 { 612 SCROW mnRow1; 613 SCROW mnRow2; 614 const ScPatternAttr* mpPattern; 615 616 PatternSpan( SCROW nRow1, SCROW nRow2, const ScPatternAttr* pPat ) : 617 mnRow1(nRow1), mnRow2(nRow2), mpPattern(pPat) {} 618 }; 619 620 } 621 622 bool ScTable::IsSortCollatorGlobal() const 623 { 624 return pSortCollator == &ScGlobal::GetCollator() || 625 pSortCollator == &ScGlobal::GetCaseCollator(); 626 } 627 628 void ScTable::InitSortCollator( const ScSortParam& rPar ) 629 { 630 if ( !rPar.aCollatorLocale.Language.isEmpty() ) 631 { 632 if ( !pSortCollator || IsSortCollatorGlobal() ) 633 pSortCollator = new CollatorWrapper( comphelper::getProcessComponentContext() ); 634 pSortCollator->loadCollatorAlgorithm( rPar.aCollatorAlgorithm, 635 rPar.aCollatorLocale, (rPar.bCaseSens ? 0 : SC_COLLATOR_IGNORES) ); 636 } 637 else 638 { // SYSTEM 639 DestroySortCollator(); 640 pSortCollator = &ScGlobal::GetCollator(rPar.bCaseSens); 641 } 642 } 643 644 void ScTable::DestroySortCollator() 645 { 646 if ( pSortCollator ) 647 { 648 if ( !IsSortCollatorGlobal() ) 649 delete pSortCollator; 650 pSortCollator = nullptr; 651 } 652 } 653 654 namespace { 655 656 template<typename Hint, typename ReorderMap, typename Index> 657 class ReorderNotifier 658 { 659 Hint maHint; 660 public: 661 ReorderNotifier( const ReorderMap& rMap, SCTAB nTab, Index nPos1, Index nPos2 ) : 662 maHint(rMap, nTab, nPos1, nPos2) {} 663 664 void operator() ( SvtListener* p ) 665 { 666 p->Notify(maHint); 667 } 668 }; 669 670 class FormulaGroupPosCollector 671 { 672 sc::RefQueryFormulaGroup& mrQuery; 673 674 public: 675 explicit FormulaGroupPosCollector( sc::RefQueryFormulaGroup& rQuery ) : mrQuery(rQuery) {} 676 677 void operator() ( const SvtListener* p ) 678 { 679 p->Query(mrQuery); 680 } 681 }; 682 683 void fillSortedColumnArray( 684 std::vector<std::unique_ptr<SortedColumn>>& rSortedCols, 685 SortedRowFlags& rRowFlags, 686 std::vector<SvtListener*>& rCellListeners, 687 ScSortInfoArray* pArray, SCTAB nTab, SCCOL nCol1, SCCOL nCol2, ScProgress* pProgress, const ScTable* pTable, 688 bool bOnlyDataAreaExtras ) 689 { 690 assert(!bOnlyDataAreaExtras || !pArray->IsUpdateRefs()); 691 692 SCROW nRow1 = pArray->GetStart(); 693 ScSortInfoArray::RowsType* pRows = pArray->GetDataRows(); 694 std::vector<SCCOLROW> aOrderIndices = pArray->GetOrderIndices(); 695 696 size_t nColCount = nCol2 - nCol1 + 1; 697 std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells. 698 SortedRowFlags aRowFlags(pTable->GetDoc().GetSheetLimits()); 699 aSortedCols.reserve(nColCount); 700 for (size_t i = 0; i < nColCount; ++i) 701 { 702 // In the sorted column container, element positions and row 703 // positions must match, else formula cells may mis-behave during 704 // grouping. 705 aSortedCols.push_back(std::make_unique<SortedColumn>(nRow1, pTable->GetDoc().GetSheetLimits())); 706 } 707 708 for (size_t i = 0; i < pRows->size(); ++i) 709 { 710 const SCROW nRow = nRow1 + i; 711 712 ScSortInfoArray::Row& rRow = (*pRows)[i]; 713 for (size_t j = 0; j < rRow.maCells.size(); ++j) 714 { 715 ScSortInfoArray::Cell& rCell = rRow.maCells[j]; 716 717 // If bOnlyDataAreaExtras, 718 // sc::CellStoreType aSortedCols.at(j)->maCells 719 // and 720 // sc::CellTextAttrStoreType aSortedCols.at(j)->maCellTextAttrs 721 // are by definition all empty mdds::multi_type_vector, so nothing 722 // needs to be done to push *all* empty. 723 724 if (!bOnlyDataAreaExtras) 725 { 726 sc::CellStoreType& rCellStore = aSortedCols.at(j)->maCells; 727 switch (rCell.maCell.getType()) 728 { 729 case CELLTYPE_STRING: 730 assert(rCell.mpAttr); 731 rCellStore.push_back(*rCell.maCell.getSharedString()); 732 break; 733 case CELLTYPE_VALUE: 734 assert(rCell.mpAttr); 735 rCellStore.push_back(rCell.maCell.getDouble()); 736 break; 737 case CELLTYPE_EDIT: 738 assert(rCell.mpAttr); 739 rCellStore.push_back(rCell.maCell.getEditText()->Clone().release()); 740 break; 741 case CELLTYPE_FORMULA: 742 { 743 assert(rCell.mpAttr); 744 ScAddress aOldPos = rCell.maCell.getFormula()->aPos; 745 746 const ScAddress aCellPos(nCol1 + j, nRow, nTab); 747 ScFormulaCell* pNew = rCell.maCell.getFormula()->Clone( aCellPos ); 748 if (pArray->IsUpdateRefs()) 749 { 750 pNew->CopyAllBroadcasters(*rCell.maCell.getFormula()); 751 pNew->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos, aCellPos); 752 } 753 else 754 { 755 pNew->GetCode()->AdjustReferenceOnMovedOriginIfOtherSheet(aOldPos, aCellPos); 756 } 757 758 if (!rCellListeners.empty()) 759 { 760 // Original source cells will be deleted during 761 // sc::CellStoreType::transfer(), SvtListener is a base 762 // class, so we need to replace it. 763 auto it( ::std::find( rCellListeners.begin(), rCellListeners.end(), rCell.maCell.getFormula())); 764 if (it != rCellListeners.end()) 765 *it = pNew; 766 } 767 768 rCellStore.push_back(pNew); 769 } 770 break; 771 default: 772 //assert(!rCell.mpAttr); 773 // This assert doesn't hold, for example 774 // CopyCellsFromClipHandler may omit copying cells during 775 // PasteSpecial for which CopyTextAttrsFromClipHandler 776 // still copies a CellTextAttr. So if that really is not 777 // expected then fix it there. 778 rCellStore.push_back_empty(); 779 } 780 781 sc::CellTextAttrStoreType& rAttrStore = aSortedCols.at(j)->maCellTextAttrs; 782 if (rCell.mpAttr) 783 rAttrStore.push_back(*rCell.mpAttr); 784 else 785 rAttrStore.push_back_empty(); 786 } 787 788 if (pArray->IsUpdateRefs()) 789 { 790 // At this point each broadcaster instance is managed by 2 791 // containers. We will release those in the original storage 792 // below before transferring them to the document. 793 const SvtBroadcaster* pBroadcaster = pTable->GetBroadcaster( nCol1 + j, aOrderIndices[i]); 794 sc::BroadcasterStoreType& rBCStore = aSortedCols.at(j)->maBroadcasters; 795 if (pBroadcaster) 796 // A const pointer would be implicitly converted to a bool type. 797 rBCStore.push_back(const_cast<SvtBroadcaster*>(pBroadcaster)); 798 else 799 rBCStore.push_back_empty(); 800 } 801 802 // The same with cell note instances ... 803 sc::CellNoteStoreType& rNoteStore = aSortedCols.at(j)->maCellNotes; 804 if (rCell.mpNote) 805 rNoteStore.push_back(const_cast<ScPostIt*>(rCell.mpNote)); 806 else 807 rNoteStore.push_back_empty(); 808 809 // Add cell anchored images 810 aSortedCols.at(j)->maCellDrawObjects.push_back(rCell.maDrawObjects); 811 812 if (rCell.mpPattern) 813 aSortedCols.at(j)->setPattern(nRow, rCell.mpPattern); 814 } 815 816 if (!bOnlyDataAreaExtras && pArray->IsKeepQuery()) 817 { 818 // Hidden and filtered flags are first converted to segments. 819 aRowFlags.setRowHidden(nRow, rRow.mbHidden); 820 aRowFlags.setRowFiltered(nRow, rRow.mbFiltered); 821 } 822 823 if (pProgress) 824 pProgress->SetStateOnPercent(i); 825 } 826 827 rSortedCols.swap(aSortedCols); 828 rRowFlags.swap(aRowFlags); 829 } 830 831 void expandRowRange( ScRange& rRange, SCROW nTop, SCROW nBottom ) 832 { 833 if (nTop < rRange.aStart.Row()) 834 rRange.aStart.SetRow(nTop); 835 836 if (rRange.aEnd.Row() < nBottom) 837 rRange.aEnd.SetRow(nBottom); 838 } 839 840 class FormulaCellCollectAction : public sc::ColumnSpanSet::ColumnAction 841 { 842 std::vector<ScFormulaCell*>& mrCells; 843 ScColumn* mpCol; 844 845 public: 846 explicit FormulaCellCollectAction( std::vector<ScFormulaCell*>& rCells ) : 847 mrCells(rCells), mpCol(nullptr) {} 848 849 virtual void startColumn( ScColumn* pCol ) override 850 { 851 mpCol = pCol; 852 } 853 854 virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override 855 { 856 assert(mpCol); 857 858 if (!bVal) 859 return; 860 861 mpCol->CollectFormulaCells(mrCells, nRow1, nRow2); 862 } 863 }; 864 865 class ListenerStartAction : public sc::ColumnSpanSet::ColumnAction 866 { 867 ScColumn* mpCol; 868 869 std::shared_ptr<sc::ColumnBlockPositionSet> mpPosSet; 870 sc::StartListeningContext maStartCxt; 871 sc::EndListeningContext maEndCxt; 872 873 public: 874 explicit ListenerStartAction( ScDocument& rDoc ) : 875 mpCol(nullptr), 876 mpPosSet(std::make_shared<sc::ColumnBlockPositionSet>(rDoc)), 877 maStartCxt(rDoc, mpPosSet), 878 maEndCxt(rDoc, mpPosSet) {} 879 880 virtual void startColumn( ScColumn* pCol ) override 881 { 882 mpCol = pCol; 883 } 884 885 virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override 886 { 887 assert(mpCol); 888 889 if (!bVal) 890 return; 891 892 mpCol->StartListeningFormulaCells(maStartCxt, maEndCxt, nRow1, nRow2); 893 } 894 }; 895 896 } 897 898 void ScTable::SortReorderAreaExtrasByRow( ScSortInfoArray* pArray, 899 SCCOL nDataCol1, SCCOL nDataCol2, 900 const ScDataAreaExtras& rDataAreaExtras, ScProgress* pProgress ) 901 { 902 const SCROW nRow1 = pArray->GetStart(); 903 const SCROW nLastRow = pArray->GetLast(); 904 const SCCOL nChunkCols = std::max<SCCOL>( 1, kSortCellsChunk / (nLastRow - nRow1 + 1)); 905 // Before data area. 906 for (SCCOL nCol = rDataAreaExtras.mnStartCol; nCol < nDataCol1; nCol += nChunkCols) 907 { 908 const SCCOL nEndCol = std::min<SCCOL>( nCol + nChunkCols - 1, nDataCol1 - 1); 909 CreateColumnIfNotExists(nEndCol); 910 initDataRows( *pArray, *this, aCol, nCol, nRow1, nEndCol, nLastRow, false, 911 rDataAreaExtras.mbCellFormats, rDataAreaExtras.mbCellNotes, rDataAreaExtras.mbCellDrawObjects, true); 912 SortReorderByRow( pArray, nCol, nEndCol, pProgress, true); 913 } 914 // Behind data area. 915 for (SCCOL nCol = nDataCol2 + 1; nCol <= rDataAreaExtras.mnEndCol; nCol += nChunkCols) 916 { 917 const SCCOL nEndCol = std::min<SCCOL>( nCol + nChunkCols - 1, rDataAreaExtras.mnEndCol); 918 CreateColumnIfNotExists(nEndCol); 919 initDataRows( *pArray, *this, aCol, nCol, nRow1, nEndCol, nLastRow, false, 920 rDataAreaExtras.mbCellFormats, rDataAreaExtras.mbCellNotes, rDataAreaExtras.mbCellDrawObjects, true); 921 SortReorderByRow( pArray, nCol, nEndCol, pProgress, true); 922 } 923 } 924 925 void ScTable::SortReorderAreaExtrasByColumn( const ScSortInfoArray* pArray, 926 SCROW nDataRow1, SCROW nDataRow2, const ScDataAreaExtras& rDataAreaExtras, ScProgress* pProgress ) 927 { 928 const SCCOL nCol1 = static_cast<SCCOL>(pArray->GetStart()); 929 const SCCOL nLastCol = static_cast<SCCOL>(pArray->GetLast()); 930 const SCROW nChunkRows = std::max<SCROW>( 1, kSortCellsChunk / (nLastCol - nCol1 + 1)); 931 // Above data area. 932 for (SCROW nRow = rDataAreaExtras.mnStartRow; nRow < nDataRow1; nRow += nChunkRows) 933 { 934 const SCROW nEndRow = std::min<SCROW>( nRow + nChunkRows - 1, nDataRow1 - 1); 935 SortReorderByColumn( pArray, nRow, nEndRow, rDataAreaExtras.mbCellFormats, pProgress); 936 } 937 // Below data area. 938 for (SCROW nRow = nDataRow2 + 1; nRow <= rDataAreaExtras.mnEndRow; nRow += nChunkRows) 939 { 940 const SCROW nEndRow = std::min<SCROW>( nRow + nChunkRows - 1, rDataAreaExtras.mnEndRow); 941 SortReorderByColumn( pArray, nRow, nEndRow, rDataAreaExtras.mbCellFormats, pProgress); 942 } 943 } 944 945 void ScTable::SortReorderByColumn( 946 const ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2, bool bPattern, ScProgress* pProgress ) 947 { 948 SCCOLROW nStart = pArray->GetStart(); 949 SCCOLROW nLast = pArray->GetLast(); 950 951 std::vector<SCCOLROW> aIndices = pArray->GetOrderIndices(); 952 size_t nCount = aIndices.size(); 953 954 // Cut formula grouping at row and reference boundaries before the reordering. 955 ScRange aSortRange(nStart, nRow1, nTab, nLast, nRow2, nTab); 956 for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol) 957 aCol[nCol].SplitFormulaGroupByRelativeRef(aSortRange); 958 959 // Collect all listeners of cell broadcasters of sorted range. 960 std::vector<SvtListener*> aCellListeners; 961 962 if (!pArray->IsUpdateRefs()) 963 { 964 // Collect listeners of cell broadcasters. 965 for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol) 966 aCol[nCol].CollectListeners(aCellListeners, nRow1, nRow2); 967 968 // Remove any duplicate listener entries. We must ensure that we 969 // notify each unique listener only once. 970 std::sort(aCellListeners.begin(), aCellListeners.end()); 971 aCellListeners.erase(std::unique(aCellListeners.begin(), aCellListeners.end()), aCellListeners.end()); 972 973 // Notify the cells' listeners to stop listening. 974 /* TODO: for performance this could be enhanced to stop and later 975 * restart only listening to within the reordered range and keep 976 * listening to everything outside untouched. */ 977 sc::RefStopListeningHint aHint; 978 for (auto const & l : aCellListeners) 979 l->Notify(aHint); 980 } 981 982 // table to keep track of column index to position in the index table. 983 std::vector<SCCOLROW> aPosTable(nCount); 984 for (size_t i = 0; i < nCount; ++i) 985 aPosTable[aIndices[i]-nStart] = i; 986 987 SCCOLROW nDest = nStart; 988 for (size_t i = 0; i < nCount; ++i, ++nDest) 989 { 990 SCCOLROW nSrc = aIndices[i]; 991 if (nDest != nSrc) 992 { 993 aCol[nDest].Swap(aCol[nSrc], nRow1, nRow2, bPattern); 994 995 // Update the position of the index that was originally equal to nDest. 996 size_t nPos = aPosTable[nDest-nStart]; 997 aIndices[nPos] = nSrc; 998 aPosTable[nSrc-nStart] = nPos; 999 } 1000 1001 if (pProgress) 1002 pProgress->SetStateOnPercent(i); 1003 } 1004 1005 // Reset formula cell positions which became out-of-sync after column reordering. 1006 bool bUpdateRefs = pArray->IsUpdateRefs(); 1007 for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol) 1008 aCol[nCol].ResetFormulaCellPositions(nRow1, nRow2, bUpdateRefs); 1009 1010 if (pArray->IsUpdateRefs()) 1011 { 1012 // Set up column reorder map (for later broadcasting of reference updates). 1013 sc::ColRowReorderMapType aColMap; 1014 const std::vector<SCCOLROW>& rOldIndices = pArray->GetOrderIndices(); 1015 for (size_t i = 0, n = rOldIndices.size(); i < n; ++i) 1016 { 1017 SCCOL nNew = i + nStart; 1018 SCCOL nOld = rOldIndices[i]; 1019 aColMap.emplace(nOld, nNew); 1020 } 1021 1022 // Collect all listeners within sorted range ahead of time. 1023 std::vector<SvtListener*> aListeners; 1024 1025 for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol) 1026 aCol[nCol].CollectListeners(aListeners, nRow1, nRow2); 1027 1028 // Get all area listeners that listen on one column within the range 1029 // and end their listening. 1030 ScRange aMoveRange( nStart, nRow1, nTab, nLast, nRow2, nTab); 1031 std::vector<sc::AreaListener> aAreaListeners = rDocument.GetBASM()->GetAllListeners( 1032 aMoveRange, sc::AreaOverlapType::OneColumnInside); 1033 { 1034 for (auto& rAreaListener : aAreaListeners) 1035 { 1036 rDocument.EndListeningArea(rAreaListener.maArea, rAreaListener.mbGroupListening, rAreaListener.mpListener); 1037 aListeners.push_back( rAreaListener.mpListener); 1038 } 1039 } 1040 1041 // Remove any duplicate listener entries and notify all listeners 1042 // afterward. We must ensure that we notify each unique listener only 1043 // once. 1044 std::sort(aListeners.begin(), aListeners.end()); 1045 aListeners.erase(std::unique(aListeners.begin(), aListeners.end()), aListeners.end()); 1046 ReorderNotifier<sc::RefColReorderHint, sc::ColRowReorderMapType, SCCOL> aFunc(aColMap, nTab, nRow1, nRow2); 1047 std::for_each(aListeners.begin(), aListeners.end(), aFunc); 1048 1049 // Re-start area listeners on the reordered columns. 1050 { 1051 for (auto& rAreaListener : aAreaListeners) 1052 { 1053 ScRange aNewRange = rAreaListener.maArea; 1054 sc::ColRowReorderMapType::const_iterator itCol = aColMap.find( aNewRange.aStart.Col()); 1055 if (itCol != aColMap.end()) 1056 { 1057 aNewRange.aStart.SetCol( itCol->second); 1058 aNewRange.aEnd.SetCol( itCol->second); 1059 } 1060 rDocument.StartListeningArea(aNewRange, rAreaListener.mbGroupListening, rAreaListener.mpListener); 1061 } 1062 } 1063 } 1064 else // !(pArray->IsUpdateRefs()) 1065 { 1066 // Notify the cells' listeners to (re-)start listening. 1067 sc::RefStartListeningHint aHint; 1068 for (auto const & l : aCellListeners) 1069 l->Notify(aHint); 1070 } 1071 1072 // Re-join formulas at row boundaries now that all the references have 1073 // been adjusted for column reordering. 1074 for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol) 1075 { 1076 sc::CellStoreType& rCells = aCol[nCol].maCells; 1077 sc::CellStoreType::position_type aPos = rCells.position(nRow1); 1078 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos); 1079 if (nRow2 < rDocument.MaxRow()) 1080 { 1081 aPos = rCells.position(aPos.first, nRow2+1); 1082 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos); 1083 } 1084 } 1085 } 1086 1087 void ScTable::SortReorderByRow( ScSortInfoArray* pArray, SCCOL nCol1, SCCOL nCol2, 1088 ScProgress* pProgress, bool bOnlyDataAreaExtras ) 1089 { 1090 assert(!pArray->IsUpdateRefs()); 1091 1092 if (nCol2 < nCol1) 1093 return; 1094 1095 // bOnlyDataAreaExtras: 1096 // Data area extras by definition do not have any cell content so no 1097 // formula cells either, so that handling doesn't need to be executed. 1098 // However, there may be listeners of formulas listening to broadcasters of 1099 // empty cells. 1100 1101 SCROW nRow1 = pArray->GetStart(); 1102 SCROW nRow2 = pArray->GetLast(); 1103 1104 // Collect all listeners of cell broadcasters of sorted range. 1105 std::vector<SvtListener*> aCellListeners; 1106 1107 // When the update ref mode is disabled, we need to detach all formula 1108 // cells in the sorted range before reordering, and re-start them 1109 // afterward. 1110 if (!bOnlyDataAreaExtras) 1111 { 1112 sc::EndListeningContext aCxt(rDocument); 1113 DetachFormulaCells(aCxt, nCol1, nRow1, nCol2, nRow2); 1114 } 1115 1116 // Collect listeners of cell broadcasters. 1117 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) 1118 aCol[nCol].CollectListeners(aCellListeners, nRow1, nRow2); 1119 1120 // Remove any duplicate listener entries. We must ensure that we notify 1121 // each unique listener only once. 1122 std::sort(aCellListeners.begin(), aCellListeners.end()); 1123 aCellListeners.erase(std::unique(aCellListeners.begin(), aCellListeners.end()), aCellListeners.end()); 1124 1125 // Notify the cells' listeners to stop listening. 1126 /* TODO: for performance this could be enhanced to stop and later 1127 * restart only listening to within the reordered range and keep 1128 * listening to everything outside untouched. */ 1129 { 1130 sc::RefStopListeningHint aHint; 1131 for (auto const & l : aCellListeners) 1132 l->Notify(aHint); 1133 } 1134 1135 // Split formula groups at the sort range boundaries (if applicable). 1136 if (!bOnlyDataAreaExtras) 1137 { 1138 std::vector<SCROW> aRowBounds 1139 { 1140 nRow1, 1141 nRow2+1 1142 }; 1143 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) 1144 SplitFormulaGroups(nCol, aRowBounds); 1145 } 1146 1147 // Cells in the data rows only reference values in the document. Make 1148 // a copy before updating the document. 1149 std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells. 1150 SortedRowFlags aRowFlags(GetDoc().GetSheetLimits()); 1151 fillSortedColumnArray(aSortedCols, aRowFlags, aCellListeners, pArray, nTab, nCol1, nCol2, 1152 pProgress, this, bOnlyDataAreaExtras); 1153 1154 for (size_t i = 0, n = aSortedCols.size(); i < n; ++i) 1155 { 1156 SCCOL nThisCol = i + nCol1; 1157 1158 if (!bOnlyDataAreaExtras) 1159 { 1160 { 1161 sc::CellStoreType& rDest = aCol[nThisCol].maCells; 1162 sc::CellStoreType& rSrc = aSortedCols[i]->maCells; 1163 rSrc.transfer(nRow1, nRow2, rDest, nRow1); 1164 } 1165 1166 { 1167 sc::CellTextAttrStoreType& rDest = aCol[nThisCol].maCellTextAttrs; 1168 sc::CellTextAttrStoreType& rSrc = aSortedCols[i]->maCellTextAttrs; 1169 rSrc.transfer(nRow1, nRow2, rDest, nRow1); 1170 } 1171 } 1172 1173 { 1174 sc::CellNoteStoreType& rSrc = aSortedCols[i]->maCellNotes; 1175 sc::CellNoteStoreType& rDest = aCol[nThisCol].maCellNotes; 1176 1177 // Do the same as broadcaster storage transfer (to prevent double deletion). 1178 rDest.release_range(nRow1, nRow2); 1179 rSrc.transfer(nRow1, nRow2, rDest, nRow1); 1180 aCol[nThisCol].UpdateNoteCaptions(nRow1, nRow2); 1181 } 1182 1183 // Update draw object positions 1184 aCol[nThisCol].UpdateDrawObjects(aSortedCols[i]->maCellDrawObjects, nRow1, nRow2); 1185 1186 { 1187 // Get all row spans where the pattern is not NULL. 1188 std::vector<PatternSpan> aSpans = 1189 sc::toSpanArrayWithValue<SCROW,const ScPatternAttr*,PatternSpan>( 1190 aSortedCols[i]->maPatterns); 1191 1192 for (const auto& rSpan : aSpans) 1193 { 1194 assert(rSpan.mpPattern); // should never be NULL. 1195 rDocument.GetPool()->Put(*rSpan.mpPattern); 1196 } 1197 1198 for (const auto& rSpan : aSpans) 1199 { 1200 aCol[nThisCol].SetPatternArea(rSpan.mnRow1, rSpan.mnRow2, *rSpan.mpPattern); 1201 rDocument.GetPool()->Remove(*rSpan.mpPattern); 1202 } 1203 } 1204 1205 aCol[nThisCol].CellStorageModified(); 1206 } 1207 1208 if (!bOnlyDataAreaExtras && pArray->IsKeepQuery()) 1209 { 1210 aRowFlags.maRowsHidden.build_tree(); 1211 aRowFlags.maRowsFiltered.build_tree(); 1212 1213 // Remove all flags in the range first. 1214 SetRowHidden(nRow1, nRow2, false); 1215 SetRowFiltered(nRow1, nRow2, false); 1216 1217 std::vector<sc::RowSpan> aSpans = 1218 sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsHidden, nRow1); 1219 1220 for (const auto& rSpan : aSpans) 1221 SetRowHidden(rSpan.mnRow1, rSpan.mnRow2, true); 1222 1223 aSpans = sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsFiltered, nRow1); 1224 1225 for (const auto& rSpan : aSpans) 1226 SetRowFiltered(rSpan.mnRow1, rSpan.mnRow2, true); 1227 } 1228 1229 // Notify the cells' listeners to (re-)start listening. 1230 { 1231 sc::RefStartListeningHint aHint; 1232 for (auto const & l : aCellListeners) 1233 l->Notify(aHint); 1234 } 1235 1236 if (!bOnlyDataAreaExtras) 1237 { 1238 // Re-group columns in the sorted range too. 1239 for (SCCOL i = nCol1; i <= nCol2; ++i) 1240 aCol[i].RegroupFormulaCells(); 1241 1242 { 1243 sc::StartListeningContext aCxt(rDocument); 1244 AttachFormulaCells(aCxt, nCol1, nRow1, nCol2, nRow2); 1245 } 1246 } 1247 } 1248 1249 void ScTable::SortReorderByRowRefUpdate( 1250 ScSortInfoArray* pArray, SCCOL nCol1, SCCOL nCol2, ScProgress* pProgress ) 1251 { 1252 assert(pArray->IsUpdateRefs()); 1253 1254 if (nCol2 < nCol1) 1255 return; 1256 1257 SCROW nRow1 = pArray->GetStart(); 1258 SCROW nRow2 = pArray->GetLast(); 1259 1260 ScRange aMoveRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab); 1261 sc::ColumnSpanSet aGrpListenerRanges; 1262 1263 { 1264 // Get the range of formula group listeners within sorted range (if any). 1265 sc::QueryRange aQuery; 1266 1267 ScBroadcastAreaSlotMachine* pBASM = rDocument.GetBASM(); 1268 std::vector<sc::AreaListener> aGrpListeners = 1269 pBASM->GetAllListeners( 1270 aMoveRange, sc::AreaOverlapType::InsideOrOverlap, sc::ListenerGroupType::Group); 1271 1272 { 1273 for (const auto& rGrpListener : aGrpListeners) 1274 { 1275 assert(rGrpListener.mbGroupListening); 1276 SvtListener* pGrpLis = rGrpListener.mpListener; 1277 pGrpLis->Query(aQuery); 1278 rDocument.EndListeningArea(rGrpListener.maArea, rGrpListener.mbGroupListening, pGrpLis); 1279 } 1280 } 1281 1282 ScRangeList aTmp; 1283 aQuery.swapRanges(aTmp); 1284 1285 // If the range is within the sorted range, we need to expand its rows 1286 // to the top and bottom of the sorted range, since the formula cells 1287 // could be anywhere in the sorted range after reordering. 1288 for (size_t i = 0, n = aTmp.size(); i < n; ++i) 1289 { 1290 ScRange aRange = aTmp[i]; 1291 if (!aMoveRange.Intersects(aRange)) 1292 { 1293 // Doesn't overlap with the sorted range at all. 1294 aGrpListenerRanges.set(GetDoc(), aRange, true); 1295 continue; 1296 } 1297 1298 if (aMoveRange.aStart.Col() <= aRange.aStart.Col() && aRange.aEnd.Col() <= aMoveRange.aEnd.Col()) 1299 { 1300 // Its column range is within the column range of the sorted range. 1301 expandRowRange(aRange, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row()); 1302 aGrpListenerRanges.set(GetDoc(), aRange, true); 1303 continue; 1304 } 1305 1306 // It intersects with the sorted range, but its column range is 1307 // not within the column range of the sorted range. Split it into 1308 // 2 ranges. 1309 ScRange aR1 = aRange; 1310 ScRange aR2 = aRange; 1311 if (aRange.aStart.Col() < aMoveRange.aStart.Col()) 1312 { 1313 // Left half is outside the sorted range while the right half is inside. 1314 aR1.aEnd.SetCol(aMoveRange.aStart.Col()-1); 1315 aR2.aStart.SetCol(aMoveRange.aStart.Col()); 1316 expandRowRange(aR2, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row()); 1317 } 1318 else 1319 { 1320 // Left half is inside the sorted range while the right half is outside. 1321 aR1.aEnd.SetCol(aMoveRange.aEnd.Col()-1); 1322 aR2.aStart.SetCol(aMoveRange.aEnd.Col()); 1323 expandRowRange(aR1, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row()); 1324 } 1325 1326 aGrpListenerRanges.set(GetDoc(), aR1, true); 1327 aGrpListenerRanges.set(GetDoc(), aR2, true); 1328 } 1329 } 1330 1331 // Split formula groups at the sort range boundaries (if applicable). 1332 std::vector<SCROW> aRowBounds 1333 { 1334 nRow1, 1335 nRow2+1 1336 }; 1337 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) 1338 SplitFormulaGroups(nCol, aRowBounds); 1339 1340 // Cells in the data rows only reference values in the document. Make 1341 // a copy before updating the document. 1342 std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells. 1343 SortedRowFlags aRowFlags(GetDoc().GetSheetLimits()); 1344 std::vector<SvtListener*> aListenersDummy; 1345 fillSortedColumnArray(aSortedCols, aRowFlags, aListenersDummy, pArray, nTab, nCol1, nCol2, pProgress, this, false); 1346 1347 for (size_t i = 0, n = aSortedCols.size(); i < n; ++i) 1348 { 1349 SCCOL nThisCol = i + nCol1; 1350 1351 { 1352 sc::CellStoreType& rDest = aCol[nThisCol].maCells; 1353 sc::CellStoreType& rSrc = aSortedCols[i]->maCells; 1354 rSrc.transfer(nRow1, nRow2, rDest, nRow1); 1355 } 1356 1357 { 1358 sc::CellTextAttrStoreType& rDest = aCol[nThisCol].maCellTextAttrs; 1359 sc::CellTextAttrStoreType& rSrc = aSortedCols[i]->maCellTextAttrs; 1360 rSrc.transfer(nRow1, nRow2, rDest, nRow1); 1361 } 1362 1363 { 1364 sc::BroadcasterStoreType& rSrc = aSortedCols[i]->maBroadcasters; 1365 sc::BroadcasterStoreType& rDest = aCol[nThisCol].maBroadcasters; 1366 1367 // Release current broadcasters first, to prevent them from getting deleted. 1368 rDest.release_range(nRow1, nRow2); 1369 1370 // Transfer sorted broadcaster segment to the document. 1371 rSrc.transfer(nRow1, nRow2, rDest, nRow1); 1372 } 1373 1374 { 1375 sc::CellNoteStoreType& rSrc = aSortedCols[i]->maCellNotes; 1376 sc::CellNoteStoreType& rDest = aCol[nThisCol].maCellNotes; 1377 1378 // Do the same as broadcaster storage transfer (to prevent double deletion). 1379 rDest.release_range(nRow1, nRow2); 1380 rSrc.transfer(nRow1, nRow2, rDest, nRow1); 1381 aCol[nThisCol].UpdateNoteCaptions(nRow1, nRow2); 1382 } 1383 1384 // Update draw object positions 1385 aCol[nThisCol].UpdateDrawObjects(aSortedCols[i]->maCellDrawObjects, nRow1, nRow2); 1386 1387 { 1388 // Get all row spans where the pattern is not NULL. 1389 std::vector<PatternSpan> aSpans = 1390 sc::toSpanArrayWithValue<SCROW,const ScPatternAttr*,PatternSpan>( 1391 aSortedCols[i]->maPatterns); 1392 1393 for (const auto& rSpan : aSpans) 1394 { 1395 assert(rSpan.mpPattern); // should never be NULL. 1396 rDocument.GetPool()->Put(*rSpan.mpPattern); 1397 } 1398 1399 for (const auto& rSpan : aSpans) 1400 { 1401 aCol[nThisCol].SetPatternArea(rSpan.mnRow1, rSpan.mnRow2, *rSpan.mpPattern); 1402 rDocument.GetPool()->Remove(*rSpan.mpPattern); 1403 } 1404 } 1405 1406 aCol[nThisCol].CellStorageModified(); 1407 } 1408 1409 if (pArray->IsKeepQuery()) 1410 { 1411 aRowFlags.maRowsHidden.build_tree(); 1412 aRowFlags.maRowsFiltered.build_tree(); 1413 1414 // Remove all flags in the range first. 1415 SetRowHidden(nRow1, nRow2, false); 1416 SetRowFiltered(nRow1, nRow2, false); 1417 1418 std::vector<sc::RowSpan> aSpans = 1419 sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsHidden, nRow1); 1420 1421 for (const auto& rSpan : aSpans) 1422 SetRowHidden(rSpan.mnRow1, rSpan.mnRow2, true); 1423 1424 aSpans = sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsFiltered, nRow1); 1425 1426 for (const auto& rSpan : aSpans) 1427 SetRowFiltered(rSpan.mnRow1, rSpan.mnRow2, true); 1428 } 1429 1430 // Set up row reorder map (for later broadcasting of reference updates). 1431 sc::ColRowReorderMapType aRowMap; 1432 const std::vector<SCCOLROW>& rOldIndices = pArray->GetOrderIndices(); 1433 for (size_t i = 0, n = rOldIndices.size(); i < n; ++i) 1434 { 1435 SCROW nNew = i + nRow1; 1436 SCROW nOld = rOldIndices[i]; 1437 aRowMap.emplace(nOld, nNew); 1438 } 1439 1440 // Collect all listeners within sorted range ahead of time. 1441 std::vector<SvtListener*> aListeners; 1442 1443 // Collect listeners of cell broadcasters. 1444 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) 1445 aCol[nCol].CollectListeners(aListeners, nRow1, nRow2); 1446 1447 // Get all area listeners that listen on one row within the range and end 1448 // their listening. 1449 std::vector<sc::AreaListener> aAreaListeners = rDocument.GetBASM()->GetAllListeners( 1450 aMoveRange, sc::AreaOverlapType::OneRowInside); 1451 { 1452 for (auto& rAreaListener : aAreaListeners) 1453 { 1454 rDocument.EndListeningArea(rAreaListener.maArea, rAreaListener.mbGroupListening, rAreaListener.mpListener); 1455 aListeners.push_back( rAreaListener.mpListener); 1456 } 1457 } 1458 1459 { 1460 // Get all formula cells from the former group area listener ranges. 1461 1462 std::vector<ScFormulaCell*> aFCells; 1463 FormulaCellCollectAction aAction(aFCells); 1464 aGrpListenerRanges.executeColumnAction(rDocument, aAction); 1465 1466 aListeners.insert( aListeners.end(), aFCells.begin(), aFCells.end() ); 1467 } 1468 1469 // Remove any duplicate listener entries. We must ensure that we notify 1470 // each unique listener only once. 1471 std::sort(aListeners.begin(), aListeners.end()); 1472 aListeners.erase(std::unique(aListeners.begin(), aListeners.end()), aListeners.end()); 1473 1474 // Collect positions of all shared formula cells outside the sorted range, 1475 // and make them unshared before notifying them. 1476 sc::RefQueryFormulaGroup aFormulaGroupPos; 1477 aFormulaGroupPos.setSkipRange(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab)); 1478 1479 std::for_each(aListeners.begin(), aListeners.end(), FormulaGroupPosCollector(aFormulaGroupPos)); 1480 const sc::RefQueryFormulaGroup::TabsType& rGroupTabs = aFormulaGroupPos.getAllPositions(); 1481 for (const auto& [rTab, rCols] : rGroupTabs) 1482 { 1483 for (const auto& [nCol, rCol] : rCols) 1484 { 1485 std::vector<SCROW> aBounds(rCol); 1486 rDocument.UnshareFormulaCells(rTab, nCol, aBounds); 1487 } 1488 } 1489 1490 // Notify the listeners to update their references. 1491 ReorderNotifier<sc::RefRowReorderHint, sc::ColRowReorderMapType, SCROW> aFunc(aRowMap, nTab, nCol1, nCol2); 1492 std::for_each(aListeners.begin(), aListeners.end(), aFunc); 1493 1494 // Re-group formulas in affected columns. 1495 for (const auto& [rTab, rCols] : rGroupTabs) 1496 { 1497 for (const auto& rEntry : rCols) 1498 rDocument.RegroupFormulaCells(rTab, rEntry.first); 1499 } 1500 1501 // Re-start area listeners on the reordered rows. 1502 for (const auto& rAreaListener : aAreaListeners) 1503 { 1504 ScRange aNewRange = rAreaListener.maArea; 1505 sc::ColRowReorderMapType::const_iterator itRow = aRowMap.find( aNewRange.aStart.Row()); 1506 if (itRow != aRowMap.end()) 1507 { 1508 aNewRange.aStart.SetRow( itRow->second); 1509 aNewRange.aEnd.SetRow( itRow->second); 1510 } 1511 rDocument.StartListeningArea(aNewRange, rAreaListener.mbGroupListening, rAreaListener.mpListener); 1512 } 1513 1514 // Re-group columns in the sorted range too. 1515 for (SCCOL i = nCol1; i <= nCol2; ++i) 1516 aCol[i].RegroupFormulaCells(); 1517 1518 { 1519 // Re-start area listeners on the old group listener ranges. 1520 ListenerStartAction aAction(rDocument); 1521 aGrpListenerRanges.executeColumnAction(rDocument, aAction); 1522 } 1523 } 1524 1525 short ScTable::CompareCell( 1526 sal_uInt16 nSort, 1527 ScRefCellValue& rCell1, SCCOL nCell1Col, SCROW nCell1Row, 1528 ScRefCellValue& rCell2, SCCOL nCell2Col, SCROW nCell2Row ) const 1529 { 1530 short nRes = 0; 1531 1532 CellType eType1 = rCell1.getType(), eType2 = rCell2.getType(); 1533 1534 if (!rCell1.isEmpty()) 1535 { 1536 if (!rCell2.isEmpty()) 1537 { 1538 bool bErr1 = false; 1539 bool bStr1 = ( eType1 != CELLTYPE_VALUE ); 1540 if (eType1 == CELLTYPE_FORMULA) 1541 { 1542 if (rCell1.getFormula()->GetErrCode() != FormulaError::NONE) 1543 { 1544 bErr1 = true; 1545 bStr1 = false; 1546 } 1547 else if (rCell1.getFormula()->IsValue()) 1548 { 1549 bStr1 = false; 1550 } 1551 } 1552 1553 bool bErr2 = false; 1554 bool bStr2 = ( eType2 != CELLTYPE_VALUE ); 1555 if (eType2 == CELLTYPE_FORMULA) 1556 { 1557 if (rCell2.getFormula()->GetErrCode() != FormulaError::NONE) 1558 { 1559 bErr2 = true; 1560 bStr2 = false; 1561 } 1562 else if (rCell2.getFormula()->IsValue()) 1563 { 1564 bStr2 = false; 1565 } 1566 } 1567 1568 if ( bStr1 && bStr2 ) // only compare strings as strings! 1569 { 1570 OUString aStr1; 1571 OUString aStr2; 1572 if (eType1 == CELLTYPE_STRING) 1573 aStr1 = rCell1.getSharedString()->getString(); 1574 else 1575 aStr1 = GetString(nCell1Col, nCell1Row); 1576 if (eType2 == CELLTYPE_STRING) 1577 aStr2 = rCell2.getSharedString()->getString(); 1578 else 1579 aStr2 = GetString(nCell2Col, nCell2Row); 1580 1581 bool bUserDef = aSortParam.bUserDef; // custom sort order 1582 bool bNaturalSort = aSortParam.bNaturalSort; // natural sort 1583 bool bCaseSens = aSortParam.bCaseSens; // case sensitivity 1584 1585 ScUserList* pList = ScGlobal::GetUserList(); 1586 if (bUserDef && pList && pList->size() > aSortParam.nUserIndex ) 1587 { 1588 const ScUserListData& rData = (*pList)[aSortParam.nUserIndex]; 1589 1590 if ( bNaturalSort ) 1591 nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, &rData, pSortCollator ); 1592 else 1593 { 1594 if ( bCaseSens ) 1595 nRes = sal::static_int_cast<short>( rData.Compare(aStr1, aStr2) ); 1596 else 1597 nRes = sal::static_int_cast<short>( rData.ICompare(aStr1, aStr2) ); 1598 } 1599 1600 } 1601 if (!bUserDef) 1602 { 1603 if ( bNaturalSort ) 1604 nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, nullptr, pSortCollator ); 1605 else 1606 nRes = static_cast<short>( pSortCollator->compareString( aStr1, aStr2 ) ); 1607 } 1608 } 1609 else if ( bStr1 ) // String <-> Number or Error 1610 { 1611 if (bErr2) 1612 nRes = -1; // String in front of Error 1613 else 1614 nRes = 1; // Number in front of String 1615 } 1616 else if ( bStr2 ) // Number or Error <-> String 1617 { 1618 if (bErr1) 1619 nRes = 1; // String in front of Error 1620 else 1621 nRes = -1; // Number in front of String 1622 } 1623 else if (bErr1 && bErr2) 1624 { 1625 // nothing, two Errors are equal 1626 } 1627 else if (bErr1) // Error <-> Number 1628 { 1629 nRes = 1; // Number in front of Error 1630 } 1631 else if (bErr2) // Number <-> Error 1632 { 1633 nRes = -1; // Number in front of Error 1634 } 1635 else // Mixed numbers 1636 { 1637 double nVal1 = rCell1.getValue(); 1638 double nVal2 = rCell2.getValue(); 1639 if (nVal1 < nVal2) 1640 nRes = -1; 1641 else if (nVal1 > nVal2) 1642 nRes = 1; 1643 } 1644 if ( !aSortParam.maKeyState[nSort].bAscending ) 1645 nRes = -nRes; 1646 } 1647 else 1648 nRes = -1; 1649 } 1650 else 1651 { 1652 if (!rCell2.isEmpty()) 1653 nRes = 1; 1654 else 1655 nRes = 0; // both empty 1656 } 1657 return nRes; 1658 } 1659 1660 short ScTable::Compare( ScSortInfoArray* pArray, SCCOLROW nIndex1, SCCOLROW nIndex2 ) const 1661 { 1662 short nRes; 1663 sal_uInt16 nSort = 0; 1664 do 1665 { 1666 ScSortInfo& rInfo1 = pArray->Get( nSort, nIndex1 ); 1667 ScSortInfo& rInfo2 = pArray->Get( nSort, nIndex2 ); 1668 if ( aSortParam.bByRow ) 1669 nRes = CompareCell( nSort, 1670 rInfo1.maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), rInfo1.nOrg, 1671 rInfo2.maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), rInfo2.nOrg ); 1672 else 1673 nRes = CompareCell( nSort, 1674 rInfo1.maCell, static_cast<SCCOL>(rInfo1.nOrg), aSortParam.maKeyState[nSort].nField, 1675 rInfo2.maCell, static_cast<SCCOL>(rInfo2.nOrg), aSortParam.maKeyState[nSort].nField ); 1676 } while ( nRes == 0 && ++nSort < pArray->GetUsedSorts() ); 1677 if( nRes == 0 ) 1678 { 1679 ScSortInfo& rInfo1 = pArray->Get( 0, nIndex1 ); 1680 ScSortInfo& rInfo2 = pArray->Get( 0, nIndex2 ); 1681 if( rInfo1.nOrg < rInfo2.nOrg ) 1682 nRes = -1; 1683 else if( rInfo1.nOrg > rInfo2.nOrg ) 1684 nRes = 1; 1685 } 1686 return nRes; 1687 } 1688 1689 void ScTable::QuickSort( ScSortInfoArray* pArray, SCCOLROW nLo, SCCOLROW nHi ) 1690 { 1691 if ((nHi - nLo) == 1) 1692 { 1693 if (Compare(pArray, nLo, nHi) > 0) 1694 pArray->Swap( nLo, nHi ); 1695 } 1696 else 1697 { 1698 SCCOLROW ni = nLo; 1699 SCCOLROW nj = nHi; 1700 do 1701 { 1702 while ((ni <= nHi) && (Compare(pArray, ni, nLo)) < 0) 1703 ni++; 1704 while ((nj >= nLo) && (Compare(pArray, nLo, nj)) < 0) 1705 nj--; 1706 if (ni <= nj) 1707 { 1708 if (ni != nj) 1709 pArray->Swap( ni, nj ); 1710 ni++; 1711 nj--; 1712 } 1713 } while (ni < nj); 1714 if ((nj - nLo) < (nHi - ni)) 1715 { 1716 if (nLo < nj) 1717 QuickSort(pArray, nLo, nj); 1718 if (ni < nHi) 1719 QuickSort(pArray, ni, nHi); 1720 } 1721 else 1722 { 1723 if (ni < nHi) 1724 QuickSort(pArray, ni, nHi); 1725 if (nLo < nj) 1726 QuickSort(pArray, nLo, nj); 1727 } 1728 } 1729 } 1730 1731 short ScTable::Compare(SCCOLROW nIndex1, SCCOLROW nIndex2) const 1732 { 1733 short nRes; 1734 sal_uInt16 nSort = 0; 1735 const sal_uInt32 nMaxSorts = aSortParam.GetSortKeyCount(); 1736 if (aSortParam.bByRow) 1737 { 1738 do 1739 { 1740 SCCOL nCol = static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField); 1741 nRes = 0; 1742 if(nCol < GetAllocatedColumnsCount()) 1743 { 1744 ScRefCellValue aCell1 = aCol[nCol].GetCellValue(nIndex1); 1745 ScRefCellValue aCell2 = aCol[nCol].GetCellValue(nIndex2); 1746 nRes = CompareCell(nSort, aCell1, nCol, nIndex1, aCell2, nCol, nIndex2); 1747 } 1748 } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort ); 1749 } 1750 else 1751 { 1752 do 1753 { 1754 SCROW nRow = aSortParam.maKeyState[nSort].nField; 1755 ScRefCellValue aCell1; 1756 ScRefCellValue aCell2; 1757 if(nIndex1 < GetAllocatedColumnsCount()) 1758 aCell1 = aCol[nIndex1].GetCellValue(nRow); 1759 if(nIndex2 < GetAllocatedColumnsCount()) 1760 aCell2 = aCol[nIndex2].GetCellValue(nRow); 1761 nRes = CompareCell( nSort, aCell1, static_cast<SCCOL>(nIndex1), 1762 nRow, aCell2, static_cast<SCCOL>(nIndex2), nRow ); 1763 } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort ); 1764 } 1765 return nRes; 1766 } 1767 1768 bool ScTable::IsSorted( SCCOLROW nStart, SCCOLROW nEnd ) const // over aSortParam 1769 { 1770 for (SCCOLROW i=nStart; i<nEnd; i++) 1771 { 1772 if (Compare( i, i+1 ) > 0) 1773 return false; 1774 } 1775 return true; 1776 } 1777 1778 void ScTable::DecoladeRow( ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2 ) 1779 { 1780 SCROW nRow; 1781 int nMax = nRow2 - nRow1; 1782 for (SCROW i = nRow1; (i + 4) <= nRow2; i += 4) 1783 { 1784 nRow = comphelper::rng::uniform_int_distribution(0, nMax-1); 1785 pArray->Swap(i, nRow1 + nRow); 1786 } 1787 } 1788 1789 void ScTable::Sort( 1790 const ScSortParam& rSortParam, bool bKeepQuery, bool bUpdateRefs, 1791 ScProgress* pProgress, sc::ReorderParam* pUndo ) 1792 { 1793 sc::DelayDeletingBroadcasters delayDeletingBroadcasters(GetDoc()); 1794 InitSortCollator( rSortParam ); 1795 bGlobalKeepQuery = bKeepQuery; 1796 1797 if (pUndo) 1798 { 1799 // Copy over the basic sort parameters. 1800 pUndo->maDataAreaExtras = rSortParam.aDataAreaExtras; 1801 pUndo->mbByRow = rSortParam.bByRow; 1802 pUndo->mbHiddenFiltered = bKeepQuery; 1803 pUndo->mbUpdateRefs = bUpdateRefs; 1804 pUndo->mbHasHeaders = rSortParam.bHasHeader; 1805 } 1806 1807 // It is assumed that the data area has already been trimmed as necessary. 1808 1809 aSortParam = rSortParam; // must be assigned before calling IsSorted() 1810 if (rSortParam.bByRow) 1811 { 1812 const SCROW nLastRow = rSortParam.nRow2; 1813 const SCROW nRow1 = (rSortParam.bHasHeader ? rSortParam.nRow1 + 1 : rSortParam.nRow1); 1814 if (nRow1 < nLastRow && !IsSorted(nRow1, nLastRow)) 1815 { 1816 if(pProgress) 1817 pProgress->SetState( 0, nLastRow-nRow1 ); 1818 1819 std::unique_ptr<ScSortInfoArray> pArray( CreateSortInfoArray( 1820 aSortParam, nRow1, nLastRow, bKeepQuery, bUpdateRefs)); 1821 1822 if ( nLastRow - nRow1 > 255 ) 1823 DecoladeRow(pArray.get(), nRow1, nLastRow); 1824 1825 QuickSort(pArray.get(), nRow1, nLastRow); 1826 if (pArray->IsUpdateRefs()) 1827 SortReorderByRowRefUpdate(pArray.get(), aSortParam.nCol1, aSortParam.nCol2, pProgress); 1828 else 1829 { 1830 SortReorderByRow(pArray.get(), aSortParam.nCol1, aSortParam.nCol2, pProgress, false); 1831 if (rSortParam.aDataAreaExtras.anyExtrasWanted()) 1832 SortReorderAreaExtrasByRow( pArray.get(), aSortParam.nCol1, aSortParam.nCol2, 1833 rSortParam.aDataAreaExtras, pProgress); 1834 } 1835 1836 if (pUndo) 1837 { 1838 // Stored is the first data row without header row. 1839 pUndo->maSortRange = ScRange(rSortParam.nCol1, nRow1, nTab, rSortParam.nCol2, nLastRow, nTab); 1840 pUndo->maDataAreaExtras.mnStartRow = nRow1; 1841 pUndo->maOrderIndices = pArray->GetOrderIndices(); 1842 } 1843 } 1844 } 1845 else 1846 { 1847 const SCCOL nLastCol = rSortParam.nCol2; 1848 const SCCOL nCol1 = (rSortParam.bHasHeader ? rSortParam.nCol1 + 1 : rSortParam.nCol1); 1849 if (nCol1 < nLastCol && !IsSorted(nCol1, nLastCol)) 1850 { 1851 if(pProgress) 1852 pProgress->SetState( 0, nLastCol-nCol1 ); 1853 1854 std::unique_ptr<ScSortInfoArray> pArray( CreateSortInfoArray( 1855 aSortParam, nCol1, nLastCol, bKeepQuery, bUpdateRefs)); 1856 1857 QuickSort(pArray.get(), nCol1, nLastCol); 1858 SortReorderByColumn(pArray.get(), rSortParam.nRow1, rSortParam.nRow2, 1859 rSortParam.aDataAreaExtras.mbCellFormats, pProgress); 1860 if (rSortParam.aDataAreaExtras.anyExtrasWanted() && !pArray->IsUpdateRefs()) 1861 SortReorderAreaExtrasByColumn( pArray.get(), 1862 rSortParam.nRow1, rSortParam.nRow2, rSortParam.aDataAreaExtras, pProgress); 1863 1864 if (pUndo) 1865 { 1866 // Stored is the first data column without header column. 1867 pUndo->maSortRange = ScRange(nCol1, aSortParam.nRow1, nTab, nLastCol, aSortParam.nRow2, nTab); 1868 pUndo->maDataAreaExtras.mnStartCol = nCol1; 1869 pUndo->maOrderIndices = pArray->GetOrderIndices(); 1870 } 1871 } 1872 } 1873 DestroySortCollator(); 1874 } 1875 1876 void ScTable::Reorder( const sc::ReorderParam& rParam ) 1877 { 1878 if (rParam.maOrderIndices.empty()) 1879 return; 1880 1881 std::unique_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(rParam)); 1882 if (!pArray) 1883 return; 1884 1885 if (rParam.mbByRow) 1886 { 1887 // Re-play sorting from the known sort indices. 1888 pArray->ReorderByRow(rParam.maOrderIndices); 1889 if (pArray->IsUpdateRefs()) 1890 SortReorderByRowRefUpdate( 1891 pArray.get(), rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(), nullptr); 1892 else 1893 { 1894 SortReorderByRow( pArray.get(), 1895 rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(), nullptr, false); 1896 if (rParam.maDataAreaExtras.anyExtrasWanted()) 1897 SortReorderAreaExtrasByRow( pArray.get(), 1898 rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(), 1899 rParam.maDataAreaExtras, nullptr); 1900 } 1901 } 1902 else 1903 { 1904 // Ordering by column is much simpler. Just set the order indices and we are done. 1905 pArray->SetOrderIndices(std::vector(rParam.maOrderIndices)); 1906 SortReorderByColumn( 1907 pArray.get(), rParam.maSortRange.aStart.Row(), rParam.maSortRange.aEnd.Row(), 1908 rParam.maDataAreaExtras.mbCellFormats, nullptr); 1909 if (rParam.maDataAreaExtras.anyExtrasWanted() && !pArray->IsUpdateRefs()) 1910 SortReorderAreaExtrasByColumn( pArray.get(), 1911 rParam.maSortRange.aStart.Row(), rParam.maSortRange.aEnd.Row(), 1912 rParam.maDataAreaExtras, nullptr); 1913 } 1914 } 1915 1916 namespace { 1917 1918 class SubTotalRowFinder 1919 { 1920 const ScTable& mrTab; 1921 const ScSubTotalParam& mrParam; 1922 1923 public: 1924 SubTotalRowFinder(const ScTable& rTab, const ScSubTotalParam& rParam) : 1925 mrTab(rTab), mrParam(rParam) {} 1926 1927 bool operator() (size_t nRow, const ScFormulaCell* pCell) 1928 { 1929 if (!pCell->IsSubTotal()) 1930 return false; 1931 1932 SCCOL nStartCol = mrParam.nCol1; 1933 SCCOL nEndCol = mrParam.nCol2; 1934 1935 for (SCCOL nCol : mrTab.GetAllocatedColumnsRange(0, nStartCol - 1)) 1936 { 1937 if (mrTab.HasData(nCol, nRow)) 1938 return true; 1939 } 1940 for (SCCOL nCol : mrTab.GetAllocatedColumnsRange(nEndCol + 1, mrTab.GetDoc().MaxCol())) 1941 { 1942 if (mrTab.HasData(nCol, nRow)) 1943 return true; 1944 } 1945 return false; 1946 } 1947 }; 1948 1949 } 1950 1951 bool ScTable::TestRemoveSubTotals( const ScSubTotalParam& rParam ) 1952 { 1953 SCCOL nStartCol = rParam.nCol1; 1954 SCROW nStartRow = rParam.nRow1 + 1; // Header 1955 SCCOL nEndCol = ClampToAllocatedColumns(rParam.nCol2); 1956 SCROW nEndRow = rParam.nRow2; 1957 1958 for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol) 1959 { 1960 const sc::CellStoreType& rCells = aCol[nCol].maCells; 1961 SubTotalRowFinder aFunc(*this, rParam); 1962 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = 1963 sc::FindFormula(rCells, nStartRow, nEndRow, aFunc); 1964 if (aPos.first != rCells.end()) 1965 return true; 1966 } 1967 return false; 1968 } 1969 1970 namespace { 1971 1972 struct RemoveSubTotalsHandler 1973 { 1974 std::set<SCROW> aRemoved; 1975 1976 void operator() (size_t nRow, const ScFormulaCell* p) 1977 { 1978 if (p->IsSubTotal()) 1979 aRemoved.insert(nRow); 1980 } 1981 }; 1982 1983 } 1984 1985 void ScTable::RemoveSubTotals( ScSubTotalParam& rParam ) 1986 { 1987 SCCOL nStartCol = rParam.nCol1; 1988 SCROW nStartRow = rParam.nRow1 + 1; // Header 1989 SCCOL nEndCol = ClampToAllocatedColumns(rParam.nCol2); 1990 SCROW nEndRow = rParam.nRow2; // will change 1991 1992 RemoveSubTotalsHandler aFunc; 1993 for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol) 1994 { 1995 const sc::CellStoreType& rCells = aCol[nCol].maCells; 1996 sc::ParseFormula(rCells.begin(), rCells, nStartRow, nEndRow, aFunc); 1997 } 1998 1999 auto& aRows = aFunc.aRemoved; 2000 2001 std::for_each(aRows.rbegin(), aRows.rend(), [this](const SCROW nRow) { 2002 RemoveRowBreak(nRow+1, false, true); 2003 rDocument.DeleteRow(0, nTab, rDocument.MaxCol(), nTab, nRow, 1); 2004 }); 2005 2006 rParam.nRow2 -= aRows.size(); 2007 } 2008 2009 // Delete hard number formats (for result formulas) 2010 2011 static void lcl_RemoveNumberFormat( ScTable* pTab, SCCOL nCol, SCROW nRow ) 2012 { 2013 const ScPatternAttr* pPattern = pTab->GetPattern( nCol, nRow ); 2014 if ( pPattern->GetItemSet().GetItemState( ATTR_VALUE_FORMAT, false ) 2015 == SfxItemState::SET ) 2016 { 2017 auto pNewPattern = std::make_unique<ScPatternAttr>( *pPattern ); 2018 SfxItemSet& rSet = pNewPattern->GetItemSet(); 2019 rSet.ClearItem( ATTR_VALUE_FORMAT ); 2020 rSet.ClearItem( ATTR_LANGUAGE_FORMAT ); 2021 pTab->SetPattern( nCol, nRow, std::move(pNewPattern) ); 2022 } 2023 } 2024 2025 namespace { 2026 2027 struct RowEntry 2028 { 2029 sal_uInt16 nGroupNo; 2030 SCROW nSubStartRow; 2031 SCROW nDestRow; 2032 SCROW nFuncStart; 2033 SCROW nFuncEnd; 2034 }; 2035 2036 } 2037 2038 static TranslateId lcl_GetSubTotalStrId(int id) 2039 { 2040 switch ( id ) 2041 { 2042 case SUBTOTAL_FUNC_AVE: return STR_FUN_TEXT_AVG; 2043 case SUBTOTAL_FUNC_CNT: 2044 case SUBTOTAL_FUNC_CNT2: return STR_FUN_TEXT_COUNT; 2045 case SUBTOTAL_FUNC_MAX: return STR_FUN_TEXT_MAX; 2046 case SUBTOTAL_FUNC_MIN: return STR_FUN_TEXT_MIN; 2047 case SUBTOTAL_FUNC_PROD: return STR_FUN_TEXT_PRODUCT; 2048 case SUBTOTAL_FUNC_STD: 2049 case SUBTOTAL_FUNC_STDP: return STR_FUN_TEXT_STDDEV; 2050 case SUBTOTAL_FUNC_SUM: return STR_FUN_TEXT_SUM; 2051 case SUBTOTAL_FUNC_VAR: 2052 case SUBTOTAL_FUNC_VARP: return STR_FUN_TEXT_VAR; 2053 default: 2054 { 2055 return STR_EMPTYDATA; 2056 // added to avoid warnings 2057 } 2058 } 2059 } 2060 2061 // new intermediate results 2062 // rParam.nRow2 is changed! 2063 2064 bool ScTable::DoSubTotals( ScSubTotalParam& rParam ) 2065 { 2066 SCCOL nStartCol = rParam.nCol1; 2067 SCROW nStartRow = rParam.nRow1 + 1; // Header 2068 SCCOL nEndCol = rParam.nCol2; 2069 SCROW nEndRow = rParam.nRow2; // will change 2070 sal_uInt16 i; 2071 2072 // Remove empty rows at the end 2073 // so that all exceeding (rDocument.MaxRow()) can be found by InsertRow (#35180#) 2074 // If sorted, all empty rows are at the end. 2075 SCSIZE nEmpty = GetEmptyLinesInBlock( nStartCol, nStartRow, nEndCol, nEndRow, DIR_BOTTOM ); 2076 nEndRow -= nEmpty; 2077 2078 sal_uInt16 nLevelCount = 0; // Number of levels 2079 bool bDoThis = true; 2080 for (i=0; i<MAXSUBTOTAL && bDoThis; i++) 2081 if (rParam.bGroupActive[i]) 2082 nLevelCount = i+1; 2083 else 2084 bDoThis = false; 2085 2086 if (nLevelCount==0) // do nothing 2087 return true; 2088 2089 SCCOL* nGroupCol = rParam.nField; // columns which will be used when grouping 2090 2091 // With (blank) as a separate category, subtotal rows from 2092 // the other columns must always be tested 2093 // (previously only when a column occurred more than once) 2094 bool bTestPrevSub = ( nLevelCount > 1 ); 2095 2096 OUString aSubString; 2097 2098 bool bIgnoreCase = !rParam.bCaseSens; 2099 2100 OUString aCompString[MAXSUBTOTAL]; 2101 2102 //TODO: sort? 2103 2104 ScStyleSheet* pStyle = static_cast<ScStyleSheet*>(rDocument.GetStyleSheetPool()->Find( 2105 ScResId(STR_STYLENAME_RESULT), SfxStyleFamily::Para )); 2106 2107 bool bSpaceLeft = true; // Success when inserting? 2108 2109 // For performance reasons collect formula entries so their 2110 // references don't have to be tested for updates each time a new row is 2111 // inserted 2112 RowEntry aRowEntry; 2113 ::std::vector< RowEntry > aRowVector; 2114 2115 for (sal_uInt16 nLevel=0; nLevel<nLevelCount && bSpaceLeft; nLevel++) 2116 { 2117 aRowEntry.nGroupNo = nLevelCount - nLevel - 1; 2118 2119 // how many results per level 2120 SCCOL nResCount = rParam.nSubTotals[aRowEntry.nGroupNo]; 2121 // result functions 2122 ScSubTotalFunc* pResFunc = rParam.pFunctions[aRowEntry.nGroupNo].get(); 2123 2124 if (nResCount > 0) // otherwise only sort 2125 { 2126 for (i=0; i<=aRowEntry.nGroupNo; i++) 2127 { 2128 aSubString = GetString( nGroupCol[i], nStartRow ); 2129 if ( bIgnoreCase ) 2130 aCompString[i] = ScGlobal::getCharClass().uppercase( aSubString ); 2131 else 2132 aCompString[i] = aSubString; 2133 } // aSubString stays on the last 2134 2135 bool bBlockVis = false; // group visible? 2136 aRowEntry.nSubStartRow = nStartRow; 2137 for (SCROW nRow=nStartRow; nRow<=nEndRow+1 && bSpaceLeft; nRow++) 2138 { 2139 bool bChanged; 2140 if (nRow>nEndRow) 2141 bChanged = true; 2142 else 2143 { 2144 bChanged = false; 2145 OUString aString; 2146 for (i=0; i<=aRowEntry.nGroupNo && !bChanged; i++) 2147 { 2148 aString = GetString( nGroupCol[i], nRow ); 2149 if (bIgnoreCase) 2150 aString = ScGlobal::getCharClass().uppercase(aString); 2151 // when sorting, blanks are separate group 2152 // otherwise blank cells are allowed below 2153 bChanged = ( ( !aString.isEmpty() || rParam.bDoSort ) && 2154 aString != aCompString[i] ); 2155 } 2156 if ( bChanged && bTestPrevSub ) 2157 { 2158 // No group change on rows that will contain subtotal formulas 2159 bChanged = std::none_of(aRowVector.begin(), aRowVector.end(), 2160 [&nRow](const RowEntry& rEntry) { return rEntry.nDestRow == nRow; }); 2161 } 2162 } 2163 if ( bChanged ) 2164 { 2165 aRowEntry.nDestRow = nRow; 2166 aRowEntry.nFuncStart = aRowEntry.nSubStartRow; 2167 aRowEntry.nFuncEnd = nRow-1; 2168 2169 bSpaceLeft = rDocument.InsertRow( 0, nTab, rDocument.MaxCol(), nTab, 2170 aRowEntry.nDestRow, 1 ); 2171 DBShowRow( aRowEntry.nDestRow, bBlockVis ); 2172 if ( rParam.bPagebreak && nRow < rDocument.MaxRow() && 2173 aRowEntry.nSubStartRow != nStartRow && nLevel == 0) 2174 SetRowBreak(aRowEntry.nSubStartRow, false, true); 2175 2176 if (bSpaceLeft) 2177 { 2178 for ( auto& rRowEntry : aRowVector) 2179 { 2180 if ( aRowEntry.nDestRow <= rRowEntry.nSubStartRow ) 2181 ++rRowEntry.nSubStartRow; 2182 if ( aRowEntry.nDestRow <= rRowEntry.nDestRow ) 2183 ++rRowEntry.nDestRow; 2184 if ( aRowEntry.nDestRow <= rRowEntry.nFuncStart ) 2185 ++rRowEntry.nFuncStart; 2186 if ( aRowEntry.nDestRow <= rRowEntry.nFuncEnd ) 2187 ++rRowEntry.nFuncEnd; 2188 } 2189 // collect formula positions 2190 aRowVector.push_back( aRowEntry ); 2191 2192 OUString aOutString = aSubString; 2193 if (aOutString.isEmpty()) 2194 aOutString = ScResId( STR_EMPTYDATA ); 2195 aOutString += " "; 2196 TranslateId pStrId = STR_TABLE_ERGEBNIS; 2197 if ( nResCount == 1 ) 2198 pStrId = lcl_GetSubTotalStrId(pResFunc[0]); 2199 aOutString += ScResId(pStrId); 2200 SetString( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, nTab, aOutString ); 2201 ApplyStyle( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, pStyle ); 2202 2203 ++nRow; 2204 ++nEndRow; 2205 aRowEntry.nSubStartRow = nRow; 2206 for (i=0; i<=aRowEntry.nGroupNo; i++) 2207 { 2208 aSubString = GetString( nGroupCol[i], nRow ); 2209 if ( bIgnoreCase ) 2210 aCompString[i] = ScGlobal::getCharClass().uppercase( aSubString ); 2211 else 2212 aCompString[i] = aSubString; 2213 } 2214 } 2215 } 2216 bBlockVis = !RowFiltered(nRow); 2217 } 2218 } 2219 } 2220 2221 if (!aRowVector.empty()) 2222 { 2223 // generate global total 2224 SCROW nGlobalStartRow = aRowVector[0].nSubStartRow; 2225 SCROW nGlobalStartFunc = aRowVector[0].nFuncStart; 2226 SCROW nGlobalEndRow = 0; 2227 SCROW nGlobalEndFunc = 0; 2228 for (const auto& rRowEntry : aRowVector) 2229 { 2230 nGlobalEndRow = (nGlobalEndRow < rRowEntry.nDestRow) ? rRowEntry.nDestRow : nGlobalEndRow; 2231 nGlobalEndFunc = (nGlobalEndFunc < rRowEntry.nFuncEnd) ? rRowEntry.nFuncEnd : nGlobalEndRow; 2232 } 2233 2234 for (sal_uInt16 nLevel = 0; nLevel<nLevelCount; nLevel++) 2235 { 2236 const sal_uInt16 nGroupNo = nLevelCount - nLevel - 1; 2237 const ScSubTotalFunc* pResFunc = rParam.pFunctions[nGroupNo].get(); 2238 if (!pResFunc) 2239 { 2240 // No subtotal function given for this group => no formula or 2241 // label and do not insert a row. 2242 continue; 2243 } 2244 2245 // increment end row 2246 nGlobalEndRow++; 2247 2248 // add row entry for formula 2249 aRowEntry.nGroupNo = nGroupNo; 2250 aRowEntry.nSubStartRow = nGlobalStartRow; 2251 aRowEntry.nFuncStart = nGlobalStartFunc; 2252 aRowEntry.nDestRow = nGlobalEndRow; 2253 aRowEntry.nFuncEnd = nGlobalEndFunc; 2254 2255 // increment row 2256 nGlobalEndFunc++; 2257 2258 bSpaceLeft = rDocument.InsertRow(0, nTab, rDocument.MaxCol(), nTab, aRowEntry.nDestRow, 1); 2259 2260 if (bSpaceLeft) 2261 { 2262 aRowVector.push_back(aRowEntry); 2263 nEndRow++; 2264 DBShowRow(aRowEntry.nDestRow, true); 2265 2266 // insert label 2267 OUString label = ScResId(STR_TABLE_GRAND) + " " + ScResId(lcl_GetSubTotalStrId(pResFunc[0])); 2268 SetString(nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, nTab, label); 2269 ApplyStyle(nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, pStyle); 2270 } 2271 } 2272 } 2273 2274 // now insert the formulas 2275 ScComplexRefData aRef; 2276 aRef.InitFlags(); 2277 aRef.Ref1.SetAbsTab(nTab); 2278 aRef.Ref2.SetAbsTab(nTab); 2279 for (const auto& rRowEntry : aRowVector) 2280 { 2281 SCCOL nResCount = rParam.nSubTotals[rRowEntry.nGroupNo]; 2282 SCCOL* nResCols = rParam.pSubTotals[rRowEntry.nGroupNo].get(); 2283 ScSubTotalFunc* pResFunc = rParam.pFunctions[rRowEntry.nGroupNo].get(); 2284 for ( SCCOL nResult=0; nResult < nResCount; ++nResult ) 2285 { 2286 aRef.Ref1.SetAbsCol(nResCols[nResult]); 2287 aRef.Ref1.SetAbsRow(rRowEntry.nFuncStart); 2288 aRef.Ref2.SetAbsCol(nResCols[nResult]); 2289 aRef.Ref2.SetAbsRow(rRowEntry.nFuncEnd); 2290 2291 ScTokenArray aArr(rDocument); 2292 aArr.AddOpCode( ocSubTotal ); 2293 aArr.AddOpCode( ocOpen ); 2294 aArr.AddDouble( static_cast<double>(pResFunc[nResult]) ); 2295 aArr.AddOpCode( ocSep ); 2296 aArr.AddDoubleReference( aRef ); 2297 aArr.AddOpCode( ocClose ); 2298 aArr.AddOpCode( ocStop ); 2299 ScFormulaCell* pCell = new ScFormulaCell( 2300 rDocument, ScAddress(nResCols[nResult], rRowEntry.nDestRow, nTab), aArr); 2301 if ( rParam.bIncludePattern ) 2302 pCell->SetNeedNumberFormat(true); 2303 2304 SetFormulaCell(nResCols[nResult], rRowEntry.nDestRow, pCell); 2305 if ( nResCols[nResult] != nGroupCol[rRowEntry.nGroupNo] ) 2306 { 2307 ApplyStyle( nResCols[nResult], rRowEntry.nDestRow, pStyle ); 2308 2309 lcl_RemoveNumberFormat( this, nResCols[nResult], rRowEntry.nDestRow ); 2310 } 2311 } 2312 2313 } 2314 2315 //TODO: according to setting, shift intermediate-sum rows up? 2316 2317 //TODO: create Outlines directly? 2318 2319 if (bSpaceLeft) 2320 DoAutoOutline( nStartCol, nStartRow, nEndCol, nEndRow ); 2321 2322 rParam.nRow2 = nEndRow; // new end 2323 return bSpaceLeft; 2324 } 2325 2326 void ScTable::TopTenQuery( ScQueryParam& rParam ) 2327 { 2328 bool bSortCollatorInitialized = false; 2329 SCSIZE nEntryCount = rParam.GetEntryCount(); 2330 SCROW nRow1 = (rParam.bHasHeader ? rParam.nRow1 + 1 : rParam.nRow1); 2331 SCSIZE nCount = static_cast<SCSIZE>(rParam.nRow2 - nRow1 + 1); 2332 for ( SCSIZE i=0; (i<nEntryCount) && (rParam.GetEntry(i).bDoQuery); i++ ) 2333 { 2334 ScQueryEntry& rEntry = rParam.GetEntry(i); 2335 ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems(); 2336 2337 for (ScQueryEntry::Item& rItem : rItems) 2338 { 2339 switch (rEntry.eOp) 2340 { 2341 case SC_TOPVAL: 2342 case SC_BOTVAL: 2343 case SC_TOPPERC: 2344 case SC_BOTPERC: 2345 { 2346 ScSortParam aLocalSortParam(rParam, static_cast<SCCOL>(rEntry.nField)); 2347 aSortParam = aLocalSortParam; // used in CreateSortInfoArray, Compare 2348 if (!bSortCollatorInitialized) 2349 { 2350 bSortCollatorInitialized = true; 2351 InitSortCollator(aLocalSortParam); 2352 } 2353 std::unique_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(aSortParam, nRow1, rParam.nRow2, bGlobalKeepQuery, false)); 2354 DecoladeRow(pArray.get(), nRow1, rParam.nRow2); 2355 QuickSort(pArray.get(), nRow1, rParam.nRow2); 2356 std::unique_ptr<ScSortInfo[]> const& ppInfo = pArray->GetFirstArray(); 2357 SCSIZE nValidCount = nCount; 2358 // Don't count note or blank cells, they are sorted to the end 2359 while (nValidCount > 0 && ppInfo[nValidCount - 1].maCell.isEmpty()) 2360 nValidCount--; 2361 // Don't count Strings, they are between Value and blank 2362 while (nValidCount > 0 && ppInfo[nValidCount - 1].maCell.hasString()) 2363 nValidCount--; 2364 if (nValidCount > 0) 2365 { 2366 if (rItem.meType == ScQueryEntry::ByString) 2367 { // by string ain't going to work 2368 rItem.meType = ScQueryEntry::ByValue; 2369 rItem.mfVal = 10; // 10 and 10% respectively 2370 } 2371 SCSIZE nVal = (rItem.mfVal >= 1 ? static_cast<SCSIZE>(rItem.mfVal) : 1); 2372 SCSIZE nOffset = 0; 2373 switch (rEntry.eOp) 2374 { 2375 case SC_TOPVAL: 2376 { 2377 rEntry.eOp = SC_GREATER_EQUAL; 2378 if (nVal > nValidCount) 2379 nVal = nValidCount; 2380 nOffset = nValidCount - nVal; // 1 <= nVal <= nValidCount 2381 } 2382 break; 2383 case SC_BOTVAL: 2384 { 2385 rEntry.eOp = SC_LESS_EQUAL; 2386 if (nVal > nValidCount) 2387 nVal = nValidCount; 2388 nOffset = nVal - 1; // 1 <= nVal <= nValidCount 2389 } 2390 break; 2391 case SC_TOPPERC: 2392 { 2393 rEntry.eOp = SC_GREATER_EQUAL; 2394 if (nVal > 100) 2395 nVal = 100; 2396 nOffset = nValidCount - (nValidCount * nVal / 100); 2397 if (nOffset >= nValidCount) 2398 nOffset = nValidCount - 1; 2399 } 2400 break; 2401 case SC_BOTPERC: 2402 { 2403 rEntry.eOp = SC_LESS_EQUAL; 2404 if (nVal > 100) 2405 nVal = 100; 2406 nOffset = (nValidCount * nVal / 100); 2407 if (nOffset >= nValidCount) 2408 nOffset = nValidCount - 1; 2409 } 2410 break; 2411 default: 2412 { 2413 // added to avoid warnings 2414 } 2415 } 2416 ScRefCellValue aCell = ppInfo[nOffset].maCell; 2417 if (aCell.hasNumeric()) 2418 rItem.mfVal = aCell.getValue(); 2419 else 2420 { 2421 OSL_FAIL("TopTenQuery: pCell no ValueData"); 2422 rEntry.eOp = SC_GREATER_EQUAL; 2423 rItem.mfVal = 0; 2424 } 2425 } 2426 else 2427 { 2428 rEntry.eOp = SC_GREATER_EQUAL; 2429 rItem.meType = ScQueryEntry::ByValue; 2430 rItem.mfVal = 0; 2431 } 2432 } 2433 break; 2434 default: 2435 { 2436 // added to avoid warnings 2437 } 2438 } 2439 } 2440 } 2441 if ( bSortCollatorInitialized ) 2442 DestroySortCollator(); 2443 } 2444 2445 namespace { 2446 2447 bool CanOptimizeQueryStringToNumber( const SvNumberFormatter* pFormatter, sal_uInt32 nFormatIndex, bool& bDateFormat ) 2448 { 2449 // tdf#105629: ScQueryEntry::ByValue queries are faster than ScQueryEntry::ByString. 2450 // The problem with this optimization is that the autofilter dialog apparently converts 2451 // the value to text and then converts that back to a number for filtering. 2452 // If that leads to any change of value (such as when time is rounded to seconds), 2453 // even matching values will be filtered out. Therefore query by value only for formats 2454 // where no such change should occur. 2455 if(const SvNumberformat* pEntry = pFormatter->GetEntry(nFormatIndex)) 2456 { 2457 switch(pEntry->GetType()) 2458 { 2459 case SvNumFormatType::NUMBER: 2460 case SvNumFormatType::FRACTION: 2461 case SvNumFormatType::SCIENTIFIC: 2462 return true; 2463 case SvNumFormatType::DATE: 2464 case SvNumFormatType::DATETIME: 2465 bDateFormat = true; 2466 break; 2467 default: 2468 break; 2469 } 2470 } 2471 return false; 2472 } 2473 2474 class PrepareQueryItem 2475 { 2476 const ScDocument& mrDoc; 2477 const bool mbRoundForFilter; 2478 public: 2479 explicit PrepareQueryItem(const ScDocument& rDoc, bool bRoundForFilter) : 2480 mrDoc(rDoc), mbRoundForFilter(bRoundForFilter) {} 2481 2482 void operator() (ScQueryEntry::Item& rItem) 2483 { 2484 rItem.mbRoundForFilter = mbRoundForFilter; 2485 2486 if (rItem.meType != ScQueryEntry::ByString && rItem.meType != ScQueryEntry::ByDate) 2487 return; 2488 2489 sal_uInt32 nIndex = 0; 2490 bool bNumber = mrDoc.GetFormatTable()-> 2491 IsNumberFormat(rItem.maString.getString(), nIndex, rItem.mfVal); 2492 2493 // Advanced Filter creates only ByString queries that need to be 2494 // converted to ByValue if appropriate. rItem.mfVal now holds the value 2495 // if bNumber==true. 2496 2497 if (rItem.meType == ScQueryEntry::ByString) 2498 { 2499 bool bDateFormat = false; 2500 if (bNumber && CanOptimizeQueryStringToNumber( mrDoc.GetFormatTable(), nIndex, bDateFormat )) 2501 rItem.meType = ScQueryEntry::ByValue; 2502 if (!bDateFormat) 2503 return; 2504 } 2505 2506 // Double-check if the query by date is really appropriate. 2507 2508 if (bNumber && ((nIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0)) 2509 { 2510 const SvNumberformat* pEntry = mrDoc.GetFormatTable()->GetEntry(nIndex); 2511 if (pEntry) 2512 { 2513 SvNumFormatType nNumFmtType = pEntry->GetType(); 2514 if (!(nNumFmtType & SvNumFormatType::DATE) || (nNumFmtType & SvNumFormatType::TIME)) 2515 rItem.meType = ScQueryEntry::ByValue; // not a date only 2516 else 2517 rItem.meType = ScQueryEntry::ByDate; // date only 2518 } 2519 else 2520 rItem.meType = ScQueryEntry::ByValue; // what the ... not a date 2521 } 2522 else 2523 rItem.meType = ScQueryEntry::ByValue; // not a date 2524 } 2525 }; 2526 2527 void lcl_PrepareQuery( const ScDocument* pDoc, ScTable* pTab, ScQueryParam& rParam, bool bRoundForFilter ) 2528 { 2529 bool bTopTen = false; 2530 SCSIZE nEntryCount = rParam.GetEntryCount(); 2531 2532 for ( SCSIZE i = 0; i < nEntryCount; ++i ) 2533 { 2534 ScQueryEntry& rEntry = rParam.GetEntry(i); 2535 if (!rEntry.bDoQuery) 2536 continue; 2537 2538 ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems(); 2539 std::for_each(rItems.begin(), rItems.end(), PrepareQueryItem(*pDoc, bRoundForFilter)); 2540 2541 if ( !bTopTen ) 2542 { 2543 switch ( rEntry.eOp ) 2544 { 2545 case SC_TOPVAL: 2546 case SC_BOTVAL: 2547 case SC_TOPPERC: 2548 case SC_BOTPERC: 2549 { 2550 bTopTen = true; 2551 } 2552 break; 2553 default: 2554 { 2555 } 2556 } 2557 } 2558 } 2559 2560 if ( bTopTen ) 2561 { 2562 pTab->TopTenQuery( rParam ); 2563 } 2564 } 2565 2566 } 2567 2568 void ScTable::PrepareQuery( ScQueryParam& rQueryParam ) 2569 { 2570 lcl_PrepareQuery(&rDocument, this, rQueryParam, false); 2571 } 2572 2573 SCSIZE ScTable::Query(const ScQueryParam& rParamOrg, bool bKeepSub) 2574 { 2575 ScQueryParam aParam( rParamOrg ); 2576 typedef std::unordered_set<OUString> StrSetType; 2577 StrSetType aStrSet; 2578 2579 bool bStarted = false; 2580 bool bOldResult = true; 2581 SCROW nOldStart = 0; 2582 SCROW nOldEnd = 0; 2583 2584 SCSIZE nCount = 0; 2585 SCROW nOutRow = 0; 2586 SCROW nHeader = aParam.bHasHeader ? 1 : 0; 2587 2588 lcl_PrepareQuery(&rDocument, this, aParam, true); 2589 2590 if (!aParam.bInplace) 2591 { 2592 nOutRow = aParam.nDestRow + nHeader; 2593 if (nHeader > 0) 2594 CopyData( aParam.nCol1, aParam.nRow1, aParam.nCol2, aParam.nRow1, 2595 aParam.nDestCol, aParam.nDestRow, aParam.nDestTab ); 2596 } 2597 2598 sc::TableColumnBlockPositionSet blockPos( GetDoc(), nTab ); // cache mdds access 2599 ScQueryEvaluator queryEvaluator(GetDoc(), *this, aParam); 2600 2601 SCROW nRealRow2 = aParam.nRow2; 2602 for (SCROW j = aParam.nRow1 + nHeader; j <= nRealRow2; ++j) 2603 { 2604 bool bResult; // Filter result 2605 bool bValid = queryEvaluator.ValidQuery(j, nullptr, &blockPos); 2606 if (!bValid && bKeepSub) // Keep subtotals 2607 { 2608 for (SCCOL nCol=aParam.nCol1; nCol<=aParam.nCol2 && !bValid; nCol++) 2609 { 2610 ScRefCellValue aCell = GetCellValue(nCol, j); 2611 if (aCell.getType() != CELLTYPE_FORMULA) 2612 continue; 2613 2614 if (!aCell.getFormula()->IsSubTotal()) 2615 continue; 2616 2617 if (RefVisible(aCell.getFormula())) 2618 bValid = true; 2619 } 2620 } 2621 if (bValid) 2622 { 2623 if (aParam.bDuplicate) 2624 bResult = true; 2625 else 2626 { 2627 OUStringBuffer aStr; 2628 for (SCCOL k=aParam.nCol1; k <= aParam.nCol2; k++) 2629 { 2630 OUString aCellStr = GetString(k, j); 2631 aStr.append(aCellStr + u"\x0001"); 2632 } 2633 2634 bResult = aStrSet.insert(aStr.makeStringAndClear()).second; // unique if inserted. 2635 } 2636 } 2637 else 2638 bResult = false; 2639 2640 if (aParam.bInplace) 2641 { 2642 if (bResult == bOldResult && bStarted) 2643 nOldEnd = j; 2644 else 2645 { 2646 if (bStarted) 2647 DBShowRows(nOldStart,nOldEnd, bOldResult); 2648 nOldStart = nOldEnd = j; 2649 bOldResult = bResult; 2650 } 2651 bStarted = true; 2652 } 2653 else 2654 { 2655 if (bResult) 2656 { 2657 CopyData( aParam.nCol1,j, aParam.nCol2,j, aParam.nDestCol,nOutRow,aParam.nDestTab ); 2658 if( nTab == aParam.nDestTab ) // copy to self, changes may invalidate caching position hints 2659 blockPos.invalidate(); 2660 ++nOutRow; 2661 } 2662 } 2663 if (bResult) 2664 ++nCount; 2665 } 2666 2667 if (aParam.bInplace && bStarted) 2668 DBShowRows(nOldStart,nOldEnd, bOldResult); 2669 2670 if (aParam.bInplace) 2671 SetDrawPageSize(); 2672 2673 return nCount; 2674 } 2675 2676 bool ScTable::CreateExcelQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam) 2677 { 2678 bool bValid = true; 2679 std::unique_ptr<SCCOL[]> pFields(new SCCOL[nCol2-nCol1+1]); 2680 OUString aCellStr; 2681 SCCOL nCol = nCol1; 2682 OSL_ENSURE( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" ); 2683 SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab); 2684 SCROW nDBRow1 = rQueryParam.nRow1; 2685 SCCOL nDBCol2 = rQueryParam.nCol2; 2686 // First row must be column headers 2687 while (bValid && (nCol <= nCol2)) 2688 { 2689 OUString aQueryStr = GetUpperCellString(nCol, nRow1); 2690 bool bFound = false; 2691 SCCOL i = rQueryParam.nCol1; 2692 while (!bFound && (i <= nDBCol2)) 2693 { 2694 if ( nTab == nDBTab ) 2695 aCellStr = GetUpperCellString(i, nDBRow1); 2696 else 2697 aCellStr = rDocument.GetUpperCellString(i, nDBRow1, nDBTab); 2698 bFound = (aCellStr == aQueryStr); 2699 if (!bFound) i++; 2700 } 2701 if (bFound) 2702 pFields[nCol - nCol1] = i; 2703 else 2704 bValid = false; 2705 nCol++; 2706 } 2707 if (bValid) 2708 { 2709 sal_uLong nVisible = 0; 2710 for ( nCol=nCol1; nCol<=ClampToAllocatedColumns(nCol2); nCol++ ) 2711 nVisible += aCol[nCol].VisibleCount( nRow1+1, nRow2 ); 2712 2713 if ( nVisible > SCSIZE_MAX / sizeof(void*) ) 2714 { 2715 OSL_FAIL("too many filter criteria"); 2716 nVisible = 0; 2717 } 2718 2719 SCSIZE nNewEntries = nVisible; 2720 rQueryParam.Resize( nNewEntries ); 2721 2722 SCSIZE nIndex = 0; 2723 SCROW nRow = nRow1 + 1; 2724 svl::SharedStringPool& rPool = rDocument.GetSharedStringPool(); 2725 while (nRow <= nRow2) 2726 { 2727 nCol = nCol1; 2728 while (nCol <= nCol2) 2729 { 2730 aCellStr = GetInputString( nCol, nRow ); 2731 if (!aCellStr.isEmpty()) 2732 { 2733 if (nIndex < nNewEntries) 2734 { 2735 rQueryParam.GetEntry(nIndex).nField = pFields[nCol - nCol1]; 2736 rQueryParam.FillInExcelSyntax(rPool, aCellStr, nIndex, nullptr); 2737 nIndex++; 2738 if (nIndex < nNewEntries) 2739 rQueryParam.GetEntry(nIndex).eConnect = SC_AND; 2740 } 2741 else 2742 bValid = false; 2743 } 2744 nCol++; 2745 } 2746 nRow++; 2747 if (nIndex < nNewEntries) 2748 rQueryParam.GetEntry(nIndex).eConnect = SC_OR; 2749 } 2750 } 2751 return bValid; 2752 } 2753 2754 bool ScTable::CreateStarQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam) 2755 { 2756 // A valid StarQuery must be at least 4 columns wide. To be precise it 2757 // should be exactly 4 columns ... 2758 // Additionally, if this wasn't checked, a formula pointing to a valid 1-3 2759 // column Excel style query range immediately left to itself would result 2760 // in a circular reference when the field name or operator or value (first 2761 // to third query range column) is obtained (#i58354#). Furthermore, if the 2762 // range wasn't sufficiently specified data changes wouldn't flag formula 2763 // cells for recalculation. 2764 if (nCol2 - nCol1 < 3) 2765 return false; 2766 2767 bool bValid; 2768 OUString aCellStr; 2769 SCSIZE nIndex = 0; 2770 SCROW nRow = nRow1; 2771 OSL_ENSURE( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" ); 2772 SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab); 2773 SCROW nDBRow1 = rQueryParam.nRow1; 2774 SCCOL nDBCol2 = rQueryParam.nCol2; 2775 2776 SCSIZE nNewEntries = static_cast<SCSIZE>(nRow2-nRow1+1); 2777 rQueryParam.Resize( nNewEntries ); 2778 svl::SharedStringPool& rPool = rDocument.GetSharedStringPool(); 2779 2780 do 2781 { 2782 ScQueryEntry& rEntry = rQueryParam.GetEntry(nIndex); 2783 2784 bValid = false; 2785 // First column AND/OR 2786 if (nIndex > 0) 2787 { 2788 aCellStr = GetUpperCellString(nCol1, nRow); 2789 if ( aCellStr == ScResId(STR_TABLE_AND) ) 2790 { 2791 rEntry.eConnect = SC_AND; 2792 bValid = true; 2793 } 2794 else if ( aCellStr == ScResId(STR_TABLE_OR) ) 2795 { 2796 rEntry.eConnect = SC_OR; 2797 bValid = true; 2798 } 2799 } 2800 // Second column field name 2801 if ((nIndex < 1) || bValid) 2802 { 2803 bool bFound = false; 2804 aCellStr = GetUpperCellString(nCol1 + 1, nRow); 2805 for (SCCOL i=rQueryParam.nCol1; (i <= nDBCol2) && (!bFound); i++) 2806 { 2807 OUString aFieldStr; 2808 if ( nTab == nDBTab ) 2809 aFieldStr = GetUpperCellString(i, nDBRow1); 2810 else 2811 aFieldStr = rDocument.GetUpperCellString(i, nDBRow1, nDBTab); 2812 bFound = (aCellStr == aFieldStr); 2813 if (bFound) 2814 { 2815 rEntry.nField = i; 2816 bValid = true; 2817 } 2818 else 2819 bValid = false; 2820 } 2821 } 2822 // Third column operator =<>... 2823 if (bValid) 2824 { 2825 aCellStr = GetUpperCellString(nCol1 + 2, nRow); 2826 if (aCellStr.startsWith("<")) 2827 { 2828 if (aCellStr[1] == '>') 2829 rEntry.eOp = SC_NOT_EQUAL; 2830 else if (aCellStr[1] == '=') 2831 rEntry.eOp = SC_LESS_EQUAL; 2832 else 2833 rEntry.eOp = SC_LESS; 2834 } 2835 else if (aCellStr.startsWith(">")) 2836 { 2837 if (aCellStr[1] == '=') 2838 rEntry.eOp = SC_GREATER_EQUAL; 2839 else 2840 rEntry.eOp = SC_GREATER; 2841 } 2842 else if (aCellStr.startsWith("=")) 2843 rEntry.eOp = SC_EQUAL; 2844 2845 } 2846 // Fourth column values 2847 if (bValid) 2848 { 2849 OUString aStr = GetString(nCol1 + 3, nRow); 2850 rEntry.GetQueryItem().maString = rPool.intern(aStr); 2851 rEntry.bDoQuery = true; 2852 } 2853 nIndex++; 2854 nRow++; 2855 } 2856 while (bValid && (nRow <= nRow2) /* && (nIndex < MAXQUERY) */ ); 2857 return bValid; 2858 } 2859 2860 bool ScTable::CreateQueryParam(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam) 2861 { 2862 SCSIZE i, nCount; 2863 PutInOrder(nCol1, nCol2); 2864 PutInOrder(nRow1, nRow2); 2865 2866 nCount = rQueryParam.GetEntryCount(); 2867 for (i=0; i < nCount; i++) 2868 rQueryParam.GetEntry(i).Clear(); 2869 2870 // Standard query table 2871 bool bValid = CreateStarQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam); 2872 // Excel Query table 2873 if (!bValid) 2874 bValid = CreateExcelQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam); 2875 2876 SvNumberFormatter* pFormatter = rDocument.GetFormatTable(); 2877 nCount = rQueryParam.GetEntryCount(); 2878 if (bValid) 2879 { 2880 // query type must be set 2881 for (i=0; i < nCount; i++) 2882 { 2883 ScQueryEntry::Item& rItem = rQueryParam.GetEntry(i).GetQueryItem(); 2884 sal_uInt32 nIndex = 0; 2885 bool bNumber = pFormatter->IsNumberFormat( 2886 rItem.maString.getString(), nIndex, rItem.mfVal); 2887 bool bDateFormat = false; 2888 rItem.meType = bNumber && CanOptimizeQueryStringToNumber( pFormatter, nIndex, bDateFormat ) 2889 ? ScQueryEntry::ByValue : (bDateFormat ? ScQueryEntry::ByDate : ScQueryEntry::ByString); 2890 } 2891 } 2892 else 2893 { 2894 for (i=0; i < nCount; i++) 2895 rQueryParam.GetEntry(i).Clear(); 2896 } 2897 return bValid; 2898 } 2899 2900 bool ScTable::HasColHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow) const 2901 { 2902 if (nStartRow == nEndRow) 2903 // Assume only data. 2904 /* XXX NOTE: previous behavior still checked this one row and could 2905 * evaluate it has header row, but that doesn't make much sense. */ 2906 return false; 2907 2908 for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++) 2909 { 2910 CellType eType = GetCellType( nCol, nStartRow ); 2911 // Any non-text cell in first row => not headers. 2912 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT) 2913 return false; 2914 } 2915 2916 // First row all text cells, any non-text cell in second row => headers. 2917 SCROW nTestRow = nStartRow + 1; 2918 for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++) 2919 { 2920 CellType eType = GetCellType( nCol, nTestRow ); 2921 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT) 2922 return true; 2923 } 2924 2925 // Also second row all text cells => first row not headers. 2926 return false; 2927 } 2928 2929 bool ScTable::HasRowHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) const 2930 { 2931 if (nStartCol == nEndCol) 2932 // Assume only data. 2933 /* XXX NOTE: previous behavior still checked this one column and could 2934 * evaluate it has header column, but that doesn't make much sense. */ 2935 return false; 2936 2937 for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++) 2938 { 2939 CellType eType = GetCellType( nStartCol, nRow ); 2940 // Any non-text cell in first column => not headers. 2941 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT) 2942 return false; 2943 } 2944 2945 // First column all text cells, any non-text cell in second column => headers. 2946 SCCOL nTestCol = nStartCol + 1; 2947 for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++) 2948 { 2949 CellType eType = GetCellType( nRow, nTestCol ); 2950 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT) 2951 return true; 2952 } 2953 2954 // Also second column all text cells => first column not headers. 2955 return false; 2956 } 2957 2958 void ScTable::GetFilterEntries( SCCOL nCol, SCROW nRow1, SCROW nRow2, ScFilterEntries& rFilterEntries, bool bFiltering ) 2959 { 2960 if (nCol >= aCol.size()) 2961 return; 2962 2963 sc::ColumnBlockConstPosition aBlockPos; 2964 aCol[nCol].InitBlockPosition(aBlockPos); 2965 aCol[nCol].GetFilterEntries(aBlockPos, nRow1, nRow2, rFilterEntries, bFiltering, false /*bFilteredRow*/); 2966 } 2967 2968 void ScTable::GetFilteredFilterEntries( 2969 SCCOL nCol, SCROW nRow1, SCROW nRow2, const ScQueryParam& rParam, ScFilterEntries& rFilterEntries, bool bFiltering ) 2970 { 2971 if (nCol >= aCol.size()) 2972 return; 2973 2974 sc::ColumnBlockConstPosition aBlockPos; 2975 aCol[nCol].InitBlockPosition(aBlockPos); 2976 2977 // remove the entry for this column from the query parameter 2978 ScQueryParam aParam( rParam ); 2979 aParam.RemoveEntryByField(nCol); 2980 2981 lcl_PrepareQuery(&rDocument, this, aParam, true); 2982 ScQueryEvaluator queryEvaluator(GetDoc(), *this, aParam); 2983 for ( SCROW j = nRow1; j <= nRow2; ++j ) 2984 { 2985 if (queryEvaluator.ValidQuery(j)) 2986 { 2987 aCol[nCol].GetFilterEntries(aBlockPos, j, j, rFilterEntries, bFiltering, false/*bFilteredRow*/); 2988 } 2989 else 2990 { 2991 aCol[nCol].GetFilterEntries(aBlockPos, j, j, rFilterEntries, bFiltering, true/*bFilteredRow*/); 2992 } 2993 } 2994 } 2995 2996 bool ScTable::GetDataEntries(SCCOL nCol, SCROW nRow, std::set<ScTypedStrData>& rStrings) 2997 { 2998 if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount()) 2999 return false; 3000 return aCol[nCol].GetDataEntries( nRow, rStrings); 3001 } 3002 3003 sal_uInt64 ScTable::GetCellCount() const 3004 { 3005 sal_uInt64 nCellCount = 0; 3006 3007 for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ ) 3008 nCellCount += aCol[nCol].GetCellCount(); 3009 3010 return nCellCount; 3011 } 3012 3013 sal_uInt64 ScTable::GetWeightedCount() const 3014 { 3015 sal_uInt64 nCellCount = 0; 3016 3017 for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ ) 3018 nCellCount += aCol[nCol].GetWeightedCount(); 3019 3020 return nCellCount; 3021 } 3022 3023 sal_uInt64 ScTable::GetWeightedCount(SCROW nStartRow, SCROW nEndRow) const 3024 { 3025 sal_uInt64 nCellCount = 0; 3026 3027 for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ ) 3028 nCellCount += aCol[nCol].GetWeightedCount(nStartRow, nEndRow); 3029 3030 return nCellCount; 3031 } 3032 3033 sal_uInt64 ScTable::GetCodeCount() const 3034 { 3035 sal_uInt64 nCodeCount = 0; 3036 3037 for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ ) 3038 if ( aCol[nCol].GetCellCount() ) 3039 nCodeCount += aCol[nCol].GetCodeCount(); 3040 3041 return nCodeCount; 3042 } 3043 3044 sal_Int32 ScTable::GetMaxStringLen( SCCOL nCol, SCROW nRowStart, 3045 SCROW nRowEnd, rtl_TextEncoding eCharSet ) const 3046 { 3047 if ( IsColValid( nCol ) ) 3048 return aCol[nCol].GetMaxStringLen( nRowStart, nRowEnd, eCharSet ); 3049 else 3050 return 0; 3051 } 3052 3053 sal_Int32 ScTable::GetMaxNumberStringLen( 3054 sal_uInt16& nPrecision, SCCOL nCol, SCROW nRowStart, SCROW nRowEnd ) const 3055 { 3056 if ( IsColValid( nCol ) ) 3057 return aCol[nCol].GetMaxNumberStringLen( nPrecision, nRowStart, nRowEnd ); 3058 else 3059 return 0; 3060 } 3061 3062 void ScTable::UpdateSelectionFunction( ScFunctionData& rData, const ScMarkData& rMark ) 3063 { 3064 ScRangeList aRanges = rMark.GetMarkedRangesForTab( nTab ); 3065 ScRange aMarkArea( ScAddress::UNINITIALIZED ); 3066 if (rMark.IsMultiMarked()) 3067 aMarkArea = rMark.GetMultiMarkArea(); 3068 else if (rMark.IsMarked()) 3069 aMarkArea = rMark.GetMarkArea(); 3070 else 3071 { 3072 assert(!"ScTable::UpdateSelectionFunction - called without anything marked"); 3073 aMarkArea.aStart.SetCol(0); 3074 aMarkArea.aEnd.SetCol(rDocument.MaxCol()); 3075 } 3076 const SCCOL nStartCol = aMarkArea.aStart.Col(); 3077 const SCCOL nEndCol = ClampToAllocatedColumns(aMarkArea.aEnd.Col()); 3078 for (SCCOL nCol = nStartCol; nCol <= nEndCol && !rData.getError(); ++nCol) 3079 { 3080 if (mpColFlags && ColHidden(nCol)) 3081 continue; 3082 3083 aCol[nCol].UpdateSelectionFunction(aRanges, rData, *mpHiddenRows); 3084 } 3085 } 3086 3087 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 3088
