xref: /AOO41X/main/sc/source/core/data/validat.cxx (revision 8809db7a87f97847b57a57f4cd2b0104b2b83182)
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