1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_sc.hxx" 30 31 #include <rtl/math.hxx> 32 #include <unotools/textsearch.hxx> 33 #include <svl/zforlist.hxx> 34 #include <svl/zformat.hxx> 35 #include <unotools/charclass.hxx> 36 #include <unotools/collatorwrapper.hxx> 37 #include <com/sun/star/i18n/CollatorOptions.hpp> 38 #include <stdlib.h> 39 #include <unotools/transliterationwrapper.hxx> 40 41 #include "table.hxx" 42 #include "scitems.hxx" 43 #include "collect.hxx" 44 #include "attrib.hxx" 45 #include "cell.hxx" 46 #include "document.hxx" 47 #include "globstr.hrc" 48 #include "global.hxx" 49 #include "stlpool.hxx" 50 #include "compiler.hxx" 51 #include "patattr.hxx" 52 #include "subtotal.hxx" 53 #include "docoptio.hxx" 54 #include "markdata.hxx" 55 #include "rangelst.hxx" 56 #include "attarray.hxx" 57 #include "userlist.hxx" 58 #include "progress.hxx" 59 #include "cellform.hxx" 60 #include "postit.hxx" 61 #include "queryparam.hxx" 62 #include "segmenttree.hxx" 63 #include "drwlayer.hxx" 64 65 #include <vector> 66 67 // STATIC DATA ----------------------------------------------------------- 68 69 const sal_uInt16 nMaxSorts = 3; // maximale Anzahl Sortierkriterien in aSortParam 70 71 struct ScSortInfo 72 { 73 ScBaseCell* pCell; 74 SCCOLROW nOrg; 75 DECL_FIXEDMEMPOOL_NEWDEL( ScSortInfo ); 76 }; 77 const sal_uInt16 nMemPoolSortInfo = (0x8000 - 64) / sizeof(ScSortInfo); 78 IMPL_FIXEDMEMPOOL_NEWDEL( ScSortInfo, nMemPoolSortInfo, nMemPoolSortInfo ) 79 80 // END OF STATIC DATA ----------------------------------------------------- 81 82 83 class ScSortInfoArray 84 { 85 private: 86 ScSortInfo** pppInfo[nMaxSorts]; 87 SCSIZE nCount; 88 SCCOLROW nStart; 89 sal_uInt16 nUsedSorts; 90 91 public: 92 ScSortInfoArray( sal_uInt16 nSorts, SCCOLROW nInd1, SCCOLROW nInd2 ) : 93 nCount( nInd2 - nInd1 + 1 ), nStart( nInd1 ), 94 nUsedSorts( Min( nSorts, nMaxSorts ) ) 95 { 96 for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ ) 97 { 98 ScSortInfo** ppInfo = new ScSortInfo* [nCount]; 99 for ( SCSIZE j = 0; j < nCount; j++ ) 100 ppInfo[j] = new ScSortInfo; 101 pppInfo[nSort] = ppInfo; 102 } 103 } 104 ~ScSortInfoArray() 105 { 106 for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ ) 107 { 108 ScSortInfo** ppInfo = pppInfo[nSort]; 109 for ( SCSIZE j = 0; j < nCount; j++ ) 110 delete ppInfo[j]; 111 delete [] ppInfo; 112 } 113 } 114 ScSortInfo* Get( sal_uInt16 nSort, SCCOLROW nInd ) 115 { return (pppInfo[nSort])[ nInd - nStart ]; } 116 void Swap( SCCOLROW nInd1, SCCOLROW nInd2 ) 117 { 118 SCSIZE n1 = static_cast<SCSIZE>(nInd1 - nStart); 119 SCSIZE n2 = static_cast<SCSIZE>(nInd2 - nStart); 120 for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ ) 121 { 122 ScSortInfo** ppInfo = pppInfo[nSort]; 123 ScSortInfo* pTmp = ppInfo[n1]; 124 ppInfo[n1] = ppInfo[n2]; 125 ppInfo[n2] = pTmp; 126 } 127 } 128 sal_uInt16 GetUsedSorts() { return nUsedSorts; } 129 ScSortInfo** GetFirstArray() { return pppInfo[0]; } 130 SCCOLROW GetStart() { return nStart; } 131 SCSIZE GetCount() { return nCount; } 132 }; 133 134 ScSortInfoArray* ScTable::CreateSortInfoArray( SCCOLROW nInd1, SCCOLROW nInd2 ) 135 { 136 sal_uInt16 nUsedSorts = 1; 137 while ( nUsedSorts < nMaxSorts && aSortParam.bDoSort[nUsedSorts] ) 138 nUsedSorts++; 139 ScSortInfoArray* pArray = new ScSortInfoArray( nUsedSorts, nInd1, nInd2 ); 140 if ( aSortParam.bByRow ) 141 { 142 for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ ) 143 { 144 SCCOL nCol = static_cast<SCCOL>(aSortParam.nField[nSort]); 145 ScColumn* pCol = &aCol[nCol]; 146 for ( SCROW nRow = nInd1; nRow <= nInd2; nRow++ ) 147 { 148 //2do: FillSortInfo an ScColumn und Array abklappern statt Search in GetCell 149 ScSortInfo* pInfo = pArray->Get( nSort, nRow ); 150 pInfo->pCell = pCol->GetCell( nRow ); 151 pInfo->nOrg = nRow; 152 } 153 } 154 } 155 else 156 { 157 for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ ) 158 { 159 SCROW nRow = aSortParam.nField[nSort]; 160 for ( SCCOL nCol = static_cast<SCCOL>(nInd1); 161 nCol <= static_cast<SCCOL>(nInd2); nCol++ ) 162 { 163 ScSortInfo* pInfo = pArray->Get( nSort, nCol ); 164 pInfo->pCell = GetCell( nCol, nRow ); 165 pInfo->nOrg = nCol; 166 } 167 } 168 } 169 return pArray; 170 } 171 172 173 sal_Bool ScTable::IsSortCollatorGlobal() const 174 { 175 return pSortCollator == ScGlobal::GetCollator() || 176 pSortCollator == ScGlobal::GetCaseCollator(); 177 } 178 179 180 void ScTable::InitSortCollator( const ScSortParam& rPar ) 181 { 182 if ( rPar.aCollatorLocale.Language.getLength() ) 183 { 184 if ( !pSortCollator || IsSortCollatorGlobal() ) 185 pSortCollator = new CollatorWrapper( pDocument->GetServiceManager() ); 186 pSortCollator->loadCollatorAlgorithm( rPar.aCollatorAlgorithm, 187 rPar.aCollatorLocale, (rPar.bCaseSens ? 0 : SC_COLLATOR_IGNORES) ); 188 } 189 else 190 { // SYSTEM 191 DestroySortCollator(); 192 pSortCollator = (rPar.bCaseSens ? ScGlobal::GetCaseCollator() : 193 ScGlobal::GetCollator()); 194 } 195 } 196 197 198 void ScTable::DestroySortCollator() 199 { 200 if ( pSortCollator ) 201 { 202 if ( !IsSortCollatorGlobal() ) 203 delete pSortCollator; 204 pSortCollator = NULL; 205 } 206 } 207 208 209 void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress& rProgress ) 210 { 211 sal_Bool bByRow = aSortParam.bByRow; 212 SCSIZE nCount = pArray->GetCount(); 213 SCCOLROW nStart = pArray->GetStart(); 214 ScSortInfo** ppInfo = pArray->GetFirstArray(); 215 ::std::vector<ScSortInfo*> aTable(nCount); 216 SCSIZE nPos; 217 for ( nPos = 0; nPos < nCount; nPos++ ) 218 aTable[ppInfo[nPos]->nOrg - nStart] = ppInfo[nPos]; 219 220 SCCOLROW nDest = nStart; 221 for ( nPos = 0; nPos < nCount; nPos++, nDest++ ) 222 { 223 SCCOLROW nOrg = ppInfo[nPos]->nOrg; 224 if ( nDest != nOrg ) 225 { 226 if ( bByRow ) 227 SwapRow( nDest, nOrg ); 228 else 229 SwapCol( static_cast<SCCOL>(nDest), static_cast<SCCOL>(nOrg) ); 230 // neue Position des weggeswapten eintragen 231 ScSortInfo* p = ppInfo[nPos]; 232 p->nOrg = nDest; 233 ::std::swap(p, aTable[nDest-nStart]); 234 p->nOrg = nOrg; 235 ::std::swap(p, aTable[nOrg-nStart]); 236 DBG_ASSERT( p == ppInfo[nPos], "SortReorder: nOrg MisMatch" ); 237 } 238 rProgress.SetStateOnPercent( nPos ); 239 } 240 } 241 242 short ScTable::CompareCell( sal_uInt16 nSort, 243 ScBaseCell* pCell1, SCCOL nCell1Col, SCROW nCell1Row, 244 ScBaseCell* pCell2, SCCOL nCell2Col, SCROW nCell2Row ) 245 { 246 short nRes = 0; 247 248 CellType eType1 = CELLTYPE_NONE, eType2 = CELLTYPE_NONE; 249 if (pCell1) 250 { 251 eType1 = pCell1->GetCellType(); 252 if (eType1 == CELLTYPE_NOTE) 253 pCell1 = NULL; 254 } 255 if (pCell2) 256 { 257 eType2 = pCell2->GetCellType(); 258 if (eType2 == CELLTYPE_NOTE) 259 pCell2 = NULL; 260 } 261 262 if (pCell1) 263 { 264 if (pCell2) 265 { 266 sal_Bool bStr1 = ( eType1 != CELLTYPE_VALUE ); 267 if ( eType1 == CELLTYPE_FORMULA && ((ScFormulaCell*)pCell1)->IsValue() ) 268 bStr1 = sal_False; 269 sal_Bool bStr2 = ( eType2 != CELLTYPE_VALUE ); 270 if ( eType2 == CELLTYPE_FORMULA && ((ScFormulaCell*)pCell2)->IsValue() ) 271 bStr2 = sal_False; 272 273 if ( bStr1 && bStr2 ) // nur Strings untereinander als String vergleichen! 274 { 275 String aStr1; 276 String aStr2; 277 if (eType1 == CELLTYPE_STRING) 278 ((ScStringCell*)pCell1)->GetString(aStr1); 279 else 280 GetString(nCell1Col, nCell1Row, aStr1); 281 if (eType2 == CELLTYPE_STRING) 282 ((ScStringCell*)pCell2)->GetString(aStr2); 283 else 284 GetString(nCell2Col, nCell2Row, aStr2); 285 sal_Bool bUserDef = aSortParam.bUserDef; 286 if (bUserDef) 287 { 288 ScUserListData* pData = 289 (ScUserListData*)(ScGlobal::GetUserList()->At( 290 aSortParam.nUserIndex)); 291 if (pData) 292 { 293 if ( aSortParam.bCaseSens ) 294 nRes = sal::static_int_cast<short>( pData->Compare(aStr1, aStr2) ); 295 else 296 nRes = sal::static_int_cast<short>( pData->ICompare(aStr1, aStr2) ); 297 } 298 else 299 bUserDef = sal_False; 300 301 } 302 if (!bUserDef) 303 nRes = (short) pSortCollator->compareString( aStr1, aStr2 ); 304 } 305 else if ( bStr1 ) // String <-> Zahl 306 nRes = 1; // Zahl vorne 307 else if ( bStr2 ) // Zahl <-> String 308 nRes = -1; // Zahl vorne 309 else // Zahlen untereinander 310 { 311 double nVal1; 312 double nVal2; 313 if (eType1 == CELLTYPE_VALUE) 314 nVal1 = ((ScValueCell*)pCell1)->GetValue(); 315 else if (eType1 == CELLTYPE_FORMULA) 316 nVal1 = ((ScFormulaCell*)pCell1)->GetValue(); 317 else 318 nVal1 = 0; 319 if (eType2 == CELLTYPE_VALUE) 320 nVal2 = ((ScValueCell*)pCell2)->GetValue(); 321 else if (eType2 == CELLTYPE_FORMULA) 322 nVal2 = ((ScFormulaCell*)pCell2)->GetValue(); 323 else 324 nVal2 = 0; 325 if (nVal1 < nVal2) 326 nRes = -1; 327 else if (nVal1 > nVal2) 328 nRes = 1; 329 } 330 if ( !aSortParam.bAscending[nSort] ) 331 nRes = -nRes; 332 } 333 else 334 nRes = -1; 335 } 336 else 337 { 338 if ( pCell2 ) 339 nRes = 1; 340 else 341 nRes = 0; // beide leer 342 } 343 return nRes; 344 } 345 346 short ScTable::Compare( ScSortInfoArray* pArray, SCCOLROW nIndex1, SCCOLROW nIndex2 ) 347 { 348 short nRes; 349 sal_uInt16 nSort = 0; 350 do 351 { 352 ScSortInfo* pInfo1 = pArray->Get( nSort, nIndex1 ); 353 ScSortInfo* pInfo2 = pArray->Get( nSort, nIndex2 ); 354 if ( aSortParam.bByRow ) 355 nRes = CompareCell( nSort, 356 pInfo1->pCell, static_cast<SCCOL>(aSortParam.nField[nSort]), pInfo1->nOrg, 357 pInfo2->pCell, static_cast<SCCOL>(aSortParam.nField[nSort]), pInfo2->nOrg ); 358 else 359 nRes = CompareCell( nSort, 360 pInfo1->pCell, static_cast<SCCOL>(pInfo1->nOrg), aSortParam.nField[nSort], 361 pInfo2->pCell, static_cast<SCCOL>(pInfo2->nOrg), aSortParam.nField[nSort] ); 362 } while ( nRes == 0 && ++nSort < pArray->GetUsedSorts() ); 363 if( nRes == 0 ) 364 { 365 ScSortInfo* pInfo1 = pArray->Get( 0, nIndex1 ); 366 ScSortInfo* pInfo2 = pArray->Get( 0, nIndex2 ); 367 if( pInfo1->nOrg < pInfo2->nOrg ) 368 nRes = -1; 369 else if( pInfo1->nOrg > pInfo2->nOrg ) 370 nRes = 1; 371 } 372 return nRes; 373 } 374 375 void ScTable::QuickSort( ScSortInfoArray* pArray, SCsCOLROW nLo, SCsCOLROW nHi ) 376 { 377 if ((nHi - nLo) == 1) 378 { 379 if (Compare(pArray, nLo, nHi) > 0) 380 pArray->Swap( nLo, nHi ); 381 } 382 else 383 { 384 SCsCOLROW ni = nLo; 385 SCsCOLROW nj = nHi; 386 do 387 { 388 while ((ni <= nHi) && (Compare(pArray, ni, nLo)) < 0) 389 ni++; 390 while ((nj >= nLo) && (Compare(pArray, nLo, nj)) < 0) 391 nj--; 392 if (ni <= nj) 393 { 394 if (ni != nj) 395 pArray->Swap( ni, nj ); 396 ni++; 397 nj--; 398 } 399 } while (ni < nj); 400 if ((nj - nLo) < (nHi - ni)) 401 { 402 if (nLo < nj) 403 QuickSort(pArray, nLo, nj); 404 if (ni < nHi) 405 QuickSort(pArray, ni, nHi); 406 } 407 else 408 { 409 if (ni < nHi) 410 QuickSort(pArray, ni, nHi); 411 if (nLo < nj) 412 QuickSort(pArray, nLo, nj); 413 } 414 } 415 } 416 417 void ScTable::SwapCol(SCCOL nCol1, SCCOL nCol2) 418 { 419 for (SCROW nRow = aSortParam.nRow1; nRow <= aSortParam.nRow2; nRow++) 420 { 421 aCol[nCol1].SwapCell(nRow, aCol[nCol2]); 422 if (aSortParam.bIncludePattern) 423 { 424 const ScPatternAttr* pPat1 = GetPattern(nCol1, nRow); 425 const ScPatternAttr* pPat2 = GetPattern(nCol2, nRow); 426 if (pPat1 != pPat2) 427 { 428 SetPattern(nCol1, nRow, *pPat2, sal_True); 429 SetPattern(nCol2, nRow, *pPat1, sal_True); 430 } 431 } 432 } 433 } 434 435 void ScTable::SwapRow(SCROW nRow1, SCROW nRow2) 436 { 437 for (SCCOL nCol = aSortParam.nCol1; nCol <= aSortParam.nCol2; nCol++) 438 { 439 aCol[nCol].SwapRow(nRow1, nRow2); 440 if (aSortParam.bIncludePattern) 441 { 442 const ScPatternAttr* pPat1 = GetPattern(nCol, nRow1); 443 const ScPatternAttr* pPat2 = GetPattern(nCol, nRow2); 444 if (pPat1 != pPat2) 445 { 446 SetPattern(nCol, nRow1, *pPat2, sal_True); 447 SetPattern(nCol, nRow2, *pPat1, sal_True); 448 } 449 } 450 } 451 if (bGlobalKeepQuery) 452 { 453 bool bRow1Hidden = RowHidden(nRow1); 454 bool bRow2Hidden = RowHidden(nRow2); 455 SetRowHidden(nRow1, nRow1, bRow2Hidden); 456 SetRowHidden(nRow2, nRow2, bRow1Hidden); 457 458 bool bRow1Filtered = RowFiltered(nRow1); 459 bool bRow2Filtered = RowFiltered(nRow2); 460 SetRowFiltered(nRow1, nRow1, bRow2Filtered); 461 SetRowFiltered(nRow2, nRow2, bRow1Filtered); 462 } 463 } 464 465 short ScTable::Compare(SCCOLROW nIndex1, SCCOLROW nIndex2) 466 { 467 short nRes; 468 sal_uInt16 nSort = 0; 469 if (aSortParam.bByRow) 470 { 471 do 472 { 473 SCCOL nCol = static_cast<SCCOL>(aSortParam.nField[nSort]); 474 ScBaseCell* pCell1 = aCol[nCol].GetCell( nIndex1 ); 475 ScBaseCell* pCell2 = aCol[nCol].GetCell( nIndex2 ); 476 nRes = CompareCell( nSort, pCell1, nCol, nIndex1, pCell2, nCol, nIndex2 ); 477 } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.bDoSort[nSort] ); 478 } 479 else 480 { 481 do 482 { 483 SCROW nRow = aSortParam.nField[nSort]; 484 ScBaseCell* pCell1 = aCol[nIndex1].GetCell( nRow ); 485 ScBaseCell* pCell2 = aCol[nIndex2].GetCell( nRow ); 486 nRes = CompareCell( nSort, pCell1, static_cast<SCCOL>(nIndex1), 487 nRow, pCell2, static_cast<SCCOL>(nIndex2), nRow ); 488 } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.bDoSort[nSort] ); 489 } 490 return nRes; 491 } 492 493 sal_Bool ScTable::IsSorted( SCCOLROW nStart, SCCOLROW nEnd ) // ueber aSortParam 494 { 495 for (SCCOLROW i=nStart; i<nEnd; i++) 496 { 497 if (Compare( i, i+1 ) > 0) 498 return sal_False; 499 } 500 return sal_True; 501 } 502 503 void ScTable::DecoladeRow( ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2 ) 504 { 505 SCROW nRow; 506 SCROW nMax = nRow2 - nRow1; 507 for (SCROW i = nRow1; (i + 4) <= nRow2; i += 4) 508 { 509 nRow = rand() % nMax; 510 pArray->Swap(i, nRow1 + nRow); 511 } 512 } 513 514 void ScTable::Sort(const ScSortParam& rSortParam, sal_Bool bKeepQuery) 515 { 516 aSortParam = rSortParam; 517 InitSortCollator( rSortParam ); 518 bGlobalKeepQuery = bKeepQuery; 519 if (rSortParam.bByRow) 520 { 521 SCROW nLastRow = 0; 522 for (SCCOL nCol = aSortParam.nCol1; nCol <= aSortParam.nCol2; nCol++) 523 nLastRow = Max(nLastRow, aCol[nCol].GetLastDataPos()); 524 nLastRow = Min(nLastRow, aSortParam.nRow2); 525 SCROW nRow1 = (rSortParam.bHasHeader ? 526 aSortParam.nRow1 + 1 : aSortParam.nRow1); 527 if (!IsSorted(nRow1, nLastRow)) 528 { 529 ScProgress aProgress( pDocument->GetDocumentShell(), 530 ScGlobal::GetRscString(STR_PROGRESS_SORTING), nLastRow - nRow1 ); 531 ScSortInfoArray* pArray = CreateSortInfoArray( nRow1, nLastRow ); 532 if ( nLastRow - nRow1 > 255 ) 533 DecoladeRow( pArray, nRow1, nLastRow ); 534 QuickSort( pArray, nRow1, nLastRow ); 535 SortReorder( pArray, aProgress ); 536 delete pArray; 537 // #158377# #i59745# update position of caption objects of cell notes 538 ScNoteUtil::UpdateCaptionPositions( *pDocument, ScRange( aSortParam.nCol1, nRow1, nTab, aSortParam.nCol2, nLastRow, nTab ) ); 539 } 540 } 541 else 542 { 543 SCCOL nLastCol; 544 for (nLastCol = aSortParam.nCol2; 545 (nLastCol > aSortParam.nCol1) && aCol[nLastCol].IsEmptyBlock(aSortParam.nRow1, aSortParam.nRow2); nLastCol--) 546 { 547 } 548 SCCOL nCol1 = (rSortParam.bHasHeader ? 549 aSortParam.nCol1 + 1 : aSortParam.nCol1); 550 if (!IsSorted(nCol1, nLastCol)) 551 { 552 ScProgress aProgress( pDocument->GetDocumentShell(), 553 ScGlobal::GetRscString(STR_PROGRESS_SORTING), nLastCol - nCol1 ); 554 ScSortInfoArray* pArray = CreateSortInfoArray( nCol1, nLastCol ); 555 QuickSort( pArray, nCol1, nLastCol ); 556 SortReorder( pArray, aProgress ); 557 delete pArray; 558 // #158377# #i59745# update position of caption objects of cell notes 559 ScNoteUtil::UpdateCaptionPositions( *pDocument, ScRange( nCol1, aSortParam.nRow1, nTab, nLastCol, aSortParam.nRow2, nTab ) ); 560 } 561 } 562 DestroySortCollator(); 563 } 564 565 566 // Testen, ob beim Loeschen von Zwischenergebnissen andere Daten mit geloescht werden 567 // (fuer Hinweis-Box) 568 569 sal_Bool ScTable::TestRemoveSubTotals( const ScSubTotalParam& rParam ) 570 { 571 SCCOL nStartCol = rParam.nCol1; 572 SCROW nStartRow = rParam.nRow1 + 1; // Header 573 SCCOL nEndCol = rParam.nCol2; 574 SCROW nEndRow = rParam.nRow2; 575 576 SCCOL nCol; 577 SCROW nRow; 578 ScBaseCell* pCell; 579 580 sal_Bool bWillDelete = sal_False; 581 for ( nCol=nStartCol; nCol<=nEndCol && !bWillDelete; nCol++ ) 582 { 583 ScColumnIterator aIter( &aCol[nCol],nStartRow,nEndRow ); 584 while ( aIter.Next( nRow, pCell ) && !bWillDelete ) 585 { 586 if ( pCell->GetCellType() == CELLTYPE_FORMULA ) 587 if (((ScFormulaCell*)pCell)->IsSubTotal()) 588 { 589 for (SCCOL nTestCol=0; nTestCol<=MAXCOL; nTestCol++) 590 if (nTestCol<nStartCol || nTestCol>nEndCol) 591 if (aCol[nTestCol].HasDataAt(nRow)) 592 bWillDelete = sal_True; 593 } 594 } 595 } 596 return bWillDelete; 597 } 598 599 // alte Ergebnisse loeschen 600 // rParam.nRow2 wird veraendert ! 601 602 void ScTable::RemoveSubTotals( ScSubTotalParam& rParam ) 603 { 604 SCCOL nStartCol = rParam.nCol1; 605 SCROW nStartRow = rParam.nRow1 + 1; // Header 606 SCCOL nEndCol = rParam.nCol2; 607 SCROW nEndRow = rParam.nRow2; // wird veraendert 608 609 SCCOL nCol; 610 SCROW nRow; 611 ScBaseCell* pCell; 612 613 for ( nCol=nStartCol; nCol<=nEndCol; nCol++ ) 614 { 615 ScColumnIterator aIter( &aCol[nCol],nStartRow,nEndRow ); 616 while ( aIter.Next( nRow, pCell ) ) 617 { 618 if ( pCell->GetCellType() == CELLTYPE_FORMULA ) 619 if (((ScFormulaCell*)pCell)->IsSubTotal()) 620 { 621 RemoveRowBreak(nRow+1, false, true); 622 pDocument->DeleteRow( 0,nTab, MAXCOL,nTab, nRow, 1 ); 623 --nEndRow; 624 aIter = ScColumnIterator( &aCol[nCol],nRow,nEndRow ); 625 } 626 } 627 } 628 629 rParam.nRow2 = nEndRow; // neues Ende 630 } 631 632 // harte Zahlenformate loeschen (fuer Ergebnisformeln) 633 634 void lcl_RemoveNumberFormat( ScTable* pTab, SCCOL nCol, SCROW nRow ) 635 { 636 const ScPatternAttr* pPattern = pTab->GetPattern( nCol, nRow ); 637 if ( pPattern->GetItemSet().GetItemState( ATTR_VALUE_FORMAT, sal_False ) 638 == SFX_ITEM_SET ) 639 { 640 ScPatternAttr aNewPattern( *pPattern ); 641 SfxItemSet& rSet = aNewPattern.GetItemSet(); 642 rSet.ClearItem( ATTR_VALUE_FORMAT ); 643 rSet.ClearItem( ATTR_LANGUAGE_FORMAT ); 644 pTab->SetPattern( nCol, nRow, aNewPattern, sal_True ); 645 } 646 } 647 648 649 // at least MSC needs this at linkage level to be able to use it in a template 650 typedef struct lcl_ScTable_DoSubTotals_RowEntry 651 { 652 sal_uInt16 nGroupNo; 653 SCROW nSubStartRow; 654 SCROW nDestRow; 655 SCROW nFuncStart; 656 SCROW nFuncEnd; 657 } RowEntry; 658 659 // neue Zwischenergebnisse 660 // rParam.nRow2 wird veraendert ! 661 662 sal_Bool ScTable::DoSubTotals( ScSubTotalParam& rParam ) 663 { 664 SCCOL nStartCol = rParam.nCol1; 665 SCROW nStartRow = rParam.nRow1 + 1; // Header 666 SCCOL nEndCol = rParam.nCol2; 667 SCROW nEndRow = rParam.nRow2; // wird veraendert 668 sal_uInt16 i; 669 670 // Leerzeilen am Ende weglassen, 671 // damit alle Ueberlaeufe (MAXROW) bei InsertRow gefunden werden (#35180#) 672 // Wenn sortiert wurde, sind alle Leerzeilen am Ende. 673 SCSIZE nEmpty = GetEmptyLinesInBlock( nStartCol, nStartRow, nEndCol, nEndRow, DIR_BOTTOM ); 674 nEndRow -= nEmpty; 675 676 sal_uInt16 nLevelCount = 0; // Anzahl Gruppierungen 677 sal_Bool bDoThis = sal_True; 678 for (i=0; i<MAXSUBTOTAL && bDoThis; i++) 679 if (rParam.bGroupActive[i]) 680 nLevelCount = i+1; 681 else 682 bDoThis = sal_False; 683 684 if (nLevelCount==0) // nichts tun 685 return sal_True; 686 687 SCCOL* nGroupCol = rParam.nField; // Spalten nach denen 688 // gruppiert wird 689 690 // #44444# Durch (leer) als eigene Kategorie muss immer auf 691 // Teilergebniszeilen aus den anderen Spalten getestet werden 692 // (frueher nur, wenn eine Spalte mehrfach vorkam) 693 sal_Bool bTestPrevSub = ( nLevelCount > 1 ); 694 695 String aSubString; 696 String aOutString; 697 698 sal_Bool bIgnoreCase = !rParam.bCaseSens; 699 700 String *pCompString[MAXSUBTOTAL]; // Pointer wegen Compiler-Problemen 701 for (i=0; i<MAXSUBTOTAL; i++) 702 pCompString[i] = new String; 703 704 //! sortieren? 705 706 ScStyleSheet* pStyle = (ScStyleSheet*) pDocument->GetStyleSheetPool()->Find( 707 ScGlobal::GetRscString(STR_STYLENAME_RESULT), SFX_STYLE_FAMILY_PARA ); 708 709 sal_Bool bSpaceLeft = sal_True; // Erfolg beim Einfuegen? 710 711 // #90279# For performance reasons collect formula entries so their 712 // references don't have to be tested for updates each time a new row is 713 // inserted 714 RowEntry aRowEntry; 715 ::std::vector< RowEntry > aRowVector; 716 717 for (sal_uInt16 nLevel=0; nLevel<=nLevelCount && bSpaceLeft; nLevel++) // incl. Gesamtergebnis 718 { 719 sal_Bool bTotal = ( nLevel == nLevelCount ); 720 aRowEntry.nGroupNo = bTotal ? 0 : (nLevelCount-nLevel-1); 721 722 // how many results per level 723 SCCOL nResCount = rParam.nSubTotals[aRowEntry.nGroupNo]; 724 // result functions 725 ScSubTotalFunc* eResFunc = rParam.pFunctions[aRowEntry.nGroupNo]; 726 727 if (nResCount > 0) // sonst nur sortieren 728 { 729 for (i=0; i<=aRowEntry.nGroupNo; i++) 730 { 731 GetString( nGroupCol[i], nStartRow, aSubString ); 732 if ( bIgnoreCase ) 733 *pCompString[i] = ScGlobal::pCharClass->upper( aSubString ); 734 else 735 *pCompString[i] = aSubString; 736 } // aSubString bleibt auf dem letzten stehen 737 738 sal_Bool bBlockVis = sal_False; // Gruppe eingeblendet? 739 aRowEntry.nSubStartRow = nStartRow; 740 for (SCROW nRow=nStartRow; nRow<=nEndRow+1 && bSpaceLeft; nRow++) 741 { 742 sal_Bool bChanged; 743 if (nRow>nEndRow) 744 bChanged = sal_True; 745 else 746 { 747 bChanged = sal_False; 748 if (!bTotal) 749 { 750 String aString; 751 for (i=0; i<=aRowEntry.nGroupNo && !bChanged; i++) 752 { 753 GetString( nGroupCol[i], nRow, aString ); 754 if (bIgnoreCase) 755 ScGlobal::pCharClass->toUpper( aString ); 756 // #41427# wenn sortiert, ist "leer" eine eigene Gruppe 757 // sonst sind leere Zellen unten erlaubt 758 bChanged = ( ( aString.Len() || rParam.bDoSort ) && 759 aString != *pCompString[i] ); 760 } 761 if ( bChanged && bTestPrevSub ) 762 { 763 // No group change on rows that will contain subtotal formulas 764 for ( ::std::vector< RowEntry >::const_iterator 765 iEntry( aRowVector.begin()); 766 iEntry != aRowVector.end(); ++iEntry) 767 { 768 if ( iEntry->nDestRow == nRow ) 769 { 770 bChanged = sal_False; 771 break; 772 } 773 } 774 } 775 } 776 } 777 if ( bChanged ) 778 { 779 aRowEntry.nDestRow = nRow; 780 aRowEntry.nFuncStart = aRowEntry.nSubStartRow; 781 aRowEntry.nFuncEnd = nRow-1; 782 783 bSpaceLeft = pDocument->InsertRow( 0, nTab, MAXCOL, nTab, 784 aRowEntry.nDestRow, 1 ); 785 DBShowRow( aRowEntry.nDestRow, bBlockVis ); 786 bBlockVis = sal_False; 787 if ( rParam.bPagebreak && nRow < MAXROW && 788 aRowEntry.nSubStartRow != nStartRow && nLevel == 0) 789 SetRowBreak(aRowEntry.nSubStartRow, false, true); 790 791 if (bSpaceLeft) 792 { 793 for ( ::std::vector< RowEntry >::iterator iMove( 794 aRowVector.begin() ); 795 iMove != aRowVector.end(); ++iMove) 796 { 797 if ( aRowEntry.nDestRow <= iMove->nSubStartRow ) 798 ++iMove->nSubStartRow; 799 if ( aRowEntry.nDestRow <= iMove->nDestRow ) 800 ++iMove->nDestRow; 801 if ( aRowEntry.nDestRow <= iMove->nFuncStart ) 802 ++iMove->nFuncStart; 803 if ( aRowEntry.nDestRow <= iMove->nFuncEnd ) 804 ++iMove->nFuncEnd; 805 } 806 // collect formula positions 807 aRowVector.push_back( aRowEntry ); 808 809 if (bTotal) // "Gesamtergebnis" 810 aOutString = ScGlobal::GetRscString( STR_TABLE_GESAMTERGEBNIS ); 811 else 812 { // " Ergebnis" 813 aOutString = aSubString; 814 if (!aOutString.Len()) 815 aOutString = ScGlobal::GetRscString( STR_EMPTYDATA ); 816 aOutString += ' '; 817 sal_uInt16 nStrId = STR_TABLE_ERGEBNIS; 818 if ( nResCount == 1 ) 819 switch ( eResFunc[0] ) 820 { 821 case SUBTOTAL_FUNC_AVE: nStrId = STR_FUN_TEXT_AVG; break; 822 case SUBTOTAL_FUNC_CNT: 823 case SUBTOTAL_FUNC_CNT2: nStrId = STR_FUN_TEXT_COUNT; break; 824 case SUBTOTAL_FUNC_MAX: nStrId = STR_FUN_TEXT_MAX; break; 825 case SUBTOTAL_FUNC_MIN: nStrId = STR_FUN_TEXT_MIN; break; 826 case SUBTOTAL_FUNC_PROD: nStrId = STR_FUN_TEXT_PRODUCT; break; 827 case SUBTOTAL_FUNC_STD: 828 case SUBTOTAL_FUNC_STDP: nStrId = STR_FUN_TEXT_STDDEV; break; 829 case SUBTOTAL_FUNC_SUM: nStrId = STR_FUN_TEXT_SUM; break; 830 case SUBTOTAL_FUNC_VAR: 831 case SUBTOTAL_FUNC_VARP: nStrId = STR_FUN_TEXT_VAR; break; 832 default: 833 { 834 // added to avoid warnings 835 } 836 } 837 aOutString += ScGlobal::GetRscString( nStrId ); 838 } 839 SetString( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, nTab, aOutString ); 840 ApplyStyle( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, *pStyle ); 841 842 ++nRow; 843 ++nEndRow; 844 aRowEntry.nSubStartRow = nRow; 845 for (i=0; i<=aRowEntry.nGroupNo; i++) 846 { 847 GetString( nGroupCol[i], nRow, aSubString ); 848 if ( bIgnoreCase ) 849 *pCompString[i] = ScGlobal::pCharClass->upper( aSubString ); 850 else 851 *pCompString[i] = aSubString; 852 } 853 } 854 } 855 bBlockVis = !RowFiltered(nRow); 856 } 857 } 858 else 859 { 860 // DBG_ERROR( "nSubTotals==0 bei DoSubTotals" ); 861 } 862 } 863 864 // now insert the formulas 865 ScComplexRefData aRef; 866 aRef.InitFlags(); 867 aRef.Ref1.nTab = nTab; 868 aRef.Ref2.nTab = nTab; 869 for ( ::std::vector< RowEntry >::const_iterator iEntry( aRowVector.begin()); 870 iEntry != aRowVector.end(); ++iEntry) 871 { 872 SCCOL nResCount = rParam.nSubTotals[iEntry->nGroupNo]; 873 SCCOL* nResCols = rParam.pSubTotals[iEntry->nGroupNo]; 874 ScSubTotalFunc* eResFunc = rParam.pFunctions[iEntry->nGroupNo]; 875 for ( SCCOL nResult=0; nResult < nResCount; ++nResult ) 876 { 877 aRef.Ref1.nCol = nResCols[nResult]; 878 aRef.Ref1.nRow = iEntry->nFuncStart; 879 aRef.Ref2.nCol = nResCols[nResult]; 880 aRef.Ref2.nRow = iEntry->nFuncEnd; 881 882 ScTokenArray aArr; 883 aArr.AddOpCode( ocSubTotal ); 884 aArr.AddOpCode( ocOpen ); 885 aArr.AddDouble( (double) eResFunc[nResult] ); 886 aArr.AddOpCode( ocSep ); 887 aArr.AddDoubleReference( aRef ); 888 aArr.AddOpCode( ocClose ); 889 aArr.AddOpCode( ocStop ); 890 ScBaseCell* pCell = new ScFormulaCell( pDocument, ScAddress( 891 nResCols[nResult], iEntry->nDestRow, nTab), &aArr ); 892 PutCell( nResCols[nResult], iEntry->nDestRow, pCell ); 893 894 if ( nResCols[nResult] != nGroupCol[iEntry->nGroupNo] ) 895 { 896 ApplyStyle( nResCols[nResult], iEntry->nDestRow, *pStyle ); 897 898 // Zahlformat loeschen 899 lcl_RemoveNumberFormat( this, nResCols[nResult], iEntry->nDestRow ); 900 } 901 } 902 903 } 904 905 //! je nach Einstellung Zwischensummen-Zeilen nach oben verschieben ? 906 907 //! Outlines direkt erzeugen? 908 909 if (bSpaceLeft) 910 DoAutoOutline( nStartCol, nStartRow, nEndCol, nEndRow ); 911 912 for (i=0; i<MAXSUBTOTAL; i++) 913 delete pCompString[i]; 914 915 rParam.nRow2 = nEndRow; // neues Ende 916 return bSpaceLeft; 917 } 918 919 920 sal_Bool ScTable::ValidQuery(SCROW nRow, const ScQueryParam& rParam, 921 sal_Bool* pSpecial /* =NULL */ , ScBaseCell* pCell /* =NULL */ , 922 sal_Bool* pbTestEqualCondition /* = NULL */ ) 923 { 924 if (!rParam.GetEntry(0).bDoQuery) 925 return sal_True; 926 927 //--------------------------------------------------------------- 928 929 const SCSIZE nFixedBools = 32; 930 sal_Bool aBool[nFixedBools]; 931 sal_Bool aTest[nFixedBools]; 932 SCSIZE nEntryCount = rParam.GetEntryCount(); 933 sal_Bool* pPasst = ( nEntryCount <= nFixedBools ? &aBool[0] : new sal_Bool[nEntryCount] ); 934 sal_Bool* pTest = ( nEntryCount <= nFixedBools ? &aTest[0] : new sal_Bool[nEntryCount] ); 935 936 long nPos = -1; 937 SCSIZE i = 0; 938 sal_Bool bMatchWholeCell = pDocument->GetDocOptions().IsMatchWholeCell(); 939 CollatorWrapper* pCollator = (rParam.bCaseSens ? ScGlobal::GetCaseCollator() : 940 ScGlobal::GetCollator()); 941 ::utl::TransliterationWrapper* pTransliteration = (rParam.bCaseSens ? 942 ScGlobal::GetCaseTransliteration() : ScGlobal::GetpTransliteration()); 943 944 while ( (i < nEntryCount) && rParam.GetEntry(i).bDoQuery ) 945 { 946 ScQueryEntry& rEntry = rParam.GetEntry(i); 947 // we can only handle one single direct query 948 if ( !pCell || i > 0 ) 949 pCell = GetCell( static_cast<SCCOL>(rEntry.nField), nRow ); 950 951 sal_Bool bOk = sal_False; 952 sal_Bool bTestEqual = sal_False; 953 954 if ( pSpecial && pSpecial[i] ) 955 { 956 if (rEntry.nVal == SC_EMPTYFIELDS) 957 bOk = !( aCol[rEntry.nField].HasDataAt( nRow ) ); 958 else // if (rEntry.nVal == SC_NONEMPTYFIELDS) 959 bOk = aCol[rEntry.nField].HasDataAt( nRow ); 960 } 961 else if ( !rEntry.bQueryByString && (pCell ? pCell->HasValueData() : 962 HasValueData( static_cast<SCCOL>(rEntry.nField), nRow))) 963 { // by Value 964 double nCellVal; 965 if ( pCell ) 966 { 967 switch ( pCell->GetCellType() ) 968 { 969 case CELLTYPE_VALUE : 970 nCellVal = ((ScValueCell*)pCell)->GetValue(); 971 break; 972 case CELLTYPE_FORMULA : 973 nCellVal = ((ScFormulaCell*)pCell)->GetValue(); 974 break; 975 default: 976 nCellVal = 0.0; 977 } 978 979 } 980 else 981 nCellVal = GetValue( static_cast<SCCOL>(rEntry.nField), nRow ); 982 983 /* NOTE: lcl_PrepareQuery() prepares a filter query such that if a 984 * date+time format was queried rEntry.bQueryByDate is not set. In 985 * case other queries wanted to use this mechanism they should do 986 * the same, in other words only if rEntry.nVal is an integer value 987 * rEntry.bQueryByDate should be true and the time fraction be 988 * stripped here. */ 989 if (rEntry.bQueryByDate) 990 { 991 sal_uInt32 nNumFmt = GetNumberFormat(static_cast<SCCOL>(rEntry.nField), nRow); 992 const SvNumberformat* pEntry = pDocument->GetFormatTable()->GetEntry(nNumFmt); 993 if (pEntry) 994 { 995 short nNumFmtType = pEntry->GetType(); 996 /* NOTE: Omitting the check for absence of 997 * NUMBERFORMAT_TIME would include also date+time formatted 998 * values of the same day. That may be desired in some 999 * cases, querying all time values of a day, but confusing 1000 * in other cases. A user can always setup a standard 1001 * filter query for x >= date AND x < date+1 */ 1002 if ((nNumFmtType & NUMBERFORMAT_DATE) && !(nNumFmtType & NUMBERFORMAT_TIME)) 1003 { 1004 // The format is of date type. Strip off the time 1005 // element. 1006 nCellVal = ::rtl::math::approxFloor(nCellVal); 1007 } 1008 } 1009 } 1010 1011 switch (rEntry.eOp) 1012 { 1013 case SC_EQUAL : 1014 bOk = ::rtl::math::approxEqual( nCellVal, rEntry.nVal ); 1015 break; 1016 case SC_LESS : 1017 bOk = (nCellVal < rEntry.nVal) && !::rtl::math::approxEqual( nCellVal, rEntry.nVal ); 1018 break; 1019 case SC_GREATER : 1020 bOk = (nCellVal > rEntry.nVal) && !::rtl::math::approxEqual( nCellVal, rEntry.nVal ); 1021 break; 1022 case SC_LESS_EQUAL : 1023 bOk = (nCellVal < rEntry.nVal) || ::rtl::math::approxEqual( nCellVal, rEntry.nVal ); 1024 if ( bOk && pbTestEqualCondition ) 1025 bTestEqual = ::rtl::math::approxEqual( nCellVal, rEntry.nVal ); 1026 break; 1027 case SC_GREATER_EQUAL : 1028 bOk = (nCellVal > rEntry.nVal) || ::rtl::math::approxEqual( nCellVal, rEntry.nVal ); 1029 if ( bOk && pbTestEqualCondition ) 1030 bTestEqual = ::rtl::math::approxEqual( nCellVal, rEntry.nVal ); 1031 break; 1032 case SC_NOT_EQUAL : 1033 bOk = !::rtl::math::approxEqual( nCellVal, rEntry.nVal ); 1034 break; 1035 default: 1036 { 1037 // added to avoid warnings 1038 } 1039 } 1040 } 1041 else if ( (rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL) || 1042 (rEntry.eOp == SC_CONTAINS || rEntry.eOp == SC_DOES_NOT_CONTAIN || 1043 rEntry.eOp == SC_BEGINS_WITH || rEntry.eOp == SC_ENDS_WITH || 1044 rEntry.eOp == SC_DOES_NOT_BEGIN_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH) || 1045 (rEntry.bQueryByString && (pCell ? pCell->HasStringData() : 1046 HasStringData( 1047 static_cast<SCCOL>(rEntry.nField), 1048 nRow)))) 1049 { // by String 1050 String aCellStr; 1051 if( rEntry.eOp == SC_CONTAINS || rEntry.eOp == SC_DOES_NOT_CONTAIN 1052 || rEntry.eOp == SC_BEGINS_WITH || rEntry.eOp == SC_ENDS_WITH 1053 || rEntry.eOp == SC_DOES_NOT_BEGIN_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH ) 1054 bMatchWholeCell = sal_False; 1055 if ( pCell ) 1056 { 1057 if (pCell->GetCellType() != CELLTYPE_NOTE) 1058 { 1059 sal_uLong nFormat = GetNumberFormat( static_cast<SCCOL>(rEntry.nField), nRow ); 1060 ScCellFormat::GetInputString( pCell, nFormat, aCellStr, *(pDocument->GetFormatTable()) ); 1061 } 1062 } 1063 else 1064 GetInputString( static_cast<SCCOL>(rEntry.nField), nRow, aCellStr ); 1065 1066 sal_Bool bRealRegExp = (rParam.bRegExp && ((rEntry.eOp == SC_EQUAL) 1067 || (rEntry.eOp == SC_NOT_EQUAL) || (rEntry.eOp == SC_CONTAINS) 1068 || (rEntry.eOp == SC_DOES_NOT_CONTAIN) || (rEntry.eOp == SC_BEGINS_WITH) 1069 || (rEntry.eOp == SC_ENDS_WITH) || (rEntry.eOp == SC_DOES_NOT_BEGIN_WITH) 1070 || (rEntry.eOp == SC_DOES_NOT_END_WITH))); 1071 sal_Bool bTestRegExp = (pbTestEqualCondition && rParam.bRegExp 1072 && ((rEntry.eOp == SC_LESS_EQUAL) 1073 || (rEntry.eOp == SC_GREATER_EQUAL))); 1074 if ( bRealRegExp || bTestRegExp ) 1075 { 1076 xub_StrLen nStart = 0; 1077 xub_StrLen nEnd = aCellStr.Len(); 1078 1079 // from 614 on, nEnd is behind the found text 1080 sal_Bool bMatch = sal_False; 1081 if ( rEntry.eOp == SC_ENDS_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH ) 1082 { 1083 nEnd = 0; 1084 nStart = aCellStr.Len(); 1085 bMatch = (sal_Bool) rEntry.GetSearchTextPtr( rParam.bCaseSens ) 1086 ->SearchBkwrd( aCellStr, &nStart, &nEnd ); 1087 } 1088 else 1089 { 1090 bMatch = (sal_Bool) rEntry.GetSearchTextPtr( rParam.bCaseSens ) 1091 ->SearchFrwrd( aCellStr, &nStart, &nEnd ); 1092 } 1093 if ( bMatch && bMatchWholeCell 1094 && (nStart != 0 || nEnd != aCellStr.Len()) ) 1095 bMatch = sal_False; // RegExp must match entire cell string 1096 if ( bRealRegExp ) 1097 switch (rEntry.eOp) 1098 { 1099 case SC_EQUAL: 1100 case SC_CONTAINS: 1101 bOk = bMatch; 1102 break; 1103 case SC_NOT_EQUAL: 1104 case SC_DOES_NOT_CONTAIN: 1105 bOk = !bMatch; 1106 break; 1107 case SC_BEGINS_WITH: 1108 bOk = ( bMatch && (nStart == 0) ); 1109 break; 1110 case SC_DOES_NOT_BEGIN_WITH: 1111 bOk = !( bMatch && (nStart == 0) ); 1112 break; 1113 case SC_ENDS_WITH: 1114 bOk = ( bMatch && (nEnd == aCellStr.Len()) ); 1115 break; 1116 case SC_DOES_NOT_END_WITH: 1117 bOk = !( bMatch && (nEnd == aCellStr.Len()) ); 1118 break; 1119 default: 1120 { 1121 // added to avoid warnings 1122 } 1123 } 1124 else 1125 bTestEqual = bMatch; 1126 } 1127 if ( !bRealRegExp ) 1128 { 1129 if ( rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL 1130 || rEntry.eOp == SC_CONTAINS || rEntry.eOp == SC_DOES_NOT_CONTAIN 1131 || rEntry.eOp == SC_BEGINS_WITH || rEntry.eOp == SC_ENDS_WITH 1132 || rEntry.eOp == SC_DOES_NOT_BEGIN_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH ) 1133 { 1134 if ( !rEntry.bQueryByString && rEntry.pStr->Len() == 0 ) 1135 { 1136 // #i18374# When used from functions (match, countif, sumif, vlookup, hlookup, lookup), 1137 // the query value is assigned directly, and the string is empty. In that case, 1138 // don't find any string (isEqual would find empty string results in formula cells). 1139 bOk = sal_False; 1140 if ( rEntry.eOp == SC_NOT_EQUAL ) 1141 bOk = !bOk; 1142 } 1143 else if ( bMatchWholeCell ) 1144 { 1145 bOk = pTransliteration->isEqual( aCellStr, *rEntry.pStr ); 1146 if ( rEntry.eOp == SC_NOT_EQUAL ) 1147 bOk = !bOk; 1148 } 1149 else 1150 { 1151 String aCell( pTransliteration->transliterate( 1152 aCellStr, ScGlobal::eLnge, 0, aCellStr.Len(), 1153 NULL ) ); 1154 String aQuer( pTransliteration->transliterate( 1155 *rEntry.pStr, ScGlobal::eLnge, 0, rEntry.pStr->Len(), 1156 NULL ) ); 1157 xub_StrLen nIndex = (rEntry.eOp == SC_ENDS_WITH 1158 || rEntry.eOp == SC_DOES_NOT_END_WITH)? (aCell.Len()-aQuer.Len()):0; 1159 xub_StrLen nStrPos = aCell.Search( aQuer, nIndex ); 1160 switch (rEntry.eOp) 1161 { 1162 case SC_EQUAL: 1163 case SC_CONTAINS: 1164 bOk = ( nStrPos != STRING_NOTFOUND ); 1165 break; 1166 case SC_NOT_EQUAL: 1167 case SC_DOES_NOT_CONTAIN: 1168 bOk = ( nStrPos == STRING_NOTFOUND ); 1169 break; 1170 case SC_BEGINS_WITH: 1171 bOk = ( nStrPos == 0 ); 1172 break; 1173 case SC_DOES_NOT_BEGIN_WITH: 1174 bOk = ( nStrPos != 0 ); 1175 break; 1176 case SC_ENDS_WITH: 1177 bOk = ( nStrPos + aQuer.Len() == aCell.Len() ); 1178 break; 1179 case SC_DOES_NOT_END_WITH: 1180 bOk = ( nStrPos + aQuer.Len() != aCell.Len() ); 1181 break; 1182 default: 1183 { 1184 // added to avoid warnings 1185 } 1186 } 1187 } 1188 } 1189 else 1190 { // use collator here because data was probably sorted 1191 sal_Int32 nCompare = pCollator->compareString( 1192 aCellStr, *rEntry.pStr ); 1193 switch (rEntry.eOp) 1194 { 1195 case SC_LESS : 1196 bOk = (nCompare < 0); 1197 break; 1198 case SC_GREATER : 1199 bOk = (nCompare > 0); 1200 break; 1201 case SC_LESS_EQUAL : 1202 bOk = (nCompare <= 0); 1203 if ( bOk && pbTestEqualCondition && !bTestEqual ) 1204 bTestEqual = (nCompare == 0); 1205 break; 1206 case SC_GREATER_EQUAL : 1207 bOk = (nCompare >= 0); 1208 if ( bOk && pbTestEqualCondition && !bTestEqual ) 1209 bTestEqual = (nCompare == 0); 1210 break; 1211 default: 1212 { 1213 // added to avoid warnings 1214 } 1215 } 1216 } 1217 } 1218 } 1219 else if (rParam.bMixedComparison) 1220 { 1221 if (rEntry.bQueryByString && 1222 (rEntry.eOp == SC_LESS || rEntry.eOp == SC_LESS_EQUAL) && 1223 (pCell ? pCell->HasValueData() : 1224 HasValueData( static_cast<SCCOL>(rEntry.nField), nRow))) 1225 { 1226 bOk = sal_True; 1227 } 1228 else if (!rEntry.bQueryByString && 1229 (rEntry.eOp == SC_GREATER || rEntry.eOp == SC_GREATER_EQUAL) && 1230 (pCell ? pCell->HasStringData() : 1231 HasStringData( static_cast<SCCOL>(rEntry.nField), nRow))) 1232 { 1233 bOk = sal_True; 1234 } 1235 } 1236 1237 if (nPos == -1) 1238 { 1239 nPos++; 1240 pPasst[nPos] = bOk; 1241 pTest[nPos] = bTestEqual; 1242 } 1243 else 1244 { 1245 if (rEntry.eConnect == SC_AND) 1246 { 1247 pPasst[nPos] = pPasst[nPos] && bOk; 1248 pTest[nPos] = pTest[nPos] && bTestEqual; 1249 } 1250 else 1251 { 1252 nPos++; 1253 pPasst[nPos] = bOk; 1254 pTest[nPos] = bTestEqual; 1255 } 1256 } 1257 i++; 1258 } 1259 1260 for ( long j=1; j <= nPos; j++ ) 1261 { 1262 pPasst[0] = pPasst[0] || pPasst[j]; 1263 pTest[0] = pTest[0] || pTest[j]; 1264 } 1265 1266 sal_Bool bRet = pPasst[0]; 1267 if ( pPasst != &aBool[0] ) 1268 delete [] pPasst; 1269 if ( pbTestEqualCondition ) 1270 *pbTestEqualCondition = pTest[0]; 1271 if ( pTest != &aTest[0] ) 1272 delete [] pTest; 1273 1274 return bRet; 1275 } 1276 1277 void ScTable::TopTenQuery( ScQueryParam& rParam ) 1278 { 1279 sal_Bool bSortCollatorInitialized = sal_False; 1280 SCSIZE nEntryCount = rParam.GetEntryCount(); 1281 SCROW nRow1 = (rParam.bHasHeader ? rParam.nRow1 + 1 : rParam.nRow1); 1282 SCSIZE nCount = static_cast<SCSIZE>(rParam.nRow2 - nRow1 + 1); 1283 for ( SCSIZE i=0; (i<nEntryCount) && (rParam.GetEntry(i).bDoQuery); i++ ) 1284 { 1285 ScQueryEntry& rEntry = rParam.GetEntry(i); 1286 switch ( rEntry.eOp ) 1287 { 1288 case SC_TOPVAL: 1289 case SC_BOTVAL: 1290 case SC_TOPPERC: 1291 case SC_BOTPERC: 1292 { 1293 ScSortParam aLocalSortParam( rParam, static_cast<SCCOL>(rEntry.nField) ); 1294 aSortParam = aLocalSortParam; // used in CreateSortInfoArray, Compare 1295 if ( !bSortCollatorInitialized ) 1296 { 1297 bSortCollatorInitialized = sal_True; 1298 InitSortCollator( aLocalSortParam ); 1299 } 1300 ScSortInfoArray* pArray = CreateSortInfoArray( nRow1, rParam.nRow2 ); 1301 DecoladeRow( pArray, nRow1, rParam.nRow2 ); 1302 QuickSort( pArray, nRow1, rParam.nRow2 ); 1303 ScSortInfo** ppInfo = pArray->GetFirstArray(); 1304 SCSIZE nValidCount = nCount; 1305 // keine Note-/Leerzellen zaehlen, sind ans Ende sortiert 1306 while ( nValidCount > 0 && ( ppInfo[nValidCount-1]->pCell == NULL || 1307 ppInfo[nValidCount-1]->pCell->GetCellType() == CELLTYPE_NOTE ) ) 1308 nValidCount--; 1309 // keine Strings zaehlen, sind zwischen Value und Leer 1310 while ( nValidCount > 0 1311 && ppInfo[nValidCount-1]->pCell->HasStringData() ) 1312 nValidCount--; 1313 if ( nValidCount > 0 ) 1314 { 1315 if ( rEntry.bQueryByString ) 1316 { // dat wird nix 1317 rEntry.bQueryByString = sal_False; 1318 rEntry.nVal = 10; // 10 bzw. 10% 1319 } 1320 SCSIZE nVal = (rEntry.nVal >= 1 ? static_cast<SCSIZE>(rEntry.nVal) : 1); 1321 SCSIZE nOffset = 0; 1322 switch ( rEntry.eOp ) 1323 { 1324 case SC_TOPVAL: 1325 { 1326 rEntry.eOp = SC_GREATER_EQUAL; 1327 if ( nVal > nValidCount ) 1328 nVal = nValidCount; 1329 nOffset = nValidCount - nVal; // 1 <= nVal <= nValidCount 1330 } 1331 break; 1332 case SC_BOTVAL: 1333 { 1334 rEntry.eOp = SC_LESS_EQUAL; 1335 if ( nVal > nValidCount ) 1336 nVal = nValidCount; 1337 nOffset = nVal - 1; // 1 <= nVal <= nValidCount 1338 } 1339 break; 1340 case SC_TOPPERC: 1341 { 1342 rEntry.eOp = SC_GREATER_EQUAL; 1343 if ( nVal > 100 ) 1344 nVal = 100; 1345 nOffset = nValidCount - (nValidCount * nVal / 100); 1346 if ( nOffset >= nValidCount ) 1347 nOffset = nValidCount - 1; 1348 } 1349 break; 1350 case SC_BOTPERC: 1351 { 1352 rEntry.eOp = SC_LESS_EQUAL; 1353 if ( nVal > 100 ) 1354 nVal = 100; 1355 nOffset = (nValidCount * nVal / 100); 1356 if ( nOffset >= nValidCount ) 1357 nOffset = nValidCount - 1; 1358 } 1359 break; 1360 default: 1361 { 1362 // added to avoid warnings 1363 } 1364 } 1365 ScBaseCell* pCell = ppInfo[nOffset]->pCell; 1366 if ( pCell->HasValueData() ) 1367 { 1368 if ( pCell->GetCellType() == CELLTYPE_VALUE ) 1369 rEntry.nVal = ((ScValueCell*)pCell)->GetValue(); 1370 else 1371 rEntry.nVal = ((ScFormulaCell*)pCell)->GetValue(); 1372 } 1373 else 1374 { 1375 DBG_ERRORFILE( "TopTenQuery: pCell kein ValueData" ); 1376 rEntry.eOp = SC_GREATER_EQUAL; 1377 rEntry.nVal = 0; 1378 } 1379 } 1380 else 1381 { 1382 rEntry.eOp = SC_GREATER_EQUAL; 1383 rEntry.bQueryByString = sal_False; 1384 rEntry.nVal = 0; 1385 } 1386 delete pArray; 1387 } 1388 break; 1389 default: 1390 { 1391 // added to avoid warnings 1392 } 1393 } 1394 } 1395 if ( bSortCollatorInitialized ) 1396 DestroySortCollator(); 1397 } 1398 1399 static void lcl_PrepareQuery( ScDocument* pDoc, ScTable* pTab, ScQueryParam& rParam, sal_Bool* pSpecial ) 1400 { 1401 bool bTopTen = false; 1402 SCSIZE nEntryCount = rParam.GetEntryCount(); 1403 1404 for ( SCSIZE i = 0; i < nEntryCount; ++i ) 1405 { 1406 pSpecial[i] = sal_False; 1407 ScQueryEntry& rEntry = rParam.GetEntry(i); 1408 if ( rEntry.bDoQuery ) 1409 { 1410 if ( rEntry.bQueryByString ) 1411 { 1412 sal_uInt32 nIndex = 0; 1413 rEntry.bQueryByString = !( pDoc->GetFormatTable()-> 1414 IsNumberFormat( *rEntry.pStr, nIndex, rEntry.nVal ) ); 1415 if (rEntry.bQueryByDate) 1416 { 1417 if (!rEntry.bQueryByString && ((nIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0)) 1418 { 1419 const SvNumberformat* pEntry = pDoc->GetFormatTable()->GetEntry(nIndex); 1420 if (pEntry) 1421 { 1422 short nNumFmtType = pEntry->GetType(); 1423 if (!((nNumFmtType & NUMBERFORMAT_DATE) && !(nNumFmtType & NUMBERFORMAT_TIME))) 1424 rEntry.bQueryByDate = false; // not a date only 1425 } 1426 else 1427 rEntry.bQueryByDate = false; // what the ... not a date 1428 } 1429 else 1430 rEntry.bQueryByDate = false; // not a date 1431 } 1432 } 1433 else 1434 { 1435 // #58736# call from UNO or second call from autofilter 1436 if ( rEntry.nVal == SC_EMPTYFIELDS || rEntry.nVal == SC_NONEMPTYFIELDS ) 1437 { 1438 pSpecial[i] = sal_True; 1439 } 1440 } 1441 if ( !bTopTen ) 1442 { 1443 switch ( rEntry.eOp ) 1444 { 1445 case SC_TOPVAL: 1446 case SC_BOTVAL: 1447 case SC_TOPPERC: 1448 case SC_BOTPERC: 1449 { 1450 bTopTen = true; 1451 } 1452 break; 1453 default: 1454 { 1455 } 1456 } 1457 } 1458 } 1459 } 1460 1461 if ( bTopTen ) 1462 { 1463 pTab->TopTenQuery( rParam ); 1464 } 1465 } 1466 1467 SCSIZE ScTable::Query(ScQueryParam& rParamOrg, sal_Bool bKeepSub) 1468 { 1469 ScQueryParam aParam( rParamOrg ); 1470 ScStrCollection aScStrCollection; 1471 StrData* pStrData = NULL; 1472 1473 sal_Bool bStarted = sal_False; 1474 sal_Bool bOldResult = sal_True; 1475 SCROW nOldStart = 0; 1476 SCROW nOldEnd = 0; 1477 1478 SCSIZE nCount = 0; 1479 SCROW nOutRow = 0; 1480 SCROW nHeader = aParam.bHasHeader ? 1 : 0; 1481 1482 SCSIZE nEntryCount = aParam.GetEntryCount(); 1483 sal_Bool* pSpecial = new sal_Bool[nEntryCount]; 1484 lcl_PrepareQuery( pDocument, this, aParam, pSpecial ); 1485 1486 if (!aParam.bInplace) 1487 { 1488 nOutRow = aParam.nDestRow + nHeader; 1489 if (nHeader > 0) 1490 CopyData( aParam.nCol1, aParam.nRow1, aParam.nCol2, aParam.nRow1, 1491 aParam.nDestCol, aParam.nDestRow, aParam.nDestTab ); 1492 } 1493 1494 if (aParam.bInplace) 1495 IncRecalcLevel(); // #i116164# once for all entries 1496 1497 // #i116164# If there are no drawing objects within the area, call SetRowHidden/SetRowFiltered for all rows at the end 1498 std::vector<ScShowRowsEntry> aEntries; 1499 ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer(); 1500 bool bHasObjects = pDrawLayer && pDrawLayer->HasObjectsInRows( nTab, aParam.nRow1 + nHeader, aParam.nRow2, false ); 1501 1502 for (SCROW j=aParam.nRow1 + nHeader; j<=aParam.nRow2; j++) 1503 { 1504 sal_Bool bResult; // Filterergebnis 1505 sal_Bool bValid = ValidQuery(j, aParam, pSpecial); 1506 if (!bValid && bKeepSub) // Subtotals stehenlassen 1507 { 1508 for (SCCOL nCol=aParam.nCol1; nCol<=aParam.nCol2 && !bValid; nCol++) 1509 { 1510 ScBaseCell* pCell; 1511 pCell = GetCell( nCol, j ); 1512 if ( pCell ) 1513 if ( pCell->GetCellType() == CELLTYPE_FORMULA ) 1514 if (((ScFormulaCell*)pCell)->IsSubTotal()) 1515 if (RefVisible((ScFormulaCell*)pCell)) 1516 bValid = sal_True; 1517 } 1518 } 1519 if (bValid) 1520 { 1521 if (aParam.bDuplicate) 1522 bResult = sal_True; 1523 else 1524 { 1525 String aStr; 1526 for (SCCOL k=aParam.nCol1; k <= aParam.nCol2; k++) 1527 { 1528 String aCellStr; 1529 GetString(k, j, aCellStr); 1530 aStr += aCellStr; 1531 aStr += (sal_Unicode)1; 1532 } 1533 pStrData = new StrData(aStr); 1534 1535 sal_Bool bIsUnique = sal_True; 1536 if (pStrData) 1537 bIsUnique = aScStrCollection.Insert(pStrData); 1538 if (bIsUnique) 1539 bResult = sal_True; 1540 else 1541 { 1542 delete pStrData; 1543 bResult = sal_False; 1544 } 1545 } 1546 } 1547 else 1548 bResult = sal_False; 1549 1550 if (aParam.bInplace) 1551 { 1552 if (bResult == bOldResult && bStarted) 1553 nOldEnd = j; 1554 else 1555 { 1556 if (bStarted) 1557 { 1558 DBShowRows(nOldStart,nOldEnd, bOldResult, bHasObjects); 1559 if (!bHasObjects) 1560 aEntries.push_back(ScShowRowsEntry(nOldStart, nOldEnd, bOldResult)); 1561 } 1562 nOldStart = nOldEnd = j; 1563 bOldResult = bResult; 1564 } 1565 bStarted = sal_True; 1566 } 1567 else 1568 { 1569 if (bResult) 1570 { 1571 CopyData( aParam.nCol1,j, aParam.nCol2,j, aParam.nDestCol,nOutRow,aParam.nDestTab ); 1572 ++nOutRow; 1573 } 1574 } 1575 if (bResult) 1576 ++nCount; 1577 } 1578 1579 if (aParam.bInplace && bStarted) 1580 { 1581 DBShowRows(nOldStart,nOldEnd, bOldResult, bHasObjects); 1582 if (!bHasObjects) 1583 aEntries.push_back(ScShowRowsEntry(nOldStart, nOldEnd, bOldResult)); 1584 } 1585 1586 // #i116164# execute the collected SetRowHidden/SetRowFiltered calls 1587 if (!bHasObjects) 1588 { 1589 std::vector<ScShowRowsEntry>::const_iterator aEnd = aEntries.end(); 1590 std::vector<ScShowRowsEntry>::const_iterator aIter = aEntries.begin(); 1591 if ( aIter != aEnd ) 1592 { 1593 // do only one HeightChanged call with the final difference in heights 1594 long nOldHeight = 0; 1595 if ( pDrawLayer ) 1596 nOldHeight = static_cast<long>(GetRowHeight(aParam.nRow1 + nHeader, aParam.nRow2)); 1597 1598 // clear the range first instead of many changes in the middle of the filled array 1599 SetRowHidden(aParam.nRow1 + nHeader, aParam.nRow2, false); 1600 SetRowFiltered(aParam.nRow1 + nHeader, aParam.nRow2, false); 1601 1602 // insert from back, in case the filter range is large 1603 mpHiddenRows->setInsertFromBack(true); 1604 mpFilteredRows->setInsertFromBack(true); 1605 1606 while (aIter != aEnd) 1607 { 1608 if (!aIter->mbShow) 1609 { 1610 SCROW nStartRow = aIter->mnRow1; 1611 SCROW nEndRow = aIter->mnRow2; 1612 SetRowHidden(nStartRow, nEndRow, true); 1613 SetRowFiltered(nStartRow, nEndRow, true); 1614 } 1615 ++aIter; 1616 } 1617 1618 mpHiddenRows->setInsertFromBack(false); 1619 mpFilteredRows->setInsertFromBack(false); 1620 1621 if ( pDrawLayer ) 1622 { 1623 // if there are no objects in the filtered range, a single HeightChanged call is enough 1624 long nNewHeight = static_cast<long>(GetRowHeight(aParam.nRow1 + nHeader, aParam.nRow2)); 1625 pDrawLayer->HeightChanged( nTab, aParam.nRow1 + nHeader, nNewHeight - nOldHeight ); 1626 } 1627 } 1628 } 1629 1630 if (aParam.bInplace) 1631 DecRecalcLevel(); 1632 1633 delete[] pSpecial; 1634 1635 return nCount; 1636 } 1637 1638 sal_Bool ScTable::CreateExcelQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam) 1639 { 1640 sal_Bool bValid = sal_True; 1641 SCCOL* pFields = new SCCOL[nCol2-nCol1+1]; 1642 String aCellStr; 1643 SCCOL nCol = nCol1; 1644 DBG_ASSERT( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" ); 1645 SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab); 1646 SCROW nDBRow1 = rQueryParam.nRow1; 1647 SCCOL nDBCol2 = rQueryParam.nCol2; 1648 // Erste Zeile muessen Spaltenkoepfe sein 1649 while (bValid && (nCol <= nCol2)) 1650 { 1651 String aQueryStr; 1652 GetUpperCellString(nCol, nRow1, aQueryStr); 1653 sal_Bool bFound = sal_False; 1654 SCCOL i = rQueryParam.nCol1; 1655 while (!bFound && (i <= nDBCol2)) 1656 { 1657 if ( nTab == nDBTab ) 1658 GetUpperCellString(i, nDBRow1, aCellStr); 1659 else 1660 pDocument->GetUpperCellString(i, nDBRow1, nDBTab, aCellStr); 1661 bFound = (aCellStr == aQueryStr); 1662 if (!bFound) i++; 1663 } 1664 if (bFound) 1665 pFields[nCol - nCol1] = i; 1666 else 1667 bValid = sal_False; 1668 nCol++; 1669 } 1670 if (bValid) 1671 { 1672 sal_uLong nVisible = 0; 1673 for ( nCol=nCol1; nCol<=nCol2; nCol++ ) 1674 nVisible += aCol[nCol].VisibleCount( nRow1+1, nRow2 ); 1675 1676 if ( nVisible > SCSIZE_MAX / sizeof(void*) ) 1677 { 1678 DBG_ERROR("zu viele Filterkritierien"); 1679 nVisible = 0; 1680 } 1681 1682 SCSIZE nNewEntries = nVisible; 1683 rQueryParam.Resize( nNewEntries ); 1684 1685 SCSIZE nIndex = 0; 1686 SCROW nRow = nRow1 + 1; 1687 while (nRow <= nRow2) 1688 { 1689 nCol = nCol1; 1690 while (nCol <= nCol2) 1691 { 1692 GetInputString( nCol, nRow, aCellStr ); 1693 ScGlobal::pCharClass->toUpper( aCellStr ); 1694 if (aCellStr.Len() > 0) 1695 { 1696 if (nIndex < nNewEntries) 1697 { 1698 rQueryParam.GetEntry(nIndex).nField = pFields[nCol - nCol1]; 1699 rQueryParam.FillInExcelSyntax(aCellStr, nIndex); 1700 nIndex++; 1701 if (nIndex < nNewEntries) 1702 rQueryParam.GetEntry(nIndex).eConnect = SC_AND; 1703 } 1704 else 1705 bValid = sal_False; 1706 } 1707 nCol++; 1708 } 1709 nRow++; 1710 if (nIndex < nNewEntries) 1711 rQueryParam.GetEntry(nIndex).eConnect = SC_OR; 1712 } 1713 } 1714 delete [] pFields; 1715 return bValid; 1716 } 1717 1718 sal_Bool ScTable::CreateStarQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam) 1719 { 1720 // A valid StarQuery must be at least 4 columns wide. To be precise it 1721 // should be exactly 4 columns ... 1722 // Additionally, if this wasn't checked, a formula pointing to a valid 1-3 1723 // column Excel style query range immediately left to itself would result 1724 // in a circular reference when the field name or operator or value (first 1725 // to third query range column) is obtained (#i58354#). Furthermore, if the 1726 // range wasn't sufficiently specified data changes wouldn't flag formula 1727 // cells for recalculation. 1728 if (nCol2 - nCol1 < 3) 1729 return sal_False; 1730 1731 sal_Bool bValid; 1732 sal_Bool bFound; 1733 String aCellStr; 1734 SCSIZE nIndex = 0; 1735 SCROW nRow = nRow1; 1736 DBG_ASSERT( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" ); 1737 SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab); 1738 SCROW nDBRow1 = rQueryParam.nRow1; 1739 SCCOL nDBCol2 = rQueryParam.nCol2; 1740 1741 SCSIZE nNewEntries = static_cast<SCSIZE>(nRow2-nRow1+1); 1742 rQueryParam.Resize( nNewEntries ); 1743 1744 do 1745 { 1746 ScQueryEntry& rEntry = rQueryParam.GetEntry(nIndex); 1747 1748 bValid = sal_False; 1749 // Erste Spalte UND/ODER 1750 if (nIndex > 0) 1751 { 1752 GetUpperCellString(nCol1, nRow, aCellStr); 1753 if ( aCellStr == ScGlobal::GetRscString(STR_TABLE_UND) ) 1754 { 1755 rEntry.eConnect = SC_AND; 1756 bValid = sal_True; 1757 } 1758 else if ( aCellStr == ScGlobal::GetRscString(STR_TABLE_ODER) ) 1759 { 1760 rEntry.eConnect = SC_OR; 1761 bValid = sal_True; 1762 } 1763 } 1764 // Zweite Spalte FeldName 1765 if ((nIndex < 1) || bValid) 1766 { 1767 bFound = sal_False; 1768 GetUpperCellString(nCol1 + 1, nRow, aCellStr); 1769 for (SCCOL i=rQueryParam.nCol1; (i <= nDBCol2) && (!bFound); i++) 1770 { 1771 String aFieldStr; 1772 if ( nTab == nDBTab ) 1773 GetUpperCellString(i, nDBRow1, aFieldStr); 1774 else 1775 pDocument->GetUpperCellString(i, nDBRow1, nDBTab, aFieldStr); 1776 bFound = (aCellStr == aFieldStr); 1777 if (bFound) 1778 { 1779 rEntry.nField = i; 1780 bValid = sal_True; 1781 } 1782 else 1783 bValid = sal_False; 1784 } 1785 } 1786 // Dritte Spalte Operator =<>... 1787 if (bValid) 1788 { 1789 bFound = sal_False; 1790 GetUpperCellString(nCol1 + 2, nRow, aCellStr); 1791 if (aCellStr.GetChar(0) == '<') 1792 { 1793 if (aCellStr.GetChar(1) == '>') 1794 rEntry.eOp = SC_NOT_EQUAL; 1795 else if (aCellStr.GetChar(1) == '=') 1796 rEntry.eOp = SC_LESS_EQUAL; 1797 else 1798 rEntry.eOp = SC_LESS; 1799 } 1800 else if (aCellStr.GetChar(0) == '>') 1801 { 1802 if (aCellStr.GetChar(1) == '=') 1803 rEntry.eOp = SC_GREATER_EQUAL; 1804 else 1805 rEntry.eOp = SC_GREATER; 1806 } 1807 else if (aCellStr.GetChar(0) == '=') 1808 rEntry.eOp = SC_EQUAL; 1809 1810 } 1811 // Vierte Spalte Wert 1812 if (bValid) 1813 { 1814 GetString(nCol1 + 3, nRow, *rEntry.pStr); 1815 rEntry.bDoQuery = sal_True; 1816 } 1817 nIndex++; 1818 nRow++; 1819 } 1820 while (bValid && (nRow <= nRow2) /* && (nIndex < MAXQUERY) */ ); 1821 return bValid; 1822 } 1823 1824 sal_Bool ScTable::CreateQueryParam(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam) 1825 { 1826 SCSIZE i, nCount; 1827 PutInOrder(nCol1, nCol2); 1828 PutInOrder(nRow1, nRow2); 1829 1830 nCount = rQueryParam.GetEntryCount(); 1831 for (i=0; i < nCount; i++) 1832 rQueryParam.GetEntry(i).Clear(); 1833 1834 // Standard QueryTabelle 1835 sal_Bool bValid = CreateStarQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam); 1836 // Excel QueryTabelle 1837 if (!bValid) 1838 bValid = CreateExcelQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam); 1839 1840 nCount = rQueryParam.GetEntryCount(); 1841 if (bValid) 1842 { 1843 // bQueryByString muss gesetzt sein 1844 for (i=0; i < nCount; i++) 1845 rQueryParam.GetEntry(i).bQueryByString = sal_True; 1846 } 1847 else 1848 { 1849 // nix 1850 for (i=0; i < nCount; i++) 1851 rQueryParam.GetEntry(i).Clear(); 1852 } 1853 return bValid; 1854 } 1855 1856 sal_Bool ScTable::HasColHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW /* nEndRow */ ) 1857 { 1858 for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++) 1859 { 1860 CellType eType = GetCellType( nCol, nStartRow ); 1861 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT) 1862 return sal_False; 1863 } 1864 return sal_True; 1865 } 1866 1867 sal_Bool ScTable::HasRowHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL /* nEndCol */, SCROW nEndRow ) 1868 { 1869 for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++) 1870 { 1871 CellType eType = GetCellType( nStartCol, nRow ); 1872 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT) 1873 return sal_False; 1874 } 1875 return sal_True; 1876 } 1877 1878 void ScTable::GetFilterEntries(SCCOL nCol, SCROW nRow1, SCROW nRow2, TypedScStrCollection& rStrings, bool& rHasDates) 1879 { 1880 aCol[nCol].GetFilterEntries( nRow1, nRow2, rStrings, rHasDates ); 1881 } 1882 1883 void ScTable::GetFilteredFilterEntries( 1884 SCCOL nCol, SCROW nRow1, SCROW nRow2, const ScQueryParam& rParam, TypedScStrCollection& rStrings, bool& rHasDates ) 1885 { 1886 // remove the entry for this column from the query parameter 1887 ScQueryParam aParam( rParam ); 1888 SCSIZE nEntryCount = aParam.GetEntryCount(); 1889 for ( SCSIZE i = 0; i < nEntryCount && aParam.GetEntry(i).bDoQuery; ++i ) 1890 { 1891 ScQueryEntry& rEntry = aParam.GetEntry(i); 1892 if ( rEntry.nField == nCol ) 1893 { 1894 aParam.DeleteQuery(i); 1895 break; 1896 } 1897 } 1898 nEntryCount = aParam.GetEntryCount(); 1899 1900 sal_Bool* pSpecial = new sal_Bool[nEntryCount]; 1901 lcl_PrepareQuery( pDocument, this, aParam, pSpecial ); 1902 bool bHasDates = false; 1903 for ( SCROW j = nRow1; j <= nRow2; ++j ) 1904 { 1905 if ( ValidQuery( j, aParam, pSpecial ) ) 1906 { 1907 bool bThisHasDates = false; 1908 aCol[nCol].GetFilterEntries( j, j, rStrings, bThisHasDates ); 1909 bHasDates |= bThisHasDates; 1910 } 1911 } 1912 1913 rHasDates = bHasDates; 1914 delete[] pSpecial; 1915 } 1916 1917 sal_Bool ScTable::GetDataEntries(SCCOL nCol, SCROW nRow, TypedScStrCollection& rStrings, sal_Bool bLimit) 1918 { 1919 return aCol[nCol].GetDataEntries( nRow, rStrings, bLimit ); 1920 } 1921 1922 SCSIZE ScTable::GetCellCount(SCCOL nCol) const 1923 { 1924 return aCol[nCol].GetCellCount(); 1925 } 1926 1927 sal_uLong ScTable::GetCellCount() const 1928 { 1929 sal_uLong nCellCount = 0; 1930 1931 for ( SCCOL nCol=0; nCol<=MAXCOL; nCol++ ) 1932 nCellCount += aCol[nCol].GetCellCount(); 1933 1934 return nCellCount; 1935 } 1936 1937 sal_uLong ScTable::GetWeightedCount() const 1938 { 1939 sal_uLong nCellCount = 0; 1940 1941 for ( SCCOL nCol=0; nCol<=MAXCOL; nCol++ ) 1942 if ( aCol[nCol].GetCellCount() ) // GetCellCount ist inline 1943 nCellCount += aCol[nCol].GetWeightedCount(); 1944 1945 return nCellCount; 1946 } 1947 1948 sal_uLong ScTable::GetCodeCount() const 1949 { 1950 sal_uLong nCodeCount = 0; 1951 1952 for ( SCCOL nCol=0; nCol<=MAXCOL; nCol++ ) 1953 if ( aCol[nCol].GetCellCount() ) // GetCellCount ist inline 1954 nCodeCount += aCol[nCol].GetCodeCount(); 1955 1956 return nCodeCount; 1957 } 1958 1959 sal_Int32 ScTable::GetMaxStringLen( SCCOL nCol, SCROW nRowStart, 1960 SCROW nRowEnd, CharSet eCharSet ) const 1961 { 1962 if ( ValidCol(nCol) ) 1963 return aCol[nCol].GetMaxStringLen( nRowStart, nRowEnd, eCharSet ); 1964 else 1965 return 0; 1966 } 1967 1968 xub_StrLen ScTable::GetMaxNumberStringLen( 1969 sal_uInt16& nPrecision, SCCOL nCol, SCROW nRowStart, SCROW nRowEnd ) const 1970 { 1971 if ( ValidCol(nCol) ) 1972 return aCol[nCol].GetMaxNumberStringLen( nPrecision, nRowStart, nRowEnd ); 1973 else 1974 return 0; 1975 } 1976 1977 void ScTable::UpdateSelectionFunction( ScFunctionData& rData, 1978 SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, 1979 const ScMarkData& rMark ) 1980 { 1981 // Cursor neben einer Markierung nicht beruecksichtigen: 1982 //! nur noch MarkData uebergeben, Cursorposition ggf. hineinselektieren!!! 1983 sal_Bool bSingle = ( rMark.IsMarked() || !rMark.IsMultiMarked() ); 1984 1985 // Mehrfachselektion: 1986 1987 SCCOL nCol; 1988 if ( rMark.IsMultiMarked() ) 1989 for (nCol=0; nCol<=MAXCOL && !rData.bError; nCol++) 1990 if ( !pColFlags || !ColHidden(nCol) ) 1991 aCol[nCol].UpdateSelectionFunction( rMark, rData, *mpHiddenRows, 1992 bSingle && ( nCol >= nStartCol && nCol <= nEndCol ), 1993 nStartRow, nEndRow ); 1994 1995 // Einfachselektion (oder Cursor) nur wenn nicht negativ (und s.o.): 1996 1997 if ( bSingle && !rMark.IsMarkNegative() ) 1998 for (nCol=nStartCol; nCol<=nEndCol && !rData.bError; nCol++) 1999 if ( !pColFlags || !ColHidden(nCol) ) 2000 aCol[nCol].UpdateAreaFunction( rData, *mpHiddenRows, nStartRow, nEndRow ); 2001 } 2002 2003 void ScTable::FindConditionalFormat( sal_uLong nKey, ScRangeList& rList ) 2004 { 2005 SCROW nStartRow = 0, nEndRow = 0; 2006 for (SCCOL nCol=0; nCol<=MAXCOL; nCol++) 2007 { 2008 ScAttrIterator* pIter = aCol[nCol].CreateAttrIterator( 0, MAXROW ); 2009 const ScPatternAttr* pPattern = pIter->Next( nStartRow, nEndRow ); 2010 while (pPattern) 2011 { 2012 if (((SfxUInt32Item&)pPattern->GetItem(ATTR_CONDITIONAL)).GetValue() == nKey) 2013 rList.Join( ScRange(nCol,nStartRow,nTab, nCol,nEndRow,nTab) ); 2014 pPattern = pIter->Next( nStartRow, nEndRow ); 2015 } 2016 delete pIter; 2017 } 2018 } 2019 2020 2021 2022 2023