/src/libreoffice/sw/source/uibase/config/StoredChapterNumbering.cxx
Line | Count | Source |
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 | | |
10 | | #include <uinums.hxx> |
11 | | |
12 | | #include <cppuhelper/implbase.hxx> |
13 | | |
14 | | #include <com/sun/star/container/XIndexReplace.hpp> |
15 | | #include <com/sun/star/container/XNamed.hpp> |
16 | | #include <com/sun/star/lang/IndexOutOfBoundsException.hpp> |
17 | | #include <com/sun/star/util/MeasureUnit.hpp> |
18 | | #include <com/sun/star/xml/sax/Writer.hpp> |
19 | | |
20 | | #include <comphelper/processfactory.hxx> |
21 | | |
22 | | #include <unotools/streamwrap.hxx> |
23 | | |
24 | | #include <xmloff/xmlnamespace.hxx> |
25 | | #include <xmloff/xmltoken.hxx> |
26 | | #include <xmloff/namespacemap.hxx> |
27 | | #include <xmloff/xmlexp.hxx> |
28 | | #include <xmloff/xmlnume.hxx> |
29 | | #include <xmloff/xmlimp.hxx> |
30 | | #include <xmloff/xmlictxt.hxx> |
31 | | #include <xmloff/xmlnumi.hxx> |
32 | | |
33 | | #include <vcl/svapp.hxx> |
34 | | #include <comphelper/diagnose_ex.hxx> |
35 | | |
36 | | #include <unosett.hxx> |
37 | | #include <names.hxx> |
38 | | |
39 | | |
40 | | using namespace ::com::sun::star; |
41 | | using namespace ::xmloff::token; |
42 | | |
43 | | namespace sw { |
44 | | |
45 | | class StoredChapterNumberingRules |
46 | | : public ::cppu::WeakImplHelper<container::XNamed,container::XIndexReplace> |
47 | | { |
48 | | private: |
49 | | // TODO in case this ever becomes accessible via API need an invalidate |
50 | | SwChapterNumRules & m_rNumRules; |
51 | | sal_uInt16 const m_nIndex; |
52 | | |
53 | | SwNumRulesWithName * GetOrCreateRules() |
54 | 0 | { |
55 | 0 | SwNumRulesWithName const* pRules(m_rNumRules.GetRules(m_nIndex)); |
56 | 0 | if (!pRules) |
57 | 0 | { |
58 | 0 | m_rNumRules.CreateEmptyNumRule(m_nIndex); |
59 | 0 | pRules = m_rNumRules.GetRules(m_nIndex); |
60 | 0 | assert(pRules); |
61 | 0 | } |
62 | 0 | return const_cast<SwNumRulesWithName*>(pRules); |
63 | 0 | } |
64 | | |
65 | | public: |
66 | | StoredChapterNumberingRules( |
67 | | SwChapterNumRules & rNumRules, sal_uInt16 const nIndex) |
68 | 0 | : m_rNumRules(rNumRules) |
69 | 0 | , m_nIndex(nIndex) |
70 | 0 | { |
71 | 0 | assert(m_nIndex < SwChapterNumRules::nMaxRules); |
72 | 0 | } |
73 | | |
74 | | // XNamed |
75 | | virtual OUString SAL_CALL getName() override |
76 | 0 | { |
77 | 0 | SolarMutexGuard g; |
78 | 0 | SwNumRulesWithName const* pRules(m_rNumRules.GetRules(m_nIndex)); |
79 | 0 | if (!pRules) |
80 | 0 | { |
81 | 0 | return OUString(); |
82 | 0 | } |
83 | 0 | return pRules->GetName().toString(); |
84 | 0 | } |
85 | | |
86 | | virtual void SAL_CALL setName(OUString const& rName) override |
87 | 0 | { |
88 | 0 | SolarMutexGuard g; |
89 | 0 | SwNumRulesWithName *const pRules(GetOrCreateRules()); |
90 | 0 | pRules->SetName(UIName(rName)); |
91 | 0 | } |
92 | | |
93 | | // XElementAccess |
94 | | virtual uno::Type SAL_CALL getElementType() override |
95 | 0 | { |
96 | 0 | return ::cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get(); |
97 | 0 | } |
98 | | |
99 | | virtual ::sal_Bool SAL_CALL hasElements() override |
100 | 0 | { |
101 | 0 | return true; |
102 | 0 | } |
103 | | |
104 | | // XIndexAccess |
105 | | virtual sal_Int32 SAL_CALL getCount() override |
106 | 0 | { |
107 | 0 | return MAXLEVEL; |
108 | 0 | } |
109 | | |
110 | | virtual uno::Any SAL_CALL getByIndex(sal_Int32 nIndex) override |
111 | 0 | { |
112 | 0 | if (nIndex < 0 || MAXLEVEL <= nIndex) |
113 | 0 | throw lang::IndexOutOfBoundsException(); |
114 | | |
115 | 0 | SolarMutexGuard g; |
116 | 0 | SwNumRulesWithName const* pRules(m_rNumRules.GetRules(m_nIndex)); |
117 | 0 | if (!pRules) |
118 | 0 | { |
119 | 0 | return uno::Any(); |
120 | 0 | } |
121 | 0 | SwNumFormat const* pNumFormat(nullptr); |
122 | 0 | UIName const* pCharStyleName(nullptr); |
123 | 0 | pRules->GetNumFormat(nIndex, pNumFormat, pCharStyleName); |
124 | 0 | if (!pNumFormat) |
125 | 0 | { // the dialog only fills in those levels that are non-default |
126 | 0 | return uno::Any(); // the export will ignore this level, yay |
127 | 0 | } |
128 | 0 | assert(pCharStyleName); |
129 | 0 | ProgName dummy; // pass in empty HeadingStyleName - can't import anyway |
130 | 0 | uno::Sequence<beans::PropertyValue> const ret( |
131 | 0 | SwXNumberingRules::GetPropertiesForNumFormat( |
132 | 0 | *pNumFormat, *pCharStyleName, &dummy, u""_ustr)); |
133 | 0 | return uno::Any(ret); |
134 | 0 | } |
135 | | |
136 | | // XIndexReplace |
137 | | virtual void SAL_CALL replaceByIndex( |
138 | | sal_Int32 nIndex, uno::Any const& rElement) override |
139 | 0 | { |
140 | 0 | if (nIndex < 0 || MAXLEVEL <= nIndex) |
141 | 0 | throw lang::IndexOutOfBoundsException(); |
142 | 0 | uno::Sequence<beans::PropertyValue> props; |
143 | 0 | if (!(rElement >>= props)) |
144 | 0 | throw lang::IllegalArgumentException(u"invalid type"_ustr, |
145 | 0 | getXWeak(), 1); |
146 | | |
147 | 0 | SolarMutexGuard g; |
148 | 0 | SwNumFormat aNumberFormat; |
149 | 0 | UIName charStyleName; |
150 | 0 | SwXNumberingRules::SetPropertiesToNumFormat( |
151 | 0 | aNumberFormat, |
152 | 0 | charStyleName, |
153 | 0 | nullptr, nullptr, nullptr, nullptr, nullptr, |
154 | 0 | props); |
155 | 0 | SwNumRulesWithName *const pRules(GetOrCreateRules()); |
156 | 0 | pRules->SetNumFormat(nIndex, aNumberFormat, charStyleName); |
157 | 0 | } |
158 | | }; |
159 | | |
160 | | namespace { |
161 | | |
162 | | class StoredChapterNumberingExport |
163 | | : public SvXMLExport |
164 | | { |
165 | | public: |
166 | | StoredChapterNumberingExport( |
167 | | uno::Reference<uno::XComponentContext> const& xContext, |
168 | | OUString const& rFileName, |
169 | | uno::Reference<xml::sax::XDocumentHandler> const& xHandler) |
170 | 0 | : SvXMLExport(xContext, u"sw::StoredChapterNumberingExport"_ustr, rFileName, |
171 | 0 | util::MeasureUnit::CM, xHandler) |
172 | 0 | { |
173 | 0 | GetNamespaceMap_().Add(GetXMLToken(XML_NP_OFFICE), |
174 | 0 | GetXMLToken(XML_N_OFFICE), XML_NAMESPACE_OFFICE); |
175 | 0 | GetNamespaceMap_().Add(GetXMLToken(XML_NP_TEXT), |
176 | 0 | GetXMLToken(XML_N_TEXT), XML_NAMESPACE_TEXT); |
177 | 0 | GetNamespaceMap_().Add(GetXMLToken(XML_NP_STYLE), |
178 | 0 | GetXMLToken(XML_N_STYLE), XML_NAMESPACE_STYLE); |
179 | 0 | GetNamespaceMap_().Add(GetXMLToken(XML_NP_FO), |
180 | 0 | GetXMLToken(XML_N_FO), XML_NAMESPACE_FO); |
181 | 0 | GetNamespaceMap_().Add(GetXMLToken(XML_NP_SVG), |
182 | 0 | GetXMLToken(XML_N_SVG), XML_NAMESPACE_SVG); |
183 | 0 | } |
184 | | |
185 | 0 | virtual void ExportAutoStyles_() override {} |
186 | 0 | virtual void ExportMasterStyles_() override {} |
187 | 0 | virtual void ExportContent_() override {} |
188 | | |
189 | | void ExportRule(SvxXMLNumRuleExport & rExport, |
190 | | uno::Reference<container::XIndexReplace> const& xRule) |
191 | 0 | { |
192 | 0 | uno::Reference<container::XNamed> const xNamed(xRule, uno::UNO_QUERY); |
193 | 0 | OUString const name(xNamed->getName()); |
194 | 0 | bool bEncoded(false); |
195 | 0 | AddAttribute( XML_NAMESPACE_STYLE, XML_NAME, |
196 | 0 | EncodeStyleName(name, &bEncoded) ); |
197 | 0 | if (bEncoded) |
198 | 0 | { |
199 | 0 | AddAttribute(XML_NAMESPACE_STYLE, XML_DISPLAY_NAME, name); |
200 | 0 | } |
201 | |
|
202 | 0 | SvXMLElementExport aElem( *this, XML_NAMESPACE_TEXT, |
203 | 0 | XML_OUTLINE_STYLE, true, true ); |
204 | 0 | rExport.exportLevelStyles(xRule, true); |
205 | 0 | } |
206 | | |
207 | | void ExportRules( |
208 | | std::set<UIName> const& rCharStyles, |
209 | | std::vector<uno::Reference<container::XIndexReplace>> const& rRules) |
210 | 0 | { |
211 | 0 | GetDocHandler()->startDocument(); |
212 | |
|
213 | 0 | AddAttribute( GetNamespaceMap_().GetAttrNameByKey(XML_NAMESPACE_OFFICE), |
214 | 0 | GetNamespaceMap_().GetNameByKey(XML_NAMESPACE_OFFICE)); |
215 | 0 | AddAttribute( GetNamespaceMap_().GetAttrNameByKey (XML_NAMESPACE_TEXT), |
216 | 0 | GetNamespaceMap_().GetNameByKey(XML_NAMESPACE_TEXT)); |
217 | 0 | AddAttribute( GetNamespaceMap_().GetAttrNameByKey(XML_NAMESPACE_STYLE), |
218 | 0 | GetNamespaceMap_().GetNameByKey(XML_NAMESPACE_STYLE)); |
219 | 0 | AddAttribute( GetNamespaceMap_().GetAttrNameByKey(XML_NAMESPACE_FO), |
220 | 0 | GetNamespaceMap_().GetNameByKey(XML_NAMESPACE_FO)); |
221 | 0 | AddAttribute( GetNamespaceMap_().GetAttrNameByKey(XML_NAMESPACE_SVG), |
222 | 0 | GetNamespaceMap_().GetNameByKey(XML_NAMESPACE_SVG)); |
223 | |
|
224 | 0 | { |
225 | | // let's just have an office:styles as a dummy root |
226 | 0 | SvXMLElementExport styles(*this, |
227 | 0 | XML_NAMESPACE_OFFICE, XML_STYLES, true, true); |
228 | | |
229 | | // horrible hack for char styles to get display-name mapping |
230 | 0 | for (const auto& rCharStyle : rCharStyles) |
231 | 0 | { |
232 | 0 | AddAttribute( XML_NAMESPACE_STYLE, XML_FAMILY, XML_TEXT ); |
233 | 0 | bool bEncoded(false); |
234 | 0 | AddAttribute( XML_NAMESPACE_STYLE, XML_NAME, |
235 | 0 | EncodeStyleName(rCharStyle.toString(), &bEncoded) ); |
236 | 0 | if (bEncoded) |
237 | 0 | { |
238 | 0 | AddAttribute(XML_NAMESPACE_STYLE, XML_DISPLAY_NAME, rCharStyle.toString()); |
239 | 0 | } |
240 | |
|
241 | 0 | SvXMLElementExport style(*this, |
242 | 0 | XML_NAMESPACE_STYLE, XML_STYLE, true, true); |
243 | 0 | } |
244 | |
|
245 | 0 | SvxXMLNumRuleExport numRuleExport(*this); |
246 | |
|
247 | 0 | for (const auto& rRule : rRules) |
248 | 0 | { |
249 | 0 | ExportRule(numRuleExport, rRule); |
250 | 0 | } |
251 | 0 | } |
252 | |
|
253 | 0 | GetDocHandler()->endDocument(); |
254 | 0 | } |
255 | | }; |
256 | | |
257 | | /** Dummy import context for style:style element that can just read the |
258 | | attributes needed to map name to display-name. |
259 | | Unfortunately the "real" context for this depends on some other things. |
260 | | The mapping is necessary to import the text:style-name attribute |
261 | | of the text:outline-level-style element. |
262 | | */ |
263 | | class StoredChapterNumberingDummyStyleContext |
264 | | : public SvXMLImportContext |
265 | | { |
266 | | public: |
267 | | StoredChapterNumberingDummyStyleContext( |
268 | | SvXMLImport & rImport, |
269 | | uno::Reference<xml::sax::XFastAttributeList> const& xAttrList) |
270 | 0 | : SvXMLImportContext(rImport) |
271 | 0 | { |
272 | 0 | OUString name; |
273 | 0 | OUString displayName; |
274 | 0 | XmlStyleFamily nFamily(XmlStyleFamily::DATA_STYLE); |
275 | |
|
276 | 0 | for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList )) |
277 | 0 | if (aIter.getToken() == (XML_NAMESPACE_STYLE | XML_FAMILY)) |
278 | 0 | { |
279 | 0 | if (IsXMLToken(aIter, XML_TEXT)) |
280 | 0 | nFamily = XmlStyleFamily::TEXT_TEXT; |
281 | 0 | else if (IsXMLToken(aIter, XML_NAME)) |
282 | 0 | name = aIter.toString(); |
283 | 0 | else if (IsXMLToken(aIter, XML_DISPLAY_NAME)) |
284 | 0 | displayName = aIter.toString(); |
285 | 0 | else |
286 | 0 | SAL_WARN("xmloff", "unknown value for style:family=" << aIter.toString()); |
287 | 0 | } |
288 | 0 | else |
289 | 0 | XMLOFF_WARN_UNKNOWN("xmloff", aIter); |
290 | |
|
291 | 0 | if (nFamily != XmlStyleFamily::DATA_STYLE && !name.isEmpty() && !displayName.isEmpty()) |
292 | 0 | { |
293 | 0 | rImport.AddStyleDisplayName(nFamily, name, displayName); |
294 | 0 | } |
295 | 0 | } |
296 | | }; |
297 | | |
298 | | class StoredChapterNumberingImport; |
299 | | |
300 | | class StoredChapterNumberingRootContext |
301 | | : public SvXMLImportContext |
302 | | { |
303 | | private: |
304 | | SwChapterNumRules & m_rNumRules; |
305 | | size_t m_nCounter; |
306 | | std::vector<rtl::Reference<SvxXMLListStyleContext>> m_Contexts; |
307 | | |
308 | | public: |
309 | | StoredChapterNumberingRootContext( |
310 | | SwChapterNumRules & rNumRules, SvXMLImport & rImport) |
311 | 0 | : SvXMLImportContext(rImport) |
312 | 0 | , m_rNumRules(rNumRules) |
313 | 0 | , m_nCounter(0) |
314 | 0 | { |
315 | 0 | } |
316 | | |
317 | | virtual void SAL_CALL endFastElement(sal_Int32 /*Element*/) override |
318 | 0 | { |
319 | 0 | assert(m_Contexts.size() <= SwChapterNumRules::nMaxRules); |
320 | 0 | for (auto iter = m_Contexts.begin(); iter != m_Contexts.end(); ++iter) |
321 | 0 | { |
322 | 0 | uno::Reference<container::XIndexReplace> const xRule( |
323 | 0 | new sw::StoredChapterNumberingRules(m_rNumRules, |
324 | 0 | iter - m_Contexts.begin())); |
325 | 0 | (*iter)->FillUnoNumRule(xRule); |
326 | | // TODO: xmloff's outline-style import seems to ignore this??? |
327 | 0 | uno::Reference<container::XNamed> const xNamed(xRule, uno::UNO_QUERY); |
328 | 0 | xNamed->setName((*iter)->GetDisplayName()); |
329 | 0 | } |
330 | 0 | } |
331 | | |
332 | | virtual css::uno::Reference<XFastContextHandler> SAL_CALL createFastChildContext( |
333 | | sal_Int32 Element, |
334 | | const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override |
335 | 0 | { |
336 | 0 | if (Element == XML_ELEMENT(STYLE, XML_STYLE)) |
337 | 0 | { |
338 | 0 | return new StoredChapterNumberingDummyStyleContext(GetImport(), xAttrList); |
339 | 0 | } |
340 | 0 | else if (Element == XML_ELEMENT(TEXT, XML_OUTLINE_STYLE)) |
341 | 0 | { |
342 | 0 | ++m_nCounter; |
343 | 0 | if (m_nCounter <= SwChapterNumRules::nMaxRules) |
344 | 0 | { |
345 | 0 | SvxXMLListStyleContext *const pContext( |
346 | 0 | new SvxXMLListStyleContext(GetImport(), true)); |
347 | 0 | m_Contexts.emplace_back(pContext); |
348 | 0 | return pContext; |
349 | 0 | } |
350 | 0 | } |
351 | | |
352 | 0 | return nullptr; |
353 | 0 | } |
354 | | }; |
355 | | |
356 | | class StoredChapterNumberingImport |
357 | | : public SvXMLImport |
358 | | { |
359 | | private: |
360 | | SwChapterNumRules & m_rNumRules; |
361 | | |
362 | | public: |
363 | | StoredChapterNumberingImport( |
364 | | uno::Reference<uno::XComponentContext> const& xContext, |
365 | | SwChapterNumRules & rNumRules) |
366 | 0 | : SvXMLImport(xContext, u"sw::StoredChapterNumberingImport"_ustr, SvXMLImportFlags::ALL) |
367 | 0 | , m_rNumRules(rNumRules) |
368 | 0 | { |
369 | 0 | } |
370 | | |
371 | | virtual SvXMLImportContext *CreateFastContext( sal_Int32 Element, |
372 | | const css::uno::Reference< css::xml::sax::XFastAttributeList > & /*xAttrList*/ ) override |
373 | 0 | { |
374 | 0 | if (Element == XML_ELEMENT(OFFICE, XML_STYLES)) |
375 | 0 | return new StoredChapterNumberingRootContext(m_rNumRules, *this); |
376 | 0 | return nullptr; |
377 | 0 | } |
378 | | }; |
379 | | |
380 | | } |
381 | | |
382 | | void ExportStoredChapterNumberingRules(SwChapterNumRules & rRules, |
383 | | SvStream & rStream, OUString const& rFileName) |
384 | 0 | { |
385 | 0 | uno::Reference<uno::XComponentContext> const& xContext( |
386 | 0 | ::comphelper::getProcessComponentContext()); |
387 | |
|
388 | 0 | uno::Reference<io::XOutputStream> const xOutStream( |
389 | 0 | new ::utl::OOutputStreamWrapper(rStream)); |
390 | |
|
391 | 0 | uno::Reference<xml::sax::XWriter> const xWriter( |
392 | 0 | xml::sax::Writer::create(xContext)); |
393 | |
|
394 | 0 | xWriter->setOutputStream(xOutStream); |
395 | |
|
396 | 0 | rtl::Reference<StoredChapterNumberingExport> exp(new StoredChapterNumberingExport(xContext, rFileName, xWriter)); |
397 | | |
398 | | // if style name contains a space then name != display-name |
399 | | // ... and the import needs to map from name to display-name then! |
400 | 0 | std::set<UIName> charStyles; |
401 | 0 | std::vector<uno::Reference<container::XIndexReplace>> numRules; |
402 | 0 | for (size_t i = 0; i < SwChapterNumRules::nMaxRules; ++i) |
403 | 0 | { |
404 | 0 | if (SwNumRulesWithName const* pRule = rRules.GetRules(i)) |
405 | 0 | { |
406 | 0 | for (size_t j = 0; j < MAXLEVEL; ++j) |
407 | 0 | { |
408 | 0 | SwNumFormat const* pDummy(nullptr); |
409 | 0 | UIName const* pCharStyleName(nullptr); |
410 | 0 | pRule->GetNumFormat(j, pDummy, pCharStyleName); |
411 | 0 | if (pCharStyleName && !pCharStyleName->isEmpty()) |
412 | 0 | { |
413 | 0 | charStyles.insert(*pCharStyleName); |
414 | 0 | } |
415 | 0 | } |
416 | 0 | numRules.push_back(new StoredChapterNumberingRules(rRules, i)); |
417 | 0 | } |
418 | 0 | } |
419 | |
|
420 | 0 | try |
421 | 0 | { |
422 | 0 | exp->ExportRules(charStyles, numRules); |
423 | 0 | } |
424 | 0 | catch (uno::Exception const&) |
425 | 0 | { |
426 | 0 | TOOLS_WARN_EXCEPTION("sw.ui", "ExportStoredChapterNumberingRules"); |
427 | 0 | } |
428 | 0 | } |
429 | | |
430 | | void ImportStoredChapterNumberingRules(SwChapterNumRules & rRules, |
431 | | SvStream & rStream, OUString const& rFileName) |
432 | 0 | { |
433 | 0 | uno::Reference<uno::XComponentContext> const& xContext( |
434 | 0 | ::comphelper::getProcessComponentContext()); |
435 | |
|
436 | 0 | uno::Reference<io::XInputStream> const xInStream( |
437 | 0 | new ::utl::OInputStreamWrapper(rStream)); |
438 | |
|
439 | 0 | rtl::Reference<StoredChapterNumberingImport> const xImport(new StoredChapterNumberingImport(xContext, rRules)); |
440 | |
|
441 | 0 | xml::sax::InputSource const source(xInStream, u""_ustr, u""_ustr, rFileName); |
442 | |
|
443 | 0 | try |
444 | 0 | { |
445 | 0 | xImport->parseStream(source); |
446 | 0 | } |
447 | 0 | catch (uno::Exception const&) |
448 | 0 | { |
449 | 0 | TOOLS_WARN_EXCEPTION("sw.ui", "ImportStoredChapterNumberingRules"); |
450 | 0 | } |
451 | 0 | } |
452 | | |
453 | | } // namespace sw |
454 | | |
455 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |