fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / sc / source / core / data / validat.cxx
blobbb19d024f4f14d89fdca438533d2ead8c384adf7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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"
46 #include "dbdata.hxx"
47 #include "typedstrdata.hxx"
48 #include "dociter.hxx"
49 #include "editutil.hxx"
50 #include "tokenarray.hxx"
51 #include "scmatrix.hxx"
53 #include <math.h>
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 )
68 , nKey( 0 )
69 , eDataMode( eMode )
70 , bShowInput(false)
71 , bShowError(false)
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 )
81 , nKey( 0 )
82 , eDataMode( eMode )
83 , bShowInput(false)
84 , bShowError(false)
85 , eErrorStyle( SC_VALERR_STOP )
86 , mnListType( css::sheet::TableValidationVisibility::UNSORTED )
90 ScValidationData::ScValidationData( const ScValidationData& r )
91 : ScConditionEntry( r )
92 , nKey( r.nKey )
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 )
108 , nKey( r.nKey )
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
128 OUString aEmpty;
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()
151 bShowInput = false;
154 void ScValidationData::ResetError()
156 bShowError = false;
159 void ScValidationData::SetInput( const OUString& rTitle, const OUString& rMsg )
161 bShowInput = true;
162 aInputTitle = rTitle;
163 aInputMessage = rMsg;
166 void ScValidationData::SetError( const OUString& rTitle, const OUString& rMsg,
167 ScValidErrorStyle eStyle )
169 bShowError = true;
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;
181 return bShowError;
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() )
190 return false;
192 bool bScriptReturnedFalse = false; // default: do not abort
194 // Set up parameters
195 ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > aParams(2);
197 // 1) entered or calculated value
198 OUString aValStr = rInput;
199 double nValue;
200 bool bIsValue = false;
201 if ( pCell ) // if cell exists, call interpret
203 bIsValue = pCell->IsValue();
204 if ( bIsValue )
205 nValue = pCell->GetValue();
206 else
207 aValStr = pCell->GetString().getString();
209 if ( bIsValue )
210 aParams[0] = ::com::sun::star::uno::makeAny( nValue );
211 else
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 );
224 if ( pCell )
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 );
234 if ( pCell )
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
242 bool bTmp = false;
243 if ( eRet == ERRCODE_NONE &&
244 aRet.getValueType() == cppu::UnoType<bool>::get() &&
245 ( aRet >>= bTmp ) &&
246 !bTmp )
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));
257 aBox->Execute();
260 return bScriptReturnedFalse;
263 // true -> abort
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() )
276 return false;
278 bool bDone = false;
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());
300 OUString aBasicStr;
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
309 else
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;
317 double nValue = 0.0;
318 bool bIsValue = false;
319 if ( pCell ) // if cell set, called from interpret
321 bIsValue = pCell->IsValue();
322 if ( bIsValue )
323 nValue = pCell->GetValue();
324 else
325 aValStr = pCell->GetString().getString();
327 if ( bIsValue )
328 refPar->Get(1)->PutDouble( nValue );
329 else
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 );
342 if ( pCell )
343 pDocument->LockTable( rPos.Tab() );
344 SbxVariableRef refRes = new SbxVariable;
345 ErrCode eRet = pDocSh->CallBasic( aMacroStr.makeStringAndClear(), aBasicStr, refPar, refRes );
346 if ( pCell )
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() )
354 bRet = true;
355 bDone = true;
357 #endif
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));
363 aBox->Execute();
366 return bRet;
369 void ScValidationData::DoCalcError( ScFormulaCell* pCell ) const
371 if ( eErrorStyle == SC_VALERR_MACRO )
372 DoMacro( pCell->aPos, EMPTY_OUSTRING, pCell, NULL );
375 // true -> abort
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)
395 WinBits nStyle = 0;
396 switch (eErrorStyle)
398 case SC_VALERR_STOP:
399 nStyle = WB_OK | WB_DEF_OK;
400 break;
401 case SC_VALERR_WARNING:
402 nStyle = WB_OK_CANCEL | WB_DEF_CANCEL;
403 break;
404 case SC_VALERR_INFO:
405 nStyle = WB_OK_CANCEL | WB_DEF_OK;
406 break;
407 default:
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
423 return true;
425 if (rTest.isEmpty()) // check whether empty cells are allowed
426 return IsIgnoreBlank();
428 if (rTest[0] == '=') // formulas do not pass the validity test
429 return false;
431 SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable();
433 // get the value if any
434 sal_uInt32 nFormat = rPattern.GetNumberFormat( pFormatter );
435 double nVal;
436 bool bIsVal = pFormatter->IsNumberFormat( rTest, nFormat, nVal );
438 bool bRet;
439 if (SC_VALID_TEXTLEN == eDataMode)
441 double nLenVal;
442 if (!bIsVal)
443 nLenVal = static_cast<double>(rTest.getLength());
444 else
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.
450 OUString aStr;
451 pFormatter->GetInputLineString( nVal, nFormat, aStr);
452 nLenVal = static_cast<double>( aStr.getLength() );
454 ScRefCellValue aTmpCell(nLenVal);
455 bRet = IsCellValid(aTmpCell, rPos);
457 else
459 if (bIsVal)
461 ScRefCellValue aTmpCell(nVal);
462 bRet = IsDataValid(aTmpCell, rPos);
464 else
466 svl::SharedString aSS = mpDoc->GetSharedStringPool().intern(rTest);
467 ScRefCellValue aTmpCell(&aSS);
468 bRet = IsDataValid(aTmpCell, rPos);
472 return bRet;
475 bool ScValidationData::IsDataValid( ScRefCellValue& rCell, const ScAddress& rPos ) const
477 if( eDataMode == SC_VALID_LIST )
478 return IsListValid(rCell, rPos);
480 double nVal = 0.0;
481 OUString aString;
482 bool bIsVal = true;
484 switch (rCell.meType)
486 case CELLTYPE_VALUE:
487 nVal = rCell.mfValue;
488 break;
489 case CELLTYPE_STRING:
490 aString = rCell.mpString->getString();
491 bIsVal = false;
492 break;
493 case CELLTYPE_EDIT:
494 if (rCell.mpEditText)
495 aString = ScEditUtil::GetString(*rCell.mpEditText, GetDocument());
496 bIsVal = false;
497 break;
498 case CELLTYPE_FORMULA:
500 ScFormulaCell* pFCell = rCell.mpFormula;
501 bIsVal = pFCell->IsValue();
502 if ( bIsVal )
503 nVal = pFCell->GetValue();
504 else
505 aString = pFCell->GetString().getString();
507 break;
508 default: // Notes, Broadcaster
509 return IsIgnoreBlank(); // as set
512 bool bOk = true;
513 switch (eDataMode)
515 // SC_VALID_ANY schon oben
517 case SC_VALID_WHOLE:
518 case SC_VALID_DECIMAL:
519 case SC_VALID_DATE: // Date/Time is only formatting
520 case SC_VALID_TIME:
521 bOk = bIsVal;
522 if ( bOk && eDataMode == SC_VALID_WHOLE )
523 bOk = ::rtl::math::approxEqual( nVal, floor(nVal+0.5) ); // integers
524 if ( bOk )
525 bOk = IsCellValid(rCell, rPos);
526 break;
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);
532 break;
534 case SC_VALID_TEXTLEN:
535 bOk = !bIsVal; // only Text
536 if ( bOk )
538 double nLenVal = (double) aString.getLength();
539 ScRefCellValue aTmpCell(nLenVal);
540 bOk = IsCellValid(aTmpCell, rPos);
542 break;
544 default:
545 OSL_FAIL("not yet done");
546 break;
549 return bOk;
552 namespace {
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
559 public:
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. */
566 rtl_uString* Next();
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; }
571 private:
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()
580 mrTokArr.Reset();
581 mbOk = true;
582 return Next();
585 rtl_uString* ScStringTokenIterator::Next()
587 if( !mbOk )
588 return NULL;
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.
598 if (mbOk && pToken)
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() );
609 if( !pPattern )
610 pPattern = rDoc.GetDefPattern();
611 return pPattern->GetNumberFormat( rDoc.GetFormatTable() );
614 } // namespace
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
625 bool bOk = true;
627 // pDoc is private in condition, use an accessor and a long winded name.
628 ScDocument* pDocument = GetDocument();
629 if( NULL == pDocument )
630 return false;
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();
639 ScMatrixRef xMatRef;
640 const ScMatrix *pValues = aValidationSrc.GetMatrix();
641 if (!pValues)
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();
651 if (nErrCode)
653 /* TODO : to use later in an alert box?
654 * OUString rStrResult = "...";
655 * rStrResult += ScGlobal::GetLongErrorString(nErrCode);
658 xMatRef->PutError( nErrCode, 0, 0);
659 bOk = false;
661 else if (aValidationSrc.IsValue())
662 xMatRef->PutDouble( aValidationSrc.GetValue(), 0);
663 else
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.
673 rMatch = -1;
675 SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable();
677 SCSIZE nCol, nRow, nCols, nRows, n = 0;
678 pValues->GetDimensions( nCols, nRows );
680 bool bRef = false;
681 ScRange aRange;
683 ScTokenArray* pArr = const_cast<ScTokenArray*>(&rTokArr);
684 pArr->Reset();
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);
694 bRef = true;
697 else if (eOpCode == ocName)
699 ScRangeData* pName = pDocument->GetRangeName()->findByIndex( t->GetIndex() );
700 if (pName && pName->IsReference(aRange))
702 bRef = true;
705 else if (t->GetType() != svIndex)
707 if (pArr->IsValidReference(aRange, rPos))
709 bRef = true;
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
722 * same. */
723 for( nRow = 0; nRow < nRows ; nRow++ )
725 for( nCol = 0; nCol < nCols ; nCol++ )
727 ScTokenArray aCondTokArr;
728 ScTypedStrData* pEntry = NULL;
729 OUString aValStr;
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())
742 if (bHaveEmpty)
743 continue;
744 bHaveEmpty = true;
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));
753 else
755 sal_uInt16 nErr = nMatVal.GetError();
757 if( 0 != nErr )
759 aValStr = ScGlobal::GetErrorString( nErr );
761 else
763 // FIXME FIXME FIXME
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.
767 if ( bRef )
769 pDocument->GetInputString((SCCOL)(nCol+aRange.aStart.Col()),
770 (SCROW)(nRow+aRange.aStart.Row()), aRange.aStart.Tab() , aValStr);
772 else
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))
790 rMatch = n;
791 // short circuit on the first match if not filling the list
792 if( NULL == pStrings )
793 return true;
796 if( NULL != pEntry )
798 pStrings->push_back(*pEntry);
799 delete pEntry;
800 n++;
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
812 bool bOk = false;
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())
824 double fValue;
825 OUString aStr(pString);
826 bool bIsValue = GetDocument()->GetFormatTable()->IsNumberFormat(aStr, nFormat, fValue);
827 rStrColl.push_back(
828 ScTypedStrData(
829 aStr, fValue, bIsValue ? ScTypedStrData::Value : ScTypedStrData::Standard));
831 bOk = aIt.Ok();
833 // *** if not a string list, try if formula results in a cell range or
834 // anything else we recognize as valid ***
836 if (!bOk)
838 int nMatch;
839 ScRefCellValue aEmptyCell;
840 bOk = GetSelectionFromFormula(&rStrColl, aEmptyCell, rPos, *pTokArr, nMatch);
844 return bOk;
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
864 or matrix/array).
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. */
880 if( !bIsValid )
882 // create a formula containing a single string or number
883 ScTokenArray aCondTokArr;
884 double fValue;
885 OUString aStr(pString);
886 if (GetDocument()->GetFormatTable()->IsNumberFormat(aStr, nFormat, fValue))
887 aCondTokArr.AddDouble( fValue );
888 else
889 aCondTokArr.AddString(rSPool.intern(aStr));
891 bIsValid = IsEqualToTokenArray(rCell, rPos, aCondTokArr);
895 if( !aIt.Ok() )
896 bIsValid = false;
898 // *** if not a string list, try if formula results in a cell range or
899 // anything else we recognize as valid ***
901 if (!bIsValid)
903 int nMatch;
904 bIsValid = GetSelectionFromFormula(NULL, rCell, rPos, *pTokArr, nMatch);
905 bIsValid = bIsValid && nMatch >= 0;
908 return bIsValid;
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 )
942 return *it;
944 OSL_FAIL("ScValidationDataList: Entry not found");
945 return NULL;
948 void ScValidationDataList::CompileXML()
950 for( iterator it = begin(); it != end(); ++it )
951 (*it)->CompileXML();
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 ?
986 bEqual = false;
988 return bEqual;
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()
1013 maData.clear();
1016 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */