Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/chart2/source/model/main/ChartModel_Persistence.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 <memory>
21
#include <ChartModel.hxx>
22
#include <MediaDescriptorHelper.hxx>
23
#include <ChartViewHelper.hxx>
24
#include <ChartTypeManager.hxx>
25
#include <ChartTypeTemplate.hxx>
26
#include <DataSourceHelper.hxx>
27
#include <AxisHelper.hxx>
28
#include <ThreeDHelper.hxx>
29
#include <Diagram.hxx>
30
#include <BaseCoordinateSystem.hxx>
31
#include <Legend.hxx>
32
#include <XMLFilter.hxx>
33
34
#include <com/sun/star/chart2/LegendPosition.hpp>
35
#include <com/sun/star/container/XNameAccess.hpp>
36
#include <com/sun/star/document/XExporter.hpp>
37
#include <com/sun/star/document/XImporter.hpp>
38
#include <com/sun/star/document/XFilter.hpp>
39
#include <com/sun/star/drawing/FillStyle.hpp>
40
#include <com/sun/star/drawing/LineStyle.hpp>
41
#include <com/sun/star/drawing/ProjectionMode.hpp>
42
#include <com/sun/star/embed/ElementModes.hpp>
43
#include <com/sun/star/embed/XStorage.hpp>
44
#include <com/sun/star/embed/StorageFactory.hpp>
45
#include <com/sun/star/io/IOException.hpp>
46
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
47
#include <com/sun/star/uno/XComponentContext.hpp>
48
#include <com/sun/star/ucb/CommandFailedException.hpp>
49
#include <com/sun/star/ucb/ContentCreationException.hpp>
50
51
#include <chart2/AbstractPivotTableDataProvider.hxx>
52
53
#include <tools/stream.hxx>
54
#include <ucbhelper/content.hxx>
55
#include <unotools/ucbstreamhelper.hxx>
56
#include <unotools/tempfile.hxx>
57
#include <utility>
58
#include <vcl/cvtgrf.hxx>
59
#include <comphelper/processfactory.hxx>
60
#include <comphelper/storagehelper.hxx>
61
#include <vcl/settings.hxx>
62
#include <vcl/svapp.hxx>
63
#include <comphelper/diagnose_ex.hxx>
64
#include <sal/log.hxx>
65
#include <sfx2/objsh.hxx>
66
67
#include <algorithm>
68
69
using namespace ::com::sun::star;
70
71
using ::com::sun::star::uno::Reference;
72
using ::com::sun::star::uno::Sequence;
73
using ::osl::MutexGuard;
74
75
namespace
76
{
77
template< typename T >
78
T lcl_getProperty(
79
    const Sequence< beans::PropertyValue > & rMediaDescriptor,
80
    const OUString & rPropName )
81
0
{
82
0
    T aResult;
83
0
    if( rMediaDescriptor.hasElements())
84
0
    {
85
0
        auto pIt = std::find_if(rMediaDescriptor.begin(), rMediaDescriptor.end(),
86
0
                                [&rPropName](auto& prop) { return prop.Name == rPropName; });
87
0
        if (pIt != rMediaDescriptor.end())
88
0
            (*pIt).Value >>= aResult;
89
0
    }
90
0
    return aResult;
91
0
}
92
93
void lcl_addStorageToMediaDescriptor(
94
    Sequence< beans::PropertyValue > & rOutMD,
95
    const Reference< embed::XStorage > & xStorage )
96
0
{
97
0
    rOutMD.realloc( rOutMD.getLength() + 1 );
98
0
    rOutMD.getArray()[rOutMD.getLength() - 1] = beans::PropertyValue(
99
0
        u"Storage"_ustr, -1, uno::Any( xStorage ), beans::PropertyState_DIRECT_VALUE );
100
0
}
101
102
Reference< embed::XStorage > lcl_createStorage(
103
    const OUString & rURL,
104
    const Reference< uno::XComponentContext > & xContext,
105
    const Sequence< beans::PropertyValue > & rMediaDescriptor )
106
0
{
107
    // create new storage
108
0
    Reference< embed::XStorage > xStorage;
109
0
    if( !xContext.is())
110
0
        return xStorage;
111
112
0
    try
113
0
    {
114
0
        Reference< io::XStream > xStream(
115
0
            ::ucbhelper::Content( rURL, Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext()).openStream(),
116
0
            uno::UNO_QUERY );
117
118
0
        Reference< lang::XSingleServiceFactory > xStorageFact( embed::StorageFactory::create( xContext ) );
119
0
        Sequence< uno::Any > aStorageArgs{ uno::Any(xStream),
120
0
                                           uno::Any(embed::ElementModes::READWRITE),
121
0
                                           uno::Any(rMediaDescriptor) };
122
0
        xStorage.set(
123
0
            xStorageFact->createInstanceWithArguments( aStorageArgs ), uno::UNO_QUERY_THROW );
124
0
    }
125
0
    catch(const css::ucb::ContentCreationException&)
126
0
    {
127
0
        DBG_UNHANDLED_EXCEPTION("chart2");
128
0
    }
129
0
    catch(const css::ucb::CommandFailedException&)
130
0
    {
131
0
        DBG_UNHANDLED_EXCEPTION("chart2");
132
0
    }
133
134
0
    return xStorage;
135
0
}
136
137
} // anonymous namespace
138
139
namespace chart
140
{
141
142
Reference< document::XFilter > ChartModel::impl_createFilter(
143
    const Sequence< beans::PropertyValue > & rMediaDescriptor )
144
0
{
145
0
    Reference< document::XFilter > xFilter;
146
147
    // find FilterName in MediaDescriptor
148
0
    OUString aFilterName(
149
0
        lcl_getProperty< OUString >( rMediaDescriptor, u"FilterName"_ustr ) );
150
151
    // if FilterName was found, get Filter from factory
152
0
    if( !aFilterName.isEmpty() )
153
0
    {
154
0
        try
155
0
        {
156
0
            Reference< container::XNameAccess > xFilterFact(
157
0
                m_xContext->getServiceManager()->createInstanceWithContext(
158
0
                    u"com.sun.star.document.FilterFactory"_ustr, m_xContext ),
159
0
                uno::UNO_QUERY_THROW );
160
0
            uno::Any aFilterProps( xFilterFact->getByName( aFilterName ));
161
0
            Sequence< beans::PropertyValue > aProps;
162
163
0
            if( aFilterProps.hasValue() &&
164
0
                (aFilterProps >>= aProps))
165
0
            {
166
0
                OUString aFilterServiceName(
167
0
                    lcl_getProperty< OUString >( aProps, u"FilterService"_ustr ) );
168
169
0
                if( !aFilterServiceName.isEmpty())
170
0
                {
171
0
                    xFilter.set(
172
0
                        m_xContext->getServiceManager()->createInstanceWithContext(
173
0
                            aFilterServiceName, m_xContext ), uno::UNO_QUERY_THROW );
174
0
                    SAL_INFO("chart2", "Filter found for service " << aFilterServiceName );
175
0
                }
176
0
            }
177
0
        }
178
0
        catch( const uno::Exception & )
179
0
        {
180
0
            DBG_UNHANDLED_EXCEPTION("chart2");
181
0
        }
182
0
        OSL_ENSURE( xFilter.is(), "Filter not found via factory" );
183
0
    }
184
185
    // fall-back: create XML-Filter
186
0
    if( ! xFilter.is())
187
0
    {
188
0
        SAL_WARN("chart2", "No FilterName passed in MediaDescriptor" );
189
0
        xFilter = new XMLFilter(m_xContext);
190
0
    }
191
192
0
    return xFilter;
193
0
}
194
195
// frame::XStorable2
196
197
void SAL_CALL ChartModel::storeSelf( const Sequence< beans::PropertyValue >& rMediaDescriptor )
198
0
{
199
    // only some parameters are allowed (see also SfxBaseModel)
200
    // "VersionComment", "Author", "InteractionHandler", "StatusIndicator"
201
    // However, they are ignored here.  They would become interesting when
202
    // charts support a standalone format again.
203
0
    impl_store( rMediaDescriptor, m_xStorage );
204
0
}
205
206
// frame::XStorable (base of XStorable2)
207
sal_Bool SAL_CALL ChartModel::hasLocation()
208
0
{
209
0
    std::unique_lock aGuard(m_aLifeTimeManager.m_aAccessMutex);
210
0
    return !m_aResource.isEmpty();
211
0
}
212
213
OUString SAL_CALL ChartModel::getLocation()
214
0
{
215
0
    return impl_g_getLocation();
216
0
}
217
218
sal_Bool SAL_CALL ChartModel::isReadonly()
219
0
{
220
    //@todo guard
221
0
    return m_bReadOnly;
222
0
}
223
224
void SAL_CALL ChartModel::store()
225
0
{
226
0
    apphelper::LifeTimeGuard aGuard(m_aLifeTimeManager);
227
0
    if(!aGuard.startApiCall(true)) //start LongLastingCall
228
0
        return; //behave passive if already disposed or closed or throw exception @todo?
229
230
0
    OUString aLocation = m_aResource;
231
232
0
    if( aLocation.isEmpty() )
233
0
        throw io::IOException( u"no location specified"_ustr, static_cast< ::cppu::OWeakObject* >(this));
234
    //@todo check whether aLocation is something like private:factory...
235
0
    if( m_bReadOnly )
236
0
        throw io::IOException( u"document is read only"_ustr, static_cast< ::cppu::OWeakObject* >(this));
237
238
0
    aGuard.clear();
239
240
    // store
241
0
    impl_store( m_aMediaDescriptor, m_xStorage );
242
0
}
243
244
void SAL_CALL ChartModel::storeAsURL(
245
    const OUString& rURL,
246
    const uno::Sequence< beans::PropertyValue >& rMediaDescriptor )
247
0
{
248
0
    apphelper::LifeTimeGuard aGuard(m_aLifeTimeManager);
249
0
    if(!aGuard.startApiCall(true)) //start LongLastingCall
250
0
        return; //behave passive if already disposed or closed or throw exception @todo?
251
252
0
    apphelper::MediaDescriptorHelper aMediaDescriptorHelper(rMediaDescriptor);
253
0
    const uno::Sequence< beans::PropertyValue >& aReducedMediaDescriptor(
254
0
        aMediaDescriptorHelper.getReducedForModel() );
255
256
0
    m_bReadOnly = false;
257
0
    aGuard.clear();
258
259
    // create new storage
260
0
    Reference< embed::XStorage > xStorage( lcl_createStorage( rURL, m_xContext, aReducedMediaDescriptor ));
261
262
0
    if( xStorage.is())
263
0
    {
264
0
        impl_store( aReducedMediaDescriptor, xStorage );
265
0
        attachResource( rURL, aReducedMediaDescriptor );
266
0
    }
267
0
}
268
269
void SAL_CALL ChartModel::storeToURL(
270
    const OUString& rURL,
271
    const uno::Sequence< beans::PropertyValue >& rMediaDescriptor )
272
0
{
273
0
    apphelper::LifeTimeGuard aGuard(m_aLifeTimeManager);
274
0
    if(!aGuard.startApiCall(true)) //start LongLastingCall
275
0
        return; //behave passive if already disposed or closed or throw exception @todo?
276
    //do not change the internal state of the document here
277
278
0
    aGuard.clear();
279
280
0
    apphelper::MediaDescriptorHelper aMediaDescriptorHelper(rMediaDescriptor);
281
0
    const uno::Sequence< beans::PropertyValue >& aReducedMediaDescriptor(
282
0
        aMediaDescriptorHelper.getReducedForModel() );
283
284
0
    if ( rURL == "private:stream" )
285
0
    {
286
0
        try
287
0
        {
288
0
            if( m_xContext.is() && aMediaDescriptorHelper.ISSET_OutputStream )
289
0
            {
290
0
                rtl::Reference< utl::TempFileFastService > xStream = new utl::TempFileFastService;
291
0
                Reference< io::XInputStream > xInputStream( xStream->getInputStream());
292
293
0
                Reference< embed::XStorage > xStorage(
294
0
                    ::comphelper::OStorageHelper::GetStorageFromStream( xStream, embed::ElementModes::READWRITE, m_xContext ));
295
0
                if( xStorage.is())
296
0
                {
297
0
                    impl_store( aReducedMediaDescriptor, xStorage );
298
299
0
                    xStream->seek( 0 );
300
0
                    ::comphelper::OStorageHelper::CopyInputToOutput( xInputStream, aMediaDescriptorHelper.OutputStream );
301
0
                }
302
0
            }
303
0
        }
304
0
        catch( const uno::Exception & )
305
0
        {
306
0
            DBG_UNHANDLED_EXCEPTION("chart2");
307
0
        }
308
0
    }
309
0
    else
310
0
    {
311
        // create new storage
312
0
        Reference< embed::XStorage > xStorage( lcl_createStorage( rURL, m_xContext, aReducedMediaDescriptor ));
313
314
0
        if( xStorage.is())
315
0
            impl_store( aReducedMediaDescriptor, xStorage );
316
0
    }
317
0
}
318
319
void ChartModel::impl_store(
320
    const Sequence< beans::PropertyValue >& rMediaDescriptor,
321
    const Reference< embed::XStorage > & xStorage )
322
0
{
323
0
    Reference< document::XFilter > xFilter( impl_createFilter( rMediaDescriptor));
324
0
    if( xFilter.is() && xStorage.is())
325
0
    {
326
0
        Sequence< beans::PropertyValue > aMD( rMediaDescriptor );
327
0
        lcl_addStorageToMediaDescriptor( aMD, xStorage );
328
0
        try
329
0
        {
330
0
            Reference< document::XExporter > xExporter( xFilter, uno::UNO_QUERY_THROW );
331
0
            xExporter->setSourceDocument( Reference< lang::XComponent >( this ));
332
0
            xFilter->filter( aMD );
333
0
        }
334
0
        catch( const uno::Exception & )
335
0
        {
336
0
            DBG_UNHANDLED_EXCEPTION("chart2");
337
0
        }
338
0
    }
339
0
    else
340
0
    {
341
0
        OSL_FAIL( "No filter" );
342
0
    }
343
344
0
    setModified( false );
345
346
    //#i66865#
347
    //for data change notification during chart is not loaded:
348
    //notify parent data provider after saving thus the parent document can store
349
    //the ranges for which a load and update of the chart will be necessary
350
0
    Reference< beans::XPropertySet > xPropSet( m_xParent, uno::UNO_QUERY );
351
0
    if ( hasInternalDataProvider() || !xPropSet.is() )
352
0
        return;
353
354
0
    apphelper::MediaDescriptorHelper aMDHelper(rMediaDescriptor);
355
0
    try
356
0
    {
357
0
        xPropSet->setPropertyValue(
358
0
            u"SavedObject"_ustr,
359
0
            uno::Any( aMDHelper.HierarchicalDocumentName ) );
360
0
    }
361
0
    catch ( const uno::Exception& )
362
0
    {
363
0
    }
364
0
}
365
366
void ChartModel::insertDefaultChart()
367
0
{
368
0
    lockControllers();
369
0
    createInternalDataProvider( false );
370
0
    try
371
0
    {
372
        // create default chart
373
0
        rtl::Reference< ::chart::ChartTypeTemplate > xTemplate( impl_createDefaultChartTypeTemplate() );
374
0
        if( xTemplate.is())
375
0
        {
376
0
            try
377
0
            {
378
0
                Reference< chart2::data::XDataSource > xDataSource( impl_createDefaultData() );
379
0
                Sequence< beans::PropertyValue > aParam;
380
381
0
                bool bSupportsCategories = xTemplate->supportsCategories();
382
0
                if( bSupportsCategories )
383
0
                {
384
0
                    aParam = { beans::PropertyValue( u"HasCategories"_ustr, -1, uno::Any( true ),
385
0
                                                     beans::PropertyState_DIRECT_VALUE ) };
386
0
                }
387
388
0
                rtl::Reference< Diagram > xDiagram( xTemplate->createDiagramByDataSource2( xDataSource, aParam ) );
389
390
0
                setFirstDiagram( xDiagram );
391
392
0
                bool bIsRTL = AllSettings::GetMathLayoutRTL();
393
                //reverse x axis for rtl charts
394
0
                if( bIsRTL )
395
0
                    AxisHelper::setRTLAxisLayout( AxisHelper::getCoordinateSystemByIndex( xDiagram, 0 ) );
396
397
                // create and attach legend
398
0
                rtl::Reference< Legend > xLegend = new Legend();
399
0
                xLegend->setPropertyValue( u"FillStyle"_ustr, uno::Any( drawing::FillStyle_NONE ));
400
0
                xLegend->setPropertyValue( u"LineStyle"_ustr, uno::Any( drawing::LineStyle_NONE ));
401
0
                xLegend->setPropertyValue( u"LineColor"_ustr, uno::Any( static_cast< sal_Int32 >( 0xb3b3b3 ) ));  // gray30
402
0
                xLegend->setPropertyValue( u"FillColor"_ustr, uno::Any( static_cast< sal_Int32 >( 0xe6e6e6 ) ) ); // gray10
403
404
0
                if( bIsRTL )
405
0
                    xLegend->setPropertyValue( u"AnchorPosition"_ustr, uno::Any( chart2::LegendPosition_LINE_START ));
406
0
                if(xDiagram.is())
407
0
                {
408
0
                    xDiagram->setLegend( xLegend );
409
410
                    // set simple 3D look
411
0
                    xDiagram->setPropertyValue( u"RightAngledAxes"_ustr, uno::Any( true ));
412
0
                    xDiagram->setPropertyValue( u"D3DScenePerspective"_ustr, uno::Any( drawing::ProjectionMode_PARALLEL ));
413
0
                    xDiagram->setScheme( ThreeDLookScheme::ThreeDLookScheme_Realistic );
414
415
                    //set some new 'defaults' for wall and floor
416
0
                    Reference< beans::XPropertySet > xWall( xDiagram->getWall() );
417
0
                    if( xWall.is() )
418
0
                    {
419
0
                        xWall->setPropertyValue( u"LineStyle"_ustr, uno::Any( drawing::LineStyle_SOLID ) );
420
0
                        xWall->setPropertyValue( u"FillStyle"_ustr, uno::Any( drawing::FillStyle_NONE ) );
421
0
                        xWall->setPropertyValue( u"LineColor"_ustr, uno::Any( static_cast< sal_Int32 >( 0xb3b3b3 ) ) ); // gray30
422
0
                        xWall->setPropertyValue( u"FillColor"_ustr, uno::Any( static_cast< sal_Int32 >( 0xe6e6e6 ) ) ); // gray10
423
0
                    }
424
0
                    Reference< beans::XPropertySet > xFloor( xDiagram->getFloor() );
425
0
                    if( xFloor.is() )
426
0
                    {
427
0
                        xFloor->setPropertyValue( u"LineStyle"_ustr, uno::Any( drawing::LineStyle_NONE ) );
428
0
                        xFloor->setPropertyValue( u"FillStyle"_ustr, uno::Any( drawing::FillStyle_SOLID ) );
429
0
                        xFloor->setPropertyValue( u"LineColor"_ustr, uno::Any( static_cast< sal_Int32 >( 0xb3b3b3 ) ) ); // gray30
430
0
                        xFloor->setPropertyValue( u"FillColor"_ustr, uno::Any( static_cast< sal_Int32 >( 0xcccccc ) ) ); // gray20
431
0
                    }
432
433
0
                }
434
0
            }
435
0
            catch( const uno::Exception & )
436
0
            {
437
0
                DBG_UNHANDLED_EXCEPTION("chart2");
438
0
            }
439
0
        }
440
0
        setIncludeHiddenCells( false );
441
0
    }
442
0
    catch( const uno::Exception & )
443
0
    {
444
0
        DBG_UNHANDLED_EXCEPTION("chart2");
445
0
    }
446
0
    setModified( false );
447
0
    unlockControllers();
448
0
}
449
450
// frame::XLoadable
451
void SAL_CALL ChartModel::initNew()
452
0
{
453
0
}
454
455
void SAL_CALL ChartModel::load(
456
    const Sequence< beans::PropertyValue >& rMediaDescriptor )
457
0
{
458
0
    Reference< embed::XStorage > xStorage;
459
0
    OUString aURL;
460
0
    try
461
0
    {
462
0
        apphelper::MediaDescriptorHelper aMDHelper( rMediaDescriptor );
463
0
        if( aMDHelper.ISSET_Storage )
464
0
        {
465
0
            xStorage = aMDHelper.Storage;
466
0
        }
467
0
        else if( aMDHelper.ISSET_Stream ||
468
0
                 aMDHelper.ISSET_InputStream )
469
0
        {
470
0
            if( aMDHelper.ISSET_FilterName &&
471
0
                (aMDHelper.FilterName == "StarChart 5.0" ||
472
0
                 aMDHelper.FilterName == "StarChart 4.0" ||
473
0
                 aMDHelper.FilterName == "StarChart 3.0" ))
474
0
            {
475
0
                attachResource( aMDHelper.URL, rMediaDescriptor );
476
0
                impl_load( rMediaDescriptor, nullptr ); // cannot create a storage from binary streams, but I do not need the storage here anyhow
477
0
                m_bReadOnly = true;
478
0
                return;
479
0
            }
480
481
0
            Reference< lang::XSingleServiceFactory > xStorageFact( embed::StorageFactory::create(m_xContext) );
482
483
0
            if( aMDHelper.ISSET_Stream )
484
0
            {
485
                // convert XStream to XStorage via the storage factory
486
0
                Sequence< uno::Any > aStorageArgs{ uno::Any(aMDHelper.Stream),
487
                                                   // todo: check if stream is read-only
488
0
                                                   uno::Any(embed::ElementModes::READ) }; //WRITE | embed::ElementModes::NOCREATE);
489
490
0
                xStorage.set( xStorageFact->createInstanceWithArguments( aStorageArgs ),
491
0
                    uno::UNO_QUERY_THROW );
492
0
            }
493
0
            else
494
0
            {
495
0
                OSL_ASSERT( aMDHelper.ISSET_InputStream );
496
                // convert XInputStream to XStorage via the storage factory
497
0
                Sequence< uno::Any > aStorageArgs{ uno::Any(aMDHelper.InputStream),
498
0
                                                   uno::Any(embed::ElementModes::READ) };
499
500
0
                xStorage.set( xStorageFact->createInstanceWithArguments( aStorageArgs ),
501
0
                    uno::UNO_QUERY_THROW );
502
0
            }
503
0
        }
504
505
0
        if( aMDHelper.ISSET_URL )
506
0
            aURL = aMDHelper.URL;
507
0
    }
508
0
    catch( const uno::Exception & )
509
0
    {
510
0
        DBG_UNHANDLED_EXCEPTION("chart2");
511
0
    }
512
513
0
    if( xStorage.is())
514
0
    {
515
0
        attachResource( aURL, rMediaDescriptor );
516
0
        impl_load( rMediaDescriptor, xStorage );
517
0
    }
518
0
}
519
520
void ChartModel::impl_load(
521
    const Sequence< beans::PropertyValue >& rMediaDescriptor,
522
    const Reference< embed::XStorage >& xStorage )
523
0
{
524
0
    {
525
0
        MutexGuard aGuard( m_aModelMutex );
526
0
        m_nInLoad++;
527
0
    }
528
529
0
    Reference< document::XFilter > xFilter( impl_createFilter( rMediaDescriptor ));
530
531
0
    if( xFilter.is())
532
0
    {
533
0
        Reference< document::XImporter > xImporter( xFilter, uno::UNO_QUERY_THROW );
534
0
        xImporter->setTargetDocument( this );
535
0
        Sequence< beans::PropertyValue > aMD( rMediaDescriptor );
536
0
        lcl_addStorageToMediaDescriptor( aMD, xStorage );
537
538
0
        xFilter->filter( aMD );
539
0
        xFilter.clear();
540
0
    }
541
0
    else
542
0
    {
543
0
        OSL_FAIL( "loadFromStorage cannot create filter" );
544
0
    }
545
546
0
    if( xStorage.is() )
547
0
        impl_loadGraphics( xStorage );
548
549
0
    setModified( false );
550
551
    // switchToStorage without notifying listeners (which shouldn't exist at
552
    // this time, anyway)
553
0
    m_xStorage = xStorage;
554
555
0
    {
556
0
        MutexGuard aGuard( m_aModelMutex );
557
0
        m_nInLoad--;
558
0
    }
559
0
}
560
561
void ChartModel::impl_loadGraphics(
562
    const Reference< embed::XStorage >& xStorage )
563
0
{
564
0
    try
565
0
    {
566
0
        const Reference< embed::XStorage > xGraphicsStorage(
567
0
            xStorage->openStorageElement( u"Pictures"_ustr,
568
0
                                          embed::ElementModes::READ ) );
569
570
0
        if( xGraphicsStorage.is() )
571
0
        {
572
0
            const uno::Sequence< OUString > aElementNames(
573
0
                xGraphicsStorage->getElementNames() );
574
575
0
            for( OUString const & streamName : aElementNames )
576
0
            {
577
0
                if( xGraphicsStorage->isStreamElement( streamName ) )
578
0
                {
579
0
                    uno::Reference< io::XStream > xElementStream(
580
0
                        xGraphicsStorage->openStreamElement(
581
0
                            streamName,
582
0
                            embed::ElementModes::READ ) );
583
584
0
                    if( xElementStream.is() )
585
0
                    {
586
0
                        std::unique_ptr< SvStream > apIStm(
587
0
                            ::utl::UcbStreamHelper::CreateStream(
588
0
                                xElementStream, true ) );
589
590
0
                        if (apIStm)
591
0
                        {
592
0
                            SolarMutexGuard aGuard;
593
0
                            Graphic aGraphic;
594
0
                            if (!GraphicConverter::Import(*apIStm, aGraphic))
595
0
                            {
596
0
                                m_aGraphicObjectVector.emplace_back(aGraphic );
597
0
                            }
598
0
                        }
599
0
                    }
600
0
                }
601
0
            }
602
0
        }
603
0
    }
604
0
    catch ( const uno::Exception& )
605
0
    {
606
0
    }
607
0
}
608
609
// util::XModifiable
610
void ChartModel::impl_notifyModifiedListeners()
611
0
{
612
0
    {
613
0
        MutexGuard aGuard( m_aModelMutex );
614
0
        m_bUpdateNotificationsPending = false;
615
0
    }
616
617
    //always notify the view first!
618
0
    ChartViewHelper::setViewToDirtyState( this );
619
620
0
    std::unique_lock aGuard(m_aLifeTimeManager.m_aAccessMutex);
621
0
    if( m_aLifeTimeManager.m_aModifyListeners.getLength(aGuard) )
622
0
    {
623
0
        lang::EventObject aEvent( static_cast< lang::XComponent*>(this) );
624
0
        m_aLifeTimeManager.m_aModifyListeners.notifyEach(aGuard, &util::XModifyListener::modified, aEvent);
625
0
    }
626
0
}
627
628
sal_Bool SAL_CALL ChartModel::isModified()
629
0
{
630
    //@todo guard
631
0
    return m_bModified;
632
0
}
633
634
void SAL_CALL ChartModel::setModified( sal_Bool bModified )
635
0
{
636
    // tdf#141914: allow to set *unmodified* when parent does not allow to set modified
637
0
    if (bModified)
638
0
    {
639
        // tdf#77007: honor parent's IsEnableSetModified
640
        // Check it before LifeTimeGuard, to avoid deadlocking solar mutex and this guard
641
0
        if (auto pParentShell = SfxObjectShell::GetShellFromComponent(getParent());
642
0
            pParentShell && !pParentShell->IsEnableSetModified())
643
0
            return;
644
0
    }
645
646
0
    apphelper::LifeTimeGuard aGuard(m_aLifeTimeManager);
647
0
    if(!aGuard.startApiCall())//@todo ? is this a long lasting call??
648
0
        return; //behave passive if already disposed or closed or throw exception @todo?
649
0
    m_bModified = bModified;
650
651
0
    if( m_nControllerLockCount > 0 )
652
0
    {
653
0
        if (bModified)
654
0
            m_bUpdateNotificationsPending = true; // Maybe !bModified should reset it?
655
0
        return;//don't call listeners if controllers are locked
656
0
    }
657
0
    aGuard.clear();
658
659
0
    if(bModified)
660
0
        impl_notifyModifiedListeners();
661
0
}
662
663
// util::XModifyBroadcaster (base of XModifiable)
664
void SAL_CALL ChartModel::addModifyListener(
665
    const uno::Reference< util::XModifyListener >& xListener )
666
0
{
667
0
    if( m_aLifeTimeManager.impl_isDisposedOrClosed() )
668
0
        return; //behave passive if already disposed or closed
669
670
0
    std::unique_lock aGuard(m_aLifeTimeManager.m_aAccessMutex);
671
0
    m_aLifeTimeManager.m_aModifyListeners.addInterface( aGuard, xListener );
672
0
}
673
674
void SAL_CALL ChartModel::removeModifyListener(
675
    const uno::Reference< util::XModifyListener >& xListener )
676
0
{
677
0
    if( m_aLifeTimeManager.impl_isDisposedOrClosed(false) )
678
0
        return; //behave passive if already disposed or closed
679
680
0
    std::unique_lock aGuard(m_aLifeTimeManager.m_aAccessMutex);
681
0
    m_aLifeTimeManager.m_aModifyListeners.removeInterface( aGuard, xListener );
682
0
}
683
684
// util::XModifyListener
685
void SAL_CALL ChartModel::modified( const lang::EventObject& rEvenObject)
686
0
{
687
0
    chart2api::AbstractPivotTableDataProvider* pPivotTableDataProvider =
688
0
        dynamic_cast<chart2api::AbstractPivotTableDataProvider*>(rEvenObject.Source.get());
689
0
    if (pPivotTableDataProvider)
690
0
    {
691
0
        lockControllers();
692
0
        uno::Reference<chart2::data::XDataProvider> xDataProvider(rEvenObject.Source, uno::UNO_QUERY);
693
0
        try
694
0
        {
695
0
            uno::Sequence<beans::PropertyValue> aArguments =
696
0
                DataSourceHelper::createArguments(u"PivotChart"_ustr, uno::Sequence<sal_Int32>(), true, true, true);
697
698
0
            Reference<chart2::data::XDataSource> xDataSource(xDataProvider->createDataSource(aArguments));
699
0
            rtl::Reference< ::chart::ChartTypeManager > xChartTypeManager = getTypeManager();
700
0
            rtl::Reference<Diagram> xDiagram(getFirstChartDiagram());
701
702
0
            Diagram::tTemplateWithServiceName aTemplateAndService = xDiagram->getTemplate(xChartTypeManager);
703
0
            aTemplateAndService.xChartTypeTemplate->changeDiagramData(xDiagram, xDataSource, aArguments);
704
0
        }
705
0
        catch (const uno::Exception &)
706
0
        {
707
0
            DBG_UNHANDLED_EXCEPTION("chart2");
708
0
        }
709
0
        unlockControllers();
710
0
    }
711
712
0
    if (m_nInLoad == 0)
713
0
        setModified(true);
714
0
}
715
716
// lang::XEventListener (base of util::XModifyListener)
717
void SAL_CALL ChartModel::disposing( const lang::EventObject& )
718
0
{
719
    // child was disposed -- should not happen from outside
720
0
}
721
722
// document::XStorageBasedDocument
723
void SAL_CALL ChartModel::loadFromStorage(
724
    const Reference< embed::XStorage >& xStorage,
725
    const Sequence< beans::PropertyValue >& rMediaDescriptor )
726
0
{
727
0
    attachResource( OUString(), rMediaDescriptor );
728
0
    impl_load( rMediaDescriptor, xStorage );
729
0
}
730
731
void SAL_CALL ChartModel::storeToStorage(
732
    const Reference< embed::XStorage >& xStorage,
733
    const Sequence< beans::PropertyValue >& rMediaDescriptor )
734
0
{
735
0
    impl_store( rMediaDescriptor, xStorage );
736
0
}
737
738
void SAL_CALL ChartModel::switchToStorage( const Reference< embed::XStorage >& xStorage )
739
0
{
740
0
    m_xStorage = xStorage;
741
0
    impl_notifyStorageChangeListeners();
742
0
}
743
744
Reference< embed::XStorage > SAL_CALL ChartModel::getDocumentStorage()
745
0
{
746
0
    return m_xStorage;
747
0
}
748
749
void ChartModel::impl_notifyStorageChangeListeners()
750
0
{
751
0
    std::unique_lock aGuard(m_aLifeTimeManager.m_aAccessMutex);
752
0
    if( m_aLifeTimeManager.m_aStorageChangeListeners.getLength(aGuard) )
753
0
    {
754
0
        m_aLifeTimeManager.m_aStorageChangeListeners.forEach(aGuard,
755
0
            [this](const uno::Reference<document::XStorageChangeListener>& l)
756
0
            {
757
0
                l->notifyStorageChange( static_cast< ::cppu::OWeakObject* >( this ), m_xStorage );
758
0
            });
759
0
    }
760
0
}
761
762
void SAL_CALL ChartModel::addStorageChangeListener( const Reference< document::XStorageChangeListener >& xListener )
763
0
{
764
0
    if( m_aLifeTimeManager.impl_isDisposedOrClosed() )
765
0
        return; //behave passive if already disposed or closed
766
767
0
    std::unique_lock aGuard(m_aLifeTimeManager.m_aAccessMutex);
768
0
    m_aLifeTimeManager.m_aStorageChangeListeners.addInterface( aGuard, xListener );
769
0
}
770
771
void SAL_CALL ChartModel::removeStorageChangeListener( const Reference< document::XStorageChangeListener >& xListener )
772
0
{
773
0
    if( m_aLifeTimeManager.impl_isDisposedOrClosed(false) )
774
0
        return; //behave passive if already disposed or closed
775
776
0
    std::unique_lock aGuard(m_aLifeTimeManager.m_aAccessMutex);
777
0
    m_aLifeTimeManager.m_aStorageChangeListeners.removeInterface(aGuard, xListener );
778
0
}
779
780
} //  namespace chart
781
782
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */