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