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 "validat.hxx"
21 :
22 : #include <sfx2/app.hxx>
23 : #include <sfx2/docfile.hxx>
24 : #include <sfx2/objsh.hxx>
25 : #include <basic/sbmeth.hxx>
26 : #include <basic/sbmod.hxx>
27 : #include <basic/sbstar.hxx>
28 : #include <basic/basmgr.hxx>
29 :
30 : #include <basic/sbx.hxx>
31 : #include <svl/zforlist.hxx>
32 : #include <vcl/msgbox.hxx>
33 : #include <rtl/math.hxx>
34 :
35 : #include "scitems.hxx"
36 : #include "document.hxx"
37 : #include "formulacell.hxx"
38 : #include "patattr.hxx"
39 : #include "rechead.hxx"
40 : #include "globstr.hrc"
41 : #include "rangenam.hxx"
42 : #include "dbdata.hxx"
43 : #include "typedstrdata.hxx"
44 : #include "dociter.hxx"
45 : #include "editutil.hxx"
46 : #include "tokenarray.hxx"
47 : #include "scmatrix.hxx"
48 :
49 : #include <math.h>
50 : #include <memory>
51 :
52 : using namespace formula;
53 :
54 : //
55 : // Eintrag fuer Gueltigkeit (es gibt nur eine Bedingung)
56 : //
57 :
58 65 : ScValidationData::ScValidationData( ScValidationMode eMode, ScConditionMode eOper,
59 : const String& rExpr1, const String& rExpr2,
60 : ScDocument* pDocument, const ScAddress& rPos,
61 : const String& rExprNmsp1, const String& rExprNmsp2,
62 : FormulaGrammar::Grammar eGrammar1, FormulaGrammar::Grammar eGrammar2 ) :
63 : ScConditionEntry( eOper, rExpr1, rExpr2, pDocument, rPos, rExprNmsp1, rExprNmsp2, eGrammar1, eGrammar2 ),
64 : nKey( 0 ),
65 : eDataMode( eMode ),
66 : eErrorStyle( SC_VALERR_STOP ),
67 65 : mnListType( ValidListType::UNSORTED )
68 : {
69 65 : bShowInput = bShowError = false;
70 65 : }
71 :
72 3 : ScValidationData::ScValidationData( ScValidationMode eMode, ScConditionMode eOper,
73 : const ScTokenArray* pArr1, const ScTokenArray* pArr2,
74 : ScDocument* pDocument, const ScAddress& rPos ) :
75 : ScConditionEntry( eOper, pArr1, pArr2, pDocument, rPos ),
76 : nKey( 0 ),
77 : eDataMode( eMode ),
78 : eErrorStyle( SC_VALERR_STOP ),
79 3 : mnListType( ValidListType::UNSORTED )
80 : {
81 3 : bShowInput = bShowError = false;
82 3 : }
83 :
84 3 : ScValidationData::ScValidationData( const ScValidationData& r ) :
85 : ScConditionEntry( r ),
86 : nKey( r.nKey ),
87 : eDataMode( r.eDataMode ),
88 : bShowInput( r.bShowInput ),
89 : bShowError( r.bShowError ),
90 : eErrorStyle( r.eErrorStyle ),
91 : mnListType( r.mnListType ),
92 : aInputTitle( r.aInputTitle ),
93 : aInputMessage( r.aInputMessage ),
94 : aErrorTitle( r.aErrorTitle ),
95 3 : aErrorMessage( r.aErrorMessage )
96 : {
97 : // Formeln per RefCount kopiert
98 3 : }
99 :
100 26 : ScValidationData::ScValidationData( ScDocument* pDocument, const ScValidationData& r ) :
101 : ScConditionEntry( pDocument, r ),
102 : nKey( r.nKey ),
103 : eDataMode( r.eDataMode ),
104 : bShowInput( r.bShowInput ),
105 : bShowError( r.bShowError ),
106 : eErrorStyle( r.eErrorStyle ),
107 : mnListType( r.mnListType ),
108 : aInputTitle( r.aInputTitle ),
109 : aInputMessage( r.aInputMessage ),
110 : aErrorTitle( r.aErrorTitle ),
111 26 : aErrorMessage( r.aErrorMessage )
112 : {
113 : // Formeln wirklich kopiert
114 26 : }
115 :
116 139 : ScValidationData::~ScValidationData()
117 : {
118 139 : }
119 :
120 33 : sal_Bool ScValidationData::IsEmpty() const
121 : {
122 33 : String aEmpty;
123 66 : ScValidationData aDefault( SC_VALID_ANY, SC_COND_EQUAL, aEmpty, aEmpty, GetDocument(), ScAddress() );
124 66 : return EqualEntries( aDefault );
125 : }
126 :
127 114 : sal_Bool ScValidationData::EqualEntries( const ScValidationData& r ) const
128 : {
129 : // gleiche Parameter eingestellt (ohne Key)
130 :
131 154 : return ScConditionEntry::operator==(r) &&
132 80 : eDataMode == r.eDataMode &&
133 80 : bShowInput == r.bShowInput &&
134 80 : bShowError == r.bShowError &&
135 80 : eErrorStyle == r.eErrorStyle &&
136 80 : mnListType == r.mnListType &&
137 73 : aInputTitle == r.aInputTitle &&
138 61 : aInputMessage == r.aInputMessage &&
139 164 : aErrorTitle == r.aErrorTitle &&
140 136 : aErrorMessage == r.aErrorMessage;
141 : }
142 :
143 30 : void ScValidationData::ResetInput()
144 : {
145 30 : bShowInput = false;
146 30 : }
147 :
148 16 : void ScValidationData::ResetError()
149 : {
150 16 : bShowError = false;
151 16 : }
152 :
153 30 : void ScValidationData::SetInput( const String& rTitle, const String& rMsg )
154 : {
155 30 : bShowInput = sal_True;
156 30 : aInputTitle = rTitle;
157 30 : aInputMessage = rMsg;
158 30 : }
159 :
160 35 : void ScValidationData::SetError( const String& rTitle, const String& rMsg,
161 : ScValidErrorStyle eStyle )
162 : {
163 35 : bShowError = sal_True;
164 35 : eErrorStyle = eStyle;
165 35 : aErrorTitle = rTitle;
166 35 : aErrorMessage = rMsg;
167 35 : }
168 :
169 48 : sal_Bool ScValidationData::GetErrMsg( String& rTitle, String& rMsg,
170 : ScValidErrorStyle& rStyle ) const
171 : {
172 48 : rTitle = aErrorTitle;
173 48 : rMsg = aErrorMessage;
174 48 : rStyle = eErrorStyle;
175 48 : return bShowError;
176 : }
177 :
178 0 : sal_Bool ScValidationData::DoScript( const ScAddress& rPos, const String& rInput,
179 : ScFormulaCell* pCell, Window* pParent ) const
180 : {
181 0 : ScDocument* pDocument = GetDocument();
182 0 : SfxObjectShell* pDocSh = pDocument->GetDocumentShell();
183 0 : if ( !pDocSh || !pDocument->CheckMacroWarn() )
184 0 : return false;
185 :
186 0 : sal_Bool bScriptReturnedFalse = false; // Standard: kein Abbruch
187 :
188 : // Set up parameters
189 0 : ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > aParams(2);
190 :
191 : // 1) eingegebener / berechneter Wert
192 0 : String aValStr = rInput;
193 : double nValue;
194 0 : sal_Bool bIsValue = false;
195 0 : if ( pCell ) // wenn Zelle gesetzt, aus Interpret gerufen
196 : {
197 0 : bIsValue = pCell->IsValue();
198 0 : if ( bIsValue )
199 0 : nValue = pCell->GetValue();
200 : else
201 0 : aValStr = pCell->GetString();
202 : }
203 0 : if ( bIsValue )
204 0 : aParams[0] = ::com::sun::star::uno::makeAny( nValue );
205 : else
206 0 : aParams[0] = ::com::sun::star::uno::makeAny( OUString( aValStr ) );
207 :
208 : // 2) Position der Zelle
209 0 : String aPosStr;
210 0 : rPos.Format( aPosStr, SCA_VALID | SCA_TAB_3D, pDocument, pDocument->GetAddressConvention() );
211 0 : aParams[1] = ::com::sun::star::uno::makeAny( OUString( aPosStr ) );
212 :
213 : // use link-update flag to prevent closing the document
214 : // while the macro is running
215 0 : sal_Bool bWasInLinkUpdate = pDocument->IsInLinkUpdate();
216 0 : if ( !bWasInLinkUpdate )
217 0 : pDocument->SetInLinkUpdate( sal_True );
218 :
219 0 : if ( pCell )
220 0 : pDocument->LockTable( rPos.Tab() );
221 :
222 0 : ::com::sun::star::uno::Any aRet;
223 0 : ::com::sun::star::uno::Sequence< sal_Int16 > aOutArgsIndex;
224 0 : ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > aOutArgs;
225 :
226 : ErrCode eRet = pDocSh->CallXScript(
227 0 : aErrorTitle, aParams, aRet, aOutArgsIndex, aOutArgs );
228 :
229 0 : if ( pCell )
230 0 : pDocument->UnlockTable( rPos.Tab() );
231 :
232 0 : if ( !bWasInLinkUpdate )
233 0 : pDocument->SetInLinkUpdate( false );
234 :
235 : // Check the return value from the script
236 : // The contents of the cell get reset if the script returns false
237 0 : sal_Bool bTmp = false;
238 0 : if ( eRet == ERRCODE_NONE &&
239 0 : aRet.getValueType() == getCppuBooleanType() &&
240 0 : sal_True == ( aRet >>= bTmp ) &&
241 0 : bTmp == false )
242 : {
243 0 : bScriptReturnedFalse = sal_True;
244 : }
245 :
246 0 : if ( eRet == ERRCODE_BASIC_METHOD_NOT_FOUND && !pCell )
247 : // Makro nicht gefunden (nur bei Eingabe)
248 : {
249 : //! andere Fehlermeldung, wenn gefunden, aber nicht bAllowed ??
250 :
251 : ErrorBox aBox( pParent, WinBits(WB_OK),
252 0 : ScGlobal::GetRscString( STR_VALID_MACRONOTFOUND ) );
253 0 : aBox.Execute();
254 : }
255 :
256 0 : return bScriptReturnedFalse;
257 : }
258 :
259 : // sal_True -> Abbruch
260 :
261 0 : sal_Bool ScValidationData::DoMacro( const ScAddress& rPos, const String& rInput,
262 : ScFormulaCell* pCell, Window* pParent ) const
263 : {
264 0 : if ( SfxApplication::IsXScriptURL( aErrorTitle ) )
265 : {
266 0 : return DoScript( rPos, rInput, pCell, pParent );
267 : }
268 :
269 0 : ScDocument* pDocument = GetDocument();
270 0 : SfxObjectShell* pDocSh = pDocument->GetDocumentShell();
271 0 : if ( !pDocSh || !pDocument->CheckMacroWarn() )
272 0 : return false;
273 :
274 0 : sal_Bool bDone = false;
275 0 : sal_Bool bRet = false; // Standard: kein Abbruch
276 :
277 : // Wenn das Dok waehrend eines Basic-Calls geladen wurde,
278 : // ist das Sbx-Objekt evtl. nicht angelegt (?)
279 : // pDocSh->GetSbxObject();
280 :
281 : #ifndef DISABLE_SCRIPTING
282 : // keine Sicherheitsabfrage mehr vorneweg (nur CheckMacroWarn), das passiert im CallBasic
283 :
284 : // Funktion ueber den einfachen Namen suchen,
285 : // dann aBasicStr, aMacroStr fuer SfxObjectShell::CallBasic zusammenbauen
286 :
287 0 : StarBASIC* pRoot = pDocSh->GetBasic();
288 0 : SbxVariable* pVar = pRoot->Find( aErrorTitle, SbxCLASS_METHOD );
289 0 : if ( pVar && pVar->ISA(SbMethod) )
290 : {
291 0 : SbMethod* pMethod = (SbMethod*)pVar;
292 0 : SbModule* pModule = pMethod->GetModule();
293 0 : SbxObject* pObject = pModule->GetParent();
294 0 : String aMacroStr = pObject->GetName();
295 0 : aMacroStr += '.';
296 0 : aMacroStr += pModule->GetName();
297 0 : aMacroStr += '.';
298 0 : aMacroStr += pMethod->GetName();
299 0 : String aBasicStr;
300 :
301 : // the distinction between document- and app-basic has to be done
302 : // by checking the parent (as in ScInterpreter::ScMacro), not by looping
303 : // over all open documents, because this may be called from within loading,
304 : // when SfxObjectShell::GetFirst/GetNext won't find the document.
305 :
306 0 : if ( pObject->GetParent() )
307 0 : aBasicStr = pObject->GetParent()->GetName(); // Dokumentenbasic
308 : else
309 0 : aBasicStr = SFX_APP()->GetName(); // Applikationsbasic
310 :
311 : // Parameter fuer Makro
312 0 : SbxArrayRef refPar = new SbxArray;
313 :
314 : // 1) eingegebener / berechneter Wert
315 0 : String aValStr = rInput;
316 0 : double nValue = 0.0;
317 0 : sal_Bool bIsValue = false;
318 0 : if ( pCell ) // wenn Zelle gesetzt, aus Interpret gerufen
319 : {
320 0 : bIsValue = pCell->IsValue();
321 0 : if ( bIsValue )
322 0 : nValue = pCell->GetValue();
323 : else
324 0 : aValStr = pCell->GetString();
325 : }
326 0 : if ( bIsValue )
327 0 : refPar->Get(1)->PutDouble( nValue );
328 : else
329 0 : refPar->Get(1)->PutString( aValStr );
330 :
331 : // 2) Position der Zelle
332 0 : String aPosStr;
333 0 : rPos.Format( aPosStr, 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 : sal_Bool bWasInLinkUpdate = pDocument->IsInLinkUpdate();
339 0 : if ( !bWasInLinkUpdate )
340 0 : pDocument->SetInLinkUpdate( sal_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, 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 : // Eingabe abbrechen, wenn Basic-Makro sal_False zurueckgibt
353 0 : if ( eRet == ERRCODE_NONE && refRes->GetType() == SbxBOOL && refRes->GetBool() == false )
354 0 : bRet = sal_True;
355 0 : bDone = sal_True;
356 : }
357 : #endif
358 0 : if ( !bDone && !pCell ) // Makro nicht gefunden (nur bei Eingabe)
359 : {
360 : //! andere Fehlermeldung, wenn gefunden, aber nicht bAllowed ??
361 :
362 : ErrorBox aBox( pParent, WinBits(WB_OK),
363 0 : ScGlobal::GetRscString( STR_VALID_MACRONOTFOUND ) );
364 0 : aBox.Execute();
365 : }
366 :
367 0 : return bRet;
368 : }
369 :
370 0 : void ScValidationData::DoCalcError( ScFormulaCell* pCell ) const
371 : {
372 0 : if ( eErrorStyle == SC_VALERR_MACRO )
373 0 : DoMacro( pCell->aPos, EMPTY_STRING, pCell, NULL );
374 0 : }
375 :
376 : // sal_True -> Abbruch
377 :
378 0 : sal_Bool ScValidationData::DoError( Window* pParent, const String& rInput,
379 : const ScAddress& rPos ) const
380 : {
381 0 : if ( eErrorStyle == SC_VALERR_MACRO )
382 0 : return DoMacro( rPos, rInput, NULL, pParent );
383 :
384 : // Fehlermeldung ausgeben
385 :
386 0 : String aTitle = aErrorTitle;
387 0 : if (!aTitle.Len())
388 0 : aTitle = ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_0 ); // application title
389 0 : String aMessage = aErrorMessage;
390 0 : if (!aMessage.Len())
391 0 : aMessage = ScGlobal::GetRscString( STR_VALID_DEFERROR );
392 :
393 : //! ErrorBox / WarningBox / InfoBox ?
394 : //! (bei InfoBox immer nur OK-Button)
395 :
396 0 : WinBits nStyle = 0;
397 0 : switch (eErrorStyle)
398 : {
399 : case SC_VALERR_STOP:
400 0 : nStyle = WB_OK | WB_DEF_OK;
401 0 : break;
402 : case SC_VALERR_WARNING:
403 0 : nStyle = WB_OK_CANCEL | WB_DEF_CANCEL;
404 0 : break;
405 : case SC_VALERR_INFO:
406 0 : nStyle = WB_OK_CANCEL | WB_DEF_OK;
407 0 : break;
408 : default:
409 : {
410 : // added to avoid warnings
411 : }
412 : }
413 :
414 0 : MessBox aBox( pParent, WinBits(nStyle), aTitle, aMessage );
415 0 : sal_uInt16 nRet = aBox.Execute();
416 :
417 0 : return ( eErrorStyle == SC_VALERR_STOP || nRet == RET_CANCEL );
418 : }
419 :
420 :
421 0 : bool ScValidationData::IsDataValid(
422 : const OUString& rTest, const ScPatternAttr& rPattern, const ScAddress& rPos ) const
423 : {
424 0 : if ( eDataMode == SC_VALID_ANY ) // check if any cell content is allowed
425 0 : return sal_True;
426 :
427 0 : if (rTest.isEmpty()) // check whether empty cells are allowed
428 0 : return IsIgnoreBlank();
429 :
430 0 : if (rTest[0] == '=') // formulas do not pass the validity test
431 0 : return false;
432 :
433 :
434 0 : SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable();
435 :
436 : // get the value if any
437 0 : sal_uInt32 nFormat = rPattern.GetNumberFormat( pFormatter );
438 : double nVal;
439 0 : sal_Bool bIsVal = pFormatter->IsNumberFormat( rTest, nFormat, nVal );
440 :
441 : sal_Bool bRet;
442 0 : if (SC_VALID_TEXTLEN == eDataMode)
443 : {
444 : double nLenVal;
445 0 : if (!bIsVal)
446 0 : nLenVal = static_cast<double>(rTest.getLength());
447 : else
448 : {
449 : // For numeric values use the resulting input line string to
450 : // determine length, otherwise a once accepted value maybe could
451 : // not be edited again, for example abbreviated dates or leading
452 : // zeros or trailing zeros after decimal separator change length.
453 0 : OUString aStr;
454 0 : pFormatter->GetInputLineString( nVal, nFormat, aStr);
455 0 : nLenVal = static_cast<double>( aStr.getLength() );
456 : }
457 0 : ScRefCellValue aTmpCell(nLenVal);
458 0 : bRet = IsCellValid(aTmpCell, rPos);
459 : }
460 : else
461 : {
462 0 : if (bIsVal)
463 : {
464 0 : ScRefCellValue aTmpCell(nVal);
465 0 : bRet = IsDataValid(aTmpCell, rPos);
466 : }
467 : else
468 : {
469 0 : ScRefCellValue aTmpCell(&rTest);
470 0 : bRet = IsDataValid(aTmpCell, rPos);
471 : }
472 : }
473 :
474 0 : return bRet;
475 : }
476 :
477 0 : bool ScValidationData::IsDataValid( ScRefCellValue& rCell, const ScAddress& rPos ) const
478 : {
479 0 : if( eDataMode == SC_VALID_LIST )
480 0 : return IsListValid(rCell, rPos);
481 :
482 0 : double nVal = 0.0;
483 0 : OUString aString;
484 0 : bool bIsVal = true;
485 :
486 0 : switch (rCell.meType)
487 : {
488 : case CELLTYPE_VALUE:
489 0 : nVal = rCell.mfValue;
490 0 : break;
491 : case CELLTYPE_STRING:
492 0 : aString = *rCell.mpString;
493 0 : bIsVal = false;
494 0 : break;
495 : case CELLTYPE_EDIT:
496 0 : if (rCell.mpEditText)
497 0 : aString = ScEditUtil::GetString(*rCell.mpEditText);
498 0 : bIsVal = false;
499 0 : break;
500 : case CELLTYPE_FORMULA:
501 : {
502 0 : ScFormulaCell* pFCell = rCell.mpFormula;
503 0 : bIsVal = pFCell->IsValue();
504 0 : if ( bIsVal )
505 0 : nVal = pFCell->GetValue();
506 : else
507 0 : aString = pFCell->GetString();
508 : }
509 0 : break;
510 : default: // Notizen, Broadcaster
511 0 : return IsIgnoreBlank(); // wie eingestellt
512 : }
513 :
514 0 : bool bOk = true;
515 0 : switch (eDataMode)
516 : {
517 : // SC_VALID_ANY schon oben
518 :
519 : case SC_VALID_WHOLE:
520 : case SC_VALID_DECIMAL:
521 : case SC_VALID_DATE: // Date/Time ist nur Formatierung
522 : case SC_VALID_TIME:
523 0 : bOk = bIsVal;
524 0 : if ( bOk && eDataMode == SC_VALID_WHOLE )
525 0 : bOk = ::rtl::math::approxEqual( nVal, floor(nVal+0.5) ); // ganze Zahlen
526 0 : if ( bOk )
527 0 : bOk = IsCellValid(rCell, rPos);
528 0 : break;
529 :
530 : case SC_VALID_CUSTOM:
531 : // fuer Custom muss eOp == SC_COND_DIRECT sein
532 : //! der Wert muss im Dokument stehen !!!!!!!!!!!!!!!!!!!!
533 0 : bOk = IsCellValid(rCell, rPos);
534 0 : break;
535 :
536 : case SC_VALID_TEXTLEN:
537 0 : bOk = !bIsVal; // nur Text
538 0 : if ( bOk )
539 : {
540 0 : double nLenVal = (double) aString.getLength();
541 0 : ScRefCellValue aTmpCell(nLenVal);
542 0 : bOk = IsCellValid(aTmpCell, rPos);
543 : }
544 0 : break;
545 :
546 : default:
547 : OSL_FAIL("not yet done");
548 0 : break;
549 : }
550 :
551 0 : return bOk;
552 : }
553 :
554 : namespace {
555 :
556 : /** Token array helper. Iterates over all string tokens.
557 : @descr The token array must contain separated string tokens only.
558 : @param bSkipEmpty true = Ignores string tokens with empty strings. */
559 : class ScStringTokenIterator
560 : {
561 : public:
562 0 : inline explicit ScStringTokenIterator( ScTokenArray& rTokArr, bool bSkipEmpty = true ) :
563 0 : mrTokArr( rTokArr ), mbSkipEmpty( bSkipEmpty ), mbOk( true ) {}
564 :
565 : /** Returns the string of the first string token or NULL on error or empty token array. */
566 : const String* First();
567 : /** Returns the string of the next string token or NULL on error or end of token array. */
568 : const String* Next();
569 :
570 : /** Returns false, if a wrong token has been found. Does NOT return false on end of token array. */
571 0 : inline bool Ok() const { return mbOk; }
572 :
573 : private:
574 : ScTokenArray& mrTokArr; /// The token array for iteration.
575 : bool mbSkipEmpty; /// Ignore empty strings.
576 : bool mbOk; /// true = correct token or end of token array.
577 : };
578 :
579 0 : const String* ScStringTokenIterator::First()
580 : {
581 0 : mrTokArr.Reset();
582 0 : mbOk = true;
583 0 : return Next();
584 : }
585 :
586 0 : const String* ScStringTokenIterator::Next()
587 : {
588 0 : if( !mbOk )
589 0 : return NULL;
590 :
591 : // seek to next non-separator token
592 0 : const FormulaToken* pToken = mrTokArr.NextNoSpaces();
593 0 : while( pToken && (pToken->GetOpCode() == ocSep) )
594 0 : pToken = mrTokArr.NextNoSpaces();
595 :
596 0 : mbOk = !pToken || (pToken->GetType() == formula::svString);
597 0 : const String* pString = (mbOk && pToken) ? &pToken->GetString() : NULL;
598 : // string found but empty -> get next token; otherwise return it
599 0 : return (mbSkipEmpty && pString && !pString->Len()) ? Next() : pString;
600 : }
601 :
602 : // ----------------------------------------------------------------------------
603 :
604 : /** Returns the number format of the passed cell, or the standard format. */
605 0 : sal_uLong lclGetCellFormat( ScDocument& rDoc, const ScAddress& rPos )
606 : {
607 0 : const ScPatternAttr* pPattern = rDoc.GetPattern( rPos.Col(), rPos.Row(), rPos.Tab() );
608 0 : if( !pPattern )
609 0 : pPattern = rDoc.GetDefPattern();
610 0 : return pPattern->GetNumberFormat( rDoc.GetFormatTable() );
611 : }
612 :
613 : } // namespace
614 :
615 : // ----------------------------------------------------------------------------
616 :
617 0 : bool ScValidationData::HasSelectionList() const
618 : {
619 0 : return (eDataMode == SC_VALID_LIST) && (mnListType != ValidListType::INVISIBLE);
620 : }
621 :
622 0 : bool ScValidationData::GetSelectionFromFormula(
623 : std::vector<ScTypedStrData>* pStrings, ScRefCellValue& rCell, const ScAddress& rPos,
624 : const ScTokenArray& rTokArr, int& rMatch) const
625 : {
626 0 : bool bOk = true;
627 :
628 : // pDoc is private in condition, use an accessor and a long winded name.
629 0 : ScDocument* pDocument = GetDocument();
630 0 : if( NULL == pDocument )
631 0 : return false;
632 :
633 : ScFormulaCell aValidationSrc( pDocument, rPos, &rTokArr,
634 0 : formula::FormulaGrammar::GRAM_DEFAULT, MM_FORMULA);
635 :
636 : // Make sure the formula gets interpreted and a result is delivered,
637 : // regardless of the AutoCalc setting.
638 0 : aValidationSrc.Interpret();
639 :
640 0 : ScMatrixRef xMatRef;
641 0 : const ScMatrix *pValues = aValidationSrc.GetMatrix();
642 0 : if (!pValues)
643 : {
644 : // The somewhat nasty case of either an error occurred, or the
645 : // dereferenced value of a single cell reference or an immediate result
646 : // is stored as a single value.
647 :
648 : // Use an interim matrix to create the TypedStrData below.
649 0 : xMatRef = new ScMatrix(1, 1, 0.0);
650 :
651 0 : sal_uInt16 nErrCode = aValidationSrc.GetErrCode();
652 0 : if (nErrCode)
653 : {
654 : /* TODO : to use later in an alert box?
655 : * String rStrResult = "...";
656 : * rStrResult += ScGlobal::GetLongErrorString(nErrCode);
657 : */
658 :
659 0 : xMatRef->PutError( nErrCode, 0, 0);
660 0 : bOk = false;
661 : }
662 0 : else if (aValidationSrc.IsValue())
663 0 : xMatRef->PutDouble( aValidationSrc.GetValue(), 0);
664 : else
665 : {
666 0 : String aStr = aValidationSrc.GetString();
667 0 : xMatRef->PutString( aStr, 0);
668 : }
669 :
670 0 : pValues = xMatRef.get();
671 : }
672 :
673 : // which index matched. We will want it eventually to pre-select that item.
674 0 : rMatch = -1;
675 :
676 0 : SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable();
677 :
678 0 : SCSIZE nCol, nRow, nCols, nRows, n = 0;
679 0 : pValues->GetDimensions( nCols, nRows );
680 :
681 0 : bool bRef = false;
682 0 : ScRange aRange;
683 :
684 0 : ScTokenArray* pArr = (ScTokenArray*) &rTokArr;
685 0 : pArr->Reset();
686 0 : ScToken* t = NULL;
687 0 : if (pArr->GetLen() == 1 && (t = static_cast<ScToken*>(pArr->GetNextReferenceOrName())) != NULL)
688 : {
689 0 : if (t->GetOpCode() == ocDBArea)
690 : {
691 0 : if (const ScDBData* pDBData = pDocument->GetDBCollection()->getNamedDBs().findByIndex(t->GetIndex()))
692 : {
693 0 : pDBData->GetArea(aRange);
694 0 : bRef = true;
695 : }
696 : }
697 0 : else if (t->GetOpCode() == 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 0 : else if (t->GetType() != svIndex)
706 : {
707 0 : t->CalcAbsIfRel(rPos);
708 0 : if (pArr->IsValidReference(aRange))
709 : {
710 0 : bRef = true;
711 : }
712 : }
713 : }
714 :
715 : /* XL artificially limits things to a single col or row in the UI but does
716 : * not list the constraint in MOOXml. If a defined name or INDIRECT
717 : * resulting in 1D is entered in the UI and the definition later modified
718 : * to 2D, it is evaluated fine and also stored and loaded. Lets get ahead
719 : * of the curve and support 2d. In XL, values are listed row-wise, do the
720 : * same. */
721 0 : for( nRow = 0; nRow < nRows ; nRow++ )
722 : {
723 0 : for( nCol = 0; nCol < nCols ; nCol++ )
724 : {
725 0 : ScTokenArray aCondTokArr;
726 0 : ScTypedStrData* pEntry = NULL;
727 0 : String aValStr;
728 0 : ScMatrixValue nMatVal = pValues->Get( nCol, nRow);
729 :
730 : // strings and empties
731 0 : if( ScMatrix::IsNonValueType( nMatVal.nType ) )
732 : {
733 0 : aValStr = nMatVal.GetString();
734 :
735 0 : if( NULL != pStrings )
736 0 : pEntry = new ScTypedStrData( aValStr, 0.0, ScTypedStrData::Standard);
737 :
738 0 : if (!rCell.isEmpty() && rMatch < 0)
739 0 : aCondTokArr.AddString( aValStr );
740 : }
741 : else
742 : {
743 0 : sal_uInt16 nErr = nMatVal.GetError();
744 :
745 0 : if( 0 != nErr )
746 : {
747 0 : aValStr = ScGlobal::GetErrorString( nErr );
748 : }
749 : else
750 : {
751 : // FIXME FIXME FIXME
752 : // Feature regression. Date formats are lost passing through the matrix
753 : //pFormatter->GetInputLineString( pMatVal->fVal, 0, aValStr );
754 : //For external reference and a formula that results in an area or array, date formats are still lost.
755 0 : if ( bRef )
756 : {
757 0 : pDocument->GetInputString((SCCOL)(nCol+aRange.aStart.Col()),
758 0 : (SCROW)(nRow+aRange.aStart.Row()), aRange.aStart.Tab() , aValStr);
759 : }
760 : else
761 : {
762 0 : OUString sTmp(aValStr);
763 0 : pFormatter->GetInputLineString( nMatVal.fVal, 0, sTmp );
764 0 : aValStr = sTmp;
765 : }
766 : }
767 :
768 0 : if (!rCell.isEmpty() && rMatch < 0)
769 : {
770 : // I am not sure errors will work here, but a user can no
771 : // manually enter an error yet so the point is somewhat moot.
772 0 : aCondTokArr.AddDouble( nMatVal.fVal );
773 : }
774 0 : if( NULL != pStrings )
775 0 : pEntry = new ScTypedStrData( aValStr, nMatVal.fVal, ScTypedStrData::Value);
776 : }
777 :
778 0 : if (rMatch < 0 && !rCell.isEmpty() && IsEqualToTokenArray(rCell, rPos, aCondTokArr))
779 : {
780 0 : rMatch = n;
781 : // short circuit on the first match if not filling the list
782 0 : if( NULL == pStrings )
783 0 : return true;
784 : }
785 :
786 0 : if( NULL != pEntry )
787 : {
788 0 : pStrings->push_back(*pEntry);
789 0 : delete pEntry;
790 0 : n++;
791 : }
792 0 : }
793 : }
794 :
795 : // In case of no match needed and an error occurred, return that error
796 : // entry as valid instead of silently failing.
797 0 : return bOk || rCell.isEmpty();
798 : }
799 :
800 0 : bool ScValidationData::FillSelectionList(std::vector<ScTypedStrData>& rStrColl, const ScAddress& rPos) const
801 : {
802 0 : bool bOk = false;
803 :
804 0 : if( HasSelectionList() )
805 : {
806 0 : boost::scoped_ptr<ScTokenArray> pTokArr( CreateTokenArry(0) );
807 :
808 : // *** try if formula is a string list ***
809 :
810 0 : sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos );
811 0 : ScStringTokenIterator aIt( *pTokArr );
812 0 : for( const String* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next() )
813 : {
814 : double fValue;
815 0 : bool bIsValue = GetDocument()->GetFormatTable()->IsNumberFormat( *pString, nFormat, fValue );
816 : rStrColl.push_back(
817 : ScTypedStrData(
818 0 : *pString, fValue, bIsValue ? ScTypedStrData::Value : ScTypedStrData::Standard));
819 : }
820 0 : bOk = aIt.Ok();
821 :
822 : // *** if not a string list, try if formula results in a cell range or
823 : // anything else we recognize as valid ***
824 :
825 0 : if (!bOk)
826 : {
827 : int nMatch;
828 0 : ScRefCellValue aEmptyCell;
829 0 : bOk = GetSelectionFromFormula(&rStrColl, aEmptyCell, rPos, *pTokArr, nMatch);
830 0 : }
831 : }
832 :
833 0 : return bOk;
834 : }
835 :
836 : // ----------------------------------------------------------------------------
837 :
838 0 : bool ScValidationData::IsEqualToTokenArray( ScRefCellValue& rCell, const ScAddress& rPos, const ScTokenArray& rTokArr ) const
839 : {
840 : // create a condition entry that tests on equality and set the passed token array
841 0 : ScConditionEntry aCondEntry( SC_COND_EQUAL, &rTokArr, NULL, GetDocument(), rPos );
842 0 : return aCondEntry.IsCellValid(rCell, rPos);
843 : }
844 :
845 0 : bool ScValidationData::IsListValid( ScRefCellValue& rCell, const ScAddress& rPos ) const
846 : {
847 0 : bool bIsValid = false;
848 :
849 : /* Compare input cell with all supported tokens from the formula.
850 : Currently a formula may contain:
851 : 1) A list of strings (at least one string).
852 : 2) A single cell or range reference.
853 : 3) A single defined name (must contain a cell/range reference, another
854 : name, or DB range, or a formula resulting in a cell/range reference
855 : or matrix/array).
856 : 4) A single database range.
857 : 5) A formula resulting in a cell/range reference or matrix/array.
858 : */
859 :
860 : SAL_WNODEPRECATED_DECLARATIONS_PUSH
861 0 : ::std::auto_ptr< ScTokenArray > pTokArr( CreateTokenArry( 0 ) );
862 : SAL_WNODEPRECATED_DECLARATIONS_POP
863 :
864 : // *** try if formula is a string list ***
865 :
866 0 : sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos );
867 0 : ScStringTokenIterator aIt( *pTokArr );
868 0 : for( const String* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next() )
869 : {
870 : /* Do not break the loop, if a valid string has been found.
871 : This is to find invalid tokens following in the formula. */
872 0 : if( !bIsValid )
873 : {
874 : // create a formula containing a single string or number
875 0 : ScTokenArray aCondTokArr;
876 : double fValue;
877 0 : if( GetDocument()->GetFormatTable()->IsNumberFormat( *pString, nFormat, fValue ) )
878 0 : aCondTokArr.AddDouble( fValue );
879 : else
880 0 : aCondTokArr.AddString( *pString );
881 :
882 0 : bIsValid = IsEqualToTokenArray(rCell, rPos, aCondTokArr);
883 : }
884 : }
885 :
886 0 : if( !aIt.Ok() )
887 0 : bIsValid = false;
888 :
889 : // *** if not a string list, try if formula results in a cell range or
890 : // anything else we recognize as valid ***
891 :
892 0 : if (!bIsValid)
893 : {
894 : int nMatch;
895 0 : bIsValid = GetSelectionFromFormula(NULL, rCell, rPos, *pTokArr, nMatch);
896 0 : bIsValid = bIsValid && nMatch >= 0;
897 : }
898 :
899 0 : return bIsValid;
900 : }
901 :
902 : // ============================================================================
903 : // ============================================================================
904 :
905 0 : ScValidationDataList::ScValidationDataList(const ScValidationDataList& rList) :
906 0 : std::set<ScValidationData*, CompareScValidationDataPtr>()
907 : {
908 : // fuer Ref-Undo - echte Kopie mit neuen Tokens!
909 :
910 0 : for (const_iterator it = rList.begin(); it != rList.end(); ++it)
911 : {
912 0 : InsertNew( (*it)->Clone() );
913 : }
914 :
915 : //! sortierte Eintraege aus rList schneller einfuegen ???
916 0 : }
917 :
918 1 : ScValidationDataList::ScValidationDataList(ScDocument* pNewDoc,
919 1 : const ScValidationDataList& rList)
920 : {
921 : // fuer neues Dokument - echte Kopie mit neuen Tokens!
922 :
923 11 : for (const_iterator it = rList.begin(); it != rList.end(); ++it)
924 : {
925 10 : InsertNew( (*it)->Clone(pNewDoc) );
926 : }
927 :
928 : //! sortierte Eintraege aus rList schneller einfuegen ???
929 1 : }
930 :
931 82 : ScValidationData* ScValidationDataList::GetData( sal_uInt32 nKey )
932 : {
933 : //! binaer suchen
934 :
935 201 : for( iterator it = begin(); it != end(); ++it )
936 201 : if( (*it)->GetKey() == nKey )
937 82 : return *it;
938 :
939 : OSL_FAIL("ScValidationDataList: Entry not found");
940 0 : return NULL;
941 : }
942 :
943 1 : void ScValidationDataList::CompileXML()
944 : {
945 3 : for( iterator it = begin(); it != end(); ++it )
946 2 : (*it)->CompileXML();
947 1 : }
948 :
949 5 : void ScValidationDataList::UpdateReference( UpdateRefMode eUpdateRefMode,
950 : const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
951 : {
952 19 : for( iterator it = begin(); it != end(); ++it )
953 14 : (*it)->UpdateReference( eUpdateRefMode, rRange, nDx, nDy, nDz);
954 5 : }
955 :
956 0 : void ScValidationDataList::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos )
957 : {
958 0 : for( iterator it = begin(); it != end(); ++it )
959 0 : (*it)->UpdateMoveTab( nOldPos, nNewPos );
960 0 : }
961 :
962 0 : sal_Bool ScValidationDataList::operator==( const ScValidationDataList& r ) const
963 : {
964 : // fuer Ref-Undo - interne Variablen werden nicht verglichen
965 :
966 0 : sal_uInt16 nCount = size();
967 0 : sal_Bool bEqual = ( nCount == r.size() );
968 0 : for( const_iterator it1 = begin(), it2 = r.begin(); it1 != end() && bEqual; ++it1, ++it2 ) // Eintraege sind sortiert
969 0 : if ( !(*it1)->EqualEntries(**it2) ) // Eintraege unterschiedlich ?
970 0 : bEqual = sal_False;
971 :
972 0 : return bEqual;
973 93 : }
974 :
975 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|