summaryrefslogtreecommitdiff
path: root/sc/source/core/data/validat.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/core/data/validat.cxx')
-rw-r--r--sc/source/core/data/validat.cxx993
1 files changed, 993 insertions, 0 deletions
diff --git a/sc/source/core/data/validat.cxx b/sc/source/core/data/validat.cxx
new file mode 100644
index 000000000000..0d7e39273000
--- /dev/null
+++ b/sc/source/core/data/validat.cxx
@@ -0,0 +1,993 @@
+/*************************************************************************
+ *
+ * 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
+ * <http://www.openoffice.org/license.html>
+ * 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 <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 )
+ 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<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;
+}
+