1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 #include "oox/drawingml/chart/converterbase.hxx"
25
26 #include <com/sun/star/chart/XAxisXSupplier.hpp>
27 #include <com/sun/star/chart/XAxisYSupplier.hpp>
28 #include <com/sun/star/chart/XAxisZSupplier.hpp>
29 #include <com/sun/star/chart/XChartDocument.hpp>
30 #include <com/sun/star/chart/XSecondAxisTitleSupplier.hpp>
31 #include <com/sun/star/chart2/RelativePosition.hpp>
32 #include <com/sun/star/chart2/RelativeSize.hpp>
33 #include <com/sun/star/drawing/FillStyle.hpp>
34 #include <com/sun/star/drawing/LineStyle.hpp>
35 #include <com/sun/star/frame/XModel.hpp>
36 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
37 #include <tools/solar.h> // for F_PI180
38 #include "oox/core/xmlfilterbase.hxx"
39 #include "oox/drawingml/theme.hxx"
40
41 namespace oox {
42 namespace drawingml {
43 namespace chart {
44
45 // ============================================================================
46
47 namespace cssc = ::com::sun::star::chart;
48
49 using namespace ::com::sun::star::awt;
50 using namespace ::com::sun::star::chart2;
51 using namespace ::com::sun::star::drawing;
52 using namespace ::com::sun::star::frame;
53 using namespace ::com::sun::star::lang;
54 using namespace ::com::sun::star::uno;
55
56 using ::oox::core::XmlFilterBase;
57 using ::rtl::OUString;
58
59 // ============================================================================
60
61 namespace {
62
63 struct TitleKey : public ::std::pair< ObjectType, ::std::pair< sal_Int32, sal_Int32 > >
64 {
TitleKeyoox::drawingml::chart::__anon311853450111::TitleKey65 inline explicit TitleKey( ObjectType eObjType, sal_Int32 nMainIdx = -1, sal_Int32 nSubIdx = -1 )
66 { first = eObjType; second.first = nMainIdx; second.second = nSubIdx; }
67 };
68
69 // ----------------------------------------------------------------------------
70
71 /** A helper structure to store all data related to title objects. Needed for
72 the conversion of manual title positions that needs the old Chart1 API.
73 */
74 struct TitleLayoutInfo
75 {
76 typedef Reference< XShape > (*GetShapeFunc)( const Reference< cssc::XChartDocument >& );
77
78 Reference< XTitle > mxTitle; /// The API title object.
79 ModelRef< LayoutModel > mxLayout; /// The layout model, if existing.
80 GetShapeFunc mpGetShape; /// Helper function to receive the title shape.
81
TitleLayoutInfooox::drawingml::chart::__anon311853450111::TitleLayoutInfo82 inline explicit TitleLayoutInfo() : mpGetShape( 0 ) {}
83
84 void convertTitlePos(
85 ConverterRoot& rRoot,
86 const Reference< cssc::XChartDocument >& rxChart1Doc );
87 };
88
convertTitlePos(ConverterRoot & rRoot,const Reference<cssc::XChartDocument> & rxChart1Doc)89 void TitleLayoutInfo::convertTitlePos( ConverterRoot& rRoot, const Reference< cssc::XChartDocument >& rxChart1Doc )
90 {
91 if( mxTitle.is() && mpGetShape ) try
92 {
93 // try to get the title shape
94 Reference< XShape > xTitleShape( mpGetShape( rxChart1Doc ), UNO_SET_THROW );
95 // get title rotation angle, needed for correction of position of top-left edge
96 double fAngle = 0.0;
97 PropertySet aTitleProp( mxTitle );
98 aTitleProp.getProperty( fAngle, PROP_TextRotation );
99 // convert the position
100 LayoutModel& rLayout = mxLayout.getOrCreate();
101 LayoutConverter aLayoutConv( rRoot, rLayout );
102 aLayoutConv.convertFromModel( xTitleShape, fAngle );
103 }
104 catch( Exception& )
105 {
106 }
107 }
108
109 // ----------------------------------------------------------------------------
110
111 /* The following local functions implement getting the XShape interface of all
112 supported title objects (chart and axes). This needs some effort due to the
113 design of the old Chart1 API used to access these objects. */
114
115 /** A code fragment that returns a shape object from the passed shape supplier
116 using the specified interface function. Checks a boolean property first. */
117 #define OOX_FRAGMENT_GETTITLESHAPE( shape_supplier, supplier_func, property_name ) \
118 PropertySet aPropSet( shape_supplier ); \
119 if( shape_supplier.is() && aPropSet.getBoolProperty( PROP_##property_name ) ) \
120 return shape_supplier->supplier_func(); \
121 return Reference< XShape >(); \
122
123 /** Implements a function returning the drawing shape of an axis title, if
124 existing, using the specified API interface and its function. */
125 #define OOX_DEFINEFUNC_GETAXISTITLESHAPE( func_name, interface_type, supplier_func, property_name ) \
126 Reference< XShape > func_name( const Reference< cssc::XChartDocument >& rxChart1Doc ) \
127 { \
128 Reference< cssc::interface_type > xAxisSupp( rxChart1Doc->getDiagram(), UNO_QUERY ); \
129 OOX_FRAGMENT_GETTITLESHAPE( xAxisSupp, supplier_func, property_name ) \
130 }
131
132 /** Returns the drawing shape of the main title, if existing. */
lclGetMainTitleShape(const Reference<cssc::XChartDocument> & rxChart1Doc)133 Reference< XShape > lclGetMainTitleShape( const Reference< cssc::XChartDocument >& rxChart1Doc )
134 {
135 OOX_FRAGMENT_GETTITLESHAPE( rxChart1Doc, getTitle, HasMainTitle )
136 }
137
138 OOX_DEFINEFUNC_GETAXISTITLESHAPE( lclGetXAxisTitleShape, XAxisXSupplier, getXAxisTitle, HasXAxisTitle )
139 OOX_DEFINEFUNC_GETAXISTITLESHAPE( lclGetYAxisTitleShape, XAxisYSupplier, getYAxisTitle, HasYAxisTitle )
140 OOX_DEFINEFUNC_GETAXISTITLESHAPE( lclGetZAxisTitleShape, XAxisZSupplier, getZAxisTitle, HasZAxisTitle )
141 OOX_DEFINEFUNC_GETAXISTITLESHAPE( lclGetSecXAxisTitleShape, XSecondAxisTitleSupplier, getSecondXAxisTitle, HasSecondaryXAxisTitle )
142 OOX_DEFINEFUNC_GETAXISTITLESHAPE( lclGetSecYAxisTitleShape, XSecondAxisTitleSupplier, getSecondYAxisTitle, HasSecondaryYAxisTitle )
143
144 #undef OOX_DEFINEFUNC_GETAXISTITLESHAPE
145 #undef OOX_IMPLEMENT_GETTITLESHAPE
146
147 } // namespace
148
149 // ============================================================================
150
151 struct ConverterData
152 {
153 typedef ::std::map< TitleKey, TitleLayoutInfo > TitleMap;
154
155 ObjectFormatter maFormatter;
156 TitleMap maTitles;
157 XmlFilterBase& mrFilter;
158 ChartConverter& mrConverter;
159 Reference< XChartDocument > mxDoc;
160 Size maSize;
161
162 explicit ConverterData(
163 XmlFilterBase& rFilter,
164 ChartConverter& rChartConverter,
165 const ChartSpaceModel& rChartModel,
166 const Reference< XChartDocument >& rxChartDoc,
167 const Size& rChartSize );
168 ~ConverterData();
169 };
170
171 // ----------------------------------------------------------------------------
172
ConverterData(XmlFilterBase & rFilter,ChartConverter & rChartConverter,const ChartSpaceModel & rChartModel,const Reference<XChartDocument> & rxChartDoc,const Size & rChartSize)173 ConverterData::ConverterData(
174 XmlFilterBase& rFilter,
175 ChartConverter& rChartConverter,
176 const ChartSpaceModel& rChartModel,
177 const Reference< XChartDocument >& rxChartDoc,
178 const Size& rChartSize ) :
179 maFormatter( rFilter, rxChartDoc, rChartModel ),
180 mrFilter( rFilter ),
181 mrConverter( rChartConverter ),
182 mxDoc( rxChartDoc ),
183 maSize( rChartSize )
184 {
185 OSL_ENSURE( mxDoc.is(), "ConverterData::ConverterData - missing chart document" );
186 // lock the model to suppress internal updates during conversion
187 try
188 {
189 Reference< XModel > xModel( mxDoc, UNO_QUERY_THROW );
190 xModel->lockControllers();
191 }
192 catch( Exception& )
193 {
194 }
195
196 // prepare conversion of title positions
197 maTitles[ TitleKey( OBJECTTYPE_CHARTTITLE ) ].mpGetShape = lclGetMainTitleShape;
198 maTitles[ TitleKey( OBJECTTYPE_AXISTITLE, API_PRIM_AXESSET, API_X_AXIS ) ].mpGetShape = lclGetXAxisTitleShape;
199 maTitles[ TitleKey( OBJECTTYPE_AXISTITLE, API_PRIM_AXESSET, API_Y_AXIS ) ].mpGetShape = lclGetYAxisTitleShape;
200 maTitles[ TitleKey( OBJECTTYPE_AXISTITLE, API_PRIM_AXESSET, API_Z_AXIS ) ].mpGetShape = lclGetZAxisTitleShape;
201 maTitles[ TitleKey( OBJECTTYPE_AXISTITLE, API_SECN_AXESSET, API_X_AXIS ) ].mpGetShape = lclGetSecXAxisTitleShape;
202 maTitles[ TitleKey( OBJECTTYPE_AXISTITLE, API_SECN_AXESSET, API_Y_AXIS ) ].mpGetShape = lclGetSecYAxisTitleShape;
203 }
204
~ConverterData()205 ConverterData::~ConverterData()
206 {
207 // unlock the model
208 try
209 {
210 Reference< XModel > xModel( mxDoc, UNO_QUERY_THROW );
211 xModel->unlockControllers();
212 }
213 catch( Exception& )
214 {
215 }
216 }
217
218 // ============================================================================
219
ConverterRoot(XmlFilterBase & rFilter,ChartConverter & rChartConverter,const ChartSpaceModel & rChartModel,const Reference<XChartDocument> & rxChartDoc,const Size & rChartSize)220 ConverterRoot::ConverterRoot(
221 XmlFilterBase& rFilter,
222 ChartConverter& rChartConverter,
223 const ChartSpaceModel& rChartModel,
224 const Reference< XChartDocument >& rxChartDoc,
225 const Size& rChartSize ) :
226 mxData( new ConverterData( rFilter, rChartConverter, rChartModel, rxChartDoc, rChartSize ) )
227 {
228 }
229
~ConverterRoot()230 ConverterRoot::~ConverterRoot()
231 {
232 }
233
createInstance(const OUString & rServiceName) const234 Reference< XInterface > ConverterRoot::createInstance( const OUString& rServiceName ) const
235 {
236 Reference< XInterface > xInt;
237 try
238 {
239 xInt = mxData->mrFilter.getServiceFactory()->createInstance( rServiceName );
240 }
241 catch( Exception& )
242 {
243 }
244 OSL_ENSURE( xInt.is(), "ConverterRoot::createInstance - cannot create instance" );
245 return xInt;
246 }
247
getFilter() const248 XmlFilterBase& ConverterRoot::getFilter() const
249 {
250 return mxData->mrFilter;
251 }
252
getChartConverter() const253 ChartConverter& ConverterRoot::getChartConverter() const
254 {
255 return mxData->mrConverter;
256 }
257
getChartDocument() const258 Reference< XChartDocument > ConverterRoot::getChartDocument() const
259 {
260 return mxData->mxDoc;
261 }
262
getChartSize() const263 const Size& ConverterRoot::getChartSize() const
264 {
265 return mxData->maSize;
266 }
267
getFormatter() const268 ObjectFormatter& ConverterRoot::getFormatter() const
269 {
270 return mxData->maFormatter;
271 }
272
registerTitleLayout(const Reference<XTitle> & rxTitle,const ModelRef<LayoutModel> & rxLayout,ObjectType eObjType,sal_Int32 nMainIdx,sal_Int32 nSubIdx)273 void ConverterRoot::registerTitleLayout( const Reference< XTitle >& rxTitle,
274 const ModelRef< LayoutModel >& rxLayout, ObjectType eObjType, sal_Int32 nMainIdx, sal_Int32 nSubIdx )
275 {
276 OSL_ENSURE( rxTitle.is(), "ConverterRoot::registerTitleLayout - missing title object" );
277 TitleLayoutInfo& rTitleInfo = mxData->maTitles[ TitleKey( eObjType, nMainIdx, nSubIdx ) ];
278 OSL_ENSURE( rTitleInfo.mpGetShape, "ConverterRoot::registerTitleLayout - invalid title key" );
279 rTitleInfo.mxTitle = rxTitle;
280 rTitleInfo.mxLayout = rxLayout;
281 }
282
convertTitlePositions()283 void ConverterRoot::convertTitlePositions()
284 {
285 try
286 {
287 Reference< cssc::XChartDocument > xChart1Doc( mxData->mxDoc, UNO_QUERY_THROW );
288 for( ConverterData::TitleMap::iterator aIt = mxData->maTitles.begin(), aEnd = mxData->maTitles.end(); aIt != aEnd; ++aIt )
289 aIt->second.convertTitlePos( *this, xChart1Doc );
290 }
291 catch( Exception& )
292 {
293 }
294 }
295
296 // ============================================================================
297
298 namespace {
299
300 /** Returns a position value in the chart area in 1/100 mm. */
lclCalcPosition(sal_Int32 nChartSize,double fPos,sal_Int32 nPosMode)301 sal_Int32 lclCalcPosition( sal_Int32 nChartSize, double fPos, sal_Int32 nPosMode )
302 {
303 switch( nPosMode )
304 {
305 case XML_edge: // absolute start position as factor of chart size
306 return getLimitedValue< sal_Int32, double >( nChartSize * fPos + 0.5, 0, nChartSize );
307 case XML_factor: // position relative to object default position
308 OSL_ENSURE( false, "lclCalcPosition - relative positioning not supported" );
309 return -1;
310 };
311
312 OSL_ENSURE( false, "lclCalcPosition - unknown positioning mode" );
313 return -1;
314 }
315
316 /** Returns a size value in the chart area in 1/100 mm. */
lclCalcSize(sal_Int32 nPos,sal_Int32 nChartSize,double fSize,sal_Int32 nSizeMode)317 sal_Int32 lclCalcSize( sal_Int32 nPos, sal_Int32 nChartSize, double fSize, sal_Int32 nSizeMode )
318 {
319 sal_Int32 nValue = getLimitedValue< sal_Int32, double >( nChartSize * fSize + 0.5, 0, nChartSize );
320 switch( nSizeMode )
321 {
322 case XML_factor: // passed value is width/height
323 return nValue;
324 case XML_edge: // passed value is right/bottom position
325 return nValue - nPos + 1;
326 };
327
328 OSL_ENSURE( false, "lclCalcSize - unknown size mode" );
329 return -1;
330 }
331
332 /** Returns a relative size value in the chart area. */
lclCalcRelSize(double fPos,double fSize,sal_Int32 nSizeMode)333 double lclCalcRelSize( double fPos, double fSize, sal_Int32 nSizeMode )
334 {
335 switch( nSizeMode )
336 {
337 case XML_factor: // passed value is width/height
338 break;
339 case XML_edge: // passed value is right/bottom position
340 fSize -= fPos;
341 break;
342 default:
343 OSL_ENSURE( false, "lclCalcRelSize - unknown size mode" );
344 fSize = 0.0;
345 };
346 return getLimitedValue< double, double >( fSize, 0.0, 1.0 - fPos );
347 }
348
349 } // namespace
350
351 // ----------------------------------------------------------------------------
352
LayoutConverter(const ConverterRoot & rParent,LayoutModel & rModel)353 LayoutConverter::LayoutConverter( const ConverterRoot& rParent, LayoutModel& rModel ) :
354 ConverterBase< LayoutModel >( rParent, rModel )
355 {
356 }
357
~LayoutConverter()358 LayoutConverter::~LayoutConverter()
359 {
360 }
361
calcAbsRectangle(Rectangle & orRect) const362 bool LayoutConverter::calcAbsRectangle( Rectangle& orRect ) const
363 {
364 if( !mrModel.mbAutoLayout )
365 {
366 const Size& rChartSize = getChartSize();
367 orRect.X = lclCalcPosition( rChartSize.Width, mrModel.mfX, mrModel.mnXMode );
368 orRect.Y = lclCalcPosition( rChartSize.Height, mrModel.mfY, mrModel.mnYMode );
369 if( (orRect.X >= 0) && (orRect.Y >= 0) )
370 {
371 orRect.Width = lclCalcSize( orRect.X, rChartSize.Width, mrModel.mfW, mrModel.mnWMode );
372 orRect.Height = lclCalcSize( orRect.Y, rChartSize.Height, mrModel.mfH, mrModel.mnHMode );
373 return (orRect.Width > 0) && (orRect.Height > 0);
374 }
375 }
376 return false;
377 }
378
convertFromModel(PropertySet & rPropSet)379 bool LayoutConverter::convertFromModel( PropertySet& rPropSet )
380 {
381 if( !mrModel.mbAutoLayout &&
382 (mrModel.mnXMode == XML_edge) && (mrModel.mfX >= 0.0) &&
383 (mrModel.mnYMode == XML_edge) && (mrModel.mfY >= 0.0) )
384 {
385 RelativePosition aPos(
386 getLimitedValue< double, double >( mrModel.mfX, 0.0, 1.0 ),
387 getLimitedValue< double, double >( mrModel.mfY, 0.0, 1.0 ),
388 Alignment_TOP_LEFT );
389 rPropSet.setProperty( PROP_RelativePosition, aPos );
390
391 RelativeSize aSize(
392 lclCalcRelSize( aPos.Primary, mrModel.mfW, mrModel.mnWMode ),
393 lclCalcRelSize( aPos.Secondary, mrModel.mfH, mrModel.mnHMode ) );
394 if( (aSize.Primary > 0.0) && (aSize.Secondary > 0.0) )
395 {
396 rPropSet.setProperty( PROP_RelativeSize, aSize );
397 return true;
398 }
399 }
400 return false;
401 }
402
convertFromModel(const Reference<XShape> & rxShape,double fRotationAngle)403 bool LayoutConverter::convertFromModel( const Reference< XShape >& rxShape, double fRotationAngle )
404 {
405 if( !mrModel.mbAutoLayout )
406 {
407 const Size& rChartSize = getChartSize();
408 Point aShapePos(
409 lclCalcPosition( rChartSize.Width, mrModel.mfX, mrModel.mnXMode ),
410 lclCalcPosition( rChartSize.Height, mrModel.mfY, mrModel.mnYMode ) );
411 if( (aShapePos.X >= 0) && (aShapePos.Y >= 0) )
412 {
413 // the call to XShape.getSize() may recalc the chart view
414 Size aShapeSize = rxShape->getSize();
415 // rotated shapes need special handling...
416 double fSin = fabs( sin( fRotationAngle * F_PI180 ) );
417 // add part of height to X direction, if title is rotated down
418 if( fRotationAngle > 180.0 )
419 aShapePos.X += static_cast< sal_Int32 >( fSin * aShapeSize.Height + 0.5 );
420 // add part of width to Y direction, if title is rotated up
421 else if( fRotationAngle > 0.0 )
422 aShapePos.Y += static_cast< sal_Int32 >( fSin * aShapeSize.Width + 0.5 );
423 // set the resulting position at the shape
424 rxShape->setPosition( aShapePos );
425 return true;
426 }
427 }
428 return false;
429 }
430
431 // ============================================================================
432
433 } // namespace chart
434 } // namespace drawingml
435 } // namespace oox
436