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