update dev300-m58
[ooovba.git] / sc / source / core / data / table3.cxx
blob52d7d327e9746c3a4d5c79d5ed73ecb9bfb0f884
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: table3.cxx,v $
10 * $Revision: 1.30.128.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
34 #include <rtl/math.hxx>
35 #include <unotools/textsearch.hxx>
36 #include <svtools/zforlist.hxx>
37 #include <unotools/charclass.hxx>
38 #include <unotools/collatorwrapper.hxx>
39 #include <com/sun/star/i18n/CollatorOptions.hpp>
40 #include <stdlib.h>
41 #include <unotools/transliterationwrapper.hxx>
43 #include "table.hxx"
44 #include "scitems.hxx"
45 #include "collect.hxx"
46 #include "attrib.hxx"
47 #include "cell.hxx"
48 #include "document.hxx"
49 #include "globstr.hrc"
50 #include "global.hxx"
51 #include "stlpool.hxx"
52 #include "compiler.hxx"
53 #include "patattr.hxx"
54 #include "subtotal.hxx"
55 #include "docoptio.hxx"
56 #include "markdata.hxx"
57 #include "rangelst.hxx"
58 #include "attarray.hxx"
59 #include "userlist.hxx"
60 #include "progress.hxx"
61 #include "cellform.hxx"
62 #include "postit.hxx"
63 #include "queryparam.hxx"
64 #include "svtools/zformat.hxx"
66 #include <vector>
68 using namespace ::com::sun::star;
70 namespace naturalsort {
72 using namespace ::com::sun::star::i18n;
74 /** Splits a given string into three parts: the prefix, number string, and
75 the suffix.
77 @param sWhole
78 Original string to be split into pieces
80 @param sPrefix
81 Prefix string that consists of the part before the first number token
83 @param sSuffix
84 String after the last number token. This may still contain number strings.
86 @param fNum
87 Number converted from the middle number string
89 @return Returns TRUE if a numeral element is found in a given string, or
90 FALSE if no numeral element is found.
92 bool SplitString( const rtl::OUString &sWhole,
93 rtl::OUString &sPrefix, rtl::OUString &sSuffix, double &fNum )
95 i18n::LocaleDataItem aLocaleItem = ScGlobal::pLocaleData->getLocaleItem();
97 // Get prefix element
98 rtl::OUString sEmpty, sUser = rtl::OUString::createFromAscii( "-" );
99 ParseResult aPRPre = ScGlobal::pCharClass->parsePredefinedToken(
100 KParseType::IDENTNAME, sWhole, 0,
101 KParseTokens::ANY_LETTER, sUser, KParseTokens::ANY_LETTER, sUser );
102 sPrefix = sWhole.copy( 0, aPRPre.EndPos );
104 // Return FALSE if no numeral element is found
105 if ( aPRPre.EndPos == sWhole.getLength() )
106 return false;
108 // Get numeral element
109 sUser = aLocaleItem.decimalSeparator;
110 ParseResult aPRNum = ScGlobal::pCharClass->parsePredefinedToken(
111 KParseType::ANY_NUMBER, sWhole, aPRPre.EndPos,
112 KParseTokens::ANY_NUMBER, sEmpty, KParseTokens::ANY_NUMBER, sUser );
114 if ( aPRNum.EndPos == aPRPre.EndPos )
115 return false;
117 fNum = aPRNum.Value;
118 sSuffix = sWhole.copy( aPRNum.EndPos );
120 return true;
123 /** Naturally compares two given strings.
125 This is the main function that should be called externally. It returns
126 either 1, 0, or -1 depending on the comparison result of given two strings.
128 @param sInput1
129 Input string 1
131 @param sInput2
132 Input string 2
134 @param bCaseSens
135 Boolean value for case sensitivity
137 @param pData
138 Pointer to user defined sort list
140 @param pCW
141 Pointer to collator wrapper for normal string comparison
143 @return Returnes 1 if sInput1 is greater, 0 if sInput1 == sInput2, and -1 if
144 sInput2 is greater.
146 short Compare( const String &sInput1, const String &sInput2,
147 const BOOL bCaseSens, const ScUserListData* pData, const CollatorWrapper *pCW )
149 rtl::OUString sStr1( sInput1 ), sStr2( sInput2 ), sPre1, sSuf1, sPre2, sSuf2;
153 double nNum1, nNum2;
154 BOOL bNumFound1 = SplitString( sStr1, sPre1, sSuf1, nNum1 );
155 BOOL bNumFound2 = SplitString( sStr2, sPre2, sSuf2, nNum2 );
157 short nPreRes; // Prefix comparison result
158 if ( pData )
160 if ( bCaseSens )
162 if ( !bNumFound1 || !bNumFound2 )
163 return static_cast<short>(pData->Compare( sStr1, sStr2 ));
164 else
165 nPreRes = pData->Compare( sPre1, sPre2 );
167 else
169 if ( !bNumFound1 || !bNumFound2 )
170 return static_cast<short>(pData->ICompare( sStr1, sStr2 ));
171 else
172 nPreRes = pData->ICompare( sPre1, sPre2 );
175 else
177 if ( !bNumFound1 || !bNumFound2 )
178 return static_cast<short>(pCW->compareString( sStr1, sStr2 ));
179 else
180 nPreRes = static_cast<short>(pCW->compareString( sPre1, sPre2 ));
183 // Prefix strings differ. Return immediately.
184 if ( nPreRes != 0 ) return nPreRes;
186 if ( nNum1 != nNum2 )
188 if ( nNum1 < nNum2 ) return -1;
189 return static_cast<short>( nNum1 > nNum2 );
192 // The prefix and the first numerical elements are equal, but the suffix
193 // strings may still differ. Stay in the loop.
195 sStr1 = sSuf1;
196 sStr2 = sSuf2;
198 } while (true);
200 return 0;
205 // STATIC DATA -----------------------------------------------------------
207 const USHORT nMaxSorts = 3; // maximale Anzahl Sortierkriterien in aSortParam
209 struct ScSortInfo
211 ScBaseCell* pCell;
212 SCCOLROW nOrg;
213 DECL_FIXEDMEMPOOL_NEWDEL( ScSortInfo );
215 const USHORT nMemPoolSortInfo = (0x8000 - 64) / sizeof(ScSortInfo);
216 IMPL_FIXEDMEMPOOL_NEWDEL( ScSortInfo, nMemPoolSortInfo, nMemPoolSortInfo )
218 // END OF STATIC DATA -----------------------------------------------------
221 class ScSortInfoArray
223 private:
224 ScSortInfo** pppInfo[nMaxSorts];
225 SCSIZE nCount;
226 SCCOLROW nStart;
227 USHORT nUsedSorts;
229 public:
230 ScSortInfoArray( USHORT nSorts, SCCOLROW nInd1, SCCOLROW nInd2 ) :
231 nCount( nInd2 - nInd1 + 1 ), nStart( nInd1 ),
232 nUsedSorts( Min( nSorts, nMaxSorts ) )
234 for ( USHORT nSort = 0; nSort < nUsedSorts; nSort++ )
236 ScSortInfo** ppInfo = new ScSortInfo* [nCount];
237 for ( SCSIZE j = 0; j < nCount; j++ )
238 ppInfo[j] = new ScSortInfo;
239 pppInfo[nSort] = ppInfo;
242 ~ScSortInfoArray()
244 for ( USHORT nSort = 0; nSort < nUsedSorts; nSort++ )
246 ScSortInfo** ppInfo = pppInfo[nSort];
247 for ( SCSIZE j = 0; j < nCount; j++ )
248 delete ppInfo[j];
249 delete [] ppInfo;
252 ScSortInfo* Get( USHORT nSort, SCCOLROW nInd )
253 { return (pppInfo[nSort])[ nInd - nStart ]; }
254 void Swap( SCCOLROW nInd1, SCCOLROW nInd2 )
256 SCSIZE n1 = static_cast<SCSIZE>(nInd1 - nStart);
257 SCSIZE n2 = static_cast<SCSIZE>(nInd2 - nStart);
258 for ( USHORT nSort = 0; nSort < nUsedSorts; nSort++ )
260 ScSortInfo** ppInfo = pppInfo[nSort];
261 ScSortInfo* pTmp = ppInfo[n1];
262 ppInfo[n1] = ppInfo[n2];
263 ppInfo[n2] = pTmp;
266 USHORT GetUsedSorts() { return nUsedSorts; }
267 ScSortInfo** GetFirstArray() { return pppInfo[0]; }
268 SCCOLROW GetStart() { return nStart; }
269 SCSIZE GetCount() { return nCount; }
272 ScSortInfoArray* ScTable::CreateSortInfoArray( SCCOLROW nInd1, SCCOLROW nInd2 )
274 USHORT nUsedSorts = 1;
275 while ( nUsedSorts < nMaxSorts && aSortParam.bDoSort[nUsedSorts] )
276 nUsedSorts++;
277 ScSortInfoArray* pArray = new ScSortInfoArray( nUsedSorts, nInd1, nInd2 );
278 if ( aSortParam.bByRow )
280 for ( USHORT nSort = 0; nSort < nUsedSorts; nSort++ )
282 SCCOL nCol = static_cast<SCCOL>(aSortParam.nField[nSort]);
283 ScColumn* pCol = &aCol[nCol];
284 for ( SCROW nRow = nInd1; nRow <= nInd2; nRow++ )
286 //2do: FillSortInfo an ScColumn und Array abklappern statt Search in GetCell
287 ScSortInfo* pInfo = pArray->Get( nSort, nRow );
288 pInfo->pCell = pCol->GetCell( nRow );
289 pInfo->nOrg = nRow;
293 else
295 for ( USHORT nSort = 0; nSort < nUsedSorts; nSort++ )
297 SCROW nRow = aSortParam.nField[nSort];
298 for ( SCCOL nCol = static_cast<SCCOL>(nInd1);
299 nCol <= static_cast<SCCOL>(nInd2); nCol++ )
301 ScSortInfo* pInfo = pArray->Get( nSort, nCol );
302 pInfo->pCell = GetCell( nCol, nRow );
303 pInfo->nOrg = nCol;
307 return pArray;
311 BOOL ScTable::IsSortCollatorGlobal() const
313 return pSortCollator == ScGlobal::GetCollator() ||
314 pSortCollator == ScGlobal::GetCaseCollator();
318 void ScTable::InitSortCollator( const ScSortParam& rPar )
320 if ( rPar.aCollatorLocale.Language.getLength() )
322 if ( !pSortCollator || IsSortCollatorGlobal() )
323 pSortCollator = new CollatorWrapper( pDocument->GetServiceManager() );
324 pSortCollator->loadCollatorAlgorithm( rPar.aCollatorAlgorithm,
325 rPar.aCollatorLocale, (rPar.bCaseSens ? 0 : SC_COLLATOR_IGNORES) );
327 else
328 { // SYSTEM
329 DestroySortCollator();
330 pSortCollator = (rPar.bCaseSens ? ScGlobal::GetCaseCollator() :
331 ScGlobal::GetCollator());
336 void ScTable::DestroySortCollator()
338 if ( pSortCollator )
340 if ( !IsSortCollatorGlobal() )
341 delete pSortCollator;
342 pSortCollator = NULL;
347 void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress& rProgress )
349 BOOL bByRow = aSortParam.bByRow;
350 SCSIZE nCount = pArray->GetCount();
351 SCCOLROW nStart = pArray->GetStart();
352 ScSortInfo** ppInfo = pArray->GetFirstArray();
353 ::std::vector<ScSortInfo*> aTable(nCount);
354 SCSIZE nPos;
355 for ( nPos = 0; nPos < nCount; nPos++ )
356 aTable[ppInfo[nPos]->nOrg - nStart] = ppInfo[nPos];
358 SCCOLROW nDest = nStart;
359 for ( nPos = 0; nPos < nCount; nPos++, nDest++ )
361 SCCOLROW nOrg = ppInfo[nPos]->nOrg;
362 if ( nDest != nOrg )
364 if ( bByRow )
365 SwapRow( nDest, nOrg );
366 else
367 SwapCol( static_cast<SCCOL>(nDest), static_cast<SCCOL>(nOrg) );
368 // neue Position des weggeswapten eintragen
369 ScSortInfo* p = ppInfo[nPos];
370 p->nOrg = nDest;
371 ::std::swap(p, aTable[nDest-nStart]);
372 p->nOrg = nOrg;
373 ::std::swap(p, aTable[nOrg-nStart]);
374 DBG_ASSERT( p == ppInfo[nPos], "SortReorder: nOrg MisMatch" );
376 rProgress.SetStateOnPercent( nPos );
380 short ScTable::CompareCell( USHORT nSort,
381 ScBaseCell* pCell1, SCCOL nCell1Col, SCROW nCell1Row,
382 ScBaseCell* pCell2, SCCOL nCell2Col, SCROW nCell2Row )
384 short nRes = 0;
386 CellType eType1 = CELLTYPE_NONE, eType2 = CELLTYPE_NONE;
387 if (pCell1)
389 eType1 = pCell1->GetCellType();
390 if (eType1 == CELLTYPE_NOTE)
391 pCell1 = NULL;
393 if (pCell2)
395 eType2 = pCell2->GetCellType();
396 if (eType2 == CELLTYPE_NOTE)
397 pCell2 = NULL;
400 if (pCell1)
402 if (pCell2)
404 BOOL bStr1 = ( eType1 != CELLTYPE_VALUE );
405 if ( eType1 == CELLTYPE_FORMULA && ((ScFormulaCell*)pCell1)->IsValue() )
406 bStr1 = FALSE;
407 BOOL bStr2 = ( eType2 != CELLTYPE_VALUE );
408 if ( eType2 == CELLTYPE_FORMULA && ((ScFormulaCell*)pCell2)->IsValue() )
409 bStr2 = FALSE;
411 if ( bStr1 && bStr2 ) // nur Strings untereinander als String vergleichen!
413 String aStr1;
414 String aStr2;
415 if (eType1 == CELLTYPE_STRING)
416 ((ScStringCell*)pCell1)->GetString(aStr1);
417 else
418 GetString(nCell1Col, nCell1Row, aStr1);
419 if (eType2 == CELLTYPE_STRING)
420 ((ScStringCell*)pCell2)->GetString(aStr2);
421 else
422 GetString(nCell2Col, nCell2Row, aStr2);
424 BOOL bUserDef = aSortParam.bUserDef; // custom sort order
425 BOOL bNaturalSort = aSortParam.bNaturalSort; // natural sort
426 BOOL bCaseSens = aSortParam.bCaseSens; // case sensitivity
428 if (bUserDef)
430 ScUserListData* pData =
431 static_cast<ScUserListData*>( (ScGlobal::GetUserList()->At(
432 aSortParam.nUserIndex)) );
434 if (pData)
436 if ( bNaturalSort )
437 nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, pData, pSortCollator );
438 else
440 if ( bCaseSens )
441 nRes = sal::static_int_cast<short>( pData->Compare(aStr1, aStr2) );
442 else
443 nRes = sal::static_int_cast<short>( pData->ICompare(aStr1, aStr2) );
446 else
447 bUserDef = FALSE;
450 if (!bUserDef)
452 if ( bNaturalSort )
453 nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, NULL, pSortCollator );
454 else
455 nRes = static_cast<short>( pSortCollator->compareString( aStr1, aStr2 ) );
458 else if ( bStr1 ) // String <-> Zahl
459 nRes = 1; // Zahl vorne
460 else if ( bStr2 ) // Zahl <-> String
461 nRes = -1; // Zahl vorne
462 else // Zahlen untereinander
464 double nVal1;
465 double nVal2;
466 if (eType1 == CELLTYPE_VALUE)
467 nVal1 = ((ScValueCell*)pCell1)->GetValue();
468 else if (eType1 == CELLTYPE_FORMULA)
469 nVal1 = ((ScFormulaCell*)pCell1)->GetValue();
470 else
471 nVal1 = 0;
472 if (eType2 == CELLTYPE_VALUE)
473 nVal2 = ((ScValueCell*)pCell2)->GetValue();
474 else if (eType2 == CELLTYPE_FORMULA)
475 nVal2 = ((ScFormulaCell*)pCell2)->GetValue();
476 else
477 nVal2 = 0;
478 if (nVal1 < nVal2)
479 nRes = -1;
480 else if (nVal1 > nVal2)
481 nRes = 1;
483 if ( !aSortParam.bAscending[nSort] )
484 nRes = -nRes;
486 else
487 nRes = -1;
489 else
491 if ( pCell2 )
492 nRes = 1;
493 else
494 nRes = 0; // beide leer
496 return nRes;
499 short ScTable::Compare( ScSortInfoArray* pArray, SCCOLROW nIndex1, SCCOLROW nIndex2 )
501 short nRes;
502 USHORT nSort = 0;
505 ScSortInfo* pInfo1 = pArray->Get( nSort, nIndex1 );
506 ScSortInfo* pInfo2 = pArray->Get( nSort, nIndex2 );
507 if ( aSortParam.bByRow )
508 nRes = CompareCell( nSort,
509 pInfo1->pCell, static_cast<SCCOL>(aSortParam.nField[nSort]), pInfo1->nOrg,
510 pInfo2->pCell, static_cast<SCCOL>(aSortParam.nField[nSort]), pInfo2->nOrg );
511 else
512 nRes = CompareCell( nSort,
513 pInfo1->pCell, static_cast<SCCOL>(pInfo1->nOrg), aSortParam.nField[nSort],
514 pInfo2->pCell, static_cast<SCCOL>(pInfo2->nOrg), aSortParam.nField[nSort] );
515 } while ( nRes == 0 && ++nSort < pArray->GetUsedSorts() );
516 if( nRes == 0 )
518 ScSortInfo* pInfo1 = pArray->Get( 0, nIndex1 );
519 ScSortInfo* pInfo2 = pArray->Get( 0, nIndex2 );
520 if( pInfo1->nOrg < pInfo2->nOrg )
521 nRes = -1;
522 else if( pInfo1->nOrg > pInfo2->nOrg )
523 nRes = 1;
525 return nRes;
528 void ScTable::QuickSort( ScSortInfoArray* pArray, SCsCOLROW nLo, SCsCOLROW nHi )
530 if ((nHi - nLo) == 1)
532 if (Compare(pArray, nLo, nHi) > 0)
533 pArray->Swap( nLo, nHi );
535 else
537 SCsCOLROW ni = nLo;
538 SCsCOLROW nj = nHi;
541 while ((ni <= nHi) && (Compare(pArray, ni, nLo)) < 0)
542 ni++;
543 while ((nj >= nLo) && (Compare(pArray, nLo, nj)) < 0)
544 nj--;
545 if (ni <= nj)
547 if (ni != nj)
548 pArray->Swap( ni, nj );
549 ni++;
550 nj--;
552 } while (ni < nj);
553 if ((nj - nLo) < (nHi - ni))
555 if (nLo < nj)
556 QuickSort(pArray, nLo, nj);
557 if (ni < nHi)
558 QuickSort(pArray, ni, nHi);
560 else
562 if (ni < nHi)
563 QuickSort(pArray, ni, nHi);
564 if (nLo < nj)
565 QuickSort(pArray, nLo, nj);
570 void ScTable::SwapCol(SCCOL nCol1, SCCOL nCol2)
572 for (SCROW nRow = aSortParam.nRow1; nRow <= aSortParam.nRow2; nRow++)
574 aCol[nCol1].SwapCell(nRow, aCol[nCol2]);
575 if (aSortParam.bIncludePattern)
577 const ScPatternAttr* pPat1 = GetPattern(nCol1, nRow);
578 const ScPatternAttr* pPat2 = GetPattern(nCol2, nRow);
579 if (pPat1 != pPat2)
581 SetPattern(nCol1, nRow, *pPat2, TRUE);
582 SetPattern(nCol2, nRow, *pPat1, TRUE);
588 void ScTable::SwapRow(SCROW nRow1, SCROW nRow2)
590 for (SCCOL nCol = aSortParam.nCol1; nCol <= aSortParam.nCol2; nCol++)
592 aCol[nCol].SwapRow(nRow1, nRow2);
593 if (aSortParam.bIncludePattern)
595 const ScPatternAttr* pPat1 = GetPattern(nCol, nRow1);
596 const ScPatternAttr* pPat2 = GetPattern(nCol, nRow2);
597 if (pPat1 != pPat2)
599 SetPattern(nCol, nRow1, *pPat2, TRUE);
600 SetPattern(nCol, nRow2, *pPat1, TRUE);
604 if (bGlobalKeepQuery)
606 bool bRow1Hidden = RowHidden(nRow1);
607 bool bRow2Hidden = RowHidden(nRow2);
608 SetRowHidden(nRow1, nRow1, bRow2Hidden);
609 SetRowHidden(nRow2, nRow2, bRow1Hidden);
611 bool bRow1Filtered = RowFiltered(nRow1);
612 bool bRow2Filtered = RowFiltered(nRow2);
613 SetRowFiltered(nRow1, nRow1, bRow2Filtered);
614 SetRowFiltered(nRow2, nRow2, bRow1Filtered);
618 short ScTable::Compare(SCCOLROW nIndex1, SCCOLROW nIndex2)
620 short nRes;
621 USHORT nSort = 0;
622 if (aSortParam.bByRow)
626 SCCOL nCol = static_cast<SCCOL>(aSortParam.nField[nSort]);
627 ScBaseCell* pCell1 = aCol[nCol].GetCell( nIndex1 );
628 ScBaseCell* pCell2 = aCol[nCol].GetCell( nIndex2 );
629 nRes = CompareCell( nSort, pCell1, nCol, nIndex1, pCell2, nCol, nIndex2 );
630 } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.bDoSort[nSort] );
632 else
636 SCROW nRow = aSortParam.nField[nSort];
637 ScBaseCell* pCell1 = aCol[nIndex1].GetCell( nRow );
638 ScBaseCell* pCell2 = aCol[nIndex2].GetCell( nRow );
639 nRes = CompareCell( nSort, pCell1, static_cast<SCCOL>(nIndex1),
640 nRow, pCell2, static_cast<SCCOL>(nIndex2), nRow );
641 } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.bDoSort[nSort] );
643 return nRes;
646 BOOL ScTable::IsSorted( SCCOLROW nStart, SCCOLROW nEnd ) // ueber aSortParam
648 for (SCCOLROW i=nStart; i<nEnd; i++)
650 if (Compare( i, i+1 ) > 0)
651 return FALSE;
653 return TRUE;
656 void ScTable::DecoladeRow( ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2 )
658 SCROW nRow;
659 SCROW nMax = nRow2 - nRow1;
660 for (SCROW i = nRow1; (i + 4) <= nRow2; i += 4)
662 nRow = rand() % nMax;
663 pArray->Swap(i, nRow1 + nRow);
667 void ScTable::Sort(const ScSortParam& rSortParam, BOOL bKeepQuery)
669 aSortParam = rSortParam;
670 InitSortCollator( rSortParam );
671 bGlobalKeepQuery = bKeepQuery;
672 if (rSortParam.bByRow)
674 SCROW nLastRow = 0;
675 for (SCCOL nCol = aSortParam.nCol1; nCol <= aSortParam.nCol2; nCol++)
676 nLastRow = Max(nLastRow, aCol[nCol].GetLastDataPos());
677 nLastRow = Min(nLastRow, aSortParam.nRow2);
678 SCROW nRow1 = (rSortParam.bHasHeader ?
679 aSortParam.nRow1 + 1 : aSortParam.nRow1);
680 if (!IsSorted(nRow1, nLastRow))
682 ScProgress aProgress( pDocument->GetDocumentShell(),
683 ScGlobal::GetRscString(STR_PROGRESS_SORTING), nLastRow - nRow1 );
684 ScSortInfoArray* pArray = CreateSortInfoArray( nRow1, nLastRow );
685 if ( nLastRow - nRow1 > 255 )
686 DecoladeRow( pArray, nRow1, nLastRow );
687 QuickSort( pArray, nRow1, nLastRow );
688 SortReorder( pArray, aProgress );
689 delete pArray;
690 // #158377# #i59745# update position of caption objects of cell notes
691 ScNoteUtil::UpdateCaptionPositions( *pDocument, ScRange( aSortParam.nCol1, nRow1, nTab, aSortParam.nCol2, nLastRow, nTab ) );
694 else
696 SCCOL nLastCol;
697 for (nLastCol = aSortParam.nCol2;
698 (nLastCol > aSortParam.nCol1) && aCol[nLastCol].IsEmptyBlock(aSortParam.nRow1, aSortParam.nRow2); nLastCol--)
701 SCCOL nCol1 = (rSortParam.bHasHeader ?
702 aSortParam.nCol1 + 1 : aSortParam.nCol1);
703 if (!IsSorted(nCol1, nLastCol))
705 ScProgress aProgress( pDocument->GetDocumentShell(),
706 ScGlobal::GetRscString(STR_PROGRESS_SORTING), nLastCol - nCol1 );
707 ScSortInfoArray* pArray = CreateSortInfoArray( nCol1, nLastCol );
708 QuickSort( pArray, nCol1, nLastCol );
709 SortReorder( pArray, aProgress );
710 delete pArray;
711 // #158377# #i59745# update position of caption objects of cell notes
712 ScNoteUtil::UpdateCaptionPositions( *pDocument, ScRange( nCol1, aSortParam.nRow1, nTab, nLastCol, aSortParam.nRow2, nTab ) );
715 DestroySortCollator();
719 // Testen, ob beim Loeschen von Zwischenergebnissen andere Daten mit geloescht werden
720 // (fuer Hinweis-Box)
722 BOOL ScTable::TestRemoveSubTotals( const ScSubTotalParam& rParam )
724 SCCOL nStartCol = rParam.nCol1;
725 SCROW nStartRow = rParam.nRow1 + 1; // Header
726 SCCOL nEndCol = rParam.nCol2;
727 SCROW nEndRow = rParam.nRow2;
729 SCCOL nCol;
730 SCROW nRow;
731 ScBaseCell* pCell;
733 BOOL bWillDelete = FALSE;
734 for ( nCol=nStartCol; nCol<=nEndCol && !bWillDelete; nCol++ )
736 ScColumnIterator aIter( &aCol[nCol],nStartRow,nEndRow );
737 while ( aIter.Next( nRow, pCell ) && !bWillDelete )
739 if ( pCell->GetCellType() == CELLTYPE_FORMULA )
740 if (((ScFormulaCell*)pCell)->IsSubTotal())
742 for (SCCOL nTestCol=0; nTestCol<=MAXCOL; nTestCol++)
743 if (nTestCol<nStartCol || nTestCol>nEndCol)
744 if (aCol[nTestCol].HasDataAt(nRow))
745 bWillDelete = TRUE;
749 return bWillDelete;
752 // alte Ergebnisse loeschen
753 // rParam.nRow2 wird veraendert !
755 void ScTable::RemoveSubTotals( ScSubTotalParam& rParam )
757 SCCOL nStartCol = rParam.nCol1;
758 SCROW nStartRow = rParam.nRow1 + 1; // Header
759 SCCOL nEndCol = rParam.nCol2;
760 SCROW nEndRow = rParam.nRow2; // wird veraendert
762 SCCOL nCol;
763 SCROW nRow;
764 ScBaseCell* pCell;
766 for ( nCol=nStartCol; nCol<=nEndCol; nCol++ )
768 ScColumnIterator aIter( &aCol[nCol],nStartRow,nEndRow );
769 while ( aIter.Next( nRow, pCell ) )
771 if ( pCell->GetCellType() == CELLTYPE_FORMULA )
772 if (((ScFormulaCell*)pCell)->IsSubTotal())
774 RemoveRowBreak(nRow+1, false, true);
775 pDocument->DeleteRow( 0,nTab, MAXCOL,nTab, nRow, 1 );
776 --nEndRow;
777 aIter = ScColumnIterator( &aCol[nCol],nRow,nEndRow );
782 rParam.nRow2 = nEndRow; // neues Ende
785 // harte Zahlenformate loeschen (fuer Ergebnisformeln)
787 void lcl_RemoveNumberFormat( ScTable* pTab, SCCOL nCol, SCROW nRow )
789 const ScPatternAttr* pPattern = pTab->GetPattern( nCol, nRow );
790 if ( pPattern->GetItemSet().GetItemState( ATTR_VALUE_FORMAT, FALSE )
791 == SFX_ITEM_SET )
793 ScPatternAttr aNewPattern( *pPattern );
794 SfxItemSet& rSet = aNewPattern.GetItemSet();
795 rSet.ClearItem( ATTR_VALUE_FORMAT );
796 rSet.ClearItem( ATTR_LANGUAGE_FORMAT );
797 pTab->SetPattern( nCol, nRow, aNewPattern, TRUE );
802 // at least MSC needs this at linkage level to be able to use it in a template
803 typedef struct lcl_ScTable_DoSubTotals_RowEntry
805 USHORT nGroupNo;
806 SCROW nSubStartRow;
807 SCROW nDestRow;
808 SCROW nFuncStart;
809 SCROW nFuncEnd;
810 } RowEntry;
812 // neue Zwischenergebnisse
813 // rParam.nRow2 wird veraendert !
815 BOOL ScTable::DoSubTotals( ScSubTotalParam& rParam )
817 SCCOL nStartCol = rParam.nCol1;
818 SCROW nStartRow = rParam.nRow1 + 1; // Header
819 SCCOL nEndCol = rParam.nCol2;
820 SCROW nEndRow = rParam.nRow2; // wird veraendert
821 USHORT i;
823 // Leerzeilen am Ende weglassen,
824 // damit alle Ueberlaeufe (MAXROW) bei InsertRow gefunden werden (#35180#)
825 // Wenn sortiert wurde, sind alle Leerzeilen am Ende.
826 SCSIZE nEmpty = GetEmptyLinesInBlock( nStartCol, nStartRow, nEndCol, nEndRow, DIR_BOTTOM );
827 nEndRow -= nEmpty;
829 USHORT nLevelCount = 0; // Anzahl Gruppierungen
830 BOOL bDoThis = TRUE;
831 for (i=0; i<MAXSUBTOTAL && bDoThis; i++)
832 if (rParam.bGroupActive[i])
833 nLevelCount = i+1;
834 else
835 bDoThis = FALSE;
837 if (nLevelCount==0) // nichts tun
838 return TRUE;
840 SCCOL* nGroupCol = rParam.nField; // Spalten nach denen
841 // gruppiert wird
843 // #44444# Durch (leer) als eigene Kategorie muss immer auf
844 // Teilergebniszeilen aus den anderen Spalten getestet werden
845 // (frueher nur, wenn eine Spalte mehrfach vorkam)
846 BOOL bTestPrevSub = ( nLevelCount > 1 );
848 String aSubString;
849 String aOutString;
851 BOOL bIgnoreCase = !rParam.bCaseSens;
853 String *pCompString[MAXSUBTOTAL]; // Pointer wegen Compiler-Problemen
854 for (i=0; i<MAXSUBTOTAL; i++)
855 pCompString[i] = new String;
857 //! sortieren?
859 ScStyleSheet* pStyle = (ScStyleSheet*) pDocument->GetStyleSheetPool()->Find(
860 ScGlobal::GetRscString(STR_STYLENAME_RESULT), SFX_STYLE_FAMILY_PARA );
862 BOOL bSpaceLeft = TRUE; // Erfolg beim Einfuegen?
864 // #90279# For performance reasons collect formula entries so their
865 // references don't have to be tested for updates each time a new row is
866 // inserted
867 RowEntry aRowEntry;
868 ::std::vector< RowEntry > aRowVector;
870 for (USHORT nLevel=0; nLevel<=nLevelCount && bSpaceLeft; nLevel++) // incl. Gesamtergebnis
872 BOOL bTotal = ( nLevel == nLevelCount );
873 aRowEntry.nGroupNo = bTotal ? 0 : (nLevelCount-nLevel-1);
875 // how many results per level
876 SCCOL nResCount = rParam.nSubTotals[aRowEntry.nGroupNo];
877 // result functions
878 ScSubTotalFunc* eResFunc = rParam.pFunctions[aRowEntry.nGroupNo];
880 if (nResCount > 0) // sonst nur sortieren
882 for (i=0; i<=aRowEntry.nGroupNo; i++)
884 GetString( nGroupCol[i], nStartRow, aSubString );
885 if ( bIgnoreCase )
886 *pCompString[i] = ScGlobal::pCharClass->upper( aSubString );
887 else
888 *pCompString[i] = aSubString;
889 } // aSubString bleibt auf dem letzten stehen
891 BOOL bBlockVis = FALSE; // Gruppe eingeblendet?
892 aRowEntry.nSubStartRow = nStartRow;
893 for (SCROW nRow=nStartRow; nRow<=nEndRow+1 && bSpaceLeft; nRow++)
895 BOOL bChanged;
896 if (nRow>nEndRow)
897 bChanged = TRUE;
898 else
900 bChanged = FALSE;
901 if (!bTotal)
903 String aString;
904 for (i=0; i<=aRowEntry.nGroupNo && !bChanged; i++)
906 GetString( nGroupCol[i], nRow, aString );
907 if (bIgnoreCase)
908 ScGlobal::pCharClass->toUpper( aString );
909 // #41427# wenn sortiert, ist "leer" eine eigene Gruppe
910 // sonst sind leere Zellen unten erlaubt
911 bChanged = ( ( aString.Len() || rParam.bDoSort ) &&
912 aString != *pCompString[i] );
914 if ( bChanged && bTestPrevSub )
916 // No group change on rows that will contain subtotal formulas
917 for ( ::std::vector< RowEntry >::const_iterator
918 iEntry( aRowVector.begin());
919 iEntry != aRowVector.end(); ++iEntry)
921 if ( iEntry->nDestRow == nRow )
923 bChanged = FALSE;
924 break;
930 if ( bChanged )
932 aRowEntry.nDestRow = nRow;
933 aRowEntry.nFuncStart = aRowEntry.nSubStartRow;
934 aRowEntry.nFuncEnd = nRow-1;
936 bSpaceLeft = pDocument->InsertRow( 0, nTab, MAXCOL, nTab,
937 aRowEntry.nDestRow, 1 );
938 DBShowRow( aRowEntry.nDestRow, bBlockVis );
939 bBlockVis = FALSE;
940 if ( rParam.bPagebreak && nRow < MAXROW &&
941 aRowEntry.nSubStartRow != nStartRow && nLevel == 0)
942 SetRowBreak(aRowEntry.nSubStartRow, false, true);
944 if (bSpaceLeft)
946 for ( ::std::vector< RowEntry >::iterator iMove(
947 aRowVector.begin() );
948 iMove != aRowVector.end(); ++iMove)
950 if ( aRowEntry.nDestRow <= iMove->nSubStartRow )
951 ++iMove->nSubStartRow;
952 if ( aRowEntry.nDestRow <= iMove->nDestRow )
953 ++iMove->nDestRow;
954 if ( aRowEntry.nDestRow <= iMove->nFuncStart )
955 ++iMove->nFuncStart;
956 if ( aRowEntry.nDestRow <= iMove->nFuncEnd )
957 ++iMove->nFuncEnd;
959 // collect formula positions
960 aRowVector.push_back( aRowEntry );
962 if (bTotal) // "Gesamtergebnis"
963 aOutString = ScGlobal::GetRscString( STR_TABLE_GESAMTERGEBNIS );
964 else
965 { // " Ergebnis"
966 aOutString = aSubString;
967 if (!aOutString.Len())
968 aOutString = ScGlobal::GetRscString( STR_EMPTYDATA );
969 aOutString += ' ';
970 USHORT nStrId = STR_TABLE_ERGEBNIS;
971 if ( nResCount == 1 )
972 switch ( eResFunc[0] )
974 case SUBTOTAL_FUNC_AVE: nStrId = STR_FUN_TEXT_AVG; break;
975 case SUBTOTAL_FUNC_CNT:
976 case SUBTOTAL_FUNC_CNT2: nStrId = STR_FUN_TEXT_COUNT; break;
977 case SUBTOTAL_FUNC_MAX: nStrId = STR_FUN_TEXT_MAX; break;
978 case SUBTOTAL_FUNC_MIN: nStrId = STR_FUN_TEXT_MIN; break;
979 case SUBTOTAL_FUNC_PROD: nStrId = STR_FUN_TEXT_PRODUCT; break;
980 case SUBTOTAL_FUNC_STD:
981 case SUBTOTAL_FUNC_STDP: nStrId = STR_FUN_TEXT_STDDEV; break;
982 case SUBTOTAL_FUNC_SUM: nStrId = STR_FUN_TEXT_SUM; break;
983 case SUBTOTAL_FUNC_VAR:
984 case SUBTOTAL_FUNC_VARP: nStrId = STR_FUN_TEXT_VAR; break;
985 default:
987 // added to avoid warnings
990 aOutString += ScGlobal::GetRscString( nStrId );
992 SetString( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, nTab, aOutString );
993 ApplyStyle( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, *pStyle );
995 ++nRow;
996 ++nEndRow;
997 aRowEntry.nSubStartRow = nRow;
998 for (i=0; i<=aRowEntry.nGroupNo; i++)
1000 GetString( nGroupCol[i], nRow, aSubString );
1001 if ( bIgnoreCase )
1002 *pCompString[i] = ScGlobal::pCharClass->upper( aSubString );
1003 else
1004 *pCompString[i] = aSubString;
1008 bBlockVis = !RowFiltered(nRow);
1011 else
1013 // DBG_ERROR( "nSubTotals==0 bei DoSubTotals" );
1017 // now insert the formulas
1018 ScComplexRefData aRef;
1019 aRef.InitFlags();
1020 aRef.Ref1.nTab = nTab;
1021 aRef.Ref2.nTab = nTab;
1022 for ( ::std::vector< RowEntry >::const_iterator iEntry( aRowVector.begin());
1023 iEntry != aRowVector.end(); ++iEntry)
1025 SCCOL nResCount = rParam.nSubTotals[iEntry->nGroupNo];
1026 SCCOL* nResCols = rParam.pSubTotals[iEntry->nGroupNo];
1027 ScSubTotalFunc* eResFunc = rParam.pFunctions[iEntry->nGroupNo];
1028 for ( SCCOL nResult=0; nResult < nResCount; ++nResult )
1030 aRef.Ref1.nCol = nResCols[nResult];
1031 aRef.Ref1.nRow = iEntry->nFuncStart;
1032 aRef.Ref2.nCol = nResCols[nResult];
1033 aRef.Ref2.nRow = iEntry->nFuncEnd;
1035 ScTokenArray aArr;
1036 aArr.AddOpCode( ocSubTotal );
1037 aArr.AddOpCode( ocOpen );
1038 aArr.AddDouble( (double) eResFunc[nResult] );
1039 aArr.AddOpCode( ocSep );
1040 aArr.AddDoubleReference( aRef );
1041 aArr.AddOpCode( ocClose );
1042 aArr.AddOpCode( ocStop );
1043 ScBaseCell* pCell = new ScFormulaCell( pDocument, ScAddress(
1044 nResCols[nResult], iEntry->nDestRow, nTab), &aArr );
1045 PutCell( nResCols[nResult], iEntry->nDestRow, pCell );
1047 if ( nResCols[nResult] != nGroupCol[iEntry->nGroupNo] )
1049 ApplyStyle( nResCols[nResult], iEntry->nDestRow, *pStyle );
1051 // Zahlformat loeschen
1052 lcl_RemoveNumberFormat( this, nResCols[nResult], iEntry->nDestRow );
1058 //! je nach Einstellung Zwischensummen-Zeilen nach oben verschieben ?
1060 //! Outlines direkt erzeugen?
1062 if (bSpaceLeft)
1063 DoAutoOutline( nStartCol, nStartRow, nEndCol, nEndRow );
1065 for (i=0; i<MAXSUBTOTAL; i++)
1066 delete pCompString[i];
1068 rParam.nRow2 = nEndRow; // neues Ende
1069 return bSpaceLeft;
1073 BOOL ScTable::ValidQuery(SCROW nRow, const ScQueryParam& rParam,
1074 BOOL* pSpecial /* =NULL */ , ScBaseCell* pCell /* =NULL */ ,
1075 BOOL* pbTestEqualCondition /* = NULL */ )
1077 if (!rParam.GetEntry(0).bDoQuery)
1078 return TRUE;
1080 //---------------------------------------------------------------
1082 const SCSIZE nFixedBools = 32;
1083 BOOL aBool[nFixedBools];
1084 BOOL aTest[nFixedBools];
1085 SCSIZE nEntryCount = rParam.GetEntryCount();
1086 BOOL* pPasst = ( nEntryCount <= nFixedBools ? &aBool[0] : new BOOL[nEntryCount] );
1087 BOOL* pTest = ( nEntryCount <= nFixedBools ? &aTest[0] : new BOOL[nEntryCount] );
1089 long nPos = -1;
1090 SCSIZE i = 0;
1091 BOOL bMatchWholeCell = pDocument->GetDocOptions().IsMatchWholeCell();
1092 CollatorWrapper* pCollator = (rParam.bCaseSens ? ScGlobal::GetCaseCollator() :
1093 ScGlobal::GetCollator());
1094 ::utl::TransliterationWrapper* pTransliteration = (rParam.bCaseSens ?
1095 ScGlobal::GetCaseTransliteration() : ScGlobal::GetpTransliteration());
1097 while ( (i < nEntryCount) && rParam.GetEntry(i).bDoQuery )
1099 ScQueryEntry& rEntry = rParam.GetEntry(i);
1100 // we can only handle one single direct query
1101 if ( !pCell || i > 0 )
1102 pCell = GetCell( static_cast<SCCOL>(rEntry.nField), nRow );
1104 BOOL bOk = FALSE;
1105 BOOL bTestEqual = FALSE;
1107 if ( pSpecial && pSpecial[i] )
1109 if (rEntry.nVal == SC_EMPTYFIELDS)
1110 bOk = !( aCol[rEntry.nField].HasDataAt( nRow ) );
1111 else // if (rEntry.nVal == SC_NONEMPTYFIELDS)
1112 bOk = aCol[rEntry.nField].HasDataAt( nRow );
1114 else if ( !rEntry.bQueryByString && (pCell ? pCell->HasValueData() :
1115 HasValueData( static_cast<SCCOL>(rEntry.nField), nRow)))
1116 { // by Value
1117 double nCellVal;
1118 if ( pCell )
1120 switch ( pCell->GetCellType() )
1122 case CELLTYPE_VALUE :
1123 nCellVal = ((ScValueCell*)pCell)->GetValue();
1124 break;
1125 case CELLTYPE_FORMULA :
1126 nCellVal = ((ScFormulaCell*)pCell)->GetValue();
1127 break;
1128 default:
1129 nCellVal = 0.0;
1133 else
1134 nCellVal = GetValue( static_cast<SCCOL>(rEntry.nField), nRow );
1136 if (rEntry.bQueryByDate)
1138 sal_uInt32 nNumFmt = GetNumberFormat(static_cast<SCCOL>(rEntry.nField), nRow);
1139 const SvNumberformat* pEntry = pDocument->GetFormatTable()->GetEntry(nNumFmt);
1140 if (pEntry)
1142 short nNumFmtType = pEntry->GetType();
1143 if ((nNumFmtType & NUMBERFORMAT_DATE) && !(nNumFmtType & NUMBERFORMAT_TIME))
1144 // The format is of date type. Strip off the time element.
1145 nCellVal = ::rtl::math::approxFloor(nCellVal);
1149 switch (rEntry.eOp)
1151 case SC_EQUAL :
1152 bOk = ::rtl::math::approxEqual( nCellVal, rEntry.nVal );
1153 break;
1154 case SC_LESS :
1155 bOk = (nCellVal < rEntry.nVal) && !::rtl::math::approxEqual( nCellVal, rEntry.nVal );
1156 break;
1157 case SC_GREATER :
1158 bOk = (nCellVal > rEntry.nVal) && !::rtl::math::approxEqual( nCellVal, rEntry.nVal );
1159 break;
1160 case SC_LESS_EQUAL :
1161 bOk = (nCellVal < rEntry.nVal) || ::rtl::math::approxEqual( nCellVal, rEntry.nVal );
1162 if ( bOk && pbTestEqualCondition )
1163 bTestEqual = ::rtl::math::approxEqual( nCellVal, rEntry.nVal );
1164 break;
1165 case SC_GREATER_EQUAL :
1166 bOk = (nCellVal > rEntry.nVal) || ::rtl::math::approxEqual( nCellVal, rEntry.nVal );
1167 if ( bOk && pbTestEqualCondition )
1168 bTestEqual = ::rtl::math::approxEqual( nCellVal, rEntry.nVal );
1169 break;
1170 case SC_NOT_EQUAL :
1171 bOk = !::rtl::math::approxEqual( nCellVal, rEntry.nVal );
1172 break;
1173 default:
1175 // added to avoid warnings
1179 else if ( (rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL) ||
1180 (rEntry.eOp == SC_CONTAINS || rEntry.eOp == SC_DOES_NOT_CONTAIN ||
1181 rEntry.eOp == SC_BEGINS_WITH || rEntry.eOp == SC_ENDS_WITH ||
1182 rEntry.eOp == SC_DOES_NOT_BEGIN_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH) ||
1183 (rEntry.bQueryByString && (pCell ? pCell->HasStringData() :
1184 HasStringData(
1185 static_cast<SCCOL>(rEntry.nField),
1186 nRow))))
1187 { // by String
1188 String aCellStr;
1189 if( rEntry.eOp == SC_CONTAINS || rEntry.eOp == SC_DOES_NOT_CONTAIN
1190 || rEntry.eOp == SC_BEGINS_WITH || rEntry.eOp == SC_ENDS_WITH
1191 || rEntry.eOp == SC_DOES_NOT_BEGIN_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH )
1192 bMatchWholeCell = FALSE;
1193 if ( pCell )
1195 if (pCell->GetCellType() != CELLTYPE_NOTE)
1197 ULONG nFormat = GetNumberFormat( static_cast<SCCOL>(rEntry.nField), nRow );
1198 ScCellFormat::GetInputString( pCell, nFormat, aCellStr, *(pDocument->GetFormatTable()) );
1201 else
1202 GetInputString( static_cast<SCCOL>(rEntry.nField), nRow, aCellStr );
1204 BOOL bRealRegExp = (rParam.bRegExp && ((rEntry.eOp == SC_EQUAL)
1205 || (rEntry.eOp == SC_NOT_EQUAL) || (rEntry.eOp == SC_CONTAINS)
1206 || (rEntry.eOp == SC_DOES_NOT_CONTAIN) || (rEntry.eOp == SC_BEGINS_WITH)
1207 || (rEntry.eOp == SC_ENDS_WITH) || (rEntry.eOp == SC_DOES_NOT_BEGIN_WITH)
1208 || (rEntry.eOp == SC_DOES_NOT_END_WITH)));
1209 BOOL bTestRegExp = (pbTestEqualCondition && rParam.bRegExp
1210 && ((rEntry.eOp == SC_LESS_EQUAL)
1211 || (rEntry.eOp == SC_GREATER_EQUAL)));
1212 if ( bRealRegExp || bTestRegExp )
1214 xub_StrLen nStart = 0;
1215 xub_StrLen nEnd = aCellStr.Len();
1217 // from 614 on, nEnd is behind the found text
1218 BOOL bMatch = FALSE;
1219 if ( rEntry.eOp == SC_ENDS_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH )
1221 nEnd = 0;
1222 nStart = aCellStr.Len();
1223 bMatch = (BOOL) rEntry.GetSearchTextPtr( rParam.bCaseSens )
1224 ->SearchBkwrd( aCellStr, &nStart, &nEnd );
1226 else
1228 bMatch = (BOOL) rEntry.GetSearchTextPtr( rParam.bCaseSens )
1229 ->SearchFrwrd( aCellStr, &nStart, &nEnd );
1231 if ( bMatch && bMatchWholeCell
1232 && (nStart != 0 || nEnd != aCellStr.Len()) )
1233 bMatch = FALSE; // RegExp must match entire cell string
1234 if ( bRealRegExp )
1235 switch (rEntry.eOp)
1237 case SC_EQUAL:
1238 case SC_CONTAINS:
1239 bOk = bMatch;
1240 break;
1241 case SC_NOT_EQUAL:
1242 case SC_DOES_NOT_CONTAIN:
1243 bOk = !bMatch;
1244 break;
1245 case SC_BEGINS_WITH:
1246 bOk = ( bMatch && (nStart == 0) );
1247 break;
1248 case SC_DOES_NOT_BEGIN_WITH:
1249 bOk = !( bMatch && (nStart == 0) );
1250 break;
1251 case SC_ENDS_WITH:
1252 bOk = ( bMatch && (nEnd == aCellStr.Len()) );
1253 break;
1254 case SC_DOES_NOT_END_WITH:
1255 bOk = !( bMatch && (nEnd == aCellStr.Len()) );
1256 break;
1257 default:
1259 // added to avoid warnings
1262 else
1263 bTestEqual = bMatch;
1265 if ( !bRealRegExp )
1267 if ( rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL
1268 || rEntry.eOp == SC_CONTAINS || rEntry.eOp == SC_DOES_NOT_CONTAIN
1269 || rEntry.eOp == SC_BEGINS_WITH || rEntry.eOp == SC_ENDS_WITH
1270 || rEntry.eOp == SC_DOES_NOT_BEGIN_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH )
1272 if ( !rEntry.bQueryByString && rEntry.pStr->Len() == 0 )
1274 // #i18374# When used from functions (match, countif, sumif, vlookup, hlookup, lookup),
1275 // the query value is assigned directly, and the string is empty. In that case,
1276 // don't find any string (isEqual would find empty string results in formula cells).
1277 bOk = FALSE;
1278 if ( rEntry.eOp == SC_NOT_EQUAL )
1279 bOk = !bOk;
1281 else if ( bMatchWholeCell )
1283 bOk = pTransliteration->isEqual( aCellStr, *rEntry.pStr );
1284 if ( rEntry.eOp == SC_NOT_EQUAL )
1285 bOk = !bOk;
1287 else
1289 String aCell( pTransliteration->transliterate(
1290 aCellStr, ScGlobal::eLnge, 0, aCellStr.Len(),
1291 NULL ) );
1292 String aQuer( pTransliteration->transliterate(
1293 *rEntry.pStr, ScGlobal::eLnge, 0, rEntry.pStr->Len(),
1294 NULL ) );
1295 xub_StrLen nIndex = (rEntry.eOp == SC_ENDS_WITH
1296 || rEntry.eOp == SC_DOES_NOT_END_WITH)? (aCell.Len()-aQuer.Len()):0;
1297 xub_StrLen nStrPos = aCell.Search( aQuer, nIndex );
1298 switch (rEntry.eOp)
1300 case SC_EQUAL:
1301 case SC_CONTAINS:
1302 bOk = ( nStrPos != STRING_NOTFOUND );
1303 break;
1304 case SC_NOT_EQUAL:
1305 case SC_DOES_NOT_CONTAIN:
1306 bOk = ( nStrPos == STRING_NOTFOUND );
1307 break;
1308 case SC_BEGINS_WITH:
1309 bOk = ( nStrPos == 0 );
1310 break;
1311 case SC_DOES_NOT_BEGIN_WITH:
1312 bOk = ( nStrPos != 0 );
1313 break;
1314 case SC_ENDS_WITH:
1315 bOk = ( nStrPos + aQuer.Len() == aCell.Len() );
1316 break;
1317 case SC_DOES_NOT_END_WITH:
1318 bOk = ( nStrPos + aQuer.Len() != aCell.Len() );
1319 break;
1320 default:
1322 // added to avoid warnings
1327 else
1328 { // use collator here because data was probably sorted
1329 sal_Int32 nCompare = pCollator->compareString(
1330 aCellStr, *rEntry.pStr );
1331 switch (rEntry.eOp)
1333 case SC_LESS :
1334 bOk = (nCompare < 0);
1335 break;
1336 case SC_GREATER :
1337 bOk = (nCompare > 0);
1338 break;
1339 case SC_LESS_EQUAL :
1340 bOk = (nCompare <= 0);
1341 if ( bOk && pbTestEqualCondition && !bTestEqual )
1342 bTestEqual = (nCompare == 0);
1343 break;
1344 case SC_GREATER_EQUAL :
1345 bOk = (nCompare >= 0);
1346 if ( bOk && pbTestEqualCondition && !bTestEqual )
1347 bTestEqual = (nCompare == 0);
1348 break;
1349 default:
1351 // added to avoid warnings
1357 else if (rParam.bMixedComparison)
1359 if (rEntry.bQueryByString &&
1360 (rEntry.eOp == SC_LESS || rEntry.eOp == SC_LESS_EQUAL) &&
1361 (pCell ? pCell->HasValueData() :
1362 HasValueData( static_cast<SCCOL>(rEntry.nField), nRow)))
1364 bOk = TRUE;
1366 else if (!rEntry.bQueryByString &&
1367 (rEntry.eOp == SC_GREATER || rEntry.eOp == SC_GREATER_EQUAL) &&
1368 (pCell ? pCell->HasStringData() :
1369 HasStringData( static_cast<SCCOL>(rEntry.nField), nRow)))
1371 bOk = TRUE;
1375 if (nPos == -1)
1377 nPos++;
1378 pPasst[nPos] = bOk;
1379 pTest[nPos] = bTestEqual;
1381 else
1383 if (rEntry.eConnect == SC_AND)
1385 pPasst[nPos] = pPasst[nPos] && bOk;
1386 pTest[nPos] = pTest[nPos] && bTestEqual;
1388 else
1390 nPos++;
1391 pPasst[nPos] = bOk;
1392 pTest[nPos] = bTestEqual;
1395 i++;
1398 for ( long j=1; j <= nPos; j++ )
1400 pPasst[0] = pPasst[0] || pPasst[j];
1401 pTest[0] = pTest[0] || pTest[j];
1404 BOOL bRet = pPasst[0];
1405 if ( pPasst != &aBool[0] )
1406 delete [] pPasst;
1407 if ( pbTestEqualCondition )
1408 *pbTestEqualCondition = pTest[0];
1409 if ( pTest != &aTest[0] )
1410 delete [] pTest;
1412 return bRet;
1415 void ScTable::TopTenQuery( ScQueryParam& rParam )
1417 BOOL bSortCollatorInitialized = FALSE;
1418 SCSIZE nEntryCount = rParam.GetEntryCount();
1419 SCROW nRow1 = (rParam.bHasHeader ? rParam.nRow1 + 1 : rParam.nRow1);
1420 SCSIZE nCount = static_cast<SCSIZE>(rParam.nRow2 - nRow1 + 1);
1421 for ( SCSIZE i=0; (i<nEntryCount) && (rParam.GetEntry(i).bDoQuery); i++ )
1423 ScQueryEntry& rEntry = rParam.GetEntry(i);
1424 switch ( rEntry.eOp )
1426 case SC_TOPVAL:
1427 case SC_BOTVAL:
1428 case SC_TOPPERC:
1429 case SC_BOTPERC:
1431 ScSortParam aLocalSortParam( rParam, static_cast<SCCOL>(rEntry.nField) );
1432 aSortParam = aLocalSortParam; // used in CreateSortInfoArray, Compare
1433 if ( !bSortCollatorInitialized )
1435 bSortCollatorInitialized = TRUE;
1436 InitSortCollator( aLocalSortParam );
1438 ScSortInfoArray* pArray = CreateSortInfoArray( nRow1, rParam.nRow2 );
1439 DecoladeRow( pArray, nRow1, rParam.nRow2 );
1440 QuickSort( pArray, nRow1, rParam.nRow2 );
1441 ScSortInfo** ppInfo = pArray->GetFirstArray();
1442 SCSIZE nValidCount = nCount;
1443 // keine Note-/Leerzellen zaehlen, sind ans Ende sortiert
1444 while ( nValidCount > 0 && ( ppInfo[nValidCount-1]->pCell == NULL ||
1445 ppInfo[nValidCount-1]->pCell->GetCellType() == CELLTYPE_NOTE ) )
1446 nValidCount--;
1447 // keine Strings zaehlen, sind zwischen Value und Leer
1448 while ( nValidCount > 0
1449 && ppInfo[nValidCount-1]->pCell->HasStringData() )
1450 nValidCount--;
1451 if ( nValidCount > 0 )
1453 if ( rEntry.bQueryByString )
1454 { // dat wird nix
1455 rEntry.bQueryByString = FALSE;
1456 rEntry.nVal = 10; // 10 bzw. 10%
1458 SCSIZE nVal = (rEntry.nVal >= 1 ? static_cast<SCSIZE>(rEntry.nVal) : 1);
1459 SCSIZE nOffset = 0;
1460 switch ( rEntry.eOp )
1462 case SC_TOPVAL:
1464 rEntry.eOp = SC_GREATER_EQUAL;
1465 if ( nVal > nValidCount )
1466 nVal = nValidCount;
1467 nOffset = nValidCount - nVal; // 1 <= nVal <= nValidCount
1469 break;
1470 case SC_BOTVAL:
1472 rEntry.eOp = SC_LESS_EQUAL;
1473 if ( nVal > nValidCount )
1474 nVal = nValidCount;
1475 nOffset = nVal - 1; // 1 <= nVal <= nValidCount
1477 break;
1478 case SC_TOPPERC:
1480 rEntry.eOp = SC_GREATER_EQUAL;
1481 if ( nVal > 100 )
1482 nVal = 100;
1483 nOffset = nValidCount - (nValidCount * nVal / 100);
1484 if ( nOffset >= nValidCount )
1485 nOffset = nValidCount - 1;
1487 break;
1488 case SC_BOTPERC:
1490 rEntry.eOp = SC_LESS_EQUAL;
1491 if ( nVal > 100 )
1492 nVal = 100;
1493 nOffset = (nValidCount * nVal / 100);
1494 if ( nOffset >= nValidCount )
1495 nOffset = nValidCount - 1;
1497 break;
1498 default:
1500 // added to avoid warnings
1503 ScBaseCell* pCell = ppInfo[nOffset]->pCell;
1504 if ( pCell->HasValueData() )
1506 if ( pCell->GetCellType() == CELLTYPE_VALUE )
1507 rEntry.nVal = ((ScValueCell*)pCell)->GetValue();
1508 else
1509 rEntry.nVal = ((ScFormulaCell*)pCell)->GetValue();
1511 else
1513 DBG_ERRORFILE( "TopTenQuery: pCell kein ValueData" );
1514 rEntry.eOp = SC_GREATER_EQUAL;
1515 rEntry.nVal = 0;
1518 else
1520 rEntry.eOp = SC_GREATER_EQUAL;
1521 rEntry.bQueryByString = FALSE;
1522 rEntry.nVal = 0;
1524 delete pArray;
1526 break;
1527 default:
1529 // added to avoid warnings
1533 if ( bSortCollatorInitialized )
1534 DestroySortCollator();
1537 static void lcl_PrepareQuery( ScDocument* pDoc, ScTable* pTab, ScQueryParam& rParam, BOOL* pSpecial )
1539 bool bTopTen = false;
1540 SCSIZE nEntryCount = rParam.GetEntryCount();
1542 for ( SCSIZE i = 0; i < nEntryCount; ++i )
1544 pSpecial[i] = FALSE;
1545 ScQueryEntry& rEntry = rParam.GetEntry(i);
1546 if ( rEntry.bDoQuery )
1548 if ( rEntry.bQueryByString )
1550 sal_uInt32 nIndex = 0;
1551 rEntry.bQueryByString = !( pDoc->GetFormatTable()->
1552 IsNumberFormat( *rEntry.pStr, nIndex, rEntry.nVal ) );
1554 else
1556 // #58736# call from UNO or second call from autofilter
1557 if ( rEntry.nVal == SC_EMPTYFIELDS || rEntry.nVal == SC_NONEMPTYFIELDS )
1559 pSpecial[i] = TRUE;
1562 if ( !bTopTen )
1564 switch ( rEntry.eOp )
1566 case SC_TOPVAL:
1567 case SC_BOTVAL:
1568 case SC_TOPPERC:
1569 case SC_BOTPERC:
1571 bTopTen = true;
1573 break;
1574 default:
1582 if ( bTopTen )
1584 pTab->TopTenQuery( rParam );
1588 SCSIZE ScTable::Query(ScQueryParam& rParamOrg, BOOL bKeepSub)
1590 ScQueryParam aParam( rParamOrg );
1591 ScStrCollection aScStrCollection;
1592 StrData* pStrData = NULL;
1594 BOOL bStarted = FALSE;
1595 BOOL bOldResult = TRUE;
1596 SCROW nOldStart = 0;
1597 SCROW nOldEnd = 0;
1599 SCSIZE nCount = 0;
1600 SCROW nOutRow = 0;
1601 SCROW nHeader = aParam.bHasHeader ? 1 : 0;
1603 SCSIZE nEntryCount = aParam.GetEntryCount();
1604 BOOL* pSpecial = new BOOL[nEntryCount];
1605 lcl_PrepareQuery( pDocument, this, aParam, pSpecial );
1607 SCROW nEndRow = aParam.bUseDynamicRange ? aParam.nDynamicEndRow : aParam.nRow2;
1608 if (!aParam.bInplace)
1610 nOutRow = aParam.nDestRow + nHeader;
1611 if (nHeader > 0)
1612 CopyData( aParam.nCol1, aParam.nRow1, aParam.nCol2, aParam.nRow1,
1613 aParam.nDestCol, aParam.nDestRow, aParam.nDestTab );
1616 for (SCROW j=aParam.nRow1 + nHeader; j<=nEndRow; j++)
1618 BOOL bResult; // Filterergebnis
1619 BOOL bValid = ValidQuery(j, aParam, pSpecial);
1620 if (!bValid && bKeepSub) // Subtotals stehenlassen
1622 for (SCCOL nCol=aParam.nCol1; nCol<=aParam.nCol2 && !bValid; nCol++)
1624 ScBaseCell* pCell;
1625 pCell = GetCell( nCol, j );
1626 if ( pCell )
1627 if ( pCell->GetCellType() == CELLTYPE_FORMULA )
1628 if (((ScFormulaCell*)pCell)->IsSubTotal())
1629 if (RefVisible((ScFormulaCell*)pCell))
1630 bValid = TRUE;
1633 if (bValid)
1635 if (aParam.bDuplicate)
1636 bResult = TRUE;
1637 else
1639 String aStr;
1640 for (SCCOL k=aParam.nCol1; k <= aParam.nCol2; k++)
1642 String aCellStr;
1643 GetString(k, j, aCellStr);
1644 aStr += aCellStr;
1645 aStr += (sal_Unicode)1;
1647 pStrData = new StrData(aStr);
1649 BOOL bIsUnique = TRUE;
1650 if (pStrData)
1651 bIsUnique = aScStrCollection.Insert(pStrData);
1652 if (bIsUnique)
1653 bResult = TRUE;
1654 else
1656 delete pStrData;
1657 bResult = FALSE;
1661 else
1662 bResult = FALSE;
1664 if (aParam.bInplace)
1666 if (bResult == bOldResult && bStarted)
1667 nOldEnd = j;
1668 else
1670 if (bStarted)
1671 DBShowRows(nOldStart,nOldEnd, bOldResult);
1672 nOldStart = nOldEnd = j;
1673 bOldResult = bResult;
1675 bStarted = TRUE;
1677 else
1679 if (bResult)
1681 CopyData( aParam.nCol1,j, aParam.nCol2,j, aParam.nDestCol,nOutRow,aParam.nDestTab );
1682 ++nOutRow;
1685 if (bResult)
1686 ++nCount;
1689 if (aParam.bInplace && bStarted)
1690 DBShowRows(nOldStart,nOldEnd, bOldResult);
1692 delete[] pSpecial;
1694 return nCount;
1697 BOOL ScTable::CreateExcelQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
1699 BOOL bValid = TRUE;
1700 SCCOL* pFields = new SCCOL[nCol2-nCol1+1];
1701 String aCellStr;
1702 SCCOL nCol = nCol1;
1703 DBG_ASSERT( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
1704 SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
1705 SCROW nDBRow1 = rQueryParam.nRow1;
1706 SCCOL nDBCol2 = rQueryParam.nCol2;
1707 // Erste Zeile muessen Spaltenkoepfe sein
1708 while (bValid && (nCol <= nCol2))
1710 String aQueryStr;
1711 GetUpperCellString(nCol, nRow1, aQueryStr);
1712 BOOL bFound = FALSE;
1713 SCCOL i = rQueryParam.nCol1;
1714 while (!bFound && (i <= nDBCol2))
1716 if ( nTab == nDBTab )
1717 GetUpperCellString(i, nDBRow1, aCellStr);
1718 else
1719 pDocument->GetUpperCellString(i, nDBRow1, nDBTab, aCellStr);
1720 bFound = (aCellStr == aQueryStr);
1721 if (!bFound) i++;
1723 if (bFound)
1724 pFields[nCol - nCol1] = i;
1725 else
1726 bValid = FALSE;
1727 nCol++;
1729 if (bValid)
1731 ULONG nVisible = 0;
1732 for ( nCol=nCol1; nCol<=nCol2; nCol++ )
1733 nVisible += aCol[nCol].VisibleCount( nRow1+1, nRow2 );
1735 if ( nVisible > SCSIZE_MAX / sizeof(void*) )
1737 DBG_ERROR("zu viele Filterkritierien");
1738 nVisible = 0;
1741 SCSIZE nNewEntries = nVisible;
1742 rQueryParam.Resize( nNewEntries );
1744 SCSIZE nIndex = 0;
1745 SCROW nRow = nRow1 + 1;
1746 while (nRow <= nRow2)
1748 nCol = nCol1;
1749 while (nCol <= nCol2)
1751 GetInputString( nCol, nRow, aCellStr );
1752 ScGlobal::pCharClass->toUpper( aCellStr );
1753 if (aCellStr.Len() > 0)
1755 if (nIndex < nNewEntries)
1757 rQueryParam.GetEntry(nIndex).nField = pFields[nCol - nCol1];
1758 rQueryParam.FillInExcelSyntax(aCellStr, nIndex);
1759 nIndex++;
1760 if (nIndex < nNewEntries)
1761 rQueryParam.GetEntry(nIndex).eConnect = SC_AND;
1763 else
1764 bValid = FALSE;
1766 nCol++;
1768 nRow++;
1769 if (nIndex < nNewEntries)
1770 rQueryParam.GetEntry(nIndex).eConnect = SC_OR;
1773 delete [] pFields;
1774 return bValid;
1777 BOOL ScTable::CreateStarQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
1779 // A valid StarQuery must be at least 4 columns wide. To be precise it
1780 // should be exactly 4 columns ...
1781 // Additionally, if this wasn't checked, a formula pointing to a valid 1-3
1782 // column Excel style query range immediately left to itself would result
1783 // in a circular reference when the field name or operator or value (first
1784 // to third query range column) is obtained (#i58354#). Furthermore, if the
1785 // range wasn't sufficiently specified data changes wouldn't flag formula
1786 // cells for recalculation.
1787 if (nCol2 - nCol1 < 3)
1788 return FALSE;
1790 BOOL bValid;
1791 BOOL bFound;
1792 String aCellStr;
1793 SCSIZE nIndex = 0;
1794 SCROW nRow = nRow1;
1795 DBG_ASSERT( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
1796 SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
1797 SCROW nDBRow1 = rQueryParam.nRow1;
1798 SCCOL nDBCol2 = rQueryParam.nCol2;
1800 SCSIZE nNewEntries = static_cast<SCSIZE>(nRow2-nRow1+1);
1801 rQueryParam.Resize( nNewEntries );
1805 ScQueryEntry& rEntry = rQueryParam.GetEntry(nIndex);
1807 bValid = FALSE;
1808 // Erste Spalte UND/ODER
1809 if (nIndex > 0)
1811 GetUpperCellString(nCol1, nRow, aCellStr);
1812 if ( aCellStr == ScGlobal::GetRscString(STR_TABLE_UND) )
1814 rEntry.eConnect = SC_AND;
1815 bValid = TRUE;
1817 else if ( aCellStr == ScGlobal::GetRscString(STR_TABLE_ODER) )
1819 rEntry.eConnect = SC_OR;
1820 bValid = TRUE;
1823 // Zweite Spalte FeldName
1824 if ((nIndex < 1) || bValid)
1826 bFound = FALSE;
1827 GetUpperCellString(nCol1 + 1, nRow, aCellStr);
1828 for (SCCOL i=rQueryParam.nCol1; (i <= nDBCol2) && (!bFound); i++)
1830 String aFieldStr;
1831 if ( nTab == nDBTab )
1832 GetUpperCellString(i, nDBRow1, aFieldStr);
1833 else
1834 pDocument->GetUpperCellString(i, nDBRow1, nDBTab, aFieldStr);
1835 bFound = (aCellStr == aFieldStr);
1836 if (bFound)
1838 rEntry.nField = i;
1839 bValid = TRUE;
1841 else
1842 bValid = FALSE;
1845 // Dritte Spalte Operator =<>...
1846 if (bValid)
1848 bFound = FALSE;
1849 GetUpperCellString(nCol1 + 2, nRow, aCellStr);
1850 if (aCellStr.GetChar(0) == '<')
1852 if (aCellStr.GetChar(1) == '>')
1853 rEntry.eOp = SC_NOT_EQUAL;
1854 else if (aCellStr.GetChar(1) == '=')
1855 rEntry.eOp = SC_LESS_EQUAL;
1856 else
1857 rEntry.eOp = SC_LESS;
1859 else if (aCellStr.GetChar(0) == '>')
1861 if (aCellStr.GetChar(1) == '=')
1862 rEntry.eOp = SC_GREATER_EQUAL;
1863 else
1864 rEntry.eOp = SC_GREATER;
1866 else if (aCellStr.GetChar(0) == '=')
1867 rEntry.eOp = SC_EQUAL;
1870 // Vierte Spalte Wert
1871 if (bValid)
1873 GetString(nCol1 + 3, nRow, *rEntry.pStr);
1874 rEntry.bDoQuery = TRUE;
1876 nIndex++;
1877 nRow++;
1879 while (bValid && (nRow <= nRow2) /* && (nIndex < MAXQUERY) */ );
1880 return bValid;
1883 BOOL ScTable::CreateQueryParam(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
1885 SCSIZE i, nCount;
1886 PutInOrder(nCol1, nCol2);
1887 PutInOrder(nRow1, nRow2);
1889 nCount = rQueryParam.GetEntryCount();
1890 for (i=0; i < nCount; i++)
1891 rQueryParam.GetEntry(i).Clear();
1893 // Standard QueryTabelle
1894 BOOL bValid = CreateStarQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
1895 // Excel QueryTabelle
1896 if (!bValid)
1897 bValid = CreateExcelQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
1899 nCount = rQueryParam.GetEntryCount();
1900 if (bValid)
1902 // bQueryByString muss gesetzt sein
1903 for (i=0; i < nCount; i++)
1904 rQueryParam.GetEntry(i).bQueryByString = TRUE;
1906 else
1908 // nix
1909 for (i=0; i < nCount; i++)
1910 rQueryParam.GetEntry(i).Clear();
1912 return bValid;
1915 BOOL ScTable::HasColHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW /* nEndRow */ )
1917 for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
1919 CellType eType = GetCellType( nCol, nStartRow );
1920 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
1921 return FALSE;
1923 return TRUE;
1926 BOOL ScTable::HasRowHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL /* nEndCol */, SCROW nEndRow )
1928 for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++)
1930 CellType eType = GetCellType( nStartCol, nRow );
1931 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
1932 return FALSE;
1934 return TRUE;
1937 void ScTable::GetFilterEntries(SCCOL nCol, SCROW nRow1, SCROW nRow2, TypedScStrCollection& rStrings, bool& rHasDates)
1939 aCol[nCol].GetFilterEntries( nRow1, nRow2, rStrings, rHasDates );
1942 void ScTable::GetFilteredFilterEntries(
1943 SCCOL nCol, SCROW nRow1, SCROW nRow2, const ScQueryParam& rParam, TypedScStrCollection& rStrings, bool& rHasDates )
1945 // remove the entry for this column from the query parameter
1946 ScQueryParam aParam( rParam );
1947 SCSIZE nEntryCount = aParam.GetEntryCount();
1948 for ( SCSIZE i = 0; i < nEntryCount && aParam.GetEntry(i).bDoQuery; ++i )
1950 ScQueryEntry& rEntry = aParam.GetEntry(i);
1951 if ( rEntry.nField == nCol )
1953 aParam.DeleteQuery(i);
1954 break;
1957 nEntryCount = aParam.GetEntryCount();
1959 BOOL* pSpecial = new BOOL[nEntryCount];
1960 lcl_PrepareQuery( pDocument, this, aParam, pSpecial );
1961 bool bHasDates = false;
1962 for ( SCROW j = nRow1; j <= nRow2; ++j )
1964 if ( ValidQuery( j, aParam, pSpecial ) )
1966 bool bThisHasDates = false;
1967 aCol[nCol].GetFilterEntries( j, j, rStrings, bThisHasDates );
1968 bHasDates |= bThisHasDates;
1972 rHasDates = bHasDates;
1973 delete[] pSpecial;
1976 BOOL ScTable::GetDataEntries(SCCOL nCol, SCROW nRow, TypedScStrCollection& rStrings, BOOL bLimit)
1978 return aCol[nCol].GetDataEntries( nRow, rStrings, bLimit );
1981 ULONG ScTable::GetCellCount() const
1983 ULONG nCellCount = 0;
1985 for ( SCCOL nCol=0; nCol<=MAXCOL; nCol++ )
1986 nCellCount += aCol[nCol].GetCellCount();
1988 return nCellCount;
1991 ULONG ScTable::GetWeightedCount() const
1993 ULONG nCellCount = 0;
1995 for ( SCCOL nCol=0; nCol<=MAXCOL; nCol++ )
1996 if ( aCol[nCol].GetCellCount() ) // GetCellCount ist inline
1997 nCellCount += aCol[nCol].GetWeightedCount();
1999 return nCellCount;
2002 ULONG ScTable::GetCodeCount() const
2004 ULONG nCodeCount = 0;
2006 for ( SCCOL nCol=0; nCol<=MAXCOL; nCol++ )
2007 if ( aCol[nCol].GetCellCount() ) // GetCellCount ist inline
2008 nCodeCount += aCol[nCol].GetCodeCount();
2010 return nCodeCount;
2013 sal_Int32 ScTable::GetMaxStringLen( SCCOL nCol, SCROW nRowStart,
2014 SCROW nRowEnd, CharSet eCharSet ) const
2016 if ( ValidCol(nCol) )
2017 return aCol[nCol].GetMaxStringLen( nRowStart, nRowEnd, eCharSet );
2018 else
2019 return 0;
2022 xub_StrLen ScTable::GetMaxNumberStringLen( USHORT& nPrecision, SCCOL nCol,
2023 SCROW nRowStart, SCROW nRowEnd ) const
2025 if ( ValidCol(nCol) )
2026 return aCol[nCol].GetMaxNumberStringLen( nPrecision, nRowStart, nRowEnd );
2027 else
2028 return 0;
2031 void ScTable::UpdateSelectionFunction( ScFunctionData& rData,
2032 SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
2033 const ScMarkData& rMark )
2035 // Cursor neben einer Markierung nicht beruecksichtigen:
2036 //! nur noch MarkData uebergeben, Cursorposition ggf. hineinselektieren!!!
2037 BOOL bSingle = ( rMark.IsMarked() || !rMark.IsMultiMarked() );
2039 // Mehrfachselektion:
2041 SCCOL nCol;
2042 if ( rMark.IsMultiMarked() )
2043 for (nCol=0; nCol<=MAXCOL && !rData.bError; nCol++)
2044 if ( !pColFlags || !ColHidden(nCol) )
2045 aCol[nCol].UpdateSelectionFunction( rMark, rData, *mpHiddenRows,
2046 bSingle && ( nCol >= nStartCol && nCol <= nEndCol ),
2047 nStartRow, nEndRow );
2049 // Einfachselektion (oder Cursor) nur wenn nicht negativ (und s.o.):
2051 if ( bSingle && !rMark.IsMarkNegative() )
2052 for (nCol=nStartCol; nCol<=nEndCol && !rData.bError; nCol++)
2053 if ( !pColFlags || !ColHidden(nCol) )
2054 aCol[nCol].UpdateAreaFunction( rData, *mpHiddenRows, nStartRow, nEndRow );
2057 void ScTable::FindConditionalFormat( ULONG nKey, ScRangeList& rList )
2059 SCROW nStartRow = 0, nEndRow = 0;
2060 for (SCCOL nCol=0; nCol<=MAXCOL; nCol++)
2062 ScAttrIterator* pIter = aCol[nCol].CreateAttrIterator( 0, MAXROW );
2063 const ScPatternAttr* pPattern = pIter->Next( nStartRow, nEndRow );
2064 while (pPattern)
2066 if (((SfxUInt32Item&)pPattern->GetItem(ATTR_CONDITIONAL)).GetValue() == nKey)
2067 rList.Join( ScRange(nCol,nStartRow,nTab, nCol,nEndRow,nTab) );
2068 pPattern = pIter->Next( nStartRow, nEndRow );
2070 delete pIter;