Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <config_features.h>
21 :
22 : #include "validat.hxx"
23 :
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>
31 :
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>
38 :
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"
52 :
53 : #include <math.h>
54 : #include <boost/scoped_ptr.hpp>
55 :
56 : using namespace formula;
57 :
58 : // Entries for validation (with only one condition)
59 :
60 134 : 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 134 : , mnListType( css::sheet::TableValidationVisibility::UNSORTED )
74 : {
75 134 : }
76 :
77 6 : 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 6 : , mnListType( css::sheet::TableValidationVisibility::UNSORTED )
87 : {
88 6 : }
89 :
90 6 : 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 6 : , aErrorMessage( r.aErrorMessage )
102 : {
103 : // Formulae copied by RefCount
104 6 : }
105 :
106 56 : 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 56 : , aErrorMessage( r.aErrorMessage )
118 : {
119 : // Formulae really copied
120 56 : }
121 :
122 290 : ScValidationData::~ScValidationData()
123 : {
124 290 : }
125 :
126 68 : bool ScValidationData::IsEmpty() const
127 : {
128 68 : OUString aEmpty;
129 136 : ScValidationData aDefault( SC_VALID_ANY, SC_COND_EQUAL, aEmpty, aEmpty, GetDocument(), ScAddress() );
130 136 : return EqualEntries( aDefault );
131 : }
132 :
133 230 : bool ScValidationData::EqualEntries( const ScValidationData& r ) const
134 : {
135 : // test same parameters (excluding Key)
136 :
137 310 : return ScConditionEntry::operator==(r) &&
138 160 : eDataMode == r.eDataMode &&
139 160 : bShowInput == r.bShowInput &&
140 160 : bShowError == r.bShowError &&
141 160 : eErrorStyle == r.eErrorStyle &&
142 160 : mnListType == r.mnListType &&
143 146 : aInputTitle == r.aInputTitle &&
144 122 : aInputMessage == r.aInputMessage &&
145 330 : aErrorTitle == r.aErrorTitle &&
146 274 : aErrorMessage == r.aErrorMessage;
147 : }
148 :
149 60 : void ScValidationData::ResetInput()
150 : {
151 60 : bShowInput = false;
152 60 : }
153 :
154 32 : void ScValidationData::ResetError()
155 : {
156 32 : bShowError = false;
157 32 : }
158 :
159 60 : void ScValidationData::SetInput( const OUString& rTitle, const OUString& rMsg )
160 : {
161 60 : bShowInput = true;
162 60 : aInputTitle = rTitle;
163 60 : aInputMessage = rMsg;
164 60 : }
165 :
166 70 : void ScValidationData::SetError( const OUString& rTitle, const OUString& rMsg,
167 : ScValidErrorStyle eStyle )
168 : {
169 70 : bShowError = true;
170 70 : eErrorStyle = eStyle;
171 70 : aErrorTitle = rTitle;
172 70 : aErrorMessage = rMsg;
173 70 : }
174 :
175 96 : bool ScValidationData::GetErrMsg( OUString& rTitle, OUString& rMsg,
176 : ScValidErrorStyle& rStyle ) const
177 : {
178 96 : rTitle = aErrorTitle;
179 96 : rMsg = aErrorMessage;
180 96 : rStyle = eErrorStyle;
181 96 : return bShowError;
182 : }
183 :
184 0 : bool ScValidationData::DoScript( const ScAddress& rPos, const OUString& rInput,
185 : ScFormulaCell* pCell, vcl::Window* pParent ) const
186 : {
187 0 : ScDocument* pDocument = GetDocument();
188 0 : SfxObjectShell* pDocSh = pDocument->GetDocumentShell();
189 0 : if ( !pDocSh || !pDocument->CheckMacroWarn() )
190 0 : return false;
191 :
192 0 : bool bScriptReturnedFalse = false; // default: do not abort
193 :
194 : // Set up parameters
195 0 : ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > aParams(2);
196 :
197 : // 1) entered or calculated value
198 0 : OUString aValStr = rInput;
199 : double nValue;
200 0 : bool bIsValue = false;
201 0 : if ( pCell ) // if cell exists, call interpret
202 : {
203 0 : bIsValue = pCell->IsValue();
204 0 : if ( bIsValue )
205 0 : nValue = pCell->GetValue();
206 : else
207 0 : aValStr = pCell->GetString().getString();
208 : }
209 0 : if ( bIsValue )
210 0 : aParams[0] = ::com::sun::star::uno::makeAny( nValue );
211 : else
212 0 : aParams[0] = ::com::sun::star::uno::makeAny( OUString( aValStr ) );
213 :
214 : // 2) Position of the cell
215 0 : OUString aPosStr(rPos.Format(SCA_VALID | SCA_TAB_3D, pDocument, pDocument->GetAddressConvention()));
216 0 : aParams[1] = ::com::sun::star::uno::makeAny(aPosStr);
217 :
218 : // use link-update flag to prevent closing the document
219 : // while the macro is running
220 0 : bool bWasInLinkUpdate = pDocument->IsInLinkUpdate();
221 0 : if ( !bWasInLinkUpdate )
222 0 : pDocument->SetInLinkUpdate( true );
223 :
224 0 : if ( pCell )
225 0 : pDocument->LockTable( rPos.Tab() );
226 :
227 0 : ::com::sun::star::uno::Any aRet;
228 0 : ::com::sun::star::uno::Sequence< sal_Int16 > aOutArgsIndex;
229 0 : ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > aOutArgs;
230 :
231 : ErrCode eRet = pDocSh->CallXScript(
232 0 : aErrorTitle, aParams, aRet, aOutArgsIndex, aOutArgs );
233 :
234 0 : if ( pCell )
235 0 : pDocument->UnlockTable( rPos.Tab() );
236 :
237 0 : if ( !bWasInLinkUpdate )
238 0 : pDocument->SetInLinkUpdate( false );
239 :
240 : // Check the return value from the script
241 : // The contents of the cell get reset if the script returns false
242 0 : bool bTmp = false;
243 0 : if ( eRet == ERRCODE_NONE &&
244 0 : aRet.getValueType() == getCppuBooleanType() &&
245 0 : ( aRet >>= bTmp ) &&
246 0 : !bTmp )
247 : {
248 0 : bScriptReturnedFalse = true;
249 : }
250 :
251 0 : if ( eRet == ERRCODE_BASIC_METHOD_NOT_FOUND && !pCell )
252 : // Macro not found (only with input)
253 : {
254 : //! different error message, if found, but not bAllowed ??
255 :
256 0 : MessageDialog aBox( pParent, ScGlobal::GetRscString(STR_VALID_MACRONOTFOUND));
257 0 : aBox.Execute();
258 : }
259 :
260 0 : return bScriptReturnedFalse;
261 : }
262 :
263 : // true -> abort
264 :
265 0 : bool ScValidationData::DoMacro( const ScAddress& rPos, const OUString& rInput,
266 : ScFormulaCell* pCell, vcl::Window* pParent ) const
267 : {
268 0 : if ( SfxApplication::IsXScriptURL( aErrorTitle ) )
269 : {
270 0 : return DoScript( rPos, rInput, pCell, pParent );
271 : }
272 :
273 0 : ScDocument* pDocument = GetDocument();
274 0 : SfxObjectShell* pDocSh = pDocument->GetDocumentShell();
275 0 : if ( !pDocSh || !pDocument->CheckMacroWarn() )
276 0 : return false;
277 :
278 0 : bool bDone = false;
279 0 : bool bRet = false; // default: do not abort
280 :
281 : // If the Doc was loaded during a Basic-Calls,
282 : // the Sbx-Objekt may not be created (?)
283 : // pDocSh->GetSbxObject();
284 :
285 : #if HAVE_FEATURE_SCRIPTING
286 : // no security check ahead (only CheckMacroWarn), that happens in CallBasic
287 :
288 : // Function search by their simple name,
289 : // then assemble aBasicStr, aMacroStr for SfxObjectShell::CallBasic
290 :
291 0 : StarBASIC* pRoot = pDocSh->GetBasic();
292 0 : SbxVariable* pVar = pRoot->Find( aErrorTitle, SbxCLASS_METHOD );
293 0 : if ( pVar && pVar->ISA(SbMethod) )
294 : {
295 0 : SbMethod* pMethod = static_cast<SbMethod*>(pVar);
296 0 : SbModule* pModule = pMethod->GetModule();
297 0 : SbxObject* pObject = pModule->GetParent();
298 0 : OUStringBuffer aMacroStr = pObject->GetName();
299 0 : aMacroStr.append('.').append(pModule->GetName()).append('.').append(pMethod->GetName());
300 0 : OUString aBasicStr;
301 :
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.
306 :
307 0 : if ( pObject->GetParent() )
308 0 : aBasicStr = pObject->GetParent()->GetName(); // Basic of document
309 : else
310 0 : aBasicStr = SfxGetpApp()->GetName(); // Basic of application
311 :
312 : // Parameter for Macro
313 0 : SbxArrayRef refPar = new SbxArray;
314 :
315 : // 1) entered or calculated value
316 0 : OUString aValStr = rInput;
317 0 : double nValue = 0.0;
318 0 : bool bIsValue = false;
319 0 : if ( pCell ) // if cell set, called from interpret
320 : {
321 0 : bIsValue = pCell->IsValue();
322 0 : if ( bIsValue )
323 0 : nValue = pCell->GetValue();
324 : else
325 0 : aValStr = pCell->GetString().getString();
326 : }
327 0 : if ( bIsValue )
328 0 : refPar->Get(1)->PutDouble( nValue );
329 : else
330 0 : refPar->Get(1)->PutString( aValStr );
331 :
332 : // 2) Position of the cell
333 0 : OUString aPosStr(rPos.Format(SCA_VALID | SCA_TAB_3D, pDocument, pDocument->GetAddressConvention()));
334 0 : refPar->Get(2)->PutString( aPosStr );
335 :
336 : // use link-update flag to prevent closing the document
337 : // while the macro is running
338 0 : bool bWasInLinkUpdate = pDocument->IsInLinkUpdate();
339 0 : if ( !bWasInLinkUpdate )
340 0 : pDocument->SetInLinkUpdate( true );
341 :
342 0 : if ( pCell )
343 0 : pDocument->LockTable( rPos.Tab() );
344 0 : SbxVariableRef refRes = new SbxVariable;
345 0 : ErrCode eRet = pDocSh->CallBasic( aMacroStr.makeStringAndClear(), aBasicStr, refPar, refRes );
346 0 : if ( pCell )
347 0 : pDocument->UnlockTable( rPos.Tab() );
348 :
349 0 : if ( !bWasInLinkUpdate )
350 0 : pDocument->SetInLinkUpdate( false );
351 :
352 : // Interrupt input if Basic macro returns false
353 0 : if ( eRet == ERRCODE_NONE && refRes->GetType() == SbxBOOL && !refRes->GetBool() )
354 0 : bRet = true;
355 0 : bDone = true;
356 : }
357 : #endif
358 0 : if ( !bDone && !pCell ) // Macro not found (only with input)
359 : {
360 : //! different error message, if found, but not bAllowed ??
361 :
362 0 : MessageDialog aBox(pParent, ScGlobal::GetRscString(STR_VALID_MACRONOTFOUND));
363 0 : aBox.Execute();
364 : }
365 :
366 0 : return bRet;
367 : }
368 :
369 0 : void ScValidationData::DoCalcError( ScFormulaCell* pCell ) const
370 : {
371 0 : if ( eErrorStyle == SC_VALERR_MACRO )
372 0 : DoMacro( pCell->aPos, EMPTY_OUSTRING, pCell, NULL );
373 0 : }
374 :
375 : // true -> abort
376 :
377 0 : bool ScValidationData::DoError( vcl::Window* pParent, const OUString& rInput,
378 : const ScAddress& rPos ) const
379 : {
380 0 : if ( eErrorStyle == SC_VALERR_MACRO )
381 0 : return DoMacro( rPos, rInput, NULL, pParent );
382 :
383 : // Output error message
384 :
385 0 : OUString aTitle = aErrorTitle;
386 0 : if (aTitle.isEmpty())
387 0 : aTitle = ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_0 ); // application title
388 0 : OUString aMessage = aErrorMessage;
389 0 : if (aMessage.isEmpty())
390 0 : aMessage = ScGlobal::GetRscString( STR_VALID_DEFERROR );
391 :
392 : //! ErrorBox / WarningBox / InfoBox ?
393 : //! (with InfoBox always OK-Button only)
394 :
395 0 : WinBits nStyle = 0;
396 0 : switch (eErrorStyle)
397 : {
398 : case SC_VALERR_STOP:
399 0 : nStyle = WB_OK | WB_DEF_OK;
400 0 : break;
401 : case SC_VALERR_WARNING:
402 0 : nStyle = WB_OK_CANCEL | WB_DEF_CANCEL;
403 0 : break;
404 : case SC_VALERR_INFO:
405 0 : nStyle = WB_OK_CANCEL | WB_DEF_OK;
406 0 : break;
407 : default:
408 : {
409 : // added to avoid warnings
410 : }
411 : }
412 :
413 0 : MessBox aBox( pParent, WinBits(nStyle), aTitle, aMessage );
414 0 : sal_uInt16 nRet = aBox.Execute();
415 :
416 0 : return ( eErrorStyle == SC_VALERR_STOP || nRet == RET_CANCEL );
417 : }
418 :
419 0 : bool ScValidationData::IsDataValid(
420 : const OUString& rTest, const ScPatternAttr& rPattern, const ScAddress& rPos ) const
421 : {
422 0 : if ( eDataMode == SC_VALID_ANY ) // check if any cell content is allowed
423 0 : return true;
424 :
425 0 : if (rTest.isEmpty()) // check whether empty cells are allowed
426 0 : return IsIgnoreBlank();
427 :
428 0 : if (rTest[0] == '=') // formulas do not pass the validity test
429 0 : return false;
430 :
431 0 : SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable();
432 :
433 : // get the value if any
434 0 : sal_uInt32 nFormat = rPattern.GetNumberFormat( pFormatter );
435 : double nVal;
436 0 : bool bIsVal = pFormatter->IsNumberFormat( rTest, nFormat, nVal );
437 :
438 : bool bRet;
439 0 : if (SC_VALID_TEXTLEN == eDataMode)
440 : {
441 : double nLenVal;
442 0 : if (!bIsVal)
443 0 : nLenVal = static_cast<double>(rTest.getLength());
444 : else
445 : {
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 0 : OUString aStr;
451 0 : pFormatter->GetInputLineString( nVal, nFormat, aStr);
452 0 : nLenVal = static_cast<double>( aStr.getLength() );
453 : }
454 0 : ScRefCellValue aTmpCell(nLenVal);
455 0 : bRet = IsCellValid(aTmpCell, rPos);
456 : }
457 : else
458 : {
459 0 : if (bIsVal)
460 : {
461 0 : ScRefCellValue aTmpCell(nVal);
462 0 : bRet = IsDataValid(aTmpCell, rPos);
463 : }
464 : else
465 : {
466 0 : svl::SharedString aSS = mpDoc->GetSharedStringPool().intern(rTest);
467 0 : ScRefCellValue aTmpCell(&aSS);
468 0 : bRet = IsDataValid(aTmpCell, rPos);
469 : }
470 : }
471 :
472 0 : return bRet;
473 : }
474 :
475 0 : bool ScValidationData::IsDataValid( ScRefCellValue& rCell, const ScAddress& rPos ) const
476 : {
477 0 : if( eDataMode == SC_VALID_LIST )
478 0 : return IsListValid(rCell, rPos);
479 :
480 0 : double nVal = 0.0;
481 0 : OUString aString;
482 0 : bool bIsVal = true;
483 :
484 0 : switch (rCell.meType)
485 : {
486 : case CELLTYPE_VALUE:
487 0 : nVal = rCell.mfValue;
488 0 : break;
489 : case CELLTYPE_STRING:
490 0 : aString = rCell.mpString->getString();
491 0 : bIsVal = false;
492 0 : break;
493 : case CELLTYPE_EDIT:
494 0 : if (rCell.mpEditText)
495 0 : aString = ScEditUtil::GetString(*rCell.mpEditText, GetDocument());
496 0 : bIsVal = false;
497 0 : break;
498 : case CELLTYPE_FORMULA:
499 : {
500 0 : ScFormulaCell* pFCell = rCell.mpFormula;
501 0 : bIsVal = pFCell->IsValue();
502 0 : if ( bIsVal )
503 0 : nVal = pFCell->GetValue();
504 : else
505 0 : aString = pFCell->GetString().getString();
506 : }
507 0 : break;
508 : default: // Notes, Broadcaster
509 0 : return IsIgnoreBlank(); // as set
510 : }
511 :
512 0 : bool bOk = true;
513 0 : switch (eDataMode)
514 : {
515 : // SC_VALID_ANY schon oben
516 :
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 0 : bOk = bIsVal;
522 0 : if ( bOk && eDataMode == SC_VALID_WHOLE )
523 0 : bOk = ::rtl::math::approxEqual( nVal, floor(nVal+0.5) ); // integers
524 0 : if ( bOk )
525 0 : bOk = IsCellValid(rCell, rPos);
526 0 : break;
527 :
528 : case SC_VALID_CUSTOM:
529 : // for Custom, it must be eOp == SC_COND_DIRECT
530 : //! the value must be in the document !!!
531 0 : bOk = IsCellValid(rCell, rPos);
532 0 : break;
533 :
534 : case SC_VALID_TEXTLEN:
535 0 : bOk = !bIsVal; // only Text
536 0 : if ( bOk )
537 : {
538 0 : double nLenVal = (double) aString.getLength();
539 0 : ScRefCellValue aTmpCell(nLenVal);
540 0 : bOk = IsCellValid(aTmpCell, rPos);
541 : }
542 0 : break;
543 :
544 : default:
545 : OSL_FAIL("not yet done");
546 0 : break;
547 : }
548 :
549 0 : return bOk;
550 : }
551 :
552 : namespace {
553 :
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 10 : class ScStringTokenIterator
558 : {
559 : public:
560 10 : inline explicit ScStringTokenIterator( ScTokenArray& rTokArr, bool bSkipEmpty = true ) :
561 10 : mrTokArr( rTokArr ), mbSkipEmpty( bSkipEmpty ), mbOk( true ) {}
562 :
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();
567 :
568 : /** Returns false, if a wrong token has been found. Does NOT return false on end of token array. */
569 10 : inline bool Ok() const { return mbOk; }
570 :
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.
576 : };
577 :
578 10 : rtl_uString* ScStringTokenIterator::First()
579 : {
580 10 : mrTokArr.Reset();
581 10 : mbOk = true;
582 10 : return Next();
583 : }
584 :
585 10 : rtl_uString* ScStringTokenIterator::Next()
586 : {
587 10 : if( !mbOk )
588 0 : return NULL;
589 :
590 : // seek to next non-separator token
591 10 : const FormulaToken* pToken = mrTokArr.NextNoSpaces();
592 20 : while( pToken && (pToken->GetOpCode() == ocSep) )
593 0 : pToken = mrTokArr.NextNoSpaces();
594 :
595 10 : mbOk = !pToken || (pToken->GetType() == formula::svString);
596 :
597 10 : maCurString = svl::SharedString(); // start with invalid string.
598 10 : if (mbOk && pToken)
599 0 : maCurString = pToken->GetString();
600 :
601 : // string found but empty -> get next token; otherwise return it
602 10 : return (mbSkipEmpty && maCurString.isValid() && maCurString.isEmpty()) ? Next() : maCurString.getData();
603 : }
604 :
605 : /** Returns the number format of the passed cell, or the standard format. */
606 10 : sal_uLong lclGetCellFormat( ScDocument& rDoc, const ScAddress& rPos )
607 : {
608 10 : const ScPatternAttr* pPattern = rDoc.GetPattern( rPos.Col(), rPos.Row(), rPos.Tab() );
609 10 : if( !pPattern )
610 0 : pPattern = rDoc.GetDefPattern();
611 10 : return pPattern->GetNumberFormat( rDoc.GetFormatTable() );
612 : }
613 :
614 : } // namespace
615 :
616 10 : bool ScValidationData::HasSelectionList() const
617 : {
618 10 : return (eDataMode == SC_VALID_LIST) && (mnListType != css::sheet::TableValidationVisibility::INVISIBLE);
619 : }
620 :
621 10 : bool ScValidationData::GetSelectionFromFormula(
622 : std::vector<ScTypedStrData>* pStrings, ScRefCellValue& rCell, const ScAddress& rPos,
623 : const ScTokenArray& rTokArr, int& rMatch) const
624 : {
625 10 : bool bOk = true;
626 :
627 : // pDoc is private in condition, use an accessor and a long winded name.
628 10 : ScDocument* pDocument = GetDocument();
629 10 : if( NULL == pDocument )
630 0 : return false;
631 :
632 : ScFormulaCell aValidationSrc(
633 10 : pDocument, rPos, rTokArr, formula::FormulaGrammar::GRAM_DEFAULT, MM_FORMULA);
634 :
635 : // Make sure the formula gets interpreted and a result is delivered,
636 : // regardless of the AutoCalc setting.
637 10 : aValidationSrc.Interpret();
638 :
639 20 : ScMatrixRef xMatRef;
640 10 : const ScMatrix *pValues = aValidationSrc.GetMatrix();
641 10 : if (!pValues)
642 : {
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.
646 :
647 : // Use an interim matrix to create the TypedStrData below.
648 0 : xMatRef = new ScMatrix(1, 1, 0.0);
649 :
650 0 : sal_uInt16 nErrCode = aValidationSrc.GetErrCode();
651 0 : if (nErrCode)
652 : {
653 : /* TODO : to use later in an alert box?
654 : * OUString rStrResult = "...";
655 : * rStrResult += ScGlobal::GetLongErrorString(nErrCode);
656 : */
657 :
658 0 : xMatRef->PutError( nErrCode, 0, 0);
659 0 : bOk = false;
660 : }
661 0 : else if (aValidationSrc.IsValue())
662 0 : xMatRef->PutDouble( aValidationSrc.GetValue(), 0);
663 : else
664 : {
665 0 : svl::SharedString aStr = aValidationSrc.GetString();
666 0 : xMatRef->PutString(aStr, 0);
667 : }
668 :
669 0 : pValues = xMatRef.get();
670 : }
671 :
672 : // which index matched. We will want it eventually to pre-select that item.
673 10 : rMatch = -1;
674 :
675 10 : SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable();
676 :
677 10 : SCSIZE nCol, nRow, nCols, nRows, n = 0;
678 10 : pValues->GetDimensions( nCols, nRows );
679 :
680 10 : bool bRef = false;
681 10 : ScRange aRange;
682 :
683 10 : ScTokenArray* pArr = (ScTokenArray*) &rTokArr;
684 10 : pArr->Reset();
685 10 : formula::FormulaToken* t = NULL;
686 10 : if (pArr->GetLen() == 1 && (t = pArr->GetNextReferenceOrName()) != NULL)
687 : {
688 10 : if (t->GetOpCode() == ocDBArea)
689 : {
690 0 : if (const ScDBData* pDBData = pDocument->GetDBCollection()->getNamedDBs().findByIndex(t->GetIndex()))
691 : {
692 0 : pDBData->GetArea(aRange);
693 0 : bRef = true;
694 : }
695 : }
696 10 : else if (t->GetOpCode() == ocName)
697 : {
698 0 : ScRangeData* pName = pDocument->GetRangeName()->findByIndex( t->GetIndex() );
699 0 : if (pName && pName->IsReference(aRange))
700 : {
701 0 : bRef = true;
702 : }
703 : }
704 10 : else if (t->GetType() != svIndex)
705 : {
706 10 : if (pArr->IsValidReference(aRange, rPos))
707 : {
708 10 : bRef = true;
709 : }
710 : }
711 : }
712 :
713 10 : bool bHaveEmpty = false;
714 10 : svl::SharedStringPool& rSPool = pDocument->GetSharedStringPool();
715 :
716 : /* XL artificially limits things to a single col or row in the UI but does
717 : * not list the constraint in MOOXml. If a defined name or INDIRECT
718 : * resulting in 1D is entered in the UI and the definition later modified
719 : * to 2D, it is evaluated fine and also stored and loaded. Lets get ahead
720 : * of the curve and support 2d. In XL, values are listed row-wise, do the
721 : * same. */
722 40 : for( nRow = 0; nRow < nRows ; nRow++ )
723 : {
724 60 : for( nCol = 0; nCol < nCols ; nCol++ )
725 : {
726 30 : ScTokenArray aCondTokArr;
727 30 : ScTypedStrData* pEntry = NULL;
728 60 : OUString aValStr;
729 60 : ScMatrixValue nMatVal = pValues->Get( nCol, nRow);
730 :
731 : // strings and empties
732 30 : if( ScMatrix::IsNonValueType( nMatVal.nType ) )
733 : {
734 0 : aValStr = nMatVal.GetString().getString();
735 :
736 : // Do not add multiple empty strings to the validation list,
737 : // especially not if they'd bloat the tail with a million empty
738 : // entries for a column range, fdo#61520
739 0 : if (aValStr.isEmpty())
740 : {
741 0 : if (bHaveEmpty)
742 0 : continue;
743 0 : bHaveEmpty = true;
744 : }
745 :
746 0 : if( NULL != pStrings )
747 0 : pEntry = new ScTypedStrData( aValStr, 0.0, ScTypedStrData::Standard);
748 :
749 0 : if (!rCell.isEmpty() && rMatch < 0)
750 0 : aCondTokArr.AddString(rSPool.intern(aValStr));
751 : }
752 : else
753 : {
754 30 : sal_uInt16 nErr = nMatVal.GetError();
755 :
756 30 : if( 0 != nErr )
757 : {
758 0 : aValStr = ScGlobal::GetErrorString( nErr );
759 : }
760 : else
761 : {
762 : // FIXME FIXME FIXME
763 : // Feature regression. Date formats are lost passing through the matrix
764 : //pFormatter->GetInputLineString( pMatVal->fVal, 0, aValStr );
765 : //For external reference and a formula that results in an area or array, date formats are still lost.
766 30 : if ( bRef )
767 : {
768 30 : pDocument->GetInputString((SCCOL)(nCol+aRange.aStart.Col()),
769 60 : (SCROW)(nRow+aRange.aStart.Row()), aRange.aStart.Tab() , aValStr);
770 : }
771 : else
772 : {
773 0 : pFormatter->GetInputLineString( nMatVal.fVal, 0, aValStr );
774 : }
775 : }
776 :
777 30 : if (!rCell.isEmpty() && rMatch < 0)
778 : {
779 : // I am not sure errors will work here, but a user can no
780 : // manually enter an error yet so the point is somewhat moot.
781 0 : aCondTokArr.AddDouble( nMatVal.fVal );
782 : }
783 30 : if( NULL != pStrings )
784 30 : pEntry = new ScTypedStrData( aValStr, nMatVal.fVal, ScTypedStrData::Value);
785 : }
786 :
787 30 : if (rMatch < 0 && !rCell.isEmpty() && IsEqualToTokenArray(rCell, rPos, aCondTokArr))
788 : {
789 0 : rMatch = n;
790 : // short circuit on the first match if not filling the list
791 0 : if( NULL == pStrings )
792 0 : return true;
793 : }
794 :
795 30 : if( NULL != pEntry )
796 : {
797 30 : pStrings->push_back(*pEntry);
798 30 : delete pEntry;
799 30 : n++;
800 : }
801 30 : }
802 : }
803 :
804 : // In case of no match needed and an error occurred, return that error
805 : // entry as valid instead of silently failing.
806 20 : return bOk || rCell.isEmpty();
807 : }
808 :
809 10 : bool ScValidationData::FillSelectionList(std::vector<ScTypedStrData>& rStrColl, const ScAddress& rPos) const
810 : {
811 10 : bool bOk = false;
812 :
813 10 : if( HasSelectionList() )
814 : {
815 10 : boost::scoped_ptr<ScTokenArray> pTokArr( CreateTokenArry(0) );
816 :
817 : // *** try if formula is a string list ***
818 :
819 10 : sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos );
820 20 : ScStringTokenIterator aIt( *pTokArr );
821 10 : for (rtl_uString* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next())
822 : {
823 : double fValue;
824 0 : OUString aStr(pString);
825 0 : bool bIsValue = GetDocument()->GetFormatTable()->IsNumberFormat(aStr, nFormat, fValue);
826 : rStrColl.push_back(
827 : ScTypedStrData(
828 0 : aStr, fValue, bIsValue ? ScTypedStrData::Value : ScTypedStrData::Standard));
829 0 : }
830 10 : bOk = aIt.Ok();
831 :
832 : // *** if not a string list, try if formula results in a cell range or
833 : // anything else we recognize as valid ***
834 :
835 10 : if (!bOk)
836 : {
837 : int nMatch;
838 10 : ScRefCellValue aEmptyCell;
839 10 : bOk = GetSelectionFromFormula(&rStrColl, aEmptyCell, rPos, *pTokArr, nMatch);
840 10 : }
841 : }
842 :
843 10 : return bOk;
844 : }
845 :
846 0 : bool ScValidationData::IsEqualToTokenArray( ScRefCellValue& rCell, const ScAddress& rPos, const ScTokenArray& rTokArr ) const
847 : {
848 : // create a condition entry that tests on equality and set the passed token array
849 0 : ScConditionEntry aCondEntry( SC_COND_EQUAL, &rTokArr, NULL, GetDocument(), rPos );
850 0 : return aCondEntry.IsCellValid(rCell, rPos);
851 : }
852 :
853 0 : bool ScValidationData::IsListValid( ScRefCellValue& rCell, const ScAddress& rPos ) const
854 : {
855 0 : bool bIsValid = false;
856 :
857 : /* Compare input cell with all supported tokens from the formula.
858 : Currently a formula may contain:
859 : 1) A list of strings (at least one string).
860 : 2) A single cell or range reference.
861 : 3) A single defined name (must contain a cell/range reference, another
862 : name, or DB range, or a formula resulting in a cell/range reference
863 : or matrix/array).
864 : 4) A single database range.
865 : 5) A formula resulting in a cell/range reference or matrix/array.
866 : */
867 :
868 0 : boost::scoped_ptr< ScTokenArray > pTokArr( CreateTokenArry( 0 ) );
869 :
870 : // *** try if formula is a string list ***
871 :
872 0 : svl::SharedStringPool& rSPool = GetDocument()->GetSharedStringPool();
873 0 : sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos );
874 0 : ScStringTokenIterator aIt( *pTokArr );
875 0 : for (rtl_uString* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next())
876 : {
877 : /* Do not break the loop, if a valid string has been found.
878 : This is to find invalid tokens following in the formula. */
879 0 : if( !bIsValid )
880 : {
881 : // create a formula containing a single string or number
882 0 : ScTokenArray aCondTokArr;
883 : double fValue;
884 0 : OUString aStr(pString);
885 0 : if (GetDocument()->GetFormatTable()->IsNumberFormat(aStr, nFormat, fValue))
886 0 : aCondTokArr.AddDouble( fValue );
887 : else
888 0 : aCondTokArr.AddString(rSPool.intern(aStr));
889 :
890 0 : bIsValid = IsEqualToTokenArray(rCell, rPos, aCondTokArr);
891 : }
892 : }
893 :
894 0 : if( !aIt.Ok() )
895 0 : bIsValid = false;
896 :
897 : // *** if not a string list, try if formula results in a cell range or
898 : // anything else we recognize as valid ***
899 :
900 0 : if (!bIsValid)
901 : {
902 : int nMatch;
903 0 : bIsValid = GetSelectionFromFormula(NULL, rCell, rPos, *pTokArr, nMatch);
904 0 : bIsValid = bIsValid && nMatch >= 0;
905 : }
906 :
907 0 : return bIsValid;
908 : }
909 :
910 0 : ScValidationDataList::ScValidationDataList(const ScValidationDataList& rList)
911 : {
912 : // for Ref-Undo - real copy with new tokens!
913 :
914 0 : for (const_iterator it = rList.begin(); it != rList.end(); ++it)
915 : {
916 0 : InsertNew( (*it)->Clone() );
917 : }
918 :
919 : //! faster insert for sorted entries from rList ???
920 0 : }
921 :
922 4 : ScValidationDataList::ScValidationDataList(ScDocument* pNewDoc,
923 4 : const ScValidationDataList& rList)
924 : {
925 : // for new documents - real copy with new tokens!
926 :
927 26 : for (const_iterator it = rList.begin(); it != rList.end(); ++it)
928 : {
929 22 : InsertNew( (*it)->Clone(pNewDoc) );
930 : }
931 :
932 : //! faster insert for sorted entries from rList ???
933 4 : }
934 :
935 166 : ScValidationData* ScValidationDataList::GetData( sal_uInt32 nKey )
936 : {
937 : //! binary search
938 :
939 404 : for( iterator it = begin(); it != end(); ++it )
940 404 : if( (*it)->GetKey() == nKey )
941 166 : return *it;
942 :
943 : OSL_FAIL("ScValidationDataList: Entry not found");
944 0 : return NULL;
945 : }
946 :
947 2 : void ScValidationDataList::CompileXML()
948 : {
949 6 : for( iterator it = begin(); it != end(); ++it )
950 4 : (*it)->CompileXML();
951 2 : }
952 :
953 14 : void ScValidationDataList::UpdateReference( sc::RefUpdateContext& rCxt )
954 : {
955 46 : for( iterator it = begin(); it != end(); ++it )
956 32 : (*it)->UpdateReference(rCxt);
957 14 : }
958 :
959 0 : void ScValidationDataList::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
960 : {
961 0 : for (iterator it = begin(); it != end(); ++it)
962 0 : (*it)->UpdateInsertTab(rCxt);
963 0 : }
964 :
965 0 : void ScValidationDataList::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
966 : {
967 0 : for (iterator it = begin(); it != end(); ++it)
968 0 : (*it)->UpdateDeleteTab(rCxt);
969 0 : }
970 :
971 0 : void ScValidationDataList::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt )
972 : {
973 0 : for (iterator it = begin(); it != end(); ++it)
974 0 : (*it)->UpdateMoveTab(rCxt);
975 0 : }
976 :
977 0 : bool ScValidationDataList::operator==( const ScValidationDataList& r ) const
978 : {
979 : // for Ref-Undo - internal variables can not be compared
980 :
981 0 : size_t nCount = maData.size();
982 0 : bool bEqual = ( nCount == r.maData.size() );
983 0 : for( const_iterator it1 = begin(), it2 = r.begin(); it1 != end() && bEqual; ++it1, ++it2 ) // Entries are sorted
984 0 : if ( !(*it1)->EqualEntries(**it2) ) // different entries ?
985 0 : bEqual = false;
986 :
987 0 : return bEqual;
988 : }
989 :
990 268 : ScValidationDataList::iterator ScValidationDataList::begin()
991 : {
992 268 : return maData.begin();
993 : }
994 :
995 4 : ScValidationDataList::const_iterator ScValidationDataList::begin() const
996 : {
997 4 : return maData.begin();
998 : }
999 :
1000 722 : ScValidationDataList::iterator ScValidationDataList::end()
1001 : {
1002 722 : return maData.end();
1003 : }
1004 :
1005 26 : ScValidationDataList::const_iterator ScValidationDataList::end() const
1006 : {
1007 26 : return maData.end();
1008 : }
1009 :
1010 18 : void ScValidationDataList::clear()
1011 : {
1012 18 : maData.clear();
1013 246 : }
1014 :
1015 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|