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

Generated by: LCOV version 1.10