blob: 8624f1b631826f5e2c8a0d1f68cb1b509e01f182 [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_chart2.hxx"
#include "VLegend.hxx"
#include "macros.hxx"
#include "PropertyMapper.hxx"
#include "CommonConverters.hxx"
#include "ObjectIdentifier.hxx"
#include "RelativePositionHelper.hxx"
#include "ShapeFactory.hxx"
#include "RelativeSizeHelper.hxx"
#include "LegendEntryProvider.hxx"
#include "chartview/DrawModelWrapper.hxx"
#include <com/sun/star/text/XTextRange.hpp>
#include <com/sun/star/text/WritingMode2.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/XPropertyState.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 <rtl/ustrbuf.hxx>
#include <svl/languageoptions.hxx>
#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;
using ::rtl::OUString;
using ::rtl::OUStringBuffer;
//.............................................................................
namespace chart
{
//.............................................................................
namespace
{
typedef ::std::pair< ::chart::tNameSequence, ::chart::tAnySequence > tPropertyValues;
typedef ::std::vector< ViewLegendEntry > tViewLegendEntryContainer;
double lcl_CalcViewFontSize(
const Reference< beans::XPropertySet > & xProp,
const awt::Size & rReferenceSize )
{
double fResult = 10.0;
awt::Size aPropRefSize;
float fFontHeight( 0.0 );
if( xProp.is() && ( xProp->getPropertyValue( C2U( "CharHeight" )) >>= fFontHeight ))
{
fResult = fFontHeight;
try
{
if( (xProp->getPropertyValue( C2U( "ReferencePageSize" )) >>= aPropRefSize) &&
(aPropRefSize.Height > 0))
{
// todo: find out if asian text is really used
// Reference< beans::XPropertySetInfo >xInfo( xProp, uno::UNO_QUERY );
// float fFontHeight2 = fFontHeight;
// if( xInfo.is() &&
// xInfo->hasPropertyByName(C2U("CharHeightAsian")) &&
// (xProp->getPropertyValue(C2U("CharHeightAsian")) >>= fFontHeight2) &&
// fFontHeight2 > fFontHeight )
// {
// fFontHeight = fFontHeight2;
// }
// if( xInfo.is() &&
// xInfo->hasPropertyByName(C2U("CharHeightComplex")) &&
// (xProp->getPropertyValue(C2U("CharHeightComplex")) >>= fFontHeight2) &&
// fFontHeight2 > fFontHeight )
// {
// fFontHeight = fFontHeight2;
// }
fResult = ::chart::RelativeSizeHelper::calculate( fFontHeight, aPropRefSize, rReferenceSize );
}
}
catch( const uno::Exception & ex )
{
ASSERT_EXCEPTION( ex );
}
}
// pt -> 1/100th mm
return (fResult * (2540.0 / 72.0));
}
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())
{
// set rOutLineFillProperties
::chart::tPropertyNameValueMap aLineFillValueMap;
::chart::PropertyMapper::getValueMap( aLineFillValueMap, ::chart::PropertyMapper::getPropertyNameMapForFillAndLineProperties(), xLegendProp );
aLineFillValueMap[ C2U("LineJoint") ] = uno::makeAny( drawing::LineJoint_ROUND );
::chart::PropertyMapper::getMultiPropertyListsFromValueMap(
rOutLineFillProperties.first, rOutLineFillProperties.second, aLineFillValueMap );
// set rOutTextProperties
::chart::tPropertyNameValueMap aTextValueMap;
::chart::PropertyMapper::getValueMap( aTextValueMap, ::chart::PropertyMapper::getPropertyNameMapForCharacterProperties(), xLegendProp );
drawing::TextHorizontalAdjust eHorizAdjust( drawing::TextHorizontalAdjust_LEFT );
aTextValueMap[ C2U("TextAutoGrowHeight") ] = uno::makeAny( sal_True );
aTextValueMap[ C2U("TextAutoGrowWidth") ] = uno::makeAny( sal_True );
aTextValueMap[ C2U("TextHorizontalAdjust") ] = uno::makeAny( eHorizAdjust );
aTextValueMap[ C2U("TextMaximumFrameWidth") ] = uno::makeAny( 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( C2U( "ReferencePageSize" )) >>= aPropRefSize) &&
(aPropRefSize.Height > 0) &&
(aTextValueMap[ C2U("CharHeight") ] >>= fFontHeight) )
{
aTextValueMap[ C2U("CharHeight") ] = uno::makeAny(
static_cast< float >(
::chart::RelativeSizeHelper::calculate( fFontHeight, aPropRefSize, rReferenceSize )));
if( aTextValueMap[ C2U("CharHeightAsian") ] >>= fFontHeight )
{
aTextValueMap[ C2U("CharHeightAsian") ] = uno::makeAny(
static_cast< float >(
::chart::RelativeSizeHelper::calculate( fFontHeight, aPropRefSize, rReferenceSize )));
}
if( aTextValueMap[ C2U("CharHeightComplex") ] >>= fFontHeight )
{
aTextValueMap[ C2U("CharHeightComplex") ] = uno::makeAny(
static_cast< float >(
::chart::RelativeSizeHelper::calculate( fFontHeight, aPropRefSize, rReferenceSize )));
}
}
::chart::PropertyMapper::getMultiPropertyListsFromValueMap(
rOutTextProperties.first, rOutTextProperties.second, aTextValueMap );
}
}
awt::Size lcl_createTextShapes(
const tViewLegendEntryContainer & rEntries,
const Reference< lang::XMultiServiceFactory > & xShapeFactory,
const Reference< drawing::XShapes > & xTarget,
::std::vector< Reference< drawing::XShape > > & rOutTextShapes,
const tPropertyValues & rTextProperties )
{
awt::Size aResult;
for( tViewLegendEntryContainer::const_iterator aIt( rEntries.begin());
aIt != rEntries.end(); ++aIt )
{
try
{
// create label shape
Reference< drawing::XShape > xEntry(
xShapeFactory->createInstance(
C2U( "com.sun.star.drawing.TextShape" )), uno::UNO_QUERY_THROW );
xTarget->add( xEntry );
// set label text
Sequence< Reference< XFormattedString > > aLabelSeq = (*aIt).aLabel;
for( sal_Int32 i = 0; i < aLabelSeq.getLength(); ++i )
{
// todo: support more than one text range
if( i == 1 )
break;
Reference< text::XTextRange > xRange( xEntry, uno::UNO_QUERY );
OUString aLabelString( aLabelSeq[i]->getString());
// workaround for Issue #i67540#
if( aLabelString.isEmpty() )
aLabelString = C2U(" ");
if( xRange.is())
xRange->setString( aLabelString );
PropertyMapper::setMultiProperties(
rTextProperties.first, rTextProperties.second,
Reference< beans::XPropertySet >( xRange, uno::UNO_QUERY ));
// 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( uno::Exception & ex )
{
ASSERT_EXCEPTION( ex );
}
}
return aResult;
}
void lcl_collectColumnWidths( std::vector< sal_Int32 >& rColumnWidths, const sal_Int32 nNumberOfRows, const sal_Int32 nNumberOfColumns
, const ::std::vector< Reference< drawing::XShape > > aTextShapes, sal_Int32 nSymbolPlusDistanceWidth )
{
rColumnWidths.clear();
sal_Int32 nRow = 0;
sal_Int32 nColumn = 0;
sal_Int32 nNumberOfEntries = aTextShapes.size();
for( ; nRow < nNumberOfRows; ++nRow )
{
for( nColumn = 0; nColumn < nNumberOfColumns; ++nColumn )
{
sal_Int32 nEntry = (nColumn + nRow * nNumberOfColumns);
if( nEntry < nNumberOfEntries )
{
awt::Size aTextSize( aTextShapes[ 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< Reference< drawing::XShape > > aTextShapes )
{
// calculate maximum height for each row
// and collect column widths
rRowHeights.clear();
sal_Int32 nRow = 0;
sal_Int32 nColumn = 0;
sal_Int32 nNumberOfEntries = aTextShapes.size();
for( ; nRow < nNumberOfRows; ++nRow )
{
sal_Int32 nCurrentRowHeight = 0;
for( nColumn = 0; nColumn < nNumberOfColumns; ++nColumn )
{
sal_Int32 nEntry = (nColumn + nRow * nNumberOfColumns);
if( nEntry < nNumberOfEntries )
{
awt::Size aTextSize( aTextShapes[ 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 );
sal_Int32 nTextLineHeight = nFontHeight;
for( sal_Int32 nR=0; nR<nNumberOfRows; nR++ )
{
sal_Int32 nFullTextHeight = aRowHeights[ nR ];
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(
tViewLegendEntryContainer & rEntries,
::com::sun::star::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 & rAvailableSpace )
{
bool bIsCustomSize = (eExpansion == ::com::sun::star::chart::ChartLegendExpansion_CUSTOM);
awt::Size aResultingLegendSize(0,0);
if( bIsCustomSize )
aResultingLegendSize = rAvailableSpace;
// #i109336# Improve auto positioning in chart
sal_Int32 nXPadding = static_cast< sal_Int32 >( std::max( 100.0, fViewFontSize * 0.33 ) );
//sal_Int32 nXPadding = static_cast< sal_Int32 >( std::max( 200.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 ) );
//sal_Int32 nYOffset = static_cast< sal_Int32 >( std::max( 230.0, fViewFontSize * 0.45 ) );
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 = rAvailableSpace.Width - (2 * nXPadding) - nSymbolPlusDistanceWidth;
rtl::OUString aPropNameTextMaximumFrameWidth( C2U("TextMaximumFrameWidth") );
uno::Any* pFrameWidthAny = PropertyMapper::getValuePointer( rTextProperties.second, rTextProperties.first, aPropNameTextMaximumFrameWidth);
if(pFrameWidthAny)
{
if( eExpansion == ::com::sun::star::chart::ChartLegendExpansion_HIGH )
{
// limit the width of texts to 30% of the total available width
// #i109336# Improve auto positioning in chart
nMaxTextWidth = rAvailableSpace.Width * 3 / 10;
}
*pFrameWidthAny = uno::makeAny(nMaxTextWidth);
}
::std::vector< Reference< drawing::XShape > > aTextShapes;
awt::Size aMaxEntryExtent = lcl_createTextShapes( rEntries, xShapeFactory, 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();
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 == ::com::sun::star::chart::ChartLegendExpansion_CUSTOM )
{
sal_Int32 nCurrentRow=0;
sal_Int32 nCurrentColumn=-1;
sal_Int32 nColumnCount=0;
sal_Int32 nMaxColumnCount=-1;
for( sal_Int32 nN=0; nN<static_cast<sal_Int32>(aTextShapes.size()); nN++ )
{
Reference< drawing::XShape > 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 wether the current column width is sufficient for the new entry
if( aColumnWidths[nCurrentColumn]>=nNewWidth )
{
//all good proceed with next entry
continue;
}
}
if( nCurrentColumn < nCurrentColumnCount )
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 nC=0; nC<nCurrentColumnCount; nC++ )
nSumWidth += aColumnWidths[nC];
if( nSumWidth <= rAvailableSpace.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;
nColumnCount=0;
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 nR=0; nR<nNumberOfRows; nR++ )
nSumHeight += aRowHeights[nR];
sal_Int32 nRemainingSpace = rAvailableSpace.Height - nSumHeight;
if( nRemainingSpace<0 )
{
//remove entries that are too big
for( sal_Int32 nR=nNumberOfRows; nR--; )
{
for( sal_Int32 nC=nNumberOfColumns; nC--; )
{
sal_Int32 nEntry = (nC + nR * nNumberOfColumns);
if( nEntry < static_cast<sal_Int32>(aTextShapes.size()) )
{
DrawModelWrapper::removeShape( aTextShapes[nEntry] );
aTextShapes.pop_back();
}
if( nEntry < nNumberOfEntries )
{
DrawModelWrapper::removeShape( rEntries[ nEntry ].aSymbol );
rEntries.pop_back();
nNumberOfEntries--;
}
}
nSumHeight -= aRowHeights[nR];
aRowHeights.pop_back();
nRemainingSpace = rAvailableSpace.Height - nSumHeight;
if( nRemainingSpace>=0 )
break;
}
nNumberOfRows = static_cast<sal_Int32>(aRowHeights.size());
}
if( nRemainingSpace > 0 )
{
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 nC=0; nC<nNumberOfColumns; nC++ )
nSumWidth += aColumnWidths[nC];
nRemainingSpace = rAvailableSpace.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 == ::com::sun::star::chart::ChartLegendExpansion_HIGH )
{
sal_Int32 nMaxNumberOfRows = nMaxEntryHeight
? (rAvailableSpace.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 == ::com::sun::star::chart::ChartLegendExpansion_WIDE )
{
sal_Int32 nMaxNumberOfColumns = nMaxEntryWidth
? (rAvailableSpace.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 // ::com::sun::star::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 != ::com::sun::star::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 = nXPadding;
sal_Int32 nCurrentYPos = nYPadding;
if( !bSymbolsLeftSide )
nCurrentXPos = -nXPadding;
// place entries into column and rows
sal_Int32 nMaxYPos = 0;
sal_Int32 nRow = 0;
sal_Int32 nColumn = 0;
for( nColumn = 0; nColumn < nNumberOfColumns; ++nColumn )
{
nCurrentYPos = nYPadding;
for( nRow = 0; nRow < nNumberOfRows; ++nRow )
{
sal_Int32 nEntry = (nColumn + nRow * nNumberOfColumns);
if( nEntry >= nNumberOfEntries )
break;
// text shape
Reference< drawing::XShape > 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
Reference< drawing::XShape > xSymbol( rEntries[ nEntry ].aSymbol );
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 = nCurrentXPos + nXPadding;
else
{
sal_Int32 nLegendWidth = -(nCurrentXPos-nXPadding);
aResultingLegendSize.Width = nLegendWidth;
}
aResultingLegendSize.Height = nMaxYPos + nYPadding;
}
if( !bSymbolsLeftSide )
{
sal_Int32 nLegendWidth = aResultingLegendSize.Width;
awt::Point aPos(0,0);
for( sal_Int32 nEntry=0; nEntry<nNumberOfEntries; nEntry++ )
{
Reference< drawing::XShape > xSymbol( rEntries[ nEntry ].aSymbol );
aPos = xSymbol->getPosition();
aPos.X += nLegendWidth;
xSymbol->setPosition( aPos );
Reference< drawing::XShape > 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 ) );
aResult = chart2::RelativePosition(
0.5, 1.0 - fDefaultDistance, drawing::Alignment_BOTTOM );
}
break;
case LegendPosition_CUSTOM:
// to avoid warning
case LegendPosition_MAKE_FIXED_SIZE:
// 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,
chart2::RelativePosition aRelPos,
LegendPosition ePos,
const awt::Size& aLegendSize )
{
// calculate position
awt::Point aResult(
static_cast< sal_Int32 >( aRelPos.Primary * rPageSize.Width ),
static_cast< sal_Int32 >( aRelPos.Secondary * rPageSize.Height ));
aResult = RelativePositionHelper::getUpperLeftCornerOfAnchoredObject(
aResult, aLegendSize, aRelPos.Anchor );
// adapt rRemainingSpace if LegendPosition is not CUSTOM
// #i109336# Improve auto positioning in chart
sal_Int32 nXDistance = lcl_getLegendLeftRightMargin();
sal_Int32 nYDistance = lcl_getLegendTopBottomMargin();
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( SvtLanguageOptions().IsCTLFontEnabled() )
{
if(xLegendProp.is())
{
sal_Int16 nWritingMode=-1;
if( (xLegendProp->getPropertyValue( C2U("WritingMode") ) >>= nWritingMode) )
{
if( nWritingMode == text::WritingMode2::PAGE )
nWritingMode = nDefaultWritingMode;
if( nWritingMode == text::WritingMode2::RL_TB )
bSymbolsLeftSide=false;
}
}
}
}
catch( uno::Exception & ex )
{
ASSERT_EXCEPTION( ex );
}
return bSymbolsLeftSide;
}
} // anonymous namespace
VLegend::VLegend(
const Reference< XLegend > & xLegend,
const Reference< uno::XComponentContext > & xContext,
const std::vector< LegendEntryProvider* >& rLegendEntryProviderList ) :
m_xLegend( xLegend ),
m_xContext( xContext ),
m_aLegendEntryProviderList( rLegendEntryProviderList )
{
}
// ----------------------------------------
void VLegend::init(
const Reference< drawing::XShapes >& xTargetPage,
const Reference< lang::XMultiServiceFactory >& xFactory,
const Reference< frame::XModel >& xModel )
{
m_xTarget = xTargetPage;
m_xShapeFactory = xFactory;
m_xModel = xModel;
}
// ----------------------------------------
void VLegend::setDefaultWritingMode( sal_Int16 nDefaultWritingMode )
{
m_nDefaultWritingMode = nDefaultWritingMode;
}
// ----------------------------------------
bool VLegend::isVisible( const Reference< XLegend > & xLegend )
{
if( ! xLegend.is())
return sal_False;
sal_Bool bShow = sal_False;
try
{
Reference< beans::XPropertySet > xLegendProp( xLegend, uno::UNO_QUERY_THROW );
xLegendProp->getPropertyValue( C2U( "Show" )) >>= bShow;
}
catch( uno::Exception & ex )
{
ASSERT_EXCEPTION( ex );
}
return bShow;
}
// ----------------------------------------
void VLegend::createShapes(
const awt::Size & rAvailableSpace,
const awt::Size & rPageSize )
{
if(! (m_xLegend.is() &&
m_xShapeFactory.is() &&
m_xTarget.is()))
return;
try
{
//create shape and add to page
m_xShape.set( m_xShapeFactory->createInstance(
C2U( "com.sun.star.drawing.GroupShape" )), uno::UNO_QUERY );
m_xTarget->add( m_xShape );
// set name to enable selection
{
OUString aLegendParticle( ObjectIdentifier::createParticleForLegend( m_xLegend, m_xModel ) );
ShapeFactory::setShapeName( m_xShape, ObjectIdentifier::createClassifiedIdentifierForParticle( aLegendParticle ) );
}
// create and insert sub-shapes
Reference< drawing::XShapes > xLegendContainer( m_xShape, uno::UNO_QUERY );
if( xLegendContainer.is())
{
Reference< drawing::XShape > xBorder(
m_xShapeFactory->createInstance(
C2U( "com.sun.star.drawing.RectangleShape" )), uno::UNO_QUERY );
// for quickly setting properties
tPropertyValues aLineFillProperties;
tPropertyValues aTextProperties;
Reference< beans::XPropertySet > xLegendProp( m_xLegend, uno::UNO_QUERY );
::com::sun::star::chart::ChartLegendExpansion eExpansion = ::com::sun::star::chart::ChartLegendExpansion_HIGH;
awt::Size aLegendSize( rAvailableSpace );
if( xLegendProp.is())
{
// get Expansion property
xLegendProp->getPropertyValue( C2U( "Expansion" )) >>= eExpansion;
if( eExpansion == ::com::sun::star::chart::ChartLegendExpansion_CUSTOM )
{
RelativeSize aRelativeSize;
if ((xLegendProp->getPropertyValue( C2U( "RelativeSize" )) >>= aRelativeSize))
{
aLegendSize.Width = static_cast<sal_Int32>( ::rtl::math::approxCeil( aRelativeSize.Primary * rPageSize.Width ) ); //i117185
aLegendSize.Height = static_cast<sal_Int32>( ::rtl::math::approxCeil( aRelativeSize.Secondary * rPageSize.Height ) ); //i117185
}
else
eExpansion = ::com::sun::star::chart::ChartLegendExpansion_HIGH;
}
lcl_getProperties( xLegendProp, aLineFillProperties, aTextProperties, rPageSize );
}
if( xBorder.is())
{
xLegendContainer->add( xBorder );
// apply legend properties
PropertyMapper::setMultiProperties(
aLineFillProperties.first, aLineFillProperties.second,
Reference< beans::XPropertySet >( xBorder, uno::UNO_QUERY ));
//because of this name this border will be used for marking the legend
ShapeFactory(m_xShapeFactory).setShapeName( xBorder, C2U("MarkHandles") );
}
// create entries
double fViewFontSize = lcl_CalcViewFontSize( xLegendProp, rPageSize );//todo
// #i109336# Improve auto positioning in chart
sal_Int32 nSymbolHeigth = static_cast< sal_Int32 >( fViewFontSize * 0.6 );
sal_Int32 nSymbolWidth = static_cast< sal_Int32 >( nSymbolHeigth );
::std::vector< LegendEntryProvider* >::const_iterator aIter = m_aLegendEntryProviderList.begin();
const ::std::vector< LegendEntryProvider* >::const_iterator aEnd = m_aLegendEntryProviderList.end();
for( aIter = m_aLegendEntryProviderList.begin(); aIter != aEnd; aIter++ )
{
LegendEntryProvider* pLegendEntryProvider( *aIter );
if( pLegendEntryProvider )
{
awt::Size aCurrentRatio = pLegendEntryProvider->getPreferredLegendKeyAspectRatio();
sal_Int32 nCurrentWidth = aCurrentRatio.Width;
if( aCurrentRatio.Height > 0 )
{
nCurrentWidth = nSymbolHeigth* aCurrentRatio.Width/aCurrentRatio.Height;
}
nSymbolWidth = std::max( nSymbolWidth, nCurrentWidth );
}
}
awt::Size aMaxSymbolExtent( nSymbolWidth, nSymbolHeigth );
tViewLegendEntryContainer aViewEntries;
for( aIter = m_aLegendEntryProviderList.begin(); aIter != aEnd; aIter++ )
{
LegendEntryProvider* pLegendEntryProvider( *aIter );
if( pLegendEntryProvider )
{
std::vector< ViewLegendEntry > aNewEntries = pLegendEntryProvider->createLegendEntries( aMaxSymbolExtent, eExpansion, xLegendProp, xLegendContainer, m_xShapeFactory, m_xContext );
aViewEntries.insert( aViewEntries.end(), aNewEntries.begin(), aNewEntries.end() );
}
}
bool bSymbolsLeftSide = lcl_shouldSymbolsBePlacedOnTheLeftSide( xLegendProp, m_nDefaultWritingMode );
// place entries
aLegendSize = lcl_placeLegendEntries( aViewEntries, eExpansion, bSymbolsLeftSide, fViewFontSize, aMaxSymbolExtent
, aTextProperties, xLegendContainer, m_xShapeFactory, aLegendSize );
if( xBorder.is() )
xBorder->setSize( aLegendSize );
}
}
catch( uno::Exception & ex )
{
ASSERT_EXCEPTION( ex );
}
}
// ----------------------------------------
void VLegend::changePosition(
awt::Rectangle & rOutAvailableSpace,
const awt::Size & rPageSize )
{
if(! m_xShape.is())
return;
try
{
// determine position and alignment depending on default position
awt::Size aLegendSize = m_xShape->getSize();
Reference< beans::XPropertySet > xLegendProp( m_xLegend, uno::UNO_QUERY_THROW );
chart2::RelativePosition aRelativePosition;
bool bAutoPosition =
! (xLegendProp->getPropertyValue( C2U( "RelativePosition" )) >>= aRelativePosition);
LegendPosition ePos = LegendPosition_CUSTOM;
xLegendProp->getPropertyValue( C2U( "AnchorPosition" )) >>= ePos;
//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 );
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, aLegendSize );
m_xShape->setPosition( aPos );
if( ePos != LegendPosition_CUSTOM )
{
// calculate remaining space as if having autoposition:
aRelativePosition = lcl_getDefaultPosition( ePos, rOutAvailableSpace, rPageSize );
lcl_calculatePositionAndRemainingSpace(
rOutAvailableSpace, rPageSize, aRelativePosition, ePos, aLegendSize );
}
}
}
catch( uno::Exception & ex )
{
ASSERT_EXCEPTION( ex );
}
}
//.............................................................................
} //namespace chart
//.............................................................................