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