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