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