LCOV - code coverage report
Current view: top level - sc/source/core/data - table3.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 722 1159 62.3 %
Date: 2012-08-25 Functions: 51 65 78.5 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 679 1725 39.4 %

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

Generated by: LCOV version 1.10