xref: /aoo42x/main/sc/source/core/data/validat.cxx (revision b3f79822)
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 
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 
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 
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 
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 
127 ScValidationData::~ScValidationData()
128 {
129 }
130 
131 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 
138 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 
154 void ScValidationData::ResetInput()
155 {
156 	bShowInput = sal_False;
157 }
158 
159 void ScValidationData::ResetError()
160 {
161 	bShowError = sal_False;
162 }
163 
164 void ScValidationData::SetInput( const String& rTitle, const String& rMsg )
165 {
166 	bShowInput = sal_True;
167 	aInputTitle = rTitle;
168 	aInputMessage = rMsg;
169 }
170 
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 
180 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 
189 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 
272 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 
390 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 
398 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 
441 sal_Bool ScValidationData::IsDataValid( const String& rTest, const ScPatternAttr& rPattern,
442 									const ScAddress& rPos ) const
443 {
444 	if ( eDataMode == SC_VALID_ANY )
445 		return sal_True;						// alles erlaubt
446 
447 	if ( rTest.GetChar(0) == '=' )
448 		return sal_False;						// Formeln sind sonst immer ungueltig
449 
450 	if ( !rTest.Len() )
451 		return IsIgnoreBlank();				// leer: wie eingestellt
452 
453 	SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable();
454 
455 	//	Test, was es denn ist - wie in ScColumn::SetString
456 
457 	sal_uInt32 nFormat = rPattern.GetNumberFormat( pFormatter );
458 
459 	double nVal;
460 	sal_Bool bIsVal = pFormatter->IsNumberFormat( rTest, nFormat, nVal );
461 	ScBaseCell* pCell;
462 	if (bIsVal)
463 		pCell = new ScValueCell( nVal );
464 	else
465 		pCell = new ScStringCell( rTest );
466 
467 	sal_Bool bRet = IsDataValid( pCell, rPos );
468 
469 	pCell->Delete();
470 	return bRet;
471 }
472 
473 sal_Bool ScValidationData::IsDataValid( ScBaseCell* pCell, const ScAddress& rPos ) const
474 {
475     if( eDataMode == SC_VALID_LIST )
476         return IsListValid( pCell, rPos );
477 
478 	double nVal = 0.0;
479 	String aString;
480 	sal_Bool bIsVal = sal_True;
481 
482 	switch (pCell->GetCellType())
483 	{
484 		case CELLTYPE_VALUE:
485 			nVal = ((ScValueCell*)pCell)->GetValue();
486 			break;
487 		case CELLTYPE_STRING:
488 			((ScStringCell*)pCell)->GetString( aString );
489 			bIsVal = sal_False;
490 			break;
491 		case CELLTYPE_EDIT:
492 			((ScEditCell*)pCell)->GetString( aString );
493 			bIsVal = sal_False;
494 			break;
495 		case CELLTYPE_FORMULA:
496 			{
497 				ScFormulaCell* pFCell = (ScFormulaCell*)pCell;
498 				bIsVal = pFCell->IsValue();
499 				if ( bIsVal )
500 					nVal  = pFCell->GetValue();
501 				else
502 					pFCell->GetString( aString );
503 			}
504 			break;
505 		default:						// Notizen, Broadcaster
506 			return IsIgnoreBlank();		// wie eingestellt
507 	}
508 
509 	sal_Bool bOk = sal_True;
510 	switch (eDataMode)
511 	{
512 		// SC_VALID_ANY schon oben
513 
514 		case SC_VALID_WHOLE:
515 		case SC_VALID_DECIMAL:
516 		case SC_VALID_DATE:			// Date/Time ist nur Formatierung
517 		case SC_VALID_TIME:
518 			bOk = bIsVal;
519 			if ( bOk && eDataMode == SC_VALID_WHOLE )
520 				bOk = ::rtl::math::approxEqual( nVal, floor(nVal+0.5) );		// ganze Zahlen
521 			if ( bOk )
522 				bOk = IsCellValid( pCell, rPos );
523 			break;
524 
525 		case SC_VALID_CUSTOM:
526 			//	fuer Custom muss eOp == SC_COND_DIRECT sein
527 			//!	der Wert muss im Dokument stehen !!!!!!!!!!!!!!!!!!!!
528 			bOk = IsCellValid( pCell, rPos );
529 			break;
530 
531 		case SC_VALID_TEXTLEN:
532 			bOk = !bIsVal;			// nur Text
533 			if ( bOk )
534 			{
535 				double nLenVal = (double) aString.Len();
536 				ScValueCell aTmpCell( nLenVal );
537 				bOk = IsCellValid( &aTmpCell, rPos );
538 			}
539 			break;
540 
541 		default:
542 			DBG_ERROR("hammanochnich");
543 			break;
544 	}
545 
546 	return bOk;
547 }
548 
549 // ----------------------------------------------------------------------------
550 
551 namespace {
552 
553 /** Token array helper. Iterates over all string tokens.
554     @descr  The token array must contain separated string tokens only.
555     @param bSkipEmpty  true = Ignores string tokens with empty strings. */
556 class ScStringTokenIterator
557 {
558 public:
559     inline explicit             ScStringTokenIterator( ScTokenArray& rTokArr, bool bSkipEmpty = true ) :
560                                     mrTokArr( rTokArr ), mbSkipEmpty( bSkipEmpty ), mbOk( true ) {}
561 
562     /** Returns the string of the first string token or NULL on error or empty token array. */
563     const String*               First();
564     /** Returns the string of the next string token or NULL on error or end of token array. */
565     const String*               Next();
566 
567     /** Returns false, if a wrong token has been found. Does NOT return false on end of token array. */
568     inline bool                 Ok() const { return mbOk; }
569 
570 private:
571     ScTokenArray&               mrTokArr;       /// The token array for iteration.
572     bool                        mbSkipEmpty;    /// Ignore empty strings.
573     bool                        mbOk;           /// true = correct token or end of token array.
574 };
575 
576 const String* ScStringTokenIterator::First()
577 {
578     mrTokArr.Reset();
579     mbOk = true;
580     return Next();
581 }
582 
583 const String* ScStringTokenIterator::Next()
584 {
585     if( !mbOk )
586         return NULL;
587 
588     // seek to next non-separator token
589     const FormulaToken* pToken = mrTokArr.NextNoSpaces();
590     while( pToken && (pToken->GetOpCode() == ocSep) )
591         pToken = mrTokArr.NextNoSpaces();
592 
593     mbOk = !pToken || (pToken->GetType() == formula::svString);
594     const String* pString = (mbOk && pToken) ? &pToken->GetString() : NULL;
595     // string found but empty -> get next token; otherwise return it
596     return (mbSkipEmpty && pString && !pString->Len()) ? Next() : pString;
597 }
598 
599 // ----------------------------------------------------------------------------
600 
601 /** Returns the number format of the passed cell, or the standard format. */
602 sal_uLong lclGetCellFormat( ScDocument& rDoc, const ScAddress& rPos )
603 {
604     const ScPatternAttr* pPattern = rDoc.GetPattern( rPos.Col(), rPos.Row(), rPos.Tab() );
605     if( !pPattern )
606         pPattern = rDoc.GetDefPattern();
607     return pPattern->GetNumberFormat( rDoc.GetFormatTable() );
608 }
609 
610 /** Inserts the passed string object. Always takes ownership. pData is invalid after this call! */
611 void lclInsertStringToCollection( TypedScStrCollection& rStrColl, TypedStrData* pData, bool bSorted )
612 {
613     if( !(bSorted ? rStrColl.Insert( pData ) : rStrColl.AtInsert( rStrColl.GetCount(), pData )) )
614         delete pData;
615 }
616 
617 } // namespace
618 
619 // ----------------------------------------------------------------------------
620 
621 bool ScValidationData::HasSelectionList() const
622 {
623     return (eDataMode == SC_VALID_LIST) && (mnListType != ValidListType::INVISIBLE);
624 }
625 
626 bool ScValidationData::GetSelectionFromFormula( TypedScStrCollection* pStrings,
627                                                 ScBaseCell* pCell,
628                                                 const ScAddress& rPos,
629                                                 const ScTokenArray& rTokArr,
630                                                 int& rMatch ) const
631 {
632     bool bOk = true;
633 
634     // pDoc is private in condition, use an accessor and a long winded name.
635     ScDocument* pDocument = GetDocument();
636     if( NULL == pDocument )
637         return false;
638 
639     ScFormulaCell aValidationSrc( pDocument, rPos, &rTokArr,
640            formula::FormulaGrammar::GRAM_DEFAULT, MM_FORMULA);
641 
642     // Make sure the formula gets interpreted and a result is delivered,
643     // regardless of the AutoCalc setting.
644     aValidationSrc.Interpret();
645 
646     ScMatrixRef xMatRef;
647     const ScMatrix *pValues = aValidationSrc.GetMatrix();
648     if (!pValues)
649     {
650         // The somewhat nasty case of either an error occured, or the
651         // dereferenced value of a single cell reference or an immediate result
652         // is stored as a single value.
653 
654         // Use an interim matrix to create the TypedStrData below.
655         xMatRef = new ScMatrix(1,1);
656 
657         sal_uInt16 nErrCode = aValidationSrc.GetErrCode();
658         if (nErrCode)
659         {
660             /* TODO : to use later in an alert box?
661              * String rStrResult = "...";
662              * rStrResult += ScGlobal::GetLongErrorString(nErrCode);
663              */
664 
665             xMatRef->PutError( nErrCode, 0);
666             bOk = false;
667         }
668         else if (aValidationSrc.HasValueData())
669             xMatRef->PutDouble( aValidationSrc.GetValue(), 0);
670         else
671         {
672             String aStr;
673             aValidationSrc.GetString( aStr);
674             xMatRef->PutString( aStr, 0);
675         }
676 
677         pValues = xMatRef;
678     }
679 
680     // which index matched.  We will want it eventually to pre-select that item.
681     rMatch = -1;
682 
683     SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable();
684 
685     bool    bSortList = (mnListType == ValidListType::SORTEDASCENDING);
686     SCSIZE  nCol, nRow, nCols, nRows, n = 0;
687     pValues->GetDimensions( nCols, nRows );
688 
689     sal_Bool bRef = sal_False;
690     ScRange aRange;
691 
692     ScTokenArray* pArr = (ScTokenArray*) &rTokArr;
693     pArr->Reset();
694     ScToken* t = NULL;
695     if (pArr->GetLen() == 1 && (t = static_cast<ScToken*>(pArr->GetNextReferenceOrName())) != NULL)
696     {
697         if (t->GetOpCode() == ocDBArea)
698         {
699             if( ScDBData* pDBData = pDocument->GetDBCollection()->FindIndex( t->GetIndex() ) )
700             {
701                 pDBData->GetArea(aRange);
702                 bRef = sal_True;
703             }
704         }
705         else if (t->GetOpCode() == ocName)
706         {
707             ScRangeData* pName = pDocument->GetRangeName()->FindIndex( t->GetIndex() );
708             if (pName && pName->IsReference(aRange))
709             {
710                 bRef = sal_True;
711             }
712         }
713         else if (t->GetType() != svIndex)
714         {
715             t->CalcAbsIfRel(rPos);
716             if (pArr->IsValidReference(aRange))
717             {
718                 bRef = sal_True;
719             }
720         }
721     }
722 
723     /* XL artificially limits things to a single col or row in the UI but does
724      * not list the constraint in MOOXml. If a defined name or INDIRECT
725      * resulting in 1D is entered in the UI and the definition later modified
726      * to 2D, it is evaluated fine and also stored and loaded.  Lets get ahead
727      * of the curve and support 2d. In XL, values are listed row-wise, do the
728      * same. */
729     for( nRow = 0; nRow < nRows ; nRow++ )
730     {
731         for( nCol = 0; nCol < nCols ; nCol++ )
732         {
733             ScTokenArray         aCondTokArr;
734             TypedStrData*        pEntry = NULL;
735             ScMatValType         nMatValType;
736             String               aValStr;
737             const ScMatrixValue* pMatVal = pValues->Get( nCol, nRow, nMatValType);
738 
739             // strings and empties
740             if( NULL == pMatVal || ScMatrix::IsNonValueType( nMatValType ) )
741             {
742                 if( NULL != pMatVal )
743                     aValStr = pMatVal->GetString();
744 
745                 if( NULL != pStrings )
746                     pEntry = new TypedStrData( aValStr, 0.0, SC_STRTYPE_STANDARD);
747 
748                 if( pCell && rMatch < 0 )
749                     aCondTokArr.AddString( aValStr );
750             }
751             else
752             {
753                 sal_uInt16 nErr = pMatVal->GetError();
754 
755                 if( 0 != nErr )
756                 {
757                     aValStr = ScGlobal::GetErrorString( nErr );
758                 }
759                 else
760                 {
761                     // FIXME FIXME FIXME
762                     // Feature regression.  Date formats are lost passing through the matrix
763                     //pFormatter->GetInputLineString( pMatVal->fVal, 0, aValStr );
764                     //For external reference and a formula that results in an area or array, date formats are still lost.
765                     if ( bRef )
766                     {
767                         pDocument->GetInputString((SCCOL)(nCol+aRange.aStart.Col()),
768                             (SCROW)(nRow+aRange.aStart.Row()), aRange.aStart.Tab() , aValStr);
769                     }
770                     else
771                         pFormatter->GetInputLineString( pMatVal->fVal, 0, aValStr );
772                 }
773 
774                 if( pCell && rMatch < 0 )
775                 {
776                     // I am not sure errors will work here, but a user can no
777                     // manually enter an error yet so the point is somewhat moot.
778                     aCondTokArr.AddDouble( pMatVal->fVal );
779                 }
780                 if( NULL != pStrings )
781                     pEntry = new TypedStrData( aValStr, pMatVal->fVal, SC_STRTYPE_VALUE);
782             }
783 
784             if( rMatch < 0 && NULL != pCell && IsEqualToTokenArray( pCell, rPos, aCondTokArr ) )
785             {
786                 rMatch = n;
787                 // short circuit on the first match if not filling the list
788                 if( NULL == pStrings )
789                     return true;
790             }
791 
792             if( NULL != pEntry )
793             {
794                 lclInsertStringToCollection( *pStrings, pEntry, bSortList );
795                 n++;
796             }
797         }
798     }
799 
800     // In case of no match needed and an error occurred, return that error
801     // entry as valid instead of silently failing.
802     return bOk || NULL == pCell;
803 }
804 
805 bool ScValidationData::FillSelectionList( TypedScStrCollection& rStrColl, const ScAddress& rPos ) const
806 {
807     bool bOk = false;
808 
809     if( HasSelectionList() )
810     {
811         ::std::auto_ptr< ScTokenArray > pTokArr( CreateTokenArry( 0 ) );
812 
813         // *** try if formula is a string list ***
814 
815         bool bSortList = (mnListType == ValidListType::SORTEDASCENDING);
816         sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos );
817         ScStringTokenIterator aIt( *pTokArr );
818         for( const String* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next() )
819         {
820             double fValue;
821             bool bIsValue = GetDocument()->GetFormatTable()->IsNumberFormat( *pString, nFormat, fValue );
822             TypedStrData* pData = new TypedStrData( *pString, fValue, bIsValue ? SC_STRTYPE_VALUE : SC_STRTYPE_STANDARD );
823             lclInsertStringToCollection( rStrColl, pData, bSortList );
824         }
825         bOk = aIt.Ok();
826 
827         // *** if not a string list, try if formula results in a cell range or
828         // anything else we recognize as valid ***
829 
830         if (!bOk)
831         {
832             int nMatch;
833             bOk = GetSelectionFromFormula( &rStrColl, NULL, rPos, *pTokArr, nMatch );
834         }
835     }
836 
837     return bOk;
838 }
839 
840 // ----------------------------------------------------------------------------
841 
842 bool ScValidationData::IsEqualToTokenArray( ScBaseCell* pCell, const ScAddress& rPos, const ScTokenArray& rTokArr ) const
843 {
844     // create a condition entry that tests on equality and set the passed token array
845     ScConditionEntry aCondEntry( SC_COND_EQUAL, &rTokArr, NULL, GetDocument(), rPos );
846     return aCondEntry.IsCellValid( pCell, rPos );
847 }
848 
849 bool ScValidationData::IsListValid( ScBaseCell* pCell, const ScAddress& rPos ) const
850 {
851     bool bIsValid = false;
852 
853     /*  Compare input cell with all supported tokens from the formula.
854         Currently a formula may contain:
855         1)  A list of strings (at least one string).
856         2)  A single cell or range reference.
857         3)  A single defined name (must contain a cell/range reference, another
858             name, or DB range, or a formula resulting in a cell/range reference
859             or matrix/array).
860         4)  A single database range.
861         5)  A formula resulting in a cell/range reference or matrix/array.
862     */
863 
864     ::std::auto_ptr< ScTokenArray > pTokArr( CreateTokenArry( 0 ) );
865 
866     // *** try if formula is a string list ***
867 
868     sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos );
869     ScStringTokenIterator aIt( *pTokArr );
870     for( const String* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next() )
871     {
872         /*  Do not break the loop, if a valid string has been found.
873             This is to find invalid tokens following in the formula. */
874         if( !bIsValid )
875         {
876             // create a formula containing a single string or number
877             ScTokenArray aCondTokArr;
878             double fValue;
879             if( GetDocument()->GetFormatTable()->IsNumberFormat( *pString, nFormat, fValue ) )
880                 aCondTokArr.AddDouble( fValue );
881             else
882                 aCondTokArr.AddString( *pString );
883 
884             bIsValid = IsEqualToTokenArray( pCell, rPos, aCondTokArr );
885         }
886     }
887 
888     if( !aIt.Ok() )
889         bIsValid = false;
890 
891     // *** if not a string list, try if formula results in a cell range or
892     // anything else we recognize as valid ***
893 
894     if (!bIsValid)
895     {
896         int nMatch;
897         bIsValid = GetSelectionFromFormula( NULL, pCell, rPos, *pTokArr, nMatch );
898         bIsValid = bIsValid && nMatch >= 0;
899     }
900 
901     return bIsValid;
902 }
903 
904 // ============================================================================
905 // ============================================================================
906 
907 ScValidationDataList::ScValidationDataList(const ScValidationDataList& rList) :
908     ScValidationEntries_Impl()
909 {
910 	//	fuer Ref-Undo - echte Kopie mit neuen Tokens!
911 
912 	sal_uInt16 nCount = rList.Count();
913 
914 	for (sal_uInt16 i=0; i<nCount; i++)
915 		InsertNew( rList[i]->Clone() );
916 
917 	//!		sortierte Eintraege aus rList schneller einfuegen ???
918 }
919 
920 ScValidationDataList::ScValidationDataList(ScDocument* pNewDoc,
921 											const ScValidationDataList& rList)
922 {
923 	//	fuer neues Dokument - echte Kopie mit neuen Tokens!
924 
925 	sal_uInt16 nCount = rList.Count();
926 
927 	for (sal_uInt16 i=0; i<nCount; i++)
928 		InsertNew( rList[i]->Clone(pNewDoc) );
929 
930 	//!		sortierte Eintraege aus rList schneller einfuegen ???
931 }
932 
933 ScValidationData* ScValidationDataList::GetData( sal_uInt32 nKey )
934 {
935 	//!	binaer suchen
936 
937 	sal_uInt16 nCount = Count();
938 	for (sal_uInt16 i=0; i<nCount; i++)
939 		if ((*this)[i]->GetKey() == nKey)
940 			return (*this)[i];
941 
942 	DBG_ERROR("ScValidationDataList: Eintrag nicht gefunden");
943 	return NULL;
944 }
945 
946 void ScValidationDataList::CompileXML()
947 {
948 	sal_uInt16 nCount = Count();
949 	for (sal_uInt16 i=0; i<nCount; i++)
950 		(*this)[i]->CompileXML();
951 }
952 
953 void ScValidationDataList::UpdateReference( UpdateRefMode eUpdateRefMode,
954 								const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
955 {
956     sal_uInt16 nCount = Count();
957     for (sal_uInt16 i=0; i<nCount; i++)
958         (*this)[i]->UpdateReference( eUpdateRefMode, rRange, nDx, nDy, nDz);
959 }
960 
961 void ScValidationDataList::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos )
962 {
963     sal_uInt16 nCount = Count();
964     for (sal_uInt16 i=0; i<nCount; i++)
965 		(*this)[i]->UpdateMoveTab( nOldPos, nNewPos );
966 }
967 
968 bool ScValidationDataList::MarkUsedExternalReferences() const
969 {
970     bool bAllMarked = false;
971 	sal_uInt16 nCount = Count();
972 	for (sal_uInt16 i=0; !bAllMarked && i<nCount; i++)
973 		bAllMarked = (*this)[i]->MarkUsedExternalReferences();
974     return bAllMarked;
975 }
976 
977 sal_Bool ScValidationDataList::operator==( const ScValidationDataList& r ) const
978 {
979 	// fuer Ref-Undo - interne Variablen werden nicht verglichen
980 
981 	sal_uInt16 nCount = Count();
982 	sal_Bool bEqual = ( nCount == r.Count() );
983 	for (sal_uInt16 i=0; i<nCount && bEqual; i++)			// Eintraege sind sortiert
984 		if ( !(*this)[i]->EqualEntries(*r[i]) )			// Eintraege unterschiedlich ?
985 			bEqual = sal_False;
986 
987 	return bEqual;
988 }
989 
990