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 "svl/sharedstringpool.hxx"
33 : #include <vcl/msgbox.hxx>
34 : #include <rtl/math.hxx>
35 :
36 : #include "scitems.hxx"
37 : #include "document.hxx"
38 : #include "formulacell.hxx"
39 : #include "patattr.hxx"
40 : #include "rechead.hxx"
41 : #include "globstr.hrc"
42 : #include "rangenam.hxx"
43 : #include "dbdata.hxx"
44 : #include "typedstrdata.hxx"
45 : #include "dociter.hxx"
46 : #include "editutil.hxx"
47 : #include "tokenarray.hxx"
48 : #include "scmatrix.hxx"
49 :
50 : #include <math.h>
51 : #include <boost/scoped_ptr.hpp>
52 :
53 : using namespace formula;
54 :
55 :
56 : // Eintrag fuer Gueltigkeit (es gibt nur eine Bedingung)
57 :
58 :
59 0 : ScValidationData::ScValidationData( ScValidationMode eMode, ScConditionMode eOper,
60 : const OUString& rExpr1, const OUString& rExpr2,
61 : ScDocument* pDocument, const ScAddress& rPos,
62 : const OUString& rExprNmsp1, const OUString& rExprNmsp2,
63 : FormulaGrammar::Grammar eGrammar1,
64 : FormulaGrammar::Grammar eGrammar2 )
65 : : ScConditionEntry( eOper, rExpr1, rExpr2, pDocument, rPos, rExprNmsp1,
66 : rExprNmsp2, eGrammar1, eGrammar2 )
67 : , nKey( 0 )
68 : , eDataMode( eMode )
69 : , bShowInput(false)
70 : , bShowError(false)
71 : , eErrorStyle( SC_VALERR_STOP )
72 0 : , mnListType( ValidListType::UNSORTED )
73 : {
74 0 : }
75 :
76 0 : ScValidationData::ScValidationData( ScValidationMode eMode, ScConditionMode eOper,
77 : const ScTokenArray* pArr1, const ScTokenArray* pArr2,
78 : ScDocument* pDocument, const ScAddress& rPos )
79 : : ScConditionEntry( eOper, pArr1, pArr2, pDocument, rPos )
80 : , nKey( 0 )
81 : , eDataMode( eMode )
82 : , bShowInput(false)
83 : , bShowError(false)
84 : , eErrorStyle( SC_VALERR_STOP )
85 0 : , mnListType( ValidListType::UNSORTED )
86 : {
87 0 : }
88 :
89 0 : ScValidationData::ScValidationData( const ScValidationData& r )
90 : : ScConditionEntry( r )
91 : , nKey( r.nKey )
92 : , eDataMode( r.eDataMode )
93 : , bShowInput( r.bShowInput )
94 : , bShowError( r.bShowError )
95 : , eErrorStyle( r.eErrorStyle )
96 : , mnListType( r.mnListType )
97 : , aInputTitle( r.aInputTitle )
98 : , aInputMessage( r.aInputMessage )
99 : , aErrorTitle( r.aErrorTitle )
100 0 : , aErrorMessage( r.aErrorMessage )
101 : {
102 : // Formeln per RefCount kopiert
103 0 : }
104 :
105 0 : ScValidationData::ScValidationData( ScDocument* pDocument, const ScValidationData& r )
106 : : ScConditionEntry( pDocument, r )
107 : , nKey( r.nKey )
108 : , eDataMode( r.eDataMode )
109 : , bShowInput( r.bShowInput )
110 : , bShowError( r.bShowError )
111 : , eErrorStyle( r.eErrorStyle )
112 : , mnListType( r.mnListType )
113 : , aInputTitle( r.aInputTitle )
114 : , aInputMessage( r.aInputMessage )
115 : , aErrorTitle( r.aErrorTitle )
116 0 : , aErrorMessage( r.aErrorMessage )
117 : {
118 : // Formeln wirklich kopiert
119 0 : }
120 :
121 0 : ScValidationData::~ScValidationData()
122 : {
123 0 : }
124 :
125 0 : bool ScValidationData::IsEmpty() const
126 : {
127 0 : OUString aEmpty;
128 0 : ScValidationData aDefault( SC_VALID_ANY, SC_COND_EQUAL, aEmpty, aEmpty, GetDocument(), ScAddress() );
129 0 : return EqualEntries( aDefault );
130 : }
131 :
132 0 : bool ScValidationData::EqualEntries( const ScValidationData& r ) const
133 : {
134 : // gleiche Parameter eingestellt (ohne Key)
135 :
136 0 : return ScConditionEntry::operator==(r) &&
137 0 : eDataMode == r.eDataMode &&
138 0 : bShowInput == r.bShowInput &&
139 0 : bShowError == r.bShowError &&
140 0 : eErrorStyle == r.eErrorStyle &&
141 0 : mnListType == r.mnListType &&
142 0 : aInputTitle == r.aInputTitle &&
143 0 : aInputMessage == r.aInputMessage &&
144 0 : aErrorTitle == r.aErrorTitle &&
145 0 : aErrorMessage == r.aErrorMessage;
146 : }
147 :
148 0 : void ScValidationData::ResetInput()
149 : {
150 0 : bShowInput = false;
151 0 : }
152 :
153 0 : void ScValidationData::ResetError()
154 : {
155 0 : bShowError = false;
156 0 : }
157 :
158 0 : void ScValidationData::SetInput( const OUString& rTitle, const OUString& rMsg )
159 : {
160 0 : bShowInput = true;
161 0 : aInputTitle = rTitle;
162 0 : aInputMessage = rMsg;
163 0 : }
164 :
165 0 : void ScValidationData::SetError( const OUString& rTitle, const OUString& rMsg,
166 : ScValidErrorStyle eStyle )
167 : {
168 0 : bShowError = true;
169 0 : eErrorStyle = eStyle;
170 0 : aErrorTitle = rTitle;
171 0 : aErrorMessage = rMsg;
172 0 : }
173 :
174 0 : bool ScValidationData::GetErrMsg( OUString& rTitle, OUString& rMsg,
175 : ScValidErrorStyle& rStyle ) const
176 : {
177 0 : rTitle = aErrorTitle;
178 0 : rMsg = aErrorMessage;
179 0 : rStyle = eErrorStyle;
180 0 : return bShowError;
181 : }
182 :
183 0 : bool ScValidationData::DoScript( const ScAddress& rPos, const OUString& rInput,
184 : ScFormulaCell* pCell, Window* pParent ) const
185 : {
186 0 : ScDocument* pDocument = GetDocument();
187 0 : SfxObjectShell* pDocSh = pDocument->GetDocumentShell();
188 0 : if ( !pDocSh || !pDocument->CheckMacroWarn() )
189 0 : return false;
190 :
191 0 : bool bScriptReturnedFalse = false; // Standard: kein Abbruch
192 :
193 : // Set up parameters
194 0 : ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > aParams(2);
195 :
196 : // 1) eingegebener / berechneter Wert
197 0 : OUString aValStr = rInput;
198 : double nValue;
199 0 : bool bIsValue = false;
200 0 : if ( pCell ) // wenn Zelle gesetzt, aus Interpret gerufen
201 : {
202 0 : bIsValue = pCell->IsValue();
203 0 : if ( bIsValue )
204 0 : nValue = pCell->GetValue();
205 : else
206 0 : aValStr = pCell->GetString().getString();
207 : }
208 0 : if ( bIsValue )
209 0 : aParams[0] = ::com::sun::star::uno::makeAny( nValue );
210 : else
211 0 : aParams[0] = ::com::sun::star::uno::makeAny( OUString( aValStr ) );
212 :
213 : // 2) Position der Zelle
214 0 : OUString aPosStr(rPos.Format(SCA_VALID | SCA_TAB_3D, pDocument, pDocument->GetAddressConvention()));
215 0 : aParams[1] = ::com::sun::star::uno::makeAny(aPosStr);
216 :
217 : // use link-update flag to prevent closing the document
218 : // while the macro is running
219 0 : bool bWasInLinkUpdate = pDocument->IsInLinkUpdate();
220 0 : if ( !bWasInLinkUpdate )
221 0 : pDocument->SetInLinkUpdate( true );
222 :
223 0 : if ( pCell )
224 0 : pDocument->LockTable( rPos.Tab() );
225 :
226 0 : ::com::sun::star::uno::Any aRet;
227 0 : ::com::sun::star::uno::Sequence< sal_Int16 > aOutArgsIndex;
228 0 : ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > aOutArgs;
229 :
230 : ErrCode eRet = pDocSh->CallXScript(
231 0 : aErrorTitle, aParams, aRet, aOutArgsIndex, aOutArgs );
232 :
233 0 : if ( pCell )
234 0 : pDocument->UnlockTable( rPos.Tab() );
235 :
236 0 : if ( !bWasInLinkUpdate )
237 0 : pDocument->SetInLinkUpdate( false );
238 :
239 : // Check the return value from the script
240 : // The contents of the cell get reset if the script returns false
241 0 : bool bTmp = false;
242 0 : if ( eRet == ERRCODE_NONE &&
243 0 : aRet.getValueType() == getCppuBooleanType() &&
244 0 : ( aRet >>= bTmp ) &&
245 0 : !bTmp )
246 : {
247 0 : bScriptReturnedFalse = true;
248 : }
249 :
250 0 : if ( eRet == ERRCODE_BASIC_METHOD_NOT_FOUND && !pCell )
251 : // Makro nicht gefunden (nur bei Eingabe)
252 : {
253 : //! andere Fehlermeldung, wenn gefunden, aber nicht bAllowed ??
254 :
255 : ErrorBox aBox( pParent, WinBits(WB_OK),
256 0 : ScGlobal::GetRscString( STR_VALID_MACRONOTFOUND ) );
257 0 : aBox.Execute();
258 : }
259 :
260 0 : return bScriptReturnedFalse;
261 : }
262 :
263 : // true -> Abbruch
264 :
265 0 : bool ScValidationData::DoMacro( const ScAddress& rPos, const OUString& rInput,
266 : ScFormulaCell* pCell, 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; // Standard: kein Abbruch
280 :
281 : // Wenn das Dok waehrend eines Basic-Calls geladen wurde,
282 : // ist das Sbx-Objekt evtl. nicht angelegt (?)
283 : // pDocSh->GetSbxObject();
284 :
285 : #ifndef DISABLE_SCRIPTING
286 : // keine Sicherheitsabfrage mehr vorneweg (nur CheckMacroWarn), das passiert im CallBasic
287 :
288 : // Funktion ueber den einfachen Namen suchen,
289 : // dann aBasicStr, aMacroStr fuer SfxObjectShell::CallBasic zusammenbauen
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 = (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(); // Dokumentenbasic
309 : else
310 0 : aBasicStr = SFX_APP()->GetName(); // Applikationsbasic
311 :
312 : // Parameter fuer Makro
313 0 : SbxArrayRef refPar = new SbxArray;
314 :
315 : // 1) eingegebener / berechneter Wert
316 0 : OUString aValStr = rInput;
317 0 : double nValue = 0.0;
318 0 : bool bIsValue = false;
319 0 : if ( pCell ) // wenn Zelle gesetzt, aus Interpret gerufen
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 der Zelle
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 : // Eingabe abbrechen, wenn Basic-Makro sal_False zurueckgibt
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 ) // 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_OUSTRING, pCell, NULL );
374 0 : }
375 :
376 : // true -> Abbruch
377 :
378 0 : bool ScValidationData::DoError( Window* pParent, const OUString& 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 : OUString aTitle = aErrorTitle;
387 0 : if (aTitle.isEmpty())
388 0 : aTitle = ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_0 ); // application title
389 0 : OUString aMessage = aErrorMessage;
390 0 : if (aMessage.isEmpty())
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 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 : bool bIsVal = pFormatter->IsNumberFormat( rTest, nFormat, nVal );
440 :
441 : 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 : svl::SharedString aSS = mpDoc->GetSharedStringPool().intern(rTest);
470 0 : ScRefCellValue aTmpCell(&aSS);
471 0 : bRet = IsDataValid(aTmpCell, rPos);
472 : }
473 : }
474 :
475 0 : return bRet;
476 : }
477 :
478 0 : bool ScValidationData::IsDataValid( ScRefCellValue& rCell, const ScAddress& rPos ) const
479 : {
480 0 : if( eDataMode == SC_VALID_LIST )
481 0 : return IsListValid(rCell, rPos);
482 :
483 0 : double nVal = 0.0;
484 0 : OUString aString;
485 0 : bool bIsVal = true;
486 :
487 0 : switch (rCell.meType)
488 : {
489 : case CELLTYPE_VALUE:
490 0 : nVal = rCell.mfValue;
491 0 : break;
492 : case CELLTYPE_STRING:
493 0 : aString = rCell.mpString->getString();
494 0 : bIsVal = false;
495 0 : break;
496 : case CELLTYPE_EDIT:
497 0 : if (rCell.mpEditText)
498 0 : aString = ScEditUtil::GetString(*rCell.mpEditText, GetDocument());
499 0 : bIsVal = false;
500 0 : break;
501 : case CELLTYPE_FORMULA:
502 : {
503 0 : ScFormulaCell* pFCell = rCell.mpFormula;
504 0 : bIsVal = pFCell->IsValue();
505 0 : if ( bIsVal )
506 0 : nVal = pFCell->GetValue();
507 : else
508 0 : aString = pFCell->GetString().getString();
509 : }
510 0 : break;
511 : default: // Notizen, Broadcaster
512 0 : return IsIgnoreBlank(); // wie eingestellt
513 : }
514 :
515 0 : bool bOk = true;
516 0 : switch (eDataMode)
517 : {
518 : // SC_VALID_ANY schon oben
519 :
520 : case SC_VALID_WHOLE:
521 : case SC_VALID_DECIMAL:
522 : case SC_VALID_DATE: // Date/Time ist nur Formatierung
523 : case SC_VALID_TIME:
524 0 : bOk = bIsVal;
525 0 : if ( bOk && eDataMode == SC_VALID_WHOLE )
526 0 : bOk = ::rtl::math::approxEqual( nVal, floor(nVal+0.5) ); // ganze Zahlen
527 0 : if ( bOk )
528 0 : bOk = IsCellValid(rCell, rPos);
529 0 : break;
530 :
531 : case SC_VALID_CUSTOM:
532 : // fuer Custom muss eOp == SC_COND_DIRECT sein
533 : //! der Wert muss im Dokument stehen !!!!!!!!!!!!!!!!!!!!
534 0 : bOk = IsCellValid(rCell, rPos);
535 0 : break;
536 :
537 : case SC_VALID_TEXTLEN:
538 0 : bOk = !bIsVal; // nur Text
539 0 : if ( bOk )
540 : {
541 0 : double nLenVal = (double) aString.getLength();
542 0 : ScRefCellValue aTmpCell(nLenVal);
543 0 : bOk = IsCellValid(aTmpCell, rPos);
544 : }
545 0 : break;
546 :
547 : default:
548 : OSL_FAIL("not yet done");
549 0 : break;
550 : }
551 :
552 0 : return bOk;
553 : }
554 :
555 : namespace {
556 :
557 : /** Token array helper. Iterates over all string tokens.
558 : @descr The token array must contain separated string tokens only.
559 : @param bSkipEmpty true = Ignores string tokens with empty strings. */
560 0 : class ScStringTokenIterator
561 : {
562 : public:
563 0 : inline explicit ScStringTokenIterator( ScTokenArray& rTokArr, bool bSkipEmpty = true ) :
564 0 : mrTokArr( rTokArr ), mbSkipEmpty( bSkipEmpty ), mbOk( true ) {}
565 :
566 : /** Returns the string of the first string token or NULL on error or empty token array. */
567 : rtl_uString* First();
568 : /** Returns the string of the next string token or NULL on error or end of token array. */
569 : rtl_uString* Next();
570 :
571 : /** Returns false, if a wrong token has been found. Does NOT return false on end of token array. */
572 0 : inline bool Ok() const { return mbOk; }
573 :
574 : private:
575 : svl::SharedString maCurString; /// Current string.
576 : ScTokenArray& mrTokArr; /// The token array for iteration.
577 : bool mbSkipEmpty; /// Ignore empty strings.
578 : bool mbOk; /// true = correct token or end of token array.
579 : };
580 :
581 0 : rtl_uString* ScStringTokenIterator::First()
582 : {
583 0 : mrTokArr.Reset();
584 0 : mbOk = true;
585 0 : return Next();
586 : }
587 :
588 0 : rtl_uString* ScStringTokenIterator::Next()
589 : {
590 0 : if( !mbOk )
591 0 : return NULL;
592 :
593 : // seek to next non-separator token
594 0 : const FormulaToken* pToken = mrTokArr.NextNoSpaces();
595 0 : while( pToken && (pToken->GetOpCode() == ocSep) )
596 0 : pToken = mrTokArr.NextNoSpaces();
597 :
598 0 : mbOk = !pToken || (pToken->GetType() == formula::svString);
599 :
600 0 : maCurString = svl::SharedString(); // start with invalid string.
601 0 : if (mbOk && pToken)
602 0 : maCurString = pToken->GetString();
603 :
604 : // string found but empty -> get next token; otherwise return it
605 0 : return (mbSkipEmpty && maCurString.isValid() && maCurString.isEmpty()) ? Next() : maCurString.getData();
606 : }
607 :
608 : /** Returns the number format of the passed cell, or the standard format. */
609 0 : sal_uLong lclGetCellFormat( ScDocument& rDoc, const ScAddress& rPos )
610 : {
611 0 : const ScPatternAttr* pPattern = rDoc.GetPattern( rPos.Col(), rPos.Row(), rPos.Tab() );
612 0 : if( !pPattern )
613 0 : pPattern = rDoc.GetDefPattern();
614 0 : return pPattern->GetNumberFormat( rDoc.GetFormatTable() );
615 : }
616 :
617 : } // namespace
618 :
619 0 : bool ScValidationData::HasSelectionList() const
620 : {
621 0 : return (eDataMode == SC_VALID_LIST) && (mnListType != ValidListType::INVISIBLE);
622 : }
623 :
624 0 : bool ScValidationData::GetSelectionFromFormula(
625 : std::vector<ScTypedStrData>* pStrings, ScRefCellValue& rCell, const ScAddress& rPos,
626 : const ScTokenArray& rTokArr, int& rMatch) const
627 : {
628 0 : bool bOk = true;
629 :
630 : // pDoc is private in condition, use an accessor and a long winded name.
631 0 : ScDocument* pDocument = GetDocument();
632 0 : if( NULL == pDocument )
633 0 : return false;
634 :
635 : ScFormulaCell aValidationSrc(
636 0 : pDocument, rPos, rTokArr, formula::FormulaGrammar::GRAM_DEFAULT, MM_FORMULA);
637 :
638 : // Make sure the formula gets interpreted and a result is delivered,
639 : // regardless of the AutoCalc setting.
640 0 : aValidationSrc.Interpret();
641 :
642 0 : ScMatrixRef xMatRef;
643 0 : const ScMatrix *pValues = aValidationSrc.GetMatrix();
644 0 : if (!pValues)
645 : {
646 : // The somewhat nasty case of either an error occurred, or the
647 : // dereferenced value of a single cell reference or an immediate result
648 : // is stored as a single value.
649 :
650 : // Use an interim matrix to create the TypedStrData below.
651 0 : xMatRef = new ScMatrix(1, 1, 0.0);
652 :
653 0 : sal_uInt16 nErrCode = aValidationSrc.GetErrCode();
654 0 : if (nErrCode)
655 : {
656 : /* TODO : to use later in an alert box?
657 : * OUString rStrResult = "...";
658 : * rStrResult += ScGlobal::GetLongErrorString(nErrCode);
659 : */
660 :
661 0 : xMatRef->PutError( nErrCode, 0, 0);
662 0 : bOk = false;
663 : }
664 0 : else if (aValidationSrc.IsValue())
665 0 : xMatRef->PutDouble( aValidationSrc.GetValue(), 0);
666 : else
667 : {
668 0 : svl::SharedString aStr = aValidationSrc.GetString();
669 0 : xMatRef->PutString(aStr, 0);
670 : }
671 :
672 0 : pValues = xMatRef.get();
673 : }
674 :
675 : // which index matched. We will want it eventually to pre-select that item.
676 0 : rMatch = -1;
677 :
678 0 : SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable();
679 :
680 0 : SCSIZE nCol, nRow, nCols, nRows, n = 0;
681 0 : pValues->GetDimensions( nCols, nRows );
682 :
683 0 : bool bRef = false;
684 0 : ScRange aRange;
685 :
686 0 : ScTokenArray* pArr = (ScTokenArray*) &rTokArr;
687 0 : pArr->Reset();
688 0 : ScToken* t = NULL;
689 0 : if (pArr->GetLen() == 1 && (t = static_cast<ScToken*>(pArr->GetNextReferenceOrName())) != NULL)
690 : {
691 0 : if (t->GetOpCode() == ocDBArea)
692 : {
693 0 : if (const ScDBData* pDBData = pDocument->GetDBCollection()->getNamedDBs().findByIndex(t->GetIndex()))
694 : {
695 0 : pDBData->GetArea(aRange);
696 0 : bRef = true;
697 : }
698 : }
699 0 : else if (t->GetOpCode() == ocName)
700 : {
701 0 : ScRangeData* pName = pDocument->GetRangeName()->findByIndex( t->GetIndex() );
702 0 : if (pName && pName->IsReference(aRange))
703 : {
704 0 : bRef = true;
705 : }
706 : }
707 0 : else if (t->GetType() != svIndex)
708 : {
709 0 : if (pArr->IsValidReference(aRange, rPos))
710 : {
711 0 : bRef = true;
712 : }
713 : }
714 : }
715 :
716 0 : bool bHaveEmpty = false;
717 :
718 : /* XL artificially limits things to a single col or row in the UI but does
719 : * not list the constraint in MOOXml. If a defined name or INDIRECT
720 : * resulting in 1D is entered in the UI and the definition later modified
721 : * to 2D, it is evaluated fine and also stored and loaded. Lets get ahead
722 : * of the curve and support 2d. In XL, values are listed row-wise, do the
723 : * same. */
724 0 : for( nRow = 0; nRow < nRows ; nRow++ )
725 : {
726 0 : for( nCol = 0; nCol < nCols ; nCol++ )
727 : {
728 0 : ScTokenArray aCondTokArr;
729 0 : ScTypedStrData* pEntry = NULL;
730 0 : OUString aValStr;
731 0 : ScMatrixValue nMatVal = pValues->Get( nCol, nRow);
732 :
733 : // strings and empties
734 0 : if( ScMatrix::IsNonValueType( nMatVal.nType ) )
735 : {
736 0 : aValStr = nMatVal.GetString().getString();
737 :
738 : // Do not add multiple empty strings to the validation list,
739 : // especially not if they'd bloat the tail with a million empty
740 : // entries for a column range, fdo#61520
741 0 : if (aValStr.isEmpty())
742 : {
743 0 : if (bHaveEmpty)
744 0 : continue;
745 0 : bHaveEmpty = true;
746 : }
747 :
748 0 : if( NULL != pStrings )
749 0 : pEntry = new ScTypedStrData( aValStr, 0.0, ScTypedStrData::Standard);
750 :
751 0 : if (!rCell.isEmpty() && rMatch < 0)
752 0 : aCondTokArr.AddString( aValStr );
753 : }
754 : else
755 : {
756 0 : sal_uInt16 nErr = nMatVal.GetError();
757 :
758 0 : if( 0 != nErr )
759 : {
760 0 : aValStr = ScGlobal::GetErrorString( nErr );
761 : }
762 : else
763 : {
764 : // FIXME FIXME FIXME
765 : // Feature regression. Date formats are lost passing through the matrix
766 : //pFormatter->GetInputLineString( pMatVal->fVal, 0, aValStr );
767 : //For external reference and a formula that results in an area or array, date formats are still lost.
768 0 : if ( bRef )
769 : {
770 0 : pDocument->GetInputString((SCCOL)(nCol+aRange.aStart.Col()),
771 0 : (SCROW)(nRow+aRange.aStart.Row()), aRange.aStart.Tab() , aValStr);
772 : }
773 : else
774 : {
775 0 : pFormatter->GetInputLineString( nMatVal.fVal, 0, aValStr );
776 : }
777 : }
778 :
779 0 : if (!rCell.isEmpty() && rMatch < 0)
780 : {
781 : // I am not sure errors will work here, but a user can no
782 : // manually enter an error yet so the point is somewhat moot.
783 0 : aCondTokArr.AddDouble( nMatVal.fVal );
784 : }
785 0 : if( NULL != pStrings )
786 0 : pEntry = new ScTypedStrData( aValStr, nMatVal.fVal, ScTypedStrData::Value);
787 : }
788 :
789 0 : if (rMatch < 0 && !rCell.isEmpty() && IsEqualToTokenArray(rCell, rPos, aCondTokArr))
790 : {
791 0 : rMatch = n;
792 : // short circuit on the first match if not filling the list
793 0 : if( NULL == pStrings )
794 0 : return true;
795 : }
796 :
797 0 : if( NULL != pEntry )
798 : {
799 0 : pStrings->push_back(*pEntry);
800 0 : delete pEntry;
801 0 : n++;
802 : }
803 0 : }
804 : }
805 :
806 : // In case of no match needed and an error occurred, return that error
807 : // entry as valid instead of silently failing.
808 0 : return bOk || rCell.isEmpty();
809 : }
810 :
811 0 : bool ScValidationData::FillSelectionList(std::vector<ScTypedStrData>& rStrColl, const ScAddress& rPos) const
812 : {
813 0 : bool bOk = false;
814 :
815 0 : if( HasSelectionList() )
816 : {
817 0 : boost::scoped_ptr<ScTokenArray> pTokArr( CreateTokenArry(0) );
818 :
819 : // *** try if formula is a string list ***
820 :
821 0 : sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos );
822 0 : ScStringTokenIterator aIt( *pTokArr );
823 0 : for (rtl_uString* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next())
824 : {
825 : double fValue;
826 0 : OUString aStr(pString);
827 0 : bool bIsValue = GetDocument()->GetFormatTable()->IsNumberFormat(aStr, nFormat, fValue);
828 : rStrColl.push_back(
829 : ScTypedStrData(
830 0 : aStr, fValue, bIsValue ? ScTypedStrData::Value : ScTypedStrData::Standard));
831 0 : }
832 0 : bOk = aIt.Ok();
833 :
834 : // *** if not a string list, try if formula results in a cell range or
835 : // anything else we recognize as valid ***
836 :
837 0 : if (!bOk)
838 : {
839 : int nMatch;
840 0 : ScRefCellValue aEmptyCell;
841 0 : bOk = GetSelectionFromFormula(&rStrColl, aEmptyCell, rPos, *pTokArr, nMatch);
842 0 : }
843 : }
844 :
845 0 : return bOk;
846 : }
847 :
848 0 : bool ScValidationData::IsEqualToTokenArray( ScRefCellValue& rCell, const ScAddress& rPos, const ScTokenArray& rTokArr ) const
849 : {
850 : // create a condition entry that tests on equality and set the passed token array
851 0 : ScConditionEntry aCondEntry( SC_COND_EQUAL, &rTokArr, NULL, GetDocument(), rPos );
852 0 : return aCondEntry.IsCellValid(rCell, rPos);
853 : }
854 :
855 0 : bool ScValidationData::IsListValid( ScRefCellValue& rCell, const ScAddress& rPos ) const
856 : {
857 0 : bool bIsValid = false;
858 :
859 : /* Compare input cell with all supported tokens from the formula.
860 : Currently a formula may contain:
861 : 1) A list of strings (at least one string).
862 : 2) A single cell or range reference.
863 : 3) A single defined name (must contain a cell/range reference, another
864 : name, or DB range, or a formula resulting in a cell/range reference
865 : or matrix/array).
866 : 4) A single database range.
867 : 5) A formula resulting in a cell/range reference or matrix/array.
868 : */
869 :
870 0 : boost::scoped_ptr< ScTokenArray > pTokArr( CreateTokenArry( 0 ) );
871 :
872 : // *** try if formula is a string list ***
873 :
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(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 :
912 0 : ScValidationDataList::ScValidationDataList(const ScValidationDataList& rList)
913 : {
914 : // fuer Ref-Undo - echte Kopie mit neuen Tokens!
915 :
916 0 : for (const_iterator it = rList.begin(); it != rList.end(); ++it)
917 : {
918 0 : InsertNew( (*it)->Clone() );
919 : }
920 :
921 : //! sortierte Eintraege aus rList schneller einfuegen ???
922 0 : }
923 :
924 0 : ScValidationDataList::ScValidationDataList(ScDocument* pNewDoc,
925 0 : const ScValidationDataList& rList)
926 : {
927 : // fuer neues Dokument - echte Kopie mit neuen Tokens!
928 :
929 0 : for (const_iterator it = rList.begin(); it != rList.end(); ++it)
930 : {
931 0 : InsertNew( (*it)->Clone(pNewDoc) );
932 : }
933 :
934 : //! sortierte Eintraege aus rList schneller einfuegen ???
935 0 : }
936 :
937 0 : ScValidationData* ScValidationDataList::GetData( sal_uInt32 nKey )
938 : {
939 : //! binaer suchen
940 :
941 0 : for( iterator it = begin(); it != end(); ++it )
942 0 : if( (*it)->GetKey() == nKey )
943 0 : return *it;
944 :
945 : OSL_FAIL("ScValidationDataList: Entry not found");
946 0 : return NULL;
947 : }
948 :
949 0 : void ScValidationDataList::CompileXML()
950 : {
951 0 : for( iterator it = begin(); it != end(); ++it )
952 0 : (*it)->CompileXML();
953 0 : }
954 :
955 0 : void ScValidationDataList::UpdateReference( sc::RefUpdateContext& rCxt )
956 : {
957 0 : for( iterator it = begin(); it != end(); ++it )
958 0 : (*it)->UpdateReference(rCxt);
959 0 : }
960 :
961 0 : void ScValidationDataList::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
962 : {
963 0 : for (iterator it = begin(); it != end(); ++it)
964 0 : (*it)->UpdateInsertTab(rCxt);
965 0 : }
966 :
967 0 : void ScValidationDataList::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
968 : {
969 0 : for (iterator it = begin(); it != end(); ++it)
970 0 : (*it)->UpdateDeleteTab(rCxt);
971 0 : }
972 :
973 0 : void ScValidationDataList::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt )
974 : {
975 0 : for (iterator it = begin(); it != end(); ++it)
976 0 : (*it)->UpdateMoveTab(rCxt);
977 0 : }
978 :
979 0 : bool ScValidationDataList::operator==( const ScValidationDataList& r ) const
980 : {
981 : // fuer Ref-Undo - interne Variablen werden nicht verglichen
982 :
983 0 : size_t nCount = maData.size();
984 0 : bool bEqual = ( nCount == r.maData.size() );
985 0 : for( const_iterator it1 = begin(), it2 = r.begin(); it1 != end() && bEqual; ++it1, ++it2 ) // Eintraege sind sortiert
986 0 : if ( !(*it1)->EqualEntries(**it2) ) // Eintraege unterschiedlich ?
987 0 : bEqual = false;
988 :
989 0 : return bEqual;
990 : }
991 :
992 0 : ScValidationDataList::iterator ScValidationDataList::begin()
993 : {
994 0 : return maData.begin();
995 : }
996 :
997 0 : ScValidationDataList::const_iterator ScValidationDataList::begin() const
998 : {
999 0 : return maData.begin();
1000 : }
1001 :
1002 0 : ScValidationDataList::iterator ScValidationDataList::end()
1003 : {
1004 0 : return maData.end();
1005 : }
1006 :
1007 0 : ScValidationDataList::const_iterator ScValidationDataList::end() const
1008 : {
1009 0 : return maData.end();
1010 : }
1011 :
1012 0 : void ScValidationDataList::clear()
1013 : {
1014 0 : maData.clear();
1015 0 : }
1016 :
1017 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|