/src/libreoffice/xmloff/source/text/XMLSectionImportContext.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 | | * 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 | | |
20 | | #include "XMLSectionImportContext.hxx" |
21 | | #include "XMLSectionSourceImportContext.hxx" |
22 | | #include "XMLSectionSourceDDEImportContext.hxx" |
23 | | #include <comphelper/base64.hxx> |
24 | | #include <xmloff/xmlictxt.hxx> |
25 | | #include <xmloff/xmlimp.hxx> |
26 | | #include <xmloff/txtimp.hxx> |
27 | | #include <xmloff/namespacemap.hxx> |
28 | | #include <xmloff/xmlnamespace.hxx> |
29 | | #include <xmloff/xmltoken.hxx> |
30 | | #include <xmloff/prstylei.hxx> |
31 | | #include <sal/log.hxx> |
32 | | #include <sax/tools/converter.hxx> |
33 | | #include <com/sun/star/container/XNamed.hpp> |
34 | | #include <com/sun/star/frame/XModel.hpp> |
35 | | #include <com/sun/star/uno/Reference.h> |
36 | | #include <com/sun/star/text/XTextContent.hpp> |
37 | | #include <com/sun/star/beans/XPropertySet.hpp> |
38 | | #include <com/sun/star/lang/XMultiServiceFactory.hpp> |
39 | | #include <com/sun/star/text/ControlCharacter.hpp> |
40 | | |
41 | | |
42 | | using ::com::sun::star::beans::XPropertySet; |
43 | | using ::com::sun::star::uno::Reference; |
44 | | using ::com::sun::star::xml::sax::XFastAttributeList; |
45 | | using ::com::sun::star::lang::XMultiServiceFactory; |
46 | | using ::com::sun::star::container::XNamed; |
47 | | |
48 | | using namespace ::com::sun::star::uno; |
49 | | using namespace ::com::sun::star::text; |
50 | | using namespace ::xmloff::token; |
51 | | |
52 | | |
53 | | // section import: This one is fairly tricky due to a variety of |
54 | | // limits of the core or the API. The main problem is that if you |
55 | | // insert a section within another section, you can't move the cursor |
56 | | // between the ends of the inner and the enclosing section. To avoid |
57 | | // these problems, additional markers are first inserted and later deleted. |
58 | | XMLSectionImportContext::XMLSectionImportContext( SvXMLImport& rImport ) |
59 | 4.40k | : SvXMLImportContext(rImport) |
60 | 4.40k | , bProtect(false) |
61 | 4.40k | , bCondOK(false) |
62 | 4.40k | , bIsVisible(true) |
63 | 4.40k | , bValid(false) |
64 | 4.40k | , bSequenceOK(false) |
65 | 4.40k | , bIsCurrentlyVisible(true) |
66 | 4.40k | , bIsCurrentlyVisibleOK(false) |
67 | 4.40k | , bHasContent(false) |
68 | 4.40k | { |
69 | 4.40k | } |
70 | | |
71 | | XMLSectionImportContext::~XMLSectionImportContext() |
72 | 4.40k | { |
73 | 4.40k | } |
74 | | |
75 | | void XMLSectionImportContext::startFastElement( sal_Int32 nElement, |
76 | | const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) |
77 | 4.40k | { |
78 | | // process attributes |
79 | 4.40k | ProcessAttributes(xAttrList); |
80 | | |
81 | | // process index headers: |
82 | 4.40k | bool bIsIndexHeader = (nElement & TOKEN_MASK) == XML_INDEX_TITLE; |
83 | 4.40k | if (bIsIndexHeader) |
84 | 4.34k | { |
85 | 4.34k | bValid = true; |
86 | 4.34k | } |
87 | | |
88 | 4.40k | rtl::Reference<XMLTextImportHelper> rHelper = GetImport().GetTextImport(); |
89 | | |
90 | | // valid? |
91 | 4.40k | if (!bValid) |
92 | 13 | return; |
93 | | |
94 | | // create text section (as XPropertySet) |
95 | 4.38k | Reference<XMultiServiceFactory> xFactory( |
96 | 4.38k | GetImport().GetModel(),UNO_QUERY); |
97 | 4.38k | if (!xFactory.is()) |
98 | 0 | return; |
99 | | |
100 | 4.38k | Reference<XInterface> xIfc = |
101 | 4.38k | xFactory->createInstance( bIsIndexHeader ? u"com.sun.star.text.IndexHeaderSection"_ustr |
102 | 4.38k | : u"com.sun.star.text.TextSection"_ustr ); |
103 | 4.38k | if (!xIfc.is()) |
104 | 456 | return; |
105 | | |
106 | 3.93k | Reference<XPropertySet> xPropSet(xIfc, UNO_QUERY); |
107 | | |
108 | | // save PropertySet (for CreateChildContext) |
109 | 3.93k | xSectionPropertySet = xPropSet; |
110 | | |
111 | | // name |
112 | 3.93k | Reference<XNamed> xNamed(xPropSet, UNO_QUERY); |
113 | 3.93k | xNamed->setName(sName); |
114 | | |
115 | | // stylename? |
116 | 3.93k | if (!sStyleName.isEmpty()) |
117 | 64 | { |
118 | 64 | XMLPropStyleContext* pStyle = rHelper-> |
119 | 64 | FindSectionStyle(sStyleName); |
120 | | |
121 | 64 | if (pStyle != nullptr) |
122 | 49 | { |
123 | 49 | pStyle->FillPropertySet( xPropSet ); |
124 | 49 | } |
125 | 64 | } |
126 | | |
127 | | // IsVisible and condition (not for index headers) |
128 | 3.93k | if (! bIsIndexHeader) |
129 | 41 | { |
130 | 41 | xPropSet->setPropertyValue( u"IsVisible"_ustr, Any(bIsVisible) ); |
131 | | |
132 | | // #97450# hidden sections must be hidden on reload |
133 | | // For backwards compatibility, set flag only if it is |
134 | | // present |
135 | 41 | if( bIsCurrentlyVisibleOK ) |
136 | 0 | { |
137 | 0 | xPropSet->setPropertyValue( u"IsCurrentlyVisible"_ustr, Any(bIsCurrentlyVisible)); |
138 | 0 | } |
139 | | |
140 | 41 | if (bCondOK) |
141 | 0 | { |
142 | 0 | xPropSet->setPropertyValue( u"Condition"_ustr, Any(sCond) ); |
143 | 0 | } |
144 | 41 | } |
145 | | |
146 | | // password (only for regular sections) |
147 | 3.93k | if ( bSequenceOK && |
148 | 0 | (nElement & TOKEN_MASK) == XML_SECTION ) |
149 | 0 | { |
150 | 0 | xPropSet->setPropertyValue(u"ProtectionKey"_ustr, Any(aSequence)); |
151 | 0 | } |
152 | | |
153 | | // protection |
154 | 3.93k | xPropSet->setPropertyValue( u"IsProtected"_ustr, Any(bProtect) ); |
155 | | |
156 | | // insert marker, <paragraph>, marker; then insert |
157 | | // section over the first marker character, and delete the |
158 | | // last paragraph (and marker) when closing a section. |
159 | 3.93k | Reference<XTextRange> xStart = |
160 | 3.93k | rHelper->GetCursor()->getStart(); |
161 | 3.93k | #ifndef DBG_UTIL |
162 | 3.93k | OUString sMarkerString(" "); |
163 | | #else |
164 | | OUString sMarkerString(u"X"_ustr); |
165 | | #endif |
166 | 3.93k | rHelper->InsertString(sMarkerString); |
167 | 3.93k | rHelper->InsertControlCharacter( |
168 | 3.93k | ControlCharacter::APPEND_PARAGRAPH ); |
169 | 3.93k | rHelper->InsertString(sMarkerString); |
170 | | |
171 | | // select first marker |
172 | 3.93k | rHelper->GetCursor()->gotoRange(xStart, false); |
173 | 3.93k | rHelper->GetCursor()->goRight(1, true); |
174 | | |
175 | | // convert section to XTextContent |
176 | 3.93k | Reference<XTextContent> xTextContent(xSectionPropertySet, |
177 | 3.93k | UNO_QUERY); |
178 | | |
179 | | // and insert (over marker) |
180 | 3.93k | rHelper->GetText()->insertTextContent( |
181 | 3.93k | rHelper->GetCursorAsRange(), xTextContent, true ); |
182 | | |
183 | | // and delete first marker (in section) |
184 | 3.93k | rHelper->GetText()->insertString( |
185 | 3.93k | rHelper->GetCursorAsRange(), u""_ustr, true); |
186 | | |
187 | | // finally, check for redlines that should start at |
188 | | // the section start node |
189 | 3.93k | rHelper->RedlineAdjustStartNodeCursor(); // start ??? |
190 | | |
191 | | // xml:id for RDF metadata |
192 | 3.93k | GetImport().SetXmlId(xIfc, sXmlId); |
193 | 3.93k | } |
194 | | |
195 | | void XMLSectionImportContext::ProcessAttributes( |
196 | | const Reference<XFastAttributeList> & xAttrList ) |
197 | 4.40k | { |
198 | 4.40k | for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) |
199 | 218 | { |
200 | 218 | switch (aIter.getToken()) |
201 | 218 | { |
202 | 90 | case XML_ELEMENT(XML, XML_ID): |
203 | 90 | sXmlId = aIter.toString(); |
204 | 90 | break; |
205 | 64 | case XML_ELEMENT(TEXT, XML_STYLE_NAME): |
206 | 64 | sStyleName = aIter.toString(); |
207 | 64 | break; |
208 | 64 | case XML_ELEMENT(TEXT, XML_NAME): |
209 | 64 | sName = aIter.toString(); |
210 | 64 | bValid = true; |
211 | 64 | break; |
212 | 0 | case XML_ELEMENT(TEXT, XML_CONDITION): |
213 | 0 | { |
214 | 0 | OUString sValue = aIter.toString(); |
215 | 0 | OUString sTmp; |
216 | 0 | sal_uInt16 nPrefix = GetImport().GetNamespaceMap(). |
217 | 0 | GetKeyByAttrValueQName(sValue, &sTmp); |
218 | 0 | if( XML_NAMESPACE_OOOW == nPrefix ) |
219 | 0 | { |
220 | 0 | sCond = sTmp; |
221 | 0 | bCondOK = true; |
222 | 0 | } |
223 | 0 | else |
224 | 0 | sCond = sValue; |
225 | 0 | } |
226 | 0 | break; |
227 | 0 | case XML_ELEMENT(TEXT, XML_DISPLAY): |
228 | 0 | if (IsXMLToken(aIter, XML_TRUE)) |
229 | 0 | { |
230 | 0 | bIsVisible = true; |
231 | 0 | } |
232 | 0 | else if ( IsXMLToken(aIter, XML_NONE) || |
233 | 0 | IsXMLToken(aIter, XML_CONDITION) ) |
234 | 0 | { |
235 | 0 | bIsVisible = false; |
236 | 0 | } |
237 | | // else: ignore |
238 | 0 | break; |
239 | 0 | case XML_ELEMENT(TEXT, XML_IS_HIDDEN): |
240 | 0 | { |
241 | 0 | bool bTmp(false); |
242 | 0 | if (::sax::Converter::convertBool(bTmp, aIter.toView())) |
243 | 0 | { |
244 | 0 | bIsCurrentlyVisible = !bTmp; |
245 | 0 | bIsCurrentlyVisibleOK = true; |
246 | 0 | } |
247 | 0 | } |
248 | 0 | break; |
249 | 0 | case XML_ELEMENT(TEXT, XML_PROTECTION_KEY): |
250 | 0 | ::comphelper::Base64::decode(aSequence, aIter.toString()); |
251 | 0 | bSequenceOK = true; |
252 | 0 | break; |
253 | 0 | case XML_ELEMENT(TEXT, XML_PROTECTED): |
254 | | // compatibility with SRC629 (or earlier) versions |
255 | 0 | case XML_ELEMENT(TEXT, XML_PROTECT): |
256 | 0 | { |
257 | 0 | bool bTmp(false); |
258 | 0 | if (::sax::Converter::convertBool(bTmp, aIter.toView())) |
259 | 0 | { |
260 | 0 | bProtect = bTmp; |
261 | 0 | } |
262 | 0 | break; |
263 | 0 | } |
264 | 0 | default: |
265 | | // ignore |
266 | 0 | XMLOFF_WARN_UNKNOWN("xmloff", aIter); |
267 | 0 | break; |
268 | 218 | } |
269 | 218 | } |
270 | 4.40k | } |
271 | | |
272 | | void XMLSectionImportContext::endFastElement(sal_Int32 ) |
273 | 123 | { |
274 | | // get rid of last paragraph |
275 | | // (unless it's the only paragraph in the section) |
276 | 123 | rtl::Reference<XMLTextImportHelper> rHelper = GetImport().GetTextImport(); |
277 | 123 | rHelper->GetCursor()->goRight(1, false); |
278 | 123 | if (bHasContent) |
279 | 88 | { |
280 | 88 | rHelper->GetCursor()->goLeft(1, true); |
281 | 88 | rHelper->GetText()->insertString(rHelper->GetCursorAsRange(), |
282 | 88 | u""_ustr, true); |
283 | 88 | } |
284 | | |
285 | | // and delete second marker |
286 | 123 | rHelper->GetCursor()->goRight(1, true); |
287 | 123 | rHelper->GetText()->insertString(rHelper->GetCursorAsRange(), |
288 | 123 | u""_ustr, true); |
289 | | |
290 | | // check for redlines to our endnode |
291 | 123 | rHelper->RedlineAdjustStartNodeCursor(); |
292 | 123 | } |
293 | | |
294 | | css::uno::Reference< css::xml::sax::XFastContextHandler > XMLSectionImportContext::createFastChildContext( |
295 | | sal_Int32 nElement, |
296 | | const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) |
297 | 48.1k | { |
298 | | // section-source (-dde) elements |
299 | 48.1k | if ( nElement == XML_ELEMENT(TEXT, XML_SECTION_SOURCE) ) |
300 | 0 | { |
301 | 0 | return new XMLSectionSourceImportContext(GetImport(), |
302 | 0 | xSectionPropertySet); |
303 | 0 | } |
304 | 48.1k | else if ( nElement == XML_ELEMENT(OFFICE, XML_DDE_SOURCE) ) |
305 | 0 | { |
306 | 0 | return new XMLSectionSourceDDEImportContext(GetImport(), |
307 | 0 | xSectionPropertySet); |
308 | 0 | } |
309 | 48.1k | else |
310 | 48.1k | { |
311 | | // otherwise: text context |
312 | 48.1k | auto pContext = GetImport().GetTextImport()->CreateTextChildContext( |
313 | 48.1k | GetImport(), nElement, xAttrList, XMLTextType::Section ); |
314 | | |
315 | | // if that fails, default context |
316 | 48.1k | if (pContext) |
317 | 35.5k | bHasContent = true; |
318 | 12.6k | else |
319 | 48.1k | XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); |
320 | 48.1k | return pContext; |
321 | 48.1k | } |
322 | 48.1k | } |
323 | | |
324 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |