update dev300-m57
[ooovba.git] / sc / source / core / data / table3.cxx
blob5c394595675b72301e40b86bd09e98020a5cdfcd
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"
64 #include <vector>
66 using namespace ::com::sun::star;
68 namespace naturalsort {
70 using namespace ::com::sun::star::i18n;
72 /** Splits a given string into three parts: the prefix, number string, and
73 the suffix.
75 @param sWhole
76 Original string to be split into pieces
78 @param sPrefix
79 Prefix string that consists of the part before the first number token
81 @param sSuffix
82 String after the last number token. This may still contain number strings.
84 @param fNum
85 Number converted from the middle number string
87 @return Returns TRUE if a numeral element is found in a given string, or
88 FALSE if no numeral element is found.
90 BOOL SplitString( const rtl::OUString &sWhole,
91 rtl::OUString &sPrefix, rtl::OUString &sSuffix, double &fNum )
93 i18n::LocaleDataItem aLocaleItem = ScGlobal::pLocaleData->getLocaleItem();
95 // Get prefix element
96 rtl::OUString sEmpty, sUser = rtl::OUString::createFromAscii( "-" );
97 ParseResult aPRPre = ScGlobal::pCharClass->parsePredefinedToken(
98 KParseType::IDENTNAME, sWhole, 0,
99 KParseTokens::ANY_LETTER, sUser, KParseTokens::ANY_LETTER, sUser );
100 sPrefix = sWhole.copy( 0, aPRPre.EndPos );
102 // Return FALSE if no numeral element is found
103 if ( aPRPre.EndPos == sWhole.getLength() )
104 return FALSE;
106 // Get numeral element
107 sUser = aLocaleItem.decimalSeparator;
108 ParseResult aPRNum = ScGlobal::pCharClass->parsePredefinedToken(
109 KParseType::ANY_NUMBER, sWhole, aPRPre.EndPos,
110 KParseTokens::ANY_NUMBER, sEmpty, KParseTokens::ANY_NUMBER, sUser );
112 if ( aPRNum.EndPos == aPRPre.EndPos )
113 return FALSE;
115 fNum = aPRNum.Value;
116 sSuffix = sWhole.copy( aPRNum.EndPos );
118 return TRUE;
121 /** Naturally compares two given strings.
123 This is the main function that should be called externally. It returns
124 either 1, 0, or -1 depending on the comparison result of given two strings.
126 @param sInput1
127 Input string 1
129 @param sInput2
130 Input string 2
132 @param bCaseSens
133 Boolean value for case sensitivity
135 @param pData
136 Pointer to user defined sort list
138 @param pCW
139 Pointer to collator wrapper for normal string comparison
141 @return Returnes 1 if sInput1 is greater, 0 if sInput1 == sInput2, and -1 if
142 sInput2 is greater.
144 short Compare( const String &sInput1, const String &sInput2,
145 const BOOL bCaseSens, const ScUserListData* pData, const CollatorWrapper *pCW )
147 rtl::OUString sStr1( sInput1 ), sStr2( sInput2 ), sPre1, sSuf1, sPre2, sSuf2;
151 double nNum1, nNum2;
152 BOOL bNumFound1 = SplitString( sStr1, sPre1, sSuf1, nNum1 );
153 BOOL bNumFound2 = SplitString( sStr2, sPre2, sSuf2, nNum2 );
155 short nPreRes; // Prefix comparison result
156 if ( pData )
158 if ( bCaseSens )
160 if ( !bNumFound1 || !bNumFound2 )
161 return static_cast<short>(pData->Compare( sStr1, sStr2 ));
162 else
163 nPreRes = pData->Compare( sPre1, sPre2 );
165 else
167 if ( !bNumFound1 || !bNumFound2 )
168 return static_cast<short>(pData->ICompare( sStr1, sStr2 ));
169 else
170 nPreRes = pData->ICompare( sPre1, sPre2 );
173 else
175 if ( !bNumFound1 || !bNumFound2 )
176 return static_cast<short>(pCW->compareString( sStr1, sStr2 ));
177 else
178 nPreRes = static_cast<short>(pCW->compareString( sPre1, sPre2 ));
181 // Prefix strings differ. Return immediately.
182 if ( nPreRes != 0 ) return nPreRes;
184 if ( nNum1 != nNum2 )
186 if ( nNum1 < nNum2 ) return -1;
187 return static_cast<short>( nNum1 > nNum2 );
190 // The prefix and the first numerical elements are equal, but the suffix
191 // strings may still differ. Stay in the loop.
193 sStr1 = sSuf1;
194 sStr2 = sSuf2;
196 } while ( true );
198 return 0;
203 // STATIC DATA -----------------------------------------------------------
205 const USHORT nMaxSorts = 3; // maximale Anzahl Sortierkriterien in aSortParam
207 struct ScSortInfo
209 ScBaseCell* pCell;
210 SCCOLROW nOrg;
211 DECL_FIXEDMEMPOOL_NEWDEL( ScSortInfo );
213 const USHORT nMemPoolSortInfo = (0x8000 - 64) / sizeof(ScSortInfo);
214 IMPL_FIXEDMEMPOOL_NEWDEL( ScSortInfo, nMemPoolSortInfo, nMemPoolSortInfo )
216 // END OF STATIC DATA -----------------------------------------------------
219 class ScSortInfoArray
221 private:
222 ScSortInfo** pppInfo[nMaxSorts];
223 SCSIZE nCount;
224 SCCOLROW nStart;
225 USHORT nUsedSorts;
227 public:
228 ScSortInfoArray( USHORT nSorts, SCCOLROW nInd1, SCCOLROW nInd2 ) :
229 nCount( nInd2 - nInd1 + 1 ), nStart( nInd1 ),
230 nUsedSorts( Min( nSorts, nMaxSorts ) )
232 for ( USHORT nSort = 0; nSort < nUsedSorts; nSort++ )
234 ScSortInfo** ppInfo = new ScSortInfo* [nCount];
235 for ( SCSIZE j = 0; j < nCount; j++ )
236 ppInfo[j] = new ScSortInfo;
237 pppInfo[nSort] = ppInfo;
240 ~ScSortInfoArray()
242 for ( USHORT nSort = 0; nSort < nUsedSorts; nSort++ )
244 ScSortInfo** ppInfo = pppInfo[nSort];
245 for ( SCSIZE j = 0; j < nCount; j++ )
246 delete ppInfo[j];
247 delete [] ppInfo;
250 ScSortInfo* Get( USHORT nSort, SCCOLROW nInd )
251 { return (pppInfo[nSort])[ nInd - nStart ]; }
252 void Swap( SCCOLROW nInd1, SCCOLROW nInd2 )
254 SCSIZE n1 = static_cast<SCSIZE>(nInd1 - nStart);
255 SCSIZE n2 = static_cast<SCSIZE>(nInd2 - nStart);
256 for ( USHORT nSort = 0; nSort < nUsedSorts; nSort++ )
258 ScSortInfo** ppInfo = pppInfo[nSort];
259 ScSortInfo* pTmp = ppInfo[n1];
260 ppInfo[n1] = ppInfo[n2];
261 ppInfo[n2] = pTmp;
264 USHORT GetUsedSorts() { return nUsedSorts; }
265 ScSortInfo** GetFirstArray() { return pppInfo[0]; }
266 SCCOLROW GetStart() { return nStart; }
267 SCSIZE GetCount() { return nCount; }
270 ScSortInfoArray* ScTable::CreateSortInfoArray( SCCOLROW nInd1, SCCOLROW nInd2 )
272 USHORT nUsedSorts = 1;
273 while ( nUsedSorts < nMaxSorts && aSortParam.bDoSort[nUsedSorts] )
274 nUsedSorts++;
275 ScSortInfoArray* pArray = new ScSortInfoArray( nUsedSorts, nInd1, nInd2 );
276 if ( aSortParam.bByRow )
278 for ( USHORT nSort = 0; nSort < nUsedSorts; nSort++ )
280 SCCOL nCol = static_cast<SCCOL>(aSortParam.nField[nSort]);
281 ScColumn* pCol = &aCol[nCol];
282 for ( SCROW nRow = nInd1; nRow <= nInd2; nRow++ )
284 //2do: FillSortInfo an ScColumn und Array abklappern statt Search in GetCell
285 ScSortInfo* pInfo = pArray->Get( nSort, nRow );
286 pInfo->pCell = pCol->GetCell( nRow );
287 pInfo->nOrg = nRow;
291 else
293 for ( USHORT nSort = 0; nSort < nUsedSorts; nSort++ )
295 SCROW nRow = aSortParam.nField[nSort];
296 for ( SCCOL nCol = static_cast<SCCOL>(nInd1);
297 nCol <= static_cast<SCCOL>(nInd2); nCol++ )
299 ScSortInfo* pInfo = pArray->Get( nSort, nCol );
300 pInfo->pCell = GetCell( nCol, nRow );
301 pInfo->nOrg = nCol;
305 return pArray;
309 BOOL ScTable::IsSortCollatorGlobal() const
311 return pSortCollator == ScGlobal::pCollator ||
312 pSortCollator == ScGlobal::pCaseCollator;
316 void ScTable::InitSortCollator( const ScSortParam& rPar )
318 if ( rPar.aCollatorLocale.Language.getLength() )
320 if ( !pSortCollator || IsSortCollatorGlobal() )
321 pSortCollator = new CollatorWrapper( pDocument->GetServiceManager() );
322 pSortCollator->loadCollatorAlgorithm( rPar.aCollatorAlgorithm,
323 rPar.aCollatorLocale, (rPar.bCaseSens ? 0 : SC_COLLATOR_IGNORES) );
325 else
326 { // SYSTEM
327 DestroySortCollator();
328 pSortCollator = (rPar.bCaseSens ? ScGlobal::pCaseCollator :
329 ScGlobal::pCollator);
334 void ScTable::DestroySortCollator()
336 if ( pSortCollator )
338 if ( !IsSortCollatorGlobal() )
339 delete pSortCollator;
340 pSortCollator = NULL;
345 void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress& rProgress )
347 BOOL bByRow = aSortParam.bByRow;
348 SCSIZE nCount = pArray->GetCount();
349 SCCOLROW nStart = pArray->GetStart();
350 ScSortInfo** ppInfo = pArray->GetFirstArray();
351 ::std::vector<ScSortInfo*> aTable(nCount);
352 SCSIZE nPos;
353 for ( nPos = 0; nPos < nCount; nPos++ )
354 aTable[ppInfo[nPos]->nOrg - nStart] = ppInfo[nPos];
356 SCCOLROW nDest = nStart;
357 for ( nPos = 0; nPos < nCount; nPos++, nDest++ )
359 SCCOLROW nOrg = ppInfo[nPos]->nOrg;
360 if ( nDest != nOrg )
362 if ( bByRow )
363 SwapRow( nDest, nOrg );
364 else
365 SwapCol( static_cast<SCCOL>(nDest), static_cast<SCCOL>(nOrg) );
366 // neue Position des weggeswapten eintragen
367 ScSortInfo* p = ppInfo[nPos];
368 p->nOrg = nDest;
369 ::std::swap(p, aTable[nDest-nStart]);
370 p->nOrg = nOrg;
371 ::std::swap(p, aTable[nOrg-nStart]);
372 DBG_ASSERT( p == ppInfo[nPos], "SortReorder: nOrg MisMatch" );
374 rProgress.SetStateOnPercent( nPos );
378 short ScTable::CompareCell( USHORT nSort,
379 ScBaseCell* pCell1, SCCOL nCell1Col, SCROW nCell1Row,
380 ScBaseCell* pCell2, SCCOL nCell2Col, SCROW nCell2Row )
382 short nRes = 0;
384 CellType eType1 = CELLTYPE_NONE, eType2 = CELLTYPE_NONE;
385 if (pCell1)
387 eType1 = pCell1->GetCellType();
388 if (eType1 == CELLTYPE_NOTE)
389 pCell1 = NULL;
391 if (pCell2)
393 eType2 = pCell2->GetCellType();
394 if (eType2 == CELLTYPE_NOTE)
395 pCell2 = NULL;
398 if (pCell1)
400 if (pCell2)
402 BOOL bStr1 = ( eType1 != CELLTYPE_VALUE );
403 if ( eType1 == CELLTYPE_FORMULA && ((ScFormulaCell*)pCell1)->IsValue() )
404 bStr1 = FALSE;
405 BOOL bStr2 = ( eType2 != CELLTYPE_VALUE );
406 if ( eType2 == CELLTYPE_FORMULA && ((ScFormulaCell*)pCell2)->IsValue() )
407 bStr2 = FALSE;
409 if ( bStr1 && bStr2 ) // nur Strings untereinander als String vergleichen!
411 String aStr1;
412 String aStr2;
413 if (eType1 == CELLTYPE_STRING)
414 ((ScStringCell*)pCell1)->GetString(aStr1);
415 else
416 GetString(nCell1Col, nCell1Row, aStr1);
417 if (eType2 == CELLTYPE_STRING)
418 ((ScStringCell*)pCell2)->GetString(aStr2);
419 else
420 GetString(nCell2Col, nCell2Row, aStr2);
422 BOOL bUserDef = aSortParam.bUserDef; // custom sort order
423 BOOL bNaturalSort = aSortParam.bNaturalSort; // natural sort
424 BOOL bCaseSens = aSortParam.bCaseSens; // case sensitivity
426 if (bUserDef)
428 ScUserListData* pData =
429 static_cast<ScUserListData*>( (ScGlobal::GetUserList()->At(
430 aSortParam.nUserIndex)) );
432 if (pData)
434 if ( bNaturalSort )
435 nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, pData, pSortCollator );
436 else
438 if ( bCaseSens )
439 nRes = sal::static_int_cast<short>( pData->Compare(aStr1, aStr2) );
440 else
441 nRes = sal::static_int_cast<short>( pData->ICompare(aStr1, aStr2) );
444 else
445 bUserDef = FALSE;
448 if (!bUserDef)
450 if ( bNaturalSort )
451 nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, NULL, pSortCollator );
452 else
453 nRes = static_cast<short>( pSortCollator->compareString( aStr1, aStr2 ) );
456 else if ( bStr1 ) // String <-> Zahl
457 nRes = 1; // Zahl vorne
458 else if ( bStr2 ) // Zahl <-> String
459 nRes = -1; // Zahl vorne
460 else // Zahlen untereinander
462 double nVal1;
463 double nVal2;
464 if (eType1 == CELLTYPE_VALUE)
465 nVal1 = ((ScValueCell*)pCell1)->GetValue();
466 else if (eType1 == CELLTYPE_FORMULA)
467 nVal1 = ((ScFormulaCell*)pCell1)->GetValue();
468 else
469 nVal1 = 0;
470 if (eType2 == CELLTYPE_VALUE)
471 nVal2 = ((ScValueCell*)pCell2)->GetValue();
472 else if (eType2 == CELLTYPE_FORMULA)
473 nVal2 = ((ScFormulaCell*)pCell2)->GetValue();
474 else
475 nVal2 = 0;
476 if (nVal1 < nVal2)
477 nRes = -1;
478 else if (nVal1 > nVal2)
479 nRes = 1;
481 if ( !aSortParam.bAscending[nSort] )
482 nRes = -nRes;
484 else
485 nRes = -1;
487 else
489 if ( pCell2 )
490 nRes = 1;
491 else
492 nRes = 0; // beide leer
494 return nRes;
497 short ScTable::Compare( ScSortInfoArray* pArray, SCCOLROW nIndex1, SCCOLROW nIndex2 )
499 short nRes;
500 USHORT nSort = 0;
503 ScSortInfo* pInfo1 = pArray->Get( nSort, nIndex1 );
504 ScSortInfo* pInfo2 = pArray->Get( nSort, nIndex2 );
505 if ( aSortParam.bByRow )
506 nRes = CompareCell( nSort,
507 pInfo1->pCell, static_cast<SCCOL>(aSortParam.nField[nSort]), pInfo1->nOrg,
508 pInfo2->pCell, static_cast<SCCOL>(aSortParam.nField[nSort]), pInfo2->nOrg );
509 else
510 nRes = CompareCell( nSort,
511 pInfo1->pCell, static_cast<SCCOL>(pInfo1->nOrg), aSortParam.nField[nSort],
512 pInfo2->pCell, static_cast<SCCOL>(pInfo2->nOrg), aSortParam.nField[nSort] );
513 } while ( nRes == 0 && ++nSort < pArray->GetUsedSorts() );
514 if( nRes == 0 )
516 ScSortInfo* pInfo1 = pArray->Get( 0, nIndex1 );
517 ScSortInfo* pInfo2 = pArray->Get( 0, nIndex2 );
518 if( pInfo1->nOrg < pInfo2->nOrg )
519 nRes = -1;
520 else if( pInfo1->nOrg > pInfo2->nOrg )
521 nRes = 1;
523 return nRes;
526 void ScTable::QuickSort( ScSortInfoArray* pArray, SCsCOLROW nLo, SCsCOLROW nHi )
528 if ((nHi - nLo) == 1)
530 if (Compare(pArray, nLo, nHi) > 0)
531 pArray->Swap( nLo, nHi );
533 else
535 SCsCOLROW ni = nLo;
536 SCsCOLROW nj = nHi;
539 while ((ni <= nHi) && (Compare(pArray, ni, nLo)) < 0)
540 ni++;
541 while ((nj >= nLo) && (Compare(pArray, nLo, nj)) < 0)
542 nj--;
543 if (ni <= nj)
545 if (ni != nj)
546 pArray->Swap( ni, nj );
547 ni++;
548 nj--;
550 } while (ni < nj);
551 if ((nj - nLo) < (nHi - ni))
553 if (nLo < nj)
554 QuickSort(pArray, nLo, nj);
555 if (ni < nHi)
556 QuickSort(pArray, ni, nHi);
558 else
560 if (ni < nHi)
561 QuickSort(pArray, ni, nHi);
562 if (nLo < nj)
563 QuickSort(pArray, nLo, nj);
568 void ScTable::SwapCol(SCCOL nCol1, SCCOL nCol2)
570 for (SCROW nRow = aSortParam.nRow1; nRow <= aSortParam.nRow2; nRow++)
572 aCol[nCol1].SwapCell(nRow, aCol[nCol2]);
573 if (aSortParam.bIncludePattern)
575 const ScPatternAttr* pPat1 = GetPattern(nCol1, nRow);
576 const ScPatternAttr* pPat2 = GetPattern(nCol2, nRow);
577 if (pPat1 != pPat2)
579 SetPattern(nCol1, nRow, *pPat2, TRUE);
580 SetPattern(nCol2, nRow, *pPat1, TRUE);
586 void ScTable::SwapRow(SCROW nRow1, SCROW nRow2)
588 for (SCCOL nCol = aSortParam.nCol1; nCol <= aSortParam.nCol2; nCol++)
590 aCol[nCol].SwapRow(nRow1, nRow2);
591 if (aSortParam.bIncludePattern)
593 const ScPatternAttr* pPat1 = GetPattern(nCol, nRow1);
594 const ScPatternAttr* pPat2 = GetPattern(nCol, nRow2);
595 if (pPat1 != pPat2)
597 SetPattern(nCol, nRow1, *pPat2, TRUE);
598 SetPattern(nCol, nRow2, *pPat1, TRUE);
602 if (bGlobalKeepQuery && pRowFlags)
604 BYTE nRow1Flags = pRowFlags->GetValue(nRow1);
605 BYTE nRow2Flags = pRowFlags->GetValue(nRow2);
606 BYTE nFlags1 = nRow1Flags & ( CR_HIDDEN | CR_FILTERED );
607 BYTE nFlags2 = nRow2Flags & ( CR_HIDDEN | CR_FILTERED );
608 pRowFlags->SetValue( nRow1, (nRow1Flags & ~( CR_HIDDEN | CR_FILTERED )) | nFlags2);
609 pRowFlags->SetValue( nRow2, (nRow2Flags & ~( CR_HIDDEN | CR_FILTERED )) | nFlags1);
613 short ScTable::Compare(SCCOLROW nIndex1, SCCOLROW nIndex2)
615 short nRes;
616 USHORT nSort = 0;
617 if (aSortParam.bByRow)
621 SCCOL nCol = static_cast<SCCOL>(aSortParam.nField[nSort]);
622 ScBaseCell* pCell1 = aCol[nCol].GetCell( nIndex1 );
623 ScBaseCell* pCell2 = aCol[nCol].GetCell( nIndex2 );
624 nRes = CompareCell( nSort, pCell1, nCol, nIndex1, pCell2, nCol, nIndex2 );
625 } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.bDoSort[nSort] );
627 else
631 SCROW nRow = aSortParam.nField[nSort];
632 ScBaseCell* pCell1 = aCol[nIndex1].GetCell( nRow );
633 ScBaseCell* pCell2 = aCol[nIndex2].GetCell( nRow );
634 nRes = CompareCell( nSort, pCell1, static_cast<SCCOL>(nIndex1),
635 nRow, pCell2, static_cast<SCCOL>(nIndex2), nRow );
636 } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.bDoSort[nSort] );
638 return nRes;
641 BOOL ScTable::IsSorted( SCCOLROW nStart, SCCOLROW nEnd ) // ueber aSortParam
643 for (SCCOLROW i=nStart; i<nEnd; i++)
645 if (Compare( i, i+1 ) > 0)
646 return FALSE;
648 return TRUE;
651 void ScTable::DecoladeRow( ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2 )
653 SCROW nRow;
654 SCROW nMax = nRow2 - nRow1;
655 for (SCROW i = nRow1; (i + 4) <= nRow2; i += 4)
657 nRow = rand() % nMax;
658 pArray->Swap(i, nRow1 + nRow);
662 void ScTable::Sort(const ScSortParam& rSortParam, BOOL bKeepQuery)
664 aSortParam = rSortParam;
665 InitSortCollator( rSortParam );
666 bGlobalKeepQuery = bKeepQuery;
667 if (rSortParam.bByRow)
669 SCROW nLastRow = 0;
670 for (SCCOL nCol = aSortParam.nCol1; nCol <= aSortParam.nCol2; nCol++)
671 nLastRow = Max(nLastRow, aCol[nCol].GetLastDataPos());
672 nLastRow = Min(nLastRow, aSortParam.nRow2);
673 SCROW nRow1 = (rSortParam.bHasHeader ?
674 aSortParam.nRow1 + 1 : aSortParam.nRow1);
675 if (!IsSorted(nRow1, nLastRow))
677 ScProgress aProgress( pDocument->GetDocumentShell(),
678 ScGlobal::GetRscString(STR_PROGRESS_SORTING), nLastRow - nRow1 );
679 ScSortInfoArray* pArray = CreateSortInfoArray( nRow1, nLastRow );
680 if ( nLastRow - nRow1 > 255 )
681 DecoladeRow( pArray, nRow1, nLastRow );
682 QuickSort( pArray, nRow1, nLastRow );
683 SortReorder( pArray, aProgress );
684 delete pArray;
685 // #158377# #i59745# update position of caption objects of cell notes
686 ScNoteUtil::UpdateCaptionPositions( *pDocument, ScRange( aSortParam.nCol1, nRow1, nTab, aSortParam.nCol2, nLastRow, nTab ) );
689 else
691 SCCOL nLastCol;
692 for (nLastCol = aSortParam.nCol2;
693 (nLastCol > aSortParam.nCol1) && aCol[nLastCol].IsEmptyBlock(aSortParam.nRow1, aSortParam.nRow2); nLastCol--)
696 SCCOL nCol1 = (rSortParam.bHasHeader ?
697 aSortParam.nCol1 + 1 : aSortParam.nCol1);
698 if (!IsSorted(nCol1, nLastCol))
700 ScProgress aProgress( pDocument->GetDocumentShell(),
701 ScGlobal::GetRscString(STR_PROGRESS_SORTING), nLastCol - nCol1 );
702 ScSortInfoArray* pArray = CreateSortInfoArray( nCol1, nLastCol );
703 QuickSort( pArray, nCol1, nLastCol );
704 SortReorder( pArray, aProgress );
705 delete pArray;
706 // #158377# #i59745# update position of caption objects of cell notes
707 ScNoteUtil::UpdateCaptionPositions( *pDocument, ScRange( nCol1, aSortParam.nRow1, nTab, nLastCol, aSortParam.nRow2, nTab ) );
710 DestroySortCollator();
714 // Testen, ob beim Loeschen von Zwischenergebnissen andere Daten mit geloescht werden
715 // (fuer Hinweis-Box)
717 BOOL ScTable::TestRemoveSubTotals( const ScSubTotalParam& rParam )
719 SCCOL nStartCol = rParam.nCol1;
720 SCROW nStartRow = rParam.nRow1 + 1; // Header
721 SCCOL nEndCol = rParam.nCol2;
722 SCROW nEndRow = rParam.nRow2;
724 SCCOL nCol;
725 SCROW nRow;
726 ScBaseCell* pCell;
728 BOOL bWillDelete = FALSE;
729 for ( nCol=nStartCol; nCol<=nEndCol && !bWillDelete; nCol++ )
731 ScColumnIterator aIter( &aCol[nCol],nStartRow,nEndRow );
732 while ( aIter.Next( nRow, pCell ) && !bWillDelete )
734 if ( pCell->GetCellType() == CELLTYPE_FORMULA )
735 if (((ScFormulaCell*)pCell)->IsSubTotal())
737 for (SCCOL nTestCol=0; nTestCol<=MAXCOL; nTestCol++)
738 if (nTestCol<nStartCol || nTestCol>nEndCol)
739 if (aCol[nTestCol].HasDataAt(nRow))
740 bWillDelete = TRUE;
744 return bWillDelete;
747 // alte Ergebnisse loeschen
748 // rParam.nRow2 wird veraendert !
750 void ScTable::RemoveSubTotals( ScSubTotalParam& rParam )
752 SCCOL nStartCol = rParam.nCol1;
753 SCROW nStartRow = rParam.nRow1 + 1; // Header
754 SCCOL nEndCol = rParam.nCol2;
755 SCROW nEndRow = rParam.nRow2; // wird veraendert
757 SCCOL nCol;
758 SCROW nRow;
759 ScBaseCell* pCell;
761 for ( nCol=nStartCol; nCol<=nEndCol; nCol++ )
763 ScColumnIterator aIter( &aCol[nCol],nStartRow,nEndRow );
764 while ( aIter.Next( nRow, pCell ) )
766 if ( pCell->GetCellType() == CELLTYPE_FORMULA )
767 if (((ScFormulaCell*)pCell)->IsSubTotal())
769 SetRowFlags(nRow+1,GetRowFlags(nRow+1)&(~CR_MANUALBREAK));
770 pDocument->DeleteRow( 0,nTab, MAXCOL,nTab, nRow, 1 );
771 --nEndRow;
772 aIter = ScColumnIterator( &aCol[nCol],nRow,nEndRow );
777 rParam.nRow2 = nEndRow; // neues Ende
780 // harte Zahlenformate loeschen (fuer Ergebnisformeln)
782 void lcl_RemoveNumberFormat( ScTable* pTab, SCCOL nCol, SCROW nRow )
784 const ScPatternAttr* pPattern = pTab->GetPattern( nCol, nRow );
785 if ( pPattern->GetItemSet().GetItemState( ATTR_VALUE_FORMAT, FALSE )
786 == SFX_ITEM_SET )
788 ScPatternAttr aNewPattern( *pPattern );
789 SfxItemSet& rSet = aNewPattern.GetItemSet();
790 rSet.ClearItem( ATTR_VALUE_FORMAT );
791 rSet.ClearItem( ATTR_LANGUAGE_FORMAT );
792 pTab->SetPattern( nCol, nRow, aNewPattern, TRUE );
797 // at least MSC needs this at linkage level to be able to use it in a template
798 typedef struct lcl_ScTable_DoSubTotals_RowEntry
800 USHORT nGroupNo;
801 SCROW nSubStartRow;
802 SCROW nDestRow;
803 SCROW nFuncStart;
804 SCROW nFuncEnd;
805 } RowEntry;
807 // neue Zwischenergebnisse
808 // rParam.nRow2 wird veraendert !
810 BOOL ScTable::DoSubTotals( ScSubTotalParam& rParam )
812 SCCOL nStartCol = rParam.nCol1;
813 SCROW nStartRow = rParam.nRow1 + 1; // Header
814 SCCOL nEndCol = rParam.nCol2;
815 SCROW nEndRow = rParam.nRow2; // wird veraendert
816 USHORT i;
818 // Leerzeilen am Ende weglassen,
819 // damit alle Ueberlaeufe (MAXROW) bei InsertRow gefunden werden (#35180#)
820 // Wenn sortiert wurde, sind alle Leerzeilen am Ende.
821 SCSIZE nEmpty = GetEmptyLinesInBlock( nStartCol, nStartRow, nEndCol, nEndRow, DIR_BOTTOM );
822 nEndRow -= nEmpty;
824 USHORT nLevelCount = 0; // Anzahl Gruppierungen
825 BOOL bDoThis = TRUE;
826 for (i=0; i<MAXSUBTOTAL && bDoThis; i++)
827 if (rParam.bGroupActive[i])
828 nLevelCount = i+1;
829 else
830 bDoThis = FALSE;
832 if (nLevelCount==0) // nichts tun
833 return TRUE;
835 SCCOL* nGroupCol = rParam.nField; // Spalten nach denen
836 // gruppiert wird
838 // #44444# Durch (leer) als eigene Kategorie muss immer auf
839 // Teilergebniszeilen aus den anderen Spalten getestet werden
840 // (frueher nur, wenn eine Spalte mehrfach vorkam)
841 BOOL bTestPrevSub = ( nLevelCount > 1 );
843 String aSubString;
844 String aOutString;
846 BOOL bIgnoreCase = !rParam.bCaseSens;
848 String *pCompString[MAXSUBTOTAL]; // Pointer wegen Compiler-Problemen
849 for (i=0; i<MAXSUBTOTAL; i++)
850 pCompString[i] = new String;
852 //! sortieren?
854 ScStyleSheet* pStyle = (ScStyleSheet*) pDocument->GetStyleSheetPool()->Find(
855 ScGlobal::GetRscString(STR_STYLENAME_RESULT), SFX_STYLE_FAMILY_PARA );
857 BOOL bSpaceLeft = TRUE; // Erfolg beim Einfuegen?
859 // #90279# For performance reasons collect formula entries so their
860 // references don't have to be tested for updates each time a new row is
861 // inserted
862 RowEntry aRowEntry;
863 ::std::vector< RowEntry > aRowVector;
865 for (USHORT nLevel=0; nLevel<=nLevelCount && bSpaceLeft; nLevel++) // incl. Gesamtergebnis
867 BOOL bTotal = ( nLevel == nLevelCount );
868 aRowEntry.nGroupNo = bTotal ? 0 : (nLevelCount-nLevel-1);
870 // how many results per level
871 SCCOL nResCount = rParam.nSubTotals[aRowEntry.nGroupNo];
872 // result functions
873 ScSubTotalFunc* eResFunc = rParam.pFunctions[aRowEntry.nGroupNo];
875 if (nResCount > 0) // sonst nur sortieren
877 for (i=0; i<=aRowEntry.nGroupNo; i++)
879 GetString( nGroupCol[i], nStartRow, aSubString );
880 if ( bIgnoreCase )
881 *pCompString[i] = ScGlobal::pCharClass->upper( aSubString );
882 else
883 *pCompString[i] = aSubString;
884 } // aSubString bleibt auf dem letzten stehen
886 BOOL bBlockVis = FALSE; // Gruppe eingeblendet?
887 aRowEntry.nSubStartRow = nStartRow;
888 for (SCROW nRow=nStartRow; nRow<=nEndRow+1 && bSpaceLeft; nRow++)
890 BOOL bChanged;
891 if (nRow>nEndRow)
892 bChanged = TRUE;
893 else
895 bChanged = FALSE;
896 if (!bTotal)
898 String aString;
899 for (i=0; i<=aRowEntry.nGroupNo && !bChanged; i++)
901 GetString( nGroupCol[i], nRow, aString );
902 if (bIgnoreCase)
903 ScGlobal::pCharClass->toUpper( aString );
904 // #41427# wenn sortiert, ist "leer" eine eigene Gruppe
905 // sonst sind leere Zellen unten erlaubt
906 bChanged = ( ( aString.Len() || rParam.bDoSort ) &&
907 aString != *pCompString[i] );
909 if ( bChanged && bTestPrevSub )
911 // No group change on rows that will contain subtotal formulas
912 for ( ::std::vector< RowEntry >::const_iterator
913 iEntry( aRowVector.begin());
914 iEntry != aRowVector.end(); ++iEntry)
916 if ( iEntry->nDestRow == nRow )
918 bChanged = FALSE;
919 break;
925 if ( bChanged )
927 aRowEntry.nDestRow = nRow;
928 aRowEntry.nFuncStart = aRowEntry.nSubStartRow;
929 aRowEntry.nFuncEnd = nRow-1;
931 bSpaceLeft = pDocument->InsertRow( 0, nTab, MAXCOL, nTab,
932 aRowEntry.nDestRow, 1 );
933 DBShowRow( aRowEntry.nDestRow, bBlockVis );
934 bBlockVis = FALSE;
935 if ( rParam.bPagebreak && nRow < MAXROW &&
936 aRowEntry.nSubStartRow != nStartRow && nLevel == 0)
937 SetRowFlags( aRowEntry.nSubStartRow,
938 GetRowFlags(aRowEntry.nSubStartRow) |
939 CR_MANUALBREAK);
941 if (bSpaceLeft)
943 for ( ::std::vector< RowEntry >::iterator iMove(
944 aRowVector.begin() );
945 iMove != aRowVector.end(); ++iMove)
947 if ( aRowEntry.nDestRow <= iMove->nSubStartRow )
948 ++iMove->nSubStartRow;
949 if ( aRowEntry.nDestRow <= iMove->nDestRow )
950 ++iMove->nDestRow;
951 if ( aRowEntry.nDestRow <= iMove->nFuncStart )
952 ++iMove->nFuncStart;
953 if ( aRowEntry.nDestRow <= iMove->nFuncEnd )
954 ++iMove->nFuncEnd;
956 // collect formula positions
957 aRowVector.push_back( aRowEntry );
959 if (bTotal) // "Gesamtergebnis"
960 aOutString = ScGlobal::GetRscString( STR_TABLE_GESAMTERGEBNIS );
961 else
962 { // " Ergebnis"
963 aOutString = aSubString;
964 if (!aOutString.Len())
965 aOutString = ScGlobal::GetRscString( STR_EMPTYDATA );
966 aOutString += ' ';
967 USHORT nStrId = STR_TABLE_ERGEBNIS;
968 if ( nResCount == 1 )
969 switch ( eResFunc[0] )
971 case SUBTOTAL_FUNC_AVE: nStrId = STR_FUN_TEXT_AVG; break;
972 case SUBTOTAL_FUNC_CNT:
973 case SUBTOTAL_FUNC_CNT2: nStrId = STR_FUN_TEXT_COUNT; break;
974 case SUBTOTAL_FUNC_MAX: nStrId = STR_FUN_TEXT_MAX; break;
975 case SUBTOTAL_FUNC_MIN: nStrId = STR_FUN_TEXT_MIN; break;
976 case SUBTOTAL_FUNC_PROD: nStrId = STR_FUN_TEXT_PRODUCT; break;
977 case SUBTOTAL_FUNC_STD:
978 case SUBTOTAL_FUNC_STDP: nStrId = STR_FUN_TEXT_STDDEV; break;
979 case SUBTOTAL_FUNC_SUM: nStrId = STR_FUN_TEXT_SUM; break;
980 case SUBTOTAL_FUNC_VAR:
981 case SUBTOTAL_FUNC_VARP: nStrId = STR_FUN_TEXT_VAR; break;
982 default:
984 // added to avoid warnings
987 aOutString += ScGlobal::GetRscString( nStrId );
989 SetString( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, nTab, aOutString );
990 ApplyStyle( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, *pStyle );
992 /* if (rParam.bPagebreak && nRow < MAXROW)
994 BYTE nFlags = GetRowFlags( nRow+1 );
995 nFlags |= CR_MANUALBREAK;
996 SetRowFlags( nRow+1, nFlags );
999 ++nRow;
1000 ++nEndRow;
1001 aRowEntry.nSubStartRow = nRow;
1002 for (i=0; i<=aRowEntry.nGroupNo; i++)
1004 GetString( nGroupCol[i], nRow, aSubString );
1005 if ( bIgnoreCase )
1006 *pCompString[i] = ScGlobal::pCharClass->upper( aSubString );
1007 else
1008 *pCompString[i] = aSubString;
1012 if (!pRowFlags)
1013 bBlockVis = TRUE;
1014 else
1015 if ( (pRowFlags->GetValue(nRow) & CR_FILTERED) == 0 )
1016 bBlockVis = TRUE;
1019 else
1021 // DBG_ERROR( "nSubTotals==0 bei DoSubTotals" );
1025 // now insert the formulas
1026 ScComplexRefData aRef;
1027 aRef.InitFlags();
1028 aRef.Ref1.nTab = nTab;
1029 aRef.Ref2.nTab = nTab;
1030 for ( ::std::vector< RowEntry >::const_iterator iEntry( aRowVector.begin());
1031 iEntry != aRowVector.end(); ++iEntry)
1033 SCCOL nResCount = rParam.nSubTotals[iEntry->nGroupNo];
1034 SCCOL* nResCols = rParam.pSubTotals[iEntry->nGroupNo];
1035 ScSubTotalFunc* eResFunc = rParam.pFunctions[iEntry->nGroupNo];
1036 for ( SCCOL nResult=0; nResult < nResCount; ++nResult )
1038 aRef.Ref1.nCol = nResCols[nResult];
1039 aRef.Ref1.nRow = iEntry->nFuncStart;
1040 aRef.Ref2.nCol = nResCols[nResult];
1041 aRef.Ref2.nRow = iEntry->nFuncEnd;
1043 ScTokenArray aArr;
1044 aArr.AddOpCode( ocSubTotal );
1045 aArr.AddOpCode( ocOpen );
1046 aArr.AddDouble( (double) eResFunc[nResult] );
1047 aArr.AddOpCode( ocSep );
1048 aArr.AddDoubleReference( aRef );
1049 aArr.AddOpCode( ocClose );
1050 aArr.AddOpCode( ocStop );
1051 ScBaseCell* pCell = new ScFormulaCell( pDocument, ScAddress(
1052 nResCols[nResult], iEntry->nDestRow, nTab), &aArr );
1053 PutCell( nResCols[nResult], iEntry->nDestRow, pCell );
1055 if ( nResCols[nResult] != nGroupCol[iEntry->nGroupNo] )
1057 ApplyStyle( nResCols[nResult], iEntry->nDestRow, *pStyle );
1059 // Zahlformat loeschen
1060 lcl_RemoveNumberFormat( this, nResCols[nResult], iEntry->nDestRow );
1066 //! je nach Einstellung Zwischensummen-Zeilen nach oben verschieben ?
1068 //! Outlines direkt erzeugen?
1070 if (bSpaceLeft)
1071 DoAutoOutline( nStartCol, nStartRow, nEndCol, nEndRow );
1073 for (i=0; i<MAXSUBTOTAL; i++)
1074 delete pCompString[i];
1076 rParam.nRow2 = nEndRow; // neues Ende
1077 return bSpaceLeft;
1081 BOOL ScTable::ValidQuery(SCROW nRow, const ScQueryParam& rParam,
1082 BOOL* pSpecial /* =NULL */ , ScBaseCell* pCell /* =NULL */ ,
1083 BOOL* pbTestEqualCondition /* = NULL */ )
1085 if (!rParam.GetEntry(0).bDoQuery)
1086 return TRUE;
1088 //---------------------------------------------------------------
1090 const SCSIZE nFixedBools = 32;
1091 BOOL aBool[nFixedBools];
1092 BOOL aTest[nFixedBools];
1093 SCSIZE nEntryCount = rParam.GetEntryCount();
1094 BOOL* pPasst = ( nEntryCount <= nFixedBools ? &aBool[0] : new BOOL[nEntryCount] );
1095 BOOL* pTest = ( nEntryCount <= nFixedBools ? &aTest[0] : new BOOL[nEntryCount] );
1097 long nPos = -1;
1098 SCSIZE i = 0;
1099 BOOL bMatchWholeCell = pDocument->GetDocOptions().IsMatchWholeCell();
1100 CollatorWrapper* pCollator = (rParam.bCaseSens ? ScGlobal::pCaseCollator :
1101 ScGlobal::pCollator);
1102 ::utl::TransliterationWrapper* pTransliteration = (rParam.bCaseSens ?
1103 ScGlobal::pCaseTransliteration : ScGlobal::pTransliteration);
1105 while ( (i < nEntryCount) && rParam.GetEntry(i).bDoQuery )
1107 ScQueryEntry& rEntry = rParam.GetEntry(i);
1108 // we can only handle one single direct query
1109 if ( !pCell || i > 0 )
1110 pCell = GetCell( static_cast<SCCOL>(rEntry.nField), nRow );
1112 BOOL bOk = FALSE;
1113 BOOL bTestEqual = FALSE;
1115 if ( pSpecial && pSpecial[i] )
1117 if (rEntry.nVal == SC_EMPTYFIELDS)
1118 bOk = !( aCol[rEntry.nField].HasDataAt( nRow ) );
1119 else // if (rEntry.nVal == SC_NONEMPTYFIELDS)
1120 bOk = aCol[rEntry.nField].HasDataAt( nRow );
1122 else if ( !rEntry.bQueryByString && (pCell ? pCell->HasValueData() :
1123 HasValueData( static_cast<SCCOL>(rEntry.nField), nRow)))
1124 { // by Value
1125 double nCellVal;
1126 if ( pCell )
1128 switch ( pCell->GetCellType() )
1130 case CELLTYPE_VALUE :
1131 nCellVal = ((ScValueCell*)pCell)->GetValue();
1132 break;
1133 case CELLTYPE_FORMULA :
1134 nCellVal = ((ScFormulaCell*)pCell)->GetValue();
1135 break;
1136 default:
1137 nCellVal = 0.0;
1141 else
1142 nCellVal = GetValue( static_cast<SCCOL>(rEntry.nField), nRow );
1143 switch (rEntry.eOp)
1145 case SC_EQUAL :
1146 bOk = ::rtl::math::approxEqual( nCellVal, rEntry.nVal );
1147 break;
1148 case SC_LESS :
1149 bOk = (nCellVal < rEntry.nVal) && !::rtl::math::approxEqual( nCellVal, rEntry.nVal );
1150 break;
1151 case SC_GREATER :
1152 bOk = (nCellVal > rEntry.nVal) && !::rtl::math::approxEqual( nCellVal, rEntry.nVal );
1153 break;
1154 case SC_LESS_EQUAL :
1155 bOk = (nCellVal < rEntry.nVal) || ::rtl::math::approxEqual( nCellVal, rEntry.nVal );
1156 if ( bOk && pbTestEqualCondition )
1157 bTestEqual = ::rtl::math::approxEqual( nCellVal, rEntry.nVal );
1158 break;
1159 case SC_GREATER_EQUAL :
1160 bOk = (nCellVal > rEntry.nVal) || ::rtl::math::approxEqual( nCellVal, rEntry.nVal );
1161 if ( bOk && pbTestEqualCondition )
1162 bTestEqual = ::rtl::math::approxEqual( nCellVal, rEntry.nVal );
1163 break;
1164 case SC_NOT_EQUAL :
1165 bOk = !::rtl::math::approxEqual( nCellVal, rEntry.nVal );
1166 break;
1167 default:
1169 // added to avoid warnings
1173 else if ( (rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL) ||
1174 (rEntry.eOp == SC_CONTAINS || rEntry.eOp == SC_DOES_NOT_CONTAIN ||
1175 rEntry.eOp == SC_BEGINS_WITH || rEntry.eOp == SC_ENDS_WITH ||
1176 rEntry.eOp == SC_DOES_NOT_BEGIN_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH) ||
1177 (rEntry.bQueryByString && (pCell ? pCell->HasStringData() :
1178 HasStringData(
1179 static_cast<SCCOL>(rEntry.nField),
1180 nRow))))
1181 { // by String
1182 String aCellStr;
1183 if( rEntry.eOp == SC_CONTAINS || rEntry.eOp == SC_DOES_NOT_CONTAIN
1184 || rEntry.eOp == SC_BEGINS_WITH || rEntry.eOp == SC_ENDS_WITH
1185 || rEntry.eOp == SC_DOES_NOT_BEGIN_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH )
1186 bMatchWholeCell = FALSE;
1187 if ( pCell )
1189 if (pCell->GetCellType() != CELLTYPE_NOTE)
1191 ULONG nFormat = GetNumberFormat( static_cast<SCCOL>(rEntry.nField), nRow );
1192 ScCellFormat::GetInputString( pCell, nFormat, aCellStr, *(pDocument->GetFormatTable()) );
1195 else
1196 GetInputString( static_cast<SCCOL>(rEntry.nField), nRow, aCellStr );
1198 BOOL bRealRegExp = (rParam.bRegExp && ((rEntry.eOp == SC_EQUAL)
1199 || (rEntry.eOp == SC_NOT_EQUAL) || (rEntry.eOp == SC_CONTAINS)
1200 || (rEntry.eOp == SC_DOES_NOT_CONTAIN) || (rEntry.eOp == SC_BEGINS_WITH)
1201 || (rEntry.eOp == SC_ENDS_WITH) || (rEntry.eOp == SC_DOES_NOT_BEGIN_WITH)
1202 || (rEntry.eOp == SC_DOES_NOT_END_WITH)));
1203 BOOL bTestRegExp = (pbTestEqualCondition && rParam.bRegExp
1204 && ((rEntry.eOp == SC_LESS_EQUAL)
1205 || (rEntry.eOp == SC_GREATER_EQUAL)));
1206 if ( bRealRegExp || bTestRegExp )
1208 xub_StrLen nStart = 0;
1209 xub_StrLen nEnd = aCellStr.Len();
1211 // from 614 on, nEnd is behind the found text
1212 BOOL bMatch = FALSE;
1213 if ( rEntry.eOp == SC_ENDS_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH )
1215 nEnd = 0;
1216 nStart = aCellStr.Len();
1217 bMatch = (BOOL) rEntry.GetSearchTextPtr( rParam.bCaseSens )
1218 ->SearchBkwrd( aCellStr, &nStart, &nEnd );
1220 else
1222 bMatch = (BOOL) rEntry.GetSearchTextPtr( rParam.bCaseSens )
1223 ->SearchFrwrd( aCellStr, &nStart, &nEnd );
1225 if ( bMatch && bMatchWholeCell
1226 && (nStart != 0 || nEnd != aCellStr.Len()) )
1227 bMatch = FALSE; // RegExp must match entire cell string
1228 if ( bRealRegExp )
1229 switch (rEntry.eOp)
1231 case SC_EQUAL:
1232 case SC_CONTAINS:
1233 bOk = bMatch;
1234 break;
1235 case SC_NOT_EQUAL:
1236 case SC_DOES_NOT_CONTAIN:
1237 bOk = !bMatch;
1238 break;
1239 case SC_BEGINS_WITH:
1240 bOk = ( bMatch && (nStart == 0) );
1241 break;
1242 case SC_DOES_NOT_BEGIN_WITH:
1243 bOk = !( bMatch && (nStart == 0) );
1244 break;
1245 case SC_ENDS_WITH:
1246 bOk = ( bMatch && (nEnd == aCellStr.Len()) );
1247 break;
1248 case SC_DOES_NOT_END_WITH:
1249 bOk = !( bMatch && (nEnd == aCellStr.Len()) );
1250 break;
1251 default:
1253 // added to avoid warnings
1256 else
1257 bTestEqual = bMatch;
1259 if ( !bRealRegExp )
1261 if ( rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL
1262 || rEntry.eOp == SC_CONTAINS || rEntry.eOp == SC_DOES_NOT_CONTAIN
1263 || rEntry.eOp == SC_BEGINS_WITH || rEntry.eOp == SC_ENDS_WITH
1264 || rEntry.eOp == SC_DOES_NOT_BEGIN_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH )
1266 if ( !rEntry.bQueryByString && rEntry.pStr->Len() == 0 )
1268 // #i18374# When used from functions (match, countif, sumif, vlookup, hlookup, lookup),
1269 // the query value is assigned directly, and the string is empty. In that case,
1270 // don't find any string (isEqual would find empty string results in formula cells).
1271 bOk = FALSE;
1272 if ( rEntry.eOp == SC_NOT_EQUAL )
1273 bOk = !bOk;
1275 else if ( bMatchWholeCell )
1277 bOk = pTransliteration->isEqual( aCellStr, *rEntry.pStr );
1278 if ( rEntry.eOp == SC_NOT_EQUAL )
1279 bOk = !bOk;
1281 else
1283 String aCell( pTransliteration->transliterate(
1284 aCellStr, ScGlobal::eLnge, 0, aCellStr.Len(),
1285 NULL ) );
1286 String aQuer( pTransliteration->transliterate(
1287 *rEntry.pStr, ScGlobal::eLnge, 0, rEntry.pStr->Len(),
1288 NULL ) );
1289 xub_StrLen nIndex = (rEntry.eOp == SC_ENDS_WITH
1290 || rEntry.eOp == SC_DOES_NOT_END_WITH)? (aCell.Len()-aQuer.Len()):0;
1291 xub_StrLen nStrPos = aCell.Search( aQuer, nIndex );
1292 switch (rEntry.eOp)
1294 case SC_EQUAL:
1295 case SC_CONTAINS:
1296 bOk = ( nStrPos != STRING_NOTFOUND );
1297 break;
1298 case SC_NOT_EQUAL:
1299 case SC_DOES_NOT_CONTAIN:
1300 bOk = ( nStrPos == STRING_NOTFOUND );
1301 break;
1302 case SC_BEGINS_WITH:
1303 bOk = ( nStrPos == 0 );
1304 break;
1305 case SC_DOES_NOT_BEGIN_WITH:
1306 bOk = ( nStrPos != 0 );
1307 break;
1308 case SC_ENDS_WITH:
1309 bOk = ( nStrPos + aQuer.Len() == aCell.Len() );
1310 break;
1311 case SC_DOES_NOT_END_WITH:
1312 bOk = ( nStrPos + aQuer.Len() != aCell.Len() );
1313 break;
1314 default:
1316 // added to avoid warnings
1321 else
1322 { // use collator here because data was probably sorted
1323 sal_Int32 nCompare = pCollator->compareString(
1324 aCellStr, *rEntry.pStr );
1325 switch (rEntry.eOp)
1327 case SC_LESS :
1328 bOk = (nCompare < 0);
1329 break;
1330 case SC_GREATER :
1331 bOk = (nCompare > 0);
1332 break;
1333 case SC_LESS_EQUAL :
1334 bOk = (nCompare <= 0);
1335 if ( bOk && pbTestEqualCondition && !bTestEqual )
1336 bTestEqual = (nCompare == 0);
1337 break;
1338 case SC_GREATER_EQUAL :
1339 bOk = (nCompare >= 0);
1340 if ( bOk && pbTestEqualCondition && !bTestEqual )
1341 bTestEqual = (nCompare == 0);
1342 break;
1343 default:
1345 // added to avoid warnings
1351 else if (rParam.bMixedComparison)
1353 if (rEntry.bQueryByString &&
1354 (rEntry.eOp == SC_LESS || rEntry.eOp == SC_LESS_EQUAL) &&
1355 (pCell ? pCell->HasValueData() :
1356 HasValueData( static_cast<SCCOL>(rEntry.nField), nRow)))
1358 bOk = TRUE;
1360 else if (!rEntry.bQueryByString &&
1361 (rEntry.eOp == SC_GREATER || rEntry.eOp == SC_GREATER_EQUAL) &&
1362 (pCell ? pCell->HasStringData() :
1363 HasStringData( static_cast<SCCOL>(rEntry.nField), nRow)))
1365 bOk = TRUE;
1369 if (nPos == -1)
1371 nPos++;
1372 pPasst[nPos] = bOk;
1373 pTest[nPos] = bTestEqual;
1375 else
1377 if (rEntry.eConnect == SC_AND)
1379 pPasst[nPos] = pPasst[nPos] && bOk;
1380 pTest[nPos] = pTest[nPos] && bTestEqual;
1382 else
1384 nPos++;
1385 pPasst[nPos] = bOk;
1386 pTest[nPos] = bTestEqual;
1389 i++;
1392 for ( long j=1; j <= nPos; j++ )
1394 pPasst[0] = pPasst[0] || pPasst[j];
1395 pTest[0] = pTest[0] || pTest[j];
1398 BOOL bRet = pPasst[0];
1399 if ( pPasst != &aBool[0] )
1400 delete [] pPasst;
1401 if ( pbTestEqualCondition )
1402 *pbTestEqualCondition = pTest[0];
1403 if ( pTest != &aTest[0] )
1404 delete [] pTest;
1406 return bRet;
1409 void ScTable::TopTenQuery( ScQueryParam& rParam )
1411 BOOL bSortCollatorInitialized = FALSE;
1412 SCSIZE nEntryCount = rParam.GetEntryCount();
1413 SCROW nRow1 = (rParam.bHasHeader ? rParam.nRow1 + 1 : rParam.nRow1);
1414 SCSIZE nCount = static_cast<SCSIZE>(rParam.nRow2 - nRow1 + 1);
1415 for ( SCSIZE i=0; (i<nEntryCount) && (rParam.GetEntry(i).bDoQuery); i++ )
1417 ScQueryEntry& rEntry = rParam.GetEntry(i);
1418 switch ( rEntry.eOp )
1420 case SC_TOPVAL:
1421 case SC_BOTVAL:
1422 case SC_TOPPERC:
1423 case SC_BOTPERC:
1425 ScSortParam aLocalSortParam( rParam, static_cast<SCCOL>(rEntry.nField) );
1426 aSortParam = aLocalSortParam; // used in CreateSortInfoArray, Compare
1427 if ( !bSortCollatorInitialized )
1429 bSortCollatorInitialized = TRUE;
1430 InitSortCollator( aLocalSortParam );
1432 ScSortInfoArray* pArray = CreateSortInfoArray( nRow1, rParam.nRow2 );
1433 DecoladeRow( pArray, nRow1, rParam.nRow2 );
1434 QuickSort( pArray, nRow1, rParam.nRow2 );
1435 ScSortInfo** ppInfo = pArray->GetFirstArray();
1436 SCSIZE nValidCount = nCount;
1437 // keine Note-/Leerzellen zaehlen, sind ans Ende sortiert
1438 while ( nValidCount > 0 && ( ppInfo[nValidCount-1]->pCell == NULL ||
1439 ppInfo[nValidCount-1]->pCell->GetCellType() == CELLTYPE_NOTE ) )
1440 nValidCount--;
1441 // keine Strings zaehlen, sind zwischen Value und Leer
1442 while ( nValidCount > 0
1443 && ppInfo[nValidCount-1]->pCell->HasStringData() )
1444 nValidCount--;
1445 if ( nValidCount > 0 )
1447 if ( rEntry.bQueryByString )
1448 { // dat wird nix
1449 rEntry.bQueryByString = FALSE;
1450 rEntry.nVal = 10; // 10 bzw. 10%
1452 SCSIZE nVal = (rEntry.nVal >= 1 ? static_cast<SCSIZE>(rEntry.nVal) : 1);
1453 SCSIZE nOffset = 0;
1454 switch ( rEntry.eOp )
1456 case SC_TOPVAL:
1458 rEntry.eOp = SC_GREATER_EQUAL;
1459 if ( nVal > nValidCount )
1460 nVal = nValidCount;
1461 nOffset = nValidCount - nVal; // 1 <= nVal <= nValidCount
1463 break;
1464 case SC_BOTVAL:
1466 rEntry.eOp = SC_LESS_EQUAL;
1467 if ( nVal > nValidCount )
1468 nVal = nValidCount;
1469 nOffset = nVal - 1; // 1 <= nVal <= nValidCount
1471 break;
1472 case SC_TOPPERC:
1474 rEntry.eOp = SC_GREATER_EQUAL;
1475 if ( nVal > 100 )
1476 nVal = 100;
1477 nOffset = nValidCount - (nValidCount * nVal / 100);
1478 if ( nOffset >= nValidCount )
1479 nOffset = nValidCount - 1;
1481 break;
1482 case SC_BOTPERC:
1484 rEntry.eOp = SC_LESS_EQUAL;
1485 if ( nVal > 100 )
1486 nVal = 100;
1487 nOffset = (nValidCount * nVal / 100);
1488 if ( nOffset >= nValidCount )
1489 nOffset = nValidCount - 1;
1491 break;
1492 default:
1494 // added to avoid warnings
1497 ScBaseCell* pCell = ppInfo[nOffset]->pCell;
1498 if ( pCell->HasValueData() )
1500 if ( pCell->GetCellType() == CELLTYPE_VALUE )
1501 rEntry.nVal = ((ScValueCell*)pCell)->GetValue();
1502 else
1503 rEntry.nVal = ((ScFormulaCell*)pCell)->GetValue();
1505 else
1507 DBG_ERRORFILE( "TopTenQuery: pCell kein ValueData" );
1508 rEntry.eOp = SC_GREATER_EQUAL;
1509 rEntry.nVal = 0;
1512 else
1514 rEntry.eOp = SC_GREATER_EQUAL;
1515 rEntry.bQueryByString = FALSE;
1516 rEntry.nVal = 0;
1518 delete pArray;
1520 break;
1521 default:
1523 // added to avoid warnings
1527 if ( bSortCollatorInitialized )
1528 DestroySortCollator();
1531 static void lcl_PrepareQuery( ScDocument* pDoc, ScTable* pTab, ScQueryParam& rParam, BOOL* pSpecial )
1533 bool bTopTen = false;
1534 SCSIZE nEntryCount = rParam.GetEntryCount();
1536 for ( SCSIZE i = 0; i < nEntryCount; ++i )
1538 pSpecial[i] = FALSE;
1539 ScQueryEntry& rEntry = rParam.GetEntry(i);
1540 if ( rEntry.bDoQuery )
1542 if ( rEntry.bQueryByString )
1544 sal_uInt32 nIndex = 0;
1545 rEntry.bQueryByString = !( pDoc->GetFormatTable()->
1546 IsNumberFormat( *rEntry.pStr, nIndex, rEntry.nVal ) );
1548 else
1550 // #58736# call from UNO or second call from autofilter
1551 if ( rEntry.nVal == SC_EMPTYFIELDS || rEntry.nVal == SC_NONEMPTYFIELDS )
1553 pSpecial[i] = TRUE;
1556 if ( !bTopTen )
1558 switch ( rEntry.eOp )
1560 case SC_TOPVAL:
1561 case SC_BOTVAL:
1562 case SC_TOPPERC:
1563 case SC_BOTPERC:
1565 bTopTen = true;
1567 break;
1568 default:
1576 if ( bTopTen )
1578 pTab->TopTenQuery( rParam );
1582 SCSIZE ScTable::Query(ScQueryParam& rParamOrg, BOOL bKeepSub)
1584 ScQueryParam aParam( rParamOrg );
1585 ScStrCollection aScStrCollection;
1586 StrData* pStrData = NULL;
1588 BOOL bStarted = FALSE;
1589 BOOL bOldResult = TRUE;
1590 SCROW nOldStart = 0;
1591 SCROW nOldEnd = 0;
1593 SCSIZE nCount = 0;
1594 SCROW nOutRow = 0;
1595 SCROW nHeader = aParam.bHasHeader ? 1 : 0;
1597 SCSIZE nEntryCount = aParam.GetEntryCount();
1598 BOOL* pSpecial = new BOOL[nEntryCount];
1599 lcl_PrepareQuery( pDocument, this, aParam, pSpecial );
1601 SCROW nEndRow = aParam.bUseDynamicRange ? aParam.nDynamicEndRow : aParam.nRow2;
1602 if (!aParam.bInplace)
1604 nOutRow = aParam.nDestRow + nHeader;
1605 if (nHeader > 0)
1606 CopyData( aParam.nCol1, aParam.nRow1, aParam.nCol2, aParam.nRow1,
1607 aParam.nDestCol, aParam.nDestRow, aParam.nDestTab );
1610 for (SCROW j=aParam.nRow1 + nHeader; j<=nEndRow; j++)
1612 BOOL bResult; // Filterergebnis
1613 BOOL bValid = ValidQuery(j, aParam, pSpecial);
1614 if (!bValid && bKeepSub) // Subtotals stehenlassen
1616 for (SCCOL nCol=aParam.nCol1; nCol<=aParam.nCol2 && !bValid; nCol++)
1618 ScBaseCell* pCell;
1619 pCell = GetCell( nCol, j );
1620 if ( pCell )
1621 if ( pCell->GetCellType() == CELLTYPE_FORMULA )
1622 if (((ScFormulaCell*)pCell)->IsSubTotal())
1623 if (RefVisible((ScFormulaCell*)pCell))
1624 bValid = TRUE;
1627 if (bValid)
1629 if (aParam.bDuplicate)
1630 bResult = TRUE;
1631 else
1633 String aStr;
1634 for (SCCOL k=aParam.nCol1; k <= aParam.nCol2; k++)
1636 String aCellStr;
1637 GetString(k, j, aCellStr);
1638 aStr += aCellStr;
1639 aStr += (sal_Unicode)1;
1641 pStrData = new StrData(aStr);
1643 BOOL bIsUnique = TRUE;
1644 if (pStrData)
1645 bIsUnique = aScStrCollection.Insert(pStrData);
1646 if (bIsUnique)
1647 bResult = TRUE;
1648 else
1650 delete pStrData;
1651 bResult = FALSE;
1655 else
1656 bResult = FALSE;
1658 if (aParam.bInplace)
1660 if (bResult == bOldResult && bStarted)
1661 nOldEnd = j;
1662 else
1664 if (bStarted)
1665 DBShowRows(nOldStart,nOldEnd, bOldResult);
1666 nOldStart = nOldEnd = j;
1667 bOldResult = bResult;
1669 bStarted = TRUE;
1671 else
1673 if (bResult)
1675 CopyData( aParam.nCol1,j, aParam.nCol2,j, aParam.nDestCol,nOutRow,aParam.nDestTab );
1676 ++nOutRow;
1679 if (bResult)
1680 ++nCount;
1683 if (aParam.bInplace && bStarted)
1684 DBShowRows(nOldStart,nOldEnd, bOldResult);
1686 delete[] pSpecial;
1688 return nCount;
1691 BOOL ScTable::CreateExcelQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
1693 BOOL bValid = TRUE;
1694 SCCOL* pFields = new SCCOL[nCol2-nCol1+1];
1695 String aCellStr;
1696 SCCOL nCol = nCol1;
1697 DBG_ASSERT( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
1698 SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
1699 SCROW nDBRow1 = rQueryParam.nRow1;
1700 SCCOL nDBCol2 = rQueryParam.nCol2;
1701 // Erste Zeile muessen Spaltenkoepfe sein
1702 while (bValid && (nCol <= nCol2))
1704 String aQueryStr;
1705 GetUpperCellString(nCol, nRow1, aQueryStr);
1706 BOOL bFound = FALSE;
1707 SCCOL i = rQueryParam.nCol1;
1708 while (!bFound && (i <= nDBCol2))
1710 if ( nTab == nDBTab )
1711 GetUpperCellString(i, nDBRow1, aCellStr);
1712 else
1713 pDocument->GetUpperCellString(i, nDBRow1, nDBTab, aCellStr);
1714 bFound = (aCellStr == aQueryStr);
1715 if (!bFound) i++;
1717 if (bFound)
1718 pFields[nCol - nCol1] = i;
1719 else
1720 bValid = FALSE;
1721 nCol++;
1723 if (bValid)
1725 ULONG nVisible = 0;
1726 for ( nCol=nCol1; nCol<=nCol2; nCol++ )
1727 nVisible += aCol[nCol].VisibleCount( nRow1+1, nRow2 );
1729 if ( nVisible > SCSIZE_MAX / sizeof(void*) )
1731 DBG_ERROR("zu viele Filterkritierien");
1732 nVisible = 0;
1735 SCSIZE nNewEntries = nVisible;
1736 rQueryParam.Resize( nNewEntries );
1738 SCSIZE nIndex = 0;
1739 SCROW nRow = nRow1 + 1;
1740 while (nRow <= nRow2)
1742 nCol = nCol1;
1743 while (nCol <= nCol2)
1745 GetInputString( nCol, nRow, aCellStr );
1746 ScGlobal::pCharClass->toUpper( aCellStr );
1747 if (aCellStr.Len() > 0)
1749 if (nIndex < nNewEntries)
1751 rQueryParam.GetEntry(nIndex).nField = pFields[nCol - nCol1];
1752 rQueryParam.FillInExcelSyntax(aCellStr, nIndex);
1753 nIndex++;
1754 if (nIndex < nNewEntries)
1755 rQueryParam.GetEntry(nIndex).eConnect = SC_AND;
1757 else
1758 bValid = FALSE;
1760 nCol++;
1762 nRow++;
1763 if (nIndex < nNewEntries)
1764 rQueryParam.GetEntry(nIndex).eConnect = SC_OR;
1767 delete [] pFields;
1768 return bValid;
1771 BOOL ScTable::CreateStarQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
1773 // A valid StarQuery must be at least 4 columns wide. To be precise it
1774 // should be exactly 4 columns ...
1775 // Additionally, if this wasn't checked, a formula pointing to a valid 1-3
1776 // column Excel style query range immediately left to itself would result
1777 // in a circular reference when the field name or operator or value (first
1778 // to third query range column) is obtained (#i58354#). Furthermore, if the
1779 // range wasn't sufficiently specified data changes wouldn't flag formula
1780 // cells for recalculation.
1781 if (nCol2 - nCol1 < 3)
1782 return FALSE;
1784 BOOL bValid;
1785 BOOL bFound;
1786 String aCellStr;
1787 SCSIZE nIndex = 0;
1788 SCROW nRow = nRow1;
1789 DBG_ASSERT( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
1790 SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
1791 SCROW nDBRow1 = rQueryParam.nRow1;
1792 SCCOL nDBCol2 = rQueryParam.nCol2;
1794 SCSIZE nNewEntries = static_cast<SCSIZE>(nRow2-nRow1+1);
1795 rQueryParam.Resize( nNewEntries );
1799 ScQueryEntry& rEntry = rQueryParam.GetEntry(nIndex);
1801 bValid = FALSE;
1802 // Erste Spalte UND/ODER
1803 if (nIndex > 0)
1805 GetUpperCellString(nCol1, nRow, aCellStr);
1806 if ( aCellStr == ScGlobal::GetRscString(STR_TABLE_UND) )
1808 rEntry.eConnect = SC_AND;
1809 bValid = TRUE;
1811 else if ( aCellStr == ScGlobal::GetRscString(STR_TABLE_ODER) )
1813 rEntry.eConnect = SC_OR;
1814 bValid = TRUE;
1817 // Zweite Spalte FeldName
1818 if ((nIndex < 1) || bValid)
1820 bFound = FALSE;
1821 GetUpperCellString(nCol1 + 1, nRow, aCellStr);
1822 for (SCCOL i=rQueryParam.nCol1; (i <= nDBCol2) && (!bFound); i++)
1824 String aFieldStr;
1825 if ( nTab == nDBTab )
1826 GetUpperCellString(i, nDBRow1, aFieldStr);
1827 else
1828 pDocument->GetUpperCellString(i, nDBRow1, nDBTab, aFieldStr);
1829 bFound = (aCellStr == aFieldStr);
1830 if (bFound)
1832 rEntry.nField = i;
1833 bValid = TRUE;
1835 else
1836 bValid = FALSE;
1839 // Dritte Spalte Operator =<>...
1840 if (bValid)
1842 bFound = FALSE;
1843 GetUpperCellString(nCol1 + 2, nRow, aCellStr);
1844 if (aCellStr.GetChar(0) == '<')
1846 if (aCellStr.GetChar(1) == '>')
1847 rEntry.eOp = SC_NOT_EQUAL;
1848 else if (aCellStr.GetChar(1) == '=')
1849 rEntry.eOp = SC_LESS_EQUAL;
1850 else
1851 rEntry.eOp = SC_LESS;
1853 else if (aCellStr.GetChar(0) == '>')
1855 if (aCellStr.GetChar(1) == '=')
1856 rEntry.eOp = SC_GREATER_EQUAL;
1857 else
1858 rEntry.eOp = SC_GREATER;
1860 else if (aCellStr.GetChar(0) == '=')
1861 rEntry.eOp = SC_EQUAL;
1864 // Vierte Spalte Wert
1865 if (bValid)
1867 GetString(nCol1 + 3, nRow, *rEntry.pStr);
1868 rEntry.bDoQuery = TRUE;
1870 nIndex++;
1871 nRow++;
1873 while (bValid && (nRow <= nRow2) /* && (nIndex < MAXQUERY) */ );
1874 return bValid;
1877 BOOL ScTable::CreateQueryParam(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
1879 SCSIZE i, nCount;
1880 PutInOrder(nCol1, nCol2);
1881 PutInOrder(nRow1, nRow2);
1883 nCount = rQueryParam.GetEntryCount();
1884 for (i=0; i < nCount; i++)
1885 rQueryParam.GetEntry(i).Clear();
1887 // Standard QueryTabelle
1888 BOOL bValid = CreateStarQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
1889 // Excel QueryTabelle
1890 if (!bValid)
1891 bValid = CreateExcelQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
1893 nCount = rQueryParam.GetEntryCount();
1894 if (bValid)
1896 // bQueryByString muss gesetzt sein
1897 for (i=0; i < nCount; i++)
1898 rQueryParam.GetEntry(i).bQueryByString = TRUE;
1900 else
1902 // nix
1903 for (i=0; i < nCount; i++)
1904 rQueryParam.GetEntry(i).Clear();
1906 return bValid;
1909 BOOL ScTable::HasColHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW /* nEndRow */ )
1911 for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
1913 CellType eType = GetCellType( nCol, nStartRow );
1914 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
1915 return FALSE;
1917 return TRUE;
1920 BOOL ScTable::HasRowHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL /* nEndCol */, SCROW nEndRow )
1922 for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++)
1924 CellType eType = GetCellType( nStartCol, nRow );
1925 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
1926 return FALSE;
1928 return TRUE;
1931 void ScTable::GetFilterEntries(SCCOL nCol, SCROW nRow1, SCROW nRow2, TypedScStrCollection& rStrings)
1933 aCol[nCol].GetFilterEntries( nRow1, nRow2, rStrings );
1936 void ScTable::GetFilteredFilterEntries( SCCOL nCol, SCROW nRow1, SCROW nRow2, const ScQueryParam& rParam, TypedScStrCollection& rStrings )
1938 // remove the entry for this column from the query parameter
1939 ScQueryParam aParam( rParam );
1940 SCSIZE nEntryCount = aParam.GetEntryCount();
1941 for ( SCSIZE i = 0; i < nEntryCount && aParam.GetEntry(i).bDoQuery; ++i )
1943 ScQueryEntry& rEntry = aParam.GetEntry(i);
1944 if ( rEntry.nField == nCol )
1946 aParam.DeleteQuery(i);
1947 break;
1950 nEntryCount = aParam.GetEntryCount();
1952 BOOL* pSpecial = new BOOL[nEntryCount];
1953 lcl_PrepareQuery( pDocument, this, aParam, pSpecial );
1955 for ( SCROW j = nRow1; j <= nRow2; ++j )
1957 if ( ValidQuery( j, aParam, pSpecial ) )
1959 aCol[nCol].GetFilterEntries( j, j, rStrings );
1963 delete[] pSpecial;
1966 BOOL ScTable::GetDataEntries(SCCOL nCol, SCROW nRow, TypedScStrCollection& rStrings, BOOL bLimit)
1968 return aCol[nCol].GetDataEntries( nRow, rStrings, bLimit );
1971 ULONG ScTable::GetCellCount() const
1973 ULONG nCellCount = 0;
1975 for ( SCCOL nCol=0; nCol<=MAXCOL; nCol++ )
1976 nCellCount += aCol[nCol].GetCellCount();
1978 return nCellCount;
1981 ULONG ScTable::GetWeightedCount() const
1983 ULONG nCellCount = 0;
1985 for ( SCCOL nCol=0; nCol<=MAXCOL; nCol++ )
1986 if ( aCol[nCol].GetCellCount() ) // GetCellCount ist inline
1987 nCellCount += aCol[nCol].GetWeightedCount();
1989 return nCellCount;
1992 ULONG ScTable::GetCodeCount() const
1994 ULONG nCodeCount = 0;
1996 for ( SCCOL nCol=0; nCol<=MAXCOL; nCol++ )
1997 if ( aCol[nCol].GetCellCount() ) // GetCellCount ist inline
1998 nCodeCount += aCol[nCol].GetCodeCount();
2000 return nCodeCount;
2003 sal_Int32 ScTable::GetMaxStringLen( SCCOL nCol, SCROW nRowStart,
2004 SCROW nRowEnd, CharSet eCharSet ) const
2006 if ( ValidCol(nCol) )
2007 return aCol[nCol].GetMaxStringLen( nRowStart, nRowEnd, eCharSet );
2008 else
2009 return 0;
2012 xub_StrLen ScTable::GetMaxNumberStringLen( USHORT& nPrecision, SCCOL nCol,
2013 SCROW nRowStart, SCROW nRowEnd ) const
2015 if ( ValidCol(nCol) )
2016 return aCol[nCol].GetMaxNumberStringLen( nPrecision, nRowStart, nRowEnd );
2017 else
2018 return 0;
2021 void ScTable::UpdateSelectionFunction( ScFunctionData& rData,
2022 SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
2023 const ScMarkData& rMark )
2025 // Cursor neben einer Markierung nicht beruecksichtigen:
2026 //! nur noch MarkData uebergeben, Cursorposition ggf. hineinselektieren!!!
2027 BOOL bSingle = ( rMark.IsMarked() || !rMark.IsMultiMarked() );
2029 // Mehrfachselektion:
2031 SCCOL nCol;
2032 if ( rMark.IsMultiMarked() )
2033 for (nCol=0; nCol<=MAXCOL && !rData.bError; nCol++)
2034 if ( !pColFlags || !( pColFlags[nCol] & CR_HIDDEN ) )
2035 aCol[nCol].UpdateSelectionFunction( rMark, rData, pRowFlags,
2036 bSingle && ( nCol >= nStartCol && nCol <= nEndCol ),
2037 nStartRow, nEndRow );
2039 // Einfachselektion (oder Cursor) nur wenn nicht negativ (und s.o.):
2041 if ( bSingle && !rMark.IsMarkNegative() )
2042 for (nCol=nStartCol; nCol<=nEndCol && !rData.bError; nCol++)
2043 if ( !pColFlags || !( pColFlags[nCol] & CR_HIDDEN ) )
2044 aCol[nCol].UpdateAreaFunction( rData, pRowFlags, nStartRow, nEndRow );
2047 void ScTable::FindConditionalFormat( ULONG nKey, ScRangeList& rList )
2049 SCROW nStartRow = 0, nEndRow = 0;
2050 for (SCCOL nCol=0; nCol<=MAXCOL; nCol++)
2052 ScAttrIterator* pIter = aCol[nCol].CreateAttrIterator( 0, MAXROW );
2053 const ScPatternAttr* pPattern = pIter->Next( nStartRow, nEndRow );
2054 while (pPattern)
2056 if (((SfxUInt32Item&)pPattern->GetItem(ATTR_CONDITIONAL)).GetValue() == nKey)
2057 rList.Join( ScRange(nCol,nStartRow,nTab, nCol,nEndRow,nTab) );
2058 pPattern = pIter->Next( nStartRow, nEndRow );
2060 delete pIter;