in favor of a regular C++ interface class, since this is purely used internally to LO Change-Id: I84991efb1b0c8829e2175dc083b0f8627fcb1712 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/194512 Code-Style: Noel Grandin <noel.grandin@collabora.co.uk> Tested-by: Noel Grandin <noel.grandin@collabora.co.uk> Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
1112 lines
44 KiB
C++
1112 lines
44 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include "VLegend.hxx"
|
|
#include "VButton.hxx"
|
|
#include <Legend.hxx>
|
|
#include <PropertyMapper.hxx>
|
|
#include <ChartModel.hxx>
|
|
#include <ObjectIdentifier.hxx>
|
|
#include <FormattedString.hxx>
|
|
#include <RelativePositionHelper.hxx>
|
|
#include <ShapeFactory.hxx>
|
|
#include <RelativeSizeHelper.hxx>
|
|
#include <LegendEntryProvider.hxx>
|
|
#include <chartview/DrawModelWrapper.hxx>
|
|
#include <com/sun/star/text/WritingMode2.hpp>
|
|
#include <com/sun/star/beans/XPropertySet.hpp>
|
|
#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
|
|
#include <com/sun/star/drawing/LineJoint.hpp>
|
|
#include <com/sun/star/chart/ChartLegendExpansion.hpp>
|
|
#include <com/sun/star/chart2/LegendPosition.hpp>
|
|
#include <com/sun/star/chart2/RelativePosition.hpp>
|
|
#include <com/sun/star/chart2/RelativeSize.hpp>
|
|
#include <com/sun/star/chart2/data/PivotTableFieldEntry.hpp>
|
|
#include <chart2/AbstractPivotTableDataProvider.hxx>
|
|
#include <rtl/math.hxx>
|
|
#include <svl/ctloptions.hxx>
|
|
#include <comphelper/diagnose_ex.hxx>
|
|
#include <tools/UnitConversion.hxx>
|
|
|
|
#include <utility>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
|
|
using namespace ::com::sun::star;
|
|
using namespace ::com::sun::star::chart2;
|
|
|
|
using ::com::sun::star::uno::Reference;
|
|
using ::com::sun::star::uno::Sequence;
|
|
|
|
namespace chart
|
|
{
|
|
|
|
namespace
|
|
{
|
|
|
|
typedef std::pair< ::chart::tNameSequence, ::chart::tAnySequence > tPropertyValues;
|
|
|
|
double lcl_CalcViewFontSize(
|
|
const Reference< beans::XPropertySet > & xProp,
|
|
const awt::Size & rReferenceSize )
|
|
{
|
|
double fResult = 10.0;
|
|
|
|
float fFontHeight( 0.0 );
|
|
if( xProp.is() && ( xProp->getPropertyValue( u"CharHeight"_ustr) >>= fFontHeight ))
|
|
{
|
|
fResult = fFontHeight;
|
|
try
|
|
{
|
|
awt::Size aPropRefSize;
|
|
if( (xProp->getPropertyValue( u"ReferencePageSize"_ustr) >>= aPropRefSize) &&
|
|
(aPropRefSize.Height > 0))
|
|
{
|
|
fResult = ::chart::RelativeSizeHelper::calculate( fFontHeight, aPropRefSize, rReferenceSize );
|
|
}
|
|
}
|
|
catch( const uno::Exception & )
|
|
{
|
|
DBG_UNHANDLED_EXCEPTION("chart2");
|
|
}
|
|
}
|
|
|
|
return convertPointToMm100(fResult);
|
|
}
|
|
|
|
void lcl_getProperties(
|
|
const Reference< beans::XPropertySet > & xLegendProp,
|
|
tPropertyValues & rOutLineFillProperties,
|
|
tPropertyValues & rOutTextProperties,
|
|
const awt::Size & rReferenceSize )
|
|
{
|
|
// Get Line- and FillProperties from model legend
|
|
if( !xLegendProp.is())
|
|
return;
|
|
|
|
// set rOutLineFillProperties
|
|
::chart::tPropertyNameValueMap aLineFillValueMap;
|
|
::chart::PropertyMapper::getValueMap( aLineFillValueMap, ::chart::PropertyMapper::getPropertyNameMapForFillAndLineProperties(), xLegendProp );
|
|
|
|
aLineFillValueMap[ u"LineJoint"_ustr ] <<= drawing::LineJoint_ROUND;
|
|
|
|
::chart::PropertyMapper::getMultiPropertyListsFromValueMap(
|
|
rOutLineFillProperties.first, rOutLineFillProperties.second, aLineFillValueMap );
|
|
|
|
// set rOutTextProperties
|
|
::chart::tPropertyNameValueMap aTextValueMap;
|
|
::chart::PropertyMapper::getValueMap( aTextValueMap, ::chart::PropertyMapper::getPropertyNameMapForCharacterProperties(), xLegendProp );
|
|
|
|
aTextValueMap[ u"TextAutoGrowHeight"_ustr ] <<= true;
|
|
aTextValueMap[ u"TextAutoGrowWidth"_ustr ] <<= true;
|
|
aTextValueMap[ u"TextHorizontalAdjust"_ustr ] <<= drawing::TextHorizontalAdjust_LEFT;
|
|
aTextValueMap[ u"TextMaximumFrameWidth"_ustr ] <<= rReferenceSize.Width; //needs to be overwritten by actual available space in the legend
|
|
|
|
// recalculate font size
|
|
awt::Size aPropRefSize;
|
|
float fFontHeight( 0.0 );
|
|
if( (xLegendProp->getPropertyValue( u"ReferencePageSize"_ustr) >>= aPropRefSize) &&
|
|
(aPropRefSize.Height > 0) &&
|
|
(aTextValueMap[ u"CharHeight"_ustr ] >>= fFontHeight) )
|
|
{
|
|
aTextValueMap[ u"CharHeight"_ustr ] <<=
|
|
static_cast< float >(
|
|
::chart::RelativeSizeHelper::calculate( fFontHeight, aPropRefSize, rReferenceSize ));
|
|
|
|
if( aTextValueMap[ u"CharHeightAsian"_ustr ] >>= fFontHeight )
|
|
{
|
|
aTextValueMap[ u"CharHeightAsian"_ustr ] <<=
|
|
static_cast< float >(
|
|
::chart::RelativeSizeHelper::calculate( fFontHeight, aPropRefSize, rReferenceSize ));
|
|
}
|
|
if( aTextValueMap[ u"CharHeightComplex"_ustr ] >>= fFontHeight )
|
|
{
|
|
aTextValueMap[ u"CharHeightComplex"_ustr ] <<=
|
|
static_cast< float >(
|
|
::chart::RelativeSizeHelper::calculate( fFontHeight, aPropRefSize, rReferenceSize ));
|
|
}
|
|
}
|
|
|
|
::chart::PropertyMapper::getMultiPropertyListsFromValueMap(
|
|
rOutTextProperties.first, rOutTextProperties.second, aTextValueMap );
|
|
}
|
|
|
|
awt::Size lcl_createTextShapes(
|
|
const std::vector<ViewLegendEntry> & rEntries,
|
|
const rtl::Reference<SvxShapeGroupAnyD> & xTarget,
|
|
std::vector< rtl::Reference<SvxShapeText> > & rOutTextShapes,
|
|
const tPropertyValues & rTextProperties )
|
|
{
|
|
awt::Size aResult;
|
|
|
|
for (ViewLegendEntry const & rEntry : rEntries)
|
|
{
|
|
try
|
|
{
|
|
OUString aLabelString;
|
|
if (rEntry.xLabel)
|
|
{
|
|
// tdf#150034 limit legend label text
|
|
if (rEntry.xLabel->getString().getLength() > 520)
|
|
{
|
|
sal_Int32 nIndex = rEntry.xLabel->getString().indexOf(' ', 500);
|
|
rEntry.xLabel->setString(
|
|
rEntry.xLabel->getString().copy(0, nIndex > 500 ? nIndex : 500));
|
|
}
|
|
|
|
aLabelString += rEntry.xLabel->getString();
|
|
// workaround for Issue #i67540#
|
|
if( aLabelString.isEmpty())
|
|
aLabelString = " ";
|
|
}
|
|
|
|
rtl::Reference<SvxShapeText> xEntry =
|
|
ShapeFactory::createText( xTarget, aLabelString,
|
|
rTextProperties.first, rTextProperties.second, uno::Any() );
|
|
|
|
// adapt max-extent
|
|
awt::Size aCurrSize( xEntry->getSize());
|
|
aResult.Width = std::max( aResult.Width, aCurrSize.Width );
|
|
aResult.Height = std::max( aResult.Height, aCurrSize.Height );
|
|
|
|
rOutTextShapes.push_back( xEntry );
|
|
}
|
|
catch( const uno::Exception & )
|
|
{
|
|
DBG_UNHANDLED_EXCEPTION("chart2");
|
|
}
|
|
}
|
|
|
|
return aResult;
|
|
}
|
|
|
|
void lcl_collectColumnWidths( std::vector< sal_Int32 >& rColumnWidths, const sal_Int32 nNumberOfRows, const sal_Int32 nNumberOfColumns,
|
|
const std::vector< rtl::Reference<SvxShapeText> >& rTextShapes, sal_Int32 nSymbolPlusDistanceWidth )
|
|
{
|
|
rColumnWidths.clear();
|
|
sal_Int32 nNumberOfEntries = rTextShapes.size();
|
|
for (sal_Int32 nRow = 0; nRow < nNumberOfRows; ++nRow )
|
|
{
|
|
for (sal_Int32 nColumn = 0; nColumn < nNumberOfColumns; ++nColumn )
|
|
{
|
|
sal_Int32 nEntry = nColumn + nRow * nNumberOfColumns;
|
|
if( nEntry < nNumberOfEntries )
|
|
{
|
|
awt::Size aTextSize( rTextShapes[ nEntry ]->getSize() );
|
|
sal_Int32 nWidth = nSymbolPlusDistanceWidth + aTextSize.Width;
|
|
if( nRow==0 )
|
|
rColumnWidths.push_back( nWidth );
|
|
else
|
|
rColumnWidths[nColumn] = std::max( nWidth, rColumnWidths[nColumn] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void lcl_collectRowHeighs( std::vector< sal_Int32 >& rRowHeights, const sal_Int32 nNumberOfRows, const sal_Int32 nNumberOfColumns,
|
|
const std::vector< rtl::Reference<SvxShapeText> >& rTextShapes )
|
|
{
|
|
// calculate maximum height for each row
|
|
// and collect column widths
|
|
rRowHeights.clear();
|
|
sal_Int32 nNumberOfEntries = rTextShapes.size();
|
|
for (sal_Int32 nRow = 0; nRow < nNumberOfRows; ++nRow)
|
|
{
|
|
sal_Int32 nCurrentRowHeight = 0;
|
|
for (sal_Int32 nColumn = 0; nColumn < nNumberOfColumns; ++nColumn)
|
|
{
|
|
sal_Int32 nEntry = nColumn + nRow * nNumberOfColumns;
|
|
if( nEntry < nNumberOfEntries )
|
|
{
|
|
awt::Size aTextSize( rTextShapes[ nEntry ]->getSize() );
|
|
nCurrentRowHeight = std::max( nCurrentRowHeight, aTextSize.Height );
|
|
}
|
|
}
|
|
rRowHeights.push_back( nCurrentRowHeight );
|
|
}
|
|
}
|
|
|
|
sal_Int32 lcl_getTextLineHeight( const std::vector< sal_Int32 >& aRowHeights, const sal_Int32 nNumberOfRows, double fViewFontSize )
|
|
{
|
|
const sal_Int32 nFontHeight = static_cast< sal_Int32 >( fViewFontSize );
|
|
if (!nFontHeight)
|
|
return 0;
|
|
sal_Int32 nTextLineHeight = nFontHeight;
|
|
for (sal_Int32 nRow = 0; nRow < nNumberOfRows; ++nRow)
|
|
{
|
|
sal_Int32 nFullTextHeight = aRowHeights[nRow];
|
|
if( ( nFullTextHeight / nFontHeight ) <= 1 )
|
|
{
|
|
nTextLineHeight = nFullTextHeight;//found an entry with one line-> have real text height
|
|
break;
|
|
}
|
|
}
|
|
return nTextLineHeight;
|
|
}
|
|
|
|
//returns resulting legend size
|
|
awt::Size lcl_placeLegendEntries(
|
|
std::vector<ViewLegendEntry> & rEntries,
|
|
css::chart::ChartLegendExpansion eExpansion,
|
|
bool bSymbolsLeftSide,
|
|
double fViewFontSize,
|
|
const awt::Size& rMaxSymbolExtent,
|
|
tPropertyValues & rTextProperties,
|
|
const rtl::Reference<SvxShapeGroupAnyD> & xTarget,
|
|
const awt::Size& rRemainingSpace,
|
|
sal_Int32 nYStartPosition,
|
|
const awt::Size& rPageSize,
|
|
bool bIsPivotChart,
|
|
awt::Size& rDefaultLegendSize)
|
|
{
|
|
bool bIsCustomSize = (eExpansion == css::chart::ChartLegendExpansion_CUSTOM);
|
|
awt::Size aResultingLegendSize(0,0);
|
|
// For Pivot charts set the *minimum* legend size as a function of page size.
|
|
if ( bIsPivotChart )
|
|
aResultingLegendSize = awt::Size((rPageSize.Width * 13) / 80, (rPageSize.Height * 31) / 90);
|
|
if( bIsCustomSize )
|
|
aResultingLegendSize = awt::Size(rRemainingSpace.Width, rRemainingSpace.Height + nYStartPosition);
|
|
|
|
// #i109336# Improve auto positioning in chart
|
|
sal_Int32 nXPadding = static_cast< sal_Int32 >( std::max( 100.0, fViewFontSize * 0.33 ) );
|
|
sal_Int32 nXOffset = static_cast< sal_Int32 >( std::max( 100.0, fViewFontSize * 0.66 ) );
|
|
sal_Int32 nYPadding = static_cast< sal_Int32 >( std::max( 100.0, fViewFontSize * 0.2 ) );
|
|
sal_Int32 nYOffset = static_cast< sal_Int32 >( std::max( 100.0, fViewFontSize * 0.2 ) );
|
|
|
|
const sal_Int32 nSymbolToTextDistance = static_cast< sal_Int32 >( std::max( 100.0, fViewFontSize * 0.22 ) );//minimum 1mm
|
|
const sal_Int32 nSymbolPlusDistanceWidth = rMaxSymbolExtent.Width + nSymbolToTextDistance;
|
|
sal_Int32 nMaxTextWidth = rRemainingSpace.Width - nSymbolPlusDistanceWidth;
|
|
uno::Any* pFrameWidthAny = PropertyMapper::getValuePointer( rTextProperties.second, rTextProperties.first, u"TextMaximumFrameWidth");
|
|
if(pFrameWidthAny)
|
|
{
|
|
if( eExpansion == css::chart::ChartLegendExpansion_HIGH )
|
|
{
|
|
// limit the width of texts to 30% of the total available width
|
|
// #i109336# Improve auto positioning in chart
|
|
nMaxTextWidth = rRemainingSpace.Width * 3 / 10;
|
|
}
|
|
*pFrameWidthAny <<= nMaxTextWidth;
|
|
}
|
|
|
|
std::vector< rtl::Reference<SvxShapeText> > aTextShapes;
|
|
awt::Size aMaxEntryExtent = lcl_createTextShapes( rEntries, xTarget, aTextShapes, rTextProperties );
|
|
OSL_ASSERT( aTextShapes.size() == rEntries.size());
|
|
|
|
sal_Int32 nMaxEntryWidth = nXOffset + nSymbolPlusDistanceWidth + aMaxEntryExtent.Width;
|
|
sal_Int32 nMaxEntryHeight = nYOffset + aMaxEntryExtent.Height;
|
|
sal_Int32 nNumberOfEntries = rEntries.size();
|
|
|
|
rDefaultLegendSize.Width = nMaxEntryWidth;
|
|
rDefaultLegendSize.Height = nMaxEntryHeight + nYPadding;
|
|
|
|
sal_Int32 nNumberOfColumns = 0, nNumberOfRows = 0;
|
|
std::vector< sal_Int32 > aColumnWidths;
|
|
std::vector< sal_Int32 > aRowHeights;
|
|
|
|
sal_Int32 nTextLineHeight = static_cast< sal_Int32 >( fViewFontSize );
|
|
|
|
// determine layout depending on LegendExpansion
|
|
if( eExpansion == css::chart::ChartLegendExpansion_CUSTOM )
|
|
{
|
|
sal_Int32 nCurrentRow=0;
|
|
sal_Int32 nCurrentColumn=-1;
|
|
sal_Int32 nMaxColumnCount=-1;
|
|
for( sal_Int32 nN=0; nN<static_cast<sal_Int32>(aTextShapes.size()); nN++ )
|
|
{
|
|
const rtl::Reference<SvxShapeText>& xShape( aTextShapes[nN] );
|
|
if( !xShape.is() )
|
|
continue;
|
|
awt::Size aSize( xShape->getSize() );
|
|
sal_Int32 nNewWidth = aSize.Width + nSymbolPlusDistanceWidth;
|
|
sal_Int32 nCurrentColumnCount = aColumnWidths.size();
|
|
|
|
//are we allowed to add a new column?
|
|
if( nMaxColumnCount==-1 || (nCurrentColumn+1) < nMaxColumnCount )
|
|
{
|
|
//try add a new column
|
|
nCurrentColumn++;
|
|
if( nCurrentColumn < nCurrentColumnCount )
|
|
{
|
|
//check whether the current column width is sufficient for the new entry
|
|
if( aColumnWidths[nCurrentColumn]>=nNewWidth )
|
|
{
|
|
//all good proceed with next entry
|
|
continue;
|
|
}
|
|
|
|
aColumnWidths[nCurrentColumn] = std::max( nNewWidth, aColumnWidths[nCurrentColumn] );
|
|
} else
|
|
aColumnWidths.push_back(nNewWidth);
|
|
|
|
//do the columns still fit into the given size?
|
|
nCurrentColumnCount = aColumnWidths.size();//update count
|
|
sal_Int32 nSumWidth = 0;
|
|
for (sal_Int32 nColumn = 0; nColumn < nCurrentColumnCount; nColumn++)
|
|
nSumWidth += aColumnWidths[nColumn];
|
|
|
|
if( nSumWidth <= rRemainingSpace.Width || nCurrentColumnCount==1 )
|
|
{
|
|
//all good proceed with next entry
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
//not enough space for the current amount of columns
|
|
//try again with less columns
|
|
nMaxColumnCount = nCurrentColumnCount-1;
|
|
nN=-1;
|
|
nCurrentRow=0;
|
|
nCurrentColumn=-1;
|
|
aColumnWidths.clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//add a new row and try the same entry again
|
|
nCurrentRow++;
|
|
nCurrentColumn=-1;
|
|
nN--;
|
|
}
|
|
}
|
|
nNumberOfColumns = aColumnWidths.size();
|
|
nNumberOfRows = nCurrentRow+1;
|
|
|
|
//check if there is not enough space so that some entries must be removed
|
|
lcl_collectRowHeighs( aRowHeights, nNumberOfRows, nNumberOfColumns, aTextShapes );
|
|
nTextLineHeight = lcl_getTextLineHeight( aRowHeights, nNumberOfRows, fViewFontSize );
|
|
sal_Int32 nSumHeight = 0;
|
|
for (sal_Int32 nRow=0; nRow < nNumberOfRows; nRow++)
|
|
nSumHeight += aRowHeights[nRow];
|
|
sal_Int32 nRemainingSpace = rRemainingSpace.Height - nSumHeight;
|
|
|
|
if( nRemainingSpace < -100 ) // 1mm tolerance for OOXML interop tdf#90404
|
|
{
|
|
//remove entries that are too big
|
|
for (sal_Int32 nRow = nNumberOfRows; nRow--; )
|
|
{
|
|
for (sal_Int32 nColumn = nNumberOfColumns; nColumn--; )
|
|
{
|
|
sal_Int32 nEntry = nColumn + nRow * nNumberOfColumns;
|
|
if( nEntry < static_cast<sal_Int32>(aTextShapes.size()) )
|
|
{
|
|
DrawModelWrapper::removeShape( aTextShapes[nEntry] );
|
|
aTextShapes.pop_back();
|
|
}
|
|
if( nEntry < nNumberOfEntries && ( nEntry != 0 || nNumberOfColumns != 1 ) )
|
|
{
|
|
DrawModelWrapper::removeShape( rEntries[ nEntry ].xSymbol );
|
|
rEntries.pop_back();
|
|
nNumberOfEntries--;
|
|
}
|
|
}
|
|
if (nRow == 0 && nNumberOfColumns == 1)
|
|
{
|
|
try
|
|
{
|
|
OUString aLabelString = rEntries[0].xLabel->getString();
|
|
static constexpr OUString sDots = u"..."_ustr;
|
|
for (sal_Int32 nNewLen = aLabelString.getLength() - sDots.getLength(); nNewLen > 0; )
|
|
{
|
|
OUString aNewLabel = aLabelString.subView(0, nNewLen) + sDots;
|
|
rtl::Reference<SvxShapeText> xEntry = ShapeFactory::createText(
|
|
xTarget, aNewLabel, rTextProperties.first, rTextProperties.second, uno::Any());
|
|
nSumHeight = xEntry->getSize().Height;
|
|
nRemainingSpace = rRemainingSpace.Height - nSumHeight;
|
|
if (nRemainingSpace >= 0)
|
|
{
|
|
sal_Int32 nWidth = xEntry->getSize().Width + nSymbolPlusDistanceWidth;
|
|
if (rRemainingSpace.Width - nWidth >= 0)
|
|
{
|
|
aTextShapes.push_back(xEntry);
|
|
rEntries[0].xLabel->setString(aNewLabel);
|
|
aRowHeights[0] = nSumHeight;
|
|
aColumnWidths[0] = nWidth;
|
|
break;
|
|
}
|
|
}
|
|
DrawModelWrapper::removeShape(xEntry);
|
|
// The intention here is to make pathological cases with extremely large labels
|
|
// converge a little faster
|
|
if (nNewLen > 10 && std::abs(nRemainingSpace) > nSumHeight / 10)
|
|
nNewLen -= nNewLen / 10;
|
|
else
|
|
--nNewLen;
|
|
}
|
|
if (aTextShapes.empty())
|
|
{
|
|
DrawModelWrapper::removeShape(rEntries[0].xSymbol);
|
|
rEntries.pop_back();
|
|
nNumberOfEntries--;
|
|
aRowHeights.pop_back();
|
|
}
|
|
}
|
|
catch (const uno::Exception&)
|
|
{
|
|
DBG_UNHANDLED_EXCEPTION("chart2");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nSumHeight -= aRowHeights[nRow];
|
|
aRowHeights.pop_back();
|
|
nRemainingSpace = rRemainingSpace.Height - nSumHeight;
|
|
if (nRemainingSpace >= 0)
|
|
break;
|
|
}
|
|
}
|
|
nNumberOfRows = static_cast<sal_Int32>(aRowHeights.size());
|
|
}
|
|
if( nRemainingSpace >= -100 ) // 1mm tolerance for OOXML interop tdf#90404
|
|
{
|
|
sal_Int32 nNormalSpacingHeight = 2*nYPadding+(nNumberOfRows-1)*nYOffset;
|
|
if( nRemainingSpace < nNormalSpacingHeight )
|
|
{
|
|
//reduce spacing between the entries
|
|
nYPadding = nYOffset = nRemainingSpace/(nNumberOfRows+1);
|
|
}
|
|
else
|
|
{
|
|
//we have some space left that should be spread equally between all rows
|
|
sal_Int32 nRemainingSingleSpace = (nRemainingSpace-nNormalSpacingHeight)/(nNumberOfRows+1);
|
|
nYPadding += nRemainingSingleSpace;
|
|
nYOffset += nRemainingSingleSpace;
|
|
}
|
|
}
|
|
|
|
//check spacing between columns
|
|
sal_Int32 nSumWidth = 0;
|
|
for (sal_Int32 nColumn = 0; nColumn < nNumberOfColumns; nColumn++)
|
|
nSumWidth += aColumnWidths[nColumn];
|
|
nRemainingSpace = rRemainingSpace.Width - nSumWidth;
|
|
if( nRemainingSpace>=0 )
|
|
{
|
|
sal_Int32 nNormalSpacingWidth = 2*nXPadding+(nNumberOfColumns-1)*nXOffset;
|
|
if( nRemainingSpace < nNormalSpacingWidth )
|
|
{
|
|
//reduce spacing between the entries
|
|
nXPadding = nXOffset = nRemainingSpace/(nNumberOfColumns+1);
|
|
}
|
|
else
|
|
{
|
|
//we have some space left that should be spread equally between all columns
|
|
sal_Int32 nRemainingSingleSpace = (nRemainingSpace-nNormalSpacingWidth)/(nNumberOfColumns+1);
|
|
nXPadding += nRemainingSingleSpace;
|
|
nXOffset += nRemainingSingleSpace;
|
|
}
|
|
}
|
|
}
|
|
else if( eExpansion == css::chart::ChartLegendExpansion_HIGH )
|
|
{
|
|
sal_Int32 nMaxNumberOfRows = nMaxEntryHeight
|
|
? (rRemainingSpace.Height - 2*nYPadding ) / nMaxEntryHeight
|
|
: 0;
|
|
|
|
nNumberOfColumns = nMaxNumberOfRows
|
|
? static_cast< sal_Int32 >(
|
|
ceil( static_cast< double >( nNumberOfEntries ) /
|
|
static_cast< double >( nMaxNumberOfRows ) ))
|
|
: 0;
|
|
nNumberOfRows = nNumberOfColumns
|
|
? static_cast< sal_Int32 >(
|
|
ceil( static_cast< double >( nNumberOfEntries ) /
|
|
static_cast< double >( nNumberOfColumns ) ))
|
|
: 0;
|
|
}
|
|
else if( eExpansion == css::chart::ChartLegendExpansion_WIDE )
|
|
{
|
|
sal_Int32 nMaxNumberOfColumns = nMaxEntryWidth
|
|
? (rRemainingSpace.Width - 2*nXPadding ) / nMaxEntryWidth
|
|
: 0;
|
|
|
|
nNumberOfRows = nMaxNumberOfColumns
|
|
? static_cast< sal_Int32 >(
|
|
ceil( static_cast< double >( nNumberOfEntries ) /
|
|
static_cast< double >( nMaxNumberOfColumns ) ))
|
|
: 0;
|
|
nNumberOfColumns = nNumberOfRows
|
|
? static_cast< sal_Int32 >(
|
|
ceil( static_cast< double >( nNumberOfEntries ) /
|
|
static_cast< double >( nNumberOfRows ) ))
|
|
: 0;
|
|
}
|
|
else // css::chart::ChartLegendExpansion_BALANCED
|
|
{
|
|
double fAspect = nMaxEntryHeight
|
|
? static_cast< double >( nMaxEntryWidth ) / static_cast< double >( nMaxEntryHeight )
|
|
: 0.0;
|
|
|
|
nNumberOfRows = static_cast< sal_Int32 >(
|
|
ceil( sqrt( static_cast< double >( nNumberOfEntries ) * fAspect )));
|
|
nNumberOfColumns = nNumberOfRows
|
|
? static_cast< sal_Int32 >(
|
|
ceil( static_cast< double >( nNumberOfEntries ) /
|
|
static_cast< double >( nNumberOfRows ) ))
|
|
: 0;
|
|
}
|
|
|
|
if(nNumberOfRows<=0)
|
|
return aResultingLegendSize;
|
|
|
|
if( eExpansion != css::chart::ChartLegendExpansion_CUSTOM )
|
|
{
|
|
lcl_collectColumnWidths( aColumnWidths, nNumberOfRows, nNumberOfColumns, aTextShapes, nSymbolPlusDistanceWidth );
|
|
lcl_collectRowHeighs( aRowHeights, nNumberOfRows, nNumberOfColumns, aTextShapes );
|
|
nTextLineHeight = lcl_getTextLineHeight( aRowHeights, nNumberOfRows, fViewFontSize );
|
|
}
|
|
|
|
sal_Int32 nCurrentXPos = bSymbolsLeftSide ? nXPadding : -nXPadding;
|
|
|
|
// place entries into column and rows
|
|
sal_Int32 nMaxYPos = 0;
|
|
|
|
for (sal_Int32 nColumn = 0; nColumn < nNumberOfColumns; ++nColumn)
|
|
{
|
|
sal_Int32 nCurrentYPos = nYPadding + nYStartPosition;
|
|
for (sal_Int32 nRow = 0; nRow < nNumberOfRows; ++nRow)
|
|
{
|
|
sal_Int32 nEntry = nColumn + nRow * nNumberOfColumns;
|
|
if( nEntry >= nNumberOfEntries )
|
|
break;
|
|
|
|
// text shape
|
|
const rtl::Reference<SvxShapeText>& xTextShape( aTextShapes[nEntry] );
|
|
if( xTextShape.is() )
|
|
{
|
|
awt::Size aTextSize( xTextShape->getSize() );
|
|
sal_Int32 nTextXPos = nCurrentXPos + nSymbolPlusDistanceWidth;
|
|
if( !bSymbolsLeftSide )
|
|
nTextXPos = nCurrentXPos - nSymbolPlusDistanceWidth - aTextSize.Width;
|
|
xTextShape->setPosition( awt::Point( nTextXPos, nCurrentYPos ));
|
|
}
|
|
|
|
// symbol
|
|
rtl::Reference<SvxShapeGroup> & xSymbol( rEntries[ nEntry ].xSymbol );
|
|
if( xSymbol.is() )
|
|
{
|
|
awt::Size aSymbolSize( rMaxSymbolExtent );
|
|
sal_Int32 nSymbolXPos = nCurrentXPos;
|
|
if( !bSymbolsLeftSide )
|
|
nSymbolXPos = nCurrentXPos - rMaxSymbolExtent.Width;
|
|
sal_Int32 nSymbolYPos = nCurrentYPos + ( ( nTextLineHeight - aSymbolSize.Height ) / 2 );
|
|
xSymbol->setPosition( awt::Point( nSymbolXPos, nSymbolYPos ) );
|
|
}
|
|
|
|
nCurrentYPos += aRowHeights[ nRow ];
|
|
if( nRow+1 < nNumberOfRows )
|
|
nCurrentYPos += nYOffset;
|
|
nMaxYPos = std::max( nMaxYPos, nCurrentYPos );
|
|
}
|
|
if( bSymbolsLeftSide )
|
|
{
|
|
nCurrentXPos += aColumnWidths[nColumn];
|
|
if( nColumn+1 < nNumberOfColumns )
|
|
nCurrentXPos += nXOffset;
|
|
}
|
|
else
|
|
{
|
|
nCurrentXPos -= aColumnWidths[nColumn];
|
|
if( nColumn+1 < nNumberOfColumns )
|
|
nCurrentXPos -= nXOffset;
|
|
}
|
|
}
|
|
|
|
if( !bIsCustomSize )
|
|
{
|
|
if( bSymbolsLeftSide )
|
|
aResultingLegendSize.Width = std::max( aResultingLegendSize.Width, nCurrentXPos + nXPadding );
|
|
else
|
|
{
|
|
sal_Int32 nLegendWidth = -(nCurrentXPos-nXPadding);
|
|
aResultingLegendSize.Width = std::max( aResultingLegendSize.Width, nLegendWidth );
|
|
}
|
|
aResultingLegendSize.Height = std::max( aResultingLegendSize.Height, nMaxYPos + nYPadding );
|
|
}
|
|
|
|
if( !bSymbolsLeftSide )
|
|
{
|
|
sal_Int32 nLegendWidth = aResultingLegendSize.Width;
|
|
awt::Point aPos(0,0);
|
|
for( sal_Int32 nEntry=0; nEntry<nNumberOfEntries; nEntry++ )
|
|
{
|
|
rtl::Reference<SvxShapeGroup> & xSymbol( rEntries[ nEntry ].xSymbol );
|
|
aPos = xSymbol->getPosition();
|
|
aPos.X += nLegendWidth;
|
|
xSymbol->setPosition( aPos );
|
|
rtl::Reference<SvxShapeText> & xText( aTextShapes[ nEntry ] );
|
|
aPos = xText->getPosition();
|
|
aPos.X += nLegendWidth;
|
|
xText->setPosition( aPos );
|
|
}
|
|
}
|
|
|
|
return aResultingLegendSize;
|
|
}
|
|
|
|
// #i109336# Improve auto positioning in chart
|
|
sal_Int32 lcl_getLegendLeftRightMargin()
|
|
{
|
|
return 210; // 1/100 mm
|
|
}
|
|
|
|
// #i109336# Improve auto positioning in chart
|
|
sal_Int32 lcl_getLegendTopBottomMargin()
|
|
{
|
|
return 185; // 1/100 mm
|
|
}
|
|
|
|
chart2::RelativePosition lcl_getDefaultPosition( LegendPosition ePos, const awt::Rectangle& rOutAvailableSpace, const awt::Size & rPageSize )
|
|
{
|
|
chart2::RelativePosition aResult;
|
|
|
|
switch( ePos )
|
|
{
|
|
case LegendPosition_LINE_START:
|
|
{
|
|
// #i109336# Improve auto positioning in chart
|
|
const double fDefaultDistance = static_cast< double >( lcl_getLegendLeftRightMargin() ) /
|
|
static_cast< double >( rPageSize.Width );
|
|
aResult = chart2::RelativePosition(
|
|
fDefaultDistance, 0.5, drawing::Alignment_LEFT );
|
|
}
|
|
break;
|
|
case LegendPosition_LINE_END:
|
|
{
|
|
// #i109336# Improve auto positioning in chart
|
|
const double fDefaultDistance = static_cast< double >( lcl_getLegendLeftRightMargin() ) /
|
|
static_cast< double >( rPageSize.Width );
|
|
aResult = chart2::RelativePosition(
|
|
1.0 - fDefaultDistance, 0.5, drawing::Alignment_RIGHT );
|
|
}
|
|
break;
|
|
case LegendPosition_PAGE_START:
|
|
{
|
|
// #i109336# Improve auto positioning in chart
|
|
const double fDefaultDistance = static_cast< double >( lcl_getLegendTopBottomMargin() ) /
|
|
static_cast< double >( rPageSize.Height );
|
|
double fDistance = (static_cast<double>(rOutAvailableSpace.Y)/static_cast<double>(rPageSize.Height)) + fDefaultDistance;
|
|
aResult = chart2::RelativePosition(
|
|
0.5, fDistance, drawing::Alignment_TOP );
|
|
}
|
|
break;
|
|
case LegendPosition_PAGE_END:
|
|
{
|
|
// #i109336# Improve auto positioning in chart
|
|
const double fDefaultDistance = static_cast< double >( lcl_getLegendTopBottomMargin() ) /
|
|
static_cast< double >( rPageSize.Height );
|
|
|
|
double fDistance = double(rPageSize.Height - (rOutAvailableSpace.Y + rOutAvailableSpace.Height));
|
|
fDistance += fDefaultDistance;
|
|
fDistance /= double(rPageSize.Height);
|
|
|
|
aResult = chart2::RelativePosition(
|
|
0.5, 1.0 - fDistance, drawing::Alignment_BOTTOM );
|
|
}
|
|
break;
|
|
case LegendPosition::LegendPosition_MAKE_FIXED_SIZE:
|
|
default:
|
|
// nothing to be set
|
|
break;
|
|
}
|
|
|
|
return aResult;
|
|
}
|
|
|
|
/** @return
|
|
a point relative to the upper left corner that can be used for
|
|
XShape::setPosition()
|
|
*/
|
|
awt::Point lcl_calculatePositionAndRemainingSpace(
|
|
awt::Rectangle & rRemainingSpace,
|
|
const awt::Size & rPageSize,
|
|
const chart2::RelativePosition& rRelPos,
|
|
LegendPosition ePos,
|
|
const awt::Size& aLegendSize,
|
|
bool bOverlay )
|
|
{
|
|
// calculate position
|
|
awt::Point aResult(
|
|
static_cast< sal_Int32 >( rRelPos.Primary * rPageSize.Width ),
|
|
static_cast< sal_Int32 >( rRelPos.Secondary * rPageSize.Height ));
|
|
|
|
aResult = RelativePositionHelper::getUpperLeftCornerOfAnchoredObject(
|
|
aResult, aLegendSize, rRelPos.Anchor );
|
|
|
|
// adapt rRemainingSpace if LegendPosition is not CUSTOM
|
|
// #i109336# Improve auto positioning in chart
|
|
sal_Int32 nXDistance = lcl_getLegendLeftRightMargin();
|
|
sal_Int32 nYDistance = lcl_getLegendTopBottomMargin();
|
|
if (!bOverlay) switch( ePos )
|
|
{
|
|
case LegendPosition_LINE_START:
|
|
{
|
|
sal_Int32 nExtent = aLegendSize.Width;
|
|
rRemainingSpace.Width -= ( nExtent + nXDistance );
|
|
rRemainingSpace.X += ( nExtent + nXDistance );
|
|
}
|
|
break;
|
|
case LegendPosition_LINE_END:
|
|
{
|
|
rRemainingSpace.Width -= ( aLegendSize.Width + nXDistance );
|
|
}
|
|
break;
|
|
case LegendPosition_PAGE_START:
|
|
{
|
|
sal_Int32 nExtent = aLegendSize.Height;
|
|
rRemainingSpace.Height -= ( nExtent + nYDistance );
|
|
rRemainingSpace.Y += ( nExtent + nYDistance );
|
|
}
|
|
break;
|
|
case LegendPosition_PAGE_END:
|
|
{
|
|
rRemainingSpace.Height -= ( aLegendSize.Height + nYDistance );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// nothing
|
|
break;
|
|
}
|
|
|
|
// adjust the legend position. Esp. for old files that had slightly smaller legends
|
|
const sal_Int32 nEdgeDistance( 30 );
|
|
if( aResult.X + aLegendSize.Width > rPageSize.Width )
|
|
{
|
|
sal_Int32 nNewX( (rPageSize.Width - aLegendSize.Width) - nEdgeDistance );
|
|
if( nNewX > rPageSize.Width / 4 )
|
|
aResult.X = nNewX;
|
|
}
|
|
if( aResult.Y + aLegendSize.Height > rPageSize.Height )
|
|
{
|
|
sal_Int32 nNewY( (rPageSize.Height - aLegendSize.Height) - nEdgeDistance );
|
|
if( nNewY > rPageSize.Height / 4 )
|
|
aResult.Y = nNewY;
|
|
}
|
|
|
|
return aResult;
|
|
}
|
|
|
|
bool lcl_shouldSymbolsBePlacedOnTheLeftSide( const Reference< beans::XPropertySet >& xLegendProp, sal_Int16 nDefaultWritingMode )
|
|
{
|
|
bool bSymbolsLeftSide = true;
|
|
try
|
|
{
|
|
if( SvtCTLOptions::IsCTLFontEnabled() )
|
|
{
|
|
if(xLegendProp.is())
|
|
{
|
|
sal_Int16 nWritingMode=-1;
|
|
if( xLegendProp->getPropertyValue( u"WritingMode"_ustr ) >>= nWritingMode )
|
|
{
|
|
if( nWritingMode == text::WritingMode2::PAGE )
|
|
nWritingMode = nDefaultWritingMode;
|
|
if( nWritingMode == text::WritingMode2::RL_TB )
|
|
bSymbolsLeftSide=false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch( const uno::Exception & )
|
|
{
|
|
DBG_UNHANDLED_EXCEPTION("chart2");
|
|
}
|
|
return bSymbolsLeftSide;
|
|
}
|
|
|
|
std::vector<std::shared_ptr<VButton>> lcl_createButtons(
|
|
rtl::Reference<SvxShapeGroupAnyD> const & xLegendContainer,
|
|
ChartModel& rModel, bool bPlaceButtonsVertically, tools::Long & nUsedHeight)
|
|
{
|
|
std::vector<std::shared_ptr<VButton>> aButtons;
|
|
|
|
chart2api::AbstractPivotTableDataProvider* pPivotTableDataProvider =
|
|
dynamic_cast<chart2api::AbstractPivotTableDataProvider*>(rModel.getDataProvider().get());
|
|
if (!pPivotTableDataProvider)
|
|
return aButtons;
|
|
|
|
if (pPivotTableDataProvider->getColumnFields().empty())
|
|
return aButtons;
|
|
|
|
awt::Size aSize(2000, 700);
|
|
int x = 100;
|
|
int y = 100;
|
|
|
|
const std::vector<chart2::data::PivotTableFieldEntry>& aPivotFieldEntries = pPivotTableDataProvider->getColumnFields();
|
|
for (chart2::data::PivotTableFieldEntry const & sColumnFieldEntry : aPivotFieldEntries)
|
|
{
|
|
auto pButton = std::make_shared<VButton>();
|
|
aButtons.push_back(pButton);
|
|
pButton->init(xLegendContainer);
|
|
awt::Point aNewPosition(x, y);
|
|
pButton->setLabel(sColumnFieldEntry.Name);
|
|
pButton->setCID("FieldButton.Column." + OUString::number(sColumnFieldEntry.DimensionIndex));
|
|
pButton->setPosition(aNewPosition);
|
|
pButton->setSize(aSize);
|
|
if (sColumnFieldEntry.Name == "Data")
|
|
{
|
|
pButton->showArrow(false);
|
|
pButton->setBGColor(Color(0x00F6F6F6));
|
|
}
|
|
if (sColumnFieldEntry.HasHiddenMembers)
|
|
pButton->setArrowColor(Color(0x0000FF));
|
|
|
|
if (bPlaceButtonsVertically)
|
|
y += aSize.Height + 100;
|
|
else
|
|
x += aSize.Width + 100;
|
|
}
|
|
if (bPlaceButtonsVertically)
|
|
nUsedHeight += y + 100;
|
|
else
|
|
nUsedHeight += aSize.Height + 100;
|
|
|
|
return aButtons;
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
VLegend::VLegend(
|
|
rtl::Reference< Legend > xLegend,
|
|
const Reference< uno::XComponentContext > & xContext,
|
|
std::vector< LegendEntryProvider* >&& rLegendEntryProviderList,
|
|
rtl::Reference<SvxShapeGroupAnyD> xTargetPage,
|
|
ChartModel& rModel )
|
|
: m_xTarget(std::move(xTargetPage))
|
|
, m_xLegend(std::move(xLegend))
|
|
, mrModel(rModel)
|
|
, m_xContext(xContext)
|
|
, m_aLegendEntryProviderList(std::move(rLegendEntryProviderList))
|
|
, m_nDefaultWritingMode(text::WritingMode2::LR_TB)
|
|
{
|
|
}
|
|
|
|
void VLegend::setDefaultWritingMode( sal_Int16 nDefaultWritingMode )
|
|
{
|
|
m_nDefaultWritingMode = nDefaultWritingMode;
|
|
}
|
|
|
|
bool VLegend::isVisible( const rtl::Reference< Legend > & xLegend )
|
|
{
|
|
if( ! xLegend.is())
|
|
return false;
|
|
|
|
bool bShow = false;
|
|
try
|
|
{
|
|
xLegend->getPropertyValue( u"Show"_ustr) >>= bShow;
|
|
}
|
|
catch( const uno::Exception & )
|
|
{
|
|
DBG_UNHANDLED_EXCEPTION("chart2");
|
|
}
|
|
|
|
return bShow;
|
|
}
|
|
|
|
void VLegend::createShapes(
|
|
const awt::Size & rAvailableSpace,
|
|
const awt::Size & rPageSize,
|
|
awt::Size & rDefaultLegendSize )
|
|
{
|
|
if(! (m_xLegend.is() && m_xTarget.is()))
|
|
return;
|
|
|
|
try
|
|
{
|
|
//create shape and add to page
|
|
OUString aLegendParticle( ObjectIdentifier::createParticleForLegend( &mrModel ) );
|
|
m_xShape = ShapeFactory::createGroup2D( m_xTarget,
|
|
ObjectIdentifier::createClassifiedIdentifierForParticle( aLegendParticle ) );
|
|
|
|
// create and insert sub-shapes
|
|
rtl::Reference<SvxShapeGroupAnyD> xLegendContainer = m_xShape;
|
|
if( xLegendContainer.is() )
|
|
{
|
|
// for quickly setting properties
|
|
tPropertyValues aLineFillProperties;
|
|
tPropertyValues aTextProperties;
|
|
|
|
css::chart::ChartLegendExpansion eExpansion = css::chart::ChartLegendExpansion_HIGH;
|
|
awt::Size aLegendSize( rAvailableSpace );
|
|
|
|
bool bCustom = false;
|
|
LegendPosition eLegendPosition = LegendPosition_LINE_END;
|
|
// get Expansion property
|
|
m_xLegend->getPropertyValue(u"Expansion"_ustr) >>= eExpansion;
|
|
if( eExpansion == css::chart::ChartLegendExpansion_CUSTOM )
|
|
{
|
|
RelativeSize aRelativeSize;
|
|
if (m_xLegend->getPropertyValue(u"RelativeSize"_ustr) >>= aRelativeSize)
|
|
{
|
|
aLegendSize.Width = static_cast<sal_Int32>(::rtl::math::approxCeil( aRelativeSize.Primary * rPageSize.Width ));
|
|
aLegendSize.Height = static_cast<sal_Int32>(::rtl::math::approxCeil( aRelativeSize.Secondary * rPageSize.Height ));
|
|
bCustom = true;
|
|
}
|
|
else
|
|
{
|
|
eExpansion = css::chart::ChartLegendExpansion_HIGH;
|
|
}
|
|
}
|
|
m_xLegend->getPropertyValue(u"AnchorPosition"_ustr) >>= eLegendPosition;
|
|
lcl_getProperties( m_xLegend, aLineFillProperties, aTextProperties, rPageSize );
|
|
|
|
// create entries
|
|
double fViewFontSize = lcl_CalcViewFontSize( m_xLegend, rPageSize );//todo
|
|
// #i109336# Improve auto positioning in chart
|
|
sal_Int32 nSymbolHeight = static_cast< sal_Int32 >( fViewFontSize * 0.6 );
|
|
sal_Int32 nSymbolWidth = nSymbolHeight;
|
|
|
|
for (LegendEntryProvider* pLegendEntryProvider : m_aLegendEntryProviderList)
|
|
{
|
|
if (pLegendEntryProvider)
|
|
{
|
|
awt::Size aCurrentRatio = pLegendEntryProvider->getPreferredLegendKeyAspectRatio();
|
|
sal_Int32 nCurrentWidth = aCurrentRatio.Width;
|
|
if( aCurrentRatio.Height > 0 )
|
|
{
|
|
nCurrentWidth = nSymbolHeight* aCurrentRatio.Width/aCurrentRatio.Height;
|
|
}
|
|
nSymbolWidth = std::max( nSymbolWidth, nCurrentWidth );
|
|
}
|
|
}
|
|
awt::Size aMaxSymbolExtent( nSymbolWidth, nSymbolHeight );
|
|
|
|
std::vector<ViewLegendEntry> aViewEntries;
|
|
for(LegendEntryProvider* pLegendEntryProvider : m_aLegendEntryProviderList)
|
|
{
|
|
if (pLegendEntryProvider)
|
|
{
|
|
std::vector<ViewLegendEntry> aNewEntries = pLegendEntryProvider->createLegendEntries(
|
|
aMaxSymbolExtent, eLegendPosition, m_xLegend,
|
|
xLegendContainer, m_xContext, mrModel);
|
|
aViewEntries.insert( aViewEntries.end(), aNewEntries.begin(), aNewEntries.end() );
|
|
}
|
|
}
|
|
|
|
bool bSymbolsLeftSide = lcl_shouldSymbolsBePlacedOnTheLeftSide( m_xLegend, m_nDefaultWritingMode );
|
|
|
|
chart2api::AbstractPivotTableDataProvider* pPivotTableDataProvider =
|
|
dynamic_cast<chart2api::AbstractPivotTableDataProvider*>(mrModel.getDataProvider().get());
|
|
bool bIsPivotChart = pPivotTableDataProvider != nullptr;
|
|
|
|
if ( !aViewEntries.empty() || bIsPivotChart )
|
|
{
|
|
// create buttons
|
|
tools::Long nUsedButtonHeight = 0;
|
|
bool bPlaceButtonsVertically = (eLegendPosition != LegendPosition_PAGE_START &&
|
|
eLegendPosition != LegendPosition_PAGE_END &&
|
|
eExpansion != css::chart::ChartLegendExpansion_WIDE);
|
|
|
|
std::vector<std::shared_ptr<VButton>> aButtons = lcl_createButtons(xLegendContainer, mrModel, bPlaceButtonsVertically, nUsedButtonHeight);
|
|
|
|
// A custom size includes the size we used for buttons already, so we need to
|
|
// subtract that from the size that is available for the legend
|
|
if (bCustom)
|
|
aLegendSize.Height -= nUsedButtonHeight;
|
|
|
|
// place the legend entries
|
|
aLegendSize = lcl_placeLegendEntries(aViewEntries, eExpansion, bSymbolsLeftSide, fViewFontSize,
|
|
aMaxSymbolExtent, aTextProperties, xLegendContainer,
|
|
aLegendSize, nUsedButtonHeight, rPageSize, bIsPivotChart, rDefaultLegendSize);
|
|
|
|
uno::Reference<beans::XPropertySet> xModelPage(mrModel.getPageBackground());
|
|
|
|
for (std::shared_ptr<VButton> const & pButton : aButtons)
|
|
{
|
|
// adjust the width of the buttons if we place them vertically
|
|
if (bPlaceButtonsVertically)
|
|
pButton->setSize({aLegendSize.Width - 200, pButton->getSize().Height});
|
|
|
|
// create the buttons
|
|
pButton->createShapes(xModelPage);
|
|
}
|
|
|
|
rtl::Reference<SvxShapeRect> xBorder = ShapeFactory::createRectangle(
|
|
xLegendContainer, aLegendSize, awt::Point(0, 0), aLineFillProperties.first,
|
|
aLineFillProperties.second, ShapeFactory::StackPosition::Bottom);
|
|
|
|
//because of this name this border will be used for marking the legend
|
|
ShapeFactory::setShapeName(xBorder, u"MarkHandles"_ustr);
|
|
}
|
|
}
|
|
}
|
|
catch( const uno::Exception & )
|
|
{
|
|
DBG_UNHANDLED_EXCEPTION("chart2" );
|
|
}
|
|
}
|
|
|
|
void VLegend::changePosition(
|
|
awt::Rectangle & rOutAvailableSpace,
|
|
const awt::Size & rPageSize,
|
|
const css::awt::Size & rDefaultLegendSize )
|
|
{
|
|
if(! m_xShape.is())
|
|
return;
|
|
|
|
try
|
|
{
|
|
// determine position and alignment depending on default position
|
|
awt::Size aLegendSize = m_xShape->getSize();
|
|
chart2::RelativePosition aRelativePosition;
|
|
|
|
bool bDefaultLegendSize = rDefaultLegendSize.Width != 0 || rDefaultLegendSize.Height != 0;
|
|
bool bAutoPosition =
|
|
! (m_xLegend->getPropertyValue( u"RelativePosition"_ustr) >>= aRelativePosition);
|
|
|
|
LegendPosition ePos = LegendPosition_LINE_END;
|
|
m_xLegend->getPropertyValue( u"AnchorPosition"_ustr) >>= ePos;
|
|
|
|
bool bOverlay = false;
|
|
m_xLegend->getPropertyValue(u"Overlay"_ustr) >>= bOverlay;
|
|
//calculate position
|
|
if( bAutoPosition )
|
|
{
|
|
// auto position: relative to remaining space
|
|
aRelativePosition = lcl_getDefaultPosition( ePos, rOutAvailableSpace, rPageSize );
|
|
awt::Point aPos = lcl_calculatePositionAndRemainingSpace(
|
|
rOutAvailableSpace, rPageSize, aRelativePosition, ePos, aLegendSize, bOverlay );
|
|
m_xShape->setPosition( aPos );
|
|
}
|
|
else
|
|
{
|
|
// manual position: relative to whole page
|
|
awt::Rectangle aAvailableSpace( 0, 0, rPageSize.Width, rPageSize.Height );
|
|
awt::Point aPos = lcl_calculatePositionAndRemainingSpace(
|
|
aAvailableSpace, rPageSize, aRelativePosition, ePos, bDefaultLegendSize ? rDefaultLegendSize : aLegendSize, bOverlay );
|
|
m_xShape->setPosition( aPos );
|
|
|
|
if (!bOverlay)
|
|
{
|
|
// calculate remaining space as if having autoposition:
|
|
aRelativePosition = lcl_getDefaultPosition( ePos, rOutAvailableSpace, rPageSize );
|
|
lcl_calculatePositionAndRemainingSpace(
|
|
rOutAvailableSpace, rPageSize, aRelativePosition, ePos, bDefaultLegendSize ? rDefaultLegendSize : aLegendSize, bOverlay );
|
|
}
|
|
}
|
|
}
|
|
catch( const uno::Exception & )
|
|
{
|
|
DBG_UNHANDLED_EXCEPTION("chart2" );
|
|
}
|
|
}
|
|
|
|
} //namespace chart
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|