1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
22 #include "validat.hxx"
24 #include <sfx2/app.hxx>
25 #include <sfx2/docfile.hxx>
26 #include <sfx2/objsh.hxx>
27 #include <basic/sbmeth.hxx>
28 #include <basic/sbmod.hxx>
29 #include <basic/sbstar.hxx>
30 #include <basic/basmgr.hxx>
32 #include <basic/sbx.hxx>
33 #include <svl/zforlist.hxx>
34 #include <svl/sharedstringpool.hxx>
35 #include <vcl/layout.hxx>
36 #include <vcl/msgbox.hxx>
37 #include <rtl/math.hxx>
39 #include "scitems.hxx"
40 #include "document.hxx"
41 #include "formulacell.hxx"
42 #include "patattr.hxx"
43 #include "rechead.hxx"
44 #include "globstr.hrc"
45 #include "rangenam.hxx"
47 #include "typedstrdata.hxx"
48 #include "dociter.hxx"
49 #include "editutil.hxx"
50 #include "tokenarray.hxx"
51 #include "scmatrix.hxx"
54 #include <boost/scoped_ptr.hpp>
56 using namespace formula
;
58 // Entries for validation (with only one condition)
60 ScValidationData::ScValidationData( ScValidationMode eMode
, ScConditionMode eOper
,
61 const OUString
& rExpr1
, const OUString
& rExpr2
,
62 ScDocument
* pDocument
, const ScAddress
& rPos
,
63 const OUString
& rExprNmsp1
, const OUString
& rExprNmsp2
,
64 FormulaGrammar::Grammar eGrammar1
,
65 FormulaGrammar::Grammar eGrammar2
)
66 : ScConditionEntry( eOper
, rExpr1
, rExpr2
, pDocument
, rPos
, rExprNmsp1
,
67 rExprNmsp2
, eGrammar1
, eGrammar2
)
72 , eErrorStyle( SC_VALERR_STOP
)
73 , mnListType( css::sheet::TableValidationVisibility::UNSORTED
)
77 ScValidationData::ScValidationData( ScValidationMode eMode
, ScConditionMode eOper
,
78 const ScTokenArray
* pArr1
, const ScTokenArray
* pArr2
,
79 ScDocument
* pDocument
, const ScAddress
& rPos
)
80 : ScConditionEntry( eOper
, pArr1
, pArr2
, pDocument
, rPos
)
85 , eErrorStyle( SC_VALERR_STOP
)
86 , mnListType( css::sheet::TableValidationVisibility::UNSORTED
)
90 ScValidationData::ScValidationData( const ScValidationData
& r
)
91 : ScConditionEntry( r
)
93 , eDataMode( r
.eDataMode
)
94 , bShowInput( r
.bShowInput
)
95 , bShowError( r
.bShowError
)
96 , eErrorStyle( r
.eErrorStyle
)
97 , mnListType( r
.mnListType
)
98 , aInputTitle( r
.aInputTitle
)
99 , aInputMessage( r
.aInputMessage
)
100 , aErrorTitle( r
.aErrorTitle
)
101 , aErrorMessage( r
.aErrorMessage
)
103 // Formulae copied by RefCount
106 ScValidationData::ScValidationData( ScDocument
* pDocument
, const ScValidationData
& r
)
107 : ScConditionEntry( pDocument
, r
)
109 , eDataMode( r
.eDataMode
)
110 , bShowInput( r
.bShowInput
)
111 , bShowError( r
.bShowError
)
112 , eErrorStyle( r
.eErrorStyle
)
113 , mnListType( r
.mnListType
)
114 , aInputTitle( r
.aInputTitle
)
115 , aInputMessage( r
.aInputMessage
)
116 , aErrorTitle( r
.aErrorTitle
)
117 , aErrorMessage( r
.aErrorMessage
)
119 // Formulae really copied
122 ScValidationData::~ScValidationData()
126 bool ScValidationData::IsEmpty() const
129 ScValidationData
aDefault( SC_VALID_ANY
, SC_COND_EQUAL
, aEmpty
, aEmpty
, GetDocument(), ScAddress() );
130 return EqualEntries( aDefault
);
133 bool ScValidationData::EqualEntries( const ScValidationData
& r
) const
135 // test same parameters (excluding Key)
137 return ScConditionEntry::operator==(r
) &&
138 eDataMode
== r
.eDataMode
&&
139 bShowInput
== r
.bShowInput
&&
140 bShowError
== r
.bShowError
&&
141 eErrorStyle
== r
.eErrorStyle
&&
142 mnListType
== r
.mnListType
&&
143 aInputTitle
== r
.aInputTitle
&&
144 aInputMessage
== r
.aInputMessage
&&
145 aErrorTitle
== r
.aErrorTitle
&&
146 aErrorMessage
== r
.aErrorMessage
;
149 void ScValidationData::ResetInput()
154 void ScValidationData::ResetError()
159 void ScValidationData::SetInput( const OUString
& rTitle
, const OUString
& rMsg
)
162 aInputTitle
= rTitle
;
163 aInputMessage
= rMsg
;
166 void ScValidationData::SetError( const OUString
& rTitle
, const OUString
& rMsg
,
167 ScValidErrorStyle eStyle
)
170 eErrorStyle
= eStyle
;
171 aErrorTitle
= rTitle
;
172 aErrorMessage
= rMsg
;
175 bool ScValidationData::GetErrMsg( OUString
& rTitle
, OUString
& rMsg
,
176 ScValidErrorStyle
& rStyle
) const
178 rTitle
= aErrorTitle
;
179 rMsg
= aErrorMessage
;
180 rStyle
= eErrorStyle
;
184 bool ScValidationData::DoScript( const ScAddress
& rPos
, const OUString
& rInput
,
185 ScFormulaCell
* pCell
, vcl::Window
* pParent
) const
187 ScDocument
* pDocument
= GetDocument();
188 SfxObjectShell
* pDocSh
= pDocument
->GetDocumentShell();
189 if ( !pDocSh
|| !ScDocument::CheckMacroWarn() )
192 bool bScriptReturnedFalse
= false; // default: do not abort
195 ::com::sun::star::uno::Sequence
< ::com::sun::star::uno::Any
> aParams(2);
197 // 1) entered or calculated value
198 OUString aValStr
= rInput
;
200 bool bIsValue
= false;
201 if ( pCell
) // if cell exists, call interpret
203 bIsValue
= pCell
->IsValue();
205 nValue
= pCell
->GetValue();
207 aValStr
= pCell
->GetString().getString();
210 aParams
[0] = ::com::sun::star::uno::makeAny( nValue
);
212 aParams
[0] = ::com::sun::star::uno::makeAny( OUString( aValStr
) );
214 // 2) Position of the cell
215 OUString
aPosStr(rPos
.Format(SCA_VALID
| SCA_TAB_3D
, pDocument
, pDocument
->GetAddressConvention()));
216 aParams
[1] = ::com::sun::star::uno::makeAny(aPosStr
);
218 // use link-update flag to prevent closing the document
219 // while the macro is running
220 bool bWasInLinkUpdate
= pDocument
->IsInLinkUpdate();
221 if ( !bWasInLinkUpdate
)
222 pDocument
->SetInLinkUpdate( true );
225 pDocument
->LockTable( rPos
.Tab() );
227 ::com::sun::star::uno::Any aRet
;
228 ::com::sun::star::uno::Sequence
< sal_Int16
> aOutArgsIndex
;
229 ::com::sun::star::uno::Sequence
< ::com::sun::star::uno::Any
> aOutArgs
;
231 ErrCode eRet
= pDocSh
->CallXScript(
232 aErrorTitle
, aParams
, aRet
, aOutArgsIndex
, aOutArgs
);
235 pDocument
->UnlockTable( rPos
.Tab() );
237 if ( !bWasInLinkUpdate
)
238 pDocument
->SetInLinkUpdate( false );
240 // Check the return value from the script
241 // The contents of the cell get reset if the script returns false
243 if ( eRet
== ERRCODE_NONE
&&
244 aRet
.getValueType() == cppu::UnoType
<bool>::get() &&
248 bScriptReturnedFalse
= true;
251 if ( eRet
== ERRCODE_BASIC_METHOD_NOT_FOUND
&& !pCell
)
252 // Macro not found (only with input)
254 //TODO: different error message, if found, but not bAllowed ??
256 ScopedVclPtrInstance
< MessageDialog
> aBox( pParent
, ScGlobal::GetRscString(STR_VALID_MACRONOTFOUND
));
260 return bScriptReturnedFalse
;
265 bool ScValidationData::DoMacro( const ScAddress
& rPos
, const OUString
& rInput
,
266 ScFormulaCell
* pCell
, vcl::Window
* pParent
) const
268 if ( SfxApplication::IsXScriptURL( aErrorTitle
) )
270 return DoScript( rPos
, rInput
, pCell
, pParent
);
273 ScDocument
* pDocument
= GetDocument();
274 SfxObjectShell
* pDocSh
= pDocument
->GetDocumentShell();
275 if ( !pDocSh
|| !ScDocument::CheckMacroWarn() )
279 bool bRet
= false; // default: do not abort
281 // If the Doc was loaded during a Basic-Calls,
282 // the Sbx-Objekt may not be created (?)
283 // pDocSh->GetSbxObject();
285 #if HAVE_FEATURE_SCRIPTING
286 // no security check ahead (only CheckMacroWarn), that happens in CallBasic
288 // Function search by their simple name,
289 // then assemble aBasicStr, aMacroStr for SfxObjectShell::CallBasic
291 StarBASIC
* pRoot
= pDocSh
->GetBasic();
292 SbxVariable
* pVar
= pRoot
->Find( aErrorTitle
, SbxCLASS_METHOD
);
293 if ( pVar
&& pVar
->ISA(SbMethod
) )
295 SbMethod
* pMethod
= static_cast<SbMethod
*>(pVar
);
296 SbModule
* pModule
= pMethod
->GetModule();
297 SbxObject
* pObject
= pModule
->GetParent();
298 OUStringBuffer aMacroStr
= pObject
->GetName();
299 aMacroStr
.append('.').append(pModule
->GetName()).append('.').append(pMethod
->GetName());
302 // the distinction between document- and app-basic has to be done
303 // by checking the parent (as in ScInterpreter::ScMacro), not by looping
304 // over all open documents, because this may be called from within loading,
305 // when SfxObjectShell::GetFirst/GetNext won't find the document.
307 if ( pObject
->GetParent() )
308 aBasicStr
= pObject
->GetParent()->GetName(); // Basic of document
310 aBasicStr
= SfxGetpApp()->GetName(); // Basic of application
312 // Parameter for Macro
313 SbxArrayRef refPar
= new SbxArray
;
315 // 1) entered or calculated value
316 OUString aValStr
= rInput
;
318 bool bIsValue
= false;
319 if ( pCell
) // if cell set, called from interpret
321 bIsValue
= pCell
->IsValue();
323 nValue
= pCell
->GetValue();
325 aValStr
= pCell
->GetString().getString();
328 refPar
->Get(1)->PutDouble( nValue
);
330 refPar
->Get(1)->PutString( aValStr
);
332 // 2) Position of the cell
333 OUString
aPosStr(rPos
.Format(SCA_VALID
| SCA_TAB_3D
, pDocument
, pDocument
->GetAddressConvention()));
334 refPar
->Get(2)->PutString( aPosStr
);
336 // use link-update flag to prevent closing the document
337 // while the macro is running
338 bool bWasInLinkUpdate
= pDocument
->IsInLinkUpdate();
339 if ( !bWasInLinkUpdate
)
340 pDocument
->SetInLinkUpdate( true );
343 pDocument
->LockTable( rPos
.Tab() );
344 SbxVariableRef refRes
= new SbxVariable
;
345 ErrCode eRet
= pDocSh
->CallBasic( aMacroStr
.makeStringAndClear(), aBasicStr
, refPar
, refRes
);
347 pDocument
->UnlockTable( rPos
.Tab() );
349 if ( !bWasInLinkUpdate
)
350 pDocument
->SetInLinkUpdate( false );
352 // Interrupt input if Basic macro returns false
353 if ( eRet
== ERRCODE_NONE
&& refRes
->GetType() == SbxBOOL
&& !refRes
->GetBool() )
358 if ( !bDone
&& !pCell
) // Macro not found (only with input)
360 //TODO: different error message, if found, but not bAllowed ??
362 ScopedVclPtrInstance
< MessageDialog
> aBox(pParent
, ScGlobal::GetRscString(STR_VALID_MACRONOTFOUND
));
369 void ScValidationData::DoCalcError( ScFormulaCell
* pCell
) const
371 if ( eErrorStyle
== SC_VALERR_MACRO
)
372 DoMacro( pCell
->aPos
, EMPTY_OUSTRING
, pCell
, NULL
);
377 bool ScValidationData::DoError( vcl::Window
* pParent
, const OUString
& rInput
,
378 const ScAddress
& rPos
) const
380 if ( eErrorStyle
== SC_VALERR_MACRO
)
381 return DoMacro( rPos
, rInput
, NULL
, pParent
);
383 // Output error message
385 OUString aTitle
= aErrorTitle
;
386 if (aTitle
.isEmpty())
387 aTitle
= ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_0
); // application title
388 OUString aMessage
= aErrorMessage
;
389 if (aMessage
.isEmpty())
390 aMessage
= ScGlobal::GetRscString( STR_VALID_DEFERROR
);
392 //TODO: ErrorBox / WarningBox / InfoBox ?
393 //TODO: (with InfoBox always OK-Button only)
399 nStyle
= WB_OK
| WB_DEF_OK
;
401 case SC_VALERR_WARNING
:
402 nStyle
= WB_OK_CANCEL
| WB_DEF_CANCEL
;
405 nStyle
= WB_OK_CANCEL
| WB_DEF_OK
;
409 // added to avoid warnings
413 ScopedVclPtrInstance
< MessBox
> aBox( pParent
, WinBits(nStyle
), aTitle
, aMessage
);
414 sal_uInt16 nRet
= aBox
->Execute();
416 return ( eErrorStyle
== SC_VALERR_STOP
|| nRet
== RET_CANCEL
);
419 bool ScValidationData::IsDataValid(
420 const OUString
& rTest
, const ScPatternAttr
& rPattern
, const ScAddress
& rPos
) const
422 if ( eDataMode
== SC_VALID_ANY
) // check if any cell content is allowed
425 if (rTest
.isEmpty()) // check whether empty cells are allowed
426 return IsIgnoreBlank();
428 if (rTest
[0] == '=') // formulas do not pass the validity test
431 SvNumberFormatter
* pFormatter
= GetDocument()->GetFormatTable();
433 // get the value if any
434 sal_uInt32 nFormat
= rPattern
.GetNumberFormat( pFormatter
);
436 bool bIsVal
= pFormatter
->IsNumberFormat( rTest
, nFormat
, nVal
);
439 if (SC_VALID_TEXTLEN
== eDataMode
)
443 nLenVal
= static_cast<double>(rTest
.getLength());
446 // For numeric values use the resulting input line string to
447 // determine length, otherwise a once accepted value maybe could
448 // not be edited again, for example abbreviated dates or leading
449 // zeros or trailing zeros after decimal separator change length.
451 pFormatter
->GetInputLineString( nVal
, nFormat
, aStr
);
452 nLenVal
= static_cast<double>( aStr
.getLength() );
454 ScRefCellValue
aTmpCell(nLenVal
);
455 bRet
= IsCellValid(aTmpCell
, rPos
);
461 ScRefCellValue
aTmpCell(nVal
);
462 bRet
= IsDataValid(aTmpCell
, rPos
);
466 svl::SharedString aSS
= mpDoc
->GetSharedStringPool().intern(rTest
);
467 ScRefCellValue
aTmpCell(&aSS
);
468 bRet
= IsDataValid(aTmpCell
, rPos
);
475 bool ScValidationData::IsDataValid( ScRefCellValue
& rCell
, const ScAddress
& rPos
) const
477 if( eDataMode
== SC_VALID_LIST
)
478 return IsListValid(rCell
, rPos
);
484 switch (rCell
.meType
)
487 nVal
= rCell
.mfValue
;
489 case CELLTYPE_STRING
:
490 aString
= rCell
.mpString
->getString();
494 if (rCell
.mpEditText
)
495 aString
= ScEditUtil::GetString(*rCell
.mpEditText
, GetDocument());
498 case CELLTYPE_FORMULA
:
500 ScFormulaCell
* pFCell
= rCell
.mpFormula
;
501 bIsVal
= pFCell
->IsValue();
503 nVal
= pFCell
->GetValue();
505 aString
= pFCell
->GetString().getString();
508 default: // Notes, Broadcaster
509 return IsIgnoreBlank(); // as set
515 // SC_VALID_ANY schon oben
518 case SC_VALID_DECIMAL
:
519 case SC_VALID_DATE
: // Date/Time is only formatting
522 if ( bOk
&& eDataMode
== SC_VALID_WHOLE
)
523 bOk
= ::rtl::math::approxEqual( nVal
, floor(nVal
+0.5) ); // integers
525 bOk
= IsCellValid(rCell
, rPos
);
528 case SC_VALID_CUSTOM
:
529 // for Custom, it must be eOp == SC_COND_DIRECT
530 //TODO: the value must be in the document !!!
531 bOk
= IsCellValid(rCell
, rPos
);
534 case SC_VALID_TEXTLEN
:
535 bOk
= !bIsVal
; // only Text
538 double nLenVal
= (double) aString
.getLength();
539 ScRefCellValue
aTmpCell(nLenVal
);
540 bOk
= IsCellValid(aTmpCell
, rPos
);
545 OSL_FAIL("not yet done");
554 /** Token array helper. Iterates over all string tokens.
555 @descr The token array must contain separated string tokens only.
556 @param bSkipEmpty true = Ignores string tokens with empty strings. */
557 class ScStringTokenIterator
560 inline explicit ScStringTokenIterator( ScTokenArray
& rTokArr
, bool bSkipEmpty
= true ) :
561 mrTokArr( rTokArr
), mbSkipEmpty( bSkipEmpty
), mbOk( true ) {}
563 /** Returns the string of the first string token or NULL on error or empty token array. */
564 rtl_uString
* First();
565 /** Returns the string of the next string token or NULL on error or end of token array. */
568 /** Returns false, if a wrong token has been found. Does NOT return false on end of token array. */
569 inline bool Ok() const { return mbOk
; }
572 svl::SharedString maCurString
; /// Current string.
573 ScTokenArray
& mrTokArr
; /// The token array for iteration.
574 bool mbSkipEmpty
; /// Ignore empty strings.
575 bool mbOk
; /// true = correct token or end of token array.
578 rtl_uString
* ScStringTokenIterator::First()
585 rtl_uString
* ScStringTokenIterator::Next()
590 // seek to next non-separator token
591 const FormulaToken
* pToken
= mrTokArr
.NextNoSpaces();
592 while( pToken
&& (pToken
->GetOpCode() == ocSep
) )
593 pToken
= mrTokArr
.NextNoSpaces();
595 mbOk
= !pToken
|| (pToken
->GetType() == formula::svString
);
597 maCurString
= svl::SharedString(); // start with invalid string.
599 maCurString
= pToken
->GetString();
601 // string found but empty -> get next token; otherwise return it
602 return (mbSkipEmpty
&& maCurString
.isValid() && maCurString
.isEmpty()) ? Next() : maCurString
.getData();
605 /** Returns the number format of the passed cell, or the standard format. */
606 sal_uLong
lclGetCellFormat( ScDocument
& rDoc
, const ScAddress
& rPos
)
608 const ScPatternAttr
* pPattern
= rDoc
.GetPattern( rPos
.Col(), rPos
.Row(), rPos
.Tab() );
610 pPattern
= rDoc
.GetDefPattern();
611 return pPattern
->GetNumberFormat( rDoc
.GetFormatTable() );
616 bool ScValidationData::HasSelectionList() const
618 return (eDataMode
== SC_VALID_LIST
) && (mnListType
!= css::sheet::TableValidationVisibility::INVISIBLE
);
621 bool ScValidationData::GetSelectionFromFormula(
622 std::vector
<ScTypedStrData
>* pStrings
, ScRefCellValue
& rCell
, const ScAddress
& rPos
,
623 const ScTokenArray
& rTokArr
, int& rMatch
) const
627 // pDoc is private in condition, use an accessor and a long winded name.
628 ScDocument
* pDocument
= GetDocument();
629 if( NULL
== pDocument
)
632 ScFormulaCell
aValidationSrc(
633 pDocument
, rPos
, rTokArr
, formula::FormulaGrammar::GRAM_DEFAULT
, MM_FORMULA
);
635 // Make sure the formula gets interpreted and a result is delivered,
636 // regardless of the AutoCalc setting.
637 aValidationSrc
.Interpret();
640 const ScMatrix
*pValues
= aValidationSrc
.GetMatrix();
643 // The somewhat nasty case of either an error occurred, or the
644 // dereferenced value of a single cell reference or an immediate result
645 // is stored as a single value.
647 // Use an interim matrix to create the TypedStrData below.
648 xMatRef
= new ScMatrix(1, 1, 0.0);
650 sal_uInt16 nErrCode
= aValidationSrc
.GetErrCode();
653 /* TODO : to use later in an alert box?
654 * OUString rStrResult = "...";
655 * rStrResult += ScGlobal::GetLongErrorString(nErrCode);
658 xMatRef
->PutError( nErrCode
, 0, 0);
661 else if (aValidationSrc
.IsValue())
662 xMatRef
->PutDouble( aValidationSrc
.GetValue(), 0);
665 svl::SharedString aStr
= aValidationSrc
.GetString();
666 xMatRef
->PutString(aStr
, 0);
669 pValues
= xMatRef
.get();
672 // which index matched. We will want it eventually to pre-select that item.
675 SvNumberFormatter
* pFormatter
= GetDocument()->GetFormatTable();
677 SCSIZE nCol
, nRow
, nCols
, nRows
, n
= 0;
678 pValues
->GetDimensions( nCols
, nRows
);
683 ScTokenArray
* pArr
= const_cast<ScTokenArray
*>(&rTokArr
);
685 formula::FormulaToken
* t
= NULL
;
686 if (pArr
->GetLen() == 1 && (t
= pArr
->GetNextReferenceOrName()) != NULL
)
688 OpCode eOpCode
= t
->GetOpCode();
689 if (eOpCode
== ocDBArea
|| eOpCode
== ocTableRef
)
691 if (const ScDBData
* pDBData
= pDocument
->GetDBCollection()->getNamedDBs().findByIndex(t
->GetIndex()))
693 pDBData
->GetArea(aRange
);
697 else if (eOpCode
== ocName
)
699 ScRangeData
* pName
= pDocument
->GetRangeName()->findByIndex( t
->GetIndex() );
700 if (pName
&& pName
->IsReference(aRange
))
705 else if (t
->GetType() != svIndex
)
707 if (pArr
->IsValidReference(aRange
, rPos
))
714 bool bHaveEmpty
= false;
715 svl::SharedStringPool
& rSPool
= pDocument
->GetSharedStringPool();
717 /* XL artificially limits things to a single col or row in the UI but does
718 * not list the constraint in MOOXml. If a defined name or INDIRECT
719 * resulting in 1D is entered in the UI and the definition later modified
720 * to 2D, it is evaluated fine and also stored and loaded. Lets get ahead
721 * of the curve and support 2d. In XL, values are listed row-wise, do the
723 for( nRow
= 0; nRow
< nRows
; nRow
++ )
725 for( nCol
= 0; nCol
< nCols
; nCol
++ )
727 ScTokenArray aCondTokArr
;
728 ScTypedStrData
* pEntry
= NULL
;
730 ScMatrixValue nMatVal
= pValues
->Get( nCol
, nRow
);
732 // strings and empties
733 if( ScMatrix::IsNonValueType( nMatVal
.nType
) )
735 aValStr
= nMatVal
.GetString().getString();
737 // Do not add multiple empty strings to the validation list,
738 // especially not if they'd bloat the tail with a million empty
739 // entries for a column range, fdo#61520
740 if (aValStr
.isEmpty())
747 if( NULL
!= pStrings
)
748 pEntry
= new ScTypedStrData( aValStr
, 0.0, ScTypedStrData::Standard
);
750 if (!rCell
.isEmpty() && rMatch
< 0)
751 aCondTokArr
.AddString(rSPool
.intern(aValStr
));
755 sal_uInt16 nErr
= nMatVal
.GetError();
759 aValStr
= ScGlobal::GetErrorString( nErr
);
764 // Feature regression. Date formats are lost passing through the matrix
765 //pFormatter->GetInputLineString( pMatVal->fVal, 0, aValStr );
766 //For external reference and a formula that results in an area or array, date formats are still lost.
769 pDocument
->GetInputString((SCCOL
)(nCol
+aRange
.aStart
.Col()),
770 (SCROW
)(nRow
+aRange
.aStart
.Row()), aRange
.aStart
.Tab() , aValStr
);
774 pFormatter
->GetInputLineString( nMatVal
.fVal
, 0, aValStr
);
778 if (!rCell
.isEmpty() && rMatch
< 0)
780 // I am not sure errors will work here, but a user can no
781 // manually enter an error yet so the point is somewhat moot.
782 aCondTokArr
.AddDouble( nMatVal
.fVal
);
784 if( NULL
!= pStrings
)
785 pEntry
= new ScTypedStrData( aValStr
, nMatVal
.fVal
, ScTypedStrData::Value
);
788 if (rMatch
< 0 && !rCell
.isEmpty() && IsEqualToTokenArray(rCell
, rPos
, aCondTokArr
))
791 // short circuit on the first match if not filling the list
792 if( NULL
== pStrings
)
798 pStrings
->push_back(*pEntry
);
805 // In case of no match needed and an error occurred, return that error
806 // entry as valid instead of silently failing.
807 return bOk
|| rCell
.isEmpty();
810 bool ScValidationData::FillSelectionList(std::vector
<ScTypedStrData
>& rStrColl
, const ScAddress
& rPos
) const
814 if( HasSelectionList() )
816 boost::scoped_ptr
<ScTokenArray
> pTokArr( CreateTokenArry(0) );
818 // *** try if formula is a string list ***
820 sal_uInt32 nFormat
= lclGetCellFormat( *GetDocument(), rPos
);
821 ScStringTokenIterator
aIt( *pTokArr
);
822 for (rtl_uString
* pString
= aIt
.First(); pString
&& aIt
.Ok(); pString
= aIt
.Next())
825 OUString
aStr(pString
);
826 bool bIsValue
= GetDocument()->GetFormatTable()->IsNumberFormat(aStr
, nFormat
, fValue
);
829 aStr
, fValue
, bIsValue
? ScTypedStrData::Value
: ScTypedStrData::Standard
));
833 // *** if not a string list, try if formula results in a cell range or
834 // anything else we recognize as valid ***
839 ScRefCellValue aEmptyCell
;
840 bOk
= GetSelectionFromFormula(&rStrColl
, aEmptyCell
, rPos
, *pTokArr
, nMatch
);
847 bool ScValidationData::IsEqualToTokenArray( ScRefCellValue
& rCell
, const ScAddress
& rPos
, const ScTokenArray
& rTokArr
) const
849 // create a condition entry that tests on equality and set the passed token array
850 ScConditionEntry
aCondEntry( SC_COND_EQUAL
, &rTokArr
, NULL
, GetDocument(), rPos
);
851 return aCondEntry
.IsCellValid(rCell
, rPos
);
854 bool ScValidationData::IsListValid( ScRefCellValue
& rCell
, const ScAddress
& rPos
) const
856 bool bIsValid
= false;
858 /* Compare input cell with all supported tokens from the formula.
859 Currently a formula may contain:
860 1) A list of strings (at least one string).
861 2) A single cell or range reference.
862 3) A single defined name (must contain a cell/range reference, another
863 name, or DB range, or a formula resulting in a cell/range reference
865 4) A single database range.
866 5) A formula resulting in a cell/range reference or matrix/array.
869 boost::scoped_ptr
< ScTokenArray
> pTokArr( CreateTokenArry( 0 ) );
871 // *** try if formula is a string list ***
873 svl::SharedStringPool
& rSPool
= GetDocument()->GetSharedStringPool();
874 sal_uInt32 nFormat
= lclGetCellFormat( *GetDocument(), rPos
);
875 ScStringTokenIterator
aIt( *pTokArr
);
876 for (rtl_uString
* pString
= aIt
.First(); pString
&& aIt
.Ok(); pString
= aIt
.Next())
878 /* Do not break the loop, if a valid string has been found.
879 This is to find invalid tokens following in the formula. */
882 // create a formula containing a single string or number
883 ScTokenArray aCondTokArr
;
885 OUString
aStr(pString
);
886 if (GetDocument()->GetFormatTable()->IsNumberFormat(aStr
, nFormat
, fValue
))
887 aCondTokArr
.AddDouble( fValue
);
889 aCondTokArr
.AddString(rSPool
.intern(aStr
));
891 bIsValid
= IsEqualToTokenArray(rCell
, rPos
, aCondTokArr
);
898 // *** if not a string list, try if formula results in a cell range or
899 // anything else we recognize as valid ***
904 bIsValid
= GetSelectionFromFormula(NULL
, rCell
, rPos
, *pTokArr
, nMatch
);
905 bIsValid
= bIsValid
&& nMatch
>= 0;
911 ScValidationDataList::ScValidationDataList(const ScValidationDataList
& rList
)
913 // for Ref-Undo - real copy with new tokens!
915 for (const_iterator it
= rList
.begin(); it
!= rList
.end(); ++it
)
917 InsertNew( (*it
)->Clone() );
920 //TODO: faster insert for sorted entries from rList ???
923 ScValidationDataList::ScValidationDataList(ScDocument
* pNewDoc
,
924 const ScValidationDataList
& rList
)
926 // for new documents - real copy with new tokens!
928 for (const_iterator it
= rList
.begin(); it
!= rList
.end(); ++it
)
930 InsertNew( (*it
)->Clone(pNewDoc
) );
933 //TODO: faster insert for sorted entries from rList ???
936 ScValidationData
* ScValidationDataList::GetData( sal_uInt32 nKey
)
938 //TODO: binary search
940 for( iterator it
= begin(); it
!= end(); ++it
)
941 if( (*it
)->GetKey() == nKey
)
944 OSL_FAIL("ScValidationDataList: Entry not found");
948 void ScValidationDataList::CompileXML()
950 for( iterator it
= begin(); it
!= end(); ++it
)
954 void ScValidationDataList::UpdateReference( sc::RefUpdateContext
& rCxt
)
956 for( iterator it
= begin(); it
!= end(); ++it
)
957 (*it
)->UpdateReference(rCxt
);
960 void ScValidationDataList::UpdateInsertTab( sc::RefUpdateInsertTabContext
& rCxt
)
962 for (iterator it
= begin(); it
!= end(); ++it
)
963 (*it
)->UpdateInsertTab(rCxt
);
966 void ScValidationDataList::UpdateDeleteTab( sc::RefUpdateDeleteTabContext
& rCxt
)
968 for (iterator it
= begin(); it
!= end(); ++it
)
969 (*it
)->UpdateDeleteTab(rCxt
);
972 void ScValidationDataList::UpdateMoveTab( sc::RefUpdateMoveTabContext
& rCxt
)
974 for (iterator it
= begin(); it
!= end(); ++it
)
975 (*it
)->UpdateMoveTab(rCxt
);
978 bool ScValidationDataList::operator==( const ScValidationDataList
& r
) const
980 // for Ref-Undo - internal variables can not be compared
982 size_t nCount
= maData
.size();
983 bool bEqual
= ( nCount
== r
.maData
.size() );
984 for( const_iterator it1
= begin(), it2
= r
.begin(); it1
!= end() && bEqual
; ++it1
, ++it2
) // Entries are sorted
985 if ( !(*it1
)->EqualEntries(**it2
) ) // different entries ?
991 ScValidationDataList::iterator
ScValidationDataList::begin()
993 return maData
.begin();
996 ScValidationDataList::const_iterator
ScValidationDataList::begin() const
998 return maData
.begin();
1001 ScValidationDataList::iterator
ScValidationDataList::end()
1003 return maData
.end();
1006 ScValidationDataList::const_iterator
ScValidationDataList::end() const
1008 return maData
.end();
1011 void ScValidationDataList::clear()
1016 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */