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