LCOV - code coverage report
Current view: top level - sc/source/core/data - validat.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 163 436 37.4 %
Date: 2015-06-13 12:38:46 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          67 : 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          67 :     , mnListType( css::sheet::TableValidationVisibility::UNSORTED )
      74             : {
      75          67 : }
      76             : 
      77           3 : 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           3 :     , mnListType( css::sheet::TableValidationVisibility::UNSORTED )
      87             : {
      88           3 : }
      89             : 
      90           3 : 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           3 :     , aErrorMessage( r.aErrorMessage )
     102             : {
     103             :     //  Formulae copied by RefCount
     104           3 : }
     105             : 
     106          28 : 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          28 :     , aErrorMessage( r.aErrorMessage )
     118             : {
     119             :     //  Formulae really copied
     120          28 : }
     121             : 
     122         145 : ScValidationData::~ScValidationData()
     123             : {
     124         145 : }
     125             : 
     126          34 : bool ScValidationData::IsEmpty() const
     127             : {
     128          34 :     OUString aEmpty;
     129          68 :     ScValidationData aDefault( SC_VALID_ANY, SC_COND_EQUAL, aEmpty, aEmpty, GetDocument(), ScAddress() );
     130          68 :     return EqualEntries( aDefault );
     131             : }
     132             : 
     133         115 : bool ScValidationData::EqualEntries( const ScValidationData& r ) const
     134             : {
     135             :         //  test same parameters (excluding Key)
     136             : 
     137         155 :     return ScConditionEntry::operator==(r) &&
     138          80 :             eDataMode       == r.eDataMode &&
     139          80 :             bShowInput      == r.bShowInput &&
     140          80 :             bShowError      == r.bShowError &&
     141          80 :             eErrorStyle     == r.eErrorStyle &&
     142          80 :             mnListType      == r.mnListType &&
     143          73 :             aInputTitle     == r.aInputTitle &&
     144          61 :             aInputMessage   == r.aInputMessage &&
     145         165 :             aErrorTitle     == r.aErrorTitle &&
     146         137 :             aErrorMessage   == r.aErrorMessage;
     147             : }
     148             : 
     149          30 : void ScValidationData::ResetInput()
     150             : {
     151          30 :     bShowInput = false;
     152          30 : }
     153             : 
     154          16 : void ScValidationData::ResetError()
     155             : {
     156          16 :     bShowError = false;
     157          16 : }
     158             : 
     159          30 : void ScValidationData::SetInput( const OUString& rTitle, const OUString& rMsg )
     160             : {
     161          30 :     bShowInput = true;
     162          30 :     aInputTitle = rTitle;
     163          30 :     aInputMessage = rMsg;
     164          30 : }
     165             : 
     166          35 : void ScValidationData::SetError( const OUString& rTitle, const OUString& rMsg,
     167             :                                     ScValidErrorStyle eStyle )
     168             : {
     169          35 :     bShowError = true;
     170          35 :     eErrorStyle = eStyle;
     171          35 :     aErrorTitle = rTitle;
     172          35 :     aErrorMessage = rMsg;
     173          35 : }
     174             : 
     175          48 : bool ScValidationData::GetErrMsg( OUString& rTitle, OUString& rMsg,
     176             :                                     ScValidErrorStyle& rStyle ) const
     177             : {
     178          48 :     rTitle = aErrorTitle;
     179          48 :     rMsg   = aErrorMessage;
     180          48 :     rStyle = eErrorStyle;
     181          48 :     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 || !ScDocument::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() == cppu::UnoType<bool>::get() &&
     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             :         //TODO: different error message, if found, but not bAllowed ??
     255             : 
     256           0 :         ScopedVclPtrInstance< 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 || !ScDocument::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             :         //TODO: different error message, if found, but not bAllowed ??
     361             : 
     362           0 :         ScopedVclPtrInstance< 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             :     //TODO: ErrorBox / WarningBox / InfoBox ?
     393             :     //TODO: (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 :     ScopedVclPtrInstance< 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             :             //TODO: 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           5 : class ScStringTokenIterator
     558             : {
     559             : public:
     560           5 :     inline explicit             ScStringTokenIterator( ScTokenArray& rTokArr, bool bSkipEmpty = true ) :
     561           5 :                                     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           5 :     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           5 : rtl_uString* ScStringTokenIterator::First()
     579             : {
     580           5 :     mrTokArr.Reset();
     581           5 :     mbOk = true;
     582           5 :     return Next();
     583             : }
     584             : 
     585           5 : rtl_uString* ScStringTokenIterator::Next()
     586             : {
     587           5 :     if( !mbOk )
     588           0 :         return NULL;
     589             : 
     590             :     // seek to next non-separator token
     591           5 :     const FormulaToken* pToken = mrTokArr.NextNoSpaces();
     592          10 :     while( pToken && (pToken->GetOpCode() == ocSep) )
     593           0 :         pToken = mrTokArr.NextNoSpaces();
     594             : 
     595           5 :     mbOk = !pToken || (pToken->GetType() == formula::svString);
     596             : 
     597           5 :     maCurString = svl::SharedString(); // start with invalid string.
     598           5 :     if (mbOk && pToken)
     599           0 :         maCurString = pToken->GetString();
     600             : 
     601             :     // string found but empty -> get next token; otherwise return it
     602           5 :     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           5 : sal_uLong lclGetCellFormat( ScDocument& rDoc, const ScAddress& rPos )
     607             : {
     608           5 :     const ScPatternAttr* pPattern = rDoc.GetPattern( rPos.Col(), rPos.Row(), rPos.Tab() );
     609           5 :     if( !pPattern )
     610           0 :         pPattern = rDoc.GetDefPattern();
     611           5 :     return pPattern->GetNumberFormat( rDoc.GetFormatTable() );
     612             : }
     613             : 
     614             : } // namespace
     615             : 
     616           5 : bool ScValidationData::HasSelectionList() const
     617             : {
     618           5 :     return (eDataMode == SC_VALID_LIST) && (mnListType != css::sheet::TableValidationVisibility::INVISIBLE);
     619             : }
     620             : 
     621           5 : bool ScValidationData::GetSelectionFromFormula(
     622             :     std::vector<ScTypedStrData>* pStrings, ScRefCellValue& rCell, const ScAddress& rPos,
     623             :     const ScTokenArray& rTokArr, int& rMatch) const
     624             : {
     625           5 :     bool bOk = true;
     626             : 
     627             :     // pDoc is private in condition, use an accessor and a long winded name.
     628           5 :     ScDocument* pDocument = GetDocument();
     629           5 :     if( NULL == pDocument )
     630           0 :         return false;
     631             : 
     632             :     ScFormulaCell aValidationSrc(
     633           5 :         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           5 :     aValidationSrc.Interpret();
     638             : 
     639          10 :     ScMatrixRef xMatRef;
     640           5 :     const ScMatrix *pValues = aValidationSrc.GetMatrix();
     641           5 :     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           5 :     rMatch = -1;
     674             : 
     675           5 :     SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable();
     676             : 
     677           5 :     SCSIZE  nCol, nRow, nCols, nRows, n = 0;
     678           5 :     pValues->GetDimensions( nCols, nRows );
     679             : 
     680           5 :     bool bRef = false;
     681           5 :     ScRange aRange;
     682             : 
     683           5 :     ScTokenArray* pArr = const_cast<ScTokenArray*>(&rTokArr);
     684           5 :     pArr->Reset();
     685           5 :     formula::FormulaToken* t = NULL;
     686           5 :     if (pArr->GetLen() == 1 && (t = pArr->GetNextReferenceOrName()) != NULL)
     687             :     {
     688           5 :         OpCode eOpCode = t->GetOpCode();
     689           5 :         if (eOpCode == ocDBArea || eOpCode == ocTableRef)
     690             :         {
     691           0 :             if (const ScDBData* pDBData = pDocument->GetDBCollection()->getNamedDBs().findByIndex(t->GetIndex()))
     692             :             {
     693           0 :                 pDBData->GetArea(aRange);
     694           0 :                 bRef = true;
     695           0 :             }
     696             :         }
     697           5 :         else if (eOpCode == ocName)
     698             :         {
     699           0 :             ScRangeData* pName = pDocument->GetRangeName()->findByIndex( t->GetIndex() );
     700           0 :             if (pName && pName->IsReference(aRange))
     701             :             {
     702           0 :                 bRef = true;
     703             :             }
     704             :         }
     705           5 :         else if (t->GetType() != svIndex)
     706             :         {
     707           5 :             if (pArr->IsValidReference(aRange, rPos))
     708             :             {
     709           5 :                 bRef = true;
     710             :             }
     711             :         }
     712             :     }
     713             : 
     714           5 :     bool bHaveEmpty = false;
     715           5 :     svl::SharedStringPool& rSPool = pDocument->GetSharedStringPool();
     716             : 
     717             :     /* XL artificially limits things to a single col or row in the UI but does
     718             :      * not list the constraint in MOOXml. If a defined name or INDIRECT
     719             :      * resulting in 1D is entered in the UI and the definition later modified
     720             :      * to 2D, it is evaluated fine and also stored and loaded.  Lets get ahead
     721             :      * of the curve and support 2d. In XL, values are listed row-wise, do the
     722             :      * same. */
     723          20 :     for( nRow = 0; nRow < nRows ; nRow++ )
     724             :     {
     725          30 :         for( nCol = 0; nCol < nCols ; nCol++ )
     726             :         {
     727          15 :             ScTokenArray         aCondTokArr;
     728          15 :             ScTypedStrData*        pEntry = NULL;
     729          30 :             OUString               aValStr;
     730          30 :             ScMatrixValue nMatVal = pValues->Get( nCol, nRow);
     731             : 
     732             :             // strings and empties
     733          15 :             if( ScMatrix::IsNonValueType( nMatVal.nType ) )
     734             :             {
     735           0 :                 aValStr = nMatVal.GetString().getString();
     736             : 
     737             :                 // Do not add multiple empty strings to the validation list,
     738             :                 // especially not if they'd bloat the tail with a million empty
     739             :                 // entries for a column range, fdo#61520
     740           0 :                 if (aValStr.isEmpty())
     741             :                 {
     742           0 :                     if (bHaveEmpty)
     743           0 :                         continue;
     744           0 :                     bHaveEmpty = true;
     745             :                 }
     746             : 
     747           0 :                 if( NULL != pStrings )
     748           0 :                     pEntry = new ScTypedStrData( aValStr, 0.0, ScTypedStrData::Standard);
     749             : 
     750           0 :                 if (!rCell.isEmpty() && rMatch < 0)
     751           0 :                     aCondTokArr.AddString(rSPool.intern(aValStr));
     752             :             }
     753             :             else
     754             :             {
     755          15 :                 sal_uInt16 nErr = nMatVal.GetError();
     756             : 
     757          15 :                 if( 0 != nErr )
     758             :                 {
     759           0 :                     aValStr = ScGlobal::GetErrorString( nErr );
     760             :                 }
     761             :                 else
     762             :                 {
     763             :                     // FIXME FIXME FIXME
     764             :                     // Feature regression.  Date formats are lost passing through the matrix
     765             :                     //pFormatter->GetInputLineString( pMatVal->fVal, 0, aValStr );
     766             :                     //For external reference and a formula that results in an area or array, date formats are still lost.
     767          15 :                     if ( bRef )
     768             :                     {
     769          15 :                         pDocument->GetInputString((SCCOL)(nCol+aRange.aStart.Col()),
     770          30 :                             (SCROW)(nRow+aRange.aStart.Row()), aRange.aStart.Tab() , aValStr);
     771             :                     }
     772             :                     else
     773             :                     {
     774           0 :                         pFormatter->GetInputLineString( nMatVal.fVal, 0, aValStr );
     775             :                     }
     776             :                 }
     777             : 
     778          15 :                 if (!rCell.isEmpty() && rMatch < 0)
     779             :                 {
     780             :                     // I am not sure errors will work here, but a user can no
     781             :                     // manually enter an error yet so the point is somewhat moot.
     782           0 :                     aCondTokArr.AddDouble( nMatVal.fVal );
     783             :                 }
     784          15 :                 if( NULL != pStrings )
     785          15 :                     pEntry = new ScTypedStrData( aValStr, nMatVal.fVal, ScTypedStrData::Value);
     786             :             }
     787             : 
     788          15 :             if (rMatch < 0 && !rCell.isEmpty() && IsEqualToTokenArray(rCell, rPos, aCondTokArr))
     789             :             {
     790           0 :                 rMatch = n;
     791             :                 // short circuit on the first match if not filling the list
     792           0 :                 if( NULL == pStrings )
     793           0 :                     return true;
     794             :             }
     795             : 
     796          15 :             if( NULL != pEntry )
     797             :             {
     798          15 :                 pStrings->push_back(*pEntry);
     799          15 :                 delete pEntry;
     800          15 :                 n++;
     801             :             }
     802          15 :         }
     803             :     }
     804             : 
     805             :     // In case of no match needed and an error occurred, return that error
     806             :     // entry as valid instead of silently failing.
     807          10 :     return bOk || rCell.isEmpty();
     808             : }
     809             : 
     810           5 : bool ScValidationData::FillSelectionList(std::vector<ScTypedStrData>& rStrColl, const ScAddress& rPos) const
     811             : {
     812           5 :     bool bOk = false;
     813             : 
     814           5 :     if( HasSelectionList() )
     815             :     {
     816           5 :         boost::scoped_ptr<ScTokenArray> pTokArr( CreateTokenArry(0) );
     817             : 
     818             :         // *** try if formula is a string list ***
     819             : 
     820           5 :         sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos );
     821          10 :         ScStringTokenIterator aIt( *pTokArr );
     822           5 :         for (rtl_uString* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next())
     823             :         {
     824             :             double fValue;
     825           0 :             OUString aStr(pString);
     826           0 :             bool bIsValue = GetDocument()->GetFormatTable()->IsNumberFormat(aStr, nFormat, fValue);
     827             :             rStrColl.push_back(
     828             :                 ScTypedStrData(
     829           0 :                     aStr, fValue, bIsValue ? ScTypedStrData::Value : ScTypedStrData::Standard));
     830           0 :         }
     831           5 :         bOk = aIt.Ok();
     832             : 
     833             :         // *** if not a string list, try if formula results in a cell range or
     834             :         // anything else we recognize as valid ***
     835             : 
     836           5 :         if (!bOk)
     837             :         {
     838             :             int nMatch;
     839           5 :             ScRefCellValue aEmptyCell;
     840           5 :             bOk = GetSelectionFromFormula(&rStrColl, aEmptyCell, rPos, *pTokArr, nMatch);
     841           5 :         }
     842             :     }
     843             : 
     844           5 :     return bOk;
     845             : }
     846             : 
     847           0 : bool ScValidationData::IsEqualToTokenArray( ScRefCellValue& rCell, const ScAddress& rPos, const ScTokenArray& rTokArr ) const
     848             : {
     849             :     // create a condition entry that tests on equality and set the passed token array
     850           0 :     ScConditionEntry aCondEntry( SC_COND_EQUAL, &rTokArr, NULL, GetDocument(), rPos );
     851           0 :     return aCondEntry.IsCellValid(rCell, rPos);
     852             : }
     853             : 
     854           0 : bool ScValidationData::IsListValid( ScRefCellValue& rCell, const ScAddress& rPos ) const
     855             : {
     856           0 :     bool bIsValid = false;
     857             : 
     858             :     /*  Compare input cell with all supported tokens from the formula.
     859             :         Currently a formula may contain:
     860             :         1)  A list of strings (at least one string).
     861             :         2)  A single cell or range reference.
     862             :         3)  A single defined name (must contain a cell/range reference, another
     863             :             name, or DB range, or a formula resulting in a cell/range reference
     864             :             or matrix/array).
     865             :         4)  A single database range.
     866             :         5)  A formula resulting in a cell/range reference or matrix/array.
     867             :     */
     868             : 
     869           0 :     boost::scoped_ptr< ScTokenArray > pTokArr( CreateTokenArry( 0 ) );
     870             : 
     871             :     // *** try if formula is a string list ***
     872             : 
     873           0 :     svl::SharedStringPool& rSPool = GetDocument()->GetSharedStringPool();
     874           0 :     sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos );
     875           0 :     ScStringTokenIterator aIt( *pTokArr );
     876           0 :     for (rtl_uString* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next())
     877             :     {
     878             :         /*  Do not break the loop, if a valid string has been found.
     879             :             This is to find invalid tokens following in the formula. */
     880           0 :         if( !bIsValid )
     881             :         {
     882             :             // create a formula containing a single string or number
     883           0 :             ScTokenArray aCondTokArr;
     884             :             double fValue;
     885           0 :             OUString aStr(pString);
     886           0 :             if (GetDocument()->GetFormatTable()->IsNumberFormat(aStr, nFormat, fValue))
     887           0 :                 aCondTokArr.AddDouble( fValue );
     888             :             else
     889           0 :                 aCondTokArr.AddString(rSPool.intern(aStr));
     890             : 
     891           0 :             bIsValid = IsEqualToTokenArray(rCell, rPos, aCondTokArr);
     892             :         }
     893             :     }
     894             : 
     895           0 :     if( !aIt.Ok() )
     896           0 :         bIsValid = false;
     897             : 
     898             :     // *** if not a string list, try if formula results in a cell range or
     899             :     // anything else we recognize as valid ***
     900             : 
     901           0 :     if (!bIsValid)
     902             :     {
     903             :         int nMatch;
     904           0 :         bIsValid = GetSelectionFromFormula(NULL, rCell, rPos, *pTokArr, nMatch);
     905           0 :         bIsValid = bIsValid && nMatch >= 0;
     906             :     }
     907             : 
     908           0 :     return bIsValid;
     909             : }
     910             : 
     911           0 : ScValidationDataList::ScValidationDataList(const ScValidationDataList& rList)
     912             : {
     913             :     //  for Ref-Undo - real copy with new tokens!
     914             : 
     915           0 :     for (const_iterator it = rList.begin(); it != rList.end(); ++it)
     916             :     {
     917           0 :         InsertNew( (*it)->Clone() );
     918             :     }
     919             : 
     920             :     //TODO: faster insert for sorted entries from rList ???
     921           0 : }
     922             : 
     923           2 : ScValidationDataList::ScValidationDataList(ScDocument* pNewDoc,
     924           2 :                                             const ScValidationDataList& rList)
     925             : {
     926             :     //  for new documents - real copy with new tokens!
     927             : 
     928          13 :     for (const_iterator it = rList.begin(); it != rList.end(); ++it)
     929             :     {
     930          11 :         InsertNew( (*it)->Clone(pNewDoc) );
     931             :     }
     932             : 
     933             :     //TODO: faster insert for sorted entries from rList ???
     934           2 : }
     935             : 
     936          83 : ScValidationData* ScValidationDataList::GetData( sal_uInt32 nKey )
     937             : {
     938             :     //TODO: binary search
     939             : 
     940         202 :     for( iterator it = begin(); it != end(); ++it )
     941         202 :         if( (*it)->GetKey() == nKey )
     942          83 :             return *it;
     943             : 
     944             :     OSL_FAIL("ScValidationDataList: Entry not found");
     945           0 :     return NULL;
     946             : }
     947             : 
     948           1 : void ScValidationDataList::CompileXML()
     949             : {
     950           3 :     for( iterator it = begin(); it != end(); ++it )
     951           2 :         (*it)->CompileXML();
     952           1 : }
     953             : 
     954           7 : void ScValidationDataList::UpdateReference( sc::RefUpdateContext& rCxt )
     955             : {
     956          23 :     for( iterator it = begin(); it != end(); ++it )
     957          16 :         (*it)->UpdateReference(rCxt);
     958           7 : }
     959             : 
     960           0 : void ScValidationDataList::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
     961             : {
     962           0 :     for (iterator it = begin(); it != end(); ++it)
     963           0 :         (*it)->UpdateInsertTab(rCxt);
     964           0 : }
     965             : 
     966           0 : void ScValidationDataList::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
     967             : {
     968           0 :     for (iterator it = begin(); it != end(); ++it)
     969           0 :         (*it)->UpdateDeleteTab(rCxt);
     970           0 : }
     971             : 
     972           0 : void ScValidationDataList::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt )
     973             : {
     974           0 :     for (iterator it = begin(); it != end(); ++it)
     975           0 :         (*it)->UpdateMoveTab(rCxt);
     976           0 : }
     977             : 
     978           0 : bool ScValidationDataList::operator==( const ScValidationDataList& r ) const
     979             : {
     980             :     // for Ref-Undo - internal variables can not be compared
     981             : 
     982           0 :     size_t nCount = maData.size();
     983           0 :     bool bEqual = ( nCount == r.maData.size() );
     984           0 :     for( const_iterator it1 = begin(), it2 = r.begin(); it1 != end() && bEqual; ++it1, ++it2 ) // Entries are sorted
     985           0 :         if ( !(*it1)->EqualEntries(**it2) )         // different entries ?
     986           0 :             bEqual = false;
     987             : 
     988           0 :     return bEqual;
     989             : }
     990             : 
     991         134 : ScValidationDataList::iterator ScValidationDataList::begin()
     992             : {
     993         134 :     return maData.begin();
     994             : }
     995             : 
     996           2 : ScValidationDataList::const_iterator ScValidationDataList::begin() const
     997             : {
     998           2 :     return maData.begin();
     999             : }
    1000             : 
    1001         361 : ScValidationDataList::iterator ScValidationDataList::end()
    1002             : {
    1003         361 :     return maData.end();
    1004             : }
    1005             : 
    1006          13 : ScValidationDataList::const_iterator ScValidationDataList::end() const
    1007             : {
    1008          13 :     return maData.end();
    1009             : }
    1010             : 
    1011           9 : void ScValidationDataList::clear()
    1012             : {
    1013           9 :     maData.clear();
    1014         165 : }
    1015             : 
    1016             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11