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