1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include "VLegend.hxx"
21 #include "VButton.hxx"
22 #include <PropertyMapper.hxx>
23 #include <ChartModel.hxx>
24 #include <ObjectIdentifier.hxx>
25 #include <RelativePositionHelper.hxx>
26 #include <ShapeFactory.hxx>
27 #include <RelativeSizeHelper.hxx>
28 #include <LegendEntryProvider.hxx>
29 #include <chartview/DrawModelWrapper.hxx>
30 #include <com/sun/star/text/WritingMode2.hpp>
31 #include <com/sun/star/beans/XPropertySet.hpp>
32 #include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
33 #include <com/sun/star/drawing/LineJoint.hpp>
34 #include <com/sun/star/drawing/XShapes.hpp>
35 #include <com/sun/star/chart/ChartLegendExpansion.hpp>
36 #include <com/sun/star/chart2/LegendPosition.hpp>
37 #include <com/sun/star/chart2/RelativePosition.hpp>
38 #include <com/sun/star/chart2/RelativeSize.hpp>
39 #include <com/sun/star/chart2/XFormattedString2.hpp>
40 #include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp>
41 #include <com/sun/star/chart2/data/PivotTableFieldEntry.hpp>
42 #include <svl/languageoptions.hxx>
43 #include <tools/diagnose_ex.h>
44 
45 #include <vector>
46 #include <algorithm>
47 
48 using namespace ::com::sun::star;
49 using namespace ::com::sun::star::chart2;
50 
51 using ::com::sun::star::uno::Reference;
52 using ::com::sun::star::uno::Sequence;
53 
54 namespace chart
55 {
56 
57 namespace
58 {
59 
60 typedef std::pair< ::chart::tNameSequence, ::chart::tAnySequence > tPropertyValues;
61 
lcl_CalcViewFontSize(const Reference<beans::XPropertySet> & xProp,const awt::Size & rReferenceSize)62 double lcl_CalcViewFontSize(
63     const Reference< beans::XPropertySet > & xProp,
64     const awt::Size & rReferenceSize )
65 {
66     double fResult = 10.0;
67 
68     awt::Size aPropRefSize;
69     float fFontHeight( 0.0 );
70     if( xProp.is() && ( xProp->getPropertyValue( "CharHeight") >>= fFontHeight ))
71     {
72         fResult = fFontHeight;
73         try
74         {
75             if( (xProp->getPropertyValue( "ReferencePageSize") >>= aPropRefSize) &&
76                 (aPropRefSize.Height > 0))
77             {
78                 fResult = ::chart::RelativeSizeHelper::calculate( fFontHeight, aPropRefSize, rReferenceSize );
79             }
80         }
81         catch( const uno::Exception & )
82         {
83             DBG_UNHANDLED_EXCEPTION("chart2");
84         }
85     }
86 
87     // pt -> 1/100th mm
88     return (fResult * (2540.0 / 72.0));
89 }
90 
lcl_getProperties(const Reference<beans::XPropertySet> & xLegendProp,tPropertyValues & rOutLineFillProperties,tPropertyValues & rOutTextProperties,const awt::Size & rReferenceSize)91 void lcl_getProperties(
92     const Reference< beans::XPropertySet > & xLegendProp,
93     tPropertyValues & rOutLineFillProperties,
94     tPropertyValues & rOutTextProperties,
95     const awt::Size & rReferenceSize )
96 {
97     // Get Line- and FillProperties from model legend
98     if( xLegendProp.is())
99     {
100         // set rOutLineFillProperties
101         ::chart::tPropertyNameValueMap aLineFillValueMap;
102         ::chart::PropertyMapper::getValueMap( aLineFillValueMap, ::chart::PropertyMapper::getPropertyNameMapForFillAndLineProperties(), xLegendProp );
103 
104         aLineFillValueMap[ "LineJoint" ] <<= drawing::LineJoint_ROUND;
105 
106         ::chart::PropertyMapper::getMultiPropertyListsFromValueMap(
107             rOutLineFillProperties.first, rOutLineFillProperties.second, aLineFillValueMap );
108 
109         // set rOutTextProperties
110         ::chart::tPropertyNameValueMap aTextValueMap;
111         ::chart::PropertyMapper::getValueMap( aTextValueMap, ::chart::PropertyMapper::getPropertyNameMapForCharacterProperties(), xLegendProp );
112 
113         aTextValueMap[ "TextAutoGrowHeight" ] <<= true;
114         aTextValueMap[ "TextAutoGrowWidth" ] <<= true;
115         aTextValueMap[ "TextHorizontalAdjust" ] <<= drawing::TextHorizontalAdjust_LEFT;
116         aTextValueMap[ "TextMaximumFrameWidth" ] <<= rReferenceSize.Width; //needs to be overwritten by actual available space in the legend
117 
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) )
124         {
125             aTextValueMap[ "CharHeight" ] <<=
126                 static_cast< float >(
127                     ::chart::RelativeSizeHelper::calculate( fFontHeight, aPropRefSize, rReferenceSize ));
128 
129             if( aTextValueMap[ "CharHeightAsian" ] >>= fFontHeight )
130             {
131                 aTextValueMap[ "CharHeightAsian" ] <<=
132                     static_cast< float >(
133                         ::chart::RelativeSizeHelper::calculate( fFontHeight, aPropRefSize, rReferenceSize ));
134             }
135             if( aTextValueMap[ "CharHeightComplex" ] >>= fFontHeight )
136             {
137                 aTextValueMap[ "CharHeightComplex" ] <<=
138                     static_cast< float >(
139                         ::chart::RelativeSizeHelper::calculate( fFontHeight, aPropRefSize, rReferenceSize ));
140             }
141         }
142 
143         ::chart::PropertyMapper::getMultiPropertyListsFromValueMap(
144             rOutTextProperties.first, rOutTextProperties.second, aTextValueMap );
145     }
146 }
147 
lcl_createTextShapes(const std::vector<ViewLegendEntry> & rEntries,const Reference<lang::XMultiServiceFactory> & xShapeFactory,const Reference<drawing::XShapes> & xTarget,std::vector<Reference<drawing::XShape>> & rOutTextShapes,const tPropertyValues & rTextProperties)148 awt::Size lcl_createTextShapes(
149     const std::vector<ViewLegendEntry> & rEntries,
150     const Reference< lang::XMultiServiceFactory > & xShapeFactory,
151     const Reference< drawing::XShapes > & xTarget,
152     std::vector< Reference< drawing::XShape > > & rOutTextShapes,
153     const tPropertyValues & rTextProperties )
154 {
155     awt::Size aResult;
156     ShapeFactory* pShapeFactory = ShapeFactory::getOrCreateShapeFactory(xShapeFactory);
157 
158     for (ViewLegendEntry const & rEntry : rEntries)
159     {
160         try
161         {
162             OUString aLabelString;
163             Sequence< Reference< XFormattedString2 > > aLabelSeq = rEntry.aLabel;
164             for( sal_Int32 i = 0; i < aLabelSeq.getLength(); ++i )
165             {
166                 // todo: support more than one text range
167                 if( i == 1 )
168                     break;
169 
170                 aLabelString += aLabelSeq[i]->getString();
171                 // workaround for Issue #i67540#
172                 if( aLabelString.isEmpty())
173                     aLabelString = " ";
174             }
175 
176             Reference< drawing::XShape > xEntry =
177                 pShapeFactory->createText( xTarget, aLabelString,
178                         rTextProperties.first, rTextProperties.second, uno::Any() );
179 
180             // adapt max-extent
181             awt::Size aCurrSize( xEntry->getSize());
182             aResult.Width  = std::max( aResult.Width,  aCurrSize.Width  );
183             aResult.Height = std::max( aResult.Height, aCurrSize.Height );
184 
185             rOutTextShapes.push_back( xEntry );
186         }
187         catch( const uno::Exception & )
188         {
189             DBG_UNHANDLED_EXCEPTION("chart2");
190         }
191     }
192 
193     return aResult;
194 }
195 
lcl_collectColumnWidths(std::vector<sal_Int32> & rColumnWidths,const sal_Int32 nNumberOfRows,const sal_Int32 nNumberOfColumns,const std::vector<Reference<drawing::XShape>> & rTextShapes,sal_Int32 nSymbolPlusDistanceWidth)196 void lcl_collectColumnWidths( std::vector< sal_Int32 >& rColumnWidths, const sal_Int32 nNumberOfRows, const sal_Int32 nNumberOfColumns,
197                               const std::vector< Reference< drawing::XShape > >& rTextShapes, sal_Int32 nSymbolPlusDistanceWidth )
198 {
199     rColumnWidths.clear();
200     sal_Int32 nNumberOfEntries = rTextShapes.size();
201     for (sal_Int32 nRow = 0; nRow < nNumberOfRows; ++nRow )
202     {
203         for (sal_Int32 nColumn = 0; nColumn < nNumberOfColumns; ++nColumn )
204         {
205             sal_Int32 nEntry = nColumn + nRow * nNumberOfColumns;
206             if( nEntry < nNumberOfEntries )
207             {
208                 awt::Size aTextSize( rTextShapes[ nEntry ]->getSize() );
209                 sal_Int32 nWidth = nSymbolPlusDistanceWidth + aTextSize.Width;
210                 if( nRow==0 )
211                     rColumnWidths.push_back( nWidth );
212                 else
213                     rColumnWidths[nColumn] = std::max( nWidth, rColumnWidths[nColumn] );
214             }
215         }
216     }
217 }
218 
lcl_collectRowHeighs(std::vector<sal_Int32> & rRowHeights,const sal_Int32 nNumberOfRows,const sal_Int32 nNumberOfColumns,const std::vector<Reference<drawing::XShape>> & rTextShapes)219 void lcl_collectRowHeighs( std::vector< sal_Int32 >& rRowHeights, const sal_Int32 nNumberOfRows, const sal_Int32 nNumberOfColumns,
220                            const std::vector< Reference< drawing::XShape > >& rTextShapes )
221 {
222     // calculate maximum height for each row
223     // and collect column widths
224     rRowHeights.clear();
225     sal_Int32 nNumberOfEntries = rTextShapes.size();
226     for (sal_Int32 nRow = 0; nRow < nNumberOfRows; ++nRow)
227     {
228         sal_Int32 nCurrentRowHeight = 0;
229         for (sal_Int32 nColumn = 0; nColumn < nNumberOfColumns; ++nColumn)
230         {
231             sal_Int32 nEntry = nColumn + nRow * nNumberOfColumns;
232             if( nEntry < nNumberOfEntries )
233             {
234                 awt::Size aTextSize( rTextShapes[ nEntry ]->getSize() );
235                 nCurrentRowHeight = std::max( nCurrentRowHeight, aTextSize.Height );
236             }
237         }
238         rRowHeights.push_back( nCurrentRowHeight );
239     }
240 }
241 
lcl_getTextLineHeight(const std::vector<sal_Int32> & aRowHeights,const sal_Int32 nNumberOfRows,double fViewFontSize)242 sal_Int32 lcl_getTextLineHeight( const std::vector< sal_Int32 >& aRowHeights, const sal_Int32 nNumberOfRows, double fViewFontSize )
243 {
244     const sal_Int32 nFontHeight = static_cast< sal_Int32 >( fViewFontSize );
245     if (!nFontHeight)
246         return 0;
247     sal_Int32 nTextLineHeight = nFontHeight;
248     for (sal_Int32 nRow = 0; nRow < nNumberOfRows; ++nRow)
249     {
250         sal_Int32 nFullTextHeight = aRowHeights[nRow];
251         if( ( nFullTextHeight / nFontHeight ) <= 1 )
252         {
253             nTextLineHeight = nFullTextHeight;//found an entry with one line-> have real text height
254             break;
255         }
256     }
257     return nTextLineHeight;
258 }
259 
260 //returns resulting legend size
lcl_placeLegendEntries(std::vector<ViewLegendEntry> & rEntries,css::chart::ChartLegendExpansion eExpansion,bool bSymbolsLeftSide,double fViewFontSize,const awt::Size & rMaxSymbolExtent,tPropertyValues & rTextProperties,const Reference<drawing::XShapes> & xTarget,const Reference<lang::XMultiServiceFactory> & xShapeFactory,const awt::Size & rRemainingSpace,sal_Int32 nYStartPosition,const awt::Size & rPageSize,bool bIsPivotChart,awt::Size & rDefaultLegendSize)261 awt::Size lcl_placeLegendEntries(
262     std::vector<ViewLegendEntry> & rEntries,
263     css::chart::ChartLegendExpansion eExpansion,
264     bool bSymbolsLeftSide,
265     double fViewFontSize,
266     const awt::Size& rMaxSymbolExtent,
267     tPropertyValues & rTextProperties,
268     const Reference< drawing::XShapes > & xTarget,
269     const Reference< lang::XMultiServiceFactory > & xShapeFactory,
270     const awt::Size& rRemainingSpace,
271     sal_Int32 nYStartPosition,
272     const awt::Size& rPageSize,
273     bool bIsPivotChart,
274     awt::Size& rDefaultLegendSize)
275 {
276     bool bIsCustomSize = (eExpansion == css::chart::ChartLegendExpansion_CUSTOM);
277     awt::Size aResultingLegendSize(0,0);
278     // For Pivot charts set the *minimum* legend size as a function of page size.
279     if ( bIsPivotChart )
280         aResultingLegendSize = awt::Size((rPageSize.Width * 13) / 80, (rPageSize.Height * 31) / 90);
281     if( bIsCustomSize )
282         aResultingLegendSize = awt::Size(rRemainingSpace.Width, rRemainingSpace.Height + nYStartPosition);
283 
284     // #i109336# Improve auto positioning in chart
285     sal_Int32 nXPadding = static_cast< sal_Int32 >( std::max( 100.0, fViewFontSize * 0.33 ) );
286     sal_Int32 nXOffset  = static_cast< sal_Int32 >( std::max( 100.0, fViewFontSize * 0.66 ) );
287     sal_Int32 nYPadding = static_cast< sal_Int32 >( std::max( 100.0, fViewFontSize * 0.2 ) );
288     sal_Int32 nYOffset  = static_cast< sal_Int32 >( std::max( 100.0, fViewFontSize * 0.2 ) );
289 
290     const sal_Int32 nSymbolToTextDistance = static_cast< sal_Int32 >( std::max( 100.0, fViewFontSize * 0.22 ) );//minimum 1mm
291     const sal_Int32 nSymbolPlusDistanceWidth = rMaxSymbolExtent.Width + nSymbolToTextDistance;
292     sal_Int32 nMaxTextWidth = rRemainingSpace.Width - (2 * nXPadding) - nSymbolPlusDistanceWidth;
293     uno::Any* pFrameWidthAny = PropertyMapper::getValuePointer( rTextProperties.second, rTextProperties.first, "TextMaximumFrameWidth");
294     if(pFrameWidthAny)
295     {
296         if( eExpansion == css::chart::ChartLegendExpansion_HIGH )
297         {
298             // limit the width of texts to 30% of the total available width
299             // #i109336# Improve auto positioning in chart
300             nMaxTextWidth = rRemainingSpace.Width * 3 / 10;
301         }
302         *pFrameWidthAny <<= nMaxTextWidth;
303     }
304 
305     std::vector< Reference< drawing::XShape > > aTextShapes;
306     awt::Size aMaxEntryExtent = lcl_createTextShapes( rEntries, xShapeFactory, xTarget, aTextShapes, rTextProperties );
307     OSL_ASSERT( aTextShapes.size() == rEntries.size());
308 
309     sal_Int32 nMaxEntryWidth = nXOffset + nSymbolPlusDistanceWidth + aMaxEntryExtent.Width;
310     sal_Int32 nMaxEntryHeight = nYOffset + aMaxEntryExtent.Height;
311     sal_Int32 nNumberOfEntries = rEntries.size();
312 
313     rDefaultLegendSize.Width = nMaxEntryWidth;
314     rDefaultLegendSize.Height = nMaxEntryHeight + nYPadding;
315 
316     sal_Int32 nNumberOfColumns = 0, nNumberOfRows = 0;
317     std::vector< sal_Int32 > aColumnWidths;
318     std::vector< sal_Int32 > aRowHeights;
319 
320     sal_Int32 nTextLineHeight = static_cast< sal_Int32 >( fViewFontSize );
321 
322     // determine layout depending on LegendExpansion
323     if( eExpansion == css::chart::ChartLegendExpansion_CUSTOM )
324     {
325         sal_Int32 nCurrentRow=0;
326         sal_Int32 nCurrentColumn=-1;
327         sal_Int32 nMaxColumnCount=-1;
328         for( sal_Int32 nN=0; nN<static_cast<sal_Int32>(aTextShapes.size()); nN++ )
329         {
330             Reference< drawing::XShape > xShape( aTextShapes[nN] );
331             if( !xShape.is() )
332                 continue;
333             awt::Size aSize( xShape->getSize() );
334             sal_Int32 nNewWidth = aSize.Width + nSymbolPlusDistanceWidth;
335             sal_Int32 nCurrentColumnCount = aColumnWidths.size();
336 
337             //are we allowed to add a new column?
338             if( nMaxColumnCount==-1 || (nCurrentColumn+1) < nMaxColumnCount )
339             {
340                 //try add a new column
341                 nCurrentColumn++;
342                 if( nCurrentColumn < nCurrentColumnCount )
343                 {
344                     //check whether the current column width is sufficient for the new entry
345                     if( aColumnWidths[nCurrentColumn]>=nNewWidth )
346                     {
347                         //all good proceed with next entry
348                         continue;
349                     }
350                 }
351                 if( nCurrentColumn < nCurrentColumnCount )
352                     aColumnWidths[nCurrentColumn] = std::max( nNewWidth, aColumnWidths[nCurrentColumn] );
353                 else
354                     aColumnWidths.push_back(nNewWidth);
355 
356                 //do the columns still fit into the given size?
357                 nCurrentColumnCount = aColumnWidths.size();//update count
358                 sal_Int32 nSumWidth = 0;
359                 for (sal_Int32 nColumn = 0; nColumn < nCurrentColumnCount; nColumn++)
360                     nSumWidth += aColumnWidths[nColumn];
361 
362                 if( nSumWidth <= rRemainingSpace.Width || nCurrentColumnCount==1 )
363                 {
364                     //all good proceed with next entry
365                     continue;
366                 }
367                 else
368                 {
369                     //not enough space for the current amount of columns
370                     //try again with less columns
371                     nMaxColumnCount = nCurrentColumnCount-1;
372                     nN=-1;
373                     nCurrentRow=0;
374                     nCurrentColumn=-1;
375                     aColumnWidths.clear();
376                 }
377             }
378             else
379             {
380                 //add a new row and try the same entry again
381                 nCurrentRow++;
382                 nCurrentColumn=-1;
383                 nN--;
384             }
385         }
386         nNumberOfColumns = aColumnWidths.size();
387         nNumberOfRows = nCurrentRow+1;
388 
389         //check if there is not enough space so that some entries must be removed
390         lcl_collectRowHeighs( aRowHeights, nNumberOfRows, nNumberOfColumns, aTextShapes );
391         nTextLineHeight = lcl_getTextLineHeight( aRowHeights, nNumberOfRows, fViewFontSize );
392         sal_Int32 nSumHeight = 0;
393         for (sal_Int32 nRow=0; nRow < nNumberOfRows; nRow++)
394             nSumHeight += aRowHeights[nRow];
395         sal_Int32 nRemainingSpace = rRemainingSpace.Height - nSumHeight;
396 
397         if( nRemainingSpace < -100 ) // 1mm tolerance for OOXML interop tdf#90404
398         {
399             //remove entries that are too big
400             for (sal_Int32 nRow = nNumberOfRows; nRow--; )
401             {
402                 for (sal_Int32 nColumn = nNumberOfColumns; nColumn--; )
403                 {
404                     sal_Int32 nEntry = nColumn + nRow * nNumberOfColumns;
405                     if( nEntry < static_cast<sal_Int32>(aTextShapes.size()) )
406                     {
407                         DrawModelWrapper::removeShape( aTextShapes[nEntry] );
408                         aTextShapes.pop_back();
409                     }
410                     if( nEntry < nNumberOfEntries )
411                     {
412                         DrawModelWrapper::removeShape( rEntries[ nEntry ].aSymbol );
413                         rEntries.pop_back();
414                         nNumberOfEntries--;
415                     }
416                 }
417                 nSumHeight -= aRowHeights[nRow];
418                 aRowHeights.pop_back();
419                 nRemainingSpace = rRemainingSpace.Height - nSumHeight;
420                 if( nRemainingSpace>=0 )
421                     break;
422             }
423             nNumberOfRows = static_cast<sal_Int32>(aRowHeights.size());
424         }
425         if( nRemainingSpace >= -100 ) // 1mm tolerance for OOXML interop tdf#90404
426         {
427             sal_Int32 nNormalSpacingHeight = 2*nYPadding+(nNumberOfRows-1)*nYOffset;
428             if( nRemainingSpace < nNormalSpacingHeight )
429             {
430                 //reduce spacing between the entries
431                 nYPadding = nYOffset = nRemainingSpace/(nNumberOfRows+1);
432             }
433             else
434             {
435                 //we have some space left that should be spread equally between all rows
436                 sal_Int32 nRemainingSingleSpace = (nRemainingSpace-nNormalSpacingHeight)/(nNumberOfRows+1);
437                 nYPadding += nRemainingSingleSpace;
438                 nYOffset += nRemainingSingleSpace;
439             }
440         }
441 
442         //check spacing between columns
443         sal_Int32 nSumWidth = 0;
444         for (sal_Int32 nColumn = 0; nColumn < nNumberOfColumns; nColumn++)
445             nSumWidth += aColumnWidths[nColumn];
446         nRemainingSpace = rRemainingSpace.Width - nSumWidth;
447         if( nRemainingSpace>=0 )
448         {
449             sal_Int32 nNormalSpacingWidth = 2*nXPadding+(nNumberOfColumns-1)*nXOffset;
450             if( nRemainingSpace < nNormalSpacingWidth )
451             {
452                 //reduce spacing between the entries
453                 nXPadding = nXOffset = nRemainingSpace/(nNumberOfColumns+1);
454             }
455             else
456             {
457                 //we have some space left that should be spread equally between all columns
458                 sal_Int32 nRemainingSingleSpace = (nRemainingSpace-nNormalSpacingWidth)/(nNumberOfColumns+1);
459                 nXPadding += nRemainingSingleSpace;
460                 nXOffset += nRemainingSingleSpace;
461             }
462         }
463     }
464     else if( eExpansion == css::chart::ChartLegendExpansion_HIGH )
465     {
466         sal_Int32 nMaxNumberOfRows = nMaxEntryHeight
467             ? (rRemainingSpace.Height - 2*nYPadding ) / nMaxEntryHeight
468             : 0;
469 
470         nNumberOfColumns = nMaxNumberOfRows
471             ? static_cast< sal_Int32 >(
472                 ceil( static_cast< double >( nNumberOfEntries ) /
473                       static_cast< double >( nMaxNumberOfRows ) ))
474             : 0;
475         nNumberOfRows =  nNumberOfColumns
476             ? static_cast< sal_Int32 >(
477                 ceil( static_cast< double >( nNumberOfEntries ) /
478                       static_cast< double >( nNumberOfColumns ) ))
479             : 0;
480     }
481     else if( eExpansion == css::chart::ChartLegendExpansion_WIDE )
482     {
483         sal_Int32 nMaxNumberOfColumns = nMaxEntryWidth
484             ? (rRemainingSpace.Width - 2*nXPadding ) / nMaxEntryWidth
485             : 0;
486 
487         nNumberOfRows = nMaxNumberOfColumns
488             ? static_cast< sal_Int32 >(
489                 ceil( static_cast< double >( nNumberOfEntries ) /
490                       static_cast< double >( nMaxNumberOfColumns ) ))
491             : 0;
492         nNumberOfColumns = nNumberOfRows
493             ? static_cast< sal_Int32 >(
494                 ceil( static_cast< double >( nNumberOfEntries ) /
495                       static_cast< double >( nNumberOfRows ) ))
496             : 0;
497     }
498     else // css::chart::ChartLegendExpansion_BALANCED
499     {
500         double fAspect = nMaxEntryHeight
501             ? static_cast< double >( nMaxEntryWidth ) / static_cast< double >( nMaxEntryHeight )
502             : 0.0;
503 
504         nNumberOfRows = static_cast< sal_Int32 >(
505             ceil( sqrt( static_cast< double >( nNumberOfEntries ) * fAspect )));
506         nNumberOfColumns = nNumberOfRows
507             ? static_cast< sal_Int32 >(
508                 ceil( static_cast< double >( nNumberOfEntries ) /
509                       static_cast< double >( nNumberOfRows ) ))
510             : 0;
511     }
512 
513     if(nNumberOfRows<=0)
514         return aResultingLegendSize;
515 
516     if( eExpansion != css::chart::ChartLegendExpansion_CUSTOM )
517     {
518         lcl_collectColumnWidths( aColumnWidths, nNumberOfRows, nNumberOfColumns, aTextShapes, nSymbolPlusDistanceWidth );
519         lcl_collectRowHeighs( aRowHeights, nNumberOfRows, nNumberOfColumns, aTextShapes );
520         nTextLineHeight = lcl_getTextLineHeight( aRowHeights, nNumberOfRows, fViewFontSize );
521     }
522 
523     sal_Int32 nCurrentXPos = bSymbolsLeftSide ? nXPadding : -nXPadding;
524 
525     // place entries into column and rows
526     sal_Int32 nMaxYPos = 0;
527 
528     for (sal_Int32 nColumn = 0; nColumn < nNumberOfColumns; ++nColumn)
529     {
530         sal_Int32 nCurrentYPos = nYPadding + nYStartPosition;
531         for (sal_Int32 nRow = 0; nRow < nNumberOfRows; ++nRow)
532         {
533             sal_Int32 nEntry = nColumn + nRow * nNumberOfColumns;
534             if( nEntry >= nNumberOfEntries )
535                 break;
536 
537             // text shape
538             Reference< drawing::XShape > xTextShape( aTextShapes[nEntry] );
539             if( xTextShape.is() )
540             {
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 ));
546             }
547 
548             // symbol
549             Reference< drawing::XShape > xSymbol( rEntries[ nEntry ].aSymbol );
550             if( xSymbol.is() )
551             {
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 ) );
558             }
559 
560             nCurrentYPos += aRowHeights[ nRow ];
561             if( nRow+1 < nNumberOfRows )
562                 nCurrentYPos += nYOffset;
563             nMaxYPos = std::max( nMaxYPos, nCurrentYPos );
564         }
565         if( bSymbolsLeftSide )
566         {
567             nCurrentXPos += aColumnWidths[nColumn];
568             if( nColumn+1 < nNumberOfColumns )
569                 nCurrentXPos += nXOffset;
570         }
571         else
572         {
573             nCurrentXPos -= aColumnWidths[nColumn];
574             if( nColumn+1 < nNumberOfColumns )
575                 nCurrentXPos -= nXOffset;
576         }
577     }
578 
579     if( !bIsCustomSize )
580     {
581         if( bSymbolsLeftSide )
582             aResultingLegendSize.Width  = std::max( aResultingLegendSize.Width, nCurrentXPos + nXPadding );
583         else
584         {
585             sal_Int32 nLegendWidth = -(nCurrentXPos-nXPadding);
586             aResultingLegendSize.Width  = std::max( aResultingLegendSize.Width, nLegendWidth );
587         }
588         aResultingLegendSize.Height = std::max( aResultingLegendSize.Height, nMaxYPos + nYPadding );
589     }
590 
591     if( !bSymbolsLeftSide )
592     {
593         sal_Int32 nLegendWidth = aResultingLegendSize.Width;
594         awt::Point aPos(0,0);
595         for( sal_Int32 nEntry=0; nEntry<nNumberOfEntries; nEntry++ )
596         {
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 );
605         }
606     }
607 
608     return aResultingLegendSize;
609 }
610 
611 // #i109336# Improve auto positioning in chart
lcl_getLegendLeftRightMargin()612 sal_Int32 lcl_getLegendLeftRightMargin()
613 {
614     return 210;  // 1/100 mm
615 }
616 
617 // #i109336# Improve auto positioning in chart
lcl_getLegendTopBottomMargin()618 sal_Int32 lcl_getLegendTopBottomMargin()
619 {
620     return 185;  // 1/100 mm
621 }
622 
lcl_getDefaultPosition(LegendPosition ePos,const awt::Rectangle & rOutAvailableSpace,const awt::Size & rPageSize)623 chart2::RelativePosition lcl_getDefaultPosition( LegendPosition ePos, const awt::Rectangle& rOutAvailableSpace, const awt::Size & rPageSize )
624 {
625     chart2::RelativePosition aResult;
626 
627     switch( ePos )
628     {
629         case LegendPosition_LINE_START:
630             {
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 );
636             }
637             break;
638         case LegendPosition_LINE_END:
639             {
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 );
645             }
646             break;
647         case LegendPosition_PAGE_START:
648             {
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 );
655             }
656             break;
657         case LegendPosition_PAGE_END:
658             {
659                 // #i109336# Improve auto positioning in chart
660                 const double fDefaultDistance = static_cast< double >( lcl_getLegendTopBottomMargin() ) /
661                     static_cast< double >( rPageSize.Height );
662 
663                 double fDistance = double(rPageSize.Height - (rOutAvailableSpace.Y + rOutAvailableSpace.Height));
664                 fDistance += fDefaultDistance;
665                 fDistance /= double(rPageSize.Height);
666 
667                 aResult = chart2::RelativePosition(
668                     0.5, 1.0 - fDistance, drawing::Alignment_BOTTOM );
669             }
670             break;
671 
672         case LegendPosition_CUSTOM:
673             // to avoid warning
674         case LegendPosition::LegendPosition_MAKE_FIXED_SIZE:
675             // nothing to be set
676             break;
677     }
678 
679     return aResult;
680 }
681 
682 /**  @return
683          a point relative to the upper left corner that can be used for
684          XShape::setPosition()
685 */
lcl_calculatePositionAndRemainingSpace(awt::Rectangle & rRemainingSpace,const awt::Size & rPageSize,const chart2::RelativePosition & rRelPos,LegendPosition ePos,const awt::Size & aLegendSize)686 awt::Point lcl_calculatePositionAndRemainingSpace(
687     awt::Rectangle & rRemainingSpace,
688     const awt::Size & rPageSize,
689     const chart2::RelativePosition& rRelPos,
690     LegendPosition ePos,
691     const awt::Size& aLegendSize )
692 {
693     // calculate position
694     awt::Point aResult(
695         static_cast< sal_Int32 >( rRelPos.Primary * rPageSize.Width ),
696         static_cast< sal_Int32 >( rRelPos.Secondary * rPageSize.Height ));
697 
698     aResult = RelativePositionHelper::getUpperLeftCornerOfAnchoredObject(
699         aResult, aLegendSize, rRelPos.Anchor );
700 
701     // adapt rRemainingSpace if LegendPosition is not CUSTOM
702     // #i109336# Improve auto positioning in chart
703     sal_Int32 nXDistance = lcl_getLegendLeftRightMargin();
704     sal_Int32 nYDistance = lcl_getLegendTopBottomMargin();
705     switch( ePos )
706     {
707         case LegendPosition_LINE_START:
708         {
709             sal_Int32 nExtent = aLegendSize.Width;
710             rRemainingSpace.Width -= ( nExtent + nXDistance );
711             rRemainingSpace.X += ( nExtent + nXDistance );
712         }
713         break;
714         case LegendPosition_LINE_END:
715         {
716             rRemainingSpace.Width -= ( aLegendSize.Width + nXDistance );
717         }
718         break;
719         case LegendPosition_PAGE_START:
720         {
721             sal_Int32 nExtent = aLegendSize.Height;
722             rRemainingSpace.Height -= ( nExtent + nYDistance );
723             rRemainingSpace.Y += ( nExtent + nYDistance );
724         }
725         break;
726         case LegendPosition_PAGE_END:
727         {
728             rRemainingSpace.Height -= ( aLegendSize.Height + nYDistance );
729         }
730         break;
731 
732         default:
733             // nothing
734             break;
735     }
736 
737     // adjust the legend position. Esp. for old files that had slightly smaller legends
738     const sal_Int32 nEdgeDistance( 30 );
739     if( aResult.X + aLegendSize.Width > rPageSize.Width )
740     {
741         sal_Int32 nNewX( (rPageSize.Width - aLegendSize.Width) - nEdgeDistance );
742         if( nNewX > rPageSize.Width / 4 )
743             aResult.X = nNewX;
744     }
745     if( aResult.Y + aLegendSize.Height > rPageSize.Height )
746     {
747         sal_Int32 nNewY( (rPageSize.Height - aLegendSize.Height) - nEdgeDistance );
748         if( nNewY > rPageSize.Height / 4 )
749             aResult.Y = nNewY;
750     }
751 
752     return aResult;
753 }
754 
lcl_shouldSymbolsBePlacedOnTheLeftSide(const Reference<beans::XPropertySet> & xLegendProp,sal_Int16 nDefaultWritingMode)755 bool lcl_shouldSymbolsBePlacedOnTheLeftSide( const Reference< beans::XPropertySet >& xLegendProp, sal_Int16 nDefaultWritingMode )
756 {
757     bool bSymbolsLeftSide = true;
758     try
759     {
760         if( SvtLanguageOptions().IsCTLFontEnabled() )
761         {
762             if(xLegendProp.is())
763             {
764                 sal_Int16 nWritingMode=-1;
765                 if( xLegendProp->getPropertyValue( "WritingMode" ) >>= nWritingMode )
766                 {
767                     if( nWritingMode == text::WritingMode2::PAGE )
768                         nWritingMode = nDefaultWritingMode;
769                     if( nWritingMode == text::WritingMode2::RL_TB )
770                         bSymbolsLeftSide=false;
771                 }
772             }
773         }
774     }
775     catch( const uno::Exception & )
776     {
777         DBG_UNHANDLED_EXCEPTION("chart2");
778     }
779     return bSymbolsLeftSide;
780 }
781 
lcl_createButtons(uno::Reference<drawing::XShapes> const & xLegendContainer,uno::Reference<lang::XMultiServiceFactory> const & xShapeFactory,ChartModel & rModel,bool bPlaceButtonsVertically,long & nUsedHeight)782 std::vector<std::shared_ptr<VButton>> lcl_createButtons(
783                        uno::Reference<drawing::XShapes> const & xLegendContainer,
784                        uno::Reference<lang::XMultiServiceFactory> const & xShapeFactory,
785                        ChartModel& rModel, bool bPlaceButtonsVertically, long & nUsedHeight)
786 {
787     std::vector<std::shared_ptr<VButton>> aButtons;
788 
789     uno::Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider(rModel.getDataProvider(), uno::UNO_QUERY);
790     if (!xPivotTableDataProvider.is())
791         return aButtons;
792 
793     if (!xPivotTableDataProvider->getColumnFields().hasElements())
794         return aButtons;
795 
796     awt::Size aSize(2000, 700);
797     int x = 100;
798     int y = 100;
799 
800     const css::uno::Sequence<chart2::data::PivotTableFieldEntry> aPivotFieldEntries = xPivotTableDataProvider->getColumnFields();
801     for (chart2::data::PivotTableFieldEntry const & sColumnFieldEntry : aPivotFieldEntries)
802     {
803         std::shared_ptr<VButton> pButton(new VButton);
804         aButtons.push_back(pButton);
805         pButton->init(xLegendContainer, xShapeFactory);
806         awt::Point aNewPosition(x, y);
807         pButton->setLabel(sColumnFieldEntry.Name);
808         pButton->setCID("FieldButton.Column." + OUString::number(sColumnFieldEntry.DimensionIndex));
809         pButton->setPosition(aNewPosition);
810         pButton->setSize(aSize);
811         if (sColumnFieldEntry.Name == "Data")
812         {
813             pButton->showArrow(false);
814             pButton->setBGColor(Color(0x00F6F6F6));
815         }
816         if (sColumnFieldEntry.HasHiddenMembers)
817             pButton->setArrowColor(Color(0x0000FF));
818 
819         if (bPlaceButtonsVertically)
820             y += aSize.Height + 100;
821         else
822             x += aSize.Width + 100;
823     }
824     if (bPlaceButtonsVertically)
825         nUsedHeight += y + 100;
826     else
827         nUsedHeight += aSize.Height + 100;
828 
829     return aButtons;
830 }
831 
832 } // anonymous namespace
833 
VLegend(const Reference<XLegend> & xLegend,const Reference<uno::XComponentContext> & xContext,const std::vector<LegendEntryProvider * > & rLegendEntryProviderList,const Reference<drawing::XShapes> & xTargetPage,const Reference<lang::XMultiServiceFactory> & xFactory,ChartModel & rModel)834 VLegend::VLegend(
835     const Reference< XLegend > & xLegend,
836     const Reference< uno::XComponentContext > & xContext,
837     const std::vector< LegendEntryProvider* >& rLegendEntryProviderList,
838     const Reference< drawing::XShapes >& xTargetPage,
839     const Reference< lang::XMultiServiceFactory >& xFactory,
840     ChartModel& rModel )
841         : m_xTarget(xTargetPage)
842         , m_xShapeFactory(xFactory)
843         , m_xLegend(xLegend)
844         , mrModel(rModel)
845         , m_xContext(xContext)
846         , m_aLegendEntryProviderList(rLegendEntryProviderList)
847         , m_nDefaultWritingMode(text::WritingMode2::LR_TB)
848 {
849 }
850 
setDefaultWritingMode(sal_Int16 nDefaultWritingMode)851 void VLegend::setDefaultWritingMode( sal_Int16 nDefaultWritingMode )
852 {
853     m_nDefaultWritingMode = nDefaultWritingMode;
854 }
855 
isVisible(const Reference<XLegend> & xLegend)856 bool VLegend::isVisible( const Reference< XLegend > & xLegend )
857 {
858     if( ! xLegend.is())
859         return false;
860 
861     bool bShow = false;
862     try
863     {
864         Reference< beans::XPropertySet > xLegendProp( xLegend, uno::UNO_QUERY_THROW );
865         xLegendProp->getPropertyValue( "Show") >>= bShow;
866     }
867     catch( const uno::Exception & )
868     {
869         DBG_UNHANDLED_EXCEPTION("chart2");
870     }
871 
872     return bShow;
873 }
874 
createShapes(const awt::Size & rAvailableSpace,const awt::Size & rPageSize,awt::Size & rDefaultLegendSize)875 void VLegend::createShapes(
876     const awt::Size & rAvailableSpace,
877     const awt::Size & rPageSize,
878     awt::Size & rDefaultLegendSize )
879 {
880     if(! (m_xLegend.is() &&
881           m_xShapeFactory.is() &&
882           m_xTarget.is()))
883         return;
884 
885     try
886     {
887         //create shape and add to page
888         ShapeFactory* pShapeFactory = ShapeFactory::getOrCreateShapeFactory(m_xShapeFactory);
889         OUString aLegendParticle( ObjectIdentifier::createParticleForLegend( mrModel ) );
890         m_xShape.set( pShapeFactory->createGroup2D( m_xTarget,
891                     ObjectIdentifier::createClassifiedIdentifierForParticle( aLegendParticle )),
892                 uno::UNO_QUERY);
893 
894         // create and insert sub-shapes
895         Reference< drawing::XShapes > xLegendContainer( m_xShape, uno::UNO_QUERY );
896         if( xLegendContainer.is())
897         {
898             // for quickly setting properties
899             tPropertyValues aLineFillProperties;
900             tPropertyValues aTextProperties;
901 
902             Reference< beans::XPropertySet > xLegendProp( m_xLegend, uno::UNO_QUERY );
903             css::chart::ChartLegendExpansion eExpansion = css::chart::ChartLegendExpansion_HIGH;
904             awt::Size aLegendSize( rAvailableSpace );
905 
906             bool bCustom = false;
907             LegendPosition eLegendPosition = LegendPosition_CUSTOM;
908             if (xLegendProp.is())
909             {
910                 // get Expansion property
911                 xLegendProp->getPropertyValue("Expansion") >>= eExpansion;
912                 if( eExpansion == css::chart::ChartLegendExpansion_CUSTOM )
913                 {
914                     RelativeSize aRelativeSize;
915                     if (xLegendProp->getPropertyValue("RelativeSize") >>= aRelativeSize)
916                     {
917                         aLegendSize.Width = static_cast<sal_Int32>(::rtl::math::approxCeil( aRelativeSize.Primary * rPageSize.Width ));
918                         aLegendSize.Height = static_cast<sal_Int32>(::rtl::math::approxCeil( aRelativeSize.Secondary * rPageSize.Height ));
919                         bCustom = true;
920                     }
921                     else
922                     {
923                         eExpansion = css::chart::ChartLegendExpansion_HIGH;
924                     }
925                 }
926                 xLegendProp->getPropertyValue("AnchorPosition") >>= eLegendPosition;
927                 lcl_getProperties( xLegendProp, aLineFillProperties, aTextProperties, rPageSize );
928             }
929 
930             // create entries
931             double fViewFontSize = lcl_CalcViewFontSize( xLegendProp, rPageSize );//todo
932             // #i109336# Improve auto positioning in chart
933             sal_Int32 nSymbolHeight = static_cast< sal_Int32 >( fViewFontSize * 0.6  );
934             sal_Int32 nSymbolWidth = nSymbolHeight;
935 
936             for (LegendEntryProvider* pLegendEntryProvider : m_aLegendEntryProviderList)
937             {
938                 if (pLegendEntryProvider)
939                 {
940                     awt::Size aCurrentRatio = pLegendEntryProvider->getPreferredLegendKeyAspectRatio();
941                     sal_Int32 nCurrentWidth = aCurrentRatio.Width;
942                     if( aCurrentRatio.Height > 0 )
943                     {
944                         nCurrentWidth = nSymbolHeight* aCurrentRatio.Width/aCurrentRatio.Height;
945                     }
946                     nSymbolWidth = std::max( nSymbolWidth, nCurrentWidth );
947                 }
948             }
949             awt::Size aMaxSymbolExtent( nSymbolWidth, nSymbolHeight );
950 
951             std::vector<ViewLegendEntry> aViewEntries;
952             for(LegendEntryProvider* pLegendEntryProvider : m_aLegendEntryProviderList)
953             {
954                 if (pLegendEntryProvider)
955                 {
956                     std::vector<ViewLegendEntry> aNewEntries = pLegendEntryProvider->createLegendEntries(
957                                                                     aMaxSymbolExtent, eExpansion, xLegendProp,
958                                                                     xLegendContainer, m_xShapeFactory, m_xContext, mrModel);
959                     if (aNewEntries.size() == 0)
960                         return;
961                     aViewEntries.insert( aViewEntries.end(), aNewEntries.begin(), aNewEntries.end() );
962                 }
963             }
964 
965             bool bSymbolsLeftSide = lcl_shouldSymbolsBePlacedOnTheLeftSide( xLegendProp, m_nDefaultWritingMode );
966 
967             uno::Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider( mrModel.getDataProvider(), uno::UNO_QUERY );
968             bool bIsPivotChart = xPivotTableDataProvider.is();
969 
970             if ( !aViewEntries.empty() || bIsPivotChart )
971             {
972                 // create buttons
973                 long nUsedButtonHeight = 0;
974                 bool bPlaceButtonsVertically = (eLegendPosition != LegendPosition_PAGE_START &&
975                                                 eLegendPosition != LegendPosition_PAGE_END &&
976                                                 eExpansion != css::chart::ChartLegendExpansion_WIDE);
977 
978                 std::vector<std::shared_ptr<VButton>> aButtons = lcl_createButtons(xLegendContainer, m_xShapeFactory, mrModel, bPlaceButtonsVertically, nUsedButtonHeight);
979 
980                 // A custom size includes the size we used for buttons already, so we need to
981                 // subtract that from the size that is available for the legend
982                 if (bCustom)
983                     aLegendSize.Height -= nUsedButtonHeight;
984 
985                 // place the legend entries
986                 aLegendSize = lcl_placeLegendEntries(aViewEntries, eExpansion, bSymbolsLeftSide, fViewFontSize,
987                                                      aMaxSymbolExtent, aTextProperties, xLegendContainer,
988                                                      m_xShapeFactory, aLegendSize, nUsedButtonHeight, rPageSize, bIsPivotChart, rDefaultLegendSize);
989 
990                 uno::Reference<beans::XPropertySet> xModelPage(mrModel.getPageBackground());
991 
992                 for (std::shared_ptr<VButton> const & pButton : aButtons)
993                 {
994                     // adjust the width of the buttons if we place them vertically
995                     if (bPlaceButtonsVertically)
996                         pButton->setSize({aLegendSize.Width - 200, pButton->getSize().Height});
997 
998                     // create the buttons
999                     pButton->createShapes(xModelPage);
1000                 }
1001             }
1002 
1003             Reference< drawing::XShape > xBorder =
1004                 pShapeFactory->createRectangle( xLegendContainer,
1005                         aLegendSize,
1006                         awt::Point(0,0),
1007                         aLineFillProperties.first,
1008                         aLineFillProperties.second, ShapeFactory::StackPosition::Bottom );
1009 
1010             //because of this name this border will be used for marking the legend
1011             ShapeFactory::setShapeName( xBorder, "MarkHandles" );
1012         }
1013     }
1014     catch( const uno::Exception & )
1015     {
1016         DBG_UNHANDLED_EXCEPTION("chart2" );
1017     }
1018 }
1019 
changePosition(awt::Rectangle & rOutAvailableSpace,const awt::Size & rPageSize,const css::awt::Size & rDefaultLegendSize)1020 void VLegend::changePosition(
1021     awt::Rectangle & rOutAvailableSpace,
1022     const awt::Size & rPageSize,
1023     const css::awt::Size & rDefaultLegendSize )
1024 {
1025     if(! m_xShape.is())
1026         return;
1027 
1028     try
1029     {
1030         // determine position and alignment depending on default position
1031         awt::Size aLegendSize = m_xShape->getSize();
1032         Reference< beans::XPropertySet > xLegendProp( m_xLegend, uno::UNO_QUERY_THROW );
1033         chart2::RelativePosition aRelativePosition;
1034 
1035         bool bDefaultLegendSize = rDefaultLegendSize.Width != 0 || rDefaultLegendSize.Height != 0;
1036         bool bAutoPosition =
1037             ! (xLegendProp->getPropertyValue( "RelativePosition") >>= aRelativePosition);
1038 
1039         LegendPosition ePos = LegendPosition_CUSTOM;
1040         xLegendProp->getPropertyValue( "AnchorPosition") >>= ePos;
1041 
1042         //calculate position
1043         if( bAutoPosition )
1044         {
1045             // auto position: relative to remaining space
1046             aRelativePosition = lcl_getDefaultPosition( ePos, rOutAvailableSpace, rPageSize );
1047             awt::Point aPos = lcl_calculatePositionAndRemainingSpace(
1048                 rOutAvailableSpace, rPageSize, aRelativePosition, ePos, aLegendSize );
1049             m_xShape->setPosition( aPos );
1050         }
1051         else
1052         {
1053             // manual position: relative to whole page
1054             awt::Rectangle aAvailableSpace( 0, 0, rPageSize.Width, rPageSize.Height );
1055             awt::Point aPos = lcl_calculatePositionAndRemainingSpace(
1056                 aAvailableSpace, rPageSize, aRelativePosition, ePos, bDefaultLegendSize ? rDefaultLegendSize : aLegendSize );
1057             m_xShape->setPosition( aPos );
1058 
1059             if( ePos != LegendPosition_CUSTOM )
1060             {
1061                 // calculate remaining space as if having autoposition:
1062                 aRelativePosition = lcl_getDefaultPosition( ePos, rOutAvailableSpace, rPageSize );
1063                 lcl_calculatePositionAndRemainingSpace(
1064                     rOutAvailableSpace, rPageSize, aRelativePosition, ePos, bDefaultLegendSize ? rDefaultLegendSize : aLegendSize );
1065             }
1066         }
1067     }
1068     catch( const uno::Exception & )
1069     {
1070         DBG_UNHANDLED_EXCEPTION("chart2" );
1071     }
1072 }
1073 
1074 } //namespace chart
1075 
1076 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1077