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 .
19 #include "vbafield.hxx"
20 #include "vbarange.hxx"
21 #include <com/sun/star/beans/XPropertySet.hpp>
22 #include <com/sun/star/frame/XModel.hpp>
23 #include <com/sun/star/text/XTextFieldsSupplier.hpp>
24 #include <com/sun/star/text/FilenameDisplayFormat.hpp>
25 #include <com/sun/star/util/XRefreshable.hpp>
26 #include <com/sun/star/util/XUpdatable.hpp>
27 #include <ooo/vba/word/WdFieldType.hpp>
28 #include <basic/sberrors.hxx>
29 #include <cppuhelper/implbase.hxx>
30 #include <sal/log.hxx>
31 #include <tools/long.hxx>
34 using namespace ::ooo::vba
;
35 using namespace ::com::sun::star
;
37 SwVbaField::SwVbaField( const uno::Reference
< ooo::vba::XHelperInterface
>& rParent
, const uno::Reference
< uno::XComponentContext
>& rContext
, const uno::Reference
< css::text::XTextField
>& xTextField
) : SwVbaField_BASE( rParent
, rContext
)
39 mxTextField
.set( xTextField
, uno::UNO_SET_THROW
);
42 sal_Bool SAL_CALL
SwVbaField::Update()
44 uno::Reference
< util::XUpdatable
> xUpdatable( mxTextField
, uno::UNO_QUERY
);
55 SwVbaField::getServiceImplName()
60 uno::Sequence
<OUString
>
61 SwVbaField::getServiceNames()
63 static uno::Sequence
< OUString
> const aServiceNames
72 // FIXME? copy and paste code
73 // the codes are copied from ww8par5.cxx
74 class SwVbaReadFieldParams
78 sal_Int32 m_nLen
, m_nFnd
, m_nNext
, m_nSavPtr
;
79 OUString m_aFieldName
;
81 explicit SwVbaReadFieldParams( const OUString
& rData
);
83 tools::Long
SkipToNextToken();
85 sal_Int32
FindNextStringPiece( sal_Int32 _nStart
);
87 OUString
GetResult() const;
88 const OUString
& GetFieldName()const { return m_aFieldName
; }
93 SwVbaReadFieldParams::SwVbaReadFieldParams( const OUString
& _rData
)
94 : m_aData( _rData
), m_nLen( _rData
.getLength() ), m_nNext( 0 )
96 // First search for an opening parenthesis or a space or a quotation mark
97 // or a backslash, so that the field command
98 // (thus INCLUDEPICTURE or ...) is ignored.
99 while( (m_nLen
> m_nNext
) && (m_aData
[ m_nNext
] == ' ') )
103 while( m_nLen
> m_nNext
104 && (c
= m_aData
[ m_nNext
]) != ' '
113 m_aFieldName
= m_aData
.copy( 0, m_nFnd
);
116 OUString
SwVbaReadFieldParams::GetResult() const
118 return (-1 == m_nFnd
)
120 : m_aData
.copy( m_nFnd
, (m_nSavPtr
- m_nFnd
) );
123 // ret: -2: NOT a '\' parameter but normal Text
124 tools::Long
SwVbaReadFieldParams::SkipToNextToken()
126 tools::Long nRet
= -1; // end
128 (-1 != m_nNext
) && (m_nLen
> m_nNext
) &&
129 -1 != (m_nFnd
= FindNextStringPiece(m_nNext
))
134 if ('\\' == m_aData
[m_nFnd
] && '\\' != m_aData
[m_nFnd
+ 1])
136 nRet
= m_aData
[++m_nFnd
];
137 m_nNext
= ++m_nFnd
; // and set behind
143 (-1 != m_nSavPtr
) &&
145 ('"' == m_aData
[m_nSavPtr
- 1]) ||
146 (0x201d == m_aData
[m_nSavPtr
- 1])
157 // FindNextPara is searching for the next Backslash-Parameter or the next string
158 // until blank or the next "\" or until the closing quotation mark
159 // or until the string end of pStr.
161 // Output ppNext (if ppNext != 0) beginning of the search for the next parameter or 0
163 // Return value: 0 if String-End reached, otherwise begin of the parameter or the string
165 sal_Int32
SwVbaReadFieldParams::FindNextStringPiece(const sal_Int32 nStart
)
167 sal_Int32 n
= ( -1 == nStart
) ? m_nFnd
: nStart
; // Start
170 m_nNext
= -1; // Default for not found
172 while( (m_nLen
> n
) && (m_aData
[ n
] == ' ') )
176 return -1; // String End reached!
178 if( (m_aData
[ n
] == '"') // quotation marks are in front of parenthesis?
179 || (m_aData
[ n
] == 0x201c)
180 || (m_aData
[ n
] == 132) )
182 n
++; // ignore quotation marks
183 n2
= n
; // From here search for the end
185 && (m_aData
[ n2
] != '"')
186 && (m_aData
[ n2
] != 0x201d)
187 && (m_aData
[ n2
] != 147) )
188 n2
++; // Search for the end of the parenthesis
190 else // no quotation marks
192 n2
= n
; // from here search for the end
193 while( (m_nLen
> n2
) && (m_aData
[ n2
] != ' ') ) // Search for the end of the parenthesis
195 if( m_aData
[ n2
] == '\\' )
197 if( m_aData
[ n2
+1 ] == '\\' )
198 n2
+= 2; // double-backslash -> OK
203 break; // single-backslash -> End
207 n2
++; // no backslash -> OK
212 if(m_aData
[ n2
] != ' ') n2
++;
220 static uno::Any
lcl_createField( const uno::Reference
< XHelperInterface
>& xParent
, const uno::Reference
< uno::XComponentContext
>& xContext
, const uno::Reference
< frame::XModel
>& xModel
, const uno::Any
& aSource
)
222 uno::Reference
< text::XTextField
> xTextField( aSource
, uno::UNO_QUERY_THROW
);
223 uno::Reference
< text::XTextDocument
> xTextDocument( xModel
, uno::UNO_QUERY_THROW
);
224 uno::Reference
< word::XField
> xField( new SwVbaField( xParent
, xContext
, xTextField
) );
225 return uno::Any( xField
);
230 class FieldEnumeration
: public ::cppu::WeakImplHelper
< css::container::XEnumeration
>
232 uno::Reference
< XHelperInterface
> mxParent
;
233 uno::Reference
< uno::XComponentContext
> mxContext
;
234 uno::Reference
< frame::XModel
> mxModel
;
235 uno::Reference
< container::XEnumeration
> mxEnumeration
;
237 FieldEnumeration( uno::Reference
< XHelperInterface
> xParent
, uno::Reference
< uno::XComponentContext
> xContext
, uno::Reference
< frame::XModel
> xModel
, uno::Reference
< container::XEnumeration
> xEnumeration
) : mxParent(std::move( xParent
)), mxContext(std::move( xContext
)), mxModel(std::move( xModel
)), mxEnumeration(std::move( xEnumeration
))
240 virtual sal_Bool SAL_CALL
hasMoreElements( ) override
242 return mxEnumeration
->hasMoreElements();
244 virtual uno::Any SAL_CALL
nextElement( ) override
246 if ( !hasMoreElements() )
247 throw container::NoSuchElementException();
248 return lcl_createField( mxParent
, mxContext
, mxModel
, mxEnumeration
->nextElement() );
252 class FieldCollectionHelper
: public ::cppu::WeakImplHelper
< container::XIndexAccess
,
253 container::XEnumerationAccess
>
255 uno::Reference
< XHelperInterface
> mxParent
;
256 uno::Reference
< uno::XComponentContext
> mxContext
;
257 uno::Reference
< frame::XModel
> mxModel
;
258 uno::Reference
< container::XEnumerationAccess
> mxEnumerationAccess
;
260 /// @throws css::uno::RuntimeException
261 FieldCollectionHelper( uno::Reference
< XHelperInterface
> xParent
, uno::Reference
< uno::XComponentContext
> xContext
, const uno::Reference
< frame::XModel
>& xModel
) : mxParent(std::move( xParent
)), mxContext(std::move( xContext
)), mxModel( xModel
)
263 uno::Reference
< text::XTextFieldsSupplier
> xSupp( xModel
, uno::UNO_QUERY_THROW
);
264 mxEnumerationAccess
.set( xSupp
->getTextFields(), uno::UNO_SET_THROW
);
267 virtual uno::Type SAL_CALL
getElementType( ) override
{ return mxEnumerationAccess
->getElementType(); }
268 virtual sal_Bool SAL_CALL
hasElements( ) override
{ return mxEnumerationAccess
->hasElements(); }
270 virtual ::sal_Int32 SAL_CALL
getCount( ) override
272 uno::Reference
< container::XEnumeration
> xEnumeration
= mxEnumerationAccess
->createEnumeration();
273 sal_Int32 nCount
= 0;
274 while( xEnumeration
->hasMoreElements() )
277 xEnumeration
->nextElement();
281 virtual uno::Any SAL_CALL
getByIndex( ::sal_Int32 Index
) override
283 if( Index
< 0 || Index
>= getCount() )
284 throw lang::IndexOutOfBoundsException();
286 uno::Reference
< container::XEnumeration
> xEnumeration
= mxEnumerationAccess
->createEnumeration();
287 sal_Int32 nCount
= 0;
288 while( xEnumeration
->hasMoreElements() )
290 if( nCount
== Index
)
292 return xEnumeration
->nextElement();
296 throw lang::IndexOutOfBoundsException();
298 // XEnumerationAccess
299 virtual uno::Reference
< container::XEnumeration
> SAL_CALL
createEnumeration( ) override
301 uno::Reference
< container::XEnumeration
> xEnumeration
= mxEnumerationAccess
->createEnumeration();
302 return uno::Reference
< container::XEnumeration
>( new FieldEnumeration( mxParent
, mxContext
, mxModel
, xEnumeration
) );
308 SwVbaFields::SwVbaFields( const uno::Reference
< XHelperInterface
>& xParent
, const uno::Reference
< uno::XComponentContext
> & xContext
, const uno::Reference
< frame::XModel
>& xModel
) : SwVbaFields_BASE( xParent
, xContext
, uno::Reference
< container::XIndexAccess
>( new FieldCollectionHelper( xParent
, xContext
, xModel
) ) ), mxModel( xModel
)
310 mxMSF
.set( mxModel
, uno::UNO_QUERY_THROW
);
313 uno::Reference
< word::XField
> SAL_CALL
314 SwVbaFields::Add( const css::uno::Reference
< ::ooo::vba::word::XRange
>& Range
, const css::uno::Any
& Type
, const css::uno::Any
& Text
, const css::uno::Any
& /*PreserveFormatting*/ )
316 sal_Int32 nType
= word::WdFieldType::wdFieldEmpty
;
322 if( ( nType
== word::WdFieldType::wdFieldEmpty
) && !sText
.isEmpty() )
324 SwVbaReadFieldParams
aReadParam(sText
);
325 sFieldName
= aReadParam
.GetFieldName();
326 SAL_INFO("sw.vba", "the field name is " << sFieldName
);
329 uno::Reference
< text::XTextContent
> xTextField
;
330 if( nType
== word::WdFieldType::wdFieldFileName
|| sFieldName
.equalsIgnoreAsciiCase("FILENAME") )
332 xTextField
.set( Create_Field_FileName( sText
), uno::UNO_QUERY_THROW
);
334 else if( nType
== word::WdFieldType::wdFieldDocProperty
|| sFieldName
.equalsIgnoreAsciiCase("DOCPROPERTY") )
336 xTextField
.set( Create_Field_DocProperty( sText
), uno::UNO_QUERY_THROW
);
340 throw uno::RuntimeException("Not implemented" );
343 SwVbaRange
& rVbaRange
= dynamic_cast<SwVbaRange
&>(*Range
);
344 uno::Reference
< text::XTextRange
> xTextRange
= rVbaRange
.getXTextRange();
345 uno::Reference
< text::XText
> xText
= xTextRange
->getText();
346 xText
->insertTextContent( xTextRange
, xTextField
, true );
347 return uno::Reference
< word::XField
>( new SwVbaField( mxParent
, mxContext
, uno::Reference
< text::XTextField
>( xTextField
, uno::UNO_QUERY_THROW
) ) );
350 uno::Reference
< text::XTextField
> SwVbaFields::Create_Field_FileName( const OUString
& _text
)
352 uno::Reference
< text::XTextField
> xTextField( mxMSF
->createInstance("com.sun.star.text.TextField.FileName"), uno::UNO_QUERY_THROW
);
353 sal_Int16 nFileFormat
= text::FilenameDisplayFormat::NAME_AND_EXT
;
354 if( !_text
.isEmpty() )
357 SwVbaReadFieldParams
aReadParam( _text
);
358 while (-1 != (nRet
= aReadParam
.SkipToNextToken()))
363 nFileFormat
= text::FilenameDisplayFormat::FULL
;
366 //Skip over MERGEFORMAT
367 aReadParam
.SkipToNextToken();
370 DebugHelper::basicexception(ERRCODE_BASIC_BAD_ARGUMENT
, {});
376 uno::Reference
< beans::XPropertySet
> xProps( xTextField
, uno::UNO_QUERY_THROW
);
377 xProps
->setPropertyValue("FileFormat", uno::Any( nFileFormat
) );
384 struct DocPropertyTable
386 const char* sDocPropertyName
;
387 const char* sFieldService
;
392 const DocPropertyTable aDocPropertyTables
[] =
394 { "Author", "com.sun.star.text.textfield.docinfo.CreateAuthor" },
395 { "Bytes", nullptr },
396 { "Category", nullptr },
397 { "Characters",nullptr },
398 { "CharactersWithSpaces", nullptr },
399 { "Comments", "com.sun.star.text.textfield.docinfo.Description" },
400 { "Company", nullptr },
401 { "CreateTime", "com.sun.star.text.textfield.docinfo.CreateDateTime" },
402 { "HyperlinkBase", nullptr },
403 { "Keywords", "com.sun.star.text.textfield.docinfo.Keywords" },
404 { "LastPrinted", "com.sun.star.text.textfield.docinfo.PrintDateTime" },
405 { "LastSavedBy", "com.sun.star.text.textfield.docinfo.ChangeAuthor" },
406 { "LastSavedTime", "com.sun.star.text.textfield.docinfo.ChangeDateTime" },
407 { "Lines", nullptr },
408 { "Manager", nullptr },
409 { "NameofApplication", nullptr },
410 { "ODMADocID", nullptr },
411 { "Pages", "com.sun.star.text.textfield.PageCount" },
412 { "Paragraphs", "com.sun.star.text.textfield.ParagraphCount" },
413 { "RevisionNumber", "com.sun.star.text.textfield.docinfo.Revision" },
414 { "Security", nullptr },
415 { "Subject", "com.sun.star.text.textfield.docinfo.Subject" },
416 { "Template", "com.sun.star.text.textfield.TemplateName" },
417 { "Title", "com.sun.star.text.textfield.docinfo.Title" },
418 { "TotalEditingTime", "com.sun.star.text.textfield.docinfo.EditTime" },
419 { "Words", "com.sun.star.text.textfield.WordCount" },
423 uno::Reference
< text::XTextField
> SwVbaFields::Create_Field_DocProperty( const OUString
& _text
)
425 OUString aDocProperty
;
426 SwVbaReadFieldParams
aReadParam( _text
);
428 while( -1 != ( nRet
= aReadParam
.SkipToNextToken() ))
433 if( aDocProperty
.isEmpty() )
434 aDocProperty
= aReadParam
.GetResult();
437 //Skip over MERGEFORMAT
438 aReadParam
.SkipToNextToken();
442 aDocProperty
= aDocProperty
.replaceAll("\"", "");
443 SAL_INFO("sw.vba", "SwVbaFields::Create_Field_DocProperty, the document property name is " << aDocProperty
);
444 if( aDocProperty
.isEmpty() )
446 throw uno::RuntimeException();
450 OUString sFieldService
;
451 // find the build in document properties
452 for( const DocPropertyTable
* pTable
= aDocPropertyTables
; pTable
->sDocPropertyName
!= nullptr; pTable
++ )
454 if( aDocProperty
.equalsIgnoreAsciiCaseAscii( pTable
->sDocPropertyName
) )
456 if( pTable
->sFieldService
!= nullptr )
457 sFieldService
= OUString::createFromAscii(pTable
->sFieldService
);
465 sFieldService
= "com.sun.star.text.textfield.docinfo.Custom";
467 else if( sFieldService
.isEmpty() )
469 throw uno::RuntimeException("Not implemented" );
472 uno::Reference
< text::XTextField
> xTextField( mxMSF
->createInstance( sFieldService
), uno::UNO_QUERY_THROW
);
476 uno::Reference
< beans::XPropertySet
> xProps( xTextField
, uno::UNO_QUERY_THROW
);
477 xProps
->setPropertyValue("Name", uno::Any( aDocProperty
) );
483 uno::Reference
< container::XEnumeration
> SAL_CALL
484 SwVbaFields::createEnumeration()
486 uno::Reference
< container::XEnumerationAccess
> xEnumerationAccess( m_xIndexAccess
, uno::UNO_QUERY_THROW
);
487 return xEnumerationAccess
->createEnumeration();
490 // ScVbaCollectionBaseImpl
492 SwVbaFields::createCollectionObject( const uno::Any
& aSource
)
494 return lcl_createField( mxParent
, mxContext
, mxModel
, aSource
);
497 sal_Int32 SAL_CALL
SwVbaFields::Update()
499 sal_Int32 nUpdate
= 1;
502 uno::Reference
< text::XTextFieldsSupplier
> xSupp( mxModel
, uno::UNO_QUERY_THROW
);
503 uno::Reference
< util::XRefreshable
> xRef( xSupp
->getTextFields(), uno::UNO_QUERY_THROW
);
507 catch(const uno::Exception
&)
516 SwVbaFields::getServiceImplName()
518 return "SwVbaFields";
521 // XEnumerationAccess
523 SwVbaFields::getElementType()
525 return cppu::UnoType
<word::XField
>::get();
528 uno::Sequence
<OUString
>
529 SwVbaFields::getServiceNames()
531 static uno::Sequence
< OUString
> const aServiceNames
533 "ooo.vba.word.Fields"
535 return aServiceNames
;
538 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */