LCOV - code coverage report
Current view: top level - sc/source/core/data - validat.cxx (source / functions) Hit Total Coverage
Test: commit 10e77ab3ff6f4314137acd6e2702a6e5c1ce1fae Lines: 162 434 37.3 %
Date: 2014-11-03 Functions: 33 46 71.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : #include <config_features.h>
      21             : 
      22             : #include "validat.hxx"
      23             : 
      24             : #include <sfx2/app.hxx>
      25             : #include <sfx2/docfile.hxx>
      26             : #include <sfx2/objsh.hxx>
      27             : #include <basic/sbmeth.hxx>
      28             : #include <basic/sbmod.hxx>
      29             : #include <basic/sbstar.hxx>
      30             : #include <basic/basmgr.hxx>
      31             : 
      32             : #include <basic/sbx.hxx>
      33             : #include <svl/zforlist.hxx>
      34             : #include <svl/sharedstringpool.hxx>
      35             : #include <vcl/layout.hxx>
      36             : #include <vcl/msgbox.hxx>
      37             : #include <rtl/math.hxx>
      38             : 
      39             : #include "scitems.hxx"
      40             : #include "document.hxx"
      41             : #include "formulacell.hxx"
      42             : #include "patattr.hxx"
      43             : #include "rechead.hxx"
      44             : #include "globstr.hrc"
      45             : #include "rangenam.hxx"
      46             : #include "dbdata.hxx"
      47             : #include "typedstrdata.hxx"
      48             : #include "dociter.hxx"
      49             : #include "editutil.hxx"
      50             : #include "tokenarray.hxx"
      51             : #include "scmatrix.hxx"
      52             : 
      53             : #include <math.h>
      54             : #include <boost/scoped_ptr.hpp>
      55             : 
      56             : using namespace formula;
      57             : 
      58             : //  Entries for validation (with only one condition)
      59             : 
      60         134 : ScValidationData::ScValidationData( ScValidationMode eMode, ScConditionMode eOper,
      61             :                                     const OUString& rExpr1, const OUString& rExpr2,
      62             :                                     ScDocument* pDocument, const ScAddress& rPos,
      63             :                                     const OUString& rExprNmsp1, const OUString& rExprNmsp2,
      64             :                                     FormulaGrammar::Grammar eGrammar1,
      65             :                                     FormulaGrammar::Grammar eGrammar2 )
      66             :     : ScConditionEntry( eOper, rExpr1, rExpr2, pDocument, rPos, rExprNmsp1,
      67             :                         rExprNmsp2, eGrammar1, eGrammar2 )
      68             :     , nKey( 0 )
      69             :     , eDataMode( eMode )
      70             :     , bShowInput(false)
      71             :     , bShowError(false)
      72             :     , eErrorStyle( SC_VALERR_STOP )
      73         134 :     , mnListType( css::sheet::TableValidationVisibility::UNSORTED )
      74             : {
      75         134 : }
      76             : 
      77           6 : ScValidationData::ScValidationData( ScValidationMode eMode, ScConditionMode eOper,
      78             :                                     const ScTokenArray* pArr1, const ScTokenArray* pArr2,
      79             :                                     ScDocument* pDocument, const ScAddress& rPos )
      80             :     : ScConditionEntry( eOper, pArr1, pArr2, pDocument, rPos )
      81             :     , nKey( 0 )
      82             :     , eDataMode( eMode )
      83             :     , bShowInput(false)
      84             :     , bShowError(false)
      85             :     , eErrorStyle( SC_VALERR_STOP )
      86           6 :     , mnListType( css::sheet::TableValidationVisibility::UNSORTED )
      87             : {
      88           6 : }
      89             : 
      90           6 : ScValidationData::ScValidationData( const ScValidationData& r )
      91             :     : ScConditionEntry( r )
      92             :     , nKey( r.nKey )
      93             :     , eDataMode( r.eDataMode )
      94             :     , bShowInput( r.bShowInput )
      95             :     , bShowError( r.bShowError )
      96             :     , eErrorStyle( r.eErrorStyle )
      97             :     , mnListType( r.mnListType )
      98             :     , aInputTitle( r.aInputTitle )
      99             :     , aInputMessage( r.aInputMessage )
     100             :     , aErrorTitle( r.aErrorTitle )
     101           6 :     , aErrorMessage( r.aErrorMessage )
     102             : {
     103             :     //  Formulae copied by RefCount
     104           6 : }
     105             : 
     106          56 : ScValidationData::ScValidationData( ScDocument* pDocument, const ScValidationData& r )
     107             :     : ScConditionEntry( pDocument, r )
     108             :     , nKey( r.nKey )
     109             :     , eDataMode( r.eDataMode )
     110             :     , bShowInput( r.bShowInput )
     111             :     , bShowError( r.bShowError )
     112             :     , eErrorStyle( r.eErrorStyle )
     113             :     , mnListType( r.mnListType )
     114             :     , aInputTitle( r.aInputTitle )
     115             :     , aInputMessage( r.aInputMessage )
     116             :     , aErrorTitle( r.aErrorTitle )
     117          56 :     , aErrorMessage( r.aErrorMessage )
     118             : {
     119             :     //  Formulae really copied
     120          56 : }
     121             : 
     122         290 : ScValidationData::~ScValidationData()
     123             : {
     124         290 : }
     125             : 
     126          68 : bool ScValidationData::IsEmpty() const
     127             : {
     128          68 :     OUString aEmpty;
     129         136 :     ScValidationData aDefault( SC_VALID_ANY, SC_COND_EQUAL, aEmpty, aEmpty, GetDocument(), ScAddress() );
     130         136 :     return EqualEntries( aDefault );
     131             : }
     132             : 
     133         230 : bool ScValidationData::EqualEntries( const ScValidationData& r ) const
     134             : {
     135             :         //  test same parameters (excluding Key)
     136             : 
     137         310 :     return ScConditionEntry::operator==(r) &&
     138         160 :             eDataMode       == r.eDataMode &&
     139         160 :             bShowInput      == r.bShowInput &&
     140         160 :             bShowError      == r.bShowError &&
     141         160 :             eErrorStyle     == r.eErrorStyle &&
     142         160 :             mnListType      == r.mnListType &&
     143         146 :             aInputTitle     == r.aInputTitle &&
     144         122 :             aInputMessage   == r.aInputMessage &&
     145         330 :             aErrorTitle     == r.aErrorTitle &&
     146         274 :             aErrorMessage   == r.aErrorMessage;
     147             : }
     148             : 
     149          60 : void ScValidationData::ResetInput()
     150             : {
     151          60 :     bShowInput = false;
     152          60 : }
     153             : 
     154          32 : void ScValidationData::ResetError()
     155             : {
     156          32 :     bShowError = false;
     157          32 : }
     158             : 
     159          60 : void ScValidationData::SetInput( const OUString& rTitle, const OUString& rMsg )
     160             : {
     161          60 :     bShowInput = true;
     162          60 :     aInputTitle = rTitle;
     163          60 :     aInputMessage = rMsg;
     164          60 : }
     165             : 
     166          70 : void ScValidationData::SetError( const OUString& rTitle, const OUString& rMsg,
     167             :                                     ScValidErrorStyle eStyle )
     168             : {
     169          70 :     bShowError = true;
     170          70 :     eErrorStyle = eStyle;
     171          70 :     aErrorTitle = rTitle;
     172          70 :     aErrorMessage = rMsg;
     173          70 : }
     174             : 
     175          96 : bool ScValidationData::GetErrMsg( OUString& rTitle, OUString& rMsg,
     176             :                                     ScValidErrorStyle& rStyle ) const
     177             : {
     178          96 :     rTitle = aErrorTitle;
     179          96 :     rMsg   = aErrorMessage;
     180          96 :     rStyle = eErrorStyle;
     181          96 :     return bShowError;
     182             : }
     183             : 
     184           0 : bool ScValidationData::DoScript( const ScAddress& rPos, const OUString& rInput,
     185             :                                 ScFormulaCell* pCell, vcl::Window* pParent ) const
     186             : {
     187           0 :     ScDocument* pDocument = GetDocument();
     188           0 :     SfxObjectShell* pDocSh = pDocument->GetDocumentShell();
     189           0 :     if ( !pDocSh || !pDocument->CheckMacroWarn() )
     190           0 :         return false;
     191             : 
     192           0 :     bool bScriptReturnedFalse = false;  // default: do not abort
     193             : 
     194             :     // Set up parameters
     195           0 :     ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > aParams(2);
     196             : 
     197             :     //  1) entered or calculated value
     198           0 :     OUString aValStr = rInput;
     199             :     double nValue;
     200           0 :     bool bIsValue = false;
     201           0 :     if ( pCell )                // if cell exists, call interpret
     202             :     {
     203           0 :         bIsValue = pCell->IsValue();
     204           0 :         if ( bIsValue )
     205           0 :             nValue  = pCell->GetValue();
     206             :         else
     207           0 :             aValStr = pCell->GetString().getString();
     208             :     }
     209           0 :     if ( bIsValue )
     210           0 :         aParams[0] = ::com::sun::star::uno::makeAny( nValue );
     211             :     else
     212           0 :         aParams[0] = ::com::sun::star::uno::makeAny( OUString( aValStr ) );
     213             : 
     214             :     //  2) Position of the cell
     215           0 :     OUString aPosStr(rPos.Format(SCA_VALID | SCA_TAB_3D, pDocument, pDocument->GetAddressConvention()));
     216           0 :     aParams[1] = ::com::sun::star::uno::makeAny(aPosStr);
     217             : 
     218             :     //  use link-update flag to prevent closing the document
     219             :     //  while the macro is running
     220           0 :     bool bWasInLinkUpdate = pDocument->IsInLinkUpdate();
     221           0 :     if ( !bWasInLinkUpdate )
     222           0 :         pDocument->SetInLinkUpdate( true );
     223             : 
     224           0 :     if ( pCell )
     225           0 :         pDocument->LockTable( rPos.Tab() );
     226             : 
     227           0 :     ::com::sun::star::uno::Any aRet;
     228           0 :     ::com::sun::star::uno::Sequence< sal_Int16 > aOutArgsIndex;
     229           0 :     ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > aOutArgs;
     230             : 
     231             :     ErrCode eRet = pDocSh->CallXScript(
     232           0 :         aErrorTitle, aParams, aRet, aOutArgsIndex, aOutArgs );
     233             : 
     234           0 :     if ( pCell )
     235           0 :         pDocument->UnlockTable( rPos.Tab() );
     236             : 
     237           0 :     if ( !bWasInLinkUpdate )
     238           0 :         pDocument->SetInLinkUpdate( false );
     239             : 
     240             :     // Check the return value from the script
     241             :     // The contents of the cell get reset if the script returns false
     242           0 :     bool bTmp = false;
     243           0 :     if ( eRet == ERRCODE_NONE &&
     244           0 :              aRet.getValueType() == getCppuBooleanType() &&
     245           0 :              ( aRet >>= bTmp ) &&
     246           0 :              !bTmp )
     247             :     {
     248           0 :         bScriptReturnedFalse =  true;
     249             :     }
     250             : 
     251           0 :     if ( eRet == ERRCODE_BASIC_METHOD_NOT_FOUND && !pCell )
     252             :     // Macro not found (only with input)
     253             :     {
     254             :         //! different error message, if found, but not bAllowed ??
     255             : 
     256           0 :         MessageDialog aBox( pParent, ScGlobal::GetRscString(STR_VALID_MACRONOTFOUND));
     257           0 :         aBox.Execute();
     258             :     }
     259             : 
     260           0 :     return bScriptReturnedFalse;
     261             : }
     262             : 
     263             :     // true -> abort
     264             : 
     265           0 : bool ScValidationData::DoMacro( const ScAddress& rPos, const OUString& rInput,
     266             :                                 ScFormulaCell* pCell, vcl::Window* pParent ) const
     267             : {
     268           0 :     if ( SfxApplication::IsXScriptURL( aErrorTitle ) )
     269             :     {
     270           0 :         return DoScript( rPos, rInput, pCell, pParent );
     271             :     }
     272             : 
     273           0 :     ScDocument* pDocument = GetDocument();
     274           0 :     SfxObjectShell* pDocSh = pDocument->GetDocumentShell();
     275           0 :     if ( !pDocSh || !pDocument->CheckMacroWarn() )
     276           0 :         return false;
     277             : 
     278           0 :     bool bDone = false;
     279           0 :     bool bRet = false;                      // default: do not abort
     280             : 
     281             :     //  If the Doc was loaded during a Basic-Calls,
     282             :     //  the Sbx-Objekt may not be created (?)
     283             : //  pDocSh->GetSbxObject();
     284             : 
     285             : #if HAVE_FEATURE_SCRIPTING
     286             :     //  no security check ahead (only CheckMacroWarn), that happens in CallBasic
     287             : 
     288             :     //  Function search by their simple name,
     289             :     //  then assemble aBasicStr, aMacroStr for SfxObjectShell::CallBasic
     290             : 
     291           0 :     StarBASIC* pRoot = pDocSh->GetBasic();
     292           0 :     SbxVariable* pVar = pRoot->Find( aErrorTitle, SbxCLASS_METHOD );
     293           0 :     if ( pVar && pVar->ISA(SbMethod) )
     294             :     {
     295           0 :         SbMethod* pMethod = static_cast<SbMethod*>(pVar);
     296           0 :         SbModule* pModule = pMethod->GetModule();
     297           0 :         SbxObject* pObject = pModule->GetParent();
     298           0 :         OUStringBuffer aMacroStr = pObject->GetName();
     299           0 :         aMacroStr.append('.').append(pModule->GetName()).append('.').append(pMethod->GetName());
     300           0 :         OUString aBasicStr;
     301             : 
     302             :         //  the distinction between document- and app-basic has to be done
     303             :         //  by checking the parent (as in ScInterpreter::ScMacro), not by looping
     304             :         //  over all open documents, because this may be called from within loading,
     305             :         //  when SfxObjectShell::GetFirst/GetNext won't find the document.
     306             : 
     307           0 :         if ( pObject->GetParent() )
     308           0 :             aBasicStr = pObject->GetParent()->GetName();    // Basic of document
     309             :         else
     310           0 :             aBasicStr = SfxGetpApp()->GetName();            // Basic of application
     311             : 
     312             :         //  Parameter for Macro
     313           0 :         SbxArrayRef refPar = new SbxArray;
     314             : 
     315             :         //  1) entered or calculated value
     316           0 :         OUString aValStr = rInput;
     317           0 :         double nValue = 0.0;
     318           0 :         bool bIsValue = false;
     319           0 :         if ( pCell )                // if cell set, called from interpret
     320             :         {
     321           0 :             bIsValue = pCell->IsValue();
     322           0 :             if ( bIsValue )
     323           0 :                 nValue  = pCell->GetValue();
     324             :             else
     325           0 :                 aValStr = pCell->GetString().getString();
     326             :         }
     327           0 :         if ( bIsValue )
     328           0 :             refPar->Get(1)->PutDouble( nValue );
     329             :         else
     330           0 :             refPar->Get(1)->PutString( aValStr );
     331             : 
     332             :         //  2) Position of the cell
     333           0 :         OUString aPosStr(rPos.Format(SCA_VALID | SCA_TAB_3D, pDocument, pDocument->GetAddressConvention()));
     334           0 :         refPar->Get(2)->PutString( aPosStr );
     335             : 
     336             :         //  use link-update flag to prevent closing the document
     337             :         //  while the macro is running
     338           0 :         bool bWasInLinkUpdate = pDocument->IsInLinkUpdate();
     339           0 :         if ( !bWasInLinkUpdate )
     340           0 :             pDocument->SetInLinkUpdate( true );
     341             : 
     342           0 :         if ( pCell )
     343           0 :             pDocument->LockTable( rPos.Tab() );
     344           0 :         SbxVariableRef refRes = new SbxVariable;
     345           0 :         ErrCode eRet = pDocSh->CallBasic( aMacroStr.makeStringAndClear(), aBasicStr, refPar, refRes );
     346           0 :         if ( pCell )
     347           0 :             pDocument->UnlockTable( rPos.Tab() );
     348             : 
     349           0 :         if ( !bWasInLinkUpdate )
     350           0 :             pDocument->SetInLinkUpdate( false );
     351             : 
     352             :         //  Interrupt input if Basic macro returns false
     353           0 :         if ( eRet == ERRCODE_NONE && refRes->GetType() == SbxBOOL && !refRes->GetBool() )
     354           0 :             bRet = true;
     355           0 :         bDone = true;
     356             :     }
     357             : #endif
     358           0 :     if ( !bDone && !pCell )         // Macro not found (only with input)
     359             :     {
     360             :         //! different error message, if found, but not bAllowed ??
     361             : 
     362           0 :         MessageDialog aBox(pParent, ScGlobal::GetRscString(STR_VALID_MACRONOTFOUND));
     363           0 :         aBox.Execute();
     364             :     }
     365             : 
     366           0 :     return bRet;
     367             : }
     368             : 
     369           0 : void ScValidationData::DoCalcError( ScFormulaCell* pCell ) const
     370             : {
     371           0 :     if ( eErrorStyle == SC_VALERR_MACRO )
     372           0 :         DoMacro( pCell->aPos, EMPTY_OUSTRING, pCell, NULL );
     373           0 : }
     374             : 
     375             :     // true -> abort
     376             : 
     377           0 : bool ScValidationData::DoError( vcl::Window* pParent, const OUString& rInput,
     378             :                                 const ScAddress& rPos ) const
     379             : {
     380           0 :     if ( eErrorStyle == SC_VALERR_MACRO )
     381           0 :         return DoMacro( rPos, rInput, NULL, pParent );
     382             : 
     383             :     //  Output error message
     384             : 
     385           0 :     OUString aTitle = aErrorTitle;
     386           0 :     if (aTitle.isEmpty())
     387           0 :         aTitle = ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_0 );  // application title
     388           0 :     OUString aMessage = aErrorMessage;
     389           0 :     if (aMessage.isEmpty())
     390           0 :         aMessage = ScGlobal::GetRscString( STR_VALID_DEFERROR );
     391             : 
     392             :     //! ErrorBox / WarningBox / InfoBox ?
     393             :     //! (with InfoBox always OK-Button only)
     394             : 
     395           0 :     WinBits nStyle = 0;
     396           0 :     switch (eErrorStyle)
     397             :     {
     398             :         case SC_VALERR_STOP:
     399           0 :             nStyle = WB_OK | WB_DEF_OK;
     400           0 :             break;
     401             :         case SC_VALERR_WARNING:
     402           0 :             nStyle = WB_OK_CANCEL | WB_DEF_CANCEL;
     403           0 :             break;
     404             :         case SC_VALERR_INFO:
     405           0 :             nStyle = WB_OK_CANCEL | WB_DEF_OK;
     406           0 :             break;
     407             :         default:
     408             :         {
     409             :             // added to avoid warnings
     410             :         }
     411             :     }
     412             : 
     413           0 :     MessBox aBox( pParent, WinBits(nStyle), aTitle, aMessage );
     414           0 :     sal_uInt16 nRet = aBox.Execute();
     415             : 
     416           0 :     return ( eErrorStyle == SC_VALERR_STOP || nRet == RET_CANCEL );
     417             : }
     418             : 
     419           0 : bool ScValidationData::IsDataValid(
     420             :     const OUString& rTest, const ScPatternAttr& rPattern, const ScAddress& rPos ) const
     421             : {
     422           0 :     if ( eDataMode == SC_VALID_ANY ) // check if any cell content is allowed
     423           0 :         return true;
     424             : 
     425           0 :     if (rTest.isEmpty())              // check whether empty cells are allowed
     426           0 :         return IsIgnoreBlank();
     427             : 
     428           0 :     if (rTest[0] == '=')   // formulas do not pass the validity test
     429           0 :         return false;
     430             : 
     431           0 :     SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable();
     432             : 
     433             :     // get the value if any
     434           0 :     sal_uInt32 nFormat = rPattern.GetNumberFormat( pFormatter );
     435             :     double nVal;
     436           0 :     bool bIsVal = pFormatter->IsNumberFormat( rTest, nFormat, nVal );
     437             : 
     438             :     bool bRet;
     439           0 :     if (SC_VALID_TEXTLEN == eDataMode)
     440             :     {
     441             :         double nLenVal;
     442           0 :         if (!bIsVal)
     443           0 :             nLenVal = static_cast<double>(rTest.getLength());
     444             :         else
     445             :         {
     446             :             // For numeric values use the resulting input line string to
     447             :             // determine length, otherwise a once accepted value maybe could
     448             :             // not be edited again, for example abbreviated dates or leading
     449             :             // zeros or trailing zeros after decimal separator change length.
     450           0 :             OUString aStr;
     451           0 :             pFormatter->GetInputLineString( nVal, nFormat, aStr);
     452           0 :             nLenVal = static_cast<double>( aStr.getLength() );
     453             :         }
     454           0 :         ScRefCellValue aTmpCell(nLenVal);
     455           0 :         bRet = IsCellValid(aTmpCell, rPos);
     456             :     }
     457             :     else
     458             :     {
     459           0 :         if (bIsVal)
     460             :         {
     461           0 :             ScRefCellValue aTmpCell(nVal);
     462           0 :             bRet = IsDataValid(aTmpCell, rPos);
     463             :         }
     464             :         else
     465             :         {
     466           0 :             svl::SharedString aSS = mpDoc->GetSharedStringPool().intern(rTest);
     467           0 :             ScRefCellValue aTmpCell(&aSS);
     468           0 :             bRet = IsDataValid(aTmpCell, rPos);
     469             :         }
     470             :     }
     471             : 
     472           0 :     return bRet;
     473             : }
     474             : 
     475           0 : bool ScValidationData::IsDataValid( ScRefCellValue& rCell, const ScAddress& rPos ) const
     476             : {
     477           0 :     if( eDataMode == SC_VALID_LIST )
     478           0 :         return IsListValid(rCell, rPos);
     479             : 
     480           0 :     double nVal = 0.0;
     481           0 :     OUString aString;
     482           0 :     bool bIsVal = true;
     483             : 
     484           0 :     switch (rCell.meType)
     485             :     {
     486             :         case CELLTYPE_VALUE:
     487           0 :             nVal = rCell.mfValue;
     488           0 :         break;
     489             :         case CELLTYPE_STRING:
     490           0 :             aString = rCell.mpString->getString();
     491           0 :             bIsVal = false;
     492           0 :         break;
     493             :         case CELLTYPE_EDIT:
     494           0 :             if (rCell.mpEditText)
     495           0 :                 aString = ScEditUtil::GetString(*rCell.mpEditText, GetDocument());
     496           0 :             bIsVal = false;
     497           0 :         break;
     498             :         case CELLTYPE_FORMULA:
     499             :         {
     500           0 :             ScFormulaCell* pFCell = rCell.mpFormula;
     501           0 :             bIsVal = pFCell->IsValue();
     502           0 :             if ( bIsVal )
     503           0 :                 nVal  = pFCell->GetValue();
     504             :             else
     505           0 :                 aString = pFCell->GetString().getString();
     506             :         }
     507           0 :         break;
     508             :         default:                        // Notes, Broadcaster
     509           0 :             return IsIgnoreBlank();     // as set
     510             :     }
     511             : 
     512           0 :     bool bOk = true;
     513           0 :     switch (eDataMode)
     514             :     {
     515             :         // SC_VALID_ANY schon oben
     516             : 
     517             :         case SC_VALID_WHOLE:
     518             :         case SC_VALID_DECIMAL:
     519             :         case SC_VALID_DATE:         // Date/Time is only formatting
     520             :         case SC_VALID_TIME:
     521           0 :             bOk = bIsVal;
     522           0 :             if ( bOk && eDataMode == SC_VALID_WHOLE )
     523           0 :                 bOk = ::rtl::math::approxEqual( nVal, floor(nVal+0.5) );        // integers
     524           0 :             if ( bOk )
     525           0 :                 bOk = IsCellValid(rCell, rPos);
     526           0 :             break;
     527             : 
     528             :         case SC_VALID_CUSTOM:
     529             :             //  for Custom, it must be eOp == SC_COND_DIRECT
     530             :             //! the value must be in the document !!!
     531           0 :             bOk = IsCellValid(rCell, rPos);
     532           0 :             break;
     533             : 
     534             :         case SC_VALID_TEXTLEN:
     535           0 :             bOk = !bIsVal;          // only Text
     536           0 :             if ( bOk )
     537             :             {
     538           0 :                 double nLenVal = (double) aString.getLength();
     539           0 :                 ScRefCellValue aTmpCell(nLenVal);
     540           0 :                 bOk = IsCellValid(aTmpCell, rPos);
     541             :             }
     542           0 :             break;
     543             : 
     544             :         default:
     545             :             OSL_FAIL("not yet done");
     546           0 :             break;
     547             :     }
     548             : 
     549           0 :     return bOk;
     550             : }
     551             : 
     552             : namespace {
     553             : 
     554             : /** Token array helper. Iterates over all string tokens.
     555             :     @descr  The token array must contain separated string tokens only.
     556             :     @param bSkipEmpty  true = Ignores string tokens with empty strings. */
     557          10 : class ScStringTokenIterator
     558             : {
     559             : public:
     560          10 :     inline explicit             ScStringTokenIterator( ScTokenArray& rTokArr, bool bSkipEmpty = true ) :
     561          10 :                                     mrTokArr( rTokArr ), mbSkipEmpty( bSkipEmpty ), mbOk( true ) {}
     562             : 
     563             :     /** Returns the string of the first string token or NULL on error or empty token array. */
     564             :     rtl_uString* First();
     565             :     /** Returns the string of the next string token or NULL on error or end of token array. */
     566             :     rtl_uString* Next();
     567             : 
     568             :     /** Returns false, if a wrong token has been found. Does NOT return false on end of token array. */
     569          10 :     inline bool                 Ok() const { return mbOk; }
     570             : 
     571             : private:
     572             :     svl::SharedString maCurString; /// Current string.
     573             :     ScTokenArray&               mrTokArr;       /// The token array for iteration.
     574             :     bool                        mbSkipEmpty;    /// Ignore empty strings.
     575             :     bool                        mbOk;           /// true = correct token or end of token array.
     576             : };
     577             : 
     578          10 : rtl_uString* ScStringTokenIterator::First()
     579             : {
     580          10 :     mrTokArr.Reset();
     581          10 :     mbOk = true;
     582          10 :     return Next();
     583             : }
     584             : 
     585          10 : rtl_uString* ScStringTokenIterator::Next()
     586             : {
     587          10 :     if( !mbOk )
     588           0 :         return NULL;
     589             : 
     590             :     // seek to next non-separator token
     591          10 :     const FormulaToken* pToken = mrTokArr.NextNoSpaces();
     592          20 :     while( pToken && (pToken->GetOpCode() == ocSep) )
     593           0 :         pToken = mrTokArr.NextNoSpaces();
     594             : 
     595          10 :     mbOk = !pToken || (pToken->GetType() == formula::svString);
     596             : 
     597          10 :     maCurString = svl::SharedString(); // start with invalid string.
     598          10 :     if (mbOk && pToken)
     599           0 :         maCurString = pToken->GetString();
     600             : 
     601             :     // string found but empty -> get next token; otherwise return it
     602          10 :     return (mbSkipEmpty && maCurString.isValid() && maCurString.isEmpty()) ? Next() : maCurString.getData();
     603             : }
     604             : 
     605             : /** Returns the number format of the passed cell, or the standard format. */
     606          10 : sal_uLong lclGetCellFormat( ScDocument& rDoc, const ScAddress& rPos )
     607             : {
     608          10 :     const ScPatternAttr* pPattern = rDoc.GetPattern( rPos.Col(), rPos.Row(), rPos.Tab() );
     609          10 :     if( !pPattern )
     610           0 :         pPattern = rDoc.GetDefPattern();
     611          10 :     return pPattern->GetNumberFormat( rDoc.GetFormatTable() );
     612             : }
     613             : 
     614             : } // namespace
     615             : 
     616          10 : bool ScValidationData::HasSelectionList() const
     617             : {
     618          10 :     return (eDataMode == SC_VALID_LIST) && (mnListType != css::sheet::TableValidationVisibility::INVISIBLE);
     619             : }
     620             : 
     621          10 : bool ScValidationData::GetSelectionFromFormula(
     622             :     std::vector<ScTypedStrData>* pStrings, ScRefCellValue& rCell, const ScAddress& rPos,
     623             :     const ScTokenArray& rTokArr, int& rMatch) const
     624             : {
     625          10 :     bool bOk = true;
     626             : 
     627             :     // pDoc is private in condition, use an accessor and a long winded name.
     628          10 :     ScDocument* pDocument = GetDocument();
     629          10 :     if( NULL == pDocument )
     630           0 :         return false;
     631             : 
     632             :     ScFormulaCell aValidationSrc(
     633          10 :         pDocument, rPos, rTokArr, formula::FormulaGrammar::GRAM_DEFAULT, MM_FORMULA);
     634             : 
     635             :     // Make sure the formula gets interpreted and a result is delivered,
     636             :     // regardless of the AutoCalc setting.
     637          10 :     aValidationSrc.Interpret();
     638             : 
     639          20 :     ScMatrixRef xMatRef;
     640          10 :     const ScMatrix *pValues = aValidationSrc.GetMatrix();
     641          10 :     if (!pValues)
     642             :     {
     643             :         // The somewhat nasty case of either an error occurred, or the
     644             :         // dereferenced value of a single cell reference or an immediate result
     645             :         // is stored as a single value.
     646             : 
     647             :         // Use an interim matrix to create the TypedStrData below.
     648           0 :         xMatRef = new ScMatrix(1, 1, 0.0);
     649             : 
     650           0 :         sal_uInt16 nErrCode = aValidationSrc.GetErrCode();
     651           0 :         if (nErrCode)
     652             :         {
     653             :             /* TODO : to use later in an alert box?
     654             :              * OUString rStrResult = "...";
     655             :              * rStrResult += ScGlobal::GetLongErrorString(nErrCode);
     656             :              */
     657             : 
     658           0 :             xMatRef->PutError( nErrCode, 0, 0);
     659           0 :             bOk = false;
     660             :         }
     661           0 :         else if (aValidationSrc.IsValue())
     662           0 :             xMatRef->PutDouble( aValidationSrc.GetValue(), 0);
     663             :         else
     664             :         {
     665           0 :             svl::SharedString aStr = aValidationSrc.GetString();
     666           0 :             xMatRef->PutString(aStr, 0);
     667             :         }
     668             : 
     669           0 :         pValues = xMatRef.get();
     670             :     }
     671             : 
     672             :     // which index matched.  We will want it eventually to pre-select that item.
     673          10 :     rMatch = -1;
     674             : 
     675          10 :     SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable();
     676             : 
     677          10 :     SCSIZE  nCol, nRow, nCols, nRows, n = 0;
     678          10 :     pValues->GetDimensions( nCols, nRows );
     679             : 
     680          10 :     bool bRef = false;
     681          10 :     ScRange aRange;
     682             : 
     683          10 :     ScTokenArray* pArr = (ScTokenArray*) &rTokArr;
     684          10 :     pArr->Reset();
     685          10 :     formula::FormulaToken* t = NULL;
     686          10 :     if (pArr->GetLen() == 1 && (t = pArr->GetNextReferenceOrName()) != NULL)
     687             :     {
     688          10 :         if (t->GetOpCode() == ocDBArea)
     689             :         {
     690           0 :             if (const ScDBData* pDBData = pDocument->GetDBCollection()->getNamedDBs().findByIndex(t->GetIndex()))
     691             :             {
     692           0 :                 pDBData->GetArea(aRange);
     693           0 :                 bRef = true;
     694             :             }
     695             :         }
     696          10 :         else if (t->GetOpCode() == ocName)
     697             :         {
     698           0 :             ScRangeData* pName = pDocument->GetRangeName()->findByIndex( t->GetIndex() );
     699           0 :             if (pName && pName->IsReference(aRange))
     700             :             {
     701           0 :                 bRef = true;
     702             :             }
     703             :         }
     704          10 :         else if (t->GetType() != svIndex)
     705             :         {
     706          10 :             if (pArr->IsValidReference(aRange, rPos))
     707             :             {
     708          10 :                 bRef = true;
     709             :             }
     710             :         }
     711             :     }
     712             : 
     713          10 :     bool bHaveEmpty = false;
     714          10 :     svl::SharedStringPool& rSPool = pDocument->GetSharedStringPool();
     715             : 
     716             :     /* XL artificially limits things to a single col or row in the UI but does
     717             :      * not list the constraint in MOOXml. If a defined name or INDIRECT
     718             :      * resulting in 1D is entered in the UI and the definition later modified
     719             :      * to 2D, it is evaluated fine and also stored and loaded.  Lets get ahead
     720             :      * of the curve and support 2d. In XL, values are listed row-wise, do the
     721             :      * same. */
     722          40 :     for( nRow = 0; nRow < nRows ; nRow++ )
     723             :     {
     724          60 :         for( nCol = 0; nCol < nCols ; nCol++ )
     725             :         {
     726          30 :             ScTokenArray         aCondTokArr;
     727          30 :             ScTypedStrData*        pEntry = NULL;
     728          60 :             OUString               aValStr;
     729          60 :             ScMatrixValue nMatVal = pValues->Get( nCol, nRow);
     730             : 
     731             :             // strings and empties
     732          30 :             if( ScMatrix::IsNonValueType( nMatVal.nType ) )
     733             :             {
     734           0 :                 aValStr = nMatVal.GetString().getString();
     735             : 
     736             :                 // Do not add multiple empty strings to the validation list,
     737             :                 // especially not if they'd bloat the tail with a million empty
     738             :                 // entries for a column range, fdo#61520
     739           0 :                 if (aValStr.isEmpty())
     740             :                 {
     741           0 :                     if (bHaveEmpty)
     742           0 :                         continue;
     743           0 :                     bHaveEmpty = true;
     744             :                 }
     745             : 
     746           0 :                 if( NULL != pStrings )
     747           0 :                     pEntry = new ScTypedStrData( aValStr, 0.0, ScTypedStrData::Standard);
     748             : 
     749           0 :                 if (!rCell.isEmpty() && rMatch < 0)
     750           0 :                     aCondTokArr.AddString(rSPool.intern(aValStr));
     751             :             }
     752             :             else
     753             :             {
     754          30 :                 sal_uInt16 nErr = nMatVal.GetError();
     755             : 
     756          30 :                 if( 0 != nErr )
     757             :                 {
     758           0 :                     aValStr = ScGlobal::GetErrorString( nErr );
     759             :                 }
     760             :                 else
     761             :                 {
     762             :                     // FIXME FIXME FIXME
     763             :                     // Feature regression.  Date formats are lost passing through the matrix
     764             :                     //pFormatter->GetInputLineString( pMatVal->fVal, 0, aValStr );
     765             :                     //For external reference and a formula that results in an area or array, date formats are still lost.
     766          30 :                     if ( bRef )
     767             :                     {
     768          30 :                         pDocument->GetInputString((SCCOL)(nCol+aRange.aStart.Col()),
     769          60 :                             (SCROW)(nRow+aRange.aStart.Row()), aRange.aStart.Tab() , aValStr);
     770             :                     }
     771             :                     else
     772             :                     {
     773           0 :                         pFormatter->GetInputLineString( nMatVal.fVal, 0, aValStr );
     774             :                     }
     775             :                 }
     776             : 
     777          30 :                 if (!rCell.isEmpty() && rMatch < 0)
     778             :                 {
     779             :                     // I am not sure errors will work here, but a user can no
     780             :                     // manually enter an error yet so the point is somewhat moot.
     781           0 :                     aCondTokArr.AddDouble( nMatVal.fVal );
     782             :                 }
     783          30 :                 if( NULL != pStrings )
     784          30 :                     pEntry = new ScTypedStrData( aValStr, nMatVal.fVal, ScTypedStrData::Value);
     785             :             }
     786             : 
     787          30 :             if (rMatch < 0 && !rCell.isEmpty() && IsEqualToTokenArray(rCell, rPos, aCondTokArr))
     788             :             {
     789           0 :                 rMatch = n;
     790             :                 // short circuit on the first match if not filling the list
     791           0 :                 if( NULL == pStrings )
     792           0 :                     return true;
     793             :             }
     794             : 
     795          30 :             if( NULL != pEntry )
     796             :             {
     797          30 :                 pStrings->push_back(*pEntry);
     798          30 :                 delete pEntry;
     799          30 :                 n++;
     800             :             }
     801          30 :         }
     802             :     }
     803             : 
     804             :     // In case of no match needed and an error occurred, return that error
     805             :     // entry as valid instead of silently failing.
     806          20 :     return bOk || rCell.isEmpty();
     807             : }
     808             : 
     809          10 : bool ScValidationData::FillSelectionList(std::vector<ScTypedStrData>& rStrColl, const ScAddress& rPos) const
     810             : {
     811          10 :     bool bOk = false;
     812             : 
     813          10 :     if( HasSelectionList() )
     814             :     {
     815          10 :         boost::scoped_ptr<ScTokenArray> pTokArr( CreateTokenArry(0) );
     816             : 
     817             :         // *** try if formula is a string list ***
     818             : 
     819          10 :         sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos );
     820          20 :         ScStringTokenIterator aIt( *pTokArr );
     821          10 :         for (rtl_uString* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next())
     822             :         {
     823             :             double fValue;
     824           0 :             OUString aStr(pString);
     825           0 :             bool bIsValue = GetDocument()->GetFormatTable()->IsNumberFormat(aStr, nFormat, fValue);
     826             :             rStrColl.push_back(
     827             :                 ScTypedStrData(
     828           0 :                     aStr, fValue, bIsValue ? ScTypedStrData::Value : ScTypedStrData::Standard));
     829           0 :         }
     830          10 :         bOk = aIt.Ok();
     831             : 
     832             :         // *** if not a string list, try if formula results in a cell range or
     833             :         // anything else we recognize as valid ***
     834             : 
     835          10 :         if (!bOk)
     836             :         {
     837             :             int nMatch;
     838          10 :             ScRefCellValue aEmptyCell;
     839          10 :             bOk = GetSelectionFromFormula(&rStrColl, aEmptyCell, rPos, *pTokArr, nMatch);
     840          10 :         }
     841             :     }
     842             : 
     843          10 :     return bOk;
     844             : }
     845             : 
     846           0 : bool ScValidationData::IsEqualToTokenArray( ScRefCellValue& rCell, const ScAddress& rPos, const ScTokenArray& rTokArr ) const
     847             : {
     848             :     // create a condition entry that tests on equality and set the passed token array
     849           0 :     ScConditionEntry aCondEntry( SC_COND_EQUAL, &rTokArr, NULL, GetDocument(), rPos );
     850           0 :     return aCondEntry.IsCellValid(rCell, rPos);
     851             : }
     852             : 
     853           0 : bool ScValidationData::IsListValid( ScRefCellValue& rCell, const ScAddress& rPos ) const
     854             : {
     855           0 :     bool bIsValid = false;
     856             : 
     857             :     /*  Compare input cell with all supported tokens from the formula.
     858             :         Currently a formula may contain:
     859             :         1)  A list of strings (at least one string).
     860             :         2)  A single cell or range reference.
     861             :         3)  A single defined name (must contain a cell/range reference, another
     862             :             name, or DB range, or a formula resulting in a cell/range reference
     863             :             or matrix/array).
     864             :         4)  A single database range.
     865             :         5)  A formula resulting in a cell/range reference or matrix/array.
     866             :     */
     867             : 
     868           0 :     boost::scoped_ptr< ScTokenArray > pTokArr( CreateTokenArry( 0 ) );
     869             : 
     870             :     // *** try if formula is a string list ***
     871             : 
     872           0 :     svl::SharedStringPool& rSPool = GetDocument()->GetSharedStringPool();
     873           0 :     sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos );
     874           0 :     ScStringTokenIterator aIt( *pTokArr );
     875           0 :     for (rtl_uString* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next())
     876             :     {
     877             :         /*  Do not break the loop, if a valid string has been found.
     878             :             This is to find invalid tokens following in the formula. */
     879           0 :         if( !bIsValid )
     880             :         {
     881             :             // create a formula containing a single string or number
     882           0 :             ScTokenArray aCondTokArr;
     883             :             double fValue;
     884           0 :             OUString aStr(pString);
     885           0 :             if (GetDocument()->GetFormatTable()->IsNumberFormat(aStr, nFormat, fValue))
     886           0 :                 aCondTokArr.AddDouble( fValue );
     887             :             else
     888           0 :                 aCondTokArr.AddString(rSPool.intern(aStr));
     889             : 
     890           0 :             bIsValid = IsEqualToTokenArray(rCell, rPos, aCondTokArr);
     891             :         }
     892             :     }
     893             : 
     894           0 :     if( !aIt.Ok() )
     895           0 :         bIsValid = false;
     896             : 
     897             :     // *** if not a string list, try if formula results in a cell range or
     898             :     // anything else we recognize as valid ***
     899             : 
     900           0 :     if (!bIsValid)
     901             :     {
     902             :         int nMatch;
     903           0 :         bIsValid = GetSelectionFromFormula(NULL, rCell, rPos, *pTokArr, nMatch);
     904           0 :         bIsValid = bIsValid && nMatch >= 0;
     905             :     }
     906             : 
     907           0 :     return bIsValid;
     908             : }
     909             : 
     910           0 : ScValidationDataList::ScValidationDataList(const ScValidationDataList& rList)
     911             : {
     912             :     //  for Ref-Undo - real copy with new tokens!
     913             : 
     914           0 :     for (const_iterator it = rList.begin(); it != rList.end(); ++it)
     915             :     {
     916           0 :         InsertNew( (*it)->Clone() );
     917             :     }
     918             : 
     919             :     //!      faster insert for sorted entries from rList ???
     920           0 : }
     921             : 
     922           4 : ScValidationDataList::ScValidationDataList(ScDocument* pNewDoc,
     923           4 :                                             const ScValidationDataList& rList)
     924             : {
     925             :     //  for new documents - real copy with new tokens!
     926             : 
     927          26 :     for (const_iterator it = rList.begin(); it != rList.end(); ++it)
     928             :     {
     929          22 :         InsertNew( (*it)->Clone(pNewDoc) );
     930             :     }
     931             : 
     932             :     //!      faster insert for sorted entries from rList ???
     933           4 : }
     934             : 
     935         166 : ScValidationData* ScValidationDataList::GetData( sal_uInt32 nKey )
     936             : {
     937             :     //! binary search
     938             : 
     939         404 :     for( iterator it = begin(); it != end(); ++it )
     940         404 :         if( (*it)->GetKey() == nKey )
     941         166 :             return *it;
     942             : 
     943             :     OSL_FAIL("ScValidationDataList: Entry not found");
     944           0 :     return NULL;
     945             : }
     946             : 
     947           2 : void ScValidationDataList::CompileXML()
     948             : {
     949           6 :     for( iterator it = begin(); it != end(); ++it )
     950           4 :         (*it)->CompileXML();
     951           2 : }
     952             : 
     953          14 : void ScValidationDataList::UpdateReference( sc::RefUpdateContext& rCxt )
     954             : {
     955          46 :     for( iterator it = begin(); it != end(); ++it )
     956          32 :         (*it)->UpdateReference(rCxt);
     957          14 : }
     958             : 
     959           0 : void ScValidationDataList::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
     960             : {
     961           0 :     for (iterator it = begin(); it != end(); ++it)
     962           0 :         (*it)->UpdateInsertTab(rCxt);
     963           0 : }
     964             : 
     965           0 : void ScValidationDataList::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
     966             : {
     967           0 :     for (iterator it = begin(); it != end(); ++it)
     968           0 :         (*it)->UpdateDeleteTab(rCxt);
     969           0 : }
     970             : 
     971           0 : void ScValidationDataList::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt )
     972             : {
     973           0 :     for (iterator it = begin(); it != end(); ++it)
     974           0 :         (*it)->UpdateMoveTab(rCxt);
     975           0 : }
     976             : 
     977           0 : bool ScValidationDataList::operator==( const ScValidationDataList& r ) const
     978             : {
     979             :     // for Ref-Undo - internal variables can not be compared
     980             : 
     981           0 :     size_t nCount = maData.size();
     982           0 :     bool bEqual = ( nCount == r.maData.size() );
     983           0 :     for( const_iterator it1 = begin(), it2 = r.begin(); it1 != end() && bEqual; ++it1, ++it2 ) // Entries are sorted
     984           0 :         if ( !(*it1)->EqualEntries(**it2) )         // different entries ?
     985           0 :             bEqual = false;
     986             : 
     987           0 :     return bEqual;
     988             : }
     989             : 
     990         268 : ScValidationDataList::iterator ScValidationDataList::begin()
     991             : {
     992         268 :     return maData.begin();
     993             : }
     994             : 
     995           4 : ScValidationDataList::const_iterator ScValidationDataList::begin() const
     996             : {
     997           4 :     return maData.begin();
     998             : }
     999             : 
    1000         722 : ScValidationDataList::iterator ScValidationDataList::end()
    1001             : {
    1002         722 :     return maData.end();
    1003             : }
    1004             : 
    1005          26 : ScValidationDataList::const_iterator ScValidationDataList::end() const
    1006             : {
    1007          26 :     return maData.end();
    1008             : }
    1009             : 
    1010          18 : void ScValidationDataList::clear()
    1011             : {
    1012          18 :     maData.clear();
    1013         246 : }
    1014             : 
    1015             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10