LibreOffice Module sw (master)  1
vbafield.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
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/XTextViewCursorSupplier.hpp>
24 #include <com/sun/star/view/XSelectionSupplier.hpp>
25 #include <com/sun/star/text/XTextFieldsSupplier.hpp>
26 #include <com/sun/star/text/FilenameDisplayFormat.hpp>
27 #include <com/sun/star/util/XRefreshable.hpp>
28 #include <com/sun/star/util/XUpdatable.hpp>
29 #include <ooo/vba/word/WdFieldType.hpp>
30 #include <swtypes.hxx>
31 #include <basic/sberrors.hxx>
32 #include <cppuhelper/implbase.hxx>
33 #include <sal/log.hxx>
34 
35 using namespace ::ooo::vba;
36 using namespace ::com::sun::star;
37 
38 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 {
40  mxTextField.set( xTextField, uno::UNO_SET_THROW );
41 }
42 
44 {
45  uno::Reference< util::XUpdatable > xUpdatable( mxTextField, uno::UNO_QUERY );
46  if( xUpdatable.is() )
47  {
48  xUpdatable->update();
49  return true;
50  }
51  return false;
52 }
53 
54 // XHelperInterface
55 OUString
57 {
58  return "SwVbaField";
59 }
60 
61 uno::Sequence<OUString>
63 {
64  static uno::Sequence< OUString > const aServiceNames
65  {
66  "ooo.vba.word.Field"
67  };
68  return aServiceNames;
69 }
70 
71 namespace {
72 
73 // FIXME? copy and paste code
74 // the codes are copied from ww8par5.cxx
75 class SwVbaReadFieldParams
76 {
77 private:
78  OUString const aData;
79  sal_Int32 nLen, nFnd, nNext, nSavPtr;
80  OUString aFieldName;
81 public:
82  explicit SwVbaReadFieldParams( const OUString& rData );
83 
84  long SkipToNextToken();
85 
86  sal_Int32 FindNextStringPiece( sal_Int32 _nStart );
87 
88  OUString GetResult() const;
89  const OUString& GetFieldName()const { return aFieldName; }
90 };
91 
92 }
93 
94 SwVbaReadFieldParams::SwVbaReadFieldParams( const OUString& _rData )
95  : aData( _rData ), nLen( _rData.getLength() ), nNext( 0 )
96 {
97  // First search for an opening parenthesis or a space or a quotation mark
98  // or a backslash, so that the field command
99  // (thus INCLUDEPICTURE or ...) is ignored.
100  while( (nLen > nNext) && (aData[ nNext ] == ' ') )
101  ++nNext;
102 
103  sal_Unicode c;
104  while( nLen > nNext
105  && (c = aData[ nNext ]) != ' '
106  && c != '"'
107  && c != '\\'
108  && c != 132
109  && c != 0x201c )
110  ++nNext;
111 
112  nFnd = nNext;
113  nSavPtr = nNext;
114  aFieldName = aData.copy( 0, nFnd );
115 }
116 
117 OUString SwVbaReadFieldParams::GetResult() const
118 {
119  return (-1 == nFnd)
120  ? OUString()
121  : aData.copy( nFnd, (nSavPtr - nFnd) );
122 }
123 
124 // ret: -2: NOT a '\' parameter but normal Text
125 long SwVbaReadFieldParams::SkipToNextToken()
126 {
127  long nRet = -1; // end
128  if (
129  (-1 != nNext) && (nLen > nNext) &&
130  -1 != (nFnd = FindNextStringPiece(nNext))
131  )
132  {
133  nSavPtr = nNext;
134 
135  if ('\\' == aData[nFnd] && '\\' != aData[nFnd + 1])
136  {
137  nRet = aData[++nFnd];
138  nNext = ++nFnd; // and set behind
139  }
140  else
141  {
142  nRet = -2;
143  if (
144  (-1 != nSavPtr ) &&
145  (
146  ('"' == aData[nSavPtr - 1]) ||
147  (0x201d == aData[nSavPtr - 1])
148  )
149  )
150  {
151  --nSavPtr;
152  }
153  }
154  }
155  return nRet;
156 }
157 
158 // FindNextPara is searching for the next Backslash-Parameter or the next string
159 // until blank or the next "\" or until the closing quotation mark
160 // or until the string end of pStr.
161 
162 // Output ppNext (if ppNext != 0) beginning of the search for the next parameter or 0
163 
164 // Return value: 0 if String-End reached, otherwise begin of the parameter or the string
165 
166 sal_Int32 SwVbaReadFieldParams::FindNextStringPiece(const sal_Int32 nStart)
167 {
168  sal_Int32 n = ( -1 == nStart ) ? nFnd : nStart; // Start
169  sal_Int32 n2; // End
170 
171  nNext = -1; // Default for not found
172 
173  while( (nLen > n) && (aData[ n ] == ' ') )
174  ++n;
175 
176  if( nLen == n )
177  return -1; // String End reached!
178 
179  if( (aData[ n ] == '"') // quotation marks are in front of parenthesis?
180  || (aData[ n ] == 0x201c)
181  || (aData[ n ] == 132) )
182  {
183  n++; // ignore quotation marks
184  n2 = n; // From here search for the end
185  while( (nLen > n2)
186  && (aData[ n2 ] != '"')
187  && (aData[ n2 ] != 0x201d)
188  && (aData[ n2 ] != 147) )
189  n2++; // Search for the end of the parenthesis
190  }
191  else // no quotation marks
192  {
193  n2 = n; // from here search for the end
194  while( (nLen > n2) && (aData[ n2 ] != ' ') ) // Search for the end of the parenthesis
195  {
196  if( aData[ n2 ] == '\\' )
197  {
198  if( aData[ n2+1 ] == '\\' )
199  n2 += 2; // double-backslash -> OK
200  else
201  {
202  if( n2 > n )
203  n2--;
204  break; // single-backslash -> End
205  }
206  }
207  else
208  n2++; // no backslash -> OK
209  }
210  }
211  if( nLen > n2 )
212  {
213  if(aData[ n2 ] != ' ') n2++;
214  nNext = n2;
215  }
216  return n;
217 }
218 
219 // SwVbaFields
220 
221 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 {
223  uno::Reference< text::XTextField > xTextField( aSource, uno::UNO_QUERY_THROW );
224  uno::Reference< text::XTextDocument > xTextDocument( xModel, uno::UNO_QUERY_THROW );
225  uno::Reference< word::XField > xField( new SwVbaField( xParent, xContext, xTextField ) );
226  return uno::makeAny( xField );
227 }
228 
229 namespace {
230 
231 class FieldEnumeration : public ::cppu::WeakImplHelper< css::container::XEnumeration >
232 {
233  uno::Reference< XHelperInterface > mxParent;
234  uno::Reference< uno::XComponentContext > mxContext;
235  uno::Reference< frame::XModel > mxModel;
236  uno::Reference< container::XEnumeration > mxEnumeration;
237 public:
238  FieldEnumeration( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< frame::XModel >& xModel, const uno::Reference< container::XEnumeration >& xEnumeration ) : mxParent( xParent ), mxContext( xContext ), mxModel( xModel ), mxEnumeration( xEnumeration )
239  {
240  }
241  virtual sal_Bool SAL_CALL hasMoreElements( ) override
242  {
243  return mxEnumeration->hasMoreElements();
244  }
245  virtual uno::Any SAL_CALL nextElement( ) override
246  {
247  if ( !hasMoreElements() )
248  throw container::NoSuchElementException();
249  return lcl_createField( mxParent, mxContext, mxModel, mxEnumeration->nextElement() );
250  }
251 };
252 
253 class FieldCollectionHelper : public ::cppu::WeakImplHelper< container::XIndexAccess,
254  container::XEnumerationAccess >
255 {
256  uno::Reference< XHelperInterface > mxParent;
257  uno::Reference< uno::XComponentContext > mxContext;
258  uno::Reference< frame::XModel > mxModel;
259  uno::Reference< container::XEnumerationAccess > mxEnumerationAccess;
260 public:
262  FieldCollectionHelper( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< frame::XModel >& xModel ) : mxParent( xParent ), mxContext( xContext ), mxModel( xModel )
263  {
264  uno::Reference< text::XTextFieldsSupplier > xSupp( xModel, uno::UNO_QUERY_THROW );
265  mxEnumerationAccess.set( xSupp->getTextFields(), uno::UNO_SET_THROW );
266  }
267  // XElementAccess
268  virtual uno::Type SAL_CALL getElementType( ) override { return mxEnumerationAccess->getElementType(); }
269  virtual sal_Bool SAL_CALL hasElements( ) override { return mxEnumerationAccess->hasElements(); }
270  // XIndexAccess
271  virtual ::sal_Int32 SAL_CALL getCount( ) override
272  {
273  uno::Reference< container::XEnumeration > xEnumeration = mxEnumerationAccess->createEnumeration();
274  sal_Int32 nCount = 0;
275  while( xEnumeration->hasMoreElements() )
276  {
277  ++nCount;
278  xEnumeration->nextElement();
279  }
280  return nCount;
281  }
282  virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override
283  {
284  if( Index < 0 || Index >= getCount() )
285  throw lang::IndexOutOfBoundsException();
286 
287  uno::Reference< container::XEnumeration > xEnumeration = mxEnumerationAccess->createEnumeration();
288  sal_Int32 nCount = 0;
289  while( xEnumeration->hasMoreElements() )
290  {
291  if( nCount == Index )
292  {
293  return xEnumeration->nextElement();
294  }
295  ++nCount;
296  }
297  throw lang::IndexOutOfBoundsException();
298  }
299  // XEnumerationAccess
300  virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration( ) override
301  {
302  uno::Reference< container::XEnumeration > xEnumeration = mxEnumerationAccess->createEnumeration();
303  return uno::Reference< container::XEnumeration >( new FieldEnumeration( mxParent, mxContext, mxModel, xEnumeration ) );
304  }
305 };
306 
307 }
308 
309 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 {
311  mxMSF.set( mxModel, uno::UNO_QUERY_THROW );
312 }
313 
314 uno::Reference< word::XField > SAL_CALL
315 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 {
317  sal_Int32 nType = word::WdFieldType::wdFieldEmpty;
318  Type >>= nType;
319  OUString sText;
320  Text >>= sText;
321 
322  OUString sFieldName;
323  if( ( nType == word::WdFieldType::wdFieldEmpty ) && !sText.isEmpty() )
324  {
325  SwVbaReadFieldParams aReadParam(sText);
326  sFieldName = aReadParam.GetFieldName();
327  SAL_INFO("sw.vba", "the field name is " << sFieldName );
328  }
329 
330  uno::Reference< text::XTextContent > xTextField;
331  if( nType == word::WdFieldType::wdFieldFileName || sFieldName.equalsIgnoreAsciiCase("FILENAME") )
332  {
333  xTextField.set( Create_Field_FileName( sText ), uno::UNO_QUERY_THROW );
334  }
335  else if( nType == word::WdFieldType::wdFieldDocProperty || sFieldName.equalsIgnoreAsciiCase("DOCPROPERTY") )
336  {
337  xTextField.set( Create_Field_DocProperty( sText ), uno::UNO_QUERY_THROW );
338  }
339  else
340  {
341  throw uno::RuntimeException("Not implemented" );
342  }
343 
344  SwVbaRange& rVbaRange = dynamic_cast<SwVbaRange&>(*Range);
345  uno::Reference< text::XTextRange > xTextRange = rVbaRange.getXTextRange();
346  uno::Reference< text::XText > xText = xTextRange->getText();
347  xText->insertTextContent( xTextRange, xTextField, true );
348  return uno::Reference< word::XField >( new SwVbaField( mxParent, mxContext, uno::Reference< text::XTextField >( xTextField, uno::UNO_QUERY_THROW ) ) );
349 }
350 
351 uno::Reference< text::XTextField > SwVbaFields::Create_Field_FileName( const OUString& _text )
352 {
353  uno::Reference< text::XTextField > xTextField( mxMSF->createInstance("com.sun.star.text.TextField.FileName"), uno::UNO_QUERY_THROW );
354  sal_Int16 nFileFormat = text::FilenameDisplayFormat::NAME_AND_EXT;
355  if( !_text.isEmpty() )
356  {
357  long nRet;
358  SwVbaReadFieldParams aReadParam( _text );
359  while (-1 != (nRet = aReadParam.SkipToNextToken()))
360  {
361  switch (nRet)
362  {
363  case 'p':
364  nFileFormat = text::FilenameDisplayFormat::FULL;
365  break;
366  case '*':
367  //Skip over MERGEFORMAT
368  aReadParam.SkipToNextToken();
369  break;
370  default:
371  DebugHelper::basicexception(ERRCODE_BASIC_BAD_ARGUMENT, OUString());
372  break;
373  }
374  }
375  }
376 
377  uno::Reference< beans::XPropertySet > xProps( xTextField, uno::UNO_QUERY_THROW );
378  xProps->setPropertyValue("FileFormat", uno::makeAny( nFileFormat ) );
379 
380  return xTextField;
381 }
382 
383 namespace {
384 
385 struct DocPropertyTable
386 {
387  const char* sDocPropertyName;
388  const char* sFieldService;
389 };
390 
391 }
392 
393 static const DocPropertyTable aDocPropertyTables[] =
394 {
395  { "Author", "com.sun.star.text.textfield.docinfo.CreateAuthor" },
396  { "Bytes", nullptr },
397  { "Category", nullptr },
398  { "Characters",nullptr },
399  { "CharactersWithSpaces", nullptr },
400  { "Comments", "com.sun.star.text.textfield.docinfo.Description" },
401  { "Company", nullptr },
402  { "CreateTime", "com.sun.star.text.textfield.docinfo.CreateDateTime" },
403  { "HyperlinkBase", nullptr },
404  { "Keywords", "com.sun.star.text.textfield.docinfo.Keywords" },
405  { "LastPrinted", "com.sun.star.text.textfield.docinfo.PrintDateTime" },
406  { "LastSavedBy", "com.sun.star.text.textfield.docinfo.ChangeAuthor" },
407  { "LastSavedTime", "com.sun.star.text.textfield.docinfo.ChangeDateTime" },
408  { "Lines", nullptr },
409  { "Manager", nullptr },
410  { "NameofApplication", nullptr },
411  { "ODMADocID", nullptr },
412  { "Pages", "com.sun.star.text.textfield.PageCount" },
413  { "Paragraphs", "com.sun.star.text.textfield.ParagraphCount" },
414  { "RevisionNumber", "com.sun.star.text.textfield.docinfo.Revision" },
415  { "Security", nullptr },
416  { "Subject", "com.sun.star.text.textfield.docinfo.Subject" },
417  { "Template", "com.sun.star.text.textfield.TemplateName" },
418  { "Title", "com.sun.star.text.textfield.docinfo.Title" },
419  { "TotalEditingTime", "com.sun.star.text.textfield.docinfo.EditTime" },
420  { "Words", "com.sun.star.text.textfield.WordCount" },
421  { nullptr, nullptr }
422 };
423 
424 uno::Reference< text::XTextField > SwVbaFields::Create_Field_DocProperty( const OUString& _text )
425 {
426  OUString aDocProperty;
427  SwVbaReadFieldParams aReadParam( _text );
428  long nRet;
429  while( -1 != ( nRet = aReadParam.SkipToNextToken() ))
430  {
431  switch( nRet )
432  {
433  case -2:
434  if( aDocProperty.isEmpty() )
435  aDocProperty = aReadParam.GetResult();
436  break;
437  case '*':
438  //Skip over MERGEFORMAT
439  aReadParam.SkipToNextToken();
440  break;
441  }
442  }
443  aDocProperty = aDocProperty.replaceAll("\"", "");
444  SAL_INFO("sw.vba", "SwVbaFields::Create_Field_DocProperty, the document property name is " << aDocProperty );
445  if( aDocProperty.isEmpty() )
446  {
447  throw uno::RuntimeException();
448  }
449 
450  bool bCustom = true;
451  OUString sFieldService;
452  // find the build in document properties
453  for( const DocPropertyTable* pTable = aDocPropertyTables; pTable->sDocPropertyName != nullptr; pTable++ )
454  {
455  if( aDocProperty.equalsIgnoreAsciiCaseAscii( pTable->sDocPropertyName ) )
456  {
457  if( pTable->sFieldService != nullptr )
458  sFieldService = OUString::createFromAscii(pTable->sFieldService);
459  bCustom = false;
460  break;
461  }
462  }
463 
464  if( bCustom )
465  {
466  sFieldService = "com.sun.star.text.textfield.docinfo.Custom";
467  }
468  else if( sFieldService.isEmpty() )
469  {
470  throw uno::RuntimeException("Not implemented" );
471  }
472 
473  uno::Reference< text::XTextField > xTextField( mxMSF->createInstance( sFieldService ), uno::UNO_QUERY_THROW );
474 
475  if( bCustom )
476  {
477  uno::Reference< beans::XPropertySet > xProps( xTextField, uno::UNO_QUERY_THROW );
478  xProps->setPropertyValue("Name", uno::makeAny( aDocProperty ) );
479  }
480 
481  return xTextField;
482 }
483 
484 uno::Reference< container::XEnumeration > SAL_CALL
486 {
487  uno::Reference< container::XEnumerationAccess > xEnumerationAccess( m_xIndexAccess, uno::UNO_QUERY_THROW );
488  return xEnumerationAccess->createEnumeration();
489 }
490 
491 // ScVbaCollectionBaseImpl
492 uno::Any
494 {
495  return lcl_createField( mxParent, mxContext, mxModel, aSource );
496 }
497 
498 sal_Int32 SAL_CALL SwVbaFields::Update()
499 {
500  sal_Int32 nUpdate = 1;
501  try
502  {
503  uno::Reference< text::XTextFieldsSupplier > xSupp( mxModel, uno::UNO_QUERY_THROW );
504  uno::Reference< util::XRefreshable > xRef( xSupp->getTextFields(), uno::UNO_QUERY_THROW );
505  xRef->refresh();
506  nUpdate = 0;
507  }
508  catch(const uno::Exception&)
509  {
510  nUpdate = 1;
511  }
512  return nUpdate;
513 }
514 
515 // XHelperInterface
516 OUString
518 {
519  return "SwVbaFields";
520 }
521 
522 // XEnumerationAccess
523 uno::Type SAL_CALL
525 {
527 }
528 
529 uno::Sequence<OUString>
531 {
532  static uno::Sequence< OUString > const aServiceNames
533  {
534  "ooo.vba.word.Fields"
535  };
536  return aServiceNames;
537 }
538 
539 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Type
#define ERRCODE_BASIC_BAD_ARGUMENT
const char aData[]
Definition: ww8scan.hxx:47
css::uno::Reference< css::container::XIndexAccess > m_xIndexAccess
WeakReference< XInterface > mxParent
sal_Int64 n
Reference
css::uno::Reference< css::frame::XModel2 > mxModel
Sequence< OUString > aServiceNames
virtual OUString getServiceImplName() override
Definition: vbafield.cxx:56
SwVbaFields(const css::uno::Reference< ov::XHelperInterface > &xParent, const css::uno::Reference< css::uno::XComponentContext > &xContext, const css::uno::Reference< css::frame::XModel > &xModel)
Definition: vbafield.cxx:309
static const DocPropertyTable aDocPropertyTables[]
Definition: vbafield.cxx:393
virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override
Definition: vbafield.cxx:485
sal_uInt16 sal_Unicode
int n2
css::uno::Reference< css::text::XTextField > Create_Field_FileName(const OUString &rText)
Definition: vbafield.cxx:351
int nCount
virtual css::uno::Reference< css::text::XTextRange > SAL_CALL getXTextRange() override
Definition: vbarange.cxx:89
css::uno::Reference< css::text::XTextField > Create_Field_DocProperty(const OUString &_text)
Definition: vbafield.cxx:424
virtual css::uno::Type SAL_CALL getElementType() override
Definition: vbafield.cxx:524
css::uno::Reference< css::text::XTextField > mxTextField
Definition: vbafield.hxx:32
SwVbaField(const css::uno::Reference< ooo::vba::XHelperInterface > &rParent, const css::uno::Reference< css::uno::XComponentContext > &rContext, const css::uno::Reference< css::text::XTextField > &xTextField)
Definition: vbafield.cxx:38
virtual OUString getServiceImplName() override
Definition: vbafield.cxx:517
QPRO_FUNC_TYPE const nType
unsigned char sal_Bool
css::uno::Reference< css::lang::XMultiServiceFactory > mxMSF
Definition: vbafield.hxx:48
virtual sal_Int32 SAL_CALL Update() override
Definition: vbafield.cxx:498
css::uno::Type const & get()
virtual css::uno::Reference< ::ooo::vba::word::XField > SAL_CALL 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) override
Definition: vbafield.cxx:315
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)
Definition: vbafield.cxx:221
virtual css::uno::Sequence< OUString > getServiceNames() override
Definition: vbafield.cxx:62
#define SAL_INFO(area, stream)
void copy(const fs::path &src, const fs::path &dest)
double getLength(const B2DPolygon &rCandidate)
virtual sal_Bool SAL_CALL Update() override
Definition: vbafield.cxx:43
virtual css::uno::Any createCollectionObject(const css::uno::Any &aSource) override
Definition: vbafield.cxx:493
virtual css::uno::Sequence< OUString > getServiceNames() override
Definition: vbafield.cxx:530
struct _ADOIndex Index
Reference< XComponentContext > mxContext