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