Coverage Report

Created: 2026-08-14 10:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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: */