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 67 : 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 67 : , mnListType( css::sheet::TableValidationVisibility::UNSORTED )
74 : {
75 67 : }
76 :
77 3 : 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 3 : , mnListType( css::sheet::TableValidationVisibility::UNSORTED )
87 : {
88 3 : }
89 :
90 3 : 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 3 : , aErrorMessage( r.aErrorMessage )
102 : {
103 : // Formulae copied by RefCount
104 3 : }
105 :
106 28 : 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 28 : , aErrorMessage( r.aErrorMessage )
118 : {
119 : // Formulae really copied
120 28 : }
121 :
122 145 : ScValidationData::~ScValidationData()
123 : {
124 145 : }
125 :
126 34 : bool ScValidationData::IsEmpty() const
127 : {
128 34 : OUString aEmpty;
129 68 : ScValidationData aDefault( SC_VALID_ANY, SC_COND_EQUAL, aEmpty, aEmpty, GetDocument(), ScAddress() );
130 68 : return EqualEntries( aDefault );
131 : }
132 :
133 115 : bool ScValidationData::EqualEntries( const ScValidationData& r ) const
134 : {
135 : // test same parameters (excluding Key)
136 :
137 155 : return ScConditionEntry::operator==(r) &&
138 80 : eDataMode == r.eDataMode &&
139 80 : bShowInput == r.bShowInput &&
140 80 : bShowError == r.bShowError &&
141 80 : eErrorStyle == r.eErrorStyle &&
142 80 : mnListType == r.mnListType &&
143 73 : aInputTitle == r.aInputTitle &&
144 61 : aInputMessage == r.aInputMessage &&
145 165 : aErrorTitle == r.aErrorTitle &&
146 137 : aErrorMessage == r.aErrorMessage;
147 : }
148 :
149 30 : void ScValidationData::ResetInput()
150 : {
151 30 : bShowInput = false;
152 30 : }
153 :
154 16 : void ScValidationData::ResetError()
155 : {
156 16 : bShowError = false;
157 16 : }
158 :
159 30 : void ScValidationData::SetInput( const OUString& rTitle, const OUString& rMsg )
160 : {
161 30 : bShowInput = true;
162 30 : aInputTitle = rTitle;
163 30 : aInputMessage = rMsg;
164 30 : }
165 :
166 35 : void ScValidationData::SetError( const OUString& rTitle, const OUString& rMsg,
167 : ScValidErrorStyle eStyle )
168 : {
169 35 : bShowError = true;
170 35 : eErrorStyle = eStyle;
171 35 : aErrorTitle = rTitle;
172 35 : aErrorMessage = rMsg;
173 35 : }
174 :
175 48 : bool ScValidationData::GetErrMsg( OUString& rTitle, OUString& rMsg,
176 : ScValidErrorStyle& rStyle ) const
177 : {
178 48 : rTitle = aErrorTitle;
179 48 : rMsg = aErrorMessage;
180 48 : rStyle = eErrorStyle;
181 48 : 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 || !ScDocument::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() == cppu::UnoType<bool>::get() &&
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 : //TODO: different error message, if found, but not bAllowed ??
255 :
256 0 : ScopedVclPtrInstance< 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 || !ScDocument::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 : //TODO: different error message, if found, but not bAllowed ??
361 :
362 0 : ScopedVclPtrInstance< 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 : //TODO: ErrorBox / WarningBox / InfoBox ?
393 : //TODO: (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 : ScopedVclPtrInstance< 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 : //TODO: 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 5 : class ScStringTokenIterator
558 : {
559 : public:
560 5 : inline explicit ScStringTokenIterator( ScTokenArray& rTokArr, bool bSkipEmpty = true ) :
561 5 : 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 5 : 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 5 : rtl_uString* ScStringTokenIterator::First()
579 : {
580 5 : mrTokArr.Reset();
581 5 : mbOk = true;
582 5 : return Next();
583 : }
584 :
585 5 : rtl_uString* ScStringTokenIterator::Next()
586 : {
587 5 : if( !mbOk )
588 0 : return NULL;
589 :
590 : // seek to next non-separator token
591 5 : const FormulaToken* pToken = mrTokArr.NextNoSpaces();
592 10 : while( pToken && (pToken->GetOpCode() == ocSep) )
593 0 : pToken = mrTokArr.NextNoSpaces();
594 :
595 5 : mbOk = !pToken || (pToken->GetType() == formula::svString);
596 :
597 5 : maCurString = svl::SharedString(); // start with invalid string.
598 5 : if (mbOk && pToken)
599 0 : maCurString = pToken->GetString();
600 :
601 : // string found but empty -> get next token; otherwise return it
602 5 : 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 5 : sal_uLong lclGetCellFormat( ScDocument& rDoc, const ScAddress& rPos )
607 : {
608 5 : const ScPatternAttr* pPattern = rDoc.GetPattern( rPos.Col(), rPos.Row(), rPos.Tab() );
609 5 : if( !pPattern )
610 0 : pPattern = rDoc.GetDefPattern();
611 5 : return pPattern->GetNumberFormat( rDoc.GetFormatTable() );
612 : }
613 :
614 : } // namespace
615 :
616 5 : bool ScValidationData::HasSelectionList() const
617 : {
618 5 : return (eDataMode == SC_VALID_LIST) && (mnListType != css::sheet::TableValidationVisibility::INVISIBLE);
619 : }
620 :
621 5 : bool ScValidationData::GetSelectionFromFormula(
622 : std::vector<ScTypedStrData>* pStrings, ScRefCellValue& rCell, const ScAddress& rPos,
623 : const ScTokenArray& rTokArr, int& rMatch) const
624 : {
625 5 : bool bOk = true;
626 :
627 : // pDoc is private in condition, use an accessor and a long winded name.
628 5 : ScDocument* pDocument = GetDocument();
629 5 : if( NULL == pDocument )
630 0 : return false;
631 :
632 : ScFormulaCell aValidationSrc(
633 5 : 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 5 : aValidationSrc.Interpret();
638 :
639 10 : ScMatrixRef xMatRef;
640 5 : const ScMatrix *pValues = aValidationSrc.GetMatrix();
641 5 : 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 5 : rMatch = -1;
674 :
675 5 : SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable();
676 :
677 5 : SCSIZE nCol, nRow, nCols, nRows, n = 0;
678 5 : pValues->GetDimensions( nCols, nRows );
679 :
680 5 : bool bRef = false;
681 5 : ScRange aRange;
682 :
683 5 : ScTokenArray* pArr = const_cast<ScTokenArray*>(&rTokArr);
684 5 : pArr->Reset();
685 5 : formula::FormulaToken* t = NULL;
686 5 : if (pArr->GetLen() == 1 && (t = pArr->GetNextReferenceOrName()) != NULL)
687 : {
688 5 : OpCode eOpCode = t->GetOpCode();
689 5 : if (eOpCode == ocDBArea || eOpCode == ocTableRef)
690 : {
691 0 : if (const ScDBData* pDBData = pDocument->GetDBCollection()->getNamedDBs().findByIndex(t->GetIndex()))
692 : {
693 0 : pDBData->GetArea(aRange);
694 0 : bRef = true;
695 0 : }
696 : }
697 5 : else if (eOpCode == ocName)
698 : {
699 0 : ScRangeData* pName = pDocument->GetRangeName()->findByIndex( t->GetIndex() );
700 0 : if (pName && pName->IsReference(aRange))
701 : {
702 0 : bRef = true;
703 : }
704 : }
705 5 : else if (t->GetType() != svIndex)
706 : {
707 5 : if (pArr->IsValidReference(aRange, rPos))
708 : {
709 5 : bRef = true;
710 : }
711 : }
712 : }
713 :
714 5 : bool bHaveEmpty = false;
715 5 : svl::SharedStringPool& rSPool = pDocument->GetSharedStringPool();
716 :
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 20 : for( nRow = 0; nRow < nRows ; nRow++ )
724 : {
725 30 : for( nCol = 0; nCol < nCols ; nCol++ )
726 : {
727 15 : ScTokenArray aCondTokArr;
728 15 : ScTypedStrData* pEntry = NULL;
729 30 : OUString aValStr;
730 30 : ScMatrixValue nMatVal = pValues->Get( nCol, nRow);
731 :
732 : // strings and empties
733 15 : if( ScMatrix::IsNonValueType( nMatVal.nType ) )
734 : {
735 0 : aValStr = nMatVal.GetString().getString();
736 :
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 0 : if (aValStr.isEmpty())
741 : {
742 0 : if (bHaveEmpty)
743 0 : continue;
744 0 : bHaveEmpty = true;
745 : }
746 :
747 0 : if( NULL != pStrings )
748 0 : pEntry = new ScTypedStrData( aValStr, 0.0, ScTypedStrData::Standard);
749 :
750 0 : if (!rCell.isEmpty() && rMatch < 0)
751 0 : aCondTokArr.AddString(rSPool.intern(aValStr));
752 : }
753 : else
754 : {
755 15 : sal_uInt16 nErr = nMatVal.GetError();
756 :
757 15 : if( 0 != nErr )
758 : {
759 0 : aValStr = ScGlobal::GetErrorString( nErr );
760 : }
761 : else
762 : {
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 15 : if ( bRef )
768 : {
769 15 : pDocument->GetInputString((SCCOL)(nCol+aRange.aStart.Col()),
770 30 : (SCROW)(nRow+aRange.aStart.Row()), aRange.aStart.Tab() , aValStr);
771 : }
772 : else
773 : {
774 0 : pFormatter->GetInputLineString( nMatVal.fVal, 0, aValStr );
775 : }
776 : }
777 :
778 15 : if (!rCell.isEmpty() && rMatch < 0)
779 : {
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 0 : aCondTokArr.AddDouble( nMatVal.fVal );
783 : }
784 15 : if( NULL != pStrings )
785 15 : pEntry = new ScTypedStrData( aValStr, nMatVal.fVal, ScTypedStrData::Value);
786 : }
787 :
788 15 : if (rMatch < 0 && !rCell.isEmpty() && IsEqualToTokenArray(rCell, rPos, aCondTokArr))
789 : {
790 0 : rMatch = n;
791 : // short circuit on the first match if not filling the list
792 0 : if( NULL == pStrings )
793 0 : return true;
794 : }
795 :
796 15 : if( NULL != pEntry )
797 : {
798 15 : pStrings->push_back(*pEntry);
799 15 : delete pEntry;
800 15 : n++;
801 : }
802 15 : }
803 : }
804 :
805 : // In case of no match needed and an error occurred, return that error
806 : // entry as valid instead of silently failing.
807 10 : return bOk || rCell.isEmpty();
808 : }
809 :
810 5 : bool ScValidationData::FillSelectionList(std::vector<ScTypedStrData>& rStrColl, const ScAddress& rPos) const
811 : {
812 5 : bool bOk = false;
813 :
814 5 : if( HasSelectionList() )
815 : {
816 5 : boost::scoped_ptr<ScTokenArray> pTokArr( CreateTokenArry(0) );
817 :
818 : // *** try if formula is a string list ***
819 :
820 5 : sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos );
821 10 : ScStringTokenIterator aIt( *pTokArr );
822 5 : for (rtl_uString* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next())
823 : {
824 : double fValue;
825 0 : OUString aStr(pString);
826 0 : bool bIsValue = GetDocument()->GetFormatTable()->IsNumberFormat(aStr, nFormat, fValue);
827 : rStrColl.push_back(
828 : ScTypedStrData(
829 0 : aStr, fValue, bIsValue ? ScTypedStrData::Value : ScTypedStrData::Standard));
830 0 : }
831 5 : bOk = aIt.Ok();
832 :
833 : // *** if not a string list, try if formula results in a cell range or
834 : // anything else we recognize as valid ***
835 :
836 5 : if (!bOk)
837 : {
838 : int nMatch;
839 5 : ScRefCellValue aEmptyCell;
840 5 : bOk = GetSelectionFromFormula(&rStrColl, aEmptyCell, rPos, *pTokArr, nMatch);
841 5 : }
842 : }
843 :
844 5 : return bOk;
845 : }
846 :
847 0 : bool ScValidationData::IsEqualToTokenArray( ScRefCellValue& rCell, const ScAddress& rPos, const ScTokenArray& rTokArr ) const
848 : {
849 : // create a condition entry that tests on equality and set the passed token array
850 0 : ScConditionEntry aCondEntry( SC_COND_EQUAL, &rTokArr, NULL, GetDocument(), rPos );
851 0 : return aCondEntry.IsCellValid(rCell, rPos);
852 : }
853 :
854 0 : bool ScValidationData::IsListValid( ScRefCellValue& rCell, const ScAddress& rPos ) const
855 : {
856 0 : bool bIsValid = false;
857 :
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.
867 : */
868 :
869 0 : boost::scoped_ptr< ScTokenArray > pTokArr( CreateTokenArry( 0 ) );
870 :
871 : // *** try if formula is a string list ***
872 :
873 0 : svl::SharedStringPool& rSPool = GetDocument()->GetSharedStringPool();
874 0 : sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos );
875 0 : ScStringTokenIterator aIt( *pTokArr );
876 0 : for (rtl_uString* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next())
877 : {
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 0 : if( !bIsValid )
881 : {
882 : // create a formula containing a single string or number
883 0 : ScTokenArray aCondTokArr;
884 : double fValue;
885 0 : OUString aStr(pString);
886 0 : if (GetDocument()->GetFormatTable()->IsNumberFormat(aStr, nFormat, fValue))
887 0 : aCondTokArr.AddDouble( fValue );
888 : else
889 0 : aCondTokArr.AddString(rSPool.intern(aStr));
890 :
891 0 : bIsValid = IsEqualToTokenArray(rCell, rPos, aCondTokArr);
892 : }
893 : }
894 :
895 0 : if( !aIt.Ok() )
896 0 : bIsValid = false;
897 :
898 : // *** if not a string list, try if formula results in a cell range or
899 : // anything else we recognize as valid ***
900 :
901 0 : if (!bIsValid)
902 : {
903 : int nMatch;
904 0 : bIsValid = GetSelectionFromFormula(NULL, rCell, rPos, *pTokArr, nMatch);
905 0 : bIsValid = bIsValid && nMatch >= 0;
906 : }
907 :
908 0 : return bIsValid;
909 : }
910 :
911 0 : ScValidationDataList::ScValidationDataList(const ScValidationDataList& rList)
912 : {
913 : // for Ref-Undo - real copy with new tokens!
914 :
915 0 : for (const_iterator it = rList.begin(); it != rList.end(); ++it)
916 : {
917 0 : InsertNew( (*it)->Clone() );
918 : }
919 :
920 : //TODO: faster insert for sorted entries from rList ???
921 0 : }
922 :
923 2 : ScValidationDataList::ScValidationDataList(ScDocument* pNewDoc,
924 2 : const ScValidationDataList& rList)
925 : {
926 : // for new documents - real copy with new tokens!
927 :
928 13 : for (const_iterator it = rList.begin(); it != rList.end(); ++it)
929 : {
930 11 : InsertNew( (*it)->Clone(pNewDoc) );
931 : }
932 :
933 : //TODO: faster insert for sorted entries from rList ???
934 2 : }
935 :
936 83 : ScValidationData* ScValidationDataList::GetData( sal_uInt32 nKey )
937 : {
938 : //TODO: binary search
939 :
940 202 : for( iterator it = begin(); it != end(); ++it )
941 202 : if( (*it)->GetKey() == nKey )
942 83 : return *it;
943 :
944 : OSL_FAIL("ScValidationDataList: Entry not found");
945 0 : return NULL;
946 : }
947 :
948 1 : void ScValidationDataList::CompileXML()
949 : {
950 3 : for( iterator it = begin(); it != end(); ++it )
951 2 : (*it)->CompileXML();
952 1 : }
953 :
954 7 : void ScValidationDataList::UpdateReference( sc::RefUpdateContext& rCxt )
955 : {
956 23 : for( iterator it = begin(); it != end(); ++it )
957 16 : (*it)->UpdateReference(rCxt);
958 7 : }
959 :
960 0 : void ScValidationDataList::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
961 : {
962 0 : for (iterator it = begin(); it != end(); ++it)
963 0 : (*it)->UpdateInsertTab(rCxt);
964 0 : }
965 :
966 0 : void ScValidationDataList::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
967 : {
968 0 : for (iterator it = begin(); it != end(); ++it)
969 0 : (*it)->UpdateDeleteTab(rCxt);
970 0 : }
971 :
972 0 : void ScValidationDataList::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt )
973 : {
974 0 : for (iterator it = begin(); it != end(); ++it)
975 0 : (*it)->UpdateMoveTab(rCxt);
976 0 : }
977 :
978 0 : bool ScValidationDataList::operator==( const ScValidationDataList& r ) const
979 : {
980 : // for Ref-Undo - internal variables can not be compared
981 :
982 0 : size_t nCount = maData.size();
983 0 : bool bEqual = ( nCount == r.maData.size() );
984 0 : for( const_iterator it1 = begin(), it2 = r.begin(); it1 != end() && bEqual; ++it1, ++it2 ) // Entries are sorted
985 0 : if ( !(*it1)->EqualEntries(**it2) ) // different entries ?
986 0 : bEqual = false;
987 :
988 0 : return bEqual;
989 : }
990 :
991 134 : ScValidationDataList::iterator ScValidationDataList::begin()
992 : {
993 134 : return maData.begin();
994 : }
995 :
996 2 : ScValidationDataList::const_iterator ScValidationDataList::begin() const
997 : {
998 2 : return maData.begin();
999 : }
1000 :
1001 361 : ScValidationDataList::iterator ScValidationDataList::end()
1002 : {
1003 361 : return maData.end();
1004 : }
1005 :
1006 13 : ScValidationDataList::const_iterator ScValidationDataList::end() const
1007 : {
1008 13 : return maData.end();
1009 : }
1010 :
1011 9 : void ScValidationDataList::clear()
1012 : {
1013 9 : maData.clear();
1014 165 : }
1015 :
1016 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|