fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / chart2 / source / view / main / VLegend.cxx
blobd2931654f9d3a0e918f97d42d4cf8e450b019a8a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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"
21 #include "macros.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>
42 #include <vector>
43 #include <algorithm>
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;
51 namespace chart
54 namespace
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;
72 try
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 );
86 // pt -> 1/100th mm
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
97 if( xLegendProp.is())
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 )
155 awt::Size aResult;
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
168 if( i == 1 )
169 break;
171 aLabelString = aLabelString + aLabelSeq[i]->getString();
172 // workaround for Issue #i67540#
173 if( aLabelString.isEmpty())
174 aLabelString = " ";
177 Reference< drawing::XShape > xEntry =
178 pShapeFactory->createText( xTarget, aLabelString,
179 rTextProperties.first, rTextProperties.second, uno::Any() );
181 // adapt max-extent
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 );
194 return aResult;
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();
201 sal_Int32 nRow = 0;
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;
213 if( nRow==0 )
214 rColumnWidths.push_back( nWidth );
215 else
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
227 rRowHeights.clear();
228 sal_Int32 nRow = 0;
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
257 break;
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);
277 if( bIsCustomSize )
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);
293 if(pFrameWidthAny)
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] );
327 if( !xShape.is() )
328 continue;
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
337 nCurrentColumn++;
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
344 continue;
347 if( nCurrentColumn < nCurrentColumnCount )
348 aColumnWidths[nCurrentColumn] = std::max( nNewWidth, aColumnWidths[nCurrentColumn] );
349 else
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
361 continue;
363 else
365 //not enough space for the current amount of columns
366 //try again with less columns
367 nMaxColumnCount = nCurrentColumnCount-1;
368 nN=-1;
369 nCurrentRow=0;
370 nCurrentColumn=-1;
371 aColumnWidths.clear();
374 else
376 //add a new row and try the same entry again
377 nCurrentRow++;
378 nCurrentColumn=-1;
379 nN--;
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 );
409 rEntries.pop_back();
410 nNumberOfEntries--;
413 nSumHeight -= aRowHeights[nR];
414 aRowHeights.pop_back();
415 nRemainingSpace = rAvailableSpace.Height - nSumHeight;
416 if( nRemainingSpace>=0 )
417 break;
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);
429 else
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);
451 else
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
464 : 0;
466 nNumberOfColumns = nMaxNumberOfRows
467 ? static_cast< sal_Int32 >(
468 ceil( static_cast< double >( nNumberOfEntries ) /
469 static_cast< double >( nMaxNumberOfRows ) ))
470 : 0;
471 nNumberOfRows = nNumberOfColumns
472 ? static_cast< sal_Int32 >(
473 ceil( static_cast< double >( nNumberOfEntries ) /
474 static_cast< double >( nNumberOfColumns ) ))
475 : 0;
477 else if( eExpansion == ::com::sun::star::chart::ChartLegendExpansion_WIDE )
479 sal_Int32 nMaxNumberOfColumns = nMaxEntryWidth
480 ? (rAvailableSpace.Width - 2*nXPadding ) / nMaxEntryWidth
481 : 0;
483 nNumberOfRows = nMaxNumberOfColumns
484 ? static_cast< sal_Int32 >(
485 ceil( static_cast< double >( nNumberOfEntries ) /
486 static_cast< double >( nMaxNumberOfColumns ) ))
487 : 0;
488 nNumberOfColumns = nNumberOfRows
489 ? static_cast< sal_Int32 >(
490 ceil( static_cast< double >( nNumberOfEntries ) /
491 static_cast< double >( nNumberOfRows ) ))
492 : 0;
494 else // ::com::sun::star::chart::ChartLegendExpansion_BALANCED
496 double fAspect = nMaxEntryHeight
497 ? static_cast< double >( nMaxEntryWidth ) / static_cast< double >( nMaxEntryHeight )
498 : 0.0;
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 ) ))
506 : 0;
509 if(nNumberOfRows<=0)
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;
526 sal_Int32 nRow = 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 )
535 break;
537 // text shape
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 ));
548 // symbol
549 Reference< drawing::XShape > xSymbol( rEntries[ nEntry ].aSymbol );
550 if( xSymbol.is() )
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;
571 else
573 nCurrentXPos -= aColumnWidths[nColumn];
574 if( nColumn+1 < nNumberOfColumns )
575 nCurrentXPos -= nXOffset;
579 if( !bIsCustomSize )
581 if( bSymbolsLeftSide )
582 aResultingLegendSize.Width = nCurrentXPos + nXPadding;
583 else
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;
627 switch( ePos )
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 );
637 break;
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 );
646 break;
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 );
656 break;
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 );
665 break;
667 case LegendPosition_CUSTOM:
668 // to avoid warning
669 case LegendPosition_MAKE_FIXED_SIZE:
670 // nothing to be set
671 break;
674 return aResult;
677 /** @return
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,
685 LegendPosition ePos,
686 const awt::Size& aLegendSize )
688 // calculate position
689 awt::Point aResult(
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();
700 switch( ePos )
702 case LegendPosition_LINE_START:
704 sal_Int32 nExtent = aLegendSize.Width;
705 rRemainingSpace.Width -= ( nExtent + nXDistance );
706 rRemainingSpace.X += ( nExtent + nXDistance );
708 break;
709 case LegendPosition_LINE_END:
711 rRemainingSpace.Width -= ( aLegendSize.Width + nXDistance );
713 break;
714 case LegendPosition_PAGE_START:
716 sal_Int32 nExtent = aLegendSize.Height;
717 rRemainingSpace.Height -= ( nExtent + nYDistance );
718 rRemainingSpace.Y += ( nExtent + nYDistance );
720 break;
721 case LegendPosition_PAGE_END:
723 rRemainingSpace.Height -= ( aLegendSize.Height + nYDistance );
725 break;
727 default:
728 // nothing
729 break;
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 )
738 aResult.X = nNewX;
740 if( aResult.Y + aLegendSize.Height > rPageSize.Height )
742 sal_Int32 nNewY( (rPageSize.Height - aLegendSize.Height) - nEdgeDistance );
743 if( nNewY > rPageSize.Height / 4 )
744 aResult.Y = nNewY;
747 return aResult;
750 bool lcl_shouldSymbolsBePlacedOnTheLeftSide( const Reference< beans::XPropertySet >& xLegendProp, sal_Int16 nDefaultWritingMode )
752 bool bSymbolsLeftSide = true;
755 if( SvtLanguageOptions().IsCTLFontEnabled() )
757 if(xLegendProp.is())
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
779 VLegend::VLegend(
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,
785 ChartModel& rModel )
786 : m_xTarget(xTargetPage)
787 , m_xShapeFactory(xFactory)
788 , m_xLegend(xLegend)
789 , mrModel(rModel)
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 )
803 if( ! xLegend.is())
804 return false;
806 bool bShow = false;
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 );
817 return bShow;
820 void VLegend::createShapes(
821 const awt::Size & rAvailableSpace,
822 const awt::Size & rPageSize )
824 if(! (m_xLegend.is() &&
825 m_xShapeFactory.is() &&
826 m_xTarget.is()))
827 return;
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 )),
836 uno::UNO_QUERY);
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 ));
862 else
863 eExpansion = ::com::sun::star::chart::ChartLegendExpansion_HIGH;
865 lcl_getProperties( xLegendProp, aLineFillProperties, aTextProperties, rPageSize );
868 // create entries
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() ) {
906 // place entries
907 aLegendSize = lcl_placeLegendEntries( aViewEntries, eExpansion, bSymbolsLeftSide, fViewFontSize, aMaxSymbolExtent,
908 aTextProperties, xLegendContainer, m_xShapeFactory, aLegendSize );
912 Reference< drawing::XShape > xBorder =
913 pShapeFactory->createRectangle( xLegendContainer,
914 aLegendSize,
915 awt::Point(0,0),
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 )
933 if(! m_xShape.is())
934 return;
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;
943 bool bAutoPosition =
944 ! (xLegendProp->getPropertyValue( "RelativePosition") >>= aRelativePosition);
946 LegendPosition ePos = LegendPosition_CUSTOM;
947 xLegendProp->getPropertyValue( "AnchorPosition") >>= ePos;
949 //calculate position
950 if( bAutoPosition )
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 );
958 else
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 );
981 } //namespace chart
983 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */