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