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