/src/libreoffice/xmloff/source/forms/layerexport.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 "layerexport.hxx" |
21 | | #include "strings.hxx" |
22 | | #include <xmloff/xmlexp.hxx> |
23 | | #include <xmloff/xmlprmap.hxx> |
24 | | #include <xmloff/prhdlfac.hxx> |
25 | | #include "elementexport.hxx" |
26 | | #include <xmloff/families.hxx> |
27 | | #include <xmloff/contextid.hxx> |
28 | | #include <xmloff/controlpropertyhdl.hxx> |
29 | | #include <xmloff/maptype.hxx> |
30 | | #include <xmloff/NamedBoolPropertyHdl.hxx> |
31 | | #include <sal/log.hxx> |
32 | | #include <comphelper/diagnose_ex.hxx> |
33 | | #include "controlpropertymap.hxx" |
34 | | #include <com/sun/star/container/XIndexAccess.hpp> |
35 | | #include <com/sun/star/form/XFormsSupplier2.hpp> |
36 | | #include <com/sun/star/frame/XModel.hpp> |
37 | | #include <com/sun/star/xforms/XFormsSupplier.hpp> |
38 | | #include <com/sun/star/form/FormComponentType.hpp> |
39 | | #include <com/sun/star/lang/XServiceInfo.hpp> |
40 | | #include <com/sun/star/script/XEventAttacherManager.hpp> |
41 | | #include <com/sun/star/util/NumberFormatsSupplier.hpp> |
42 | | #include <xmloff/XMLEventExport.hxx> |
43 | | #include "formevents.hxx" |
44 | | #include <xmloff/xmlnumfe.hxx> |
45 | | #include <xmloff/xformsexport.hxx> |
46 | | |
47 | | #include <com/sun/star/text/XText.hpp> |
48 | | |
49 | | #include <stack> |
50 | | #include <numeric> |
51 | | |
52 | | namespace xmloff |
53 | | { |
54 | | |
55 | | using namespace ::com::sun::star::uno; |
56 | | using namespace ::com::sun::star::awt; |
57 | | using namespace ::com::sun::star::lang; |
58 | | using namespace ::com::sun::star::beans; |
59 | | using namespace ::com::sun::star::container; |
60 | | using namespace ::com::sun::star::drawing; |
61 | | using namespace ::com::sun::star::form; |
62 | | using namespace ::com::sun::star::script; |
63 | | using namespace ::com::sun::star::util; |
64 | | using namespace ::com::sun::star::text; |
65 | | |
66 | | //= OFormLayerXMLExport_Impl |
67 | | const OUString& OFormLayerXMLExport_Impl::getControlNumberStyleNamePrefix() |
68 | 0 | { |
69 | 0 | static constexpr OUString s_sControlNumberStyleNamePrefix(u"C"_ustr); |
70 | 0 | return s_sControlNumberStyleNamePrefix; |
71 | 0 | } |
72 | | |
73 | | OFormLayerXMLExport_Impl::OFormLayerXMLExport_Impl(SvXMLExport& _rContext) |
74 | 75 | :m_rContext(_rContext) |
75 | 75 | { |
76 | 75 | initializePropertyMaps(); |
77 | | |
78 | | // add our style family to the export context's style pool |
79 | 75 | m_xPropertyHandlerFactory = new OControlPropertyHandlerFactory(); |
80 | 75 | ::rtl::Reference< XMLPropertySetMapper > xStylePropertiesMapper = new XMLPropertySetMapper( getControlStylePropertyMap(), m_xPropertyHandlerFactory, true ); |
81 | 75 | m_xStyleExportMapper = new OFormComponentStyleExportMapper( xStylePropertiesMapper ); |
82 | | |
83 | | // our style family |
84 | 75 | m_rContext.GetAutoStylePool()->AddFamily( |
85 | 75 | XmlStyleFamily::CONTROL_ID, token::GetXMLToken(token::XML_PARAGRAPH), |
86 | 75 | m_xStyleExportMapper.get(), |
87 | 75 | XML_STYLE_FAMILY_CONTROL_PREFIX |
88 | 75 | ); |
89 | | |
90 | | // add our event translation table |
91 | 75 | m_rContext.GetEventExport().AddTranslationTable(g_pFormsEventTranslation); |
92 | | |
93 | 75 | clear(); |
94 | 75 | } |
95 | | |
96 | | OFormLayerXMLExport_Impl::~OFormLayerXMLExport_Impl() |
97 | 75 | { |
98 | 75 | } |
99 | | |
100 | | bool OFormLayerXMLExport_Impl::impl_isFormPageContainingForms(const Reference< XDrawPage >& _rxDrawPage, Reference< XIndexAccess >& _rxForms) |
101 | 157 | { |
102 | 157 | Reference< XFormsSupplier2 > xFormsSupp(_rxDrawPage, UNO_QUERY); |
103 | 157 | OSL_ENSURE(xFormsSupp.is(), "OFormLayerXMLExport_Impl::impl_isFormPageContainingForms: invalid draw page (no XFormsSupplier)! Doin' nothing!"); |
104 | 157 | if (!xFormsSupp.is()) |
105 | 0 | return false; |
106 | | |
107 | 157 | if ( !xFormsSupp->hasForms() ) |
108 | | // nothing to do at all |
109 | 157 | return false; |
110 | | |
111 | 0 | _rxForms.set(xFormsSupp->getForms(), UNO_QUERY); |
112 | 0 | Reference< XServiceInfo > xSI(_rxForms, UNO_QUERY); // order is important! |
113 | 0 | OSL_ENSURE(xSI.is(), "OFormLayerXMLExport_Impl::impl_isFormPageContainingForms: invalid collection (must not be NULL and must have a ServiceInfo)!"); |
114 | 0 | if (!xSI.is()) |
115 | 0 | return false; |
116 | | |
117 | 0 | if (!xSI->supportsService(u"com.sun.star.form.Forms"_ustr)) |
118 | 0 | { |
119 | 0 | OSL_FAIL("OFormLayerXMLExport_Impl::impl_isFormPageContainingForms: invalid collection (is no com.sun.star.form.Forms)!"); |
120 | | // nothing to do |
121 | 0 | return false; |
122 | 0 | } |
123 | 0 | return true; |
124 | 0 | } |
125 | | |
126 | | void OFormLayerXMLExport_Impl::exportGridColumn(const Reference< XPropertySet >& _rxColumn, |
127 | | const Sequence< ScriptEventDescriptor >& _rEvents) |
128 | 0 | { |
129 | | // do the exporting |
130 | 0 | OColumnExport aExportImpl(*this, _rxColumn, getControlId( _rxColumn ), _rEvents); |
131 | 0 | aExportImpl.doExport(); |
132 | 0 | } |
133 | | |
134 | | void OFormLayerXMLExport_Impl::exportControl(const Reference< XPropertySet >& _rxControl, |
135 | | const Sequence< ScriptEventDescriptor >& _rEvents) |
136 | 0 | { |
137 | | // the list of the referring controls |
138 | 0 | OUString sReferringControls; |
139 | 0 | MapPropertySet2String::const_iterator aReferring = m_aCurrentPageReferring->second.find(_rxControl); |
140 | 0 | if (aReferring != m_aCurrentPageReferring->second.end()) |
141 | 0 | sReferringControls = aReferring->second; |
142 | | |
143 | | // the control id (should already have been created in examineForms) |
144 | 0 | OUString sControlId( getControlId( _rxControl ) ); |
145 | | |
146 | | // do the exporting |
147 | 0 | OControlExport aExportImpl(*this, _rxControl, sControlId, sReferringControls, _rEvents); |
148 | 0 | aExportImpl.doExport(); |
149 | 0 | } |
150 | | |
151 | | void OFormLayerXMLExport_Impl::exportForm(const Reference< XPropertySet >& _rxProps, |
152 | | const Sequence< ScriptEventDescriptor >& _rEvents) |
153 | 0 | { |
154 | 0 | OSL_ENSURE(_rxProps.is(), "OFormLayerXMLExport_Impl::exportForm: invalid property set!"); |
155 | 0 | OFormExport aAttributeHandler(*this, _rxProps, _rEvents); |
156 | 0 | aAttributeHandler.doExport(); |
157 | 0 | } |
158 | | |
159 | | ::rtl::Reference< SvXMLExportPropertyMapper > OFormLayerXMLExport_Impl::getStylePropertyMapper() |
160 | 0 | { |
161 | 0 | return m_xStyleExportMapper; |
162 | 0 | } |
163 | | |
164 | | SvXMLExport& OFormLayerXMLExport_Impl::getGlobalContext() |
165 | 0 | { |
166 | 0 | return m_rContext; |
167 | 0 | } |
168 | | |
169 | | void OFormLayerXMLExport_Impl::exportCollectionElements(const Reference< XIndexAccess >& _rxCollection) |
170 | 0 | { |
171 | | // step through all the elements of the collection |
172 | 0 | sal_Int32 nElements = _rxCollection->getCount(); |
173 | |
|
174 | 0 | Reference< XEventAttacherManager > xElementEventManager(_rxCollection, UNO_QUERY); |
175 | 0 | Sequence< ScriptEventDescriptor > aElementEvents; |
176 | |
|
177 | 0 | Reference< XPropertySetInfo > xPropsInfo; |
178 | 0 | for (sal_Int32 i=0; i<nElements; ++i) |
179 | 0 | { |
180 | 0 | try |
181 | 0 | { |
182 | | // extract the current element |
183 | 0 | Reference< XPropertySet > xCurrentProps( _rxCollection->getByIndex(i), UNO_QUERY ); |
184 | 0 | OSL_ENSURE(xCurrentProps.is(), "OFormLayerXMLExport_Impl::exportCollectionElements: invalid child element, skipping!"); |
185 | 0 | if (!xCurrentProps.is()) |
186 | 0 | continue; |
187 | | |
188 | | // check if there is a ClassId property on the current element. If so, we assume it to be a control |
189 | 0 | xPropsInfo = xCurrentProps->getPropertySetInfo(); |
190 | 0 | OSL_ENSURE(xPropsInfo.is(), "OFormLayerXMLExport_Impl::exportCollectionElements: no property set info!"); |
191 | 0 | if (!xPropsInfo.is()) |
192 | | // without this, a lot of stuff in the export routines may fail |
193 | 0 | continue; |
194 | | |
195 | | // if the element is part of an ignore list, we are not allowed to export it |
196 | 0 | if ( m_aIgnoreList.end() != m_aIgnoreList.find( xCurrentProps ) ) |
197 | 0 | continue; |
198 | | |
199 | 0 | if (xElementEventManager.is()) |
200 | 0 | aElementEvents = xElementEventManager->getScriptEvents(i); |
201 | |
|
202 | 0 | if (xPropsInfo->hasPropertyByName(PROPERTY_COLUMNSERVICENAME)) |
203 | 0 | { |
204 | 0 | exportGridColumn(xCurrentProps, aElementEvents); |
205 | 0 | } |
206 | 0 | else if (xPropsInfo->hasPropertyByName(PROPERTY_CLASSID)) |
207 | 0 | { |
208 | 0 | exportControl(xCurrentProps, aElementEvents); |
209 | 0 | } |
210 | 0 | else |
211 | 0 | { |
212 | 0 | exportForm(xCurrentProps, aElementEvents); |
213 | 0 | } |
214 | 0 | } |
215 | 0 | catch(Exception&) |
216 | 0 | { |
217 | 0 | TOOLS_WARN_EXCEPTION("xmloff.forms", |
218 | 0 | "caught an exception ... skipping the current element!"); |
219 | 0 | continue; |
220 | 0 | } |
221 | 0 | } |
222 | 0 | } |
223 | | |
224 | | OUString OFormLayerXMLExport_Impl::getObjectStyleName( const Reference< XPropertySet >& _rxObject ) |
225 | 0 | { |
226 | 0 | OUString aObjectStyle; |
227 | |
|
228 | 0 | MapPropertySet2String::const_iterator aObjectStylePos = m_aGridColumnStyles.find( _rxObject ); |
229 | 0 | if ( m_aGridColumnStyles.end() != aObjectStylePos ) |
230 | 0 | aObjectStyle = aObjectStylePos->second; |
231 | 0 | return aObjectStyle; |
232 | 0 | } |
233 | | |
234 | | void OFormLayerXMLExport_Impl::clear() |
235 | 75 | { |
236 | 75 | m_aControlIds.clear(); |
237 | 75 | m_aReferringControls.clear(); |
238 | 75 | m_aCurrentPageIds = m_aControlIds.end(); |
239 | 75 | m_aCurrentPageReferring = m_aReferringControls.end(); |
240 | | |
241 | 75 | m_aControlNumberFormats.clear(); |
242 | 75 | m_aGridColumnStyles.clear(); |
243 | | |
244 | 75 | m_aIgnoreList.clear(); |
245 | 75 | } |
246 | | |
247 | | void OFormLayerXMLExport_Impl::exportAutoControlNumberStyles() |
248 | 49 | { |
249 | 49 | if ( m_pControlNumberStyles ) |
250 | 0 | m_pControlNumberStyles->Export( true ); |
251 | 49 | } |
252 | | |
253 | | void OFormLayerXMLExport_Impl::exportAutoStyles() |
254 | 49 | { |
255 | 49 | m_rContext.GetAutoStylePool()->exportXML( XmlStyleFamily::CONTROL_ID ); |
256 | 49 | } |
257 | | |
258 | | void OFormLayerXMLExport_Impl::exportForms(const Reference< XDrawPage >& _rxDrawPage) |
259 | 0 | { |
260 | | // get the forms collection of the page |
261 | 0 | Reference< XIndexAccess > xCollectionIndex; |
262 | 0 | if (!impl_isFormPageContainingForms(_rxDrawPage, xCollectionIndex)) |
263 | 0 | { |
264 | 0 | return; |
265 | 0 | } |
266 | | |
267 | 0 | bool bPageIsKnown = implMoveIterators(_rxDrawPage, false); |
268 | 0 | OSL_ENSURE(bPageIsKnown, "OFormLayerXMLExport_Impl::exportForms: exporting a page which has not been examined!"); |
269 | | |
270 | | // export forms collection |
271 | 0 | exportCollectionElements(xCollectionIndex); |
272 | 0 | } |
273 | | |
274 | | void OFormLayerXMLExport_Impl::exportXForms() const |
275 | 0 | { |
276 | | // export XForms models |
277 | 0 | ::exportXForms( m_rContext ); |
278 | 0 | } |
279 | | |
280 | | bool OFormLayerXMLExport_Impl::pageContainsForms( const Reference< XDrawPage >& _rxDrawPage ) |
281 | 0 | { |
282 | 0 | Reference< XFormsSupplier2 > xFormsSupp( _rxDrawPage, UNO_QUERY ); |
283 | 0 | SAL_WARN_IF( !xFormsSupp.is(), "xmloff", "OFormLayerXMLExport_Impl::pageContainsForms: no XFormsSupplier2!" ); |
284 | 0 | return xFormsSupp.is() && xFormsSupp->hasForms(); |
285 | 0 | } |
286 | | |
287 | | bool OFormLayerXMLExport_Impl::documentContainsXForms() const |
288 | 0 | { |
289 | 0 | Reference< css::xforms::XFormsSupplier > xXFormSupp( m_rContext.GetModel(), UNO_QUERY ); |
290 | 0 | Reference< XNameContainer > xForms; |
291 | 0 | if ( xXFormSupp.is() ) |
292 | 0 | xForms = xXFormSupp->getXForms(); |
293 | 0 | return xForms.is() && xForms->hasElements(); |
294 | 0 | } |
295 | | |
296 | | bool OFormLayerXMLExport_Impl::implMoveIterators(const Reference< XDrawPage >& _rxDrawPage, bool _bClear) |
297 | 102 | { |
298 | 102 | if (!_rxDrawPage.is()) |
299 | 0 | return false; |
300 | | |
301 | 102 | bool bKnownPage = false; |
302 | | |
303 | | // the one for the ids |
304 | 102 | m_aCurrentPageIds = m_aControlIds.find(_rxDrawPage); |
305 | 102 | if (m_aControlIds.end() == m_aCurrentPageIds) |
306 | 102 | { |
307 | 102 | m_aControlIds[_rxDrawPage] = MapPropertySet2String(); |
308 | 102 | m_aCurrentPageIds = m_aControlIds.find(_rxDrawPage); |
309 | 102 | } |
310 | 0 | else |
311 | 0 | { |
312 | 0 | bKnownPage = true; |
313 | 0 | if (_bClear && !m_aCurrentPageIds->second.empty() ) |
314 | 0 | m_aCurrentPageIds->second.clear(); |
315 | 0 | } |
316 | | |
317 | | // the one for the ids of the referring controls |
318 | 102 | m_aCurrentPageReferring = m_aReferringControls.find(_rxDrawPage); |
319 | 102 | if (m_aReferringControls.end() == m_aCurrentPageReferring) |
320 | 102 | { |
321 | 102 | m_aReferringControls[_rxDrawPage] = MapPropertySet2String(); |
322 | 102 | m_aCurrentPageReferring = m_aReferringControls.find(_rxDrawPage); |
323 | 102 | } |
324 | 0 | else |
325 | 0 | { |
326 | 0 | bKnownPage = true; |
327 | 0 | if (_bClear && !m_aCurrentPageReferring->second.empty() ) |
328 | 0 | m_aCurrentPageReferring->second.clear(); |
329 | 0 | } |
330 | 102 | return bKnownPage; |
331 | 102 | } |
332 | | |
333 | | bool OFormLayerXMLExport_Impl::seekPage(const Reference< XDrawPage >& _rxDrawPage) |
334 | 102 | { |
335 | 102 | bool bKnownPage = implMoveIterators( _rxDrawPage, false ); |
336 | 102 | if ( bKnownPage ) |
337 | 0 | return true; |
338 | | |
339 | | // if the page is not yet known, this does not automatically mean that it has |
340 | | // not been examined. Instead, examineForms returns silently and successfully |
341 | | // if a page is a XFormsPageSupplier2, but does not have a forms collection |
342 | | // (This behaviour of examineForms is a performance optimization, to not force |
343 | | // the page to create a forms container just to see that it's empty.) |
344 | | |
345 | | // So, in such a case, seekPage is considered to be successful, too, though the |
346 | | // page was not yet known |
347 | 102 | Reference< XFormsSupplier2 > xFormsSupp( _rxDrawPage, UNO_QUERY ); |
348 | 102 | if ( xFormsSupp.is() && !xFormsSupp->hasForms() ) |
349 | 102 | return true; |
350 | | |
351 | | // anything else means that the page has not been examined before, or it's no |
352 | | // valid form page. Both cases are Bad (TM). |
353 | 0 | return false; |
354 | 102 | } |
355 | | |
356 | | OUString OFormLayerXMLExport_Impl::getControlId(const Reference< XPropertySet >& _rxControl) |
357 | 0 | { |
358 | 0 | if (m_aCurrentPageIds == m_aControlIds.end()) |
359 | 0 | return OUString(); |
360 | | |
361 | 0 | OSL_ENSURE(m_aCurrentPageIds->second.end() != m_aCurrentPageIds->second.find(_rxControl), |
362 | 0 | "OFormLayerXMLExport_Impl::getControlId: can not find the control!"); |
363 | 0 | return m_aCurrentPageIds->second[_rxControl]; |
364 | 0 | } |
365 | | |
366 | | OUString OFormLayerXMLExport_Impl::getImmediateNumberStyle( const Reference< XPropertySet >& _rxObject ) |
367 | 0 | { |
368 | 0 | OUString sNumberStyle; |
369 | |
|
370 | 0 | sal_Int32 nOwnFormatKey = implExamineControlNumberFormat( _rxObject ); |
371 | 0 | if ( -1 != nOwnFormatKey ) |
372 | 0 | sNumberStyle = getControlNumberStyleExport()->GetStyleName( nOwnFormatKey ); |
373 | |
|
374 | 0 | return sNumberStyle; |
375 | 0 | } |
376 | | |
377 | | OUString OFormLayerXMLExport_Impl::getControlNumberStyle( const Reference< XPropertySet >& _rxControl ) |
378 | 0 | { |
379 | 0 | OUString sNumberStyle; |
380 | |
|
381 | 0 | MapPropertySet2Int::const_iterator aControlFormatPos = m_aControlNumberFormats.find(_rxControl); |
382 | 0 | if (m_aControlNumberFormats.end() != aControlFormatPos) |
383 | 0 | { |
384 | 0 | OSL_ENSURE(m_pControlNumberStyles, "OFormLayerXMLExport_Impl::getControlNumberStyle: have a control which has a format style, but no style exporter!"); |
385 | 0 | sNumberStyle = getControlNumberStyleExport()->GetStyleName(aControlFormatPos->second); |
386 | 0 | } |
387 | | // it's allowed to ask for a control which does not have format information. |
388 | | // (This is for performance reasons) |
389 | |
|
390 | 0 | return sNumberStyle; |
391 | 0 | } |
392 | | |
393 | | void OFormLayerXMLExport_Impl::examineForms(const Reference< XDrawPage >& _rxDrawPage) |
394 | 157 | { |
395 | | // get the forms collection of the page |
396 | 157 | Reference< XIndexAccess > xCollectionIndex; |
397 | 157 | if (!impl_isFormPageContainingForms(_rxDrawPage, xCollectionIndex)) |
398 | 157 | { |
399 | 157 | return; |
400 | 157 | } |
401 | | |
402 | | // move the iterator which specify the currently handled page |
403 | 0 | bool bPageIsKnown = implMoveIterators(_rxDrawPage, true); |
404 | 0 | OSL_ENSURE(!bPageIsKnown, "OFormLayerXMLExport_Impl::examineForms: examining a page twice!"); |
405 | |
|
406 | 0 | ::std::stack< Reference< XIndexAccess > > aContainerHistory; |
407 | 0 | ::std::stack< sal_Int32 > aIndexHistory; |
408 | |
|
409 | 0 | Reference< XIndexAccess > xLoop = xCollectionIndex; |
410 | 0 | sal_Int32 nChildPos = 0; |
411 | 0 | do |
412 | 0 | { |
413 | 0 | if (nChildPos < xLoop->getCount()) |
414 | 0 | { |
415 | 0 | Reference< XPropertySet > xCurrent( xLoop->getByIndex( nChildPos ), UNO_QUERY ); |
416 | 0 | OSL_ENSURE(xCurrent.is(), "OFormLayerXMLExport_Impl::examineForms: invalid child object"); |
417 | 0 | if (!xCurrent.is()) |
418 | 0 | continue; |
419 | | |
420 | 0 | if (!checkExamineControl(xCurrent)) |
421 | 0 | { |
422 | | // step down |
423 | 0 | Reference< XIndexAccess > xNextContainer(xCurrent, UNO_QUERY); |
424 | 0 | OSL_ENSURE(xNextContainer.is(), "OFormLayerXMLExport_Impl::examineForms: what the heck is this ... no control, no container?"); |
425 | 0 | aContainerHistory.push(xLoop); |
426 | 0 | aIndexHistory.push(nChildPos); |
427 | |
|
428 | 0 | xLoop = std::move(xNextContainer); |
429 | 0 | nChildPos = -1; // will be incremented below |
430 | 0 | } |
431 | 0 | ++nChildPos; |
432 | 0 | } |
433 | 0 | else |
434 | 0 | { |
435 | | // step up |
436 | 0 | while ((nChildPos >= xLoop->getCount()) && !aContainerHistory.empty() ) |
437 | 0 | { |
438 | 0 | xLoop = aContainerHistory.top(); |
439 | 0 | aContainerHistory.pop(); |
440 | 0 | nChildPos = aIndexHistory.top(); |
441 | 0 | aIndexHistory.pop(); |
442 | |
|
443 | 0 | ++nChildPos; |
444 | 0 | } |
445 | 0 | if (nChildPos >= xLoop->getCount()) |
446 | | // exited the loop above because we have no history anymore (0 == aContainerHistory.size()), |
447 | | // and on the current level there are no more children |
448 | | // -> leave |
449 | 0 | break; |
450 | 0 | } |
451 | 0 | } |
452 | 0 | while (xLoop.is()); |
453 | 0 | } |
454 | | |
455 | | namespace |
456 | | { |
457 | | struct AccumulateSize |
458 | | { |
459 | | size_t operator()( size_t _size, const MapPropertySet2Map::value_type& _map ) const |
460 | 0 | { |
461 | 0 | return _size + _map.second.size(); |
462 | 0 | } |
463 | | }; |
464 | | |
465 | | OUString lcl_findFreeControlId( const MapPropertySet2Map& _rAllPagesControlIds ) |
466 | 0 | { |
467 | 0 | OUString sControlId = u"control"_ustr; |
468 | |
|
469 | 0 | size_t nKnownControlCount = ::std::accumulate( _rAllPagesControlIds.begin(), _rAllPagesControlIds.end(), size_t(0), AccumulateSize() ); |
470 | 0 | sControlId += OUString::number( static_cast<sal_Int32>(nKnownControlCount) + 1 ); |
471 | |
|
472 | | #ifdef DBG_UTIL |
473 | | // Check if the id is already used. It shouldn't, as we currently have no mechanism for removing entries |
474 | | // from the map, so the approach used above (take the accumulated map size) should be sufficient. But if |
475 | | // somebody changes this (e.g. allows removing entries from the map), the assertion below probably will fail. |
476 | | for ( const auto& outer : _rAllPagesControlIds ) |
477 | | for ( const auto& inner : outer.second ) |
478 | | { |
479 | | OSL_ENSURE( inner.second != sControlId, |
480 | | "lcl_findFreeControlId: auto-generated control ID is already used!" ); |
481 | | } |
482 | | #endif |
483 | 0 | return sControlId; |
484 | 0 | } |
485 | | } |
486 | | |
487 | | bool OFormLayerXMLExport_Impl::checkExamineControl(const Reference< XPropertySet >& _rxObject) |
488 | 0 | { |
489 | 0 | Reference< XPropertySetInfo > xCurrentInfo = _rxObject->getPropertySetInfo(); |
490 | 0 | OSL_ENSURE(xCurrentInfo.is(), "OFormLayerXMLExport_Impl::checkExamineControl: no property set info"); |
491 | |
|
492 | 0 | bool bIsControl = xCurrentInfo->hasPropertyByName( PROPERTY_CLASSID ); |
493 | 0 | if (bIsControl) |
494 | 0 | { |
495 | | // generate a new control id |
496 | | |
497 | | // find a free id |
498 | 0 | OUString sCurrentId = lcl_findFreeControlId( m_aControlIds ); |
499 | | // add it to the map |
500 | 0 | m_aCurrentPageIds->second[_rxObject] = sCurrentId; |
501 | | |
502 | | // check if this control has a "LabelControl" property referring another control |
503 | 0 | if ( xCurrentInfo->hasPropertyByName( PROPERTY_CONTROLLABEL ) ) |
504 | 0 | { |
505 | 0 | Reference< XPropertySet > xCurrentReference( _rxObject->getPropertyValue( PROPERTY_CONTROLLABEL ), UNO_QUERY ); |
506 | 0 | if (xCurrentReference.is()) |
507 | 0 | { |
508 | 0 | OUString& sReferencedBy = m_aCurrentPageReferring->second[xCurrentReference]; |
509 | 0 | if (!sReferencedBy.isEmpty()) |
510 | | // it's not the first _rxObject referring to the xCurrentReference |
511 | | // -> separate the id |
512 | 0 | sReferencedBy += ","; |
513 | 0 | sReferencedBy += sCurrentId; |
514 | 0 | } |
515 | 0 | } |
516 | | |
517 | | // check if the control needs a number format style |
518 | 0 | if ( xCurrentInfo->hasPropertyByName( PROPERTY_FORMATKEY ) ) |
519 | 0 | { |
520 | 0 | examineControlNumberFormat(_rxObject); |
521 | 0 | } |
522 | | |
523 | | // check if it's a control providing text |
524 | 0 | Reference< XText > xControlText( _rxObject, UNO_QUERY ); |
525 | 0 | if ( xControlText.is() ) |
526 | 0 | { |
527 | 0 | try |
528 | 0 | { |
529 | | // tdf#120397: similar to the fix of tdf#153161 where |
530 | | // XTextRange::getText() --> ::GetSelection() flushes the changes |
531 | | // for some Shape objects we also need to set the end cursor pos |
532 | | // to the end of the form text objects, otherwise fail to get |
533 | | // exported correctly. Maybe at some point it would make sense |
534 | | // to find a better place for more targeted flush. |
535 | 0 | xControlText = xControlText->getText(); |
536 | 0 | } |
537 | 0 | catch (css::uno::RuntimeException const&) |
538 | 0 | { |
539 | | // just in case if we would hit something here |
540 | 0 | } |
541 | 0 | m_rContext.GetTextParagraphExport()->collectTextAutoStyles( xControlText ); |
542 | 0 | } |
543 | | |
544 | | // check if it is a grid control - in this case, we need special handling for the columns |
545 | 0 | sal_Int16 nControlType = FormComponentType::CONTROL; |
546 | 0 | _rxObject->getPropertyValue( PROPERTY_CLASSID ) >>= nControlType; |
547 | 0 | if ( FormComponentType::GRIDCONTROL == nControlType ) |
548 | 0 | { |
549 | 0 | collectGridColumnStylesAndIds( _rxObject ); |
550 | 0 | } |
551 | 0 | } |
552 | |
|
553 | 0 | return bIsControl; |
554 | 0 | } |
555 | | |
556 | | void OFormLayerXMLExport_Impl::collectGridColumnStylesAndIds( const Reference< XPropertySet >& _rxControl ) |
557 | 0 | { |
558 | | // loop through all columns of the grid |
559 | 0 | try |
560 | 0 | { |
561 | 0 | Reference< XIndexAccess > xContainer( _rxControl, UNO_QUERY ); |
562 | 0 | OSL_ENSURE( xContainer.is(), "OFormLayerXMLExport_Impl::collectGridColumnStylesAndIds: grid control not being a container?!" ); |
563 | 0 | if ( !xContainer.is() ) |
564 | 0 | return; |
565 | | |
566 | 0 | Reference< XPropertySetInfo > xColumnPropertiesMeta; |
567 | |
|
568 | 0 | sal_Int32 nCount = xContainer->getCount(); |
569 | 0 | for ( sal_Int32 i=0; i<nCount; ++i ) |
570 | 0 | { |
571 | 0 | Reference< XPropertySet > xColumnProperties( xContainer->getByIndex( i ), UNO_QUERY ); |
572 | 0 | OSL_ENSURE( xColumnProperties.is(), "OFormLayerXMLExport_Impl::collectGridColumnStylesAndIds: invalid grid column encountered!" ); |
573 | 0 | if ( !xColumnProperties.is() ) |
574 | 0 | continue; |
575 | | |
576 | | // generate a new control id |
577 | | |
578 | | // find a free id and add it to the map |
579 | 0 | m_aCurrentPageIds->second[xColumnProperties] = lcl_findFreeControlId(m_aControlIds); |
580 | | |
581 | | // determine a number style, if needed |
582 | 0 | xColumnPropertiesMeta = xColumnProperties->getPropertySetInfo(); |
583 | | // get the styles of the column |
584 | 0 | ::std::vector<XMLPropertyState> aPropertyStates = m_xStyleExportMapper->Filter(m_rContext, xColumnProperties); |
585 | | |
586 | | // care for the number format, additionally |
587 | 0 | OUString sColumnNumberStyle; |
588 | 0 | if ( xColumnPropertiesMeta.is() && xColumnPropertiesMeta->hasPropertyByName( PROPERTY_FORMATKEY ) ) |
589 | 0 | sColumnNumberStyle = getImmediateNumberStyle( xColumnProperties ); |
590 | |
|
591 | 0 | if ( !sColumnNumberStyle.isEmpty() ) |
592 | 0 | { // the column indeed has a formatting |
593 | 0 | sal_Int32 nStyleMapIndex = m_xStyleExportMapper->getPropertySetMapper()->FindEntryIndex( CTF_FORMS_DATA_STYLE ); |
594 | | // TODO: move this to the ctor |
595 | 0 | OSL_ENSURE ( -1 != nStyleMapIndex, "OFormLayerXMLExport_Impl::collectGridColumnStylesAndIds: could not obtain the index for our context id!"); |
596 | |
|
597 | 0 | XMLPropertyState aNumberStyleState( nStyleMapIndex, Any( sColumnNumberStyle ) ); |
598 | 0 | aPropertyStates.push_back( aNumberStyleState ); |
599 | 0 | } |
600 | | |
601 | | // determine the column style |
602 | |
|
603 | 0 | if ( !aPropertyStates.empty() ) |
604 | 0 | { // add to the style pool |
605 | 0 | OUString sColumnStyleName = m_rContext.GetAutoStylePool()->Add( XmlStyleFamily::CONTROL_ID, std::move(aPropertyStates) ); |
606 | |
|
607 | 0 | OSL_ENSURE( m_aGridColumnStyles.end() == m_aGridColumnStyles.find( xColumnProperties ), |
608 | 0 | "OFormLayerXMLExport_Impl::collectGridColumnStylesAndIds: already have a style for this column!" ); |
609 | |
|
610 | 0 | m_aGridColumnStyles.emplace( xColumnProperties, sColumnStyleName ); |
611 | 0 | } |
612 | 0 | } |
613 | 0 | } |
614 | 0 | catch( const Exception& ) |
615 | 0 | { |
616 | 0 | DBG_UNHANDLED_EXCEPTION("xmloff.forms"); |
617 | 0 | } |
618 | 0 | } |
619 | | |
620 | | sal_Int32 OFormLayerXMLExport_Impl::implExamineControlNumberFormat( const Reference< XPropertySet >& _rxObject ) |
621 | 0 | { |
622 | | // get the format key relative to our own formats supplier |
623 | 0 | sal_Int32 nOwnFormatKey = ensureTranslateFormat( _rxObject ); |
624 | |
|
625 | 0 | if ( -1 != nOwnFormatKey ) |
626 | | // tell the exporter that we used this format |
627 | 0 | getControlNumberStyleExport()->SetUsed( nOwnFormatKey ); |
628 | |
|
629 | 0 | return nOwnFormatKey; |
630 | 0 | } |
631 | | |
632 | | void OFormLayerXMLExport_Impl::examineControlNumberFormat( const Reference< XPropertySet >& _rxControl ) |
633 | 0 | { |
634 | 0 | sal_Int32 nOwnFormatKey = implExamineControlNumberFormat( _rxControl ); |
635 | |
|
636 | 0 | if ( -1 == nOwnFormatKey ) |
637 | | // nothing to do, the number format of this control is void |
638 | 0 | return; |
639 | | |
640 | | // remember the format key for this control (we'll be asked in getControlNumberStyle for this) |
641 | 0 | OSL_ENSURE(m_aControlNumberFormats.end() == m_aControlNumberFormats.find(_rxControl), |
642 | 0 | "OFormLayerXMLExport_Impl::examineControlNumberFormat: already handled this control!"); |
643 | 0 | m_aControlNumberFormats[_rxControl] = nOwnFormatKey; |
644 | 0 | } |
645 | | |
646 | | sal_Int32 OFormLayerXMLExport_Impl::ensureTranslateFormat(const Reference< XPropertySet >& _rxFormattedControl) |
647 | 0 | { |
648 | 0 | ensureControlNumberStyleExport(); |
649 | 0 | OSL_ENSURE(m_xControlNumberFormats.is(), "OFormLayerXMLExport_Impl::ensureTranslateFormat: no own formats supplier!"); |
650 | | // (should have been created in ensureControlNumberStyleExport) |
651 | |
|
652 | 0 | sal_Int32 nOwnFormatKey = -1; |
653 | | |
654 | | // the format key (relative to the control's supplier) |
655 | 0 | sal_Int32 nControlFormatKey = -1; |
656 | 0 | Any aControlFormatKey = _rxFormattedControl->getPropertyValue(PROPERTY_FORMATKEY); |
657 | 0 | if (aControlFormatKey >>= nControlFormatKey) |
658 | 0 | { |
659 | | // the control's number format |
660 | 0 | Reference< XNumberFormatsSupplier > xControlFormatsSupplier; |
661 | 0 | _rxFormattedControl->getPropertyValue(PROPERTY_FORMATSSUPPLIER) >>= xControlFormatsSupplier; |
662 | 0 | Reference< XNumberFormats > xControlFormats; |
663 | 0 | if (xControlFormatsSupplier.is()) |
664 | 0 | xControlFormats = xControlFormatsSupplier->getNumberFormats(); |
665 | 0 | OSL_ENSURE(xControlFormats.is(), "OFormLayerXMLExport_Impl::ensureTranslateFormat: formatted control without supplier!"); |
666 | | |
667 | | // obtain the persistent (does not depend on the formats supplier) representation of the control's format |
668 | 0 | Locale aFormatLocale; |
669 | 0 | OUString sFormatDescription; |
670 | 0 | if (xControlFormats.is()) |
671 | 0 | { |
672 | 0 | Reference< XPropertySet > xControlFormat = xControlFormats->getByKey(nControlFormatKey); |
673 | |
|
674 | 0 | xControlFormat->getPropertyValue(PROPERTY_LOCALE) >>= aFormatLocale; |
675 | 0 | xControlFormat->getPropertyValue(PROPERTY_FORMATSTRING) >>= sFormatDescription; |
676 | 0 | } |
677 | | |
678 | | // check if our own formats collection already knows the format |
679 | 0 | nOwnFormatKey = m_xControlNumberFormats->queryKey(sFormatDescription, aFormatLocale, false); |
680 | 0 | if (-1 == nOwnFormatKey) |
681 | 0 | { // no, we don't |
682 | | // -> create a new format |
683 | 0 | nOwnFormatKey = m_xControlNumberFormats->addNew(sFormatDescription, aFormatLocale); |
684 | 0 | } |
685 | 0 | OSL_ENSURE(-1 != nOwnFormatKey, "OFormLayerXMLExport_Impl::ensureTranslateFormat: could not translate the controls format key!"); |
686 | 0 | } |
687 | 0 | else |
688 | 0 | OSL_ENSURE(!aControlFormatKey.hasValue(), "OFormLayerXMLExport_Impl::ensureTranslateFormat: invalid number format property value!"); |
689 | |
|
690 | 0 | return nOwnFormatKey; |
691 | 0 | } |
692 | | |
693 | | void OFormLayerXMLExport_Impl::ensureControlNumberStyleExport() |
694 | 0 | { |
695 | 0 | if (m_pControlNumberStyles) |
696 | 0 | return; |
697 | | |
698 | | // create our number formats supplier (if necessary) |
699 | 0 | Reference< XNumberFormatsSupplier > xFormatsSupplier; |
700 | |
|
701 | 0 | OSL_ENSURE(!m_xControlNumberFormats.is(), "OFormLayerXMLExport_Impl::getControlNumberStyleExport: inconsistence!"); |
702 | | // the m_xControlNumberFormats and m_pControlNumberStyles should be maintained together |
703 | |
|
704 | 0 | try |
705 | 0 | { |
706 | | // create it for en-US (does not really matter, as we will specify a locale for every |
707 | | // concrete language to use) |
708 | 0 | Locale aLocale ( u"en"_ustr, u"US"_ustr, OUString() ); |
709 | 0 | xFormatsSupplier = NumberFormatsSupplier::createWithLocale( m_rContext.getComponentContext(), aLocale ); |
710 | 0 | m_xControlNumberFormats = xFormatsSupplier->getNumberFormats(); |
711 | 0 | } |
712 | 0 | catch(const Exception&) |
713 | 0 | { |
714 | 0 | } |
715 | |
|
716 | 0 | OSL_ENSURE(m_xControlNumberFormats.is(), "OFormLayerXMLExport_Impl::getControlNumberStyleExport: could not obtain my default number formats!"); |
717 | | |
718 | | // create the exporter |
719 | 0 | m_pControlNumberStyles = std::make_unique<SvXMLNumFmtExport>(m_rContext, xFormatsSupplier, getControlNumberStyleNamePrefix()); |
720 | 0 | } |
721 | | |
722 | | SvXMLNumFmtExport* OFormLayerXMLExport_Impl::getControlNumberStyleExport() |
723 | 0 | { |
724 | 0 | ensureControlNumberStyleExport(); |
725 | 0 | return m_pControlNumberStyles.get(); |
726 | 0 | } |
727 | | |
728 | | void OFormLayerXMLExport_Impl::excludeFromExport( const Reference< XControlModel >& _rxControl ) |
729 | 0 | { |
730 | 0 | Reference< XPropertySet > xProps( _rxControl, UNO_QUERY ); |
731 | 0 | OSL_ENSURE( xProps.is(), "OFormLayerXMLExport_Impl::excludeFromExport: invalid control model!" ); |
732 | 0 | ::std::pair< PropertySetBag::const_iterator, bool > aPos = |
733 | 0 | m_aIgnoreList.insert( xProps ); |
734 | 0 | OSL_ENSURE( aPos.second, "OFormLayerXMLExport_Impl::excludeFromExport: element already exists in the ignore list!" ); |
735 | 0 | } |
736 | | |
737 | | } // namespace xmloff |
738 | | |
739 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |