blob: 841a1a8a28d34f42a355c774bf567c7fe0cac614 [file] [log] [blame]
/**************************************************************
*
* 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;
}