1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <o3tl/any.hxx>
22 #include <osl/diagnose.h>
23 #include <svl/numformat.hxx>
24 #include <com/sun/star/sdbc/DataType.hpp>
29 #include <IDocumentFieldsAccess.hxx>
34 #include <unofldmid.h>
35 #include <o3tl/string_view.hxx>
38 using namespace ::com::sun::star::sdbc
;
39 using namespace ::com::sun::star
;
41 /// replace database separator by dots for display
42 static OUString
lcl_DBSeparatorConvert(const OUString
& aContent
)
44 return aContent
.replaceAll(OUStringChar(DB_DELIM
), ".");
47 // database field type
49 SwDBFieldType::SwDBFieldType(SwDoc
* pDocPtr
, const OUString
& rNam
, SwDBData aDBData
) :
50 SwValueFieldType( pDocPtr
, SwFieldIds::Database
),
51 m_aDBData(std::move(aDBData
)),
56 if(!m_aDBData
.sDataSource
.isEmpty() || !m_aDBData
.sCommand
.isEmpty())
58 m_sName
= m_aDBData
.sDataSource
59 + OUStringChar(DB_DELIM
)
61 + OUStringChar(DB_DELIM
)
66 SwDBFieldType::~SwDBFieldType()
70 std::unique_ptr
<SwFieldType
> SwDBFieldType::Copy() const
72 return std::make_unique
<SwDBFieldType
>(GetDoc(), m_sColumn
, m_aDBData
);
75 OUString
SwDBFieldType::GetName() const
80 void SwDBFieldType::ReleaseRef()
82 OSL_ENSURE(m_nRefCnt
> 0, "RefCount < 0!");
88 for (auto const & pFieldType
: *GetDoc()->getIDocumentFieldsAccess().GetFieldTypes())
90 if (pFieldType
.get() == this)
94 if (nPos
< GetDoc()->getIDocumentFieldsAccess().GetFieldTypes()->size())
96 GetDoc()->getIDocumentFieldsAccess().RemoveFieldType(nPos
);
101 void SwDBFieldType::QueryValue( uno::Any
& rAny
, sal_uInt16 nWhichId
) const
105 case FIELD_PROP_PAR1
:
106 rAny
<<= m_aDBData
.sDataSource
;
108 case FIELD_PROP_PAR2
:
109 rAny
<<= m_aDBData
.sCommand
;
111 case FIELD_PROP_PAR3
:
114 case FIELD_PROP_SHORT1
:
115 rAny
<<= m_aDBData
.nCommandType
;
122 void SwDBFieldType::PutValue( const uno::Any
& rAny
, sal_uInt16 nWhichId
)
126 case FIELD_PROP_PAR1
:
127 rAny
>>= m_aDBData
.sDataSource
;
129 case FIELD_PROP_PAR2
:
130 rAny
>>= m_aDBData
.sCommand
;
132 case FIELD_PROP_PAR3
:
136 if( sTmp
!= m_sColumn
)
139 std::vector
<SwFormatField
*> vFields
;
140 GatherFields(vFields
);
141 for(auto pFormatField
: vFields
)
143 SwDBField
* pDBField
= static_cast<SwDBField
*>(pFormatField
->GetField());
144 pDBField
->ClearInitialized();
145 pDBField
->InitContent();
150 case FIELD_PROP_SHORT1
:
151 rAny
>>= m_aDBData
.nCommandType
;
160 SwDBField::SwDBField(SwDBFieldType
* pTyp
, sal_uInt32 nFormat
)
161 : SwValueField(pTyp
, nFormat
),
163 m_bIsInBodyText(true),
164 m_bValidValue(false),
165 m_bInitialized(false)
168 static_cast<SwDBFieldType
*>(GetTyp())->AddRef();
172 SwDBField::~SwDBField()
175 static_cast<SwDBFieldType
*>(GetTyp())->ReleaseRef();
178 void SwDBField::InitContent()
180 if (!IsInitialized())
182 m_aContent
= "<" + static_cast<const SwDBFieldType
*>(GetTyp())->GetColumnName() + ">";
186 void SwDBField::InitContent(const OUString
& rExpansion
)
188 if (rExpansion
.startsWith("<") && rExpansion
.endsWith(">"))
190 const OUString
sColumn( rExpansion
.copy( 1, rExpansion
.getLength() - 2 ) );
191 if( ::GetAppCmpStrIgnore().isEqual( sColumn
,
192 static_cast<SwDBFieldType
*>(GetTyp())->GetColumnName() ))
198 SetExpansion( rExpansion
);
201 OUString
SwDBField::ExpandImpl(SwRootFrame
const*const) const
203 if(0 ==(GetSubType() & nsSwExtendedSubType::SUB_INVISIBLE
))
204 return lcl_DBSeparatorConvert(m_aContent
);
208 std::unique_ptr
<SwField
> SwDBField::Copy() const
210 std::unique_ptr
<SwDBField
> pTmp(new SwDBField(static_cast<SwDBFieldType
*>(GetTyp()), GetFormat()));
211 pTmp
->m_aContent
= m_aContent
;
212 pTmp
->m_bIsInBodyText
= m_bIsInBodyText
;
213 pTmp
->m_bValidValue
= m_bValidValue
;
214 pTmp
->m_bInitialized
= m_bInitialized
;
215 pTmp
->m_nSubType
= m_nSubType
;
216 pTmp
->SetValue(GetValue());
217 pTmp
->m_sFieldCode
= m_sFieldCode
;
219 return std::unique_ptr
<SwField
>(pTmp
.release());
222 OUString
SwDBField::GetFieldName() const
224 const OUString rDBName
= static_cast<SwDBFieldType
*>(GetTyp())->GetName();
226 OUString
sContent( rDBName
.getToken(0, DB_DELIM
) );
228 if (sContent
.getLength() > 1)
230 sContent
+= OUStringChar(DB_DELIM
)
231 + o3tl::getToken(rDBName
, 1, DB_DELIM
)
232 + OUStringChar(DB_DELIM
)
233 + o3tl::getToken(rDBName
, 2, DB_DELIM
);
235 return lcl_DBSeparatorConvert(sContent
);
238 void SwDBField::ChgValue( double d
, bool bVal
)
240 m_bValidValue
= bVal
;
244 m_aContent
= static_cast<SwValueFieldType
*>(GetTyp())->ExpandValue(d
, GetFormat(), GetLanguage());
247 SwFieldType
* SwDBField::ChgTyp( SwFieldType
* pNewType
)
249 SwFieldType
* pOld
= SwValueField::ChgTyp( pNewType
);
251 static_cast<SwDBFieldType
*>(pNewType
)->AddRef();
252 static_cast<SwDBFieldType
*>(pOld
)->ReleaseRef();
257 bool SwDBField::FormatValue( SvNumberFormatter
const * pDocFormatter
, OUString
const &aString
, sal_uInt32 nFormat
,
258 double &aNumber
, sal_Int32 nColumnType
, SwDBField
*pField
)
260 bool bValidValue
= false;
262 if( DBL_MAX
!= aNumber
)
264 if( DataType::DATE
== nColumnType
|| DataType::TIME
== nColumnType
||
265 DataType::TIMESTAMP
== nColumnType
)
267 Date
aStandard( 1, 1, 1900 );
268 if( pDocFormatter
->GetNullDate() != aStandard
)
269 aNumber
+= (aStandard
- pDocFormatter
->GetNullDate());
273 pField
->SetValue( aNumber
);
278 aVal
.PutString( aString
);
280 if (aVal
.IsNumeric())
283 pField
->SetValue(aVal
.GetDouble());
285 aNumber
= aVal
.GetDouble();
287 if (nFormat
&& nFormat
!= SAL_MAX_UINT32
&& !pDocFormatter
->IsTextFormat(nFormat
))
288 bValidValue
= true; // because of bug #60339 not for all strings
292 // if string length > 0 then true, else false
294 pField
->SetValue(aString
.isEmpty() ? 0 : 1);
296 aNumber
= aString
.isEmpty() ? 0 : 1;
303 /// get current field value and cache it
304 void SwDBField::Evaluate()
306 SwDBManager
* pMgr
= GetDoc()->GetDBManager();
309 m_bValidValue
= false;
310 double nValue
= DBL_MAX
;
311 const SwDBData
& aTmpData
= GetDBData();
313 if(!pMgr
|| !pMgr
->IsDataSourceOpen(aTmpData
.sDataSource
, aTmpData
.sCommand
, true))
316 sal_uInt32 nFormat
= 0;
318 // search corresponding column name
319 OUString
aColNm( static_cast<SwDBFieldType
*>(GetTyp())->GetColumnName() );
321 SvNumberFormatter
* pDocFormatter
= GetDoc()->GetNumberFormatter();
322 pMgr
->GetMergeColumnCnt(aColNm
, GetLanguage(), m_aContent
, &nValue
);
323 if( !( m_nSubType
& nsSwExtendedSubType::SUB_OWN_FMT
) )
325 nFormat
= pMgr
->GetColumnFormat( aTmpData
.sDataSource
, aTmpData
.sCommand
,
326 aColNm
, pDocFormatter
, GetLanguage() );
327 SetFormat( nFormat
);
330 sal_Int32 nColumnType
= nValue
== DBL_MAX
332 : pMgr
->GetColumnType(aTmpData
.sDataSource
, aTmpData
.sCommand
, aColNm
);
334 m_bValidValue
= FormatValue( pDocFormatter
, m_aContent
, nFormat
, nValue
, nColumnType
, this );
336 if( DBL_MAX
!= nValue
)
337 m_aContent
= static_cast<SwValueFieldType
*>(GetTyp())->ExpandValue(nValue
, GetFormat(), GetLanguage());
339 m_bInitialized
= true;
343 OUString
SwDBField::GetPar1() const
345 return static_cast<const SwDBFieldType
*>(GetTyp())->GetName();
348 sal_uInt16
SwDBField::GetSubType() const
353 void SwDBField::SetSubType(sal_uInt16 nType
)
358 bool SwDBField::QueryValue( uno::Any
& rAny
, sal_uInt16 nWhichId
) const
362 case FIELD_PROP_BOOL1
:
363 rAny
<<= 0 == (GetSubType()&nsSwExtendedSubType::SUB_OWN_FMT
);
365 case FIELD_PROP_BOOL2
:
366 rAny
<<= 0 == (GetSubType() & nsSwExtendedSubType::SUB_INVISIBLE
);
368 case FIELD_PROP_FORMAT
:
369 rAny
<<= static_cast<sal_Int32
>(GetFormat());
371 case FIELD_PROP_PAR1
:
374 case FIELD_PROP_PAR2
:
375 rAny
<<= m_sFieldCode
;
378 OSL_FAIL("illegal property");
383 bool SwDBField::PutValue( const uno::Any
& rAny
, sal_uInt16 nWhichId
)
387 case FIELD_PROP_BOOL1
:
388 if( *o3tl::doAccess
<bool>(rAny
) )
389 SetSubType(GetSubType()&~nsSwExtendedSubType::SUB_OWN_FMT
);
391 SetSubType(GetSubType()|nsSwExtendedSubType::SUB_OWN_FMT
);
393 case FIELD_PROP_BOOL2
:
395 sal_uInt16 nSubTyp
= GetSubType();
396 bool bVisible
= false;
397 if(!(rAny
>>= bVisible
))
400 nSubTyp
&= ~nsSwExtendedSubType::SUB_INVISIBLE
;
402 nSubTyp
|= nsSwExtendedSubType::SUB_INVISIBLE
;
404 //invalidate text node
405 auto pType
= GetTyp();
408 std::vector
<SwFormatField
*> vFields
;
409 pType
->GatherFields(vFields
, false);
410 for(auto pFormatField
: vFields
)
412 SwTextField
* pTextField
= pFormatField
->GetTextField();
413 if(pTextField
&& static_cast<SwDBField
*>(pFormatField
->GetField()) == this)
416 pTextField
->NotifyContentChange(*pFormatField
);
422 case FIELD_PROP_FORMAT
:
429 case FIELD_PROP_PAR1
:
432 case FIELD_PROP_PAR2
:
433 rAny
>>= m_sFieldCode
;
436 OSL_FAIL("illegal property");
441 // base class for all further database fields
443 SwDBNameInfField::SwDBNameInfField(SwFieldType
* pTyp
, SwDBData aDBData
, sal_uInt32 nFormat
) :
444 SwField(pTyp
, nFormat
),
445 m_aDBData(std::move(aDBData
)),
450 SwDBData
SwDBNameInfField::GetDBData(SwDoc
* pDoc
)
453 if(!m_aDBData
.sDataSource
.isEmpty())
456 aRet
= pDoc
->GetDBData();
460 void SwDBNameInfField::SetDBData(const SwDBData
& rDBData
)
465 OUString
SwDBNameInfField::GetFieldName() const
467 OUString
sStr( SwField::GetFieldName() );
468 if (!m_aDBData
.sDataSource
.isEmpty())
471 + m_aDBData
.sDataSource
472 + OUStringChar(DB_DELIM
)
473 + m_aDBData
.sCommand
;
475 return lcl_DBSeparatorConvert(sStr
);
478 bool SwDBNameInfField::QueryValue( uno::Any
& rAny
, sal_uInt16 nWhichId
) const
482 case FIELD_PROP_PAR1
:
483 rAny
<<= m_aDBData
.sDataSource
;
485 case FIELD_PROP_PAR2
:
486 rAny
<<= m_aDBData
.sCommand
;
488 case FIELD_PROP_SHORT1
:
489 rAny
<<= m_aDBData
.nCommandType
;
491 case FIELD_PROP_BOOL2
:
492 rAny
<<= 0 == (GetSubType() & nsSwExtendedSubType::SUB_INVISIBLE
);
500 bool SwDBNameInfField::PutValue( const uno::Any
& rAny
, sal_uInt16 nWhichId
)
504 case FIELD_PROP_PAR1
:
505 rAny
>>= m_aDBData
.sDataSource
;
507 case FIELD_PROP_PAR2
:
508 rAny
>>= m_aDBData
.sCommand
;
510 case FIELD_PROP_SHORT1
:
511 rAny
>>= m_aDBData
.nCommandType
;
513 case FIELD_PROP_BOOL2
:
515 sal_uInt16 nSubTyp
= GetSubType();
516 bool bVisible
= false;
517 if(!(rAny
>>= bVisible
))
520 nSubTyp
&= ~nsSwExtendedSubType::SUB_INVISIBLE
;
522 nSubTyp
|= nsSwExtendedSubType::SUB_INVISIBLE
;
532 sal_uInt16
SwDBNameInfField::GetSubType() const
537 void SwDBNameInfField::SetSubType(sal_uInt16 nType
)
544 SwDBNextSetFieldType::SwDBNextSetFieldType()
545 : SwFieldType( SwFieldIds::DbNextSet
)
549 std::unique_ptr
<SwFieldType
> SwDBNextSetFieldType::Copy() const
551 return std::make_unique
<SwDBNextSetFieldType
>();
556 SwDBNextSetField::SwDBNextSetField(SwDBNextSetFieldType
* pTyp
,
558 const SwDBData
& rDBData
) :
559 SwDBNameInfField(pTyp
, rDBData
), m_aCond(std::move(aCond
)), m_bCondValid(true)
562 OUString
SwDBNextSetField::ExpandImpl(SwRootFrame
const*const) const
567 std::unique_ptr
<SwField
> SwDBNextSetField::Copy() const
569 std::unique_ptr
<SwDBNextSetField
> pTmp(new SwDBNextSetField(static_cast<SwDBNextSetFieldType
*>(GetTyp()),
570 m_aCond
, GetDBData()));
571 pTmp
->SetSubType(GetSubType());
572 pTmp
->m_bCondValid
= m_bCondValid
;
573 return std::unique_ptr
<SwField
>(pTmp
.release());
576 void SwDBNextSetField::Evaluate(const SwDoc
& rDoc
)
578 SwDBManager
* pMgr
= rDoc
.GetDBManager();
579 const SwDBData
& rData
= GetDBData();
581 !pMgr
|| !pMgr
->IsDataSourceOpen(rData
.sDataSource
, rData
.sCommand
, false))
583 pMgr
->ToNextRecord(rData
.sDataSource
, rData
.sCommand
);
587 OUString
SwDBNextSetField::GetPar1() const
593 void SwDBNextSetField::SetPar1(const OUString
& rStr
)
598 bool SwDBNextSetField::QueryValue( uno::Any
& rAny
, sal_uInt16 nWhichId
) const
603 case FIELD_PROP_PAR3
:
607 bRet
= SwDBNameInfField::QueryValue( rAny
, nWhichId
);
612 bool SwDBNextSetField::PutValue( const uno::Any
& rAny
, sal_uInt16 nWhichId
)
617 case FIELD_PROP_PAR3
:
621 bRet
= SwDBNameInfField::PutValue( rAny
, nWhichId
);
626 // dataset with certain ID
628 SwDBNumSetFieldType::SwDBNumSetFieldType() :
629 SwFieldType( SwFieldIds::DbNumSet
)
633 std::unique_ptr
<SwFieldType
> SwDBNumSetFieldType::Copy() const
635 return std::make_unique
<SwDBNumSetFieldType
>();
638 SwDBNumSetField::SwDBNumSetField(SwDBNumSetFieldType
* pTyp
,
641 const SwDBData
& rDBData
) :
642 SwDBNameInfField(pTyp
, rDBData
),
643 m_aCond(std::move(aCond
)),
644 m_aPar2(std::move(aDBNum
)),
648 OUString
SwDBNumSetField::ExpandImpl(SwRootFrame
const*const) const
653 std::unique_ptr
<SwField
> SwDBNumSetField::Copy() const
655 std::unique_ptr
<SwDBNumSetField
> pTmp(new SwDBNumSetField(static_cast<SwDBNumSetFieldType
*>(GetTyp()),
656 m_aCond
, m_aPar2
, GetDBData()));
657 pTmp
->m_bCondValid
= m_bCondValid
;
658 pTmp
->SetSubType(GetSubType());
659 return std::unique_ptr
<SwField
>(pTmp
.release());
662 void SwDBNumSetField::Evaluate(const SwDoc
& rDoc
)
664 SwDBManager
* pMgr
= rDoc
.GetDBManager();
665 const SwDBData
& aTmpData
= GetDBData();
667 if( m_bCondValid
&& pMgr
&& pMgr
->IsInMerge() &&
668 pMgr
->IsDataSourceOpen(aTmpData
.sDataSource
, aTmpData
.sCommand
, true))
669 { // condition OK -> adjust current Set
670 pMgr
->ToRecordId(std::max(o3tl::narrowing
<sal_uInt16
>(m_aPar2
.toInt32()), sal_uInt16(1))-1);
675 OUString
SwDBNumSetField::GetPar1() const
681 void SwDBNumSetField::SetPar1(const OUString
& rStr
)
687 OUString
SwDBNumSetField::GetPar2() const
693 void SwDBNumSetField::SetPar2(const OUString
& rStr
)
698 bool SwDBNumSetField::QueryValue( uno::Any
& rAny
, sal_uInt16 nWhichId
) const
703 case FIELD_PROP_PAR3
:
706 case FIELD_PROP_FORMAT
:
707 rAny
<<= m_aPar2
.toInt32();
710 bRet
= SwDBNameInfField::QueryValue(rAny
, nWhichId
);
715 bool SwDBNumSetField::PutValue( const uno::Any
& rAny
, sal_uInt16 nWhichId
)
720 case FIELD_PROP_PAR3
:
723 case FIELD_PROP_FORMAT
:
727 m_aPar2
= OUString::number(nVal
);
731 bRet
= SwDBNameInfField::PutValue(rAny
, nWhichId
);
736 SwDBNameFieldType::SwDBNameFieldType(SwDoc
* pDocument
)
737 : SwFieldType( SwFieldIds::DatabaseName
)
742 OUString
SwDBNameFieldType::Expand() const
744 const SwDBData aData
= m_pDoc
->GetDBData();
745 return aData
.sDataSource
+ "." + aData
.sCommand
;
748 std::unique_ptr
<SwFieldType
> SwDBNameFieldType::Copy() const
750 return std::make_unique
<SwDBNameFieldType
>(m_pDoc
);
753 // name of the connected database
755 SwDBNameField::SwDBNameField(SwDBNameFieldType
* pTyp
, const SwDBData
& rDBData
)
756 : SwDBNameInfField(pTyp
, rDBData
, 0)
759 OUString
SwDBNameField::ExpandImpl(SwRootFrame
const*const) const
761 if(0 ==(GetSubType() & nsSwExtendedSubType::SUB_INVISIBLE
))
762 return static_cast<SwDBNameFieldType
*>(GetTyp())->Expand();
766 std::unique_ptr
<SwField
> SwDBNameField::Copy() const
768 std::unique_ptr
<SwDBNameField
> pTmp(new SwDBNameField(static_cast<SwDBNameFieldType
*>(GetTyp()), GetDBData()));
769 pTmp
->ChangeFormat(GetFormat());
770 pTmp
->SetLanguage(GetLanguage());
771 pTmp
->SetSubType(GetSubType());
772 return std::unique_ptr
<SwField
>(pTmp
.release());
775 bool SwDBNameField::QueryValue( uno::Any
& rAny
, sal_uInt16 nWhichId
) const
777 return SwDBNameInfField::QueryValue(rAny
, nWhichId
);
780 bool SwDBNameField::PutValue( const uno::Any
& rAny
, sal_uInt16 nWhichId
)
782 return SwDBNameInfField::PutValue(rAny
, nWhichId
);
785 SwDBSetNumberFieldType::SwDBSetNumberFieldType()
786 : SwFieldType( SwFieldIds::DbSetNumber
)
790 std::unique_ptr
<SwFieldType
> SwDBSetNumberFieldType::Copy() const
792 return std::make_unique
<SwDBSetNumberFieldType
>();
795 // set-number of the connected database
797 SwDBSetNumberField::SwDBSetNumberField(SwDBSetNumberFieldType
* pTyp
,
798 const SwDBData
& rDBData
,
800 : SwDBNameInfField(pTyp
, rDBData
, nFormat
), m_nNumber(0)
803 OUString
SwDBSetNumberField::ExpandImpl(SwRootFrame
const*const) const
805 if(0 !=(GetSubType() & nsSwExtendedSubType::SUB_INVISIBLE
) || m_nNumber
== 0)
807 return FormatNumber(m_nNumber
, static_cast<SvxNumType
>(GetFormat()));
810 void SwDBSetNumberField::Evaluate(const SwDoc
& rDoc
)
812 SwDBManager
* pMgr
= rDoc
.GetDBManager();
814 const SwDBData
& aTmpData
= GetDBData();
815 if (!pMgr
|| !pMgr
->IsInMerge() ||
816 !pMgr
->IsDataSourceOpen(aTmpData
.sDataSource
, aTmpData
.sCommand
, false))
818 m_nNumber
= pMgr
->GetSelectedRecordId();
821 std::unique_ptr
<SwField
> SwDBSetNumberField::Copy() const
823 std::unique_ptr
<SwDBSetNumberField
> pTmp(
824 new SwDBSetNumberField(static_cast<SwDBSetNumberFieldType
*>(GetTyp()), GetDBData(), GetFormat()));
825 pTmp
->SetLanguage(GetLanguage());
826 pTmp
->SetSetNumber(m_nNumber
);
827 pTmp
->SetSubType(GetSubType());
828 return std::unique_ptr
<SwField
>(pTmp
.release());
831 bool SwDBSetNumberField::QueryValue( uno::Any
& rAny
, sal_uInt16 nWhichId
) const
836 case FIELD_PROP_USHORT1
:
837 rAny
<<= static_cast<sal_Int16
>(GetFormat());
839 case FIELD_PROP_FORMAT
:
843 bRet
= SwDBNameInfField::QueryValue( rAny
, nWhichId
);
848 bool SwDBSetNumberField::PutValue( const uno::Any
& rAny
, sal_uInt16 nWhichId
)
853 case FIELD_PROP_USHORT1
:
857 if(nSet
< css::style::NumberingType::NUMBER_NONE
)
861 case FIELD_PROP_FORMAT
:
865 bRet
= SwDBNameInfField::PutValue( rAny
, nWhichId
);
870 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */