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