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