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