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>
34 #include <comphelper/sequenceashashmap.hxx>
35 #include <comphelper/scopeguard.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
SwVbaListFormat(const uno::Reference<ooo::vba::XHelperInterface> & rParent,const uno::Reference<uno::XComponentContext> & rContext,const uno::Reference<text::XTextRange> & xTextRange)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
~SwVbaListFormat()48 SwVbaListFormat::~SwVbaListFormat()
49 {
50 }
51
ApplyListTemplate(const css::uno::Reference<word::XListTemplate> & ListTemplate,const css::uno::Any & ContinuePreviousList,const css::uno::Any & ApplyTo,const css::uno::Any & DefaultListBehavior)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>
addParagraphsToList(const Ref & a,std::vector<css::uno::Reference<css::beans::XPropertySet>> & rList)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
ConvertNumbersToText()131 void SAL_CALL SwVbaListFormat::ConvertNumbersToText( )
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;
163 sal_Int32 nSymbolTextDistance = 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
getServiceImplName()309 SwVbaListFormat::getServiceImplName()
310 {
311 return "SwVbaListFormat";
312 }
313
314 uno::Sequence< OUString >
getServiceNames()315 SwVbaListFormat::getServiceNames()
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: */
325