/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sc.hxx" // INCLUDE --------------------------------------------------------------- #include "scitems.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #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 #include 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 ) return sal_True; // alles erlaubt if ( rTest.GetChar(0) == '=' ) return sal_False; // Formeln sind sonst immer ungueltig if ( !rTest.Len() ) return IsIgnoreBlank(); // leer: wie eingestellt SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable(); // Test, was es denn ist - wie in ScColumn::SetString 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 = 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(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; iClone() ); //! 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; iClone(pNewDoc) ); //! sortierte Eintraege aus rList schneller einfuegen ??? } ScValidationData* ScValidationDataList::GetData( sal_uInt32 nKey ) { //! binaer suchen sal_uInt16 nCount = Count(); for (sal_uInt16 i=0; iGetKey() == nKey) return (*this)[i]; DBG_ERROR("ScValidationDataList: Eintrag nicht gefunden"); return NULL; } void ScValidationDataList::CompileXML() { sal_uInt16 nCount = Count(); for (sal_uInt16 i=0; iCompileXML(); } void ScValidationDataList::UpdateReference( UpdateRefMode eUpdateRefMode, const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) { sal_uInt16 nCount = Count(); for (sal_uInt16 i=0; iUpdateReference( eUpdateRefMode, rRange, nDx, nDy, nDz); } void ScValidationDataList::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos ) { sal_uInt16 nCount = Count(); for (sal_uInt16 i=0; iUpdateMoveTab( nOldPos, nNewPos ); } bool ScValidationDataList::MarkUsedExternalReferences() const { bool bAllMarked = false; sal_uInt16 nCount = Count(); for (sal_uInt16 i=0; !bAllMarked && iMarkUsedExternalReferences(); 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; iEqualEntries(*r[i]) ) // Eintraege unterschiedlich ? bEqual = sal_False; return bEqual; }