LibreOffice Module sw (master)  1
vbalistformat.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 "vbalistformat.hxx"
20 #include <vbahelper/vbahelper.hxx>
21 #include <ooo/vba/word/WdListApplyTo.hpp>
22 #include <ooo/vba/word/WdDefaultListBehavior.hpp>
23 #include <com/sun/star/awt/FontDescriptor.hpp>
24 #include <com/sun/star/container/XEnumerationAccess.hpp>
25 #include <com/sun/star/container/XEnumeration.hpp>
26 #include <com/sun/star/document/XUndoManagerSupplier.hpp>
27 #include <com/sun/star/beans/XMultiPropertySet.hpp>
28 #include <com/sun/star/beans/XPropertySet.hpp>
29 #include <com/sun/star/style/TabStop.hpp>
30 #include <com/sun/star/text/PositionAndSpaceMode.hpp>
31 #include <com/sun/star/text/XTextTable.hpp>
32 #include <com/sun/star/util/Color.hpp>
33 #include <comphelper/sequence.hxx>
36 #include <editeng/numitem.hxx>
37 #include "vbalisttemplate.hxx"
38 
39 #include <vector>
40 
41 using namespace ::ooo::vba;
42 using namespace ::com::sun::star;
43 
44 SwVbaListFormat::SwVbaListFormat( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Reference< text::XTextRange >& xTextRange ) : SwVbaListFormat_BASE( rParent, rContext ), mxTextRange( xTextRange )
45 {
46 }
47 
49 {
50 }
51 
52 void SAL_CALL SwVbaListFormat::ApplyListTemplate( const css::uno::Reference< word::XListTemplate >& ListTemplate, const css::uno::Any& ContinuePreviousList, const css::uno::Any& ApplyTo, const css::uno::Any& DefaultListBehavior )
53 {
54  bool bContinuePreviousList = true;
55  if( ContinuePreviousList.hasValue() )
56  ContinuePreviousList >>= bContinuePreviousList;
57 
58  // "applyto" must be current selection
59  sal_Int32 bApplyTo = word::WdListApplyTo::wdListApplyToSelection;
60  if( ApplyTo.hasValue() )
61  ApplyTo >>= bApplyTo;
62  if( bApplyTo != word::WdListApplyTo::wdListApplyToSelection )
63  throw uno::RuntimeException();
64 
65  // default behaviour must be wdWord8ListBehavior
66  sal_Int32 nDefaultListBehavior = word::WdDefaultListBehavior::wdWord8ListBehavior;
67  if( DefaultListBehavior.hasValue() )
68  DefaultListBehavior >>= nDefaultListBehavior;
69  if( nDefaultListBehavior != word::WdDefaultListBehavior::wdWord8ListBehavior )
70  throw uno::RuntimeException();
71 
72  uno::Reference< container::XEnumerationAccess > xEnumAccess( mxTextRange, uno::UNO_QUERY_THROW );
73  uno::Reference< container::XEnumeration > xEnum = xEnumAccess->createEnumeration();
74  if (!xEnum->hasMoreElements())
75  return;
76 
77  SwVbaListTemplate& rListTemplate = dynamic_cast<SwVbaListTemplate&>(*ListTemplate);
78 
79  bool isFirstElement = true;
80  do
81  {
82  uno::Reference< beans::XPropertySet > xProps( xEnum->nextElement(), uno::UNO_QUERY_THROW );
83  if( isFirstElement )
84  {
85  bool isNumberingRestart = !bContinuePreviousList;
86  xProps->setPropertyValue("ParaIsNumberingRestart", uno::makeAny( isNumberingRestart ) );
87  if( isNumberingRestart )
88  {
89  xProps->setPropertyValue("NumberingStartValue", uno::makeAny( sal_Int16(1) ) );
90  }
91  isFirstElement = false;
92  }
93  else
94  {
95  xProps->setPropertyValue("ParaIsNumberingRestart", uno::makeAny( false ) );
96  }
97  rListTemplate.applyListTemplate( xProps );
98  }
99  while( xEnum->hasMoreElements() );
100 }
101 
102 template <class Ref>
103 static void addParagraphsToList(const Ref& a,
104  std::vector<css::uno::Reference<css::beans::XPropertySet>>& rList)
105 {
106  if (css::uno::Reference<css::lang::XServiceInfo> xInfo{ a, css::uno::UNO_QUERY })
107  {
108  if (xInfo->supportsService("com.sun.star.text.Paragraph"))
109  {
110  rList.emplace_back(xInfo, css::uno::UNO_QUERY_THROW);
111  }
112  else if (xInfo->supportsService("com.sun.star.text.TextTable"))
113  {
114  css::uno::Reference<css::text::XTextTable> xTable(xInfo, css::uno::UNO_QUERY_THROW);
115  const auto aNames = xTable->getCellNames();
116  for (const auto& rName : aNames)
117  {
118  addParagraphsToList(xTable->getCellByName(rName), rList);
119  }
120  }
121  }
122  if (css::uno::Reference<css::container::XEnumerationAccess> xEnumAccess{ a,
123  css::uno::UNO_QUERY })
124  {
125  auto xEnum = xEnumAccess->createEnumeration();
126  while (xEnum->hasMoreElements())
127  addParagraphsToList(xEnum->nextElement(), rList);
128  }
129 }
130 
132 {
133  css::uno::Reference<css::frame::XModel> xModel(getThisWordDoc(mxContext));
134  css::uno::Reference<css::document::XUndoManagerSupplier> xUndoSupplier(
135  xModel, css::uno::UNO_QUERY_THROW);
136  css::uno::Reference<css::document::XUndoManager> xUndoManager(xUndoSupplier->getUndoManager());
137  xUndoManager->enterUndoContext("ConvertNumbersToText");
138  xModel->lockControllers();
139  comphelper::ScopeGuard g([xModel, xUndoManager]() {
140  xModel->unlockControllers();
141  xUndoManager->leaveUndoContext();
142  });
143 
144  std::vector<css::uno::Reference<css::beans::XPropertySet>> aParagraphs;
145  addParagraphsToList(mxTextRange, aParagraphs);
146 
147  // in reverse order, to get proper label strings
148  for (auto it = aParagraphs.rbegin(); it != aParagraphs.rend(); ++it)
149  {
150  if (bool bNumber; ((*it)->getPropertyValue("NumberingIsNumber") >>= bNumber) && bNumber)
151  {
152  css::uno::Reference<css::text::XTextRange> xRange(*it, css::uno::UNO_QUERY_THROW);
153  OUString sLabelString;
154  (*it)->getPropertyValue("ListLabelString") >>= sLabelString;
155  // sal_Int16 nAdjust = SAL_MAX_INT16; // TODO?
156  sal_Int16 nNumberingType = SAL_MAX_INT16; // css::style::NumberingType
157  sal_Int16 nPositionAndSpaceMode = SAL_MAX_INT16;
158  sal_Int16 nLabelFollowedBy = SAL_MAX_INT16;
159  sal_Int32 nListtabStopPosition = SAL_MAX_INT32;
160  sal_Int32 nFirstLineIndent = SAL_MAX_INT32;
161  sal_Int32 nIndentAt = SAL_MAX_INT32;
162  sal_Int32 nLeftMargin = SAL_MAX_INT32;
164  sal_Int32 nFirstLineOffset = SAL_MAX_INT32;
165  OUString sCharStyleName, sBulletChar;
166  css::awt::FontDescriptor aBulletFont;
167  bool bHasFont;
168  css::util::Color aBulletColor = css::util::Color(COL_AUTO);
169  bool bHasColor;
170 
171  {
172  sal_uInt16 nLevel = SAL_MAX_UINT16;
173  (*it)->getPropertyValue("NumberingLevel") >>= nLevel;
174  css::uno::Reference<css::container::XIndexAccess> xNumberingRules;
175  (*it)->getPropertyValue("NumberingRules") >>= xNumberingRules;
176  comphelper::SequenceAsHashMap aLevelRule(xNumberingRules->getByIndex(nLevel));
177 
178  // See offapi/com/sun/star/text/NumberingLevel.idl
179  aLevelRule["CharStyleName"] >>= sCharStyleName;
180  aLevelRule["NumberingType"] >>= nNumberingType;
181  // TODO: aLevelRule["Adjust"] >>= nAdjust; // HoriOrientation::LEFT/RIGHT/CENTER
182  aLevelRule["PositionAndSpaceMode"] >>= nPositionAndSpaceMode;
183 
184  // for css::text::PositionAndSpaceMode::LABEL_ALIGNMENT
185  aLevelRule["LabelFollowedBy"] >>= nLabelFollowedBy;
186  aLevelRule["ListtabStopPosition"] >>= nListtabStopPosition;
187  aLevelRule["FirstLineIndent"] >>= nFirstLineIndent;
188  aLevelRule["IndentAt"] >>= nIndentAt;
189 
190  // for css::text::PositionAndSpaceMode::LABEL_WIDTH_AND_POSITION
191  aLevelRule["LeftMargin"] >>= nLeftMargin;
192  aLevelRule["SymbolTextDistance"] >>= nSymbolTextDistance;
193  aLevelRule["FirstLineOffset"] >>= nFirstLineOffset;
194 
195  aLevelRule["BulletChar"] >>= sBulletChar;
196  bHasFont = (aLevelRule["BulletFont"] >>= aBulletFont);
197  bHasColor = (aLevelRule["BulletColor"] >>= aBulletColor);
198  }
199 
200  if (nNumberingType != css::style::NumberingType::BITMAP) // TODO
201  {
202  if (nPositionAndSpaceMode
203  == css::text::PositionAndSpaceMode::LABEL_WIDTH_AND_POSITION)
204  {
205  nIndentAt = nLeftMargin;
206  nFirstLineIndent = nFirstLineOffset;
207  nListtabStopPosition = nSymbolTextDistance;
208  nLabelFollowedBy = SvxNumberFormat::LabelFollowedBy::LISTTAB;
209  }
210 
211  switch (nLabelFollowedBy)
212  {
213  case SvxNumberFormat::LabelFollowedBy::LISTTAB:
214  sLabelString += "\t";
215  break;
216  case SvxNumberFormat::LabelFollowedBy::SPACE:
217  sLabelString += " ";
218  break;
219  case SvxNumberFormat::LabelFollowedBy::NEWLINE:
220  sLabelString += "\n";
221  break;
222  }
223 
224  css::uno::Reference<css::text::XTextRange> xNumberText(xRange->getStart());
225  xNumberText->setString(sLabelString);
226  css::uno::Reference<css::beans::XPropertySet> xNumberProps(
227  xNumberText, css::uno::UNO_QUERY_THROW);
228  if (!sCharStyleName.isEmpty())
229  xNumberProps->setPropertyValue("CharStyleName", css::uno::Any(sCharStyleName));
230 
231  if (nNumberingType == css::style::NumberingType::CHAR_SPECIAL)
232  {
233  css::uno::Reference<css::text::XTextRange> xBulletText(xNumberText->getStart());
234  xBulletText->setString(sBulletChar);
235 
236  std::unordered_map<OUString, css::uno::Any> aNameValues;
237  if (bHasFont)
238  {
239  aNameValues.insert({
240  { "CharFontName", css::uno::Any(aBulletFont.Name) },
241  { "CharFontStyleName", css::uno::Any(aBulletFont.StyleName) },
242  { "CharFontFamily", css::uno::Any(aBulletFont.Family) },
243  { "CharFontCharSet", css::uno::Any(aBulletFont.CharSet) },
244  { "CharWeight", css::uno::Any(aBulletFont.Weight) },
245  { "CharUnderline", css::uno::Any(aBulletFont.Underline) },
246  { "CharStrikeout", css::uno::Any(aBulletFont.Strikeout) },
247  { "CharAutoKerning", css::uno::Any(aBulletFont.Kerning) },
248  { "CharFontPitch", css::uno::Any(aBulletFont.Pitch) },
249  { "CharWordMode", css::uno::Any(aBulletFont.WordLineMode) },
250  { "CharRotation", css::uno::Any(static_cast<sal_Int16>(
251  std::round(aBulletFont.Orientation * 10))) },
252  });
253  if (aBulletFont.Height)
254  aNameValues["CharHeight"] <<= aBulletFont.Height;
255  }
256  if (bHasColor)
257  {
258  aNameValues["CharColor"] <<= aBulletColor;
259  }
260 
261  if (css::uno::Reference<css::beans::XMultiPropertySet> xBulletMultiProps{
262  xBulletText, css::uno::UNO_QUERY })
263  {
264  xBulletMultiProps->setPropertyValues(
265  comphelper::mapKeysToSequence(aNameValues),
266  comphelper::mapValuesToSequence(aNameValues));
267  }
268  else
269  {
270  css::uno::Reference<css::beans::XPropertySet> xBulletProps(
271  xBulletText, css::uno::UNO_QUERY_THROW);
272  for (const auto& [rName, rVal] : aNameValues)
273  xBulletProps->setPropertyValue(rName, rVal);
274  }
275  }
276  else
277  {
278  // TODO: css::style::NumberingType::BITMAP
279  }
280 
281  (*it)->setPropertyValue("ParaLeftMargin", css::uno::Any(nIndentAt));
282  (*it)->setPropertyValue("ParaFirstLineIndent", css::uno::Any(nFirstLineIndent));
283  if (nLabelFollowedBy == SvxNumberFormat::LabelFollowedBy::LISTTAB)
284  {
285  css::uno::Sequence<css::style::TabStop> stops;
286  (*it)->getPropertyValue("ParaTabStops") >>= stops;
287  css::style::TabStop tabStop{};
288  tabStop.Position = nListtabStopPosition;
289  tabStop.Alignment = com::sun::star::style::TabAlign::TabAlign_LEFT;
290  tabStop.FillChar = ' ';
291  (*it)->setPropertyValue(
292  "ParaTabStops",
293  css::uno::Any(comphelper::combineSequences({ tabStop }, stops)));
294  // FIXME: What if added tap stop is greater than already defined ones?
295  }
296  }
297  else
298  {
299  continue; // for now, keep such lists as is
300  }
301  // In case of higher outline levels, each assignment of empty value just sets level 1
302  while ((*it)->getPropertyValue("NumberingRules") != css::uno::Any())
303  (*it)->setPropertyValue("NumberingRules", css::uno::Any());
304  }
305  }
306 }
307 
308 OUString
310 {
311  return "SwVbaListFormat";
312 }
313 
314 uno::Sequence< OUString >
316 {
317  static uno::Sequence< OUString > const aServiceNames
318  {
319  "ooo.vba.word.ListFormat"
320  };
321  return aServiceNames;
322 }
323 
324 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
uno::Reference< frame::XModel > getThisWordDoc(const uno::Reference< uno::XComponentContext > &xContext)
virtual void SAL_CALL ApplyListTemplate(const css::uno::Reference< ::ooo::vba::word::XListTemplate > &ListTemplate, const css::uno::Any &ContinuePreviousList, const css::uno::Any &ApplyTo, const css::uno::Any &DefaultListBehavior) override
virtual OUString getServiceImplName() override
OUString sCharStyleName
tools::Long const nLeftMargin
constexpr::Color COL_AUTO(ColorTransparency, 0xFF, 0xFF, 0xFF, 0xFF)
Sequence< OUString > aServiceNames
SwVbaListFormat(const css::uno::Reference< ooo::vba::XHelperInterface > &rParent, const css::uno::Reference< css::uno::XComponentContext > &rContext, const css::uno::Reference< css::text::XTextRange > &xTextRange)
sal_Int32 nFirstLineOffset
#define SAL_MAX_UINT16
void applyListTemplate(css::uno::Reference< css::beans::XPropertySet > const &xProps)
css::uno::Sequence< T > combineSequences(css::uno::Sequence< T > const &left, css::uno::Sequence< T > const &right)
#define SAL_MAX_INT32
sal_Int32 nSymbolTextDistance
css::uno::Reference< css::text::XTextRange > mxTextRange
static void addParagraphsToList(const Ref &a, std::vector< css::uno::Reference< css::beans::XPropertySet >> &rList)
css::uno::Reference< css::uno::XComponentContext > mxContext
virtual css::uno::Sequence< OUString > getServiceNames() override
css::uno::Sequence< typename M::mapped_type > mapValuesToSequence(M const &map)
#define SAL_MAX_INT16
virtual void SAL_CALL ConvertNumbersToText() override
virtual ~SwVbaListFormat() override
Reference< XModel > xModel
css::uno::Sequence< typename M::key_type > mapKeysToSequence(M const &map)