LCOV - code coverage report
Current view: top level - usr/local/src/libreoffice/sc/source/core/data - table3.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 772 1183 65.3 %
Date: 2013-07-09 Functions: 57 73 78.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : 
      21             : #include <rtl/math.hxx>
      22             : #include <unotools/textsearch.hxx>
      23             : #include <svl/zforlist.hxx>
      24             : #include <svl/zformat.hxx>
      25             : #include <unotools/charclass.hxx>
      26             : #include <unotools/collatorwrapper.hxx>
      27             : #include <com/sun/star/i18n/CollatorOptions.hpp>
      28             : #include <stdlib.h>
      29             : #include <unotools/transliterationwrapper.hxx>
      30             : 
      31             : #include "table.hxx"
      32             : #include "scitems.hxx"
      33             : #include "attrib.hxx"
      34             : #include "formulacell.hxx"
      35             : #include "document.hxx"
      36             : #include "globstr.hrc"
      37             : #include "global.hxx"
      38             : #include "stlpool.hxx"
      39             : #include "compiler.hxx"
      40             : #include "patattr.hxx"
      41             : #include "subtotal.hxx"
      42             : #include "docoptio.hxx"
      43             : #include "markdata.hxx"
      44             : #include "rangelst.hxx"
      45             : #include "attarray.hxx"
      46             : #include "userlist.hxx"
      47             : #include "progress.hxx"
      48             : #include "cellform.hxx"
      49             : #include "postit.hxx"
      50             : #include "queryparam.hxx"
      51             : #include "queryentry.hxx"
      52             : #include "segmenttree.hxx"
      53             : #include "subtotalparam.hxx"
      54             : #include "docpool.hxx"
      55             : #include "cellvalue.hxx"
      56             : #include "tokenarray.hxx"
      57             : #include "mtvcellfunc.hxx"
      58             : #include "columnspanset.hxx"
      59             : 
      60             : #include <vector>
      61             : #include <boost/unordered_set.hpp>
      62             : 
      63             : using namespace ::com::sun::star;
      64             : 
      65             : namespace naturalsort {
      66             : 
      67             : using namespace ::com::sun::star::i18n;
      68             : 
      69             : /** Splits a given string into three parts: the prefix, number string, and
      70             :     the suffix.
      71             : 
      72             :     @param sWhole
      73             :     Original string to be split into pieces
      74             : 
      75             :     @param sPrefix
      76             :     Prefix string that consists of the part before the first number token
      77             : 
      78             :     @param sSuffix
      79             :     String after the last number token.  This may still contain number strings.
      80             : 
      81             :     @param fNum
      82             :     Number converted from the middle number string
      83             : 
      84             :     @return Returns TRUE if a numeral element is found in a given string, or
      85             :     FALSE if no numeral element is found.
      86             : */
      87           0 : bool SplitString( const OUString &sWhole,
      88             :     OUString &sPrefix, OUString &sSuffix, double &fNum )
      89             : {
      90           0 :     i18n::LocaleDataItem aLocaleItem = ScGlobal::pLocaleData->getLocaleItem();
      91             : 
      92             :     // Get prefix element
      93           0 :     OUString sEmpty, sUser = OUString( "-" );
      94             :     ParseResult aPRPre = ScGlobal::pCharClass->parsePredefinedToken(
      95             :         KParseType::IDENTNAME, sWhole, 0,
      96           0 :         KParseTokens::ANY_LETTER, sUser, KParseTokens::ANY_LETTER, sUser );
      97           0 :     sPrefix = sWhole.copy( 0, aPRPre.EndPos );
      98             : 
      99             :     // Return FALSE if no numeral element is found
     100           0 :     if ( aPRPre.EndPos == sWhole.getLength() )
     101           0 :         return false;
     102             : 
     103             :     // Get numeral element
     104           0 :     sUser = aLocaleItem.decimalSeparator;
     105             :     ParseResult aPRNum = ScGlobal::pCharClass->parsePredefinedToken(
     106             :         KParseType::ANY_NUMBER, sWhole, aPRPre.EndPos,
     107           0 :         KParseTokens::ANY_NUMBER, sEmpty, KParseTokens::ANY_NUMBER, sUser );
     108             : 
     109           0 :     if ( aPRNum.EndPos == aPRPre.EndPos )
     110           0 :         return false;
     111             : 
     112           0 :     fNum = aPRNum.Value;
     113           0 :     sSuffix = sWhole.copy( aPRNum.EndPos );
     114             : 
     115           0 :     return true;
     116             : }
     117             : 
     118             : /** Naturally compares two given strings.
     119             : 
     120             :     This is the main function that should be called externally.  It returns
     121             :     either 1, 0, or -1 depending on the comparison result of given two strings.
     122             : 
     123             :     @param sInput1
     124             :     Input string 1
     125             : 
     126             :     @param sInput2
     127             :     Input string 2
     128             : 
     129             :     @param bCaseSens
     130             :     Boolean value for case sensitivity
     131             : 
     132             :     @param pData
     133             :     Pointer to user defined sort list
     134             : 
     135             :     @param pCW
     136             :     Pointer to collator wrapper for normal string comparison
     137             : 
     138             :     @return Returnes 1 if sInput1 is greater, 0 if sInput1 == sInput2, and -1 if
     139             :     sInput2 is greater.
     140             : */
     141           0 : short Compare( const String &sInput1, const String &sInput2,
     142             :                const bool bCaseSens, const ScUserListData* pData, const CollatorWrapper *pCW )
     143             : {
     144           0 :     OUString sStr1( sInput1 ), sStr2( sInput2 ), sPre1, sSuf1, sPre2, sSuf2;
     145             : 
     146             :     do
     147             :     {
     148             :         double nNum1, nNum2;
     149           0 :         bool bNumFound1 = SplitString( sStr1, sPre1, sSuf1, nNum1 );
     150           0 :         bool bNumFound2 = SplitString( sStr2, sPre2, sSuf2, nNum2 );
     151             : 
     152             :         short nPreRes; // Prefix comparison result
     153           0 :         if ( pData )
     154             :         {
     155           0 :             if ( bCaseSens )
     156             :             {
     157           0 :                 if ( !bNumFound1 || !bNumFound2 )
     158           0 :                     return static_cast<short>(pData->Compare( sStr1, sStr2 ));
     159             :                 else
     160           0 :                     nPreRes = pData->Compare( sPre1, sPre2 );
     161             :             }
     162             :             else
     163             :             {
     164           0 :                 if ( !bNumFound1 || !bNumFound2 )
     165           0 :                     return static_cast<short>(pData->ICompare( sStr1, sStr2 ));
     166             :                 else
     167           0 :                     nPreRes = pData->ICompare( sPre1, sPre2 );
     168             :             }
     169             :         }
     170             :         else
     171             :         {
     172           0 :             if ( !bNumFound1 || !bNumFound2 )
     173           0 :                 return static_cast<short>(pCW->compareString( sStr1, sStr2 ));
     174             :             else
     175           0 :                 nPreRes = static_cast<short>(pCW->compareString( sPre1, sPre2 ));
     176             :         }
     177             : 
     178             :         // Prefix strings differ.  Return immediately.
     179           0 :         if ( nPreRes != 0 ) return nPreRes;
     180             : 
     181           0 :         if ( nNum1 != nNum2 )
     182             :         {
     183           0 :             if ( nNum1 < nNum2 ) return -1;
     184           0 :             return static_cast<short>( nNum1 > nNum2 );
     185             :         }
     186             : 
     187             :         // The prefix and the first numerical elements are equal, but the suffix
     188             :         // strings may still differ.  Stay in the loop.
     189             : 
     190           0 :         sStr1 = sSuf1;
     191           0 :         sStr2 = sSuf2;
     192             : 
     193             :     } while (true);
     194             : 
     195           0 :     return 0;
     196             : }
     197             : 
     198             : }
     199             : 
     200             : // STATIC DATA -----------------------------------------------------------
     201             : 
     202          60 : struct ScSortInfo
     203             : {
     204             :     ScRefCellValue maCell;
     205             :     SCCOLROW        nOrg;
     206          60 :     DECL_FIXEDMEMPOOL_NEWDEL( ScSortInfo );
     207             : };
     208          31 : IMPL_FIXEDMEMPOOL_NEWDEL( ScSortInfo )
     209             : 
     210             : // END OF STATIC DATA -----------------------------------------------------
     211             : 
     212             : 
     213             : class ScSortInfoArray
     214             : {
     215             : private:
     216             :     ScSortInfo***   pppInfo;
     217             :     SCSIZE          nCount;
     218             :     SCCOLROW        nStart;
     219             :     sal_uInt16      nUsedSorts;
     220             : 
     221             : public:
     222           7 :                 ScSortInfoArray( sal_uInt16 nSorts, SCCOLROW nInd1, SCCOLROW nInd2 ) :
     223           7 :                         pppInfo( new ScSortInfo**[nSorts]),
     224           7 :                         nCount( nInd2 - nInd1 + 1 ), nStart( nInd1 ),
     225          14 :                         nUsedSorts( nSorts )
     226             :                     {
     227          14 :                         for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
     228             :                         {
     229           7 :                             ScSortInfo** ppInfo = new ScSortInfo* [nCount];
     230          37 :                             for ( SCSIZE j = 0; j < nCount; j++ )
     231          30 :                                 ppInfo[j] = new ScSortInfo;
     232           7 :                             pppInfo[nSort] = ppInfo;
     233             :                         }
     234           7 :                     }
     235           7 :                 ~ScSortInfoArray()
     236             :                     {
     237          14 :                         for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
     238             :                         {
     239           7 :                             ScSortInfo** ppInfo = pppInfo[nSort];
     240          37 :                             for ( SCSIZE j = 0; j < nCount; j++ )
     241          30 :                                 delete ppInfo[j];
     242           7 :                             delete [] ppInfo;
     243             :                         }
     244           7 :                         delete[] pppInfo;
     245           7 :                     }
     246         224 :     ScSortInfo* Get( sal_uInt16 nSort, SCCOLROW nInd )
     247         224 :                     { return (pppInfo[nSort])[ nInd - nStart ]; }
     248          18 :     void        Swap( SCCOLROW nInd1, SCCOLROW nInd2 )
     249             :                     {
     250          18 :                         SCSIZE n1 = static_cast<SCSIZE>(nInd1 - nStart);
     251          18 :                         SCSIZE n2 = static_cast<SCSIZE>(nInd2 - nStart);
     252          36 :                         for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
     253             :                         {
     254          18 :                             ScSortInfo** ppInfo = pppInfo[nSort];
     255          18 :                             ScSortInfo* pTmp = ppInfo[n1];
     256          18 :                             ppInfo[n1] = ppInfo[n2];
     257          18 :                             ppInfo[n2] = pTmp;
     258             :                         }
     259          18 :                     }
     260          23 :     sal_uInt16      GetUsedSorts() const { return nUsedSorts; }
     261           7 :     ScSortInfo**    GetFirstArray() const { return pppInfo[0]; }
     262           7 :     SCCOLROW    GetStart() const { return nStart; }
     263           7 :     SCSIZE      GetCount() const { return nCount; }
     264             : };
     265             : 
     266           7 : ScSortInfoArray* ScTable::CreateSortInfoArray( SCCOLROW nInd1, SCCOLROW nInd2 )
     267             : {
     268           7 :     sal_uInt16 nUsedSorts = 1;
     269          14 :     while ( nUsedSorts < aSortParam.GetSortKeyCount() && aSortParam.maKeyState[nUsedSorts].bDoSort )
     270           0 :         nUsedSorts++;
     271           7 :     ScSortInfoArray* pArray = new ScSortInfoArray( nUsedSorts, nInd1, nInd2 );
     272           7 :     if ( aSortParam.bByRow )
     273             :     {
     274          14 :         for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
     275             :         {
     276           7 :             SCCOL nCol = static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField);
     277           7 :             ScColumn* pCol = &aCol[nCol];
     278          37 :             for ( SCROW nRow = nInd1; nRow <= nInd2; nRow++ )
     279             :             {
     280          30 :                 ScSortInfo* pInfo = pArray->Get( nSort, nRow );
     281          30 :                 pInfo->maCell = pCol->GetCellValue(nRow);
     282          30 :                 pInfo->nOrg = nRow;
     283             :             }
     284             :         }
     285             :     }
     286             :     else
     287             :     {
     288           0 :         for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
     289             :         {
     290           0 :             SCROW nRow = aSortParam.maKeyState[nSort].nField;
     291           0 :             for ( SCCOL nCol = static_cast<SCCOL>(nInd1);
     292           0 :                     nCol <= static_cast<SCCOL>(nInd2); nCol++ )
     293             :             {
     294           0 :                 ScSortInfo* pInfo = pArray->Get( nSort, nCol );
     295           0 :                 pInfo->maCell = GetCellValue(nCol, nRow);
     296           0 :                 pInfo->nOrg = nCol;
     297             :             }
     298             :         }
     299             :     }
     300           7 :     return pArray;
     301             : }
     302             : 
     303             : 
     304           8 : bool ScTable::IsSortCollatorGlobal() const
     305             : {
     306           8 :     return  pSortCollator == ScGlobal::GetCollator() ||
     307           8 :             pSortCollator == ScGlobal::GetCaseCollator();
     308             : }
     309             : 
     310             : 
     311           8 : void ScTable::InitSortCollator( const ScSortParam& rPar )
     312             : {
     313           8 :     if ( !rPar.aCollatorLocale.Language.isEmpty() )
     314             :     {
     315           0 :         if ( !pSortCollator || IsSortCollatorGlobal() )
     316           0 :             pSortCollator = new CollatorWrapper( comphelper::getProcessComponentContext() );
     317             :         pSortCollator->loadCollatorAlgorithm( rPar.aCollatorAlgorithm,
     318           0 :             rPar.aCollatorLocale, (rPar.bCaseSens ? 0 : SC_COLLATOR_IGNORES) );
     319             :     }
     320             :     else
     321             :     {   // SYSTEM
     322           8 :         DestroySortCollator();
     323             :         pSortCollator = (rPar.bCaseSens ? ScGlobal::GetCaseCollator() :
     324           8 :             ScGlobal::GetCollator());
     325             :     }
     326           8 : }
     327             : 
     328             : 
     329        1534 : void ScTable::DestroySortCollator()
     330             : {
     331        1534 :     if ( pSortCollator )
     332             :     {
     333           8 :         if ( !IsSortCollatorGlobal() )
     334           0 :             delete pSortCollator;
     335           8 :         pSortCollator = NULL;
     336             :     }
     337        1534 : }
     338             : 
     339             : 
     340           7 : void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress )
     341             : {
     342           7 :     bool bByRow = aSortParam.bByRow;
     343           7 :     SCSIZE nCount = pArray->GetCount();
     344           7 :     SCCOLROW nStart = pArray->GetStart();
     345           7 :     ScSortInfo** ppInfo = pArray->GetFirstArray();
     346           7 :     ::std::vector<ScSortInfo*> aTable(nCount);
     347             :     SCSIZE nPos;
     348          37 :     for ( nPos = 0; nPos < nCount; nPos++ )
     349          30 :         aTable[ppInfo[nPos]->nOrg - nStart] = ppInfo[nPos];
     350             : 
     351           7 :     SCCOLROW nDest = nStart;
     352          37 :     for ( nPos = 0; nPos < nCount; nPos++, nDest++ )
     353             :     {
     354          30 :         SCCOLROW nOrg = ppInfo[nPos]->nOrg;
     355          30 :         if ( nDest != nOrg )
     356             :         {
     357          18 :             if ( bByRow )
     358          18 :                 SwapRow( nDest, nOrg );
     359             :             else
     360           0 :                 SwapCol( static_cast<SCCOL>(nDest), static_cast<SCCOL>(nOrg) );
     361             :             // neue Position des weggeswapten eintragen
     362          18 :             ScSortInfo* p = ppInfo[nPos];
     363          18 :             p->nOrg = nDest;
     364          18 :             ::std::swap(p, aTable[nDest-nStart]);
     365          18 :             p->nOrg = nOrg;
     366          18 :             ::std::swap(p, aTable[nOrg-nStart]);
     367             :             OSL_ENSURE( p == ppInfo[nPos], "SortReorder: nOrg MisMatch" );
     368             :         }
     369          30 :         if(pProgress)
     370          16 :             pProgress->SetStateOnPercent( nPos );
     371           7 :     }
     372           7 : }
     373             : 
     374          85 : short ScTable::CompareCell(
     375             :     sal_uInt16 nSort,
     376             :     ScRefCellValue& rCell1, SCCOL nCell1Col, SCROW nCell1Row,
     377             :     ScRefCellValue& rCell2, SCCOL nCell2Col, SCROW nCell2Row ) const
     378             : {
     379          85 :     short nRes = 0;
     380             : 
     381          85 :     CellType eType1 = rCell1.meType, eType2 = rCell2.meType;
     382             : 
     383          85 :     if (!rCell1.isEmpty())
     384             :     {
     385          79 :         if (!rCell2.isEmpty())
     386             :         {
     387          77 :             bool bStr1 = ( eType1 != CELLTYPE_VALUE );
     388          77 :             if (eType1 == CELLTYPE_FORMULA && rCell1.mpFormula->IsValue())
     389           0 :                 bStr1 = false;
     390          77 :             bool bStr2 = ( eType2 != CELLTYPE_VALUE );
     391          77 :             if (eType2 == CELLTYPE_FORMULA && rCell2.mpFormula->IsValue())
     392           0 :                 bStr2 = false;
     393             : 
     394          77 :             if ( bStr1 && bStr2 )           // nur Strings untereinander als String vergleichen!
     395             :             {
     396          17 :                 OUString aStr1;
     397          34 :                 OUString aStr2;
     398          17 :                 if (eType1 == CELLTYPE_STRING)
     399          17 :                     aStr1 = *rCell1.mpString;
     400             :                 else
     401           0 :                     GetString(nCell1Col, nCell1Row, aStr1);
     402          17 :                 if (eType2 == CELLTYPE_STRING)
     403          17 :                     aStr2 = *rCell2.mpString;
     404             :                 else
     405           0 :                     GetString(nCell2Col, nCell2Row, aStr2);
     406             : 
     407          17 :                 bool bUserDef     = aSortParam.bUserDef;        // custom sort order
     408          17 :                 bool bNaturalSort = aSortParam.bNaturalSort;    // natural sort
     409          17 :                 bool bCaseSens    = aSortParam.bCaseSens;       // case sensitivity
     410             : 
     411          17 :                 if (bUserDef)
     412             :                 {
     413           0 :                     ScUserList* pList = ScGlobal::GetUserList();
     414           0 :                     const ScUserListData* pData = (*pList)[aSortParam.nUserIndex];
     415             : 
     416           0 :                     if (pData)
     417             :                     {
     418           0 :                         if ( bNaturalSort )
     419           0 :                             nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, pData, pSortCollator );
     420             :                         else
     421             :                         {
     422           0 :                             if ( bCaseSens )
     423           0 :                                 nRes = sal::static_int_cast<short>( pData->Compare(aStr1, aStr2) );
     424             :                             else
     425           0 :                                 nRes = sal::static_int_cast<short>( pData->ICompare(aStr1, aStr2) );
     426             :                         }
     427             :                     }
     428             :                     else
     429           0 :                         bUserDef = false;
     430             : 
     431             :                 }
     432          17 :                 if (!bUserDef)
     433             :                 {
     434          17 :                     if ( bNaturalSort )
     435           0 :                         nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, NULL, pSortCollator );
     436             :                     else
     437          17 :                         nRes = static_cast<short>( pSortCollator->compareString( aStr1, aStr2 ) );
     438          17 :                 }
     439             :             }
     440          60 :             else if ( bStr1 )               // String <-> Zahl
     441           9 :                 nRes = 1;                   // Zahl vorne
     442          51 :             else if ( bStr2 )               // Zahl <-> String
     443           9 :                 nRes = -1;                  // Zahl vorne
     444             :             else                            // Zahlen untereinander
     445             :             {
     446          42 :                 double nVal1 = rCell1.getValue();
     447          42 :                 double nVal2 = rCell2.getValue();
     448          42 :                 if (nVal1 < nVal2)
     449          14 :                     nRes = -1;
     450          28 :                 else if (nVal1 > nVal2)
     451          15 :                     nRes = 1;
     452             :             }
     453          77 :             if ( !aSortParam.maKeyState[nSort].bAscending )
     454          22 :                 nRes = -nRes;
     455             :         }
     456             :         else
     457           2 :             nRes = -1;
     458             :     }
     459             :     else
     460             :     {
     461           6 :         if (!rCell2.isEmpty())
     462           3 :             nRes = 1;
     463             :         else
     464           3 :             nRes = 0;                   // beide leer
     465             :     }
     466          85 :     return nRes;
     467             : }
     468             : 
     469          74 : short ScTable::Compare( ScSortInfoArray* pArray, SCCOLROW nIndex1, SCCOLROW nIndex2 ) const
     470             : {
     471             :     short nRes;
     472          74 :     sal_uInt16 nSort = 0;
     473          74 :     do
     474             :     {
     475          74 :         ScSortInfo* pInfo1 = pArray->Get( nSort, nIndex1 );
     476          74 :         ScSortInfo* pInfo2 = pArray->Get( nSort, nIndex2 );
     477          74 :         if ( aSortParam.bByRow )
     478             :             nRes = CompareCell( nSort,
     479          74 :                 pInfo1->maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), pInfo1->nOrg,
     480         148 :                 pInfo2->maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), pInfo2->nOrg );
     481             :         else
     482             :             nRes = CompareCell( nSort,
     483           0 :                 pInfo1->maCell, static_cast<SCCOL>(pInfo1->nOrg), aSortParam.maKeyState[nSort].nField,
     484           0 :                 pInfo2->maCell, static_cast<SCCOL>(pInfo2->nOrg), aSortParam.maKeyState[nSort].nField );
     485          74 :     } while ( nRes == 0 && ++nSort < pArray->GetUsedSorts() );
     486          74 :     if( nRes == 0 )
     487             :     {
     488          23 :         ScSortInfo* pInfo1 = pArray->Get( 0, nIndex1 );
     489          23 :         ScSortInfo* pInfo2 = pArray->Get( 0, nIndex2 );
     490          23 :         if( pInfo1->nOrg < pInfo2->nOrg )
     491           0 :             nRes = -1;
     492          23 :         else if( pInfo1->nOrg > pInfo2->nOrg )
     493           1 :             nRes = 1;
     494             :     }
     495          74 :     return nRes;
     496             : }
     497             : 
     498          27 : void ScTable::QuickSort( ScSortInfoArray* pArray, SCsCOLROW nLo, SCsCOLROW nHi )
     499             : {
     500          27 :     if ((nHi - nLo) == 1)
     501             :     {
     502          12 :         if (Compare(pArray, nLo, nHi) > 0)
     503           4 :             pArray->Swap( nLo, nHi );
     504             :     }
     505             :     else
     506             :     {
     507          15 :         SCsCOLROW ni = nLo;
     508          15 :         SCsCOLROW nj = nHi;
     509          21 :         do
     510             :         {
     511          43 :             while ((ni <= nHi) && (Compare(pArray, ni, nLo)) < 0)
     512           1 :                 ni++;
     513          61 :             while ((nj >= nLo) && (Compare(pArray, nLo, nj)) < 0)
     514          19 :                 nj--;
     515          21 :             if (ni <= nj)
     516             :             {
     517          17 :                 if (ni != nj)
     518          14 :                     pArray->Swap( ni, nj );
     519          17 :                 ni++;
     520          17 :                 nj--;
     521             :             }
     522             :         } while (ni < nj);
     523          15 :         if ((nj - nLo) < (nHi - ni))
     524             :         {
     525          11 :             if (nLo < nj)
     526           1 :                 QuickSort(pArray, nLo, nj);
     527          11 :             if (ni < nHi)
     528          11 :                 QuickSort(pArray, ni, nHi);
     529             :         }
     530             :         else
     531             :         {
     532           4 :             if (ni < nHi)
     533           4 :                 QuickSort(pArray, ni, nHi);
     534           4 :             if (nLo < nj)
     535           4 :                 QuickSort(pArray, nLo, nj);
     536             :         }
     537             :     }
     538          27 : }
     539             : 
     540           0 : void ScTable::SwapCol(SCCOL nCol1, SCCOL nCol2)
     541             : {
     542           0 :     SCROW nRowStart = aSortParam.nRow1;
     543           0 :     SCROW nRowEnd = aSortParam.nRow2;
     544           0 :     for (SCROW nRow = nRowStart; nRow <= nRowEnd; nRow++)
     545             :     {
     546           0 :         aCol[nCol1].SwapCell(nRow, aCol[nCol2]);
     547           0 :         if (aSortParam.bIncludePattern)
     548             :         {
     549           0 :             const ScPatternAttr* pPat1 = GetPattern(nCol1, nRow);
     550           0 :             const ScPatternAttr* pPat2 = GetPattern(nCol2, nRow);
     551           0 :             if (pPat1 != pPat2)
     552             :             {
     553           0 :                 pDocument->GetPool()->Put(*pPat1);
     554           0 :                 SetPattern(nCol1, nRow, *pPat2, true);
     555           0 :                 SetPattern(nCol2, nRow, *pPat1, true);
     556           0 :                 pDocument->GetPool()->Remove(*pPat1);
     557             :             }
     558             :         }
     559             :     }
     560             : 
     561           0 :     ScNotes aNoteMap(pDocument);
     562           0 :     ScNotes::iterator itr = maNotes.begin();
     563           0 :     while(itr != maNotes.end())
     564             :     {
     565           0 :         SCCOL nCol = itr->first.first;
     566           0 :         SCROW nRow = itr->first.second;
     567           0 :         ScPostIt* pPostIt = itr->second;
     568           0 :         ++itr;
     569             : 
     570           0 :         if(nRow >= nRowStart && nRow <= nRowEnd)
     571             :         {
     572           0 :             if (nCol == nCol1)
     573             :             {
     574           0 :                 aNoteMap.insert(nCol2, nRow, pPostIt);
     575           0 :                 maNotes.ReleaseNote(nCol, nRow);
     576             :             }
     577           0 :             else if (nCol == nCol2)
     578             :             {
     579           0 :                 aNoteMap.insert(nCol1, nRow, pPostIt);
     580           0 :                 maNotes.ReleaseNote(nCol, nRow);
     581             : 
     582             :             }
     583             :         }
     584             :     }
     585             : 
     586           0 :     itr = aNoteMap.begin();
     587           0 :     while(itr != aNoteMap.end())
     588             :     {
     589             :         //we can here assume that there is no note in the target location
     590           0 :         SCCOL nCol = itr->first.first;
     591           0 :         SCROW nRow = itr->first.second;
     592           0 :         ScPostIt* pPostIt = itr->second;
     593           0 :         ++itr;
     594             : 
     595           0 :         maNotes.insert(nCol, nRow, pPostIt);
     596           0 :         aNoteMap.ReleaseNote(nCol, nRow);
     597           0 :     }
     598           0 : }
     599             : 
     600          18 : void ScTable::SwapRow(SCROW nRow1, SCROW nRow2)
     601             : {
     602          18 :     SCCOL nColStart = aSortParam.nCol1;
     603          18 :     SCCOL nColEnd = aSortParam.nCol2;
     604          63 :     for (SCCOL nCol = nColStart; nCol <= nColEnd; nCol++)
     605             :     {
     606          45 :         aCol[nCol].SwapRow(nRow1, nRow2);
     607          45 :         if (aSortParam.bIncludePattern)
     608             :         {
     609          45 :             const ScPatternAttr* pPat1 = GetPattern(nCol, nRow1);
     610          45 :             const ScPatternAttr* pPat2 = GetPattern(nCol, nRow2);
     611          45 :             if (pPat1 != pPat2)
     612             :             {
     613           0 :                 pDocument->GetPool()->Put(*pPat1);
     614           0 :                 SetPattern(nCol, nRow1, *pPat2, true);
     615           0 :                 SetPattern(nCol, nRow2, *pPat1, true);
     616           0 :                 pDocument->GetPool()->Remove(*pPat1);
     617             :             }
     618             :         }
     619             :     }
     620          18 :     if (bGlobalKeepQuery)
     621             :     {
     622           0 :         bool bRow1Hidden = RowHidden(nRow1);
     623           0 :         bool bRow2Hidden = RowHidden(nRow2);
     624           0 :         SetRowHidden(nRow1, nRow1, bRow2Hidden);
     625           0 :         SetRowHidden(nRow2, nRow2, bRow1Hidden);
     626             : 
     627           0 :         bool bRow1Filtered = RowFiltered(nRow1);
     628           0 :         bool bRow2Filtered = RowFiltered(nRow2);
     629           0 :         SetRowFiltered(nRow1, nRow1, bRow2Filtered);
     630           0 :         SetRowFiltered(nRow2, nRow2, bRow1Filtered);
     631             :     }
     632             : 
     633          18 :     ScNotes aNoteMap(pDocument);
     634          18 :     ScNotes::iterator itr = maNotes.begin();
     635          38 :     while(itr != maNotes.end())
     636             :     {
     637           2 :         SCCOL nCol = itr->first.first;
     638           2 :         SCROW nRow = itr->first.second;
     639           2 :         ScPostIt* pPostIt = itr->second;
     640           2 :         ++itr;
     641             : 
     642           2 :         if( nCol >= nColStart && nCol <= nColEnd )
     643             :         {
     644           2 :             if (nRow == nRow1)
     645             :             {
     646           0 :                 aNoteMap.insert(nCol, nRow2, pPostIt);
     647           0 :                 maNotes.ReleaseNote(nCol, nRow);
     648             :             }
     649           2 :             else if (nRow == nRow2)
     650             :             {
     651           1 :                 aNoteMap.insert(nCol, nRow1, pPostIt);
     652           1 :                 maNotes.ReleaseNote(nCol, nRow);
     653             :             }
     654             :         }
     655             :     }
     656             : 
     657          18 :     itr = aNoteMap.begin();
     658          37 :     while(itr != aNoteMap.end())
     659             :     {
     660             :         //we can here assume that there is no note in the target location
     661           1 :         SCCOL nCol = itr->first.first;
     662           1 :         SCROW nRow = itr->first.second;
     663           1 :         ScPostIt* pPostIt = itr->second;
     664           1 :         ++itr;
     665             : 
     666           1 :         maNotes.insert(nCol, nRow, pPostIt);
     667           1 :         aNoteMap.ReleaseNote(nCol, nRow);
     668          18 :     }
     669          18 : }
     670             : 
     671          11 : short ScTable::Compare(SCCOLROW nIndex1, SCCOLROW nIndex2) const
     672             : {
     673             :     short nRes;
     674          11 :     sal_uInt16 nSort = 0;
     675          11 :     const sal_uInt32 nMaxSorts = aSortParam.GetSortKeyCount();
     676          11 :     if (aSortParam.bByRow)
     677             :     {
     678          11 :         do
     679             :         {
     680          11 :             SCCOL nCol = static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField);
     681          11 :             ScRefCellValue aCell1 = aCol[nCol].GetCellValue(nIndex1);
     682          22 :             ScRefCellValue aCell2 = aCol[nCol].GetCellValue(nIndex2);
     683          22 :             nRes = CompareCell(nSort, aCell1, nCol, nIndex1, aCell2, nCol, nIndex2);
     684          11 :         } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort );
     685             :     }
     686             :     else
     687             :     {
     688           0 :         do
     689             :         {
     690           0 :             SCROW nRow = aSortParam.maKeyState[nSort].nField;
     691           0 :             ScRefCellValue aCell1 = aCol[nIndex1].GetCellValue(nRow);
     692           0 :             ScRefCellValue aCell2 = aCol[nIndex2].GetCellValue(nRow);
     693             :             nRes = CompareCell( nSort, aCell1, static_cast<SCCOL>(nIndex1),
     694           0 :                     nRow, aCell2, static_cast<SCCOL>(nIndex2), nRow );
     695           0 :         } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort );
     696             :     }
     697          11 :     return nRes;
     698             : }
     699             : 
     700           8 : bool ScTable::IsSorted( SCCOLROW nStart, SCCOLROW nEnd ) const   // ueber aSortParam
     701             : {
     702          12 :     for (SCCOLROW i=nStart; i<nEnd; i++)
     703             :     {
     704          11 :         if (Compare( i, i+1 ) > 0)
     705           7 :             return false;
     706             :     }
     707           1 :     return true;
     708             : }
     709             : 
     710           0 : void ScTable::DecoladeRow( ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2 )
     711             : {
     712             :     SCROW nRow;
     713           0 :     SCROW nMax = nRow2 - nRow1;
     714           0 :     for (SCROW i = nRow1; (i + 4) <= nRow2; i += 4)
     715             :     {
     716           0 :         nRow = rand() % nMax;
     717           0 :         pArray->Swap(i, nRow1 + nRow);
     718             :     }
     719           0 : }
     720             : 
     721           8 : void ScTable::Sort(const ScSortParam& rSortParam, bool bKeepQuery, ScProgress* pProgress)
     722             : {
     723           8 :     aSortParam = rSortParam;
     724           8 :     InitSortCollator( rSortParam );
     725           8 :     bGlobalKeepQuery = bKeepQuery;
     726           8 :     if (rSortParam.bByRow)
     727             :     {
     728           8 :         SCROW nLastRow = 0;
     729          31 :         for (SCCOL nCol = aSortParam.nCol1; nCol <= aSortParam.nCol2; nCol++)
     730          23 :             nLastRow = std::max(nLastRow, aCol[nCol].GetLastDataPos());
     731           8 :         nLastRow = std::min(nLastRow, aSortParam.nRow2);
     732             :         SCROW nRow1 = (rSortParam.bHasHeader ?
     733           8 :             aSortParam.nRow1 + 1 : aSortParam.nRow1);
     734           8 :         if (!IsSorted(nRow1, nLastRow))
     735             :         {
     736           7 :             if(pProgress)
     737           4 :                 pProgress->SetState( 0, nLastRow-nRow1 );
     738           7 :             ScSortInfoArray* pArray = CreateSortInfoArray( nRow1, nLastRow );
     739           7 :             if ( nLastRow - nRow1 > 255 )
     740           0 :                 DecoladeRow( pArray, nRow1, nLastRow );
     741           7 :             QuickSort( pArray, nRow1, nLastRow );
     742           7 :             SortReorder( pArray, pProgress );
     743           7 :             delete pArray;
     744             :             // #i59745# update position of caption objects of cell notes
     745           7 :             ScNoteUtil::UpdateCaptionPositions( *pDocument, ScRange( aSortParam.nCol1, nRow1, nTab, aSortParam.nCol2, nLastRow, nTab ) );
     746             :         }
     747             :     }
     748             :     else
     749             :     {
     750             :         SCCOL nLastCol;
     751           0 :         for (nLastCol = aSortParam.nCol2;
     752           0 :              (nLastCol > aSortParam.nCol1) && aCol[nLastCol].IsEmptyBlock(aSortParam.nRow1, aSortParam.nRow2); nLastCol--)
     753             :         {
     754             :         }
     755             :         SCCOL nCol1 = (rSortParam.bHasHeader ?
     756           0 :             aSortParam.nCol1 + 1 : aSortParam.nCol1);
     757           0 :         if (!IsSorted(nCol1, nLastCol))
     758             :         {
     759           0 :             if(pProgress)
     760           0 :                 pProgress->SetState( 0, nLastCol-nCol1 );
     761           0 :             ScSortInfoArray* pArray = CreateSortInfoArray( nCol1, nLastCol );
     762           0 :             QuickSort( pArray, nCol1, nLastCol );
     763           0 :             SortReorder( pArray, pProgress );
     764           0 :             delete pArray;
     765             :             // #i59745# update position of caption objects of cell notes
     766           0 :             ScNoteUtil::UpdateCaptionPositions( *pDocument, ScRange( nCol1, aSortParam.nRow1, nTab, nLastCol, aSortParam.nRow2, nTab ) );
     767             :         }
     768             :     }
     769           8 :     DestroySortCollator();
     770           8 : }
     771             : 
     772             : namespace {
     773             : 
     774             : class SubTotalRowFinder
     775             : {
     776             :     const ScTable& mrTab;
     777             :     const ScSubTotalParam& mrParam;
     778             : 
     779             : public:
     780           8 :     SubTotalRowFinder(const ScTable& rTab, const ScSubTotalParam& rParam) :
     781           8 :         mrTab(rTab), mrParam(rParam) {}
     782             : 
     783           3 :     bool operator() (size_t nRow, const ScFormulaCell* pCell)
     784             :     {
     785           3 :         if (!pCell->IsSubTotal())
     786           0 :             return false;
     787             : 
     788           3 :         SCCOL nStartCol = mrParam.nCol1;
     789           3 :         SCCOL nEndCol = mrParam.nCol2;
     790             : 
     791        3075 :         for (SCCOL i = 0; i <= MAXCOL; ++i)
     792             :         {
     793        3072 :             if (nStartCol <= i && i <= nEndCol)
     794          12 :                 continue;
     795             : 
     796        3060 :             if (mrTab.HasData(i, nRow))
     797           0 :                 return true;
     798             :         }
     799             : 
     800           3 :         return false;
     801             :     }
     802             : };
     803             : 
     804             : }
     805             : 
     806           2 : bool ScTable::TestRemoveSubTotals( const ScSubTotalParam& rParam )
     807             : {
     808           2 :     SCCOL nStartCol = rParam.nCol1;
     809           2 :     SCROW nStartRow = rParam.nRow1 + 1;     // Header
     810           2 :     SCCOL nEndCol   = rParam.nCol2;
     811           2 :     SCROW nEndRow    = rParam.nRow2;
     812             : 
     813          10 :     for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
     814             :     {
     815           8 :         const sc::CellStoreType& rCells = aCol[nCol].maCells;
     816           8 :         SubTotalRowFinder aFunc(*this, rParam);
     817             :         std::pair<sc::CellStoreType::const_iterator,size_t> aPos =
     818           8 :             sc::FindFormula(rCells, nStartRow, nEndRow, aFunc);
     819           8 :         if (aPos.first != rCells.end())
     820           0 :             return true;
     821             :     }
     822           2 :     return false;
     823             : }
     824             : 
     825             : namespace {
     826             : 
     827           4 : class RemoveSubTotalsHandler
     828             : {
     829             :     std::vector<SCROW> maRemoved;
     830             : public:
     831             : 
     832           3 :     void operator() (size_t nRow, const ScFormulaCell* p)
     833             :     {
     834           3 :         if (p->IsSubTotal())
     835           3 :             maRemoved.push_back(nRow);
     836           3 :     }
     837             : 
     838           2 :     void getRows(std::vector<SCROW>& rRows)
     839             :     {
     840             :         // Sort and remove duplicates.
     841           2 :         std::sort(maRemoved.begin(), maRemoved.end());
     842           2 :         std::vector<SCROW>::iterator it = std::unique(maRemoved.begin(), maRemoved.end());
     843           2 :         maRemoved.erase(it, maRemoved.end());
     844             : 
     845           2 :         maRemoved.swap(rRows);
     846           2 :     }
     847             : };
     848             : 
     849             : }
     850             : 
     851           2 : void ScTable::RemoveSubTotals( ScSubTotalParam& rParam )
     852             : {
     853           2 :     SCCOL nStartCol = rParam.nCol1;
     854           2 :     SCROW nStartRow = rParam.nRow1 + 1;     // Header
     855           2 :     SCCOL nEndCol   = rParam.nCol2;
     856           2 :     SCROW nEndRow    = rParam.nRow2;            // wird veraendert
     857             : 
     858           2 :     RemoveSubTotalsHandler aFunc;
     859          10 :     for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
     860             :     {
     861           8 :         const sc::CellStoreType& rCells = aCol[nCol].maCells;
     862           8 :         sc::ParseFormula(rCells.begin(), rCells, nStartRow, nEndRow, aFunc);
     863             :     }
     864             : 
     865           4 :     std::vector<SCROW> aRows;
     866           2 :     aFunc.getRows(aRows);
     867             : 
     868           2 :     std::vector<SCROW>::reverse_iterator it = aRows.rbegin(), itEnd = aRows.rend();
     869           5 :     for (; it != itEnd; ++it)
     870             :     {
     871           3 :         SCROW nRow = *it;
     872           3 :         RemoveRowBreak(nRow+1, false, true);
     873           3 :         pDocument->DeleteRow(0, nTab, MAXCOL, nTab, nRow, 1);
     874             :     }
     875             : 
     876           4 :     rParam.nRow2 -= aRows.size();
     877           2 : }
     878             : 
     879             : //  harte Zahlenformate loeschen (fuer Ergebnisformeln)
     880             : 
     881           0 : static void lcl_RemoveNumberFormat( ScTable* pTab, SCCOL nCol, SCROW nRow )
     882             : {
     883           0 :     const ScPatternAttr* pPattern = pTab->GetPattern( nCol, nRow );
     884           0 :     if ( pPattern->GetItemSet().GetItemState( ATTR_VALUE_FORMAT, false )
     885             :             == SFX_ITEM_SET )
     886             :     {
     887           0 :         ScPatternAttr aNewPattern( *pPattern );
     888           0 :         SfxItemSet& rSet = aNewPattern.GetItemSet();
     889           0 :         rSet.ClearItem( ATTR_VALUE_FORMAT );
     890           0 :         rSet.ClearItem( ATTR_LANGUAGE_FORMAT );
     891           0 :         pTab->SetPattern( nCol, nRow, aNewPattern, true );
     892             :     }
     893           0 : }
     894             : 
     895             : 
     896             : // at least MSC needs this at linkage level to be able to use it in a template
     897             : typedef struct lcl_ScTable_DoSubTotals_RowEntry
     898             : {
     899             :     sal_uInt16  nGroupNo;
     900             :     SCROW   nSubStartRow;
     901             :     SCROW   nDestRow;
     902             :     SCROW   nFuncStart;
     903             :     SCROW   nFuncEnd;
     904             : } RowEntry;
     905             : 
     906             : //      neue Zwischenergebnisse
     907             : //      rParam.nRow2 wird veraendert !
     908             : 
     909           1 : bool ScTable::DoSubTotals( ScSubTotalParam& rParam )
     910             : {
     911           1 :     SCCOL nStartCol = rParam.nCol1;
     912           1 :     SCROW nStartRow = rParam.nRow1 + 1;     // Header
     913           1 :     SCCOL nEndCol   = rParam.nCol2;
     914           1 :     SCROW nEndRow    = rParam.nRow2;            // wird veraendert
     915             :     sal_uInt16 i;
     916             : 
     917             :     //  Leerzeilen am Ende weglassen,
     918             :     //  damit alle Ueberlaeufe (MAXROW) bei InsertRow gefunden werden (#35180#)
     919             :     //  Wenn sortiert wurde, sind alle Leerzeilen am Ende.
     920           1 :     SCSIZE nEmpty = GetEmptyLinesInBlock( nStartCol, nStartRow, nEndCol, nEndRow, DIR_BOTTOM );
     921           1 :     nEndRow -= nEmpty;
     922             : 
     923           1 :     sal_uInt16 nLevelCount = 0;             // Anzahl Gruppierungen
     924           1 :     bool bDoThis = true;
     925           3 :     for (i=0; i<MAXSUBTOTAL && bDoThis; i++)
     926           2 :         if (rParam.bGroupActive[i])
     927           1 :             nLevelCount = i+1;
     928             :         else
     929           1 :             bDoThis = false;
     930             : 
     931           1 :     if (nLevelCount==0)                 // nichts tun
     932           0 :         return true;
     933             : 
     934           1 :     SCCOL*          nGroupCol = rParam.nField;  // Spalten nach denen
     935             :                                                 // gruppiert wird
     936             : 
     937             :     //  Durch (leer) als eigene Kategorie muss immer auf
     938             :     //  Teilergebniszeilen aus den anderen Spalten getestet werden
     939             :     //  (frueher nur, wenn eine Spalte mehrfach vorkam)
     940           1 :     bool bTestPrevSub = ( nLevelCount > 1 );
     941             : 
     942           1 :     OUString  aSubString;
     943           2 :     String  aOutString;
     944             : 
     945           1 :     bool bIgnoreCase = !rParam.bCaseSens;
     946             : 
     947             :     String *pCompString[MAXSUBTOTAL];               // Pointer wegen Compiler-Problemen
     948           4 :     for (i=0; i<MAXSUBTOTAL; i++)
     949           3 :         pCompString[i] = new String;
     950             : 
     951             :                                 //! sortieren?
     952             : 
     953           1 :     ScStyleSheet* pStyle = (ScStyleSheet*) pDocument->GetStyleSheetPool()->Find(
     954           1 :                                 ScGlobal::GetRscString(STR_STYLENAME_RESULT), SFX_STYLE_FAMILY_PARA );
     955             : 
     956           1 :     bool bSpaceLeft = true;                                         // Erfolg beim Einfuegen?
     957             : 
     958             :     // For performance reasons collect formula entries so their
     959             :     // references don't have to be tested for updates each time a new row is
     960             :     // inserted
     961             :     RowEntry aRowEntry;
     962           2 :     ::std::vector< RowEntry > aRowVector;
     963             : 
     964           3 :     for (sal_uInt16 nLevel=0; nLevel<=nLevelCount && bSpaceLeft; nLevel++)      // incl. Gesamtergebnis
     965             :     {
     966           2 :         bool bTotal = ( nLevel == nLevelCount );
     967           2 :         aRowEntry.nGroupNo = bTotal ? 0 : (nLevelCount-nLevel-1);
     968             : 
     969             :         // how many results per level
     970           2 :         SCCOL nResCount         = rParam.nSubTotals[aRowEntry.nGroupNo];
     971             :         // result functions
     972           2 :         ScSubTotalFunc* eResFunc = rParam.pFunctions[aRowEntry.nGroupNo];
     973             : 
     974           2 :         if (nResCount > 0)                                      // sonst nur sortieren
     975             :         {
     976           4 :             for (i=0; i<=aRowEntry.nGroupNo; i++)
     977             :             {
     978           2 :                 GetString( nGroupCol[i], nStartRow, aSubString );
     979           2 :                 if ( bIgnoreCase )
     980           2 :                     *pCompString[i] = ScGlobal::pCharClass->uppercase( aSubString );
     981             :                 else
     982           0 :                     *pCompString[i] = aSubString;
     983             :             }                                                   // aSubString bleibt auf dem letzten stehen
     984             : 
     985           2 :             bool bBlockVis = false;             // Gruppe eingeblendet?
     986           2 :             aRowEntry.nSubStartRow = nStartRow;
     987          12 :             for (SCROW nRow=nStartRow; nRow<=nEndRow+1 && bSpaceLeft; nRow++)
     988             :             {
     989             :                 bool bChanged;
     990          10 :                 if (nRow>nEndRow)
     991           2 :                     bChanged = true;
     992             :                 else
     993             :                 {
     994           8 :                     bChanged = false;
     995           8 :                     if (!bTotal)
     996             :                     {
     997           3 :                         OUString aString;
     998           6 :                         for (i=0; i<=aRowEntry.nGroupNo && !bChanged; i++)
     999             :                         {
    1000           3 :                             GetString( nGroupCol[i], nRow, aString );
    1001           3 :                             if (bIgnoreCase)
    1002           3 :                                 aString = ScGlobal::pCharClass->uppercase(aString);
    1003             :                             //  wenn sortiert, ist "leer" eine eigene Gruppe
    1004             :                             //  sonst sind leere Zellen unten erlaubt
    1005           9 :                             bChanged = ( ( !aString.isEmpty() || rParam.bDoSort ) &&
    1006           9 :                                             aString != OUString(*pCompString[i]) );
    1007             :                         }
    1008           3 :                         if ( bChanged && bTestPrevSub )
    1009             :                         {
    1010             :                             // No group change on rows that will contain subtotal formulas
    1011           0 :                             for ( ::std::vector< RowEntry >::const_iterator
    1012           0 :                                     iEntry( aRowVector.begin());
    1013           0 :                                     iEntry != aRowVector.end(); ++iEntry)
    1014             :                             {
    1015           0 :                                 if ( iEntry->nDestRow == nRow )
    1016             :                                 {
    1017           0 :                                     bChanged = false;
    1018           0 :                                     break;
    1019             :                                 }
    1020             :                             }
    1021           3 :                         }
    1022             :                     }
    1023             :                 }
    1024          10 :                 if ( bChanged )
    1025             :                 {
    1026           3 :                     aRowEntry.nDestRow   = nRow;
    1027           3 :                     aRowEntry.nFuncStart = aRowEntry.nSubStartRow;
    1028           3 :                     aRowEntry.nFuncEnd   = nRow-1;
    1029             : 
    1030             :                     bSpaceLeft = pDocument->InsertRow( 0, nTab, MAXCOL, nTab,
    1031           3 :                             aRowEntry.nDestRow, 1 );
    1032           3 :                     DBShowRow( aRowEntry.nDestRow, bBlockVis );
    1033           3 :                     bBlockVis = false;
    1034           3 :                     if ( rParam.bPagebreak && nRow < MAXROW &&
    1035           0 :                             aRowEntry.nSubStartRow != nStartRow && nLevel == 0)
    1036           0 :                         SetRowBreak(aRowEntry.nSubStartRow, false, true);
    1037             : 
    1038           3 :                     if (bSpaceLeft)
    1039             :                     {
    1040          18 :                         for ( ::std::vector< RowEntry >::iterator iMove(
    1041           3 :                                     aRowVector.begin() );
    1042          12 :                                 iMove != aRowVector.end(); ++iMove)
    1043             :                         {
    1044           3 :                             if ( aRowEntry.nDestRow <= iMove->nSubStartRow )
    1045           0 :                                 ++iMove->nSubStartRow;
    1046           3 :                             if ( aRowEntry.nDestRow <= iMove->nDestRow )
    1047           0 :                                 ++iMove->nDestRow;
    1048           3 :                             if ( aRowEntry.nDestRow <= iMove->nFuncStart )
    1049           0 :                                 ++iMove->nFuncStart;
    1050           3 :                             if ( aRowEntry.nDestRow <= iMove->nFuncEnd )
    1051           0 :                                 ++iMove->nFuncEnd;
    1052             :                         }
    1053             :                         // collect formula positions
    1054           3 :                         aRowVector.push_back( aRowEntry );
    1055             : 
    1056           3 :                         if (bTotal)     // "Gesamtergebnis"
    1057           1 :                             aOutString = ScGlobal::GetRscString( STR_TABLE_GESAMTERGEBNIS );
    1058             :                         else
    1059             :                         {               // " Ergebnis"
    1060           2 :                             aOutString = aSubString;
    1061           2 :                             if (!aOutString.Len())
    1062           1 :                                 aOutString = ScGlobal::GetRscString( STR_EMPTYDATA );
    1063           2 :                             aOutString += ' ';
    1064           2 :                             sal_uInt16 nStrId = STR_TABLE_ERGEBNIS;
    1065           2 :                             if ( nResCount == 1 )
    1066           2 :                                 switch ( eResFunc[0] )
    1067             :                                 {
    1068           0 :                                     case SUBTOTAL_FUNC_AVE:     nStrId = STR_FUN_TEXT_AVG;      break;
    1069             :                                     case SUBTOTAL_FUNC_CNT:
    1070           0 :                                     case SUBTOTAL_FUNC_CNT2:    nStrId = STR_FUN_TEXT_COUNT;    break;
    1071           0 :                                     case SUBTOTAL_FUNC_MAX:     nStrId = STR_FUN_TEXT_MAX;      break;
    1072           0 :                                     case SUBTOTAL_FUNC_MIN:     nStrId = STR_FUN_TEXT_MIN;      break;
    1073           0 :                                     case SUBTOTAL_FUNC_PROD:    nStrId = STR_FUN_TEXT_PRODUCT;  break;
    1074             :                                     case SUBTOTAL_FUNC_STD:
    1075           0 :                                     case SUBTOTAL_FUNC_STDP:    nStrId = STR_FUN_TEXT_STDDEV;   break;
    1076           2 :                                     case SUBTOTAL_FUNC_SUM:     nStrId = STR_FUN_TEXT_SUM;      break;
    1077             :                                     case SUBTOTAL_FUNC_VAR:
    1078           0 :                                     case SUBTOTAL_FUNC_VARP:    nStrId = STR_FUN_TEXT_VAR;      break;
    1079             :                                     default:
    1080             :                                     {
    1081             :                                         // added to avoid warnings
    1082             :                                     }
    1083             :                                 }
    1084           2 :                             aOutString += ScGlobal::GetRscString( nStrId );
    1085             :                         }
    1086           3 :                         SetString( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, nTab, aOutString );
    1087           3 :                         ApplyStyle( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, *pStyle );
    1088             : 
    1089           3 :                         ++nRow;
    1090           3 :                         ++nEndRow;
    1091           3 :                         aRowEntry.nSubStartRow = nRow;
    1092           6 :                         for (i=0; i<=aRowEntry.nGroupNo; i++)
    1093             :                         {
    1094           3 :                             GetString( nGroupCol[i], nRow, aSubString );
    1095           3 :                             if ( bIgnoreCase )
    1096           3 :                                 *pCompString[i] = ScGlobal::pCharClass->uppercase( aSubString );
    1097             :                             else
    1098           0 :                                 *pCompString[i] = aSubString;
    1099             :                         }
    1100             :                     }
    1101             :                 }
    1102          10 :                 bBlockVis = !RowFiltered(nRow);
    1103             :             }
    1104             :         }
    1105             :     }
    1106             : 
    1107             :     // now insert the formulas
    1108             :     ScComplexRefData aRef;
    1109           1 :     aRef.InitFlags();
    1110           1 :     aRef.Ref1.nTab = nTab;
    1111           1 :     aRef.Ref2.nTab = nTab;
    1112          12 :     for ( ::std::vector< RowEntry >::const_iterator iEntry( aRowVector.begin());
    1113           8 :             iEntry != aRowVector.end(); ++iEntry)
    1114             :     {
    1115           3 :         SCCOL nResCount         = rParam.nSubTotals[iEntry->nGroupNo];
    1116           3 :         SCCOL* nResCols         = rParam.pSubTotals[iEntry->nGroupNo];
    1117           3 :         ScSubTotalFunc* eResFunc = rParam.pFunctions[iEntry->nGroupNo];
    1118           6 :         for ( SCCOL nResult=0; nResult < nResCount; ++nResult )
    1119             :         {
    1120           3 :             aRef.Ref1.nCol = nResCols[nResult];
    1121           3 :             aRef.Ref1.nRow = iEntry->nFuncStart;
    1122           3 :             aRef.Ref2.nCol = nResCols[nResult];
    1123           3 :             aRef.Ref2.nRow = iEntry->nFuncEnd;
    1124             : 
    1125           3 :             ScTokenArray aArr;
    1126           3 :             aArr.AddOpCode( ocSubTotal );
    1127           3 :             aArr.AddOpCode( ocOpen );
    1128           3 :             aArr.AddDouble( (double) eResFunc[nResult] );
    1129           3 :             aArr.AddOpCode( ocSep );
    1130           3 :             aArr.AddDoubleReference( aRef );
    1131           3 :             aArr.AddOpCode( ocClose );
    1132           3 :             aArr.AddOpCode( ocStop );
    1133             :             ScFormulaCell* pCell = new ScFormulaCell(
    1134           3 :                 pDocument, ScAddress(nResCols[nResult], iEntry->nDestRow, nTab), &aArr);
    1135             : 
    1136           3 :             SetFormulaCell(nResCols[nResult], iEntry->nDestRow, pCell);
    1137             : 
    1138           3 :             if ( nResCols[nResult] != nGroupCol[iEntry->nGroupNo] )
    1139             :             {
    1140           0 :                 ApplyStyle( nResCols[nResult], iEntry->nDestRow, *pStyle );
    1141             : 
    1142             :                 //  Zahlformat loeschen
    1143           0 :                 lcl_RemoveNumberFormat( this, nResCols[nResult], iEntry->nDestRow );
    1144             :             }
    1145           3 :         }
    1146             : 
    1147             :     }
    1148             : 
    1149             :     //!     je nach Einstellung Zwischensummen-Zeilen nach oben verschieben ?
    1150             : 
    1151             :     //!     Outlines direkt erzeugen?
    1152             : 
    1153           1 :     if (bSpaceLeft)
    1154           1 :         DoAutoOutline( nStartCol, nStartRow, nEndCol, nEndRow );
    1155             : 
    1156           4 :     for (i=0; i<MAXSUBTOTAL; i++)
    1157           3 :         delete pCompString[i];
    1158             : 
    1159           1 :     rParam.nRow2 = nEndRow;                 // neues Ende
    1160           2 :     return bSpaceLeft;
    1161             : }
    1162             : 
    1163           6 : void ScTable::MarkSubTotalCells(
    1164             :     sc::ColumnSpanSet& rSet, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, bool bVal ) const
    1165             : {
    1166           6 :     if (!ValidCol(nCol1) || !ValidCol(nCol2))
    1167           0 :         return;
    1168             : 
    1169             :     // Pick up all subtotal formula cells.
    1170          12 :     for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
    1171           6 :         aCol[nCol].MarkSubTotalCells(rSet, nRow1, nRow2, bVal);
    1172             : 
    1173             :     // Pick up all filtered rows.
    1174             :     ScFlatBoolRowSegments::RangeData aFilteredSpan;
    1175           6 :     SCROW nRow = nRow1;
    1176          18 :     while (nRow <= nRow2)
    1177             :     {
    1178           6 :         if (!mpFilteredRows->getRangeData(nRow, aFilteredSpan))
    1179             :             // Failed for whatever reason.
    1180           0 :             return;
    1181             : 
    1182           6 :         if (aFilteredSpan.mbValue)
    1183             :         {
    1184             :             // Filtered span found.
    1185           0 :             for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
    1186           0 :                 rSet.set(nTab, nCol, nRow, aFilteredSpan.mnRow2, bVal);
    1187             :         }
    1188             : 
    1189           6 :         nRow = aFilteredSpan.mnRow2 + 1;
    1190             :     }
    1191             : }
    1192             : 
    1193             : namespace {
    1194             : 
    1195             : class QueryEvaluator
    1196             : {
    1197             :     const ScDocument& mrDoc;
    1198             :     const ScTable& mrTab;
    1199             :     const ScQueryParam& mrParam;
    1200             :     const bool* mpTestEqualCondition;
    1201             :     utl::TransliterationWrapper* mpTransliteration;
    1202             :     CollatorWrapper* mpCollator;
    1203             :     const bool mbMatchWholeCell;
    1204             : 
    1205         625 :     bool isPartialTextMatchOp(const ScQueryEntry& rEntry) const
    1206             :     {
    1207         625 :         switch (rEntry.eOp)
    1208             :         {
    1209             :             // these operators can only be used with textural comparisons.
    1210             :             case SC_CONTAINS:
    1211             :             case SC_DOES_NOT_CONTAIN:
    1212             :             case SC_BEGINS_WITH:
    1213             :             case SC_ENDS_WITH:
    1214             :             case SC_DOES_NOT_BEGIN_WITH:
    1215             :             case SC_DOES_NOT_END_WITH:
    1216           0 :                 return true;
    1217             :             default:
    1218             :                 ;
    1219             :         }
    1220         625 :         return false;
    1221             :     }
    1222             : 
    1223         424 :     bool isTextMatchOp(const ScQueryEntry& rEntry) const
    1224             :     {
    1225         424 :         if (isPartialTextMatchOp(rEntry))
    1226           0 :             return true;
    1227             : 
    1228         424 :         switch (rEntry.eOp)
    1229             :         {
    1230             :             // these operators can be used for either textural or value comparison.
    1231             :             case SC_EQUAL:
    1232             :             case SC_NOT_EQUAL:
    1233         350 :                 return true;
    1234             :             default:
    1235             :                 ;
    1236             :         }
    1237          74 :         return false;
    1238             :     }
    1239             : 
    1240         201 :     bool isRealRegExp(const ScQueryEntry& rEntry) const
    1241             :     {
    1242         201 :         if (!mrParam.bRegExp)
    1243         169 :             return false;
    1244             : 
    1245          32 :         return isTextMatchOp(rEntry);
    1246             :     }
    1247             : 
    1248         201 :     bool isTestRegExp(const ScQueryEntry& rEntry) const
    1249             :     {
    1250         201 :         if (!mpTestEqualCondition)
    1251         178 :             return false;
    1252             : 
    1253          23 :         if (!mrParam.bRegExp)
    1254          23 :             return false;
    1255             : 
    1256           0 :         return (rEntry.eOp == SC_LESS_EQUAL || rEntry.eOp == SC_GREATER_EQUAL);
    1257             :     }
    1258             : 
    1259             : public:
    1260         513 :     QueryEvaluator(const ScDocument& rDoc, const ScTable& rTab, const ScQueryParam& rParam,
    1261             :                    const bool* pTestEqualCondition) :
    1262             :         mrDoc(rDoc),
    1263             :         mrTab(rTab),
    1264             :         mrParam(rParam),
    1265             :         mpTestEqualCondition(pTestEqualCondition),
    1266         513 :         mbMatchWholeCell(rDoc.GetDocOptions().IsMatchWholeCell())
    1267             :     {
    1268         513 :         if (rParam.bCaseSens)
    1269             :         {
    1270           0 :             mpTransliteration = ScGlobal::GetCaseTransliteration();
    1271           0 :             mpCollator = ScGlobal::GetCaseCollator();
    1272             :         }
    1273             :         else
    1274             :         {
    1275         513 :             mpTransliteration = ScGlobal::GetpTransliteration();
    1276         513 :             mpCollator = ScGlobal::GetCollator();
    1277             :         }
    1278         513 :     }
    1279             : 
    1280         534 :     bool isQueryByValue(
    1281             :         const ScQueryEntry::Item& rItem, SCCOL nCol, SCROW nRow, ScRefCellValue& rCell)
    1282             :     {
    1283         534 :         if (rItem.meType == ScQueryEntry::ByString)
    1284         190 :             return false;
    1285             : 
    1286         344 :         if (!rCell.isEmpty())
    1287             :         {
    1288         342 :             if (rCell.meType == CELLTYPE_FORMULA && rCell.mpFormula->GetErrCode())
    1289             :                 // Error values are compared as string.
    1290           0 :                 return false;
    1291             : 
    1292         342 :             return rCell.hasNumeric();
    1293             :         }
    1294             : 
    1295           2 :         return mrTab.HasValueData(nCol, nRow);
    1296             :     }
    1297             : 
    1298         223 :     bool isQueryByString(
    1299             :         const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem,
    1300             :         SCCOL nCol, SCROW nRow, ScRefCellValue& rCell)
    1301             :     {
    1302         223 :         if (isTextMatchOp(rEntry))
    1303         175 :             return true;
    1304             : 
    1305          48 :         if (rItem.meType != ScQueryEntry::ByString)
    1306          17 :             return false;
    1307             : 
    1308          31 :         if (!rCell.isEmpty())
    1309          29 :             return rCell.hasString();
    1310             : 
    1311           2 :         return mrTab.HasStringData(nCol, nRow);
    1312             :     }
    1313             : 
    1314         311 :     std::pair<bool,bool> compareByValue(
    1315             :         const ScRefCellValue& rCell, SCCOL nCol, SCROW nRow,
    1316             :         const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem)
    1317             :     {
    1318         311 :         bool bOk = false;
    1319         311 :         bool bTestEqual = false;
    1320             :         double nCellVal;
    1321         311 :         if (!rCell.isEmpty())
    1322             :         {
    1323         311 :             switch (rCell.meType)
    1324             :             {
    1325             :                 case CELLTYPE_VALUE :
    1326         301 :                     nCellVal = rCell.mfValue;
    1327         301 :                 break;
    1328             :                 case CELLTYPE_FORMULA :
    1329          10 :                     nCellVal = rCell.mpFormula->GetValue();
    1330          10 :                 break;
    1331             :                 default:
    1332           0 :                     nCellVal = 0.0;
    1333             :             }
    1334             : 
    1335             :         }
    1336             :         else
    1337           0 :             nCellVal = mrTab.GetValue(nCol, nRow);
    1338             : 
    1339             :         /* NOTE: lcl_PrepareQuery() prepares a filter query such that if a
    1340             :          * date+time format was queried rEntry.bQueryByDate is not set. In
    1341             :          * case other queries wanted to use this mechanism they should do
    1342             :          * the same, in other words only if rEntry.nVal is an integer value
    1343             :          * rEntry.bQueryByDate should be true and the time fraction be
    1344             :          * stripped here. */
    1345         311 :         if (rItem.meType == ScQueryEntry::ByDate)
    1346             :         {
    1347           0 :             sal_uInt32 nNumFmt = mrTab.GetNumberFormat(nCol, nRow);
    1348           0 :             const SvNumberformat* pEntry = mrDoc.GetFormatTable()->GetEntry(nNumFmt);
    1349           0 :             if (pEntry)
    1350             :             {
    1351           0 :                 short nNumFmtType = pEntry->GetType();
    1352             :                 /* NOTE: Omitting the check for absence of
    1353             :                  * NUMBERFORMAT_TIME would include also date+time formatted
    1354             :                  * values of the same day. That may be desired in some
    1355             :                  * cases, querying all time values of a day, but confusing
    1356             :                  * in other cases. A user can always setup a standard
    1357             :                  * filter query for x >= date AND x < date+1 */
    1358           0 :                 if ((nNumFmtType & NUMBERFORMAT_DATE) && !(nNumFmtType & NUMBERFORMAT_TIME))
    1359             :                 {
    1360             :                     // The format is of date type.  Strip off the time
    1361             :                     // element.
    1362           0 :                     nCellVal = ::rtl::math::approxFloor(nCellVal);
    1363             :                 }
    1364             :             }
    1365             :         }
    1366             : 
    1367         311 :         switch (rEntry.eOp)
    1368             :         {
    1369             :             case SC_EQUAL :
    1370         118 :                 bOk = ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
    1371         118 :                 break;
    1372             :             case SC_LESS :
    1373          19 :                 bOk = (nCellVal < rItem.mfVal) && !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
    1374          19 :                 break;
    1375             :             case SC_GREATER :
    1376          54 :                 bOk = (nCellVal > rItem.mfVal) && !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
    1377          54 :                 break;
    1378             :             case SC_LESS_EQUAL :
    1379          39 :                 bOk = (nCellVal < rItem.mfVal) || ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
    1380          39 :                 if ( bOk && mpTestEqualCondition )
    1381          20 :                     bTestEqual = ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
    1382          39 :                 break;
    1383             :             case SC_GREATER_EQUAL :
    1384          81 :                 bOk = (nCellVal > rItem.mfVal) || ::rtl::math::approxEqual( nCellVal, rItem.mfVal);
    1385          81 :                 if ( bOk && mpTestEqualCondition )
    1386          11 :                     bTestEqual = ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
    1387          81 :                 break;
    1388             :             case SC_NOT_EQUAL :
    1389           0 :                 bOk = !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
    1390           0 :                 break;
    1391             :             default:
    1392             :             {
    1393             :                 // added to avoid warnings
    1394             :             }
    1395             :         }
    1396             : 
    1397         311 :         return std::pair<bool,bool>(bOk, bTestEqual);
    1398             :     }
    1399             : 
    1400         201 :     std::pair<bool,bool> compareByString(
    1401             :         ScRefCellValue& rCell, SCROW nRow, const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem)
    1402             :     {
    1403         201 :         bool bOk = false;
    1404         201 :         bool bTestEqual = false;
    1405         201 :         bool bMatchWholeCell = mbMatchWholeCell;
    1406         201 :         OUString  aCellStr;
    1407         201 :         if (isPartialTextMatchOp(rEntry))
    1408             :             // may have to do partial textural comparison.
    1409           0 :             bMatchWholeCell = false;
    1410             : 
    1411         201 :         if (!rCell.isEmpty())
    1412             :         {
    1413         200 :             if (rCell.meType == CELLTYPE_FORMULA && rCell.mpFormula->GetErrCode())
    1414             :             {
    1415             :                 // Error cell is evaluated as string (for now).
    1416           0 :                 aCellStr = ScGlobal::GetErrorString(rCell.mpFormula->GetErrCode());
    1417             :             }
    1418             :             else
    1419             :             {
    1420         200 :                 sal_uLong nFormat = mrTab.GetNumberFormat( static_cast<SCCOL>(rEntry.nField), nRow );
    1421         200 :                 ScCellFormat::GetInputString(rCell, nFormat, aCellStr, *mrDoc.GetFormatTable());
    1422             :             }
    1423             :         }
    1424             :         else
    1425           1 :             mrTab.GetInputString( static_cast<SCCOL>(rEntry.nField), nRow, aCellStr );
    1426             : 
    1427         201 :         bool bRealRegExp = isRealRegExp(rEntry);
    1428         201 :         bool bTestRegExp = isTestRegExp(rEntry);
    1429             : 
    1430         201 :         if ( bRealRegExp || bTestRegExp )
    1431             :         {
    1432          32 :             sal_Int32 nStart = 0;
    1433          32 :             sal_Int32 nEnd   = aCellStr.getLength();
    1434             : 
    1435             :             // from 614 on, nEnd is behind the found text
    1436          32 :             bool bMatch = false;
    1437          32 :             if ( rEntry.eOp == SC_ENDS_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH )
    1438             :             {
    1439           0 :                 nEnd = 0;
    1440           0 :                 nStart = aCellStr.getLength();
    1441             :                 bMatch = rEntry.GetSearchTextPtr( mrParam.bCaseSens )
    1442           0 :                     ->SearchBackward( aCellStr, &nStart, &nEnd );
    1443             :             }
    1444             :             else
    1445             :             {
    1446             :                 bMatch = rEntry.GetSearchTextPtr( mrParam.bCaseSens )
    1447          32 :                     ->SearchForward( aCellStr, &nStart, &nEnd );
    1448             :             }
    1449          40 :             if ( bMatch && bMatchWholeCell
    1450          40 :                     && (nStart != 0 || nEnd != aCellStr.getLength()) )
    1451           0 :                 bMatch = false;    // RegExp must match entire cell string
    1452          32 :             if ( bRealRegExp )
    1453          32 :                 switch (rEntry.eOp)
    1454             :             {
    1455             :                 case SC_EQUAL:
    1456             :                 case SC_CONTAINS:
    1457          28 :                     bOk = bMatch;
    1458          28 :                     break;
    1459             :                 case SC_NOT_EQUAL:
    1460             :                 case SC_DOES_NOT_CONTAIN:
    1461           4 :                     bOk = !bMatch;
    1462           4 :                     break;
    1463             :                 case SC_BEGINS_WITH:
    1464           0 :                     bOk = ( bMatch && (nStart == 0) );
    1465           0 :                     break;
    1466             :                 case SC_DOES_NOT_BEGIN_WITH:
    1467           0 :                     bOk = !( bMatch && (nStart == 0) );
    1468           0 :                     break;
    1469             :                 case SC_ENDS_WITH:
    1470           0 :                     bOk = ( bMatch && (nEnd == aCellStr.getLength()) );
    1471           0 :                     break;
    1472             :                 case SC_DOES_NOT_END_WITH:
    1473           0 :                     bOk = !( bMatch && (nEnd == aCellStr.getLength()) );
    1474           0 :                     break;
    1475             :                 default:
    1476             :                     {
    1477             :                         // added to avoid warnings
    1478             :                     }
    1479             :             }
    1480             :             else
    1481           0 :                 bTestEqual = bMatch;
    1482             :         }
    1483         201 :         if ( !bRealRegExp )
    1484             :         {
    1485             :             // Simple string matching i.e. no regexp match.
    1486         169 :             if (isTextMatchOp(rEntry))
    1487             :             {
    1488         143 :                 if (rItem.meType != ScQueryEntry::ByString && rItem.maString.isEmpty())
    1489             :                 {
    1490             :                     // #i18374# When used from functions (match, countif, sumif, vlookup, hlookup, lookup),
    1491             :                     // the query value is assigned directly, and the string is empty. In that case,
    1492             :                     // don't find any string (isEqual would find empty string results in formula cells).
    1493          16 :                     bOk = false;
    1494          16 :                     if ( rEntry.eOp == SC_NOT_EQUAL )
    1495           0 :                         bOk = !bOk;
    1496             :                 }
    1497         127 :                 else if ( bMatchWholeCell )
    1498             :                 {
    1499         127 :                     bOk = mpTransliteration->isEqual(aCellStr, rItem.maString);
    1500         127 :                     if ( rEntry.eOp == SC_NOT_EQUAL )
    1501           0 :                         bOk = !bOk;
    1502             :                 }
    1503             :                 else
    1504             :                 {
    1505           0 :                     const OUString& rQueryStr = rItem.maString;
    1506             :                     String aCell( mpTransliteration->transliterate(
    1507             :                         aCellStr, ScGlobal::eLnge, 0, aCellStr.getLength(),
    1508           0 :                         NULL ) );
    1509             :                     String aQuer( mpTransliteration->transliterate(
    1510             :                         rQueryStr, ScGlobal::eLnge, 0, rQueryStr.getLength(),
    1511           0 :                         NULL ) );
    1512           0 :                     xub_StrLen nIndex = (rEntry.eOp == SC_ENDS_WITH
    1513           0 :                         || rEntry.eOp == SC_DOES_NOT_END_WITH)? (aCell.Len()-aQuer.Len()):0;
    1514           0 :                     xub_StrLen nStrPos = aCell.Search( aQuer, nIndex );
    1515           0 :                     switch (rEntry.eOp)
    1516             :                     {
    1517             :                     case SC_EQUAL:
    1518             :                     case SC_CONTAINS:
    1519           0 :                         bOk = ( nStrPos != STRING_NOTFOUND );
    1520           0 :                         break;
    1521             :                     case SC_NOT_EQUAL:
    1522             :                     case SC_DOES_NOT_CONTAIN:
    1523           0 :                         bOk = ( nStrPos == STRING_NOTFOUND );
    1524           0 :                         break;
    1525             :                     case SC_BEGINS_WITH:
    1526           0 :                         bOk = ( nStrPos == 0 );
    1527           0 :                         break;
    1528             :                     case SC_DOES_NOT_BEGIN_WITH:
    1529           0 :                         bOk = ( nStrPos != 0 );
    1530           0 :                         break;
    1531             :                     case SC_ENDS_WITH:
    1532           0 :                         bOk = ( nStrPos + aQuer.Len() == aCell.Len() );
    1533           0 :                         break;
    1534             :                     case SC_DOES_NOT_END_WITH:
    1535           0 :                         bOk = ( nStrPos + aQuer.Len() != aCell.Len() );
    1536           0 :                         break;
    1537             :                     default:
    1538             :                         {
    1539             :                             // added to avoid warnings
    1540             :                         }
    1541           0 :                     }
    1542             :                 }
    1543             :             }
    1544             :             else
    1545             :             {   // use collator here because data was probably sorted
    1546             :                 sal_Int32 nCompare = mpCollator->compareString(
    1547          26 :                     aCellStr, rItem.maString);
    1548          26 :                 switch (rEntry.eOp)
    1549             :                 {
    1550             :                     case SC_LESS :
    1551           3 :                         bOk = (nCompare < 0);
    1552           3 :                         break;
    1553             :                     case SC_GREATER :
    1554           0 :                         bOk = (nCompare > 0);
    1555           0 :                         break;
    1556             :                     case SC_LESS_EQUAL :
    1557          15 :                         bOk = (nCompare <= 0);
    1558          15 :                         if ( bOk && mpTestEqualCondition && !bTestEqual )
    1559          10 :                             bTestEqual = (nCompare == 0);
    1560          15 :                         break;
    1561             :                     case SC_GREATER_EQUAL :
    1562           8 :                         bOk = (nCompare >= 0);
    1563           8 :                         if ( bOk && mpTestEqualCondition && !bTestEqual )
    1564           5 :                             bTestEqual = (nCompare == 0);
    1565           8 :                         break;
    1566             :                     default:
    1567             :                     {
    1568             :                         // added to avoid warnings
    1569             :                     }
    1570             :                 }
    1571             :             }
    1572             :         }
    1573             : 
    1574         201 :         return std::pair<bool,bool>(bOk, bTestEqual);
    1575             :     }
    1576             : };
    1577             : 
    1578             : }
    1579             : 
    1580         532 : bool ScTable::ValidQuery(
    1581             :     SCROW nRow, const ScQueryParam& rParam, ScRefCellValue* pCell, bool* pbTestEqualCondition)
    1582             : {
    1583         532 :     if (!rParam.GetEntry(0).bDoQuery)
    1584          19 :         return true;
    1585             : 
    1586         513 :     SCSIZE nEntryCount = rParam.GetEntryCount();
    1587             : 
    1588             :     typedef std::pair<bool,bool> ResultType;
    1589         513 :     static std::vector<ResultType> aResults;
    1590         513 :     if (aResults.size() < nEntryCount)
    1591           5 :         aResults.resize(nEntryCount);
    1592             : 
    1593         513 :     long    nPos = -1;
    1594         513 :     QueryEvaluator aEval(*pDocument, *this, rParam, pbTestEqualCondition);
    1595         513 :     ScQueryParam::const_iterator it, itBeg = rParam.begin(), itEnd = rParam.end();
    1596        1059 :     for (it = itBeg; it != itEnd && it->bDoQuery; ++it)
    1597             :     {
    1598         546 :         const ScQueryEntry& rEntry = *it;
    1599         546 :         SCCOL nCol = static_cast<SCCOL>(rEntry.nField);
    1600             : 
    1601             :         // we can only handle one single direct query
    1602         546 :         ScRefCellValue aCell;
    1603         546 :         if (pCell && it == itBeg)
    1604         446 :             aCell = *pCell;
    1605             :         else
    1606         100 :             aCell = GetCellValue(nCol, nRow);
    1607             : 
    1608         546 :         std::pair<bool,bool> aRes(false, false);
    1609             : 
    1610         546 :         const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
    1611         546 :         if (rItems.size() == 1 && rItems.front().meType == ScQueryEntry::ByEmpty)
    1612             :         {
    1613          22 :             if (rEntry.IsQueryByEmpty())
    1614          11 :                 aRes.first = !aCol[rEntry.nField].HasDataAt(nRow);
    1615             :             else
    1616             :             {
    1617             :                 OSL_ASSERT(rEntry.IsQueryByNonEmpty());
    1618          11 :                 aRes.first = aCol[rEntry.nField].HasDataAt(nRow);
    1619             :             }
    1620             :         }
    1621             :         else
    1622             :         {
    1623         524 :             ScQueryEntry::QueryItemsType::const_iterator itr = rItems.begin(), itrEnd = rItems.end();
    1624             : 
    1625        1056 :             for (; itr != itrEnd; ++itr)
    1626             :             {
    1627         534 :                 if (aEval.isQueryByValue(*itr, nCol, nRow, aCell))
    1628             :                 {
    1629             :                     std::pair<bool,bool> aThisRes =
    1630         311 :                         aEval.compareByValue(aCell, nCol, nRow, rEntry, *itr);
    1631         311 :                     aRes.first |= aThisRes.first;
    1632         311 :                     aRes.second |= aThisRes.second;
    1633             :                 }
    1634         223 :                 else if (aEval.isQueryByString(rEntry, *itr, nCol, nRow, aCell))
    1635             :                 {
    1636             :                     std::pair<bool,bool> aThisRes =
    1637         201 :                         aEval.compareByString(aCell, nRow, rEntry, *itr);
    1638         201 :                     aRes.first |= aThisRes.first;
    1639         201 :                     aRes.second |= aThisRes.second;
    1640             :                 }
    1641             : 
    1642         534 :                 if (aRes.first && aRes.second)
    1643           2 :                     break;
    1644             :             }
    1645             :         }
    1646             : 
    1647         546 :         if (nPos == -1)
    1648             :         {
    1649         513 :             nPos++;
    1650         513 :             aResults[nPos] = aRes;
    1651             :         }
    1652             :         else
    1653             :         {
    1654          33 :             if (rEntry.eConnect == SC_AND)
    1655             :             {
    1656          29 :                 aResults[nPos].first = aResults[nPos].first && aRes.first;
    1657          29 :                 aResults[nPos].second = aResults[nPos].second && aRes.second;
    1658             :             }
    1659             :             else
    1660             :             {
    1661           4 :                 nPos++;
    1662           4 :                 aResults[nPos] = aRes;
    1663             :             }
    1664             :         }
    1665         546 :     }
    1666             : 
    1667         517 :     for ( long j=1; j <= nPos; j++ )
    1668             :     {
    1669           4 :         aResults[0].first = aResults[0].first || aResults[j].first;
    1670           4 :         aResults[0].second = aResults[0].second || aResults[j].second;
    1671             :     }
    1672             : 
    1673         513 :     bool bRet = aResults[0].first;
    1674         513 :     if ( pbTestEqualCondition )
    1675          81 :         *pbTestEqualCondition = aResults[0].second;
    1676             : 
    1677         513 :     return bRet;
    1678             : }
    1679             : 
    1680           0 : void ScTable::TopTenQuery( ScQueryParam& rParam )
    1681             : {
    1682           0 :     bool bSortCollatorInitialized = false;
    1683           0 :     SCSIZE nEntryCount = rParam.GetEntryCount();
    1684           0 :     SCROW nRow1 = (rParam.bHasHeader ? rParam.nRow1 + 1 : rParam.nRow1);
    1685           0 :     SCSIZE nCount = static_cast<SCSIZE>(rParam.nRow2 - nRow1 + 1);
    1686           0 :     for ( SCSIZE i=0; (i<nEntryCount) && (rParam.GetEntry(i).bDoQuery); i++ )
    1687             :     {
    1688           0 :         ScQueryEntry& rEntry = rParam.GetEntry(i);
    1689           0 :         ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
    1690             : 
    1691           0 :         switch ( rEntry.eOp )
    1692             :         {
    1693             :             case SC_TOPVAL:
    1694             :             case SC_BOTVAL:
    1695             :             case SC_TOPPERC:
    1696             :             case SC_BOTPERC:
    1697             :             {
    1698           0 :                 ScSortParam aLocalSortParam( rParam, static_cast<SCCOL>(rEntry.nField) );
    1699           0 :                 aSortParam = aLocalSortParam;       // used in CreateSortInfoArray, Compare
    1700           0 :                 if ( !bSortCollatorInitialized )
    1701             :                 {
    1702           0 :                     bSortCollatorInitialized = true;
    1703           0 :                     InitSortCollator( aLocalSortParam );
    1704             :                 }
    1705           0 :                 ScSortInfoArray* pArray = CreateSortInfoArray( nRow1, rParam.nRow2 );
    1706           0 :                 DecoladeRow( pArray, nRow1, rParam.nRow2 );
    1707           0 :                 QuickSort( pArray, nRow1, rParam.nRow2 );
    1708           0 :                 ScSortInfo** ppInfo = pArray->GetFirstArray();
    1709           0 :                 SCSIZE nValidCount = nCount;
    1710             :                 // keine Note-/Leerzellen zaehlen, sind ans Ende sortiert
    1711           0 :                 while (nValidCount > 0 && ppInfo[nValidCount-1]->maCell.isEmpty())
    1712           0 :                     nValidCount--;
    1713             :                 // keine Strings zaehlen, sind zwischen Value und Leer
    1714           0 :                 while (nValidCount > 0 && ppInfo[nValidCount-1]->maCell.hasString())
    1715           0 :                     nValidCount--;
    1716           0 :                 if ( nValidCount > 0 )
    1717             :                 {
    1718           0 :                     if ( rItem.meType == ScQueryEntry::ByString )
    1719             :                     {   // dat wird nix
    1720           0 :                         rItem.meType = ScQueryEntry::ByValue;
    1721           0 :                         rItem.mfVal = 10;   // 10 bzw. 10%
    1722             :                     }
    1723           0 :                     SCSIZE nVal = (rItem.mfVal >= 1 ? static_cast<SCSIZE>(rItem.mfVal) : 1);
    1724           0 :                     SCSIZE nOffset = 0;
    1725           0 :                     switch ( rEntry.eOp )
    1726             :                     {
    1727             :                         case SC_TOPVAL:
    1728             :                         {
    1729           0 :                             rEntry.eOp = SC_GREATER_EQUAL;
    1730           0 :                             if ( nVal > nValidCount )
    1731           0 :                                 nVal = nValidCount;
    1732           0 :                             nOffset = nValidCount - nVal;   // 1 <= nVal <= nValidCount
    1733             :                         }
    1734           0 :                         break;
    1735             :                         case SC_BOTVAL:
    1736             :                         {
    1737           0 :                             rEntry.eOp = SC_LESS_EQUAL;
    1738           0 :                             if ( nVal > nValidCount )
    1739           0 :                                 nVal = nValidCount;
    1740           0 :                             nOffset = nVal - 1;     // 1 <= nVal <= nValidCount
    1741             :                         }
    1742           0 :                         break;
    1743             :                         case SC_TOPPERC:
    1744             :                         {
    1745           0 :                             rEntry.eOp = SC_GREATER_EQUAL;
    1746           0 :                             if ( nVal > 100 )
    1747           0 :                                 nVal = 100;
    1748           0 :                             nOffset = nValidCount - (nValidCount * nVal / 100);
    1749           0 :                             if ( nOffset >= nValidCount )
    1750           0 :                                 nOffset = nValidCount - 1;
    1751             :                         }
    1752           0 :                         break;
    1753             :                         case SC_BOTPERC:
    1754             :                         {
    1755           0 :                             rEntry.eOp = SC_LESS_EQUAL;
    1756           0 :                             if ( nVal > 100 )
    1757           0 :                                 nVal = 100;
    1758           0 :                             nOffset = (nValidCount * nVal / 100);
    1759           0 :                             if ( nOffset >= nValidCount )
    1760           0 :                                 nOffset = nValidCount - 1;
    1761             :                         }
    1762           0 :                         break;
    1763             :                         default:
    1764             :                         {
    1765             :                             // added to avoid warnings
    1766             :                         }
    1767             :                     }
    1768           0 :                     ScRefCellValue aCell = ppInfo[nOffset]->maCell;
    1769           0 :                     if (aCell.hasNumeric())
    1770           0 :                         rItem.mfVal = aCell.getValue();
    1771             :                     else
    1772             :                     {
    1773             :                         OSL_FAIL( "TopTenQuery: pCell no ValueData" );
    1774           0 :                         rEntry.eOp = SC_GREATER_EQUAL;
    1775           0 :                         rItem.mfVal = 0;
    1776           0 :                     }
    1777             :                 }
    1778             :                 else
    1779             :                 {
    1780           0 :                     rEntry.eOp = SC_GREATER_EQUAL;
    1781           0 :                     rItem.meType = ScQueryEntry::ByValue;
    1782           0 :                     rItem.mfVal = 0;
    1783             :                 }
    1784           0 :                 delete pArray;
    1785             :             }
    1786           0 :             break;
    1787             :             default:
    1788             :             {
    1789             :                 // added to avoid warnings
    1790             :             }
    1791             :         }
    1792             :     }
    1793           0 :     if ( bSortCollatorInitialized )
    1794           0 :         DestroySortCollator();
    1795           0 : }
    1796             : 
    1797             : namespace {
    1798             : 
    1799             : class PrepareQueryItem : public std::unary_function<ScQueryEntry::Item, void>
    1800             : {
    1801             :     const ScDocument& mrDoc;
    1802             : public:
    1803          16 :     PrepareQueryItem(const ScDocument& rDoc) : mrDoc(rDoc) {}
    1804             : 
    1805          18 :     void operator() (ScQueryEntry::Item& rItem)
    1806             :     {
    1807          18 :         if (rItem.meType != ScQueryEntry::ByString && rItem.meType != ScQueryEntry::ByDate)
    1808          27 :             return;
    1809             : 
    1810           9 :         sal_uInt32 nIndex = 0;
    1811             :         bool bNumber = mrDoc.GetFormatTable()->
    1812           9 :             IsNumberFormat(rItem.maString, nIndex, rItem.mfVal);
    1813             : 
    1814             :         // Advanced Filter creates only ByString queries that need to be
    1815             :         // converted to ByValue if appropriate. rItem.mfVal now holds the value
    1816             :         // if bNumber==true.
    1817             : 
    1818           9 :         if (rItem.meType == ScQueryEntry::ByString)
    1819             :         {
    1820           9 :             if (bNumber)
    1821           3 :                 rItem.meType = ScQueryEntry::ByValue;
    1822           9 :             return;
    1823             :         }
    1824             : 
    1825             :         // Double-check if the query by date is really appropriate.
    1826             : 
    1827           0 :         if (bNumber && ((nIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0))
    1828             :         {
    1829           0 :             const SvNumberformat* pEntry = mrDoc.GetFormatTable()->GetEntry(nIndex);
    1830           0 :             if (pEntry)
    1831             :             {
    1832           0 :                 short nNumFmtType = pEntry->GetType();
    1833           0 :                 if (!((nNumFmtType & NUMBERFORMAT_DATE) && !(nNumFmtType & NUMBERFORMAT_TIME)))
    1834           0 :                     rItem.meType = ScQueryEntry::ByValue;    // not a date only
    1835             :             }
    1836             :             else
    1837           0 :                 rItem.meType = ScQueryEntry::ByValue;    // what the ... not a date
    1838             :         }
    1839             :         else
    1840           0 :             rItem.meType = ScQueryEntry::ByValue;    // not a date
    1841             :     }
    1842             : };
    1843             : 
    1844          18 : void lcl_PrepareQuery( const ScDocument* pDoc, ScTable* pTab, ScQueryParam& rParam )
    1845             : {
    1846          18 :     bool bTopTen = false;
    1847          18 :     SCSIZE nEntryCount = rParam.GetEntryCount();
    1848             : 
    1849         162 :     for ( SCSIZE i = 0; i < nEntryCount; ++i )
    1850             :     {
    1851         144 :         ScQueryEntry& rEntry = rParam.GetEntry(i);
    1852         144 :         if (!rEntry.bDoQuery)
    1853         128 :             continue;
    1854             : 
    1855          16 :         ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
    1856          16 :         std::for_each(rItems.begin(), rItems.end(), PrepareQueryItem(*pDoc));
    1857             : 
    1858          16 :         if ( !bTopTen )
    1859             :         {
    1860          16 :             switch ( rEntry.eOp )
    1861             :             {
    1862             :                 case SC_TOPVAL:
    1863             :                 case SC_BOTVAL:
    1864             :                 case SC_TOPPERC:
    1865             :                 case SC_BOTPERC:
    1866             :                 {
    1867           0 :                     bTopTen = true;
    1868             :                 }
    1869           0 :                 break;
    1870             :                 default:
    1871             :                 {
    1872             :                 }
    1873             :             }
    1874             :         }
    1875             :     }
    1876             : 
    1877          18 :     if ( bTopTen )
    1878             :     {
    1879           0 :         pTab->TopTenQuery( rParam );
    1880             :     }
    1881          18 : }
    1882             : 
    1883             : }
    1884             : 
    1885          18 : SCSIZE ScTable::Query(ScQueryParam& rParamOrg, bool bKeepSub)
    1886             : {
    1887          18 :     ScQueryParam    aParam( rParamOrg );
    1888             :     typedef boost::unordered_set<OUString, OUStringHash> StrSetType;
    1889          36 :     StrSetType aStrSet;
    1890             : 
    1891          18 :     bool    bStarted = false;
    1892          18 :     bool    bOldResult = true;
    1893          18 :     SCROW   nOldStart = 0;
    1894          18 :     SCROW   nOldEnd = 0;
    1895             : 
    1896          18 :     SCSIZE nCount   = 0;
    1897          18 :     SCROW nOutRow   = 0;
    1898          18 :     SCROW nHeader   = aParam.bHasHeader ? 1 : 0;
    1899             : 
    1900          18 :     lcl_PrepareQuery(pDocument, this, aParam);
    1901             : 
    1902          18 :     if (!aParam.bInplace)
    1903             :     {
    1904           0 :         nOutRow = aParam.nDestRow + nHeader;
    1905           0 :         if (nHeader > 0)
    1906             :             CopyData( aParam.nCol1, aParam.nRow1, aParam.nCol2, aParam.nRow1,
    1907           0 :                             aParam.nDestCol, aParam.nDestRow, aParam.nDestTab );
    1908             :     }
    1909             : 
    1910             : 
    1911          18 :     if (aParam.bInplace)
    1912          18 :         InitializeNoteCaptions();
    1913             : 
    1914          18 :     SCROW nRealRow2 = aParam.nRow2;
    1915         104 :     for (SCROW j = aParam.nRow1 + nHeader; j <= nRealRow2; ++j)
    1916             :     {
    1917             :         bool bResult;                                   // Filterergebnis
    1918          86 :         bool bValid = ValidQuery(j, aParam);
    1919          86 :         if (!bValid && bKeepSub)                        // Subtotals stehenlassen
    1920             :         {
    1921          24 :             for (SCCOL nCol=aParam.nCol1; nCol<=aParam.nCol2 && !bValid; nCol++)
    1922             :             {
    1923          18 :                 ScRefCellValue aCell = GetCellValue(nCol, j);
    1924          18 :                 if (aCell.meType != CELLTYPE_FORMULA)
    1925          18 :                     continue;
    1926             : 
    1927           0 :                 if (!aCell.mpFormula->IsSubTotal())
    1928           0 :                     continue;
    1929             : 
    1930           0 :                 if (RefVisible(aCell.mpFormula))
    1931           0 :                     bValid = true;
    1932           0 :             }
    1933             :         }
    1934          86 :         if (bValid)
    1935             :         {
    1936          52 :             if (aParam.bDuplicate)
    1937          52 :                 bResult = true;
    1938             :             else
    1939             :             {
    1940           0 :                 OUString aStr;
    1941           0 :                 for (SCCOL k=aParam.nCol1; k <= aParam.nCol2; k++)
    1942             :                 {
    1943           0 :                     OUString aCellStr;
    1944           0 :                     GetString(k, j, aCellStr);
    1945           0 :                     OUStringBuffer aBuf(aStr);
    1946           0 :                     aBuf.append(aCellStr);
    1947           0 :                     aBuf.append(static_cast<sal_Unicode>(1));
    1948           0 :                     aStr = aBuf.makeStringAndClear();
    1949           0 :                 }
    1950             : 
    1951           0 :                 std::pair<StrSetType::iterator, bool> r = aStrSet.insert(aStr);
    1952           0 :                 bool bIsUnique = r.second; // unique if inserted.
    1953           0 :                 bResult = bIsUnique;
    1954             :             }
    1955             :         }
    1956             :         else
    1957          34 :             bResult = false;
    1958             : 
    1959          86 :         if (aParam.bInplace)
    1960             :         {
    1961          86 :             if (bResult == bOldResult && bStarted)
    1962          40 :                 nOldEnd = j;
    1963             :             else
    1964             :             {
    1965          46 :                 if (bStarted)
    1966          28 :                     DBShowRows(nOldStart,nOldEnd, bOldResult);
    1967          46 :                 nOldStart = nOldEnd = j;
    1968          46 :                 bOldResult = bResult;
    1969             :             }
    1970          86 :             bStarted = true;
    1971             :         }
    1972             :         else
    1973             :         {
    1974           0 :             if (bResult)
    1975             :             {
    1976           0 :                 CopyData( aParam.nCol1,j, aParam.nCol2,j, aParam.nDestCol,nOutRow,aParam.nDestTab );
    1977           0 :                 ++nOutRow;
    1978             :             }
    1979             :         }
    1980          86 :         if (bResult)
    1981          52 :             ++nCount;
    1982             :     }
    1983             : 
    1984          18 :     if (aParam.bInplace && bStarted)
    1985          18 :         DBShowRows(nOldStart,nOldEnd, bOldResult);
    1986             : 
    1987          18 :     if (aParam.bInplace)
    1988          18 :         SetDrawPageSize();
    1989             : 
    1990          36 :     return nCount;
    1991             : }
    1992             : 
    1993           1 : bool ScTable::CreateExcelQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
    1994             : {
    1995           1 :     bool    bValid = true;
    1996           1 :     SCCOL* pFields = new SCCOL[nCol2-nCol1+1];
    1997           1 :     OUString  aCellStr;
    1998           1 :     SCCOL   nCol = nCol1;
    1999             :     OSL_ENSURE( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
    2000           1 :     SCTAB   nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
    2001           1 :     SCROW   nDBRow1 = rQueryParam.nRow1;
    2002           1 :     SCCOL   nDBCol2 = rQueryParam.nCol2;
    2003             :     // Erste Zeile muessen Spaltenkoepfe sein
    2004           6 :     while (bValid && (nCol <= nCol2))
    2005             :     {
    2006           4 :         OUString aQueryStr;
    2007           4 :         GetUpperCellString(nCol, nRow1, aQueryStr);
    2008           4 :         bool bFound = false;
    2009           4 :         SCCOL i = rQueryParam.nCol1;
    2010          12 :         while (!bFound && (i <= nDBCol2))
    2011             :         {
    2012           4 :             if ( nTab == nDBTab )
    2013           4 :                 GetUpperCellString(i, nDBRow1, aCellStr);
    2014             :             else
    2015           0 :                 pDocument->GetUpperCellString(i, nDBRow1, nDBTab, aCellStr);
    2016           4 :             bFound = (aCellStr == aQueryStr);
    2017           4 :             if (!bFound) i++;
    2018             :         }
    2019           4 :         if (bFound)
    2020           4 :             pFields[nCol - nCol1] = i;
    2021             :         else
    2022           0 :             bValid = false;
    2023           4 :         nCol++;
    2024           4 :     }
    2025           1 :     if (bValid)
    2026             :     {
    2027           1 :         sal_uLong nVisible = 0;
    2028           5 :         for ( nCol=nCol1; nCol<=nCol2; nCol++ )
    2029           4 :             nVisible += aCol[nCol].VisibleCount( nRow1+1, nRow2 );
    2030             : 
    2031           1 :         if ( nVisible > SCSIZE_MAX / sizeof(void*) )
    2032             :         {
    2033             :             OSL_FAIL("too many filter criteria");
    2034           0 :             nVisible = 0;
    2035             :         }
    2036             : 
    2037           1 :         SCSIZE nNewEntries = nVisible;
    2038           1 :         rQueryParam.Resize( nNewEntries );
    2039             : 
    2040           1 :         SCSIZE nIndex = 0;
    2041           1 :         SCROW nRow = nRow1 + 1;
    2042           6 :         while (nRow <= nRow2)
    2043             :         {
    2044           4 :             nCol = nCol1;
    2045          24 :             while (nCol <= nCol2)
    2046             :             {
    2047          16 :                 GetInputString( nCol, nRow, aCellStr );
    2048          16 :                 if (!aCellStr.isEmpty())
    2049             :                 {
    2050           0 :                     if (nIndex < nNewEntries)
    2051             :                     {
    2052           0 :                         rQueryParam.GetEntry(nIndex).nField = pFields[nCol - nCol1];
    2053           0 :                         rQueryParam.FillInExcelSyntax(aCellStr, nIndex);
    2054           0 :                         nIndex++;
    2055           0 :                         if (nIndex < nNewEntries)
    2056           0 :                             rQueryParam.GetEntry(nIndex).eConnect = SC_AND;
    2057             :                     }
    2058             :                     else
    2059           0 :                         bValid = false;
    2060             :                 }
    2061          16 :                 nCol++;
    2062             :             }
    2063           4 :             nRow++;
    2064           4 :             if (nIndex < nNewEntries)
    2065           0 :                 rQueryParam.GetEntry(nIndex).eConnect = SC_OR;
    2066             :         }
    2067             :     }
    2068           1 :     delete [] pFields;
    2069           1 :     return bValid;
    2070             : }
    2071             : 
    2072           1 : bool ScTable::CreateStarQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
    2073             : {
    2074             :     // A valid StarQuery must be at least 4 columns wide. To be precise it
    2075             :     // should be exactly 4 columns ...
    2076             :     // Additionally, if this wasn't checked, a formula pointing to a valid 1-3
    2077             :     // column Excel style query range immediately left to itself would result
    2078             :     // in a circular reference when the field name or operator or value (first
    2079             :     // to third query range column) is obtained (#i58354#). Furthermore, if the
    2080             :     // range wasn't sufficiently specified data changes wouldn't flag formula
    2081             :     // cells for recalculation.
    2082           1 :     if (nCol2 - nCol1 < 3)
    2083           0 :         return false;
    2084             : 
    2085             :     bool bValid;
    2086             :     bool bFound;
    2087           1 :     OUString aCellStr;
    2088           1 :     SCSIZE nIndex = 0;
    2089           1 :     SCROW nRow = nRow1;
    2090             :     OSL_ENSURE( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
    2091           1 :     SCTAB   nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
    2092           1 :     SCROW   nDBRow1 = rQueryParam.nRow1;
    2093           1 :     SCCOL   nDBCol2 = rQueryParam.nCol2;
    2094             : 
    2095           1 :     SCSIZE nNewEntries = static_cast<SCSIZE>(nRow2-nRow1+1);
    2096           1 :     rQueryParam.Resize( nNewEntries );
    2097             : 
    2098           2 :     do
    2099             :     {
    2100           2 :         ScQueryEntry& rEntry = rQueryParam.GetEntry(nIndex);
    2101             : 
    2102           2 :         bValid = false;
    2103             :         // Erste Spalte UND/ODER
    2104           2 :         if (nIndex > 0)
    2105             :         {
    2106           1 :             GetUpperCellString(nCol1, nRow, aCellStr);
    2107           1 :             if ( aCellStr == ScGlobal::GetRscString(STR_TABLE_UND) )
    2108             :             {
    2109           0 :                 rEntry.eConnect = SC_AND;
    2110           0 :                 bValid = true;
    2111             :             }
    2112           1 :             else if ( aCellStr == ScGlobal::GetRscString(STR_TABLE_ODER) )
    2113             :             {
    2114           0 :                 rEntry.eConnect = SC_OR;
    2115           0 :                 bValid = true;
    2116             :             }
    2117             :         }
    2118             :         // Zweite Spalte FeldName
    2119           2 :         if ((nIndex < 1) || bValid)
    2120             :         {
    2121           1 :             bFound = false;
    2122           1 :             GetUpperCellString(nCol1 + 1, nRow, aCellStr);
    2123           2 :             for (SCCOL i=rQueryParam.nCol1; (i <= nDBCol2) && (!bFound); i++)
    2124             :             {
    2125           1 :                 OUString aFieldStr;
    2126           1 :                 if ( nTab == nDBTab )
    2127           1 :                     GetUpperCellString(i, nDBRow1, aFieldStr);
    2128             :                 else
    2129           0 :                     pDocument->GetUpperCellString(i, nDBRow1, nDBTab, aFieldStr);
    2130           1 :                 bFound = (aCellStr == aFieldStr);
    2131           1 :                 if (bFound)
    2132             :                 {
    2133           1 :                     rEntry.nField = i;
    2134           1 :                     bValid = true;
    2135             :                 }
    2136             :                 else
    2137           0 :                     bValid = false;
    2138           1 :             }
    2139             :         }
    2140             :         // Dritte Spalte Operator =<>...
    2141           2 :         if (bValid)
    2142             :         {
    2143           1 :             bFound = false;
    2144           1 :             GetUpperCellString(nCol1 + 2, nRow, aCellStr);
    2145           1 :             if (aCellStr[0] == '<')
    2146             :             {
    2147           0 :                 if (aCellStr[1] == '>')
    2148           0 :                     rEntry.eOp = SC_NOT_EQUAL;
    2149           0 :                 else if (aCellStr[1] == '=')
    2150           0 :                     rEntry.eOp = SC_LESS_EQUAL;
    2151             :                 else
    2152           0 :                     rEntry.eOp = SC_LESS;
    2153             :             }
    2154           1 :             else if (aCellStr[0] == '>')
    2155             :             {
    2156           0 :                 if (aCellStr[1] == '=')
    2157           0 :                     rEntry.eOp = SC_GREATER_EQUAL;
    2158             :                 else
    2159           0 :                     rEntry.eOp = SC_GREATER;
    2160             :             }
    2161           1 :             else if (aCellStr[0] == '=')
    2162           0 :                 rEntry.eOp = SC_EQUAL;
    2163             : 
    2164             :         }
    2165             :         // Vierte Spalte Wert
    2166           2 :         if (bValid)
    2167             :         {
    2168           1 :             OUString aStr;
    2169           1 :             GetString(nCol1 + 3, nRow, aStr);
    2170           1 :             rEntry.GetQueryItem().maString = aStr;
    2171           1 :             rEntry.bDoQuery = true;
    2172             :         }
    2173           2 :         nIndex++;
    2174           2 :         nRow++;
    2175             :     }
    2176           1 :     while (bValid && (nRow <= nRow2) /* && (nIndex < MAXQUERY) */ );
    2177           1 :     return bValid;
    2178             : }
    2179             : 
    2180           1 : bool ScTable::CreateQueryParam(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
    2181             : {
    2182             :     SCSIZE i, nCount;
    2183           1 :     PutInOrder(nCol1, nCol2);
    2184           1 :     PutInOrder(nRow1, nRow2);
    2185             : 
    2186           1 :     nCount = rQueryParam.GetEntryCount();
    2187           9 :     for (i=0; i < nCount; i++)
    2188           8 :         rQueryParam.GetEntry(i).Clear();
    2189             : 
    2190             :     // Standard QueryTabelle
    2191           1 :     bool bValid = CreateStarQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
    2192             :     // Excel QueryTabelle
    2193           1 :     if (!bValid)
    2194           1 :         bValid = CreateExcelQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
    2195             : 
    2196           1 :     nCount = rQueryParam.GetEntryCount();
    2197           1 :     if (bValid)
    2198             :     {
    2199             :         //  bQueryByString muss gesetzt sein
    2200           9 :         for (i=0; i < nCount; i++)
    2201           8 :             rQueryParam.GetEntry(i).GetQueryItem().meType = ScQueryEntry::ByString;
    2202             :     }
    2203             :     else
    2204             :     {
    2205             :         //  nix
    2206           0 :         for (i=0; i < nCount; i++)
    2207           0 :             rQueryParam.GetEntry(i).Clear();
    2208             :     }
    2209           1 :     return bValid;
    2210             : }
    2211             : 
    2212           9 : bool ScTable::HasColHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW /* nEndRow */ ) const
    2213             : {
    2214          18 :     for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
    2215             :     {
    2216          16 :         CellType eType = GetCellType( nCol, nStartRow );
    2217          16 :         if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
    2218           7 :             return false;
    2219             :     }
    2220           2 :     return true;
    2221             : }
    2222             : 
    2223           0 : bool ScTable::HasRowHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL /* nEndCol */, SCROW nEndRow ) const
    2224             : {
    2225           0 :     for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++)
    2226             :     {
    2227           0 :         CellType eType = GetCellType( nStartCol, nRow );
    2228           0 :         if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
    2229           0 :             return false;
    2230             :     }
    2231           0 :     return true;
    2232             : }
    2233             : 
    2234           0 : void ScTable::GetFilterEntries(SCCOL nCol, SCROW nRow1, SCROW nRow2, std::vector<ScTypedStrData>& rStrings, bool& rHasDates)
    2235             : {
    2236           0 :     aCol[nCol].GetFilterEntries( nRow1, nRow2, rStrings, rHasDates );
    2237           0 : }
    2238             : 
    2239           0 : void ScTable::GetFilteredFilterEntries(
    2240             :     SCCOL nCol, SCROW nRow1, SCROW nRow2, const ScQueryParam& rParam, std::vector<ScTypedStrData>& rStrings, bool& rHasDates)
    2241             : {
    2242             :     // remove the entry for this column from the query parameter
    2243           0 :     ScQueryParam aParam( rParam );
    2244           0 :     aParam.RemoveEntryByField(nCol);
    2245             : 
    2246           0 :     lcl_PrepareQuery(pDocument, this, aParam);
    2247           0 :     bool bHasDates = false;
    2248           0 :     for ( SCROW j = nRow1; j <= nRow2; ++j )
    2249             :     {
    2250           0 :         if (ValidQuery(j, aParam))
    2251             :         {
    2252           0 :             bool bThisHasDates = false;
    2253           0 :             aCol[nCol].GetFilterEntries( j, j, rStrings, bThisHasDates );
    2254           0 :             bHasDates |= bThisHasDates;
    2255             :         }
    2256             :     }
    2257             : 
    2258           0 :     rHasDates = bHasDates;
    2259           0 : }
    2260             : 
    2261           0 : bool ScTable::GetDataEntries(SCCOL nCol, SCROW nRow, std::set<ScTypedStrData>& rStrings, bool bLimit)
    2262             : {
    2263           0 :     return aCol[nCol].GetDataEntries( nRow, rStrings, bLimit );
    2264             : }
    2265             : 
    2266           0 : ScDocument& ScTable::GetDoc()
    2267             : {
    2268           0 :     return *pDocument;
    2269             : }
    2270             : 
    2271           0 : const ScDocument& ScTable::GetDoc() const
    2272             : {
    2273           0 :     return *pDocument;
    2274             : }
    2275             : 
    2276           0 : SCSIZE ScTable::GetCellCount(SCCOL nCol) const
    2277             : {
    2278           0 :     return aCol[nCol].GetCellCount();
    2279             : }
    2280             : 
    2281          26 : sal_uLong ScTable::GetCellCount() const
    2282             : {
    2283          26 :     sal_uLong nCellCount = 0;
    2284             : 
    2285       26650 :     for ( SCCOL nCol=0; nCol<=MAXCOL; nCol++ )
    2286       26624 :         nCellCount += aCol[nCol].GetCellCount();
    2287             : 
    2288          26 :     return nCellCount;
    2289             : }
    2290             : 
    2291         873 : sal_uLong ScTable::GetWeightedCount() const
    2292             : {
    2293         873 :     sal_uLong nCellCount = 0;
    2294             : 
    2295      894825 :     for ( SCCOL nCol=0; nCol<=MAXCOL; nCol++ )
    2296      893952 :         if ( aCol[nCol].GetCellCount() )                    // GetCellCount ist inline
    2297           0 :             nCellCount += aCol[nCol].GetWeightedCount();
    2298             : 
    2299         873 :     return nCellCount;
    2300             : }
    2301             : 
    2302           0 : sal_uLong ScTable::GetCodeCount() const
    2303             : {
    2304           0 :     sal_uLong nCodeCount = 0;
    2305             : 
    2306           0 :     for ( SCCOL nCol=0; nCol<=MAXCOL; nCol++ )
    2307           0 :         if ( aCol[nCol].GetCellCount() )                    // GetCellCount ist inline
    2308           0 :             nCodeCount += aCol[nCol].GetCodeCount();
    2309             : 
    2310           0 :     return nCodeCount;
    2311             : }
    2312             : 
    2313           0 : sal_Int32 ScTable::GetMaxStringLen( SCCOL nCol, SCROW nRowStart,
    2314             :         SCROW nRowEnd, CharSet eCharSet ) const
    2315             : {
    2316           0 :     if ( ValidCol(nCol) )
    2317           0 :         return aCol[nCol].GetMaxStringLen( nRowStart, nRowEnd, eCharSet );
    2318             :     else
    2319           0 :         return 0;
    2320             : }
    2321             : 
    2322           0 : xub_StrLen ScTable::GetMaxNumberStringLen(
    2323             :     sal_uInt16& nPrecision, SCCOL nCol, SCROW nRowStart, SCROW nRowEnd ) const
    2324             : {
    2325           0 :     if ( ValidCol(nCol) )
    2326           0 :         return aCol[nCol].GetMaxNumberStringLen( nPrecision, nRowStart, nRowEnd );
    2327             :     else
    2328           0 :         return 0;
    2329             : }
    2330             : 
    2331         191 : void ScTable::UpdateSelectionFunction( ScFunctionData& rData,
    2332             :                         SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
    2333             :                         const ScMarkData& rMark )
    2334             : {
    2335             :     //  Cursor neben einer Markierung nicht beruecksichtigen:
    2336             :     //! nur noch MarkData uebergeben, Cursorposition ggf. hineinselektieren!!!
    2337         191 :     bool bSingle = ( rMark.IsMarked() || !rMark.IsMultiMarked() );
    2338             : 
    2339             :     // Mehrfachselektion:
    2340             : 
    2341             :     SCCOL nCol;
    2342         191 :     if ( rMark.IsMultiMarked() )
    2343        4100 :         for (nCol=0; nCol<=MAXCOL && !rData.bError; nCol++)
    2344        4096 :             if ( !pColFlags || !ColHidden(nCol) )
    2345        4096 :                 aCol[nCol].UpdateSelectionFunction( rMark, rData, *mpHiddenRows,
    2346        4096 :                                                     bSingle && ( nCol >= nStartCol && nCol <= nEndCol ),
    2347        8192 :                                                     nStartRow, nEndRow );
    2348             : 
    2349             :     //  Einfachselektion (oder Cursor) nur wenn nicht negativ (und s.o.):
    2350             : 
    2351         191 :     if ( bSingle && !rMark.IsMarkNegative() )
    2352         380 :         for (nCol=nStartCol; nCol<=nEndCol && !rData.bError; nCol++)
    2353         193 :             if ( !pColFlags || !ColHidden(nCol) )
    2354         193 :                 aCol[nCol].UpdateAreaFunction( rData, *mpHiddenRows, nStartRow, nEndRow );
    2355         284 : }
    2356             : 
    2357             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10