| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_sc.hxx" |
| |
| |
| |
| // INCLUDE --------------------------------------------------------------- |
| |
| #include "scitems.hxx" |
| #include <sfx2/app.hxx> |
| #include <sfx2/docfile.hxx> |
| #include <sfx2/objsh.hxx> |
| #include <basic/sbmeth.hxx> |
| #include <basic/sbmod.hxx> |
| #include <basic/sbstar.hxx> |
| #include <basic/basmgr.hxx> |
| |
| #include <basic/sbx.hxx> |
| #include <svl/zforlist.hxx> |
| #include <vcl/msgbox.hxx> |
| #include <tools/urlobj.hxx> |
| #include <rtl/math.hxx> |
| |
| #include "validat.hxx" |
| #include "document.hxx" |
| #include "cell.hxx" |
| #include "patattr.hxx" |
| #include "rechead.hxx" |
| #include "globstr.hrc" |
| #include "rangenam.hxx" |
| #include "dbcolect.hxx" |
| |
| #include <math.h> |
| #include <memory> |
| |
| using namespace formula; |
| //------------------------------------------------------------------------ |
| |
| SV_IMPL_OP_PTRARR_SORT( ScValidationEntries_Impl, ScValidationDataPtr ); |
| |
| //------------------------------------------------------------------------ |
| |
| // |
| // Eintrag fuer Gueltigkeit (es gibt nur eine Bedingung) |
| // |
| |
| ScValidationData::ScValidationData( ScValidationMode eMode, ScConditionMode eOper, |
| const String& rExpr1, const String& rExpr2, |
| ScDocument* pDocument, const ScAddress& rPos, |
| const String& rExprNmsp1, const String& rExprNmsp2, |
| FormulaGrammar::Grammar eGrammar1, FormulaGrammar::Grammar eGrammar2 ) : |
| ScConditionEntry( eOper, rExpr1, rExpr2, pDocument, rPos, rExprNmsp1, rExprNmsp2, eGrammar1, eGrammar2 ), |
| nKey( 0 ), |
| eDataMode( eMode ), |
| eErrorStyle( SC_VALERR_STOP ), |
| mnListType( ValidListType::UNSORTED ) |
| { |
| bShowInput = bShowError = sal_False; |
| } |
| |
| ScValidationData::ScValidationData( ScValidationMode eMode, ScConditionMode eOper, |
| const ScTokenArray* pArr1, const ScTokenArray* pArr2, |
| ScDocument* pDocument, const ScAddress& rPos ) : |
| ScConditionEntry( eOper, pArr1, pArr2, pDocument, rPos ), |
| nKey( 0 ), |
| eDataMode( eMode ), |
| eErrorStyle( SC_VALERR_STOP ), |
| mnListType( ValidListType::UNSORTED ) |
| { |
| bShowInput = bShowError = sal_False; |
| } |
| |
| ScValidationData::ScValidationData( const ScValidationData& r ) : |
| ScConditionEntry( r ), |
| nKey( r.nKey ), |
| eDataMode( r.eDataMode ), |
| bShowInput( r.bShowInput ), |
| bShowError( r.bShowError ), |
| eErrorStyle( r.eErrorStyle ), |
| mnListType( r.mnListType ), |
| aInputTitle( r.aInputTitle ), |
| aInputMessage( r.aInputMessage ), |
| aErrorTitle( r.aErrorTitle ), |
| aErrorMessage( r.aErrorMessage ) |
| { |
| // Formeln per RefCount kopiert |
| } |
| |
| ScValidationData::ScValidationData( ScDocument* pDocument, const ScValidationData& r ) : |
| ScConditionEntry( pDocument, r ), |
| nKey( r.nKey ), |
| eDataMode( r.eDataMode ), |
| bShowInput( r.bShowInput ), |
| bShowError( r.bShowError ), |
| eErrorStyle( r.eErrorStyle ), |
| mnListType( r.mnListType ), |
| aInputTitle( r.aInputTitle ), |
| aInputMessage( r.aInputMessage ), |
| aErrorTitle( r.aErrorTitle ), |
| aErrorMessage( r.aErrorMessage ) |
| { |
| // Formeln wirklich kopiert |
| } |
| |
| ScValidationData::~ScValidationData() |
| { |
| } |
| |
| sal_Bool ScValidationData::IsEmpty() const |
| { |
| String aEmpty; |
| ScValidationData aDefault( SC_VALID_ANY, SC_COND_EQUAL, aEmpty, aEmpty, GetDocument(), ScAddress() ); |
| return EqualEntries( aDefault ); |
| } |
| |
| sal_Bool ScValidationData::EqualEntries( const ScValidationData& r ) const |
| { |
| // gleiche Parameter eingestellt (ohne Key) |
| |
| return ScConditionEntry::operator==(r) && |
| eDataMode == r.eDataMode && |
| bShowInput == r.bShowInput && |
| bShowError == r.bShowError && |
| eErrorStyle == r.eErrorStyle && |
| mnListType == r.mnListType && |
| aInputTitle == r.aInputTitle && |
| aInputMessage == r.aInputMessage && |
| aErrorTitle == r.aErrorTitle && |
| aErrorMessage == r.aErrorMessage; |
| } |
| |
| void ScValidationData::ResetInput() |
| { |
| bShowInput = sal_False; |
| } |
| |
| void ScValidationData::ResetError() |
| { |
| bShowError = sal_False; |
| } |
| |
| void ScValidationData::SetInput( const String& rTitle, const String& rMsg ) |
| { |
| bShowInput = sal_True; |
| aInputTitle = rTitle; |
| aInputMessage = rMsg; |
| } |
| |
| void ScValidationData::SetError( const String& rTitle, const String& rMsg, |
| ScValidErrorStyle eStyle ) |
| { |
| bShowError = sal_True; |
| eErrorStyle = eStyle; |
| aErrorTitle = rTitle; |
| aErrorMessage = rMsg; |
| } |
| |
| sal_Bool ScValidationData::GetErrMsg( String& rTitle, String& rMsg, |
| ScValidErrorStyle& rStyle ) const |
| { |
| rTitle = aErrorTitle; |
| rMsg = aErrorMessage; |
| rStyle = eErrorStyle; |
| return bShowError; |
| } |
| |
| sal_Bool ScValidationData::DoScript( const ScAddress& rPos, const String& rInput, |
| ScFormulaCell* pCell, Window* pParent ) const |
| { |
| ScDocument* pDocument = GetDocument(); |
| SfxObjectShell* pDocSh = pDocument->GetDocumentShell(); |
| if ( !pDocSh || !pDocument->CheckMacroWarn() ) |
| return sal_False; |
| |
| sal_Bool bScriptReturnedFalse = sal_False; // Standard: kein Abbruch |
| |
| // Set up parameters |
| ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > aParams(2); |
| |
| // 1) eingegebener / berechneter Wert |
| String aValStr = rInput; |
| double nValue; |
| sal_Bool bIsValue = sal_False; |
| if ( pCell ) // wenn Zelle gesetzt, aus Interpret gerufen |
| { |
| bIsValue = pCell->IsValue(); |
| if ( bIsValue ) |
| nValue = pCell->GetValue(); |
| else |
| pCell->GetString( aValStr ); |
| } |
| if ( bIsValue ) |
| aParams[0] = ::com::sun::star::uno::makeAny( nValue ); |
| else |
| aParams[0] = ::com::sun::star::uno::makeAny( ::rtl::OUString( aValStr ) ); |
| |
| // 2) Position der Zelle |
| String aPosStr; |
| rPos.Format( aPosStr, SCA_VALID | SCA_TAB_3D, pDocument, pDocument->GetAddressConvention() ); |
| aParams[1] = ::com::sun::star::uno::makeAny( ::rtl::OUString( aPosStr ) ); |
| |
| // use link-update flag to prevent closing the document |
| // while the macro is running |
| sal_Bool bWasInLinkUpdate = pDocument->IsInLinkUpdate(); |
| if ( !bWasInLinkUpdate ) |
| pDocument->SetInLinkUpdate( sal_True ); |
| |
| if ( pCell ) |
| pDocument->LockTable( rPos.Tab() ); |
| |
| ::com::sun::star::uno::Any aRet; |
| ::com::sun::star::uno::Sequence< sal_Int16 > aOutArgsIndex; |
| ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > aOutArgs; |
| |
| ErrCode eRet = pDocSh->CallXScript( |
| aErrorTitle, aParams, aRet, aOutArgsIndex, aOutArgs ); |
| |
| if ( pCell ) |
| pDocument->UnlockTable( rPos.Tab() ); |
| |
| if ( !bWasInLinkUpdate ) |
| pDocument->SetInLinkUpdate( sal_False ); |
| |
| // Check the return value from the script |
| // The contents of the cell get reset if the script returns false |
| sal_Bool bTmp = sal_False; |
| if ( eRet == ERRCODE_NONE && |
| aRet.getValueType() == getCppuBooleanType() && |
| sal_True == ( aRet >>= bTmp ) && |
| bTmp == sal_False ) |
| { |
| bScriptReturnedFalse = sal_True; |
| } |
| |
| if ( eRet == ERRCODE_BASIC_METHOD_NOT_FOUND && !pCell ) |
| // Makro nicht gefunden (nur bei Eingabe) |
| { |
| //! andere Fehlermeldung, wenn gefunden, aber nicht bAllowed ?? |
| |
| ErrorBox aBox( pParent, WinBits(WB_OK), |
| ScGlobal::GetRscString( STR_VALID_MACRONOTFOUND ) ); |
| aBox.Execute(); |
| } |
| |
| return bScriptReturnedFalse; |
| } |
| |
| // sal_True -> Abbruch |
| |
| sal_Bool ScValidationData::DoMacro( const ScAddress& rPos, const String& rInput, |
| ScFormulaCell* pCell, Window* pParent ) const |
| { |
| if ( SfxApplication::IsXScriptURL( aErrorTitle ) ) |
| { |
| return DoScript( rPos, rInput, pCell, pParent ); |
| } |
| |
| ScDocument* pDocument = GetDocument(); |
| SfxObjectShell* pDocSh = pDocument->GetDocumentShell(); |
| if ( !pDocSh || !pDocument->CheckMacroWarn() ) |
| return sal_False; |
| |
| sal_Bool bDone = sal_False; |
| sal_Bool bRet = sal_False; // Standard: kein Abbruch |
| |
| // Wenn das Dok waehrend eines Basic-Calls geladen wurde, |
| // ist das Sbx-Objekt evtl. nicht angelegt (?) |
| // pDocSh->GetSbxObject(); |
| |
| // keine Sicherheitsabfrage mehr vorneweg (nur CheckMacroWarn), das passiert im CallBasic |
| |
| #if 0 |
| // Makro-Name liegt in folgender Form vor: |
| // "Macroname.Modulname.Libname.Dokumentname" oder |
| // "Macroname.Modulname.Libname.Applikationsname" |
| String aMacroName = aErrorTitle.GetToken(0, '.'); |
| String aModulName = aErrorTitle.GetToken(1, '.'); |
| String aLibName = aErrorTitle.GetToken(2, '.'); |
| String aDocName = aErrorTitle.GetToken(3, '.'); |
| #endif |
| |
| // Funktion ueber den einfachen Namen suchen, |
| // dann aBasicStr, aMacroStr fuer SfxObjectShell::CallBasic zusammenbauen |
| |
| StarBASIC* pRoot = pDocSh->GetBasic(); |
| SbxVariable* pVar = pRoot->Find( aErrorTitle, SbxCLASS_METHOD ); |
| if ( pVar && pVar->ISA(SbMethod) ) |
| { |
| SbMethod* pMethod = (SbMethod*)pVar; |
| SbModule* pModule = pMethod->GetModule(); |
| SbxObject* pObject = pModule->GetParent(); |
| String aMacroStr = pObject->GetName(); |
| aMacroStr += '.'; |
| aMacroStr += pModule->GetName(); |
| aMacroStr += '.'; |
| aMacroStr += pMethod->GetName(); |
| String aBasicStr; |
| |
| // #95867# the distinction between document- and app-basic has to be done |
| // by checking the parent (as in ScInterpreter::ScMacro), not by looping |
| // over all open documents, because this may be called from within loading, |
| // when SfxObjectShell::GetFirst/GetNext won't find the document. |
| |
| if ( pObject->GetParent() ) |
| aBasicStr = pObject->GetParent()->GetName(); // Dokumentenbasic |
| else |
| aBasicStr = SFX_APP()->GetName(); // Applikationsbasic |
| |
| // Parameter fuer Makro |
| SbxArrayRef refPar = new SbxArray; |
| |
| // 1) eingegebener / berechneter Wert |
| String aValStr = rInput; |
| double nValue = 0.0; |
| sal_Bool bIsValue = sal_False; |
| if ( pCell ) // wenn Zelle gesetzt, aus Interpret gerufen |
| { |
| bIsValue = pCell->IsValue(); |
| if ( bIsValue ) |
| nValue = pCell->GetValue(); |
| else |
| pCell->GetString( aValStr ); |
| } |
| if ( bIsValue ) |
| refPar->Get(1)->PutDouble( nValue ); |
| else |
| refPar->Get(1)->PutString( aValStr ); |
| |
| // 2) Position der Zelle |
| String aPosStr; |
| rPos.Format( aPosStr, SCA_VALID | SCA_TAB_3D, pDocument, pDocument->GetAddressConvention() ); |
| refPar->Get(2)->PutString( aPosStr ); |
| |
| // use link-update flag to prevent closing the document |
| // while the macro is running |
| sal_Bool bWasInLinkUpdate = pDocument->IsInLinkUpdate(); |
| if ( !bWasInLinkUpdate ) |
| pDocument->SetInLinkUpdate( sal_True ); |
| |
| if ( pCell ) |
| pDocument->LockTable( rPos.Tab() ); |
| SbxVariableRef refRes = new SbxVariable; |
| ErrCode eRet = pDocSh->CallBasic( aMacroStr, aBasicStr, refPar, refRes ); |
| if ( pCell ) |
| pDocument->UnlockTable( rPos.Tab() ); |
| |
| if ( !bWasInLinkUpdate ) |
| pDocument->SetInLinkUpdate( sal_False ); |
| |
| // Eingabe abbrechen, wenn Basic-Makro sal_False zurueckgibt |
| if ( eRet == ERRCODE_NONE && refRes->GetType() == SbxBOOL && refRes->GetBool() == sal_False ) |
| bRet = sal_True; |
| bDone = sal_True; |
| } |
| |
| if ( !bDone && !pCell ) // Makro nicht gefunden (nur bei Eingabe) |
| { |
| //! andere Fehlermeldung, wenn gefunden, aber nicht bAllowed ?? |
| |
| ErrorBox aBox( pParent, WinBits(WB_OK), |
| ScGlobal::GetRscString( STR_VALID_MACRONOTFOUND ) ); |
| aBox.Execute(); |
| } |
| |
| return bRet; |
| } |
| |
| void ScValidationData::DoCalcError( ScFormulaCell* pCell ) const |
| { |
| if ( eErrorStyle == SC_VALERR_MACRO ) |
| DoMacro( pCell->aPos, EMPTY_STRING, pCell, NULL ); |
| } |
| |
| // sal_True -> Abbruch |
| |
| sal_Bool ScValidationData::DoError( Window* pParent, const String& rInput, |
| const ScAddress& rPos ) const |
| { |
| if ( eErrorStyle == SC_VALERR_MACRO ) |
| return DoMacro( rPos, rInput, NULL, pParent ); |
| |
| // Fehlermeldung ausgeben |
| |
| String aTitle = aErrorTitle; |
| if (!aTitle.Len()) |
| aTitle = ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_0 ); // application title |
| String aMessage = aErrorMessage; |
| if (!aMessage.Len()) |
| aMessage = ScGlobal::GetRscString( STR_VALID_DEFERROR ); |
| |
| //! ErrorBox / WarningBox / InfoBox ? |
| //! (bei InfoBox immer nur OK-Button) |
| |
| WinBits nStyle = 0; |
| switch (eErrorStyle) |
| { |
| case SC_VALERR_STOP: |
| nStyle = WB_OK | WB_DEF_OK; |
| break; |
| case SC_VALERR_WARNING: |
| nStyle = WB_OK_CANCEL | WB_DEF_CANCEL; |
| break; |
| case SC_VALERR_INFO: |
| nStyle = WB_OK_CANCEL | WB_DEF_OK; |
| break; |
| default: |
| { |
| // added to avoid warnings |
| } |
| } |
| |
| MessBox aBox( pParent, WinBits(nStyle), aTitle, aMessage ); |
| sal_uInt16 nRet = aBox.Execute(); |
| |
| return ( eErrorStyle == SC_VALERR_STOP || nRet == RET_CANCEL ); |
| } |
| |
| |
| sal_Bool ScValidationData::IsDataValid( const String& rTest, const ScPatternAttr& rPattern, |
| const ScAddress& rPos ) const |
| { |
| if ( eDataMode == SC_VALID_ANY ) // check if any cell content is allowed |
| return sal_True; |
| |
| if ( rTest.GetChar(0) == '=' ) // formulas do not pass the validity test |
| return sal_False; |
| |
| if ( !rTest.Len() ) // check whether empty cells are allowed |
| return IsIgnoreBlank(); |
| |
| SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable(); |
| |
| // get the value if any |
| sal_uInt32 nFormat = rPattern.GetNumberFormat( pFormatter ); |
| |
| double nVal; |
| sal_Bool bIsVal = pFormatter->IsNumberFormat( rTest, nFormat, nVal ); |
| ScBaseCell* pCell; |
| if (bIsVal) |
| pCell = new ScValueCell( nVal ); |
| else |
| pCell = new ScStringCell( rTest ); |
| |
| sal_Bool bRet; |
| if (SC_VALID_TEXTLEN == eDataMode) |
| { |
| const double nLenVal = static_cast<double>( rTest.Len() ); |
| ScValueCell aTmpCell( nLenVal ); |
| bRet = IsCellValid( &aTmpCell, rPos ); |
| } |
| else |
| bRet = IsDataValid( pCell, rPos ); |
| |
| pCell->Delete(); |
| return bRet; |
| } |
| |
| sal_Bool ScValidationData::IsDataValid( ScBaseCell* pCell, const ScAddress& rPos ) const |
| { |
| if( eDataMode == SC_VALID_LIST ) |
| return IsListValid( pCell, rPos ); |
| |
| double nVal = 0.0; |
| String aString; |
| sal_Bool bIsVal = sal_True; |
| |
| switch (pCell->GetCellType()) |
| { |
| case CELLTYPE_VALUE: |
| nVal = ((ScValueCell*)pCell)->GetValue(); |
| break; |
| case CELLTYPE_STRING: |
| ((ScStringCell*)pCell)->GetString( aString ); |
| bIsVal = sal_False; |
| break; |
| case CELLTYPE_EDIT: |
| ((ScEditCell*)pCell)->GetString( aString ); |
| bIsVal = sal_False; |
| break; |
| case CELLTYPE_FORMULA: |
| { |
| ScFormulaCell* pFCell = (ScFormulaCell*)pCell; |
| bIsVal = pFCell->IsValue(); |
| if ( bIsVal ) |
| nVal = pFCell->GetValue(); |
| else |
| pFCell->GetString( aString ); |
| } |
| break; |
| default: // Notizen, Broadcaster |
| return IsIgnoreBlank(); // wie eingestellt |
| } |
| |
| sal_Bool bOk = sal_True; |
| switch (eDataMode) |
| { |
| // SC_VALID_ANY schon oben |
| |
| case SC_VALID_WHOLE: |
| case SC_VALID_DECIMAL: |
| case SC_VALID_DATE: // Date/Time ist nur Formatierung |
| case SC_VALID_TIME: |
| bOk = bIsVal; |
| if ( bOk && eDataMode == SC_VALID_WHOLE ) |
| bOk = ::rtl::math::approxEqual( nVal, floor(nVal+0.5) ); // ganze Zahlen |
| if ( bOk ) |
| bOk = IsCellValid( pCell, rPos ); |
| break; |
| |
| case SC_VALID_CUSTOM: |
| // fuer Custom muss eOp == SC_COND_DIRECT sein |
| //! der Wert muss im Dokument stehen !!!!!!!!!!!!!!!!!!!! |
| bOk = IsCellValid( pCell, rPos ); |
| break; |
| |
| case SC_VALID_TEXTLEN: |
| bOk = !bIsVal; // nur Text |
| if ( bOk ) |
| { |
| double nLenVal = (double) aString.Len(); |
| ScValueCell aTmpCell( nLenVal ); |
| bOk = IsCellValid( &aTmpCell, rPos ); |
| } |
| break; |
| |
| default: |
| DBG_ERROR("hammanochnich"); |
| break; |
| } |
| |
| return bOk; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| namespace { |
| |
| /** Token array helper. Iterates over all string tokens. |
| @descr The token array must contain separated string tokens only. |
| @param bSkipEmpty true = Ignores string tokens with empty strings. */ |
| class ScStringTokenIterator |
| { |
| public: |
| inline explicit ScStringTokenIterator( ScTokenArray& rTokArr, bool bSkipEmpty = true ) : |
| mrTokArr( rTokArr ), mbSkipEmpty( bSkipEmpty ), mbOk( true ) {} |
| |
| /** Returns the string of the first string token or NULL on error or empty token array. */ |
| const String* First(); |
| /** Returns the string of the next string token or NULL on error or end of token array. */ |
| const String* Next(); |
| |
| /** Returns false, if a wrong token has been found. Does NOT return false on end of token array. */ |
| inline bool Ok() const { return mbOk; } |
| |
| private: |
| ScTokenArray& mrTokArr; /// The token array for iteration. |
| bool mbSkipEmpty; /// Ignore empty strings. |
| bool mbOk; /// true = correct token or end of token array. |
| }; |
| |
| const String* ScStringTokenIterator::First() |
| { |
| mrTokArr.Reset(); |
| mbOk = true; |
| return Next(); |
| } |
| |
| const String* ScStringTokenIterator::Next() |
| { |
| if( !mbOk ) |
| return NULL; |
| |
| // seek to next non-separator token |
| const FormulaToken* pToken = mrTokArr.NextNoSpaces(); |
| while( pToken && (pToken->GetOpCode() == ocSep) ) |
| pToken = mrTokArr.NextNoSpaces(); |
| |
| mbOk = !pToken || (pToken->GetType() == formula::svString); |
| const String* pString = (mbOk && pToken) ? &pToken->GetString() : NULL; |
| // string found but empty -> get next token; otherwise return it |
| return (mbSkipEmpty && pString && !pString->Len()) ? Next() : pString; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| /** Returns the number format of the passed cell, or the standard format. */ |
| sal_uLong lclGetCellFormat( ScDocument& rDoc, const ScAddress& rPos ) |
| { |
| const ScPatternAttr* pPattern = rDoc.GetPattern( rPos.Col(), rPos.Row(), rPos.Tab() ); |
| if( !pPattern ) |
| pPattern = rDoc.GetDefPattern(); |
| return pPattern->GetNumberFormat( rDoc.GetFormatTable() ); |
| } |
| |
| /** Inserts the passed string object. Always takes ownership. pData is invalid after this call! */ |
| void lclInsertStringToCollection( TypedScStrCollection& rStrColl, TypedStrData* pData, bool bSorted ) |
| { |
| if( !(bSorted ? rStrColl.Insert( pData ) : rStrColl.AtInsert( rStrColl.GetCount(), pData )) ) |
| delete pData; |
| } |
| |
| } // namespace |
| |
| // ---------------------------------------------------------------------------- |
| |
| bool ScValidationData::HasSelectionList() const |
| { |
| return (eDataMode == SC_VALID_LIST) && (mnListType != ValidListType::INVISIBLE); |
| } |
| |
| bool ScValidationData::GetSelectionFromFormula( TypedScStrCollection* pStrings, |
| ScBaseCell* pCell, |
| const ScAddress& rPos, |
| const ScTokenArray& rTokArr, |
| int& rMatch ) const |
| { |
| bool bOk = true; |
| |
| // pDoc is private in condition, use an accessor and a long winded name. |
| ScDocument* pDocument = GetDocument(); |
| if( NULL == pDocument ) |
| return false; |
| |
| ScFormulaCell aValidationSrc( pDocument, rPos, &rTokArr, |
| formula::FormulaGrammar::GRAM_DEFAULT, MM_FORMULA); |
| |
| // Make sure the formula gets interpreted and a result is delivered, |
| // regardless of the AutoCalc setting. |
| aValidationSrc.Interpret(); |
| |
| ScMatrixRef xMatRef; |
| const ScMatrix *pValues = aValidationSrc.GetMatrix(); |
| if (!pValues) |
| { |
| // The somewhat nasty case of either an error occured, or the |
| // dereferenced value of a single cell reference or an immediate result |
| // is stored as a single value. |
| |
| // Use an interim matrix to create the TypedStrData below. |
| xMatRef = new ScMatrix(1,1); |
| |
| sal_uInt16 nErrCode = aValidationSrc.GetErrCode(); |
| if (nErrCode) |
| { |
| /* TODO : to use later in an alert box? |
| * String rStrResult = "..."; |
| * rStrResult += ScGlobal::GetLongErrorString(nErrCode); |
| */ |
| |
| xMatRef->PutError( nErrCode, 0); |
| bOk = false; |
| } |
| else if (aValidationSrc.HasValueData()) |
| xMatRef->PutDouble( aValidationSrc.GetValue(), 0); |
| else |
| { |
| String aStr; |
| aValidationSrc.GetString( aStr); |
| xMatRef->PutString( aStr, 0); |
| } |
| |
| pValues = xMatRef; |
| } |
| |
| // which index matched. We will want it eventually to pre-select that item. |
| rMatch = -1; |
| |
| SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable(); |
| |
| bool bSortList = (mnListType == ValidListType::SORTEDASCENDING); |
| SCSIZE nCol, nRow, nCols, nRows, n = 0; |
| pValues->GetDimensions( nCols, nRows ); |
| |
| sal_Bool bRef = sal_False; |
| ScRange aRange; |
| |
| ScTokenArray* pArr = (ScTokenArray*) &rTokArr; |
| pArr->Reset(); |
| ScToken* t = NULL; |
| if (pArr->GetLen() == 1 && (t = static_cast<ScToken*>(pArr->GetNextReferenceOrName())) != NULL) |
| { |
| if (t->GetOpCode() == ocDBArea) |
| { |
| if( ScDBData* pDBData = pDocument->GetDBCollection()->FindIndex( t->GetIndex() ) ) |
| { |
| pDBData->GetArea(aRange); |
| bRef = sal_True; |
| } |
| } |
| else if (t->GetOpCode() == ocName) |
| { |
| ScRangeData* pName = pDocument->GetRangeName()->FindIndex( t->GetIndex() ); |
| if (pName && pName->IsReference(aRange)) |
| { |
| bRef = sal_True; |
| } |
| } |
| else if (t->GetType() != svIndex) |
| { |
| t->CalcAbsIfRel(rPos); |
| if (pArr->IsValidReference(aRange)) |
| { |
| bRef = sal_True; |
| } |
| } |
| } |
| |
| /* XL artificially limits things to a single col or row in the UI but does |
| * not list the constraint in MOOXml. If a defined name or INDIRECT |
| * resulting in 1D is entered in the UI and the definition later modified |
| * to 2D, it is evaluated fine and also stored and loaded. Lets get ahead |
| * of the curve and support 2d. In XL, values are listed row-wise, do the |
| * same. */ |
| for( nRow = 0; nRow < nRows ; nRow++ ) |
| { |
| for( nCol = 0; nCol < nCols ; nCol++ ) |
| { |
| ScTokenArray aCondTokArr; |
| TypedStrData* pEntry = NULL; |
| ScMatValType nMatValType; |
| String aValStr; |
| const ScMatrixValue* pMatVal = pValues->Get( nCol, nRow, nMatValType); |
| |
| // strings and empties |
| if( NULL == pMatVal || ScMatrix::IsNonValueType( nMatValType ) ) |
| { |
| if( NULL != pMatVal ) |
| aValStr = pMatVal->GetString(); |
| |
| if( NULL != pStrings ) |
| pEntry = new TypedStrData( aValStr, 0.0, SC_STRTYPE_STANDARD); |
| |
| if( pCell && rMatch < 0 ) |
| aCondTokArr.AddString( aValStr ); |
| } |
| else |
| { |
| sal_uInt16 nErr = pMatVal->GetError(); |
| |
| if( 0 != nErr ) |
| { |
| aValStr = ScGlobal::GetErrorString( nErr ); |
| } |
| else |
| { |
| // FIXME FIXME FIXME |
| // Feature regression. Date formats are lost passing through the matrix |
| //pFormatter->GetInputLineString( pMatVal->fVal, 0, aValStr ); |
| //For external reference and a formula that results in an area or array, date formats are still lost. |
| if ( bRef ) |
| { |
| pDocument->GetInputString((SCCOL)(nCol+aRange.aStart.Col()), |
| (SCROW)(nRow+aRange.aStart.Row()), aRange.aStart.Tab() , aValStr); |
| } |
| else |
| pFormatter->GetInputLineString( pMatVal->fVal, 0, aValStr ); |
| } |
| |
| if( pCell && rMatch < 0 ) |
| { |
| // I am not sure errors will work here, but a user can no |
| // manually enter an error yet so the point is somewhat moot. |
| aCondTokArr.AddDouble( pMatVal->fVal ); |
| } |
| if( NULL != pStrings ) |
| pEntry = new TypedStrData( aValStr, pMatVal->fVal, SC_STRTYPE_VALUE); |
| } |
| |
| if( rMatch < 0 && NULL != pCell && IsEqualToTokenArray( pCell, rPos, aCondTokArr ) ) |
| { |
| rMatch = n; |
| // short circuit on the first match if not filling the list |
| if( NULL == pStrings ) |
| return true; |
| } |
| |
| if( NULL != pEntry ) |
| { |
| lclInsertStringToCollection( *pStrings, pEntry, bSortList ); |
| n++; |
| } |
| } |
| } |
| |
| // In case of no match needed and an error occurred, return that error |
| // entry as valid instead of silently failing. |
| return bOk || NULL == pCell; |
| } |
| |
| bool ScValidationData::FillSelectionList( TypedScStrCollection& rStrColl, const ScAddress& rPos ) const |
| { |
| bool bOk = false; |
| |
| if( HasSelectionList() ) |
| { |
| ::std::auto_ptr< ScTokenArray > pTokArr( CreateTokenArry( 0 ) ); |
| |
| // *** try if formula is a string list *** |
| |
| bool bSortList = (mnListType == ValidListType::SORTEDASCENDING); |
| sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos ); |
| ScStringTokenIterator aIt( *pTokArr ); |
| for( const String* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next() ) |
| { |
| double fValue; |
| bool bIsValue = GetDocument()->GetFormatTable()->IsNumberFormat( *pString, nFormat, fValue ); |
| TypedStrData* pData = new TypedStrData( *pString, fValue, bIsValue ? SC_STRTYPE_VALUE : SC_STRTYPE_STANDARD ); |
| lclInsertStringToCollection( rStrColl, pData, bSortList ); |
| } |
| bOk = aIt.Ok(); |
| |
| // *** if not a string list, try if formula results in a cell range or |
| // anything else we recognize as valid *** |
| |
| if (!bOk) |
| { |
| int nMatch; |
| bOk = GetSelectionFromFormula( &rStrColl, NULL, rPos, *pTokArr, nMatch ); |
| } |
| } |
| |
| return bOk; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| bool ScValidationData::IsEqualToTokenArray( ScBaseCell* pCell, const ScAddress& rPos, const ScTokenArray& rTokArr ) const |
| { |
| // create a condition entry that tests on equality and set the passed token array |
| ScConditionEntry aCondEntry( SC_COND_EQUAL, &rTokArr, NULL, GetDocument(), rPos ); |
| return aCondEntry.IsCellValid( pCell, rPos ); |
| } |
| |
| bool ScValidationData::IsListValid( ScBaseCell* pCell, const ScAddress& rPos ) const |
| { |
| bool bIsValid = false; |
| |
| /* Compare input cell with all supported tokens from the formula. |
| Currently a formula may contain: |
| 1) A list of strings (at least one string). |
| 2) A single cell or range reference. |
| 3) A single defined name (must contain a cell/range reference, another |
| name, or DB range, or a formula resulting in a cell/range reference |
| or matrix/array). |
| 4) A single database range. |
| 5) A formula resulting in a cell/range reference or matrix/array. |
| */ |
| |
| ::std::auto_ptr< ScTokenArray > pTokArr( CreateTokenArry( 0 ) ); |
| |
| // *** try if formula is a string list *** |
| |
| sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos ); |
| ScStringTokenIterator aIt( *pTokArr ); |
| for( const String* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next() ) |
| { |
| /* Do not break the loop, if a valid string has been found. |
| This is to find invalid tokens following in the formula. */ |
| if( !bIsValid ) |
| { |
| // create a formula containing a single string or number |
| ScTokenArray aCondTokArr; |
| double fValue; |
| if( GetDocument()->GetFormatTable()->IsNumberFormat( *pString, nFormat, fValue ) ) |
| aCondTokArr.AddDouble( fValue ); |
| else |
| aCondTokArr.AddString( *pString ); |
| |
| bIsValid = IsEqualToTokenArray( pCell, rPos, aCondTokArr ); |
| } |
| } |
| |
| if( !aIt.Ok() ) |
| bIsValid = false; |
| |
| // *** if not a string list, try if formula results in a cell range or |
| // anything else we recognize as valid *** |
| |
| if (!bIsValid) |
| { |
| int nMatch; |
| bIsValid = GetSelectionFromFormula( NULL, pCell, rPos, *pTokArr, nMatch ); |
| bIsValid = bIsValid && nMatch >= 0; |
| } |
| |
| return bIsValid; |
| } |
| |
| // ============================================================================ |
| // ============================================================================ |
| |
| ScValidationDataList::ScValidationDataList(const ScValidationDataList& rList) : |
| ScValidationEntries_Impl() |
| { |
| // fuer Ref-Undo - echte Kopie mit neuen Tokens! |
| |
| sal_uInt16 nCount = rList.Count(); |
| |
| for (sal_uInt16 i=0; i<nCount; i++) |
| InsertNew( rList[i]->Clone() ); |
| |
| //! sortierte Eintraege aus rList schneller einfuegen ??? |
| } |
| |
| ScValidationDataList::ScValidationDataList(ScDocument* pNewDoc, |
| const ScValidationDataList& rList) |
| { |
| // fuer neues Dokument - echte Kopie mit neuen Tokens! |
| |
| sal_uInt16 nCount = rList.Count(); |
| |
| for (sal_uInt16 i=0; i<nCount; i++) |
| InsertNew( rList[i]->Clone(pNewDoc) ); |
| |
| //! sortierte Eintraege aus rList schneller einfuegen ??? |
| } |
| |
| ScValidationData* ScValidationDataList::GetData( sal_uInt32 nKey ) |
| { |
| //! binaer suchen |
| |
| sal_uInt16 nCount = Count(); |
| for (sal_uInt16 i=0; i<nCount; i++) |
| if ((*this)[i]->GetKey() == nKey) |
| return (*this)[i]; |
| |
| DBG_ERROR("ScValidationDataList: Eintrag nicht gefunden"); |
| return NULL; |
| } |
| |
| void ScValidationDataList::CompileXML() |
| { |
| sal_uInt16 nCount = Count(); |
| for (sal_uInt16 i=0; i<nCount; i++) |
| (*this)[i]->CompileXML(); |
| } |
| |
| void ScValidationDataList::UpdateReference( UpdateRefMode eUpdateRefMode, |
| const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) |
| { |
| sal_uInt16 nCount = Count(); |
| for (sal_uInt16 i=0; i<nCount; i++) |
| (*this)[i]->UpdateReference( eUpdateRefMode, rRange, nDx, nDy, nDz); |
| } |
| |
| void ScValidationDataList::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos ) |
| { |
| sal_uInt16 nCount = Count(); |
| for (sal_uInt16 i=0; i<nCount; i++) |
| (*this)[i]->UpdateMoveTab( nOldPos, nNewPos ); |
| } |
| |
| bool ScValidationDataList::MarkUsedExternalReferences() const |
| { |
| bool bAllMarked = false; |
| sal_uInt16 nCount = Count(); |
| for (sal_uInt16 i=0; !bAllMarked && i<nCount; i++) |
| bAllMarked = (*this)[i]->MarkUsedExternalReferences(); |
| return bAllMarked; |
| } |
| |
| sal_Bool ScValidationDataList::operator==( const ScValidationDataList& r ) const |
| { |
| // fuer Ref-Undo - interne Variablen werden nicht verglichen |
| |
| sal_uInt16 nCount = Count(); |
| sal_Bool bEqual = ( nCount == r.Count() ); |
| for (sal_uInt16 i=0; i<nCount && bEqual; i++) // Eintraege sind sortiert |
| if ( !(*this)[i]->EqualEntries(*r[i]) ) // Eintraege unterschiedlich ? |
| bEqual = sal_False; |
| |
| return bEqual; |
| } |
| |