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