1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "VLegend.hxx"
22 #include "PropertyMapper.hxx"
23 #include "CommonConverters.hxx"
24 #include "ObjectIdentifier.hxx"
25 #include "RelativePositionHelper.hxx"
26 #include "AbstractShapeFactory.hxx"
27 #include "RelativeSizeHelper.hxx"
28 #include "LegendEntryProvider.hxx"
29 #include "chartview/DrawModelWrapper.hxx"
30 #include <com/sun/star/text/XTextRange.hpp>
31 #include <com/sun/star/text/WritingMode2.hpp>
32 #include <com/sun/star/beans/XPropertySet.hpp>
33 #include <com/sun/star/beans/XPropertyState.hpp>
34 #include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
35 #include <com/sun/star/drawing/LineJoint.hpp>
36 #include <com/sun/star/chart/ChartLegendExpansion.hpp>
37 #include <com/sun/star/chart2/LegendPosition.hpp>
38 #include <com/sun/star/chart2/RelativePosition.hpp>
39 #include <rtl/ustrbuf.hxx>
40 #include <svl/languageoptions.hxx>
45 using namespace ::com::sun::star
;
46 using namespace ::com::sun::star::chart2
;
48 using ::com::sun::star::uno::Reference
;
49 using ::com::sun::star::uno::Sequence
;
57 typedef ::std::pair
< ::chart::tNameSequence
, ::chart::tAnySequence
> tPropertyValues
;
59 typedef ::std::vector
< ViewLegendEntry
> tViewLegendEntryContainer
;
61 double lcl_CalcViewFontSize(
62 const Reference
< beans::XPropertySet
> & xProp
,
63 const awt::Size
& rReferenceSize
)
65 double fResult
= 10.0;
67 awt::Size aPropRefSize
;
68 float fFontHeight( 0.0 );
69 if( xProp
.is() && ( xProp
->getPropertyValue( "CharHeight") >>= fFontHeight
))
71 fResult
= fFontHeight
;
74 if( (xProp
->getPropertyValue( "ReferencePageSize") >>= aPropRefSize
) &&
75 (aPropRefSize
.Height
> 0))
77 fResult
= ::chart::RelativeSizeHelper::calculate( fFontHeight
, aPropRefSize
, rReferenceSize
);
80 catch( const uno::Exception
& ex
)
82 ASSERT_EXCEPTION( ex
);
87 return (fResult
* (2540.0 / 72.0));
90 void lcl_getProperties(
91 const Reference
< beans::XPropertySet
> & xLegendProp
,
92 tPropertyValues
& rOutLineFillProperties
,
93 tPropertyValues
& rOutTextProperties
,
94 const awt::Size
& rReferenceSize
)
96 // Get Line- and FillProperties from model legend
99 // set rOutLineFillProperties
100 ::chart::tPropertyNameValueMap aLineFillValueMap
;
101 ::chart::PropertyMapper::getValueMap( aLineFillValueMap
, ::chart::PropertyMapper::getPropertyNameMapForFillAndLineProperties(), xLegendProp
);
103 aLineFillValueMap
[ "LineJoint" ] = uno::makeAny( drawing::LineJoint_ROUND
);
105 ::chart::PropertyMapper::getMultiPropertyListsFromValueMap(
106 rOutLineFillProperties
.first
, rOutLineFillProperties
.second
, aLineFillValueMap
);
108 // set rOutTextProperties
109 ::chart::tPropertyNameValueMap aTextValueMap
;
110 ::chart::PropertyMapper::getValueMap( aTextValueMap
, ::chart::PropertyMapper::getPropertyNameMapForCharacterProperties(), xLegendProp
);
112 drawing::TextHorizontalAdjust
eHorizAdjust( drawing::TextHorizontalAdjust_LEFT
);
113 aTextValueMap
[ "TextAutoGrowHeight" ] = uno::makeAny( sal_True
);
114 aTextValueMap
[ "TextAutoGrowWidth" ] = uno::makeAny( sal_True
);
115 aTextValueMap
[ "TextHorizontalAdjust" ] = uno::makeAny( eHorizAdjust
);
116 aTextValueMap
[ "TextMaximumFrameWidth" ] = uno::makeAny( rReferenceSize
.Width
); //needs to be overwritten by actual available space in the legend
118 // recalculate font size
119 awt::Size aPropRefSize
;
120 float fFontHeight( 0.0 );
121 if( (xLegendProp
->getPropertyValue( "ReferencePageSize") >>= aPropRefSize
) &&
122 (aPropRefSize
.Height
> 0) &&
123 (aTextValueMap
[ "CharHeight" ] >>= fFontHeight
) )
125 aTextValueMap
[ "CharHeight" ] = uno::makeAny(
126 static_cast< float >(
127 ::chart::RelativeSizeHelper::calculate( fFontHeight
, aPropRefSize
, rReferenceSize
)));
129 if( aTextValueMap
[ "CharHeightAsian" ] >>= fFontHeight
)
131 aTextValueMap
[ "CharHeightAsian" ] = uno::makeAny(
132 static_cast< float >(
133 ::chart::RelativeSizeHelper::calculate( fFontHeight
, aPropRefSize
, rReferenceSize
)));
135 if( aTextValueMap
[ "CharHeightComplex" ] >>= fFontHeight
)
137 aTextValueMap
[ "CharHeightComplex" ] = uno::makeAny(
138 static_cast< float >(
139 ::chart::RelativeSizeHelper::calculate( fFontHeight
, aPropRefSize
, rReferenceSize
)));
143 ::chart::PropertyMapper::getMultiPropertyListsFromValueMap(
144 rOutTextProperties
.first
, rOutTextProperties
.second
, aTextValueMap
);
148 awt::Size
lcl_createTextShapes(
149 const tViewLegendEntryContainer
& rEntries
,
150 const Reference
< lang::XMultiServiceFactory
> & xShapeFactory
,
151 const Reference
< drawing::XShapes
> & xTarget
,
152 ::std::vector
< Reference
< drawing::XShape
> > & rOutTextShapes
,
153 const tPropertyValues
& rTextProperties
)
156 AbstractShapeFactory
* pShapeFactory
= AbstractShapeFactory::getOrCreateShapeFactory(xShapeFactory
);
158 for( tViewLegendEntryContainer::const_iterator
aIt( rEntries
.begin());
159 aIt
!= rEntries
.end(); ++aIt
)
163 OUString aLabelString
;
164 Sequence
< Reference
< XFormattedString2
> > aLabelSeq
= (*aIt
).aLabel
;
165 for( sal_Int32 i
= 0; i
< aLabelSeq
.getLength(); ++i
)
167 // todo: support more than one text range
171 aLabelString
= aLabelString
+ aLabelSeq
[i
]->getString();
172 // workaround for Issue #i67540#
173 if( aLabelString
.isEmpty())
177 Reference
< drawing::XShape
> xEntry
=
178 pShapeFactory
->createText( xTarget
, aLabelString
,
179 rTextProperties
.first
, rTextProperties
.second
, uno::Any() );
182 awt::Size
aCurrSize( xEntry
->getSize());
183 aResult
.Width
= ::std::max( aResult
.Width
, aCurrSize
.Width
);
184 aResult
.Height
= ::std::max( aResult
.Height
, aCurrSize
.Height
);
186 rOutTextShapes
.push_back( xEntry
);
188 catch( const uno::Exception
& ex
)
190 ASSERT_EXCEPTION( ex
);
197 void lcl_collectColumnWidths( std::vector
< sal_Int32
>& rColumnWidths
, const sal_Int32 nNumberOfRows
, const sal_Int32 nNumberOfColumns
,
198 const ::std::vector
< Reference
< drawing::XShape
> >& rTextShapes
, sal_Int32 nSymbolPlusDistanceWidth
)
200 rColumnWidths
.clear();
202 sal_Int32 nColumn
= 0;
203 sal_Int32 nNumberOfEntries
= rTextShapes
.size();
204 for( ; nRow
< nNumberOfRows
; ++nRow
)
206 for( nColumn
= 0; nColumn
< nNumberOfColumns
; ++nColumn
)
208 sal_Int32 nEntry
= (nColumn
+ nRow
* nNumberOfColumns
);
209 if( nEntry
< nNumberOfEntries
)
211 awt::Size
aTextSize( rTextShapes
[ nEntry
]->getSize() );
212 sal_Int32 nWidth
= nSymbolPlusDistanceWidth
+ aTextSize
.Width
;
214 rColumnWidths
.push_back( nWidth
);
216 rColumnWidths
[nColumn
] = ::std::max( nWidth
, rColumnWidths
[nColumn
] );
222 void lcl_collectRowHeighs( std::vector
< sal_Int32
>& rRowHeights
, const sal_Int32 nNumberOfRows
, const sal_Int32 nNumberOfColumns
,
223 const ::std::vector
< Reference
< drawing::XShape
> >& rTextShapes
)
225 // calculate maximum height for each row
226 // and collect column widths
229 sal_Int32 nColumn
= 0;
230 sal_Int32 nNumberOfEntries
= rTextShapes
.size();
231 for( ; nRow
< nNumberOfRows
; ++nRow
)
233 sal_Int32 nCurrentRowHeight
= 0;
234 for( nColumn
= 0; nColumn
< nNumberOfColumns
; ++nColumn
)
236 sal_Int32 nEntry
= (nColumn
+ nRow
* nNumberOfColumns
);
237 if( nEntry
< nNumberOfEntries
)
239 awt::Size
aTextSize( rTextShapes
[ nEntry
]->getSize() );
240 nCurrentRowHeight
= ::std::max( nCurrentRowHeight
, aTextSize
.Height
);
243 rRowHeights
.push_back( nCurrentRowHeight
);
247 sal_Int32
lcl_getTextLineHeight( const std::vector
< sal_Int32
>& aRowHeights
, const sal_Int32 nNumberOfRows
, double fViewFontSize
)
249 const sal_Int32 nFontHeight
= static_cast< sal_Int32
>( fViewFontSize
);
250 sal_Int32 nTextLineHeight
= nFontHeight
;
251 for( sal_Int32 nR
=0; nR
<nNumberOfRows
; nR
++ )
253 sal_Int32 nFullTextHeight
= aRowHeights
[ nR
];
254 if( ( nFullTextHeight
/ nFontHeight
) <= 1 )
256 nTextLineHeight
= nFullTextHeight
;//found an entry with one line-> have real text height
260 return nTextLineHeight
;
263 //returns resulting legend size
264 awt::Size
lcl_placeLegendEntries(
265 tViewLegendEntryContainer
& rEntries
,
266 ::com::sun::star::chart::ChartLegendExpansion eExpansion
,
267 bool bSymbolsLeftSide
,
268 double fViewFontSize
,
269 const awt::Size
& rMaxSymbolExtent
,
270 tPropertyValues
& rTextProperties
,
271 const Reference
< drawing::XShapes
> & xTarget
,
272 const Reference
< lang::XMultiServiceFactory
> & xShapeFactory
,
273 const awt::Size
& rAvailableSpace
)
275 bool bIsCustomSize
= (eExpansion
== ::com::sun::star::chart::ChartLegendExpansion_CUSTOM
);
276 awt::Size
aResultingLegendSize(0,0);
278 aResultingLegendSize
= rAvailableSpace
;
280 // #i109336# Improve auto positioning in chart
281 sal_Int32 nXPadding
= static_cast< sal_Int32
>( std::max( 100.0, fViewFontSize
* 0.33 ) );
282 //sal_Int32 nXPadding = static_cast< sal_Int32 >( std::max( 200.0, fViewFontSize * 0.33 ) );
283 sal_Int32 nXOffset
= static_cast< sal_Int32
>( std::max( 100.0, fViewFontSize
* 0.66 ) );
284 sal_Int32 nYPadding
= static_cast< sal_Int32
>( std::max( 100.0, fViewFontSize
* 0.2 ) );
285 sal_Int32 nYOffset
= static_cast< sal_Int32
>( std::max( 100.0, fViewFontSize
* 0.2 ) );
286 //sal_Int32 nYOffset = static_cast< sal_Int32 >( std::max( 230.0, fViewFontSize * 0.45 ) );
288 const sal_Int32 nSymbolToTextDistance
= static_cast< sal_Int32
>( std::max( 100.0, fViewFontSize
* 0.22 ) );//minimum 1mm
289 const sal_Int32 nSymbolPlusDistanceWidth
= rMaxSymbolExtent
.Width
+ nSymbolToTextDistance
;
290 sal_Int32 nMaxTextWidth
= rAvailableSpace
.Width
- (2 * nXPadding
) - nSymbolPlusDistanceWidth
;
291 OUString
aPropNameTextMaximumFrameWidth( "TextMaximumFrameWidth" );
292 uno::Any
* pFrameWidthAny
= PropertyMapper::getValuePointer( rTextProperties
.second
, rTextProperties
.first
, aPropNameTextMaximumFrameWidth
);
295 if( eExpansion
== ::com::sun::star::chart::ChartLegendExpansion_HIGH
)
297 // limit the width of texts to 30% of the total available width
298 // #i109336# Improve auto positioning in chart
299 nMaxTextWidth
= rAvailableSpace
.Width
* 3 / 10;
301 *pFrameWidthAny
= uno::makeAny(nMaxTextWidth
);
304 ::std::vector
< Reference
< drawing::XShape
> > aTextShapes
;
305 awt::Size aMaxEntryExtent
= lcl_createTextShapes( rEntries
, xShapeFactory
, xTarget
, aTextShapes
, rTextProperties
);
306 OSL_ASSERT( aTextShapes
.size() == rEntries
.size());
308 sal_Int32 nMaxEntryWidth
= nXOffset
+ nSymbolPlusDistanceWidth
+ aMaxEntryExtent
.Width
;
309 sal_Int32 nMaxEntryHeight
= nYOffset
+ aMaxEntryExtent
.Height
;
310 sal_Int32 nNumberOfEntries
= rEntries
.size();
312 sal_Int32 nNumberOfColumns
= 0, nNumberOfRows
= 0;
313 std::vector
< sal_Int32
> aColumnWidths
;
314 std::vector
< sal_Int32
> aRowHeights
;
316 sal_Int32 nTextLineHeight
= static_cast< sal_Int32
>( fViewFontSize
);
318 // determine layout depending on LegendExpansion
319 if( eExpansion
== ::com::sun::star::chart::ChartLegendExpansion_CUSTOM
)
321 sal_Int32 nCurrentRow
=0;
322 sal_Int32 nCurrentColumn
=-1;
323 sal_Int32 nMaxColumnCount
=-1;
324 for( sal_Int32 nN
=0; nN
<static_cast<sal_Int32
>(aTextShapes
.size()); nN
++ )
326 Reference
< drawing::XShape
> xShape( aTextShapes
[nN
] );
329 awt::Size
aSize( xShape
->getSize() );
330 sal_Int32 nNewWidth
= aSize
.Width
+ nSymbolPlusDistanceWidth
;
331 sal_Int32 nCurrentColumnCount
= aColumnWidths
.size();
333 //are we allowed to add a new column?
334 if( nMaxColumnCount
==-1 || (nCurrentColumn
+1) < nMaxColumnCount
)
336 //try add a new column
338 if( nCurrentColumn
< nCurrentColumnCount
)
340 //check whether the current column width is sufficient for the new entry
341 if( aColumnWidths
[nCurrentColumn
]>=nNewWidth
)
343 //all good proceed with next entry
347 if( nCurrentColumn
< nCurrentColumnCount
)
348 aColumnWidths
[nCurrentColumn
] = std::max( nNewWidth
, aColumnWidths
[nCurrentColumn
] );
350 aColumnWidths
.push_back(nNewWidth
);
352 //do the columns still fit into the given size?
353 nCurrentColumnCount
= aColumnWidths
.size();//update count
354 sal_Int32 nSumWidth
= 0;
355 for( sal_Int32 nC
=0; nC
<nCurrentColumnCount
; nC
++ )
356 nSumWidth
+= aColumnWidths
[nC
];
358 if( nSumWidth
<= rAvailableSpace
.Width
|| nCurrentColumnCount
==1 )
360 //all good proceed with next entry
365 //not enough space for the current amount of columns
366 //try again with less columns
367 nMaxColumnCount
= nCurrentColumnCount
-1;
371 aColumnWidths
.clear();
376 //add a new row and try the same entry again
382 nNumberOfColumns
= aColumnWidths
.size();
383 nNumberOfRows
= nCurrentRow
+1;
385 //check if there is not enough space so that some entries must be removed
386 lcl_collectRowHeighs( aRowHeights
, nNumberOfRows
, nNumberOfColumns
, aTextShapes
);
387 nTextLineHeight
= lcl_getTextLineHeight( aRowHeights
, nNumberOfRows
, fViewFontSize
);
388 sal_Int32 nSumHeight
= 0;
389 for( sal_Int32 nR
=0; nR
<nNumberOfRows
; nR
++ )
390 nSumHeight
+= aRowHeights
[nR
];
391 sal_Int32 nRemainingSpace
= rAvailableSpace
.Height
- nSumHeight
;
393 if( nRemainingSpace
< -100 ) // 1mm tolerance for OOXML interop tdf#90404
395 //remove entries that are too big
396 for( sal_Int32 nR
=nNumberOfRows
; nR
--; )
398 for( sal_Int32 nC
=nNumberOfColumns
; nC
--; )
400 sal_Int32 nEntry
= (nC
+ nR
* nNumberOfColumns
);
401 if( nEntry
< static_cast<sal_Int32
>(aTextShapes
.size()) )
403 DrawModelWrapper::removeShape( aTextShapes
[nEntry
] );
404 aTextShapes
.pop_back();
406 if( nEntry
< nNumberOfEntries
)
408 DrawModelWrapper::removeShape( rEntries
[ nEntry
].aSymbol
);
413 nSumHeight
-= aRowHeights
[nR
];
414 aRowHeights
.pop_back();
415 nRemainingSpace
= rAvailableSpace
.Height
- nSumHeight
;
416 if( nRemainingSpace
>=0 )
419 nNumberOfRows
= static_cast<sal_Int32
>(aRowHeights
.size());
421 if( nRemainingSpace
>= -100 ) // 1mm tolerance for OOXML interop tdf#90404
423 sal_Int32 nNormalSpacingHeight
= 2*nYPadding
+(nNumberOfRows
-1)*nYOffset
;
424 if( nRemainingSpace
< nNormalSpacingHeight
)
426 //reduce spacing between the entries
427 nYPadding
= nYOffset
= nRemainingSpace
/(nNumberOfRows
+1);
431 //we have some space left that should be spread equally between all rows
432 sal_Int32 nRemainingSingleSpace
= (nRemainingSpace
-nNormalSpacingHeight
)/(nNumberOfRows
+1);
433 nYPadding
+= nRemainingSingleSpace
;
434 nYOffset
+= nRemainingSingleSpace
;
438 //check spacing between columns
439 sal_Int32 nSumWidth
= 0;
440 for( sal_Int32 nC
=0; nC
<nNumberOfColumns
; nC
++ )
441 nSumWidth
+= aColumnWidths
[nC
];
442 nRemainingSpace
= rAvailableSpace
.Width
- nSumWidth
;
443 if( nRemainingSpace
>=0 )
445 sal_Int32 nNormalSpacingWidth
= 2*nXPadding
+(nNumberOfColumns
-1)*nXOffset
;
446 if( nRemainingSpace
< nNormalSpacingWidth
)
448 //reduce spacing between the entries
449 nXPadding
= nXOffset
= nRemainingSpace
/(nNumberOfColumns
+1);
453 //we have some space left that should be spread equally between all columns
454 sal_Int32 nRemainingSingleSpace
= (nRemainingSpace
-nNormalSpacingWidth
)/(nNumberOfColumns
+1);
455 nXPadding
+= nRemainingSingleSpace
;
456 nXOffset
+= nRemainingSingleSpace
;
460 else if( eExpansion
== ::com::sun::star::chart::ChartLegendExpansion_HIGH
)
462 sal_Int32 nMaxNumberOfRows
= nMaxEntryHeight
463 ? (rAvailableSpace
.Height
- 2*nYPadding
) / nMaxEntryHeight
466 nNumberOfColumns
= nMaxNumberOfRows
467 ? static_cast< sal_Int32
>(
468 ceil( static_cast< double >( nNumberOfEntries
) /
469 static_cast< double >( nMaxNumberOfRows
) ))
471 nNumberOfRows
= nNumberOfColumns
472 ? static_cast< sal_Int32
>(
473 ceil( static_cast< double >( nNumberOfEntries
) /
474 static_cast< double >( nNumberOfColumns
) ))
477 else if( eExpansion
== ::com::sun::star::chart::ChartLegendExpansion_WIDE
)
479 sal_Int32 nMaxNumberOfColumns
= nMaxEntryWidth
480 ? (rAvailableSpace
.Width
- 2*nXPadding
) / nMaxEntryWidth
483 nNumberOfRows
= nMaxNumberOfColumns
484 ? static_cast< sal_Int32
>(
485 ceil( static_cast< double >( nNumberOfEntries
) /
486 static_cast< double >( nMaxNumberOfColumns
) ))
488 nNumberOfColumns
= nNumberOfRows
489 ? static_cast< sal_Int32
>(
490 ceil( static_cast< double >( nNumberOfEntries
) /
491 static_cast< double >( nNumberOfRows
) ))
494 else // ::com::sun::star::chart::ChartLegendExpansion_BALANCED
496 double fAspect
= nMaxEntryHeight
497 ? static_cast< double >( nMaxEntryWidth
) / static_cast< double >( nMaxEntryHeight
)
500 nNumberOfRows
= static_cast< sal_Int32
>(
501 ceil( sqrt( static_cast< double >( nNumberOfEntries
) * fAspect
)));
502 nNumberOfColumns
= nNumberOfRows
503 ? static_cast< sal_Int32
>(
504 ceil( static_cast< double >( nNumberOfEntries
) /
505 static_cast< double >( nNumberOfRows
) ))
510 return aResultingLegendSize
;
512 if( eExpansion
!= ::com::sun::star::chart::ChartLegendExpansion_CUSTOM
)
514 lcl_collectColumnWidths( aColumnWidths
, nNumberOfRows
, nNumberOfColumns
, aTextShapes
, nSymbolPlusDistanceWidth
);
515 lcl_collectRowHeighs( aRowHeights
, nNumberOfRows
, nNumberOfColumns
, aTextShapes
);
516 nTextLineHeight
= lcl_getTextLineHeight( aRowHeights
, nNumberOfRows
, fViewFontSize
);
519 sal_Int32 nCurrentXPos
= nXPadding
;
520 sal_Int32 nCurrentYPos
= nYPadding
;
521 if( !bSymbolsLeftSide
)
522 nCurrentXPos
= -nXPadding
;
524 // place entries into column and rows
525 sal_Int32 nMaxYPos
= 0;
527 sal_Int32 nColumn
= 0;
528 for( nColumn
= 0; nColumn
< nNumberOfColumns
; ++nColumn
)
530 nCurrentYPos
= nYPadding
;
531 for( nRow
= 0; nRow
< nNumberOfRows
; ++nRow
)
533 sal_Int32 nEntry
= (nColumn
+ nRow
* nNumberOfColumns
);
534 if( nEntry
>= nNumberOfEntries
)
538 Reference
< drawing::XShape
> xTextShape( aTextShapes
[nEntry
] );
539 if( xTextShape
.is() )
541 awt::Size
aTextSize( xTextShape
->getSize() );
542 sal_Int32 nTextXPos
= nCurrentXPos
+ nSymbolPlusDistanceWidth
;
543 if( !bSymbolsLeftSide
)
544 nTextXPos
= nCurrentXPos
- nSymbolPlusDistanceWidth
- aTextSize
.Width
;
545 xTextShape
->setPosition( awt::Point( nTextXPos
, nCurrentYPos
));
549 Reference
< drawing::XShape
> xSymbol( rEntries
[ nEntry
].aSymbol
);
552 awt::Size
aSymbolSize( rMaxSymbolExtent
);
553 sal_Int32 nSymbolXPos
= nCurrentXPos
;
554 if( !bSymbolsLeftSide
)
555 nSymbolXPos
= nCurrentXPos
- rMaxSymbolExtent
.Width
;
556 sal_Int32 nSymbolYPos
= nCurrentYPos
+ ( ( nTextLineHeight
- aSymbolSize
.Height
) / 2 );
557 xSymbol
->setPosition( awt::Point( nSymbolXPos
, nSymbolYPos
) );
560 nCurrentYPos
+= aRowHeights
[ nRow
];
561 if( nRow
+1 < nNumberOfRows
)
562 nCurrentYPos
+= nYOffset
;
563 nMaxYPos
= ::std::max( nMaxYPos
, nCurrentYPos
);
565 if( bSymbolsLeftSide
)
567 nCurrentXPos
+= aColumnWidths
[nColumn
];
568 if( nColumn
+1 < nNumberOfColumns
)
569 nCurrentXPos
+= nXOffset
;
573 nCurrentXPos
-= aColumnWidths
[nColumn
];
574 if( nColumn
+1 < nNumberOfColumns
)
575 nCurrentXPos
-= nXOffset
;
581 if( bSymbolsLeftSide
)
582 aResultingLegendSize
.Width
= nCurrentXPos
+ nXPadding
;
585 sal_Int32 nLegendWidth
= -(nCurrentXPos
-nXPadding
);
586 aResultingLegendSize
.Width
= nLegendWidth
;
588 aResultingLegendSize
.Height
= nMaxYPos
+ nYPadding
;
591 if( !bSymbolsLeftSide
)
593 sal_Int32 nLegendWidth
= aResultingLegendSize
.Width
;
594 awt::Point
aPos(0,0);
595 for( sal_Int32 nEntry
=0; nEntry
<nNumberOfEntries
; nEntry
++ )
597 Reference
< drawing::XShape
> xSymbol( rEntries
[ nEntry
].aSymbol
);
598 aPos
= xSymbol
->getPosition();
599 aPos
.X
+= nLegendWidth
;
600 xSymbol
->setPosition( aPos
);
601 Reference
< drawing::XShape
> xText( aTextShapes
[ nEntry
] );
602 aPos
= xText
->getPosition();
603 aPos
.X
+= nLegendWidth
;
604 xText
->setPosition( aPos
);
608 return aResultingLegendSize
;
611 // #i109336# Improve auto positioning in chart
612 inline sal_Int32
lcl_getLegendLeftRightMargin()
614 return 210; // 1/100 mm
617 // #i109336# Improve auto positioning in chart
618 inline sal_Int32
lcl_getLegendTopBottomMargin()
620 return 185; // 1/100 mm
623 chart2::RelativePosition
lcl_getDefaultPosition( LegendPosition ePos
, const awt::Rectangle
& rOutAvailableSpace
, const awt::Size
& rPageSize
)
625 chart2::RelativePosition aResult
;
629 case LegendPosition_LINE_START
:
631 // #i109336# Improve auto positioning in chart
632 const double fDefaultDistance
= ( static_cast< double >( lcl_getLegendLeftRightMargin() ) /
633 static_cast< double >( rPageSize
.Width
) );
634 aResult
= chart2::RelativePosition(
635 fDefaultDistance
, 0.5, drawing::Alignment_LEFT
);
638 case LegendPosition_LINE_END
:
640 // #i109336# Improve auto positioning in chart
641 const double fDefaultDistance
= ( static_cast< double >( lcl_getLegendLeftRightMargin() ) /
642 static_cast< double >( rPageSize
.Width
) );
643 aResult
= chart2::RelativePosition(
644 1.0 - fDefaultDistance
, 0.5, drawing::Alignment_RIGHT
);
647 case LegendPosition_PAGE_START
:
649 // #i109336# Improve auto positioning in chart
650 const double fDefaultDistance
= ( static_cast< double >( lcl_getLegendTopBottomMargin() ) /
651 static_cast< double >( rPageSize
.Height
) );
652 double fDistance
= (static_cast<double>(rOutAvailableSpace
.Y
)/static_cast<double>(rPageSize
.Height
)) + fDefaultDistance
;
653 aResult
= chart2::RelativePosition(
654 0.5, fDistance
, drawing::Alignment_TOP
);
657 case LegendPosition_PAGE_END
:
659 // #i109336# Improve auto positioning in chart
660 const double fDefaultDistance
= ( static_cast< double >( lcl_getLegendTopBottomMargin() ) /
661 static_cast< double >( rPageSize
.Height
) );
662 aResult
= chart2::RelativePosition(
663 0.5, 1.0 - fDefaultDistance
, drawing::Alignment_BOTTOM
);
667 case LegendPosition_CUSTOM
:
669 case LegendPosition_MAKE_FIXED_SIZE
:
678 a point relative to the upper left corner that can be used for
679 XShape::setPosition()
681 awt::Point
lcl_calculatePositionAndRemainingSpace(
682 awt::Rectangle
& rRemainingSpace
,
683 const awt::Size
& rPageSize
,
684 const chart2::RelativePosition
& rRelPos
,
686 const awt::Size
& aLegendSize
)
688 // calculate position
690 static_cast< sal_Int32
>( rRelPos
.Primary
* rPageSize
.Width
),
691 static_cast< sal_Int32
>( rRelPos
.Secondary
* rPageSize
.Height
));
693 aResult
= RelativePositionHelper::getUpperLeftCornerOfAnchoredObject(
694 aResult
, aLegendSize
, rRelPos
.Anchor
);
696 // adapt rRemainingSpace if LegendPosition is not CUSTOM
697 // #i109336# Improve auto positioning in chart
698 sal_Int32 nXDistance
= lcl_getLegendLeftRightMargin();
699 sal_Int32 nYDistance
= lcl_getLegendTopBottomMargin();
702 case LegendPosition_LINE_START
:
704 sal_Int32 nExtent
= aLegendSize
.Width
;
705 rRemainingSpace
.Width
-= ( nExtent
+ nXDistance
);
706 rRemainingSpace
.X
+= ( nExtent
+ nXDistance
);
709 case LegendPosition_LINE_END
:
711 rRemainingSpace
.Width
-= ( aLegendSize
.Width
+ nXDistance
);
714 case LegendPosition_PAGE_START
:
716 sal_Int32 nExtent
= aLegendSize
.Height
;
717 rRemainingSpace
.Height
-= ( nExtent
+ nYDistance
);
718 rRemainingSpace
.Y
+= ( nExtent
+ nYDistance
);
721 case LegendPosition_PAGE_END
:
723 rRemainingSpace
.Height
-= ( aLegendSize
.Height
+ nYDistance
);
732 // adjust the legend position. Esp. for old files that had slightly smaller legends
733 const sal_Int32
nEdgeDistance( 30 );
734 if( aResult
.X
+ aLegendSize
.Width
> rPageSize
.Width
)
736 sal_Int32
nNewX( (rPageSize
.Width
- aLegendSize
.Width
) - nEdgeDistance
);
737 if( nNewX
> rPageSize
.Width
/ 4 )
740 if( aResult
.Y
+ aLegendSize
.Height
> rPageSize
.Height
)
742 sal_Int32
nNewY( (rPageSize
.Height
- aLegendSize
.Height
) - nEdgeDistance
);
743 if( nNewY
> rPageSize
.Height
/ 4 )
750 bool lcl_shouldSymbolsBePlacedOnTheLeftSide( const Reference
< beans::XPropertySet
>& xLegendProp
, sal_Int16 nDefaultWritingMode
)
752 bool bSymbolsLeftSide
= true;
755 if( SvtLanguageOptions().IsCTLFontEnabled() )
759 sal_Int16 nWritingMode
=-1;
760 if( (xLegendProp
->getPropertyValue( "WritingMode" ) >>= nWritingMode
) )
762 if( nWritingMode
== text::WritingMode2::PAGE
)
763 nWritingMode
= nDefaultWritingMode
;
764 if( nWritingMode
== text::WritingMode2::RL_TB
)
765 bSymbolsLeftSide
=false;
770 catch( const uno::Exception
& ex
)
772 ASSERT_EXCEPTION( ex
);
774 return bSymbolsLeftSide
;
777 } // anonymous namespace
780 const Reference
< XLegend
> & xLegend
,
781 const Reference
< uno::XComponentContext
> & xContext
,
782 const std::vector
< LegendEntryProvider
* >& rLegendEntryProviderList
,
783 const Reference
< drawing::XShapes
>& xTargetPage
,
784 const Reference
< lang::XMultiServiceFactory
>& xFactory
,
786 : m_xTarget(xTargetPage
)
787 , m_xShapeFactory(xFactory
)
790 , m_xContext(xContext
)
791 , m_aLegendEntryProviderList(rLegendEntryProviderList
)
792 , m_nDefaultWritingMode(text::WritingMode2::LR_TB
)
796 void VLegend::setDefaultWritingMode( sal_Int16 nDefaultWritingMode
)
798 m_nDefaultWritingMode
= nDefaultWritingMode
;
801 bool VLegend::isVisible( const Reference
< XLegend
> & xLegend
)
809 Reference
< beans::XPropertySet
> xLegendProp( xLegend
, uno::UNO_QUERY_THROW
);
810 xLegendProp
->getPropertyValue( "Show") >>= bShow
;
812 catch( const uno::Exception
& ex
)
814 ASSERT_EXCEPTION( ex
);
820 void VLegend::createShapes(
821 const awt::Size
& rAvailableSpace
,
822 const awt::Size
& rPageSize
)
824 if(! (m_xLegend
.is() &&
825 m_xShapeFactory
.is() &&
831 //create shape and add to page
832 AbstractShapeFactory
* pShapeFactory
= AbstractShapeFactory::getOrCreateShapeFactory(m_xShapeFactory
);
833 OUString
aLegendParticle( ObjectIdentifier::createParticleForLegend( m_xLegend
, mrModel
) );
834 m_xShape
.set( pShapeFactory
->createGroup2D( m_xTarget
,
835 ObjectIdentifier::createClassifiedIdentifierForParticle( aLegendParticle
)),
838 // create and insert sub-shapes
839 Reference
< drawing::XShapes
> xLegendContainer( m_xShape
, uno::UNO_QUERY
);
840 if( xLegendContainer
.is())
842 // for quickly setting properties
843 tPropertyValues aLineFillProperties
;
844 tPropertyValues aTextProperties
;
846 Reference
< beans::XPropertySet
> xLegendProp( m_xLegend
, uno::UNO_QUERY
);
847 ::com::sun::star::chart::ChartLegendExpansion eExpansion
= ::com::sun::star::chart::ChartLegendExpansion_HIGH
;
848 awt::Size
aLegendSize( rAvailableSpace
);
850 if( xLegendProp
.is())
852 // get Expansion property
853 xLegendProp
->getPropertyValue( "Expansion") >>= eExpansion
;
854 if( eExpansion
== ::com::sun::star::chart::ChartLegendExpansion_CUSTOM
)
856 RelativeSize aRelativeSize
;
857 if ((xLegendProp
->getPropertyValue( "RelativeSize") >>= aRelativeSize
))
859 aLegendSize
.Width
= static_cast<sal_Int32
>(::rtl::math::approxCeil( aRelativeSize
.Primary
* rPageSize
.Width
));
860 aLegendSize
.Height
= static_cast<sal_Int32
>(::rtl::math::approxCeil( aRelativeSize
.Secondary
* rPageSize
.Height
));
863 eExpansion
= ::com::sun::star::chart::ChartLegendExpansion_HIGH
;
865 lcl_getProperties( xLegendProp
, aLineFillProperties
, aTextProperties
, rPageSize
);
869 double fViewFontSize
= lcl_CalcViewFontSize( xLegendProp
, rPageSize
);//todo
870 // #i109336# Improve auto positioning in chart
871 sal_Int32 nSymbolHeight
= static_cast< sal_Int32
>( fViewFontSize
* 0.6 );
872 sal_Int32 nSymbolWidth
= static_cast< sal_Int32
>( nSymbolHeight
);
874 ::std::vector
< LegendEntryProvider
* >::const_iterator aIter
= m_aLegendEntryProviderList
.begin();
875 const ::std::vector
< LegendEntryProvider
* >::const_iterator aEnd
= m_aLegendEntryProviderList
.end();
876 for( aIter
= m_aLegendEntryProviderList
.begin(); aIter
!= aEnd
; ++aIter
)
878 LegendEntryProvider
* pLegendEntryProvider( *aIter
);
879 if( pLegendEntryProvider
)
881 awt::Size aCurrentRatio
= pLegendEntryProvider
->getPreferredLegendKeyAspectRatio();
882 sal_Int32 nCurrentWidth
= aCurrentRatio
.Width
;
883 if( aCurrentRatio
.Height
> 0 )
885 nCurrentWidth
= nSymbolHeight
* aCurrentRatio
.Width
/aCurrentRatio
.Height
;
887 nSymbolWidth
= std::max( nSymbolWidth
, nCurrentWidth
);
890 awt::Size
aMaxSymbolExtent( nSymbolWidth
, nSymbolHeight
);
892 tViewLegendEntryContainer aViewEntries
;
893 for( aIter
= m_aLegendEntryProviderList
.begin(); aIter
!= aEnd
; ++aIter
)
895 LegendEntryProvider
* pLegendEntryProvider( *aIter
);
896 if( pLegendEntryProvider
)
898 std::vector
< ViewLegendEntry
> aNewEntries
= pLegendEntryProvider
->createLegendEntries( aMaxSymbolExtent
, eExpansion
, xLegendProp
, xLegendContainer
, m_xShapeFactory
, m_xContext
);
899 aViewEntries
.insert( aViewEntries
.end(), aNewEntries
.begin(), aNewEntries
.end() );
903 bool bSymbolsLeftSide
= lcl_shouldSymbolsBePlacedOnTheLeftSide( xLegendProp
, m_nDefaultWritingMode
);
905 if( !aViewEntries
.empty() ) {
907 aLegendSize
= lcl_placeLegendEntries( aViewEntries
, eExpansion
, bSymbolsLeftSide
, fViewFontSize
, aMaxSymbolExtent
,
908 aTextProperties
, xLegendContainer
, m_xShapeFactory
, aLegendSize
);
912 Reference
< drawing::XShape
> xBorder
=
913 pShapeFactory
->createRectangle( xLegendContainer
,
916 aLineFillProperties
.first
,
917 aLineFillProperties
.second
, AbstractShapeFactory::Bottom
);
919 //because of this name this border will be used for marking the legend
920 AbstractShapeFactory::setShapeName( xBorder
, "MarkHandles" );
923 catch( const uno::Exception
& ex
)
925 ASSERT_EXCEPTION( ex
);
929 void VLegend::changePosition(
930 awt::Rectangle
& rOutAvailableSpace
,
931 const awt::Size
& rPageSize
)
938 // determine position and alignment depending on default position
939 awt::Size aLegendSize
= m_xShape
->getSize();
940 Reference
< beans::XPropertySet
> xLegendProp( m_xLegend
, uno::UNO_QUERY_THROW
);
941 chart2::RelativePosition aRelativePosition
;
944 ! (xLegendProp
->getPropertyValue( "RelativePosition") >>= aRelativePosition
);
946 LegendPosition ePos
= LegendPosition_CUSTOM
;
947 xLegendProp
->getPropertyValue( "AnchorPosition") >>= ePos
;
952 // auto position: relative to remaining space
953 aRelativePosition
= lcl_getDefaultPosition( ePos
, rOutAvailableSpace
, rPageSize
);
954 awt::Point aPos
= lcl_calculatePositionAndRemainingSpace(
955 rOutAvailableSpace
, rPageSize
, aRelativePosition
, ePos
, aLegendSize
);
956 m_xShape
->setPosition( aPos
);
960 // manual position: relative to whole page
961 awt::Rectangle
aAvailableSpace( 0, 0, rPageSize
.Width
, rPageSize
.Height
);
962 awt::Point aPos
= lcl_calculatePositionAndRemainingSpace(
963 aAvailableSpace
, rPageSize
, aRelativePosition
, ePos
, aLegendSize
);
964 m_xShape
->setPosition( aPos
);
966 if( ePos
!= LegendPosition_CUSTOM
)
968 // calculate remaining space as if having autoposition:
969 aRelativePosition
= lcl_getDefaultPosition( ePos
, rOutAvailableSpace
, rPageSize
);
970 lcl_calculatePositionAndRemainingSpace(
971 rOutAvailableSpace
, rPageSize
, aRelativePosition
, ePos
, aLegendSize
);
975 catch( const uno::Exception
& ex
)
977 ASSERT_EXCEPTION( ex
);
983 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */