summaryrefslogtreecommitdiffstats
path: root/chart2/source/view/main
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /chart2/source/view/main
parentInitial commit. (diff)
downloadlibreoffice-upstream/4%7.4.7.tar.xz
libreoffice-upstream/4%7.4.7.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--chart2/source/view/main/AxisUsage.hxx143
-rw-r--r--chart2/source/view/main/ChartItemPool.cxx226
-rw-r--r--chart2/source/view/main/ChartItemPool.hxx48
-rw-r--r--chart2/source/view/main/ChartView.cxx2035
-rw-r--r--chart2/source/view/main/Clipping.cxx425
-rw-r--r--chart2/source/view/main/DataPointSymbolSupplier.cxx44
-rw-r--r--chart2/source/view/main/DrawModelWrapper.cxx335
-rw-r--r--chart2/source/view/main/ExplicitValueProvider.cxx211
-rw-r--r--chart2/source/view/main/LabelPositionHelper.cxx467
-rw-r--r--chart2/source/view/main/Linear3DTransformation.cxx124
-rw-r--r--chart2/source/view/main/PlotterBase.cxx106
-rw-r--r--chart2/source/view/main/PlottingPositionHelper.cxx708
-rw-r--r--chart2/source/view/main/PolarLabelPositionHelper.cxx158
-rw-r--r--chart2/source/view/main/PropertyMapper.cxx561
-rw-r--r--chart2/source/view/main/SeriesPlotterContainer.cxx739
-rw-r--r--chart2/source/view/main/SeriesPlotterContainer.hxx159
-rw-r--r--chart2/source/view/main/ShapeFactory.cxx2551
-rw-r--r--chart2/source/view/main/Stripe.cxx347
-rw-r--r--chart2/source/view/main/VButton.cxx136
-rw-r--r--chart2/source/view/main/VButton.hxx86
-rw-r--r--chart2/source/view/main/VDataSeries.cxx1118
-rw-r--r--chart2/source/view/main/VLegend.cxx1099
-rw-r--r--chart2/source/view/main/VLegend.hxx91
-rw-r--r--chart2/source/view/main/VLegendSymbolFactory.cxx188
-rw-r--r--chart2/source/view/main/VLineProperties.cxx85
-rw-r--r--chart2/source/view/main/VPolarTransformation.cxx88
-rw-r--r--chart2/source/view/main/VTitle.cxx158
-rw-r--r--chart2/source/view/main/VTitle.hxx72
28 files changed, 12508 insertions, 0 deletions
diff --git a/chart2/source/view/main/AxisUsage.hxx b/chart2/source/view/main/AxisUsage.hxx
new file mode 100644
index 000000000..834518707
--- /dev/null
+++ b/chart2/source/view/main/AxisUsage.hxx
@@ -0,0 +1,143 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <memory>
+#include <map>
+
+#include <VCoordinateSystem.hxx>
+#include <AxisHelper.hxx>
+#include <ScaleAutomatism.hxx>
+
+namespace chart
+{
+//first index is the dimension, second index is the axis index that indicates whether this is a main or secondary axis
+typedef std::pair<sal_Int32, sal_Int32> tFullAxisIndex;
+typedef std::map<VCoordinateSystem*, tFullAxisIndex> tCoordinateSystemMap;
+
+/** This class handles a collection of coordinate systems and is used for
+ * executing some action on all coordinate systems such as
+ * "prepareAutomaticAxisScaling" and "setExplicitScaleAndIncrement".
+ * Moreover it contains the "aAutoScaling" object that is an instance of
+ * the "ScaleAutomatism" class. The initialization of "aAutoScaling" is
+ * performed in the "SeriesPlotterContainer::initAxisUsageList" method and is
+ * used in the "SeriesPlotterContainer::doAutoScaling" for calculating explicit
+ * scale and increment objects (see "SeriesPlotterContainer::doAutoScaling").
+ */
+class AxisUsage
+{
+public:
+ AxisUsage()
+ : aAutoScaling(AxisHelper::createDefaultScale(), Date(Date::SYSTEM))
+ {
+ }
+
+ void addCoordinateSystem(VCoordinateSystem* pCooSys, sal_Int32 nDimensionIndex,
+ sal_Int32 nAxisIndex)
+ {
+ if (!pCooSys)
+ return;
+
+ tFullAxisIndex aFullAxisIndex(nDimensionIndex, nAxisIndex);
+ tCoordinateSystemMap::const_iterator aFound(aCoordinateSystems.find(pCooSys));
+
+ //use one scale only once for each coordinate system
+ //main axis are preferred over secondary axis
+ //value scales are preferred
+ if (aFound != aCoordinateSystems.end())
+ {
+ sal_Int32 nFoundAxisIndex = aFound->second.second;
+ if (nFoundAxisIndex < nAxisIndex)
+ return;
+ sal_Int32 nFoundDimension = aFound->second.first;
+ if (nFoundDimension == 1)
+ return;
+ if (nFoundDimension < nDimensionIndex)
+ return;
+ }
+ aCoordinateSystems[pCooSys] = aFullAxisIndex;
+
+ //set maximum scale index
+ auto aIter = aMaxIndexPerDimension.find(nDimensionIndex);
+ if (aIter != aMaxIndexPerDimension.end())
+ {
+ sal_Int32 nCurrentMaxIndex = aIter->second;
+ if (nCurrentMaxIndex < nAxisIndex)
+ aMaxIndexPerDimension[nDimensionIndex] = nAxisIndex;
+ }
+ else
+ aMaxIndexPerDimension[nDimensionIndex] = nAxisIndex;
+ }
+
+ std::vector<VCoordinateSystem*> getCoordinateSystems(sal_Int32 nDimensionIndex,
+ sal_Int32 nAxisIndex)
+ {
+ std::vector<VCoordinateSystem*> aRet;
+
+ for (auto const& coordinateSystem : aCoordinateSystems)
+ {
+ if (coordinateSystem.second.first != nDimensionIndex)
+ continue;
+ if (coordinateSystem.second.second != nAxisIndex)
+ continue;
+ aRet.push_back(coordinateSystem.first);
+ }
+
+ return aRet;
+ }
+
+ sal_Int32 getMaxAxisIndexForDimension(sal_Int32 nDimensionIndex)
+ {
+ sal_Int32 nRet = -1;
+ auto aIter = aMaxIndexPerDimension.find(nDimensionIndex);
+ if (aIter != aMaxIndexPerDimension.end())
+ nRet = aIter->second;
+ return nRet;
+ }
+
+ void prepareAutomaticAxisScaling(ScaleAutomatism& rScaleAutomatism, sal_Int32 nDimIndex,
+ sal_Int32 nAxisIndex)
+ {
+ std::vector<VCoordinateSystem*> aVCooSysList = getCoordinateSystems(nDimIndex, nAxisIndex);
+ for (VCoordinateSystem* pVCoordinateSystem : aVCooSysList)
+ pVCoordinateSystem->prepareAutomaticAxisScaling(rScaleAutomatism, nDimIndex,
+ nAxisIndex);
+ }
+
+ void setExplicitScaleAndIncrement(sal_Int32 nDimIndex, sal_Int32 nAxisIndex,
+ const ExplicitScaleData& rScale,
+ const ExplicitIncrementData& rInc)
+ {
+ std::vector<VCoordinateSystem*> aVCooSysList = getCoordinateSystems(nDimIndex, nAxisIndex);
+ for (VCoordinateSystem* pVCoordinateSystem : aVCooSysList)
+ pVCoordinateSystem->setExplicitScaleAndIncrement(nDimIndex, nAxisIndex, rScale, rInc);
+ }
+
+ ScaleAutomatism aAutoScaling;
+
+private:
+ tCoordinateSystemMap aCoordinateSystems;
+ std::map<sal_Int32, sal_Int32> aMaxIndexPerDimension;
+};
+
+} //end chart2 namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/ChartItemPool.cxx b/chart2/source/view/main/ChartItemPool.cxx
new file mode 100644
index 000000000..3410bcdbf
--- /dev/null
+++ b/chart2/source/view/main/ChartItemPool.cxx
@@ -0,0 +1,226 @@
+/* -*- 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 "ChartItemPool.hxx"
+#include <chartview/ChartSfxItemIds.hxx>
+#include <svx/chrtitem.hxx>
+#include <svx/sdangitm.hxx>
+#include <svx/svdpool.hxx>
+#include <svx/svx3ditems.hxx>
+#include <svl/intitem.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/sizeitem.hxx>
+#include <svl/stritem.hxx>
+#include <svl/ilstitem.hxx>
+#include <comphelper/processfactory.hxx>
+#include <editeng/editids.hrc>
+#include <svx/svxids.hrc>
+#include <vector>
+
+#include <com/sun/star/chart2/LegendPosition.hpp>
+#include <com/sun/star/chart2/MovingAverageType.hpp>
+#include <com/sun/star/frame/XTerminateListener.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+
+namespace chart
+{
+
+ChartItemPool::ChartItemPool():
+ SfxItemPool( "ChartItemPool" , SCHATTR_START, SCHATTR_END, nullptr, nullptr ),
+ pItemInfos(new SfxItemInfo[SCHATTR_END - SCHATTR_START + 1])
+{
+ /**************************************************************************
+ * PoolDefaults
+ **************************************************************************/
+ std::vector<SfxPoolItem*>* ppPoolDefaults = new std::vector<SfxPoolItem*>(SCHATTR_END - SCHATTR_START + 1);
+ std::vector<SfxPoolItem*>& rPoolDefaults = *ppPoolDefaults;
+ rPoolDefaults[SCHATTR_DATADESCR_SHOW_NUMBER - SCHATTR_START] = new SfxBoolItem(SCHATTR_DATADESCR_SHOW_NUMBER);
+ rPoolDefaults[SCHATTR_DATADESCR_SHOW_PERCENTAGE- SCHATTR_START] = new SfxBoolItem(SCHATTR_DATADESCR_SHOW_PERCENTAGE);
+ rPoolDefaults[SCHATTR_DATADESCR_SHOW_CATEGORY - SCHATTR_START] = new SfxBoolItem(SCHATTR_DATADESCR_SHOW_CATEGORY);
+ rPoolDefaults[SCHATTR_DATADESCR_SHOW_SYMBOL - SCHATTR_START] = new SfxBoolItem(SCHATTR_DATADESCR_SHOW_SYMBOL);
+ rPoolDefaults[SCHATTR_DATADESCR_SHOW_DATA_SERIES_NAME - SCHATTR_START] = new SfxBoolItem(SCHATTR_DATADESCR_SHOW_DATA_SERIES_NAME);
+ rPoolDefaults[SCHATTR_DATADESCR_WRAP_TEXT - SCHATTR_START] = new SfxBoolItem(SCHATTR_DATADESCR_WRAP_TEXT);
+ rPoolDefaults[SCHATTR_DATADESCR_SEPARATOR - SCHATTR_START] = new SfxStringItem(SCHATTR_DATADESCR_SEPARATOR," ");
+ rPoolDefaults[SCHATTR_DATADESCR_PLACEMENT - SCHATTR_START] = new SfxInt32Item(SCHATTR_DATADESCR_PLACEMENT,0);
+ rPoolDefaults[SCHATTR_DATADESCR_AVAILABLE_PLACEMENTS - SCHATTR_START] = new SfxIntegerListItem(SCHATTR_DATADESCR_AVAILABLE_PLACEMENTS, std::vector < sal_Int32 >() );
+ rPoolDefaults[SCHATTR_DATADESCR_NO_PERCENTVALUE - SCHATTR_START] = new SfxBoolItem(SCHATTR_DATADESCR_NO_PERCENTVALUE);
+ rPoolDefaults[SCHATTR_DATADESCR_CUSTOM_LEADER_LINES - SCHATTR_START] = new SfxBoolItem(SCHATTR_DATADESCR_CUSTOM_LEADER_LINES, true);
+ rPoolDefaults[SCHATTR_PERCENT_NUMBERFORMAT_VALUE - SCHATTR_START] = new SfxUInt32Item(SCHATTR_PERCENT_NUMBERFORMAT_VALUE, 0);
+ rPoolDefaults[SCHATTR_PERCENT_NUMBERFORMAT_SOURCE - SCHATTR_START] = new SfxBoolItem(SCHATTR_PERCENT_NUMBERFORMAT_SOURCE);
+
+ //legend
+ rPoolDefaults[SCHATTR_LEGEND_POS - SCHATTR_START] = new SfxInt32Item(SCHATTR_LEGEND_POS, sal_Int32(css::chart2::LegendPosition_LINE_END) );
+ rPoolDefaults[SCHATTR_LEGEND_SHOW - SCHATTR_START] = new SfxBoolItem(SCHATTR_LEGEND_SHOW, true);
+ rPoolDefaults[SCHATTR_LEGEND_NO_OVERLAY - SCHATTR_START] = new SfxBoolItem(SCHATTR_LEGEND_NO_OVERLAY, true);
+
+ //text
+ rPoolDefaults[SCHATTR_TEXT_DEGREES - SCHATTR_START] = new SdrAngleItem(SCHATTR_TEXT_DEGREES, 0_deg100);
+ rPoolDefaults[SCHATTR_TEXT_STACKED - SCHATTR_START] = new SfxBoolItem(SCHATTR_TEXT_STACKED,false);
+
+ //statistic
+ rPoolDefaults[SCHATTR_STAT_AVERAGE - SCHATTR_START] = new SfxBoolItem (SCHATTR_STAT_AVERAGE);
+ rPoolDefaults[SCHATTR_STAT_KIND_ERROR - SCHATTR_START] = new SvxChartKindErrorItem (SvxChartKindError::NONE, SCHATTR_STAT_KIND_ERROR);
+ rPoolDefaults[SCHATTR_STAT_PERCENT - SCHATTR_START] = new SvxDoubleItem (0.0, SCHATTR_STAT_PERCENT);
+ rPoolDefaults[SCHATTR_STAT_BIGERROR - SCHATTR_START] = new SvxDoubleItem (0.0, SCHATTR_STAT_BIGERROR);
+ rPoolDefaults[SCHATTR_STAT_CONSTPLUS - SCHATTR_START] = new SvxDoubleItem (0.0, SCHATTR_STAT_CONSTPLUS);
+ rPoolDefaults[SCHATTR_STAT_CONSTMINUS - SCHATTR_START] = new SvxDoubleItem (0.0, SCHATTR_STAT_CONSTMINUS);
+ rPoolDefaults[SCHATTR_STAT_INDICATE - SCHATTR_START] = new SvxChartIndicateItem (SvxChartIndicate::NONE, SCHATTR_STAT_INDICATE);
+ rPoolDefaults[SCHATTR_STAT_RANGE_POS - SCHATTR_START] = new SfxStringItem (SCHATTR_STAT_RANGE_POS, OUString());
+ rPoolDefaults[SCHATTR_STAT_RANGE_NEG - SCHATTR_START] = new SfxStringItem (SCHATTR_STAT_RANGE_NEG, OUString());
+ rPoolDefaults[SCHATTR_STAT_ERRORBAR_TYPE - SCHATTR_START] = new SfxBoolItem(SCHATTR_STAT_ERRORBAR_TYPE, true);
+
+ rPoolDefaults[SCHATTR_STYLE_DEEP - SCHATTR_START] = new SfxBoolItem (SCHATTR_STYLE_DEEP, false);
+ rPoolDefaults[SCHATTR_STYLE_3D - SCHATTR_START] = new SfxBoolItem (SCHATTR_STYLE_3D, false);
+ rPoolDefaults[SCHATTR_STYLE_VERTICAL - SCHATTR_START] = new SfxBoolItem (SCHATTR_STYLE_VERTICAL, false);
+ rPoolDefaults[SCHATTR_STYLE_BASETYPE - SCHATTR_START] = new SfxInt32Item(SCHATTR_STYLE_BASETYPE, 0);
+ rPoolDefaults[SCHATTR_STYLE_LINES - SCHATTR_START] = new SfxBoolItem (SCHATTR_STYLE_LINES, false);
+ rPoolDefaults[SCHATTR_STYLE_PERCENT - SCHATTR_START] = new SfxBoolItem (SCHATTR_STYLE_PERCENT, false);
+ rPoolDefaults[SCHATTR_STYLE_STACKED - SCHATTR_START] = new SfxBoolItem (SCHATTR_STYLE_STACKED, false);
+ rPoolDefaults[SCHATTR_STYLE_SPLINES - SCHATTR_START] = new SfxInt32Item (SCHATTR_STYLE_SPLINES, 0); //Bug: was Bool! test ->Fileformat (touches only 5's)
+ rPoolDefaults[SCHATTR_STYLE_SYMBOL - SCHATTR_START] = new SfxInt32Item (SCHATTR_STYLE_SYMBOL, 0);
+ rPoolDefaults[SCHATTR_STYLE_SHAPE - SCHATTR_START] = new SfxInt32Item (SCHATTR_STYLE_SHAPE, 0);
+
+ rPoolDefaults[SCHATTR_AXIS - SCHATTR_START] = new SfxInt32Item(SCHATTR_AXIS,2); //2 = Y-Axis!!!
+
+ //axis scale
+ rPoolDefaults[SCHATTR_AXISTYPE - SCHATTR_START] = new SfxInt32Item(SCHATTR_AXISTYPE, CHART_AXIS_REALNUMBER);
+ rPoolDefaults[SCHATTR_AXIS_REVERSE - SCHATTR_START] = new SfxBoolItem(SCHATTR_AXIS_REVERSE,false);
+ rPoolDefaults[SCHATTR_AXIS_AUTO_MIN - SCHATTR_START] = new SfxBoolItem(SCHATTR_AXIS_AUTO_MIN);
+ rPoolDefaults[SCHATTR_AXIS_MIN - SCHATTR_START] = new SvxDoubleItem(0.0, SCHATTR_AXIS_MIN);
+ rPoolDefaults[SCHATTR_AXIS_AUTO_MAX - SCHATTR_START] = new SfxBoolItem(SCHATTR_AXIS_AUTO_MAX);
+ rPoolDefaults[SCHATTR_AXIS_MAX - SCHATTR_START] = new SvxDoubleItem(0.0, SCHATTR_AXIS_MAX);
+ rPoolDefaults[SCHATTR_AXIS_AUTO_STEP_MAIN - SCHATTR_START] = new SfxBoolItem(SCHATTR_AXIS_AUTO_STEP_MAIN);
+ rPoolDefaults[SCHATTR_AXIS_STEP_MAIN - SCHATTR_START] = new SvxDoubleItem(0.0, SCHATTR_AXIS_STEP_MAIN);
+ rPoolDefaults[SCHATTR_AXIS_MAIN_TIME_UNIT - SCHATTR_START] = new SfxInt32Item(SCHATTR_AXIS_MAIN_TIME_UNIT,2);
+ rPoolDefaults[SCHATTR_AXIS_AUTO_STEP_HELP - SCHATTR_START] = new SfxBoolItem(SCHATTR_AXIS_AUTO_STEP_HELP);
+ rPoolDefaults[SCHATTR_AXIS_STEP_HELP - SCHATTR_START] = new SfxInt32Item(SCHATTR_AXIS_STEP_HELP,0);
+ rPoolDefaults[SCHATTR_AXIS_HELP_TIME_UNIT - SCHATTR_START] = new SfxInt32Item(SCHATTR_AXIS_HELP_TIME_UNIT,2);
+ rPoolDefaults[SCHATTR_AXIS_AUTO_TIME_RESOLUTION - SCHATTR_START] = new SfxBoolItem(SCHATTR_AXIS_AUTO_TIME_RESOLUTION);
+ rPoolDefaults[SCHATTR_AXIS_TIME_RESOLUTION - SCHATTR_START] = new SfxInt32Item(SCHATTR_AXIS_TIME_RESOLUTION,2);
+ rPoolDefaults[SCHATTR_AXIS_LOGARITHM - SCHATTR_START] = new SfxBoolItem(SCHATTR_AXIS_LOGARITHM);
+ rPoolDefaults[SCHATTR_AXIS_AUTO_DATEAXIS - SCHATTR_START] = new SfxBoolItem(SCHATTR_AXIS_AUTO_DATEAXIS);
+ rPoolDefaults[SCHATTR_AXIS_ALLOW_DATEAXIS - SCHATTR_START] = new SfxBoolItem(SCHATTR_AXIS_ALLOW_DATEAXIS);
+ rPoolDefaults[SCHATTR_AXIS_AUTO_ORIGIN - SCHATTR_START] = new SfxBoolItem(SCHATTR_AXIS_AUTO_ORIGIN);
+ rPoolDefaults[SCHATTR_AXIS_ORIGIN - SCHATTR_START] = new SvxDoubleItem(0.0, SCHATTR_AXIS_ORIGIN);
+
+ //axis position
+ rPoolDefaults[SCHATTR_AXIS_TICKS - SCHATTR_START] = new SfxInt32Item(SCHATTR_AXIS_TICKS,CHAXIS_MARK_OUTER);
+ rPoolDefaults[SCHATTR_AXIS_HELPTICKS - SCHATTR_START] = new SfxInt32Item(SCHATTR_AXIS_HELPTICKS,0);
+ rPoolDefaults[SCHATTR_AXIS_POSITION - SCHATTR_START] = new SfxInt32Item(SCHATTR_AXIS_POSITION,0);
+ rPoolDefaults[SCHATTR_AXIS_POSITION_VALUE - SCHATTR_START] = new SvxDoubleItem(0.0, SCHATTR_AXIS_POSITION_VALUE);
+ rPoolDefaults[SCHATTR_AXIS_CROSSING_MAIN_AXIS_NUMBERFORMAT - SCHATTR_START] = new SfxUInt32Item(SCHATTR_AXIS_CROSSING_MAIN_AXIS_NUMBERFORMAT,0);
+ rPoolDefaults[SCHATTR_AXIS_SHIFTED_CATEGORY_POSITION - SCHATTR_START] = new SfxBoolItem(SCHATTR_AXIS_SHIFTED_CATEGORY_POSITION,false);
+ rPoolDefaults[SCHATTR_AXIS_LABEL_POSITION - SCHATTR_START] = new SfxInt32Item(SCHATTR_AXIS_LABEL_POSITION,0);
+ rPoolDefaults[SCHATTR_AXIS_MARK_POSITION - SCHATTR_START] = new SfxInt32Item(SCHATTR_AXIS_MARK_POSITION,0);
+
+ //axis label
+ rPoolDefaults[SCHATTR_AXIS_SHOWDESCR - SCHATTR_START] = new SfxBoolItem(SCHATTR_AXIS_SHOWDESCR,false);
+ rPoolDefaults[SCHATTR_AXIS_LABEL_ORDER - SCHATTR_START] = new SvxChartTextOrderItem(SvxChartTextOrder::SideBySide, SCHATTR_AXIS_LABEL_ORDER);
+ rPoolDefaults[SCHATTR_AXIS_LABEL_OVERLAP - SCHATTR_START] = new SfxBoolItem(SCHATTR_AXIS_LABEL_OVERLAP,false);
+ rPoolDefaults[SCHATTR_AXIS_LABEL_BREAK - SCHATTR_START] = new SfxBoolItem(SCHATTR_AXIS_LABEL_BREAK, false );
+
+ rPoolDefaults[SCHATTR_SYMBOL_BRUSH - SCHATTR_START] = new SvxBrushItem(SCHATTR_SYMBOL_BRUSH);
+ rPoolDefaults[SCHATTR_STOCK_VOLUME - SCHATTR_START] = new SfxBoolItem(SCHATTR_STOCK_VOLUME,false);
+ rPoolDefaults[SCHATTR_STOCK_UPDOWN - SCHATTR_START] = new SfxBoolItem(SCHATTR_STOCK_UPDOWN,false);
+ rPoolDefaults[SCHATTR_SYMBOL_SIZE - SCHATTR_START] = new SvxSizeItem(SCHATTR_SYMBOL_SIZE,Size(0,0));
+ rPoolDefaults[SCHATTR_HIDE_DATA_POINT_LEGEND_ENTRY - SCHATTR_START] = new SfxBoolItem(SCHATTR_HIDE_DATA_POINT_LEGEND_ENTRY, false);
+
+ // new for New Chart
+ rPoolDefaults[SCHATTR_BAR_OVERLAP - SCHATTR_START] = new SfxInt32Item(SCHATTR_BAR_OVERLAP,0);
+ rPoolDefaults[SCHATTR_BAR_GAPWIDTH - SCHATTR_START] = new SfxInt32Item(SCHATTR_BAR_GAPWIDTH,0);
+ rPoolDefaults[SCHATTR_BAR_CONNECT - SCHATTR_START] = new SfxBoolItem(SCHATTR_BAR_CONNECT, false);
+ rPoolDefaults[SCHATTR_NUM_OF_LINES_FOR_BAR - SCHATTR_START] = new SfxInt32Item( SCHATTR_NUM_OF_LINES_FOR_BAR, 0 );
+ rPoolDefaults[SCHATTR_SPLINE_ORDER - SCHATTR_START] = new SfxInt32Item( SCHATTR_SPLINE_ORDER, 3 );
+ rPoolDefaults[SCHATTR_SPLINE_RESOLUTION - SCHATTR_START] = new SfxInt32Item( SCHATTR_SPLINE_RESOLUTION, 20 );
+ rPoolDefaults[SCHATTR_GROUP_BARS_PER_AXIS - SCHATTR_START] = new SfxBoolItem(SCHATTR_GROUP_BARS_PER_AXIS, false);
+ rPoolDefaults[SCHATTR_STARTING_ANGLE - SCHATTR_START] = new SdrAngleItem( SCHATTR_STARTING_ANGLE, 9000_deg100 );
+ rPoolDefaults[SCHATTR_CLOCKWISE - SCHATTR_START] = new SfxBoolItem( SCHATTR_CLOCKWISE, false );
+
+ rPoolDefaults[SCHATTR_MISSING_VALUE_TREATMENT - SCHATTR_START] = new SfxInt32Item(SCHATTR_MISSING_VALUE_TREATMENT, 0);
+ rPoolDefaults[SCHATTR_AVAILABLE_MISSING_VALUE_TREATMENTS - SCHATTR_START] = new SfxIntegerListItem(SCHATTR_AVAILABLE_MISSING_VALUE_TREATMENTS, std::vector < sal_Int32 >() );
+ rPoolDefaults[SCHATTR_INCLUDE_HIDDEN_CELLS - SCHATTR_START] = new SfxBoolItem(SCHATTR_INCLUDE_HIDDEN_CELLS, true);
+ rPoolDefaults[SCHATTR_HIDE_LEGEND_ENTRY - SCHATTR_START] = new SfxBoolItem(SCHATTR_HIDE_LEGEND_ENTRY, false);
+
+ rPoolDefaults[SCHATTR_AXIS_FOR_ALL_SERIES - SCHATTR_START] = new SfxInt32Item(SCHATTR_AXIS_FOR_ALL_SERIES, 0);
+
+ rPoolDefaults[SCHATTR_REGRESSION_TYPE - SCHATTR_START] = new SvxChartRegressItem (SvxChartRegress::NONE, SCHATTR_REGRESSION_TYPE);
+ rPoolDefaults[SCHATTR_REGRESSION_SHOW_EQUATION - SCHATTR_START] = new SfxBoolItem(SCHATTR_REGRESSION_SHOW_EQUATION, false);
+ rPoolDefaults[SCHATTR_REGRESSION_SHOW_COEFF - SCHATTR_START] = new SfxBoolItem(SCHATTR_REGRESSION_SHOW_COEFF, false);
+ rPoolDefaults[SCHATTR_REGRESSION_DEGREE - SCHATTR_START] = new SfxInt32Item(SCHATTR_REGRESSION_DEGREE, 2);
+ rPoolDefaults[SCHATTR_REGRESSION_PERIOD - SCHATTR_START] = new SfxInt32Item(SCHATTR_REGRESSION_PERIOD, 2);
+ rPoolDefaults[SCHATTR_REGRESSION_EXTRAPOLATE_FORWARD - SCHATTR_START] = new SvxDoubleItem(0.0, SCHATTR_REGRESSION_EXTRAPOLATE_FORWARD);
+ rPoolDefaults[SCHATTR_REGRESSION_EXTRAPOLATE_BACKWARD - SCHATTR_START] = new SvxDoubleItem(0.0, SCHATTR_REGRESSION_EXTRAPOLATE_BACKWARD);
+ rPoolDefaults[SCHATTR_REGRESSION_SET_INTERCEPT - SCHATTR_START] = new SfxBoolItem(SCHATTR_REGRESSION_SET_INTERCEPT, false);
+ rPoolDefaults[SCHATTR_REGRESSION_INTERCEPT_VALUE - SCHATTR_START] = new SvxDoubleItem(0.0, SCHATTR_REGRESSION_INTERCEPT_VALUE);
+ rPoolDefaults[SCHATTR_REGRESSION_CURVE_NAME - SCHATTR_START] = new SfxStringItem(SCHATTR_REGRESSION_CURVE_NAME, OUString());
+ rPoolDefaults[SCHATTR_REGRESSION_XNAME - SCHATTR_START] = new SfxStringItem(SCHATTR_REGRESSION_XNAME, "x");
+ rPoolDefaults[SCHATTR_REGRESSION_YNAME - SCHATTR_START] = new SfxStringItem(SCHATTR_REGRESSION_YNAME, "f(x)");
+ rPoolDefaults[SCHATTR_REGRESSION_MOVING_TYPE - SCHATTR_START] = new SfxInt32Item(SCHATTR_REGRESSION_MOVING_TYPE, css::chart2::MovingAverageType::Prior);
+
+ /**************************************************************************
+ * ItemInfos
+ **************************************************************************/
+ const sal_uInt16 nMax = SCHATTR_END - SCHATTR_START + 1;
+ for( sal_uInt16 i = 0; i < nMax; i++ )
+ {
+ pItemInfos[i]._nSID = 0;
+ pItemInfos[i]._bPoolable = true;
+ }
+
+ // slot ids differing from which ids
+ pItemInfos[SCHATTR_SYMBOL_BRUSH - SCHATTR_START]._nSID = SID_ATTR_BRUSH;
+ pItemInfos[SCHATTR_STYLE_SYMBOL - SCHATTR_START]._nSID = SID_ATTR_SYMBOLTYPE;
+ pItemInfos[SCHATTR_SYMBOL_SIZE - SCHATTR_START]._nSID = SID_ATTR_SYMBOLSIZE;
+
+ SetDefaults(ppPoolDefaults);
+ SetItemInfos(pItemInfos.get());
+}
+
+ChartItemPool::ChartItemPool(const ChartItemPool& rPool):
+ SfxItemPool(rPool)
+{
+}
+
+ChartItemPool::~ChartItemPool()
+{
+ Delete();
+ // release and delete static pool default items
+ ReleaseDefaults(true);
+}
+
+rtl::Reference<SfxItemPool> ChartItemPool::Clone() const
+{
+ return new ChartItemPool(*this);
+}
+
+MapUnit ChartItemPool::GetMetric(sal_uInt16 /* nWhich */) const
+{
+ return MapUnit::Map100thMM;
+}
+
+rtl::Reference<SfxItemPool> ChartItemPool::CreateChartItemPool()
+{
+ return new ChartItemPool();
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/ChartItemPool.hxx b/chart2/source/view/main/ChartItemPool.hxx
new file mode 100644
index 000000000..74a7ab1eb
--- /dev/null
+++ b/chart2/source/view/main/ChartItemPool.hxx
@@ -0,0 +1,48 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#include <tools/mapunit.hxx>
+#include <svl/itempool.hxx>
+#include <memory>
+
+namespace chart
+{
+class ChartItemPool : public SfxItemPool
+{
+private:
+ std::unique_ptr<SfxItemInfo[]> pItemInfos;
+
+public:
+ ChartItemPool();
+ ChartItemPool(const ChartItemPool& rPool);
+ virtual ~ChartItemPool() override;
+
+ virtual rtl::Reference<SfxItemPool> Clone() const override;
+ MapUnit GetMetric(sal_uInt16 nWhich) const override;
+
+ /// creates a pure chart item pool
+ static rtl::Reference<SfxItemPool> CreateChartItemPool();
+};
+
+} // namespace chart
+
+// INCLUDED_CHART2_SOURCE_VIEW_MAIN_CHARTITEMPOOL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/ChartView.cxx b/chart2/source/view/main/ChartView.cxx
new file mode 100644
index 000000000..3f96a68b2
--- /dev/null
+++ b/chart2/source/view/main/ChartView.cxx
@@ -0,0 +1,2035 @@
+/* -*- 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 <config_feature_desktop.h>
+
+#include "SeriesPlotterContainer.hxx"
+
+#include <ChartView.hxx>
+#include <chartview/DrawModelWrapper.hxx>
+#include <Diagram.hxx>
+#include <ChartType.hxx>
+#include <DataSeries.hxx>
+#include <NumberFormatterWrapper.hxx>
+#include <VDiagram.hxx>
+#include "VTitle.hxx"
+#include "VButton.hxx"
+#include <ShapeFactory.hxx>
+#include <BaseCoordinateSystem.hxx>
+#include <VCoordinateSystem.hxx>
+#include <VSeriesPlotter.hxx>
+#include <CommonConverters.hxx>
+#include <TitleHelper.hxx>
+#include <Legend.hxx>
+#include <LegendHelper.hxx>
+#include "VLegend.hxx"
+#include <PropertyMapper.hxx>
+#include <ChartModel.hxx>
+#include <ChartTypeHelper.hxx>
+#include <ScaleAutomatism.hxx>
+#include <ObjectIdentifier.hxx>
+#include <DiagramHelper.hxx>
+#include <RelativePositionHelper.hxx>
+#include <servicenames.hxx>
+#include <Axis.hxx>
+#include <AxisHelper.hxx>
+#include "AxisUsage.hxx"
+#include <AxisIndexDefines.hxx>
+#include <BaseGFXHelper.hxx>
+#include <DataSeriesHelper.hxx>
+#include <DateHelper.hxx>
+#include <ExplicitCategoriesProvider.hxx>
+#include <defines.hxx>
+#include <unonames.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/eeitem.hxx>
+#include <tools/globname.hxx>
+#include <comphelper/fileformat.h>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <rtl/math.hxx>
+#include <unotools/streamwrap.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/unopage.hxx>
+#include <vcl/svapp.hxx>
+#include <osl/mutex.hxx>
+#include <svx/unofill.hxx>
+#include <drawinglayer/XShapeDumper.hxx>
+
+#include <time.h>
+
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/chart/ChartAxisPosition.hpp>
+#include <com/sun/star/chart/TimeUnit.hpp>
+#include <com/sun/star/chart2/AxisType.hpp>
+#include <com/sun/star/chart2/StackingDirection.hpp>
+#include <com/sun/star/chart2/RelativePosition.hpp>
+#include <com/sun/star/chart2/RelativeSize.hpp>
+#include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp>
+#include <com/sun/star/chart2/data/PivotTableFieldEntry.hpp>
+#include <com/sun/star/drawing/GraphicExportFilter.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/util/XRefreshable.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <svl/itempool.hxx>
+#include <svl/ctloptions.hxx>
+#include <comphelper/classids.hxx>
+#include <servicenames_charttypes.hxx>
+
+
+#include <rtl/ustring.hxx>
+
+#include <tools/diagnose_ex.h>
+#include <tools/stream.hxx>
+
+#include <memory>
+#include <libxml/xmlwriter.h>
+
+namespace com::sun::star::chart2 { class XChartDocument; }
+
+namespace chart {
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::Any;
+
+struct CreateShapeParam2D
+{
+ css::awt::Rectangle maRemainingSpace;
+
+ std::shared_ptr<SeriesPlotterContainer> mpSeriesPlotterContainer;
+
+ std::shared_ptr<VTitle> mpVTitleX;
+ std::shared_ptr<VTitle> mpVTitleY;
+ std::shared_ptr<VTitle> mpVTitleZ;
+
+ std::shared_ptr<VTitle> mpVTitleSecondX;
+ std::shared_ptr<VTitle> mpVTitleSecondY;
+
+ rtl::Reference<SvxShapeRect> mxMarkHandles;
+ rtl::Reference<SvxShapeRect> mxPlotAreaWithAxes;
+
+ rtl::Reference<SvxShapeGroup> mxDiagramWithAxesShapes;
+
+ bool mbAutoPosTitleX;
+ bool mbAutoPosTitleY;
+ bool mbAutoPosTitleZ;
+
+ bool mbAutoPosSecondTitleX;
+ bool mbAutoPosSecondTitleY;
+
+ bool mbUseFixedInnerSize;
+
+ CreateShapeParam2D() :
+ mbAutoPosTitleX(true),
+ mbAutoPosTitleY(true),
+ mbAutoPosTitleZ(true),
+ mbAutoPosSecondTitleX(true),
+ mbAutoPosSecondTitleY(true),
+ mbUseFixedInnerSize(false) {}
+};
+
+
+
+ChartView::ChartView(
+ uno::Reference<uno::XComponentContext> const & xContext,
+ ChartModel& rModel)
+ : m_xCC(xContext)
+ , mrChartModel(rModel)
+ , m_aListenerContainer( m_aMutex )
+ , m_bViewDirty(true)
+ , m_bInViewUpdate(false)
+ , m_bViewUpdatePending(false)
+ , m_bRefreshAddIn(true)
+ , m_aPageResolution(1000,1000)
+ , m_bPointsWereSkipped(false)
+ , m_nScaleXNumerator(1)
+ , m_nScaleXDenominator(1)
+ , m_nScaleYNumerator(1)
+ , m_nScaleYDenominator(1)
+ , m_bSdrViewIsInEditMode(false)
+ , m_aResultingDiagramRectangleExcludingAxes(0,0,0,0)
+{
+ init();
+}
+
+void ChartView::init()
+{
+ if( !m_pDrawModelWrapper )
+ {
+ SolarMutexGuard aSolarGuard;
+ m_pDrawModelWrapper = std::make_shared< DrawModelWrapper >();
+ m_xShapeFactory = m_pDrawModelWrapper->getShapeFactory();
+ m_xDrawPage = m_pDrawModelWrapper->getMainDrawPage();
+ StartListening( m_pDrawModelWrapper->getSdrModel() );
+ }
+}
+
+void SAL_CALL ChartView::initialize( const uno::Sequence< uno::Any >& )
+{
+ init();
+}
+
+ChartView::~ChartView()
+{
+ maTimeBased.maTimer.Stop();
+ // #i120831#. In ChartView::initialize(), m_xShapeFactory is created from SdrModel::getUnoModel() and indirectly
+ // from SfxBaseModel, it needs call dispose() to make sure SfxBaseModel object is freed correctly.
+ uno::Reference< lang::XComponent > xComp( m_xShapeFactory, uno::UNO_QUERY);
+ if ( xComp.is() )
+ xComp->dispose();
+
+ if( m_pDrawModelWrapper )
+ {
+ SolarMutexGuard aSolarGuard;
+ EndListening( m_pDrawModelWrapper->getSdrModel() );
+ m_pDrawModelWrapper.reset();
+ }
+ m_xDrawPage = nullptr;
+ impl_deleteCoordinateSystems();
+}
+
+void ChartView::impl_deleteCoordinateSystems()
+{
+ //delete all coordinate systems
+ m_aVCooSysList.clear();
+}
+
+// datatransfer::XTransferable
+namespace
+{
+constexpr OUStringLiteral lcl_aGDIMetaFileMIMEType(
+ u"application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"" );
+constexpr OUStringLiteral lcl_aGDIMetaFileMIMETypeHighContrast(
+ u"application/x-openoffice-highcontrast-gdimetafile;windows_formatname=\"GDIMetaFile\"" );
+} // anonymous namespace
+
+void ChartView::getMetaFile( const uno::Reference< io::XOutputStream >& xOutStream
+ , bool bUseHighContrast )
+{
+ if( !m_xDrawPage.is() )
+ return;
+
+ // creating the graphic exporter
+ uno::Reference< drawing::XGraphicExportFilter > xExporter = drawing::GraphicExportFilter::create( m_xCC );
+
+ uno::Sequence< beans::PropertyValue > aFilterData{
+ comphelper::makePropertyValue("ExportOnlyBackground", false),
+ comphelper::makePropertyValue("HighContrast", bUseHighContrast),
+ comphelper::makePropertyValue("Version", sal_Int32(SOFFICE_FILEFORMAT_50)),
+ comphelper::makePropertyValue("CurrentPage", uno::Reference< uno::XInterface >( static_cast<cppu::OWeakObject*>(m_xDrawPage.get()), uno::UNO_QUERY )),
+ //#i75867# poor quality of ole's alternative view with 3D scenes and zoomfactors besides 100%
+ comphelper::makePropertyValue("ScaleXNumerator", m_nScaleXNumerator),
+ comphelper::makePropertyValue("ScaleXDenominator", m_nScaleXDenominator),
+ comphelper::makePropertyValue("ScaleYNumerator", m_nScaleYNumerator),
+ comphelper::makePropertyValue("ScaleYDenominator", m_nScaleYDenominator)
+ };
+
+ uno::Sequence< beans::PropertyValue > aProps{
+ comphelper::makePropertyValue("FilterName", OUString("SVM")),
+ comphelper::makePropertyValue("OutputStream", xOutStream),
+ comphelper::makePropertyValue("FilterData", aFilterData)
+ };
+
+ xExporter->setSourceDocument( m_xDrawPage );
+ if( xExporter->filter( aProps ) )
+ {
+ xOutStream->flush();
+ xOutStream->closeOutput();
+ uno::Reference< io::XSeekable > xSeekable( xOutStream, uno::UNO_QUERY );
+ if( xSeekable.is() )
+ xSeekable->seek(0);
+ }
+}
+
+uno::Any SAL_CALL ChartView::getTransferData( const datatransfer::DataFlavor& aFlavor )
+{
+ bool bHighContrastMetaFile( aFlavor.MimeType == lcl_aGDIMetaFileMIMETypeHighContrast);
+ uno::Any aRet;
+ if( ! (bHighContrastMetaFile || aFlavor.MimeType == lcl_aGDIMetaFileMIMEType) )
+ return aRet;
+
+ update();
+
+ SvMemoryStream aStream( 1024, 1024 );
+ rtl::Reference<utl::OStreamWrapper> pStreamWrapper = new utl::OStreamWrapper( aStream );
+
+ this->getMetaFile( pStreamWrapper, bHighContrastMetaFile );
+
+ pStreamWrapper->seek(0);
+ sal_Int32 nBytesToRead = pStreamWrapper->available();
+ uno::Sequence< sal_Int8 > aSeq( nBytesToRead );
+ pStreamWrapper->readBytes( aSeq, nBytesToRead);
+ aRet <<= aSeq;
+ pStreamWrapper->closeInput();
+
+ return aRet;
+}
+uno::Sequence< datatransfer::DataFlavor > SAL_CALL ChartView::getTransferDataFlavors()
+{
+ return
+ {
+ { lcl_aGDIMetaFileMIMEType, "GDIMetaFile", cppu::UnoType<uno::Sequence< sal_Int8 >>::get() },
+ { lcl_aGDIMetaFileMIMETypeHighContrast, "GDIMetaFile", cppu::UnoType<uno::Sequence< sal_Int8 >>::get() }
+ };
+}
+sal_Bool SAL_CALL ChartView::isDataFlavorSupported( const datatransfer::DataFlavor& aFlavor )
+{
+ return ( aFlavor.MimeType == lcl_aGDIMetaFileMIMEType ||
+ aFlavor.MimeType == lcl_aGDIMetaFileMIMETypeHighContrast );
+}
+
+// ____ XUnoTunnel ___
+::sal_Int64 SAL_CALL ChartView::getSomething( const uno::Sequence< ::sal_Int8 >& aIdentifier )
+{
+ return comphelper::getSomethingImpl<ExplicitValueProvider>(aIdentifier, this);
+}
+
+// lang::XServiceInfo
+
+OUString SAL_CALL ChartView::getImplementationName()
+{
+ return CHART_VIEW_SERVICE_IMPLEMENTATION_NAME;
+}
+
+sal_Bool SAL_CALL ChartView::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL ChartView::getSupportedServiceNames()
+{
+ return { CHART_VIEW_SERVICE_NAME };
+}
+
+static ::basegfx::B3DHomMatrix createTransformationSceneToScreen(
+ const ::basegfx::B2IRectangle& rDiagramRectangleWithoutAxes )
+{
+ ::basegfx::B3DHomMatrix aM;
+ aM.scale(double(rDiagramRectangleWithoutAxes.getWidth())/FIXED_SIZE_FOR_3D_CHART_VOLUME
+ , -double(rDiagramRectangleWithoutAxes.getHeight())/FIXED_SIZE_FOR_3D_CHART_VOLUME, 1.0 );
+ aM.translate(double(rDiagramRectangleWithoutAxes.getMinX())
+ , double(rDiagramRectangleWithoutAxes.getMinY()+rDiagramRectangleWithoutAxes.getHeight()-1), 0);
+ return aM;
+}
+
+namespace
+{
+
+bool lcl_IsPieOrDonut( const rtl::Reference< Diagram >& xDiagram )
+{
+ //special treatment for pie charts
+ //the size is checked after complete creation to get the datalabels into the given space
+
+ //todo: this is just a workaround at the moment for pie and donut labels
+ return DiagramHelper::isPieOrDonutChart( xDiagram );
+}
+
+void lcl_setDefaultWritingMode( const std::shared_ptr< DrawModelWrapper >& pDrawModelWrapper, ChartModel& rModel)
+{
+ //get writing mode from parent document:
+ if( !SvtCTLOptions().IsCTLFontEnabled() )
+ return;
+
+ try
+ {
+ sal_Int16 nWritingMode=-1;
+ uno::Reference< beans::XPropertySet > xParentProps( rModel.getParent(), uno::UNO_QUERY );
+ uno::Reference< style::XStyleFamiliesSupplier > xStyleFamiliesSupplier( xParentProps, uno::UNO_QUERY );
+ if( xStyleFamiliesSupplier.is() )
+ {
+ uno::Reference< container::XNameAccess > xStylesFamilies( xStyleFamiliesSupplier->getStyleFamilies() );
+ if( xStylesFamilies.is() )
+ {
+ if( !xStylesFamilies->hasByName( "PageStyles" ) )
+ {
+ //draw/impress is parent document
+ uno::Reference< lang::XMultiServiceFactory > xFatcory( xParentProps, uno::UNO_QUERY );
+ if( xFatcory.is() )
+ {
+ uno::Reference< beans::XPropertySet > xDrawDefaults( xFatcory->createInstance( "com.sun.star.drawing.Defaults" ), uno::UNO_QUERY );
+ if( xDrawDefaults.is() )
+ xDrawDefaults->getPropertyValue( "WritingMode" ) >>= nWritingMode;
+ }
+ }
+ else
+ {
+ uno::Reference< container::XNameAccess > xPageStyles( xStylesFamilies->getByName( "PageStyles" ), uno::UNO_QUERY );
+ if( xPageStyles.is() )
+ {
+ OUString aPageStyle;
+
+ uno::Reference< text::XTextDocument > xTextDocument( xParentProps, uno::UNO_QUERY );
+ if( xTextDocument.is() )
+ {
+ //writer is parent document
+ //retrieve the current page style from the text cursor property PageStyleName
+
+ uno::Reference< text::XTextEmbeddedObjectsSupplier > xTextEmbeddedObjectsSupplier( xTextDocument, uno::UNO_QUERY );
+ if( xTextEmbeddedObjectsSupplier.is() )
+ {
+ uno::Reference< container::XNameAccess > xEmbeddedObjects( xTextEmbeddedObjectsSupplier->getEmbeddedObjects() );
+ if( xEmbeddedObjects.is() )
+ {
+ uno::Sequence< OUString > aNames( xEmbeddedObjects->getElementNames() );
+
+ sal_Int32 nCount = aNames.getLength();
+ for( sal_Int32 nN=0; nN<nCount; nN++ )
+ {
+ uno::Reference< beans::XPropertySet > xEmbeddedProps( xEmbeddedObjects->getByName( aNames[nN] ), uno::UNO_QUERY );
+ if( xEmbeddedProps.is() )
+ {
+ static OUString aChartCLSID = SvGlobalName( SO3_SCH_CLASSID ).GetHexName();
+ OUString aCLSID;
+ xEmbeddedProps->getPropertyValue( "CLSID" ) >>= aCLSID;
+ if( aCLSID == aChartCLSID )
+ {
+ uno::Reference< text::XTextContent > xEmbeddedObject( xEmbeddedProps, uno::UNO_QUERY );
+ if( xEmbeddedObject.is() )
+ {
+ uno::Reference< text::XTextRange > xAnchor( xEmbeddedObject->getAnchor() );
+ if( xAnchor.is() )
+ {
+ uno::Reference< beans::XPropertySet > xAnchorProps( xAnchor, uno::UNO_QUERY );
+ if( xAnchorProps.is() )
+ {
+ xAnchorProps->getPropertyValue( "WritingMode" ) >>= nWritingMode;
+ }
+ uno::Reference< text::XText > xText( xAnchor->getText() );
+ if( xText.is() )
+ {
+ uno::Reference< beans::XPropertySet > xTextCursorProps( xText->createTextCursor(), uno::UNO_QUERY );
+ if( xTextCursorProps.is() )
+ xTextCursorProps->getPropertyValue( "PageStyleName" ) >>= aPageStyle;
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ if( aPageStyle.isEmpty() )
+ {
+ uno::Reference< text::XText > xText( xTextDocument->getText() );
+ if( xText.is() )
+ {
+ uno::Reference< beans::XPropertySet > xTextCursorProps( xText->createTextCursor(), uno::UNO_QUERY );
+ if( xTextCursorProps.is() )
+ xTextCursorProps->getPropertyValue( "PageStyleName" ) >>= aPageStyle;
+ }
+ }
+ if(aPageStyle.isEmpty())
+ aPageStyle = "Standard";
+ }
+ else
+ {
+ //Calc is parent document
+ Reference< com::sun::star::beans::XPropertySetInfo > xInfo = xParentProps->getPropertySetInfo();
+ if (xInfo->hasPropertyByName("PageStyle"))
+ {
+ xParentProps->getPropertyValue( "PageStyle" ) >>= aPageStyle;
+ }
+ if(aPageStyle.isEmpty())
+ aPageStyle = "Default";
+ }
+ if( nWritingMode == -1 || nWritingMode == text::WritingMode2::PAGE )
+ {
+ uno::Reference< beans::XPropertySet > xPageStyle( xPageStyles->getByName( aPageStyle ), uno::UNO_QUERY );
+ Reference< com::sun::star::beans::XPropertySetInfo > xInfo = xPageStyle->getPropertySetInfo();
+ if (xInfo->hasPropertyByName("WritingMode"))
+ {
+ if( xPageStyle.is() )
+ xPageStyle->getPropertyValue( "WritingMode" ) >>= nWritingMode;
+ }
+ }
+ }
+ }
+ }
+ }
+ if( nWritingMode != -1 && nWritingMode != text::WritingMode2::PAGE )
+ {
+ if( pDrawModelWrapper )
+ pDrawModelWrapper->GetItemPool().SetPoolDefaultItem(SvxFrameDirectionItem(static_cast<SvxFrameDirection>(nWritingMode), EE_PARA_WRITINGDIR) );
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2" );
+ }
+}
+
+sal_Int16 lcl_getDefaultWritingModeFromPool( const std::shared_ptr<DrawModelWrapper>& pDrawModelWrapper )
+{
+ sal_Int16 nWritingMode = text::WritingMode2::LR_TB;
+ if(!pDrawModelWrapper)
+ return nWritingMode;
+
+ const SfxPoolItem& rItem = pDrawModelWrapper->GetItemPool().GetDefaultItem(EE_PARA_WRITINGDIR);
+ nWritingMode
+ = static_cast<sal_Int16>(static_cast<const SvxFrameDirectionItem&>(rItem).GetValue());
+ return nWritingMode;
+}
+
+} //end anonymous namespace
+
+awt::Rectangle ChartView::impl_createDiagramAndContent( const CreateShapeParam2D& rParam, const awt::Size& rPageSize )
+{
+ //return the used rectangle
+ awt::Rectangle aUsedOuterRect(rParam.maRemainingSpace.X, rParam.maRemainingSpace.Y, 0, 0);
+
+ rtl::Reference< Diagram > xDiagram( mrChartModel.getFirstChartDiagram() );
+ if( !xDiagram.is())
+ return aUsedOuterRect;
+
+ sal_Int32 nDimensionCount = DiagramHelper::getDimension( xDiagram );
+ if(!nDimensionCount)
+ {
+ //@todo handle mixed dimension
+ nDimensionCount = 2;
+ }
+
+ basegfx::B2IRectangle aAvailableOuterRect = BaseGFXHelper::makeRectangle(rParam.maRemainingSpace);
+
+ const std::vector< std::unique_ptr<VCoordinateSystem> >& rVCooSysList( rParam.mpSeriesPlotterContainer->getCooSysList() );
+ auto& rSeriesPlotterList = rParam.mpSeriesPlotterContainer->getSeriesPlotterList();
+
+ //create VAxis, so they can give necessary information for automatic scaling
+ uno::Reference<util::XNumberFormatsSupplier> const xNumberFormatsSupplier(
+ mrChartModel.getNumberFormatsSupplier());
+
+ for (auto& rpVCooSys : rVCooSysList)
+ {
+ if (nDimensionCount == 3)
+ {
+ CuboidPlanePosition eLeftWallPos( ThreeDHelper::getAutomaticCuboidPlanePositionForStandardLeftWall( xDiagram ) );
+ CuboidPlanePosition eBackWallPos( ThreeDHelper::getAutomaticCuboidPlanePositionForStandardBackWall( xDiagram ) );
+ CuboidPlanePosition eBottomPos( ThreeDHelper::getAutomaticCuboidPlanePositionForStandardBottom( xDiagram ) );
+ rpVCooSys->set3DWallPositions( eLeftWallPos, eBackWallPos, eBottomPos );
+ }
+
+ rpVCooSys->createVAxisList(&mrChartModel, rPageSize, rParam.maRemainingSpace, rParam.mbUseFixedInnerSize);
+ }
+
+ // - prepare list of all axis and how they are used
+ Date aNullDate = NumberFormatterWrapper( xNumberFormatsSupplier ).getNullDate();
+ rParam.mpSeriesPlotterContainer->initAxisUsageList(aNullDate);
+ rParam.mpSeriesPlotterContainer->doAutoScaling( mrChartModel );
+ rParam.mpSeriesPlotterContainer->setScalesFromCooSysToPlotter();
+ rParam.mpSeriesPlotterContainer->setNumberFormatsFromAxes();
+
+ //create shapes
+
+ //aspect ratio
+ drawing::Direction3D aPreferredAspectRatio =
+ rParam.mpSeriesPlotterContainer->getPreferredAspectRatio();
+
+ rtl::Reference<SvxShapeGroupAnyD> xSeriesTargetInFrontOfAxis;
+ rtl::Reference<SvxShapeGroupAnyD> xSeriesTargetBehindAxis;
+ VDiagram aVDiagram(xDiagram, aPreferredAspectRatio, nDimensionCount);
+ {//create diagram
+ aVDiagram.init(rParam.mxDiagramWithAxesShapes);
+ aVDiagram.createShapes(
+ awt::Point(rParam.maRemainingSpace.X, rParam.maRemainingSpace.Y),
+ awt::Size(rParam.maRemainingSpace.Width, rParam.maRemainingSpace.Height));
+
+ xSeriesTargetInFrontOfAxis = aVDiagram.getCoordinateRegion();
+ // It is preferable to use full size than minimum for pie charts
+ if (!rParam.mbUseFixedInnerSize)
+ aVDiagram.reduceToMinimumSize();
+ }
+
+ rtl::Reference<SvxShapeGroup> xTextTargetShapes =
+ ShapeFactory::createGroup2D(rParam.mxDiagramWithAxesShapes);
+
+ // - create axis and grids for all coordinate systems
+
+ //init all coordinate systems
+ for (auto& rpVCooSys : rVCooSysList)
+ {
+ rpVCooSys->initPlottingTargets(xSeriesTargetInFrontOfAxis, xTextTargetShapes, xSeriesTargetBehindAxis);
+
+ rpVCooSys->setTransformationSceneToScreen( B3DHomMatrixToHomogenMatrix(
+ createTransformationSceneToScreen( aVDiagram.getCurrentRectangle() ) ));
+
+ rpVCooSys->initVAxisInList();
+ }
+
+ //calculate resulting size respecting axis label layout and fontscaling
+
+ rtl::Reference<SvxShapeGroup> xBoundingShape(rParam.mxDiagramWithAxesShapes);
+ ::basegfx::B2IRectangle aConsumedOuterRect;
+
+ //use first coosys only so far; todo: calculate for more than one coosys if we have more in future
+ //todo: this is just a workaround at the moment for pie and donut labels
+ bool bIsPieOrDonut = lcl_IsPieOrDonut(xDiagram);
+ if( !bIsPieOrDonut && (!rVCooSysList.empty()) )
+ {
+ VCoordinateSystem* pVCooSys = rVCooSysList[0].get();
+ pVCooSys->createMaximumAxesLabels();
+
+ aConsumedOuterRect = ShapeFactory::getRectangleOfShape(*xBoundingShape);
+ ::basegfx::B2IRectangle aNewInnerRect( aVDiagram.getCurrentRectangle() );
+ if (!rParam.mbUseFixedInnerSize)
+ aNewInnerRect = aVDiagram.adjustInnerSize( aConsumedOuterRect );
+
+ pVCooSys->setTransformationSceneToScreen( B3DHomMatrixToHomogenMatrix(
+ createTransformationSceneToScreen( aNewInnerRect ) ));
+
+ //redo autoscaling to get size and text dependent automatic main increment count
+ rParam.mpSeriesPlotterContainer->doAutoScaling( mrChartModel );
+ rParam.mpSeriesPlotterContainer->updateScalesAndIncrementsOnAxes();
+ rParam.mpSeriesPlotterContainer->setScalesFromCooSysToPlotter();
+
+ pVCooSys->createAxesLabels();
+
+ bool bLessSpaceConsumedThanExpected = false;
+ {
+ aConsumedOuterRect = ShapeFactory::getRectangleOfShape(*xBoundingShape);
+ if( aConsumedOuterRect.getMinX() > aAvailableOuterRect.getMinX()
+ || aConsumedOuterRect.getMaxX() < aAvailableOuterRect.getMaxX()
+ || aConsumedOuterRect.getMinY() > aAvailableOuterRect.getMinY()
+ || aConsumedOuterRect.getMinY() < aAvailableOuterRect.getMaxY() )
+ {
+ bLessSpaceConsumedThanExpected = true;
+ }
+ }
+
+ if (bLessSpaceConsumedThanExpected && !rParam.mbUseFixedInnerSize)
+ {
+ aVDiagram.adjustInnerSize( aConsumedOuterRect );
+ pVCooSys->setTransformationSceneToScreen( B3DHomMatrixToHomogenMatrix(
+ createTransformationSceneToScreen( aVDiagram.getCurrentRectangle() ) ));
+
+ // Need to re-adjust again if the labels have changed height because of
+ // text can break. Ideally this shouldn't be needed, but the chart height
+ // isn't readjusted otherwise.
+ pVCooSys->createAxesLabels();
+ aConsumedOuterRect = ShapeFactory::getRectangleOfShape(*xBoundingShape);
+ aVDiagram.adjustInnerSize(aConsumedOuterRect);
+ pVCooSys->setTransformationSceneToScreen(B3DHomMatrixToHomogenMatrix(
+ createTransformationSceneToScreen(aVDiagram.getCurrentRectangle())));
+
+ }
+ pVCooSys->updatePositions();//todo: logically this belongs to the condition above, but it seems also to be necessary to give the axes group shapes the right bounding rects for hit test - probably caused by bug i106183 -> check again if fixed
+ }
+
+ //create axes and grids for the final size
+ for (auto& rpVCooSys : rVCooSysList)
+ {
+ rpVCooSys->setTransformationSceneToScreen( B3DHomMatrixToHomogenMatrix(
+ createTransformationSceneToScreen( aVDiagram.getCurrentRectangle() ) ));
+
+ rpVCooSys->createAxesShapes();
+ rpVCooSys->createGridShapes();
+ }
+
+ // - create data series for all charttypes
+ m_bPointsWereSkipped = false;
+ for( const std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
+ {
+ VSeriesPlotter* pSeriesPlotter = aPlotter.get();
+ rtl::Reference<SvxShapeGroupAnyD> xSeriesTarget;
+ if( pSeriesPlotter->WantToPlotInFrontOfAxisLine() )
+ xSeriesTarget = xSeriesTargetInFrontOfAxis;
+ else
+ {
+ xSeriesTarget = xSeriesTargetBehindAxis;
+ OSL_ENSURE( !bIsPieOrDonut, "not implemented yet! - during a complete recreation this shape is destroyed so no series can be created anymore" );
+ }
+ pSeriesPlotter->initPlotter( xSeriesTarget,xTextTargetShapes,OUString() );
+ pSeriesPlotter->setPageReferenceSize( rPageSize );
+ VCoordinateSystem* pVCooSys = SeriesPlotterContainer::getCooSysForPlotter( rVCooSysList, pSeriesPlotter );
+ if(nDimensionCount==2)
+ pSeriesPlotter->setTransformationSceneToScreen( pVCooSys->getTransformationSceneToScreen() );
+ //better performance for big data
+ {
+ //calculate resolution for coordinate system
+ Sequence<sal_Int32> aCoordinateSystemResolution = pVCooSys->getCoordinateSystemResolution( rPageSize, m_aPageResolution );
+ pSeriesPlotter->setCoordinateSystemResolution( aCoordinateSystemResolution );
+ }
+ // Do not allow to move data labels in case of pie or donut chart, yet!
+ pSeriesPlotter->setPieLabelsAllowToMove(!bIsPieOrDonut);
+ // use the pagesize as remaining space if we have a fixed inner size
+ if( rParam.mbUseFixedInnerSize )
+ aAvailableOuterRect = BaseGFXHelper::makeRectangle(awt::Rectangle(0, 0, rPageSize.Width, rPageSize.Height));
+ // set the available space for data labels to avoid moving out from chart area
+ pSeriesPlotter->setAvailableOuterRect(aAvailableOuterRect);
+ pSeriesPlotter->createShapes();
+ m_bPointsWereSkipped = m_bPointsWereSkipped || pSeriesPlotter->PointsWereSkipped();
+ }
+
+ //recreate all with corrected sizes if requested
+ if( bIsPieOrDonut )
+ {
+ m_bPointsWereSkipped = false;
+
+ aConsumedOuterRect = ShapeFactory::getRectangleOfShape(*xBoundingShape);
+ ::basegfx::B2IRectangle aNewInnerRect( aVDiagram.getCurrentRectangle() );
+ if (!rParam.mbUseFixedInnerSize)
+ aNewInnerRect = aVDiagram.adjustInnerSize( aConsumedOuterRect );
+
+ for( std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
+ {
+ aPlotter->releaseShapes();
+ }
+
+ //clear and recreate
+ ShapeFactory::removeSubShapes( xSeriesTargetInFrontOfAxis ); //xSeriesTargetBehindAxis is a sub shape of xSeriesTargetInFrontOfAxis and will be removed here
+ xSeriesTargetBehindAxis.clear();
+ ShapeFactory::removeSubShapes( xTextTargetShapes );
+
+ //set new transformation
+ for (auto& rpVCooSys : rVCooSysList)
+ {
+ auto aMatrix = createTransformationSceneToScreen(aNewInnerRect);
+ rpVCooSys->setTransformationSceneToScreen(B3DHomMatrixToHomogenMatrix(aMatrix));
+ }
+
+ // - create data series for all charttypes
+ for( std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
+ {
+ VCoordinateSystem* pVCooSys = SeriesPlotterContainer::getCooSysForPlotter( rVCooSysList, aPlotter.get() );
+ if(nDimensionCount==2)
+ aPlotter->setTransformationSceneToScreen( pVCooSys->getTransformationSceneToScreen() );
+ // Now we can move data labels in case of pie or donut chart!
+ aPlotter->setPieLabelsAllowToMove(bIsPieOrDonut);
+ aPlotter->createShapes();
+ m_bPointsWereSkipped = m_bPointsWereSkipped || aPlotter->PointsWereSkipped();
+ }
+
+ for( std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
+ aPlotter->rearrangeLabelToAvoidOverlapIfRequested(rPageSize);
+ }
+
+ if (rParam.mbUseFixedInnerSize)
+ {
+ aUsedOuterRect = awt::Rectangle( aConsumedOuterRect.getMinX(), aConsumedOuterRect.getMinY(), aConsumedOuterRect.getWidth(), aConsumedOuterRect.getHeight() );
+ }
+ else
+ aUsedOuterRect = rParam.maRemainingSpace;
+
+ bool bSnapRectToUsedArea = false;
+ for( std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
+ {
+ bSnapRectToUsedArea = aPlotter->shouldSnapRectToUsedArea();
+ if(bSnapRectToUsedArea)
+ break;
+ }
+ if(bSnapRectToUsedArea)
+ {
+ if (rParam.mbUseFixedInnerSize)
+ m_aResultingDiagramRectangleExcludingAxes = getRectangleOfObject( "PlotAreaExcludingAxes" );
+ else
+ {
+ ::basegfx::B2IRectangle aConsumedInnerRect = aVDiagram.getCurrentRectangle();
+ m_aResultingDiagramRectangleExcludingAxes = BaseGFXHelper::toAwtRectangle(aConsumedInnerRect);
+ }
+ }
+ else
+ {
+ if (rParam.mbUseFixedInnerSize)
+ m_aResultingDiagramRectangleExcludingAxes = rParam.maRemainingSpace;
+ else
+ {
+ ::basegfx::B2IRectangle aConsumedInnerRect = aVDiagram.getCurrentRectangle();
+ m_aResultingDiagramRectangleExcludingAxes = BaseGFXHelper::toAwtRectangle(aConsumedInnerRect);
+ }
+ }
+
+ if (rParam.mxMarkHandles.is())
+ {
+ awt::Point aPos(rParam.maRemainingSpace.X, rParam.maRemainingSpace.Y);
+ awt::Size aSize(rParam.maRemainingSpace.Width, rParam.maRemainingSpace.Height);
+
+ bool bPosSizeExcludeAxesProperty = true;
+ xDiagram->getPropertyValue("PosSizeExcludeAxes") >>= bPosSizeExcludeAxesProperty;
+ if (rParam.mbUseFixedInnerSize || bPosSizeExcludeAxesProperty)
+ {
+ aPos = awt::Point( m_aResultingDiagramRectangleExcludingAxes.X, m_aResultingDiagramRectangleExcludingAxes.Y );
+ aSize = awt::Size( m_aResultingDiagramRectangleExcludingAxes.Width, m_aResultingDiagramRectangleExcludingAxes.Height );
+ }
+ rParam.mxMarkHandles->setPosition(aPos);
+ rParam.mxMarkHandles->setSize(aSize);
+ }
+
+ return aUsedOuterRect;
+}
+
+bool ChartView::getExplicitValuesForAxis(
+ uno::Reference< XAxis > xAxis
+ , ExplicitScaleData& rExplicitScale
+ , ExplicitIncrementData& rExplicitIncrement )
+{
+ SolarMutexGuard aSolarGuard;
+
+ impl_updateView();
+
+ if(!xAxis.is())
+ return false;
+
+ rtl::Reference< BaseCoordinateSystem > xCooSys = AxisHelper::getCoordinateSystemOfAxis(xAxis, mrChartModel.getFirstChartDiagram() );
+ const VCoordinateSystem* pVCooSys = SeriesPlotterContainer::findInCooSysList(m_aVCooSysList, xCooSys);
+ if(!pVCooSys)
+ return false;
+
+ sal_Int32 nDimensionIndex=-1;
+ sal_Int32 nAxisIndex=-1;
+ if( !AxisHelper::getIndicesForAxis( xAxis, xCooSys, nDimensionIndex, nAxisIndex ) )
+ return false;
+
+ rExplicitScale = pVCooSys->getExplicitScale(nDimensionIndex,nAxisIndex);
+ rExplicitIncrement = pVCooSys->getExplicitIncrement(nDimensionIndex,nAxisIndex);
+ if( !rExplicitScale.m_bShiftedCategoryPosition )
+ return true;
+
+ //remove 'one' from max
+ if( rExplicitScale.AxisType == css::chart2::AxisType::DATE )
+ {
+ Date aMaxDate(rExplicitScale.NullDate); aMaxDate.AddDays(::rtl::math::approxFloor(rExplicitScale.Maximum));
+ //for explicit scales with shifted categories we need one interval more
+ switch( rExplicitScale.TimeResolution )
+ {
+ case css::chart::TimeUnit::DAY:
+ --aMaxDate;
+ break;
+ case css::chart::TimeUnit::MONTH:
+ aMaxDate = DateHelper::GetDateSomeMonthsAway(aMaxDate,-1);
+ break;
+ case css::chart::TimeUnit::YEAR:
+ aMaxDate = DateHelper::GetDateSomeYearsAway(aMaxDate,-1);
+ break;
+ }
+ rExplicitScale.Maximum = aMaxDate - rExplicitScale.NullDate;
+ }
+ else if( rExplicitScale.AxisType == css::chart2::AxisType::CATEGORY )
+ rExplicitScale.Maximum -= 1.0;
+ else if( rExplicitScale.AxisType == css::chart2::AxisType::SERIES )
+ rExplicitScale.Maximum -= 1.0;
+ return true;
+}
+
+SdrPage* ChartView::getSdrPage()
+{
+ if(m_xDrawPage)
+ return m_xDrawPage->GetSdrPage();
+
+ return nullptr;
+}
+
+rtl::Reference< SvxShape > ChartView::getShapeForCID( const OUString& rObjectCID )
+{
+ SolarMutexGuard aSolarGuard;
+ SdrObject* pObj = DrawModelWrapper::getNamedSdrObject( rObjectCID, this->getSdrPage() );
+ if( !pObj )
+ return nullptr;
+
+ uno::Reference< drawing::XShape > xShape = pObj->getUnoShape();
+ rtl::Reference<SvxShape> xShape2 = dynamic_cast<SvxShape*>(xShape.get());
+ assert(xShape2 || !xShape);
+ return xShape2;
+}
+
+awt::Rectangle ChartView::getDiagramRectangleExcludingAxes()
+{
+ impl_updateView();
+ return m_aResultingDiagramRectangleExcludingAxes;
+}
+
+awt::Rectangle ChartView::getRectangleOfObject( const OUString& rObjectCID, bool bSnapRect )
+{
+ impl_updateView();
+
+ awt::Rectangle aRet;
+ rtl::Reference< SvxShape > xShape = getShapeForCID(rObjectCID);
+ if(xShape.is())
+ {
+ //special handling for axis for old api:
+ //same special handling for diagram
+ ObjectType eObjectType( ObjectIdentifier::getObjectType( rObjectCID ) );
+ if( eObjectType == OBJECTTYPE_AXIS || eObjectType == OBJECTTYPE_DIAGRAM )
+ {
+ SolarMutexGuard aSolarGuard;
+ SdrObject* pRootSdrObject = xShape->GetSdrObject();
+ if( pRootSdrObject )
+ {
+ SdrObjList* pRootList = pRootSdrObject->GetSubList();
+ if( pRootList )
+ {
+ OUString aShapeName = "MarkHandles";
+ if( eObjectType == OBJECTTYPE_DIAGRAM )
+ aShapeName = "PlotAreaIncludingAxes";
+ SdrObject* pShape = DrawModelWrapper::getNamedSdrObject( aShapeName, pRootList );
+ if( pShape )
+ {
+ xShape = dynamic_cast<SvxShape*>(pShape->getUnoShape().get());
+ assert(xShape);
+ }
+ }
+ }
+ }
+
+ awt::Size aSize( xShape->getSize() );
+ awt::Point aPoint( xShape->getPosition() );
+ aRet = awt::Rectangle( aPoint.X, aPoint.Y, aSize.Width, aSize.Height );
+ if( bSnapRect )
+ {
+ //for rotated objects the shape size and position differs from the visible rectangle
+ SdrObject* pSdrObject = xShape->GetSdrObject();
+ if( pSdrObject )
+ {
+ tools::Rectangle aSnapRect( pSdrObject->GetSnapRect() );
+ aRet = awt::Rectangle(aSnapRect.Left(),aSnapRect.Top(),aSnapRect.GetWidth(),aSnapRect.GetHeight());
+ }
+ }
+ }
+ return aRet;
+}
+
+std::shared_ptr< DrawModelWrapper > ChartView::getDrawModelWrapper()
+{
+ return m_pDrawModelWrapper;
+}
+
+namespace
+{
+
+constexpr double constPageLayoutDistancePercentage = 0.02;
+
+bool getAvailablePosAndSizeForDiagram(
+ CreateShapeParam2D& rParam, const awt::Size & rPageSize, const uno::Reference< beans::XPropertySet >& xProp)
+{
+ rParam.mbUseFixedInnerSize = false;
+
+ //@todo: we need a size dependent on the axis labels
+ sal_Int32 nYDistance = static_cast<sal_Int32>(rPageSize.Height * constPageLayoutDistancePercentage);
+ sal_Int32 nXDistance = static_cast<sal_Int32>(rPageSize.Width * constPageLayoutDistancePercentage);
+ rParam.maRemainingSpace.X += nXDistance;
+ rParam.maRemainingSpace.Width -= 2*nXDistance;
+ rParam.maRemainingSpace.Y += nYDistance;
+ rParam.maRemainingSpace.Height -= 2*nYDistance;
+
+ bool bPosSizeExcludeAxes = false;
+ if( xProp.is() )
+ xProp->getPropertyValue( "PosSizeExcludeAxes" ) >>= bPosSizeExcludeAxes;
+
+ //size:
+ css::chart2::RelativeSize aRelativeSize;
+ if( xProp.is() && (xProp->getPropertyValue( "RelativeSize" )>>=aRelativeSize) )
+ {
+ rParam.maRemainingSpace.Height = static_cast<sal_Int32>(aRelativeSize.Secondary*rPageSize.Height);
+ rParam.maRemainingSpace.Width = static_cast<sal_Int32>(aRelativeSize.Primary*rPageSize.Width);
+ rParam.mbUseFixedInnerSize = bPosSizeExcludeAxes;
+ }
+
+ if (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0)
+ return false;
+
+ //position:
+ chart2::RelativePosition aRelativePosition;
+ if( xProp.is() && (xProp->getPropertyValue( "RelativePosition" )>>=aRelativePosition) )
+ {
+ //@todo decide whether x is primary or secondary
+
+ //the coordinates re relative to the page
+ double fX = aRelativePosition.Primary*rPageSize.Width;
+ double fY = aRelativePosition.Secondary*rPageSize.Height;
+
+ awt::Point aPos = RelativePositionHelper::getUpperLeftCornerOfAnchoredObject(
+ awt::Point(static_cast<sal_Int32>(fX),static_cast<sal_Int32>(fY)),
+ awt::Size(rParam.maRemainingSpace.Width, rParam.maRemainingSpace.Height),
+ aRelativePosition.Anchor);
+
+ rParam.maRemainingSpace.X = aPos.X;
+ rParam.maRemainingSpace.Y = aPos.Y;
+
+ rParam.mbUseFixedInnerSize = bPosSizeExcludeAxes;
+ }
+
+ //ensure that the diagram does not lap out right side or out of bottom
+ if (rParam.maRemainingSpace.Y + rParam.maRemainingSpace.Height > rPageSize.Height)
+ rParam.maRemainingSpace.Height = rPageSize.Height - rParam.maRemainingSpace.Y;
+
+ if (rParam.maRemainingSpace.X + rParam.maRemainingSpace.Width > rPageSize.Width)
+ rParam.maRemainingSpace.Width = rPageSize.Width - rParam.maRemainingSpace.X;
+
+ return true;
+}
+
+enum class TitleAlignment { ALIGN_LEFT, ALIGN_TOP, ALIGN_RIGHT, ALIGN_BOTTOM, ALIGN_Z };
+
+void changePositionOfAxisTitle( VTitle* pVTitle, TitleAlignment eAlignment
+ , awt::Rectangle const & rDiagramPlusAxesRect, const awt::Size & rPageSize )
+{
+ if(!pVTitle)
+ return;
+
+ awt::Point aNewPosition(0,0);
+ awt::Size aTitleSize = pVTitle->getFinalSize();
+ sal_Int32 nYDistance = static_cast<sal_Int32>(rPageSize.Height * constPageLayoutDistancePercentage);
+ sal_Int32 nXDistance = static_cast<sal_Int32>(rPageSize.Width * constPageLayoutDistancePercentage);
+ switch (eAlignment)
+ {
+ case TitleAlignment::ALIGN_TOP:
+ aNewPosition = awt::Point( rDiagramPlusAxesRect.X + rDiagramPlusAxesRect.Width/2
+ , rDiagramPlusAxesRect.Y - aTitleSize.Height/2 - nYDistance );
+ break;
+ case TitleAlignment::ALIGN_BOTTOM:
+ aNewPosition = awt::Point( rDiagramPlusAxesRect.X + rDiagramPlusAxesRect.Width/2
+ , rDiagramPlusAxesRect.Y + rDiagramPlusAxesRect.Height + aTitleSize.Height/2 + nYDistance );
+ break;
+ case TitleAlignment::ALIGN_LEFT:
+ aNewPosition = awt::Point( rDiagramPlusAxesRect.X - aTitleSize.Width/2 - nXDistance
+ , rDiagramPlusAxesRect.Y + rDiagramPlusAxesRect.Height/2 );
+ break;
+ case TitleAlignment::ALIGN_RIGHT:
+ aNewPosition = awt::Point( rDiagramPlusAxesRect.X + rDiagramPlusAxesRect.Width + aTitleSize.Width/2 + nXDistance
+ , rDiagramPlusAxesRect.Y + rDiagramPlusAxesRect.Height/2 );
+ break;
+ case TitleAlignment::ALIGN_Z:
+ aNewPosition = awt::Point( rDiagramPlusAxesRect.X + rDiagramPlusAxesRect.Width + aTitleSize.Width/2 + nXDistance
+ , rDiagramPlusAxesRect.Y + rDiagramPlusAxesRect.Height - aTitleSize.Height/2 );
+ break;
+ }
+
+ sal_Int32 nMaxY = rPageSize.Height - aTitleSize.Height/2;
+ sal_Int32 nMaxX = rPageSize.Width - aTitleSize.Width/2;
+ sal_Int32 nMinX = aTitleSize.Width/2;
+ sal_Int32 nMinY = aTitleSize.Height/2;
+ if( aNewPosition.Y > nMaxY )
+ aNewPosition.Y = nMaxY;
+ if( aNewPosition.X > nMaxX )
+ aNewPosition.X = nMaxX;
+ if( aNewPosition.Y < nMinY )
+ aNewPosition.Y = nMinY;
+ if( aNewPosition.X < nMinX )
+ aNewPosition.X = nMinX;
+
+ pVTitle->changePosition( aNewPosition );
+}
+
+std::shared_ptr<VTitle> lcl_createTitle( TitleHelper::eTitleType eType
+ , const rtl::Reference<SvxShapeGroupAnyD>& xPageShapes
+ , ChartModel& rModel
+ , awt::Rectangle& rRemainingSpace
+ , const awt::Size & rPageSize
+ , TitleAlignment eAlignment
+ , bool& rbAutoPosition )
+{
+ std::shared_ptr<VTitle> apVTitle;
+
+ // #i109336# Improve auto positioning in chart
+ double fPercentage = constPageLayoutDistancePercentage;
+ sal_Int32 nXDistance = static_cast< sal_Int32 >( rPageSize.Width * fPercentage );
+ sal_Int32 nYDistance = static_cast< sal_Int32 >( rPageSize.Height * fPercentage );
+ if ( eType == TitleHelper::MAIN_TITLE )
+ {
+ nYDistance += 135; // 1/100 mm
+ }
+ else if ( eType == TitleHelper::TITLE_AT_STANDARD_X_AXIS_POSITION )
+ {
+ nYDistance = 420; // 1/100 mm
+ }
+ else if ( eType == TitleHelper::TITLE_AT_STANDARD_Y_AXIS_POSITION )
+ {
+ nXDistance = 450; // 1/100 mm
+ }
+
+ uno::Reference< XTitle > xTitle( TitleHelper::getTitle( eType, rModel ) );
+ OUString aCompleteString = TitleHelper::getCompleteString(xTitle);
+ if (aCompleteString.isEmpty() || !VTitle::isVisible(xTitle))
+ return apVTitle;
+
+ //create title
+ awt::Size aTextMaxWidth(rPageSize.Width, rPageSize.Height);
+ bool bYAxisTitle = false;
+ if (eType == TitleHelper::MAIN_TITLE || eType == TitleHelper::SUB_TITLE)
+ {
+ aTextMaxWidth.Width = static_cast<sal_Int32>(rPageSize.Width * 0.8);
+ aTextMaxWidth.Height = static_cast<sal_Int32>(rPageSize.Height * 0.5);
+ }
+ else if (eType == TitleHelper::X_AXIS_TITLE || eType == TitleHelper::SECONDARY_X_AXIS_TITLE
+ || eType == TitleHelper::TITLE_AT_STANDARD_X_AXIS_POSITION)
+ {
+ aTextMaxWidth.Width = static_cast<sal_Int32>(rPageSize.Width * 0.8);
+ aTextMaxWidth.Height = static_cast<sal_Int32>(rPageSize.Height * 0.2);
+ }
+ else if (eType == TitleHelper::Y_AXIS_TITLE || eType == TitleHelper::SECONDARY_Y_AXIS_TITLE
+ || eType == TitleHelper::TITLE_AT_STANDARD_Y_AXIS_POSITION)
+ {
+ aTextMaxWidth.Width = static_cast<sal_Int32>(rPageSize.Width * 0.2);
+ aTextMaxWidth.Height = static_cast<sal_Int32>(rPageSize.Height * 0.8);
+ bYAxisTitle = true;
+ }
+ apVTitle = std::make_shared<VTitle>(xTitle);
+ OUString aCID = ObjectIdentifier::createClassifiedIdentifierForObject(xTitle, &rModel);
+ apVTitle->init(xPageShapes, aCID);
+ apVTitle->createShapes(awt::Point(0, 0), rPageSize, aTextMaxWidth, bYAxisTitle);
+ awt::Size aTitleUnrotatedSize = apVTitle->getUnrotatedSize();
+ awt::Size aTitleSize = apVTitle->getFinalSize();
+
+ //position
+ rbAutoPosition = true;
+ awt::Point aNewPosition(0,0);
+ chart2::RelativePosition aRelativePosition;
+ uno::Reference<beans::XPropertySet> xProp(xTitle, uno::UNO_QUERY);
+ if (xProp.is() && (xProp->getPropertyValue("RelativePosition") >>= aRelativePosition))
+ {
+ rbAutoPosition = false;
+
+ //@todo decide whether x is primary or secondary
+ double fX = aRelativePosition.Primary*rPageSize.Width;
+ double fY = aRelativePosition.Secondary*rPageSize.Height;
+
+ double fAnglePi = apVTitle->getRotationAnglePi();
+ aNewPosition = RelativePositionHelper::getCenterOfAnchoredObject(
+ awt::Point(static_cast<sal_Int32>(fX),static_cast<sal_Int32>(fY))
+ , aTitleUnrotatedSize, aRelativePosition.Anchor, fAnglePi );
+ }
+ else //auto position
+ {
+ switch( eAlignment )
+ {
+ case TitleAlignment::ALIGN_TOP:
+ aNewPosition = awt::Point( rRemainingSpace.X + rRemainingSpace.Width/2
+ , rRemainingSpace.Y + aTitleSize.Height/2 + nYDistance );
+ break;
+ case TitleAlignment::ALIGN_BOTTOM:
+ aNewPosition = awt::Point( rRemainingSpace.X + rRemainingSpace.Width/2
+ , rRemainingSpace.Y + rRemainingSpace.Height - aTitleSize.Height/2 - nYDistance );
+ break;
+ case TitleAlignment::ALIGN_LEFT:
+ aNewPosition = awt::Point( rRemainingSpace.X + aTitleSize.Width/2 + nXDistance
+ , rRemainingSpace.Y + rRemainingSpace.Height/2 );
+ break;
+ case TitleAlignment::ALIGN_RIGHT:
+ aNewPosition = awt::Point( rRemainingSpace.X + rRemainingSpace.Width - aTitleSize.Width/2 - nXDistance
+ , rRemainingSpace.Y + rRemainingSpace.Height/2 );
+ break;
+ case TitleAlignment::ALIGN_Z:
+ break;
+
+ }
+ }
+ apVTitle->changePosition( aNewPosition );
+
+ //remaining space
+ switch( eAlignment )
+ {
+ case TitleAlignment::ALIGN_TOP:
+ // Push the remaining space down from top.
+ rRemainingSpace.Y += ( aTitleSize.Height + nYDistance );
+ rRemainingSpace.Height -= ( aTitleSize.Height + nYDistance );
+ break;
+ case TitleAlignment::ALIGN_BOTTOM:
+ // Push the remaining space up from bottom.
+ rRemainingSpace.Height -= ( aTitleSize.Height + nYDistance );
+ break;
+ case TitleAlignment::ALIGN_LEFT:
+ // Push the remaining space to the right from left edge.
+ rRemainingSpace.X += ( aTitleSize.Width + nXDistance );
+ rRemainingSpace.Width -= ( aTitleSize.Width + nXDistance );
+ break;
+ case TitleAlignment::ALIGN_RIGHT:
+ // Push the remaining space to the left from right edge.
+ rRemainingSpace.Width -= ( aTitleSize.Width + nXDistance );
+ break;
+ case TitleAlignment::ALIGN_Z:
+ break;
+ }
+
+ return apVTitle;
+}
+
+bool lcl_createLegend( const rtl::Reference< Legend > & xLegend
+ , const rtl::Reference<SvxShapeGroupAnyD>& xPageShapes
+ , const uno::Reference< uno::XComponentContext > & xContext
+ , awt::Rectangle & rRemainingSpace
+ , const awt::Size & rPageSize
+ , ChartModel& rModel
+ , std::vector< LegendEntryProvider* >&& rLegendEntryProviderList
+ , sal_Int16 nDefaultWritingMode )
+{
+ if (!VLegend::isVisible(xLegend))
+ return false;
+
+ awt::Size rDefaultLegendSize;
+ VLegend aVLegend( xLegend, xContext, std::move(rLegendEntryProviderList),
+ xPageShapes, rModel);
+ aVLegend.setDefaultWritingMode( nDefaultWritingMode );
+ aVLegend.createShapes( awt::Size( rRemainingSpace.Width, rRemainingSpace.Height ),
+ rPageSize, rDefaultLegendSize );
+ aVLegend.changePosition( rRemainingSpace, rPageSize, rDefaultLegendSize );
+ return true;
+}
+
+void lcl_createButtons(const rtl::Reference<SvxShapeGroupAnyD>& xPageShapes,
+ ChartModel& rModel,
+ awt::Rectangle& rRemainingSpace)
+{
+ uno::Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider(rModel.getDataProvider(), uno::UNO_QUERY);
+ if (!xPivotTableDataProvider.is())
+ return;
+
+ uno::Reference<beans::XPropertySet> xModelPage(rModel.getPageBackground());
+
+ awt::Size aSize(4000, 700); // size of the button
+
+ tools::Long x = 0;
+
+ if (xPivotTableDataProvider->getPageFields().hasElements())
+ {
+ x = 0;
+
+ const css::uno::Sequence<chart2::data::PivotTableFieldEntry> aPivotFieldEntries = xPivotTableDataProvider->getPageFields();
+ for (css::chart2::data::PivotTableFieldEntry const & rPageFieldEntry : aPivotFieldEntries)
+ {
+ VButton aButton;
+ aButton.init(xPageShapes);
+ awt::Point aNewPosition(rRemainingSpace.X + x + 100, rRemainingSpace.Y + 100);
+ sal_Int32 nDimensionIndex = rPageFieldEntry.DimensionIndex;
+ OUString aFieldOutputDescription = xPivotTableDataProvider->getFieldOutputDescription(nDimensionIndex);
+ aButton.setLabel(rPageFieldEntry.Name + " | " + aFieldOutputDescription);
+ aButton.setCID("FieldButton.Page." + OUString::number(nDimensionIndex));
+ aButton.setPosition(aNewPosition);
+ aButton.setSize(aSize);
+ if (rPageFieldEntry.HasHiddenMembers)
+ aButton.setArrowColor(Color(0x0000FF));
+
+ aButton.createShapes(xModelPage);
+ x += aSize.Width + 100;
+ }
+ rRemainingSpace.Y += (aSize.Height + 100 + 100);
+ rRemainingSpace.Height -= (aSize.Height + 100 + 100);
+ }
+
+ aSize = awt::Size(3000, 700); // size of the button
+
+ if (!xPivotTableDataProvider->getRowFields().hasElements())
+ return;
+
+ x = 200;
+ const css::uno::Sequence<chart2::data::PivotTableFieldEntry> aPivotFieldEntries = xPivotTableDataProvider->getRowFields();
+ for (css::chart2::data::PivotTableFieldEntry const & rRowFieldEntry : aPivotFieldEntries)
+ {
+ VButton aButton;
+ aButton.init(xPageShapes);
+ awt::Point aNewPosition(rRemainingSpace.X + x + 100,
+ rRemainingSpace.Y + rRemainingSpace.Height - aSize.Height - 100);
+ aButton.setLabel(rRowFieldEntry.Name);
+ aButton.setCID("FieldButton.Row." + OUString::number(rRowFieldEntry.DimensionIndex));
+ aButton.setPosition(aNewPosition);
+ aButton.setSize(aSize);
+ if ( rRowFieldEntry.Name == "Data" )
+ {
+ aButton.setBGColor( Color(0x00F6F6F6) );
+ aButton.showArrow( false );
+ }
+ else if (rRowFieldEntry.HasHiddenMembers)
+ aButton.setArrowColor(Color(0x0000FF));
+ aButton.createShapes(xModelPage);
+ x += aSize.Width + 100;
+ }
+ rRemainingSpace.Height -= (aSize.Height + 100 + 100);
+}
+
+void formatPage(
+ ChartModel& rChartModel
+ , const awt::Size& rPageSize
+ , const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ )
+{
+ try
+ {
+ uno::Reference< beans::XPropertySet > xModelPage( rChartModel.getPageBackground());
+ if( ! xModelPage.is())
+ return;
+
+ //format page
+ tPropertyNameValueMap aNameValueMap;
+ PropertyMapper::getValueMap( aNameValueMap, PropertyMapper::getPropertyNameMapForFillAndLineProperties(), xModelPage );
+
+ OUString aCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, u"" ) );
+ aNameValueMap.emplace( "Name", uno::Any( aCID ) ); //CID OUString
+
+ tNameSequence aNames;
+ tAnySequence aValues;
+ PropertyMapper::getMultiPropertyListsFromValueMap( aNames, aValues, aNameValueMap );
+
+ ShapeFactory::createRectangle(
+ xTarget, rPageSize, awt::Point(0, 0), aNames, aValues);
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2" );
+ }
+}
+
+void lcl_removeEmptyGroupShapes( const SdrObject& rParent )
+{
+ SdrObjList* pObjList = rParent.getChildrenOfSdrObject();
+ if (!pObjList || pObjList->GetObjCount() == 0)
+ return;
+
+ //iterate from back!
+ for(auto nIdx = static_cast<sal_Int32>(pObjList->GetObjCount() - 1); nIdx >= 0; --nIdx)
+ {
+ SdrObject* pChildSdrObject = pObjList->GetObj(nIdx);
+ SdrObjList* pChildObjList = pChildSdrObject->getChildrenOfSdrObject();
+ if (!pChildObjList)
+ continue;
+ if (pChildObjList->GetObjCount() == 0)
+ {
+ //remove empty group shape
+ SdrObject* pRemoved = pObjList->NbcRemoveObject(nIdx);
+ SdrObject::Free( pRemoved );
+ }
+ else
+ lcl_removeEmptyGroupShapes(*pChildSdrObject);
+ }
+}
+
+}
+
+void ChartView::impl_refreshAddIn()
+{
+ if( !m_bRefreshAddIn )
+ return;
+
+ uno::Reference< beans::XPropertySet > xProp( static_cast< ::cppu::OWeakObject* >( &mrChartModel ), uno::UNO_QUERY );
+ if( !xProp.is())
+ return;
+
+ try
+ {
+ uno::Reference< util::XRefreshable > xAddIn;
+ xProp->getPropertyValue( "AddIn" ) >>= xAddIn;
+ if( xAddIn.is() )
+ {
+ bool bRefreshAddInAllowed = true;
+ xProp->getPropertyValue( "RefreshAddInAllowed" ) >>= bRefreshAddInAllowed;
+ if( bRefreshAddInAllowed )
+ xAddIn->refresh();
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+}
+
+void ChartView::createShapes()
+{
+ SolarMutexGuard aSolarGuard;
+
+ osl::MutexGuard aTimedGuard(maTimeMutex);
+ if(mrChartModel.isTimeBased())
+ {
+ maTimeBased.bTimeBased = true;
+ }
+
+ //make sure add-in is refreshed after creating the shapes
+ const ::comphelper::ScopeGuard aGuard( [this]() { this->impl_refreshAddIn(); } );
+
+ m_aResultingDiagramRectangleExcludingAxes = awt::Rectangle(0,0,0,0);
+ impl_deleteCoordinateSystems();
+ if( m_pDrawModelWrapper )
+ {
+ // #i12587# support for shapes in chart
+ m_pDrawModelWrapper->getSdrModel().EnableUndo( false );
+ m_pDrawModelWrapper->clearMainDrawPage();
+ }
+
+ lcl_setDefaultWritingMode( m_pDrawModelWrapper, mrChartModel );
+
+ awt::Size aPageSize = mrChartModel.getVisualAreaSize( embed::Aspects::MSOLE_CONTENT );
+
+ if(!mxRootShape.is())
+ mxRootShape = ShapeFactory::getOrCreateChartRootShape( m_xDrawPage );
+
+ SdrPage* pPage = ChartView::getSdrPage();
+ if(pPage) //it is necessary to use the implementation here as the uno page does not provide a propertyset
+ pPage->SetSize(Size(aPageSize.Width,aPageSize.Height));
+ else
+ {
+ OSL_FAIL("could not set page size correctly");
+ }
+ ShapeFactory::setPageSize(mxRootShape, aPageSize);
+
+ createShapes2D(aPageSize);
+
+ // #i12587# support for shapes in chart
+ if ( m_pDrawModelWrapper )
+ {
+ m_pDrawModelWrapper->getSdrModel().EnableUndo( true );
+ }
+
+ if(maTimeBased.bTimeBased)
+ {
+ maTimeBased.nFrame++;
+ }
+}
+
+// util::XEventListener (base of XCloseListener)
+void SAL_CALL ChartView::disposing( const lang::EventObject& /* rSource */ )
+{
+}
+
+void ChartView::impl_updateView( bool bCheckLockedCtrler )
+{
+ if( !m_pDrawModelWrapper )
+ return;
+
+ // #i12587# support for shapes in chart
+ if ( m_bSdrViewIsInEditMode )
+ {
+ return;
+ }
+
+ if (bCheckLockedCtrler && mrChartModel.hasControllersLocked())
+ return;
+
+ if( !m_bViewDirty || m_bInViewUpdate )
+ return;
+
+ m_bInViewUpdate = true;
+ //bool bOldRefreshAddIn = m_bRefreshAddIn;
+ //m_bRefreshAddIn = false;
+ try
+ {
+ impl_notifyModeChangeListener("invalid");
+
+ //prepare draw model
+ {
+ SolarMutexGuard aSolarGuard;
+ m_pDrawModelWrapper->lockControllers();
+ }
+
+ //create chart view
+ {
+ m_bViewDirty = false;
+ m_bViewUpdatePending = false;
+ createShapes();
+
+ if( m_bViewDirty )
+ {
+ //avoid recursions due to add-in
+ m_bRefreshAddIn = false;
+ m_bViewDirty = false;
+ m_bViewUpdatePending = false;
+ //delete old chart view
+ createShapes();
+ m_bRefreshAddIn = true;
+ }
+ }
+
+ m_bViewDirty = m_bViewUpdatePending;
+ m_bViewUpdatePending = false;
+ m_bInViewUpdate = false;
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2" );
+ m_bViewDirty = m_bViewUpdatePending;
+ m_bViewUpdatePending = false;
+ m_bInViewUpdate = false;
+ }
+
+ {
+ SolarMutexGuard aSolarGuard;
+ m_pDrawModelWrapper->unlockControllers();
+ }
+
+ impl_notifyModeChangeListener("valid");
+
+ //m_bRefreshAddIn = bOldRefreshAddIn;
+}
+
+// ____ XModifyListener ____
+void SAL_CALL ChartView::modified( const lang::EventObject& /* aEvent */ )
+{
+ m_bViewDirty = true;
+ if( m_bInViewUpdate )
+ m_bViewUpdatePending = true;
+
+ impl_notifyModeChangeListener("dirty");
+}
+
+//SfxListener
+void ChartView::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
+{
+ //#i77362 change notification for changes on additional shapes are missing
+ if( m_bInViewUpdate )
+ return;
+
+ // #i12587# support for shapes in chart
+ if ( m_bSdrViewIsInEditMode )
+ {
+ uno::Reference< view::XSelectionSupplier > xSelectionSupplier( mrChartModel.getCurrentController(), uno::UNO_QUERY );
+ if ( xSelectionSupplier.is() )
+ {
+ OUString aSelObjCID;
+ uno::Any aSelObj( xSelectionSupplier->getSelection() );
+ aSelObj >>= aSelObjCID;
+ if ( !aSelObjCID.isEmpty() )
+ {
+ return;
+ }
+ }
+ }
+
+ if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
+ return;
+ const SdrHint* pSdrHint = static_cast< const SdrHint* >(&rHint);
+
+ bool bShapeChanged = false;
+ switch( pSdrHint->GetKind() )
+ {
+ case SdrHintKind::ObjectChange:
+ bShapeChanged = true;
+ break;
+ case SdrHintKind::ObjectInserted:
+ bShapeChanged = true;
+ break;
+ case SdrHintKind::ObjectRemoved:
+ bShapeChanged = true;
+ break;
+ case SdrHintKind::ModelCleared:
+ bShapeChanged = true;
+ break;
+ case SdrHintKind::EndEdit:
+ bShapeChanged = true;
+ break;
+ default:
+ break;
+ }
+
+ if(bShapeChanged)
+ {
+ //#i76053# do not send view modified notifications for changes on the hidden page which contains e.g. the symbols for the dialogs
+ if( ChartView::getSdrPage() != pSdrHint->GetPage() )
+ bShapeChanged=false;
+ }
+
+ if(!bShapeChanged)
+ return;
+
+ mrChartModel.setModified(true);
+}
+
+void ChartView::impl_notifyModeChangeListener( const OUString& rNewMode )
+{
+ try
+ {
+ comphelper::OInterfaceContainerHelper2* pIC = m_aListenerContainer
+ .getContainer( cppu::UnoType<util::XModeChangeListener>::get());
+ if( pIC )
+ {
+ util::ModeChangeEvent aEvent( static_cast< uno::XWeak* >( this ), rNewMode );
+ comphelper::OInterfaceIteratorHelper2 aIt( *pIC );
+ while( aIt.hasMoreElements() )
+ {
+ static_cast< util::XModeChangeListener* >( aIt.next() )->modeChanged( aEvent );
+ }
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+// ____ XModeChangeBroadcaster ____
+
+void SAL_CALL ChartView::addModeChangeListener( const uno::Reference< util::XModeChangeListener >& xListener )
+{
+ m_aListenerContainer.addInterface(
+ cppu::UnoType<util::XModeChangeListener>::get(), xListener );
+}
+void SAL_CALL ChartView::removeModeChangeListener( const uno::Reference< util::XModeChangeListener >& xListener )
+{
+ m_aListenerContainer.removeInterface(
+ cppu::UnoType<util::XModeChangeListener>::get(), xListener );
+}
+void SAL_CALL ChartView::addModeChangeApproveListener( const uno::Reference< util::XModeChangeApproveListener >& /* _rxListener */ )
+{
+
+}
+void SAL_CALL ChartView::removeModeChangeApproveListener( const uno::Reference< util::XModeChangeApproveListener >& /* _rxListener */ )
+{
+
+}
+
+// ____ XUpdatable ____
+void SAL_CALL ChartView::update()
+{
+ impl_updateView();
+
+ //#i100778# migrate all imported or old documents to a plot area sizing exclusive axes (in case the save settings allow for this):
+ //Although in general it is a bad idea to change the model from within the view this is exceptionally the best place to do this special conversion.
+ //When a view update is requested (what happens for creating the metafile or displaying
+ //the chart in edit mode or printing) it is most likely that all necessary information is available - like the underlying spreadsheet data for example.
+ //Those data are important for the correct axis label sizes which are needed during conversion.
+ if( DiagramHelper::switchDiagramPositioningToExcludingPositioning( mrChartModel, true, false ) )
+ impl_updateView();
+}
+
+void SAL_CALL ChartView::updateSoft()
+{
+ update();
+}
+
+void SAL_CALL ChartView::updateHard()
+{
+ impl_updateView(false);
+}
+
+// ____ XPropertySet ____
+Reference< beans::XPropertySetInfo > SAL_CALL ChartView::getPropertySetInfo()
+{
+ OSL_FAIL("not implemented");
+ return nullptr;
+}
+
+void SAL_CALL ChartView::setPropertyValue( const OUString& rPropertyName
+ , const Any& rValue )
+{
+ if( rPropertyName == "Resolution" )
+ {
+ awt::Size aNewResolution;
+ if( ! (rValue >>= aNewResolution) )
+ throw lang::IllegalArgumentException( "Property 'Resolution' requires value of type awt::Size", nullptr, 0 );
+
+ if( m_aPageResolution.Width!=aNewResolution.Width || m_aPageResolution.Height!=aNewResolution.Height )
+ {
+ //set modified only when the new resolution is higher and points were skipped before
+ bool bSetModified = m_bPointsWereSkipped && (m_aPageResolution.Width<aNewResolution.Width || m_aPageResolution.Height<aNewResolution.Height);
+
+ m_aPageResolution = aNewResolution;
+
+ if( bSetModified )
+ this->modified( lang::EventObject( static_cast< uno::XWeak* >( this ) ) );
+ }
+ }
+ else if( rPropertyName == "ZoomFactors" )
+ {
+ //#i75867# poor quality of ole's alternative view with 3D scenes and zoomfactors besides 100%
+ uno::Sequence< beans::PropertyValue > aZoomFactors;
+ if( ! (rValue >>= aZoomFactors) )
+ throw lang::IllegalArgumentException( "Property 'ZoomFactors' requires value of type Sequence< PropertyValue >", nullptr, 0 );
+
+ sal_Int32 nFilterArgs = aZoomFactors.getLength();
+ const beans::PropertyValue* pDataValues = aZoomFactors.getConstArray();
+ while( nFilterArgs-- )
+ {
+ if ( pDataValues->Name == "ScaleXNumerator" )
+ pDataValues->Value >>= m_nScaleXNumerator;
+ else if ( pDataValues->Name == "ScaleXDenominator" )
+ pDataValues->Value >>= m_nScaleXDenominator;
+ else if ( pDataValues->Name == "ScaleYNumerator" )
+ pDataValues->Value >>= m_nScaleYNumerator;
+ else if ( pDataValues->Name == "ScaleYDenominator" )
+ pDataValues->Value >>= m_nScaleYDenominator;
+
+ pDataValues++;
+ }
+ }
+ else if( rPropertyName == "SdrViewIsInEditMode" )
+ {
+ //#i77362 change notification for changes on additional shapes are missing
+ if( ! (rValue >>= m_bSdrViewIsInEditMode) )
+ throw lang::IllegalArgumentException( "Property 'SdrViewIsInEditMode' requires value of type sal_Bool", nullptr, 0 );
+ }
+ else
+ throw beans::UnknownPropertyException( "unknown property was tried to set to chart wizard " + rPropertyName, nullptr );
+}
+
+Any SAL_CALL ChartView::getPropertyValue( const OUString& rPropertyName )
+{
+ if( rPropertyName != "Resolution" )
+ throw beans::UnknownPropertyException( "unknown property was tried to get from chart wizard " + rPropertyName, nullptr );
+
+ return Any(m_aPageResolution);
+}
+
+void SAL_CALL ChartView::addPropertyChangeListener(
+ const OUString& /* aPropertyName */, const Reference< beans::XPropertyChangeListener >& /* xListener */ )
+{
+ OSL_FAIL("not implemented");
+}
+void SAL_CALL ChartView::removePropertyChangeListener(
+ const OUString& /* aPropertyName */, const Reference< beans::XPropertyChangeListener >& /* aListener */ )
+{
+ OSL_FAIL("not implemented");
+}
+
+void SAL_CALL ChartView::addVetoableChangeListener( const OUString& /* PropertyName */, const Reference< beans::XVetoableChangeListener >& /* aListener */ )
+{
+ OSL_FAIL("not implemented");
+}
+
+void SAL_CALL ChartView::removeVetoableChangeListener( const OUString& /* PropertyName */, const Reference< beans::XVetoableChangeListener >& /* aListener */ )
+{
+ OSL_FAIL("not implemented");
+}
+
+// ____ XMultiServiceFactory ____
+
+Reference< uno::XInterface > ChartView::createInstance( const OUString& aServiceSpecifier )
+{
+ SolarMutexGuard aSolarGuard;
+
+ SdrModel* pModel = ( m_pDrawModelWrapper ? &m_pDrawModelWrapper->getSdrModel() : nullptr );
+ if ( pModel )
+ {
+ if ( aServiceSpecifier == "com.sun.star.drawing.DashTable" )
+ {
+ if ( !m_xDashTable.is() )
+ {
+ m_xDashTable = SvxUnoDashTable_createInstance( pModel );
+ }
+ return m_xDashTable;
+ }
+ else if ( aServiceSpecifier == "com.sun.star.drawing.GradientTable" )
+ {
+ if ( !m_xGradientTable.is() )
+ {
+ m_xGradientTable = SvxUnoGradientTable_createInstance( pModel );
+ }
+ return m_xGradientTable;
+ }
+ else if ( aServiceSpecifier == "com.sun.star.drawing.HatchTable" )
+ {
+ if ( !m_xHatchTable.is() )
+ {
+ m_xHatchTable = SvxUnoHatchTable_createInstance( pModel );
+ }
+ return m_xHatchTable;
+ }
+ else if ( aServiceSpecifier == "com.sun.star.drawing.BitmapTable" )
+ {
+ if ( !m_xBitmapTable.is() )
+ {
+ m_xBitmapTable = SvxUnoBitmapTable_createInstance( pModel );
+ }
+ return m_xBitmapTable;
+ }
+ else if ( aServiceSpecifier == "com.sun.star.drawing.TransparencyGradientTable" )
+ {
+ if ( !m_xTransGradientTable.is() )
+ {
+ m_xTransGradientTable = SvxUnoTransGradientTable_createInstance( pModel );
+ }
+ return m_xTransGradientTable;
+ }
+ else if ( aServiceSpecifier == "com.sun.star.drawing.MarkerTable" )
+ {
+ if ( !m_xMarkerTable.is() )
+ {
+ m_xMarkerTable = SvxUnoMarkerTable_createInstance( pModel );
+ }
+ return m_xMarkerTable;
+ }
+ }
+
+ return nullptr;
+}
+
+Reference< uno::XInterface > ChartView::createInstanceWithArguments( const OUString& ServiceSpecifier, const uno::Sequence< uno::Any >& Arguments )
+{
+ OSL_ENSURE( Arguments.hasElements(), "ChartView::createInstanceWithArguments: arguments are ignored" );
+ return createInstance( ServiceSpecifier );
+}
+
+uno::Sequence< OUString > ChartView::getAvailableServiceNames()
+{
+ uno::Sequence< OUString > aServiceNames{ "com.sun.star.drawing.DashTable",
+ "com.sun.star.drawing.GradientTable",
+ "com.sun.star.drawing.HatchTable",
+ "com.sun.star.drawing.BitmapTable",
+ "com.sun.star.drawing.TransparencyGradientTable",
+ "com.sun.star.drawing.MarkerTable" };
+
+ return aServiceNames;
+}
+
+OUString ChartView::dump()
+{
+#if HAVE_FEATURE_DESKTOP
+ // Used for unit tests and in chartcontroller only, no need to drag in this when cross-compiling
+ // for non-desktop
+ impl_updateView();
+ sal_Int32 n = m_xDrawPage->getCount();
+ OUStringBuffer aBuffer;
+ for(sal_Int32 i = 0; i < n; ++i)
+ {
+ uno::Reference< drawing::XShapes > xShape(m_xDrawPage->getByIndex(i), uno::UNO_QUERY);
+ if(xShape.is())
+ {
+ OUString aString = XShapeDumper::dump(uno::Reference<drawing::XShapes>(mxRootShape));
+ aBuffer.append(aString);
+ }
+ else
+ {
+ uno::Reference< drawing::XShape > xSingleShape(m_xDrawPage->getByIndex(i), uno::UNO_QUERY);
+ if(!xSingleShape.is())
+ continue;
+ OUString aString = XShapeDumper::dump(xSingleShape);
+ aBuffer.append(aString);
+ }
+ aBuffer.append("\n\n");
+ }
+
+ return aBuffer.makeStringAndClear();
+#else
+ return OUString();
+#endif
+}
+
+void ChartView::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ChartView"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ if (m_pDrawModelWrapper)
+ {
+ m_pDrawModelWrapper->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void ChartView::setViewDirty()
+{
+ osl::MutexGuard aGuard(maTimeMutex);
+ m_bViewDirty = true;
+}
+
+IMPL_LINK_NOARG(ChartView, UpdateTimeBased, Timer *, void)
+{
+ setViewDirty();
+ update();
+}
+
+void ChartView::createShapes2D( const awt::Size& rPageSize )
+{
+ // todo: it would be nicer to just pass the page m_xDrawPage and format it,
+ // but the draw page does not support XPropertySet
+ formatPage( mrChartModel, rPageSize, mxRootShape );
+
+ CreateShapeParam2D aParam;
+ aParam.maRemainingSpace.X = 0;
+ aParam.maRemainingSpace.Y = 0;
+ aParam.maRemainingSpace.Width = rPageSize.Width;
+ aParam.maRemainingSpace.Height = rPageSize.Height;
+
+ //create the group shape for diagram and axes first to have title and legends on top of it
+ rtl::Reference< Diagram > xDiagram( mrChartModel.getFirstChartDiagram() );
+ bool bHasRelativeSize = false;
+ if( xDiagram.is() && xDiagram->getPropertyValue("RelativeSize").hasValue() )
+ bHasRelativeSize = true;
+
+ OUString aDiagramCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM, OUString::number( 0 ) ) );//todo: other index if more than one diagram is possible
+ rtl::Reference<SvxShapeGroup> xDiagramPlusAxesPlusMarkHandlesGroup_Shapes =
+ ShapeFactory::createGroup2D(mxRootShape,aDiagramCID);
+
+ aParam.mxMarkHandles = ShapeFactory::createInvisibleRectangle(
+ xDiagramPlusAxesPlusMarkHandlesGroup_Shapes, awt::Size(0,0));
+ ShapeFactory::setShapeName(aParam.mxMarkHandles, "MarkHandles");
+
+ aParam.mxPlotAreaWithAxes = ShapeFactory::createInvisibleRectangle(
+ xDiagramPlusAxesPlusMarkHandlesGroup_Shapes, awt::Size(0, 0));
+ ShapeFactory::setShapeName(aParam.mxPlotAreaWithAxes, "PlotAreaIncludingAxes");
+
+ aParam.mxDiagramWithAxesShapes = ShapeFactory::createGroup2D(xDiagramPlusAxesPlusMarkHandlesGroup_Shapes);
+
+ bool bAutoPositionDummy = true;
+
+ // create buttons
+ lcl_createButtons(mxRootShape, mrChartModel, aParam.maRemainingSpace);
+
+ lcl_createTitle(
+ TitleHelper::MAIN_TITLE, mxRootShape, mrChartModel,
+ aParam.maRemainingSpace, rPageSize, TitleAlignment::ALIGN_TOP, bAutoPositionDummy);
+ if (!bHasRelativeSize && (aParam.maRemainingSpace.Width <= 0 || aParam.maRemainingSpace.Height <= 0))
+ return;
+
+ lcl_createTitle(
+ TitleHelper::SUB_TITLE, mxRootShape, mrChartModel,
+ aParam.maRemainingSpace, rPageSize, TitleAlignment::ALIGN_TOP, bAutoPositionDummy );
+ if (!bHasRelativeSize && (aParam.maRemainingSpace.Width <= 0 || aParam.maRemainingSpace.Height <= 0))
+ return;
+
+ aParam.mpSeriesPlotterContainer = std::make_shared<SeriesPlotterContainer>(m_aVCooSysList);
+ aParam.mpSeriesPlotterContainer->initializeCooSysAndSeriesPlotter( mrChartModel );
+ if(maTimeBased.bTimeBased && maTimeBased.nFrame != 0)
+ {
+ auto& rSeriesPlotter = aParam.mpSeriesPlotterContainer->getSeriesPlotterList();
+ size_t n = rSeriesPlotter.size();
+ for(size_t i = 0; i < n; ++i)
+ {
+ std::vector<VDataSeries*> aAllNewDataSeries = rSeriesPlotter[i]->getAllSeries();
+ std::vector< VDataSeries* >& rAllOldDataSeries =
+ maTimeBased.m_aDataSeriesList[i];
+ size_t m = std::min(aAllNewDataSeries.size(), rAllOldDataSeries.size());
+ for(size_t j = 0; j < m; ++j)
+ {
+ aAllNewDataSeries[j]->setOldTimeBased(
+ rAllOldDataSeries[j], (maTimeBased.nFrame % 60)/60.0);
+ }
+ }
+ }
+
+ lcl_createLegend(
+ LegendHelper::getLegend( mrChartModel ), mxRootShape, m_xCC,
+ aParam.maRemainingSpace, rPageSize, mrChartModel, aParam.mpSeriesPlotterContainer->getLegendEntryProviderList(),
+ lcl_getDefaultWritingModeFromPool( m_pDrawModelWrapper ) );
+
+ if (!bHasRelativeSize && (aParam.maRemainingSpace.Width <= 0 || aParam.maRemainingSpace.Height <= 0))
+ return;
+
+ if (!createAxisTitleShapes2D(aParam, rPageSize, bHasRelativeSize))
+ return;
+
+ bool bDummy = false;
+ bool bIsVertical = DiagramHelper::getVertical(xDiagram, bDummy, bDummy);
+
+ if (getAvailablePosAndSizeForDiagram(aParam, rPageSize, xDiagram))
+ {
+ awt::Rectangle aUsedOuterRect = impl_createDiagramAndContent(aParam, rPageSize);
+
+ if (aParam.mxPlotAreaWithAxes.is())
+ {
+ aParam.mxPlotAreaWithAxes->setPosition(awt::Point(aUsedOuterRect.X, aUsedOuterRect.Y));
+ aParam.mxPlotAreaWithAxes->setSize(awt::Size(aUsedOuterRect.Width, aUsedOuterRect.Height));
+ }
+
+ //correct axis title position
+ awt::Rectangle aDiagramPlusAxesRect( aUsedOuterRect );
+ if (aParam.mbAutoPosTitleX)
+ changePositionOfAxisTitle(aParam.mpVTitleX.get(), TitleAlignment::ALIGN_BOTTOM, aDiagramPlusAxesRect, rPageSize);
+ if (aParam.mbAutoPosTitleY)
+ changePositionOfAxisTitle(aParam.mpVTitleY.get(), TitleAlignment::ALIGN_LEFT, aDiagramPlusAxesRect, rPageSize);
+ if (aParam.mbAutoPosTitleZ)
+ changePositionOfAxisTitle(aParam.mpVTitleZ.get(), TitleAlignment::ALIGN_Z, aDiagramPlusAxesRect, rPageSize);
+ if (aParam.mbAutoPosSecondTitleX)
+ changePositionOfAxisTitle(aParam.mpVTitleSecondX.get(), bIsVertical? TitleAlignment::ALIGN_RIGHT : TitleAlignment::ALIGN_TOP, aDiagramPlusAxesRect, rPageSize);
+ if (aParam.mbAutoPosSecondTitleY)
+ changePositionOfAxisTitle(aParam.mpVTitleSecondY.get(), bIsVertical? TitleAlignment::ALIGN_TOP : TitleAlignment::ALIGN_RIGHT, aDiagramPlusAxesRect, rPageSize);
+ }
+
+ //cleanup: remove all empty group shapes to avoid grey border lines:
+ lcl_removeEmptyGroupShapes( *mxRootShape->GetSdrObject() );
+
+ if(maTimeBased.bTimeBased && maTimeBased.nFrame % 60 == 0)
+ {
+ // create copy of the data for next frame
+ auto& rSeriesPlotter = aParam.mpSeriesPlotterContainer->getSeriesPlotterList();
+ size_t n = rSeriesPlotter.size();
+ maTimeBased.m_aDataSeriesList.clear();
+ maTimeBased.m_aDataSeriesList.resize(n);
+ for(size_t i = 0; i < n; ++i)
+ {
+ std::vector<VDataSeries*> aAllNewDataSeries = rSeriesPlotter[i]->getAllSeries();
+ std::vector<VDataSeries*>& rAllOldDataSeries = maTimeBased.m_aDataSeriesList[i];
+ size_t m = aAllNewDataSeries.size();
+ for(size_t j = 0; j < m; ++j)
+ {
+ rAllOldDataSeries.push_back( aAllNewDataSeries[j]->
+ createCopyForTimeBased() );
+ }
+ }
+
+ maTimeBased.maTimer.Stop();
+ }
+
+ if(maTimeBased.bTimeBased && !maTimeBased.maTimer.IsActive())
+ {
+ maTimeBased.maTimer.SetTimeout(15);
+ maTimeBased.maTimer.SetInvokeHandler(LINK(this, ChartView, UpdateTimeBased));
+ maTimeBased.maTimer.Start();
+ }
+}
+
+bool ChartView::createAxisTitleShapes2D( CreateShapeParam2D& rParam, const css::awt::Size& rPageSize, bool bHasRelativeSize )
+{
+ rtl::Reference<Diagram> xDiagram = mrChartModel.getFirstChartDiagram();
+
+ rtl::Reference< ChartType > xChartType( DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) );
+ sal_Int32 nDimension = DiagramHelper::getDimension( xDiagram );
+
+ if( ChartTypeHelper::isSupportingMainAxis( xChartType, nDimension, 0 ) )
+ rParam.mpVTitleX = lcl_createTitle( TitleHelper::TITLE_AT_STANDARD_X_AXIS_POSITION, mxRootShape, mrChartModel
+ , rParam.maRemainingSpace, rPageSize, TitleAlignment::ALIGN_BOTTOM, rParam.mbAutoPosTitleX );
+ if (!bHasRelativeSize && (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0))
+ return false;
+
+ if( ChartTypeHelper::isSupportingMainAxis( xChartType, nDimension, 1 ) )
+ rParam.mpVTitleY = lcl_createTitle( TitleHelper::TITLE_AT_STANDARD_Y_AXIS_POSITION, mxRootShape, mrChartModel
+ , rParam.maRemainingSpace, rPageSize, TitleAlignment::ALIGN_LEFT, rParam.mbAutoPosTitleY );
+ if (!bHasRelativeSize && (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0))
+ return false;
+
+ if( ChartTypeHelper::isSupportingMainAxis( xChartType, nDimension, 2 ) )
+ rParam.mpVTitleZ = lcl_createTitle( TitleHelper::Z_AXIS_TITLE, mxRootShape, mrChartModel
+ , rParam.maRemainingSpace, rPageSize, TitleAlignment::ALIGN_RIGHT, rParam.mbAutoPosTitleZ );
+ if (!bHasRelativeSize && (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0))
+ return false;
+
+ bool bDummy = false;
+ bool bIsVertical = DiagramHelper::getVertical( xDiagram, bDummy, bDummy );
+
+ if( ChartTypeHelper::isSupportingSecondaryAxis( xChartType, nDimension ) )
+ rParam.mpVTitleSecondX = lcl_createTitle( TitleHelper::SECONDARY_X_AXIS_TITLE, mxRootShape, mrChartModel
+ , rParam.maRemainingSpace, rPageSize, bIsVertical? TitleAlignment::ALIGN_RIGHT : TitleAlignment::ALIGN_TOP, rParam.mbAutoPosSecondTitleX );
+ if (!bHasRelativeSize && (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0))
+ return false;
+
+ if( ChartTypeHelper::isSupportingSecondaryAxis( xChartType, nDimension ) )
+ rParam.mpVTitleSecondY = lcl_createTitle( TitleHelper::SECONDARY_Y_AXIS_TITLE, mxRootShape, mrChartModel
+ , rParam.maRemainingSpace, rPageSize, bIsVertical? TitleAlignment::ALIGN_TOP : TitleAlignment::ALIGN_RIGHT, rParam.mbAutoPosSecondTitleY );
+ if (!bHasRelativeSize && (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0))
+ return false;
+
+ return true;
+}
+
+} //namespace chart
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_chart2_ChartView_get_implementation(css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ rtl::Reference<::chart::ChartModel> pChartModel = new ::chart::ChartModel(context);
+ return cppu::acquire(new ::chart::ChartView(context, *pChartModel));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/Clipping.cxx b/chart2/source/view/main/Clipping.cxx
new file mode 100644
index 000000000..713e88c26
--- /dev/null
+++ b/chart2/source/view/main/Clipping.cxx
@@ -0,0 +1,425 @@
+/* -*- 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 <Clipping.hxx>
+#include <CommonConverters.hxx>
+#include <BaseGFXHelper.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/drawing/Position3D.hpp>
+#include <com/sun/star/drawing/DoubleSequence.hpp>
+
+namespace chart
+{
+using namespace ::com::sun::star;
+using ::basegfx::B2DRectangle;
+using ::basegfx::B2DTuple;
+
+namespace{
+/** @descr This is a supporting function for lcl_clip2d. It computes a new parametric
+ value for an entering (dTE) or leaving (dTL) intersection point with one
+ of the edges bounding the clipping area.
+ For explanation of the parameters please refer to :
+
+ Liang-Biarsky parametric line-clipping algorithm as described in:
+ Computer Graphics: principles and practice, 2nd ed.,
+ James D. Foley et al.,
+ Section 3.12.4 on page 117.
+*/
+bool lcl_CLIPt(double fDenom,double fNum, double & fTE, double & fTL)
+{
+ double fT;
+
+ if (fDenom > 0) // Intersection enters: PE
+ {
+ fT = fNum / fDenom; // Parametric value at the intersection.
+ if (fT > fTL) // fTE and fTL crossover
+ return false; // therefore reject the line.
+ else if (fT > fTE) // A new fTE has been found.
+ fTE = fT;
+ }
+ else if (fDenom < 0) // Intersection leaves: PL
+ {
+ fT = fNum / fDenom; // Parametric Value at the intersection.
+ if (fT < fTE) // fTE and fTL crossover
+ return false; // therefore reject the line.
+ else if (fT < fTL) // A new fTL has been found.
+ fTL = fT;
+ }
+ else if (fNum > 0)
+ return false; // Line lies on the outside of the edge.
+
+ return true;
+}
+
+/** @descr The line given by its two endpoints rP0 and rP1 is clipped at the rectangle
+ rRectangle. If there is at least a part of it visible then sal_True is returned and
+ the endpoints of that part are stored in rP0 and rP1. The points rP0 and rP1
+ may have the same coordinates.
+ @param rP0 Start point of the line to clip. Modified to contain a start point inside
+ the clipping area if possible.
+ @param rP1 End point of the line to clip. Modified to contain an end point inside
+ the clipping area if possible.
+ @param rRectangle Clipping area.
+ @return If the line lies completely or partly inside the clipping area then TRUE
+ is returned. If the line lies completely outside then sal_False is returned and rP0 and
+ rP1 are left unmodified.
+*/
+bool lcl_clip2d(B2DTuple& rPoint0, B2DTuple& rPoint1, const B2DRectangle& rRectangle)
+{
+ //Direction vector of the line.
+ B2DTuple aDirection = rPoint1 - rPoint0;
+
+ if( aDirection.getX()==0 && aDirection.getY()==0 && rRectangle.isInside(rPoint0) )
+ {
+ // Degenerate case of a zero length line.
+ return true;
+ }
+ else
+ {
+ // Values of the line parameter where the line enters resp. leaves the rectangle.
+ double fTE = 0,
+ fTL = 1;
+
+ // Test whether at least a part lies in the four half-planes with respect to
+ // the rectangles four edges.
+ if( lcl_CLIPt(aDirection.getX(), rRectangle.getMinX() - rPoint0.getX(), fTE, fTL) )
+ if( lcl_CLIPt(-aDirection.getX(), rPoint0.getX() - rRectangle.getMaxX(), fTE, fTL) )
+ if( lcl_CLIPt(aDirection.getY(), rRectangle.getMinY() - rPoint0.getY(), fTE, fTL) )
+ if( lcl_CLIPt(-aDirection.getY(), rPoint0.getY() - rRectangle.getMaxY(), fTE, fTL) )
+ {
+ // At least a part is visible.
+ if (fTL < 1)
+ {
+ // Compute the new end point.
+ rPoint1.setX( rPoint0.getX() + fTL * aDirection.getX() );
+ rPoint1.setY( rPoint0.getY() + fTL * aDirection.getY() );
+ }
+ if (fTE > 0)
+ {
+ // Compute the new starting point.
+ rPoint0.setX( rPoint0.getX() + fTE * aDirection.getX() );
+ rPoint0.setY( rPoint0.getY() + fTE * aDirection.getY() );
+ }
+ return true;
+ }
+
+ // Line is not visible.
+ return false;
+ }
+}
+
+bool lcl_clip2d_(drawing::Position3D& rPoint0, drawing::Position3D& rPoint1, const B2DRectangle& rRectangle)
+{
+ B2DTuple aP0(rPoint0.PositionX,rPoint0.PositionY);
+ B2DTuple aP1(rPoint1.PositionX,rPoint1.PositionY);
+ bool bRet = lcl_clip2d( aP0, aP1, rRectangle );
+
+ rPoint0.PositionX = aP0.getX();
+ rPoint0.PositionY = aP0.getY();
+ rPoint1.PositionX = aP1.getX();
+ rPoint1.PositionY = aP1.getY();
+
+ return bRet;
+}
+
+unsigned int round_up_nearest_pow2(unsigned int v)
+{
+ // compute the next highest power of 2 of 32-bit v
+ --v;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ ++v;
+ return v;
+}
+
+void lcl_addPointToPoly( drawing::PolyPolygonShape3D& rPoly
+ , const drawing::Position3D& rPos
+ , sal_Int32 nPolygonIndex
+ , std::vector< sal_Int32 >& rResultPointCount
+ , sal_Int32 nReservePointCount )
+{
+ if(nPolygonIndex<0)
+ {
+ OSL_FAIL( "The polygon index needs to be > 0");
+ nPolygonIndex=0;
+ }
+
+ //make sure that we have enough polygons
+ if(nPolygonIndex >= rPoly.SequenceX.getLength() )
+ {
+ rPoly.SequenceX.realloc(nPolygonIndex+1);
+ rPoly.SequenceY.realloc(nPolygonIndex+1);
+ rPoly.SequenceZ.realloc(nPolygonIndex+1);
+ rResultPointCount.resize(nPolygonIndex+1,0);
+ }
+
+ drawing::DoubleSequence* pOuterSequenceX = &rPoly.SequenceX.getArray()[nPolygonIndex];
+ drawing::DoubleSequence* pOuterSequenceY = &rPoly.SequenceY.getArray()[nPolygonIndex];
+ drawing::DoubleSequence* pOuterSequenceZ = &rPoly.SequenceZ.getArray()[nPolygonIndex];
+
+ sal_Int32 nNewResultPointCount = rResultPointCount[nPolygonIndex]+1;
+ sal_Int32 nSeqLength = pOuterSequenceX->getLength();
+
+ if( nSeqLength <= nNewResultPointCount )
+ {
+ sal_Int32 nReallocLength = nReservePointCount > SAL_MAX_INT16 ? round_up_nearest_pow2(nNewResultPointCount) * 2 : nReservePointCount;
+ if( nNewResultPointCount > nReallocLength )
+ {
+ nReallocLength = nNewResultPointCount;
+ OSL_FAIL("this should not be the case to avoid performance problems");
+ }
+ pOuterSequenceX->realloc(nReallocLength);
+ pOuterSequenceY->realloc(nReallocLength);
+ pOuterSequenceZ->realloc(nReallocLength);
+ }
+
+ double* pInnerSequenceX = pOuterSequenceX->getArray();
+ double* pInnerSequenceY = pOuterSequenceY->getArray();
+ double* pInnerSequenceZ = pOuterSequenceZ->getArray();
+
+ pInnerSequenceX[nNewResultPointCount-1] = rPos.PositionX;
+ pInnerSequenceY[nNewResultPointCount-1] = rPos.PositionY;
+ pInnerSequenceZ[nNewResultPointCount-1] = rPos.PositionZ;
+ rResultPointCount[nPolygonIndex]=nNewResultPointCount;
+}
+
+void lcl_addPointToPoly( std::vector<std::vector<css::drawing::Position3D>>& rPoly
+ , const drawing::Position3D& rPos
+ , sal_Int32 nPolygonIndex
+ , std::vector< sal_Int32 >& rResultPointCount
+ , sal_Int32 nReservePointCount )
+{
+ if(nPolygonIndex<0)
+ {
+ OSL_FAIL( "The polygon index needs to be > 0");
+ nPolygonIndex=0;
+ }
+
+ //make sure that we have enough polygons
+ if(o3tl::make_unsigned(nPolygonIndex) >= rPoly.size() )
+ {
+ rPoly.resize(nPolygonIndex+1);
+ rResultPointCount.resize(nPolygonIndex+1,0);
+ }
+
+ std::vector<css::drawing::Position3D>* pOuterSequence = &rPoly[nPolygonIndex];
+
+ sal_Int32 nNewResultPointCount = rResultPointCount[nPolygonIndex]+1;
+ sal_Int32 nSeqLength = pOuterSequence->size();
+
+ if( nSeqLength <= nNewResultPointCount )
+ {
+ sal_Int32 nReallocLength = nReservePointCount > SAL_MAX_INT16 ? round_up_nearest_pow2(nNewResultPointCount) * 2 : nReservePointCount;
+ if( nNewResultPointCount > nReallocLength )
+ {
+ nReallocLength = nNewResultPointCount;
+ OSL_FAIL("this should not be the case to avoid performance problems");
+ }
+ pOuterSequence->resize(nReallocLength);
+ }
+
+ css::drawing::Position3D* pInnerSequence = pOuterSequence->data();
+
+ pInnerSequence[nNewResultPointCount-1] = rPos;
+ rResultPointCount[nPolygonIndex]=nNewResultPointCount;
+}
+
+}//end anonymous namespace
+
+void Clipping::clipPolygonAtRectangle( const drawing::PolyPolygonShape3D& rPolygon
+ , const B2DRectangle& rRectangle
+ , drawing::PolyPolygonShape3D& aResult
+ , bool bSplitPiecesToDifferentPolygons )
+{
+ aResult.SequenceX.realloc(0);
+ aResult.SequenceY.realloc(0);
+ aResult.SequenceZ.realloc(0);
+
+ if(!rPolygon.SequenceX.hasElements())
+ return;
+
+ //need clipping?:
+ {
+ ::basegfx::B3DRange a3DRange( BaseGFXHelper::getBoundVolume( rPolygon ) );
+ ::basegfx::B2DRange a2DRange( a3DRange.getMinX(), a3DRange.getMinY(), a3DRange.getMaxX(), a3DRange.getMaxY() );
+ if( rRectangle.isInside( a2DRange ) )
+ {
+ aResult = rPolygon;
+ return;
+ }
+ else
+ {
+ a2DRange.intersect( rRectangle );
+ if( a2DRange.isEmpty() )
+ return;
+ }
+ }
+
+ std::vector< sal_Int32 > aResultPointCount;//per polygon index
+
+ //apply clipping:
+ drawing::Position3D aFrom;
+ drawing::Position3D aTo;
+
+ sal_Int32 nNewPolyIndex = 0;
+ sal_Int32 nOldPolyCount = rPolygon.SequenceX.getLength();
+ for(sal_Int32 nOldPolyIndex=0; nOldPolyIndex<nOldPolyCount; nOldPolyIndex++, nNewPolyIndex++ )
+ {
+ sal_Int32 nOldPointCount = rPolygon.SequenceX[nOldPolyIndex].getLength();
+
+ // set last point to a position outside the rectangle, such that the first
+ // time lcl_clip2d returns true, the comparison to last will always yield false
+ drawing::Position3D aLast(rRectangle.getMinX()-1.0,rRectangle.getMinY()-1.0, 0.0 );
+
+ for(sal_Int32 nOldPoint=1; nOldPoint<nOldPointCount; nOldPoint++)
+ {
+ aFrom = getPointFromPoly(rPolygon,nOldPoint-1,nOldPolyIndex);
+ aTo = getPointFromPoly(rPolygon,nOldPoint,nOldPolyIndex);
+ if( lcl_clip2d_(aFrom, aTo, rRectangle) )
+ {
+ // compose a Polygon of as many consecutive points as possible
+ if(aFrom == aLast)
+ {
+ if( aTo != aFrom )
+ {
+ lcl_addPointToPoly( aResult, aTo, nNewPolyIndex, aResultPointCount, nOldPointCount );
+ }
+ }
+ else
+ {
+ if( bSplitPiecesToDifferentPolygons && nOldPoint!=1 )
+ {
+ if( nNewPolyIndex < aResult.SequenceX.getLength()
+ && aResultPointCount[nNewPolyIndex]>0 )
+ nNewPolyIndex++;
+ }
+ lcl_addPointToPoly( aResult, aFrom, nNewPolyIndex, aResultPointCount, nOldPointCount );
+ if( aTo != aFrom )
+ lcl_addPointToPoly( aResult, aTo, nNewPolyIndex, aResultPointCount, nOldPointCount );
+ }
+ aLast = aTo;
+ }
+ }
+ }
+ //free unused space
+ for( sal_Int32 nPolygonIndex = aResultPointCount.size(); nPolygonIndex--; )
+ {
+ drawing::DoubleSequence* pOuterSequenceX = &aResult.SequenceX.getArray()[nPolygonIndex];
+ drawing::DoubleSequence* pOuterSequenceY = &aResult.SequenceY.getArray()[nPolygonIndex];
+ drawing::DoubleSequence* pOuterSequenceZ = &aResult.SequenceZ.getArray()[nPolygonIndex];
+
+ sal_Int32 nUsedPointCount = aResultPointCount[nPolygonIndex];
+ pOuterSequenceX->realloc(nUsedPointCount);
+ pOuterSequenceY->realloc(nUsedPointCount);
+ pOuterSequenceZ->realloc(nUsedPointCount);
+ }
+}
+
+void Clipping::clipPolygonAtRectangle( const std::vector<std::vector<css::drawing::Position3D>>& rPolygon
+ , const B2DRectangle& rRectangle
+ , std::vector<std::vector<css::drawing::Position3D>>& aResult
+ , bool bSplitPiecesToDifferentPolygons )
+{
+ aResult.clear();
+
+ if(rPolygon.empty())
+ return;
+
+ //need clipping?:
+ {
+ ::basegfx::B3DRange a3DRange( BaseGFXHelper::getBoundVolume( rPolygon ) );
+ ::basegfx::B2DRange a2DRange( a3DRange.getMinX(), a3DRange.getMinY(), a3DRange.getMaxX(), a3DRange.getMaxY() );
+ if( rRectangle.isInside( a2DRange ) )
+ {
+ aResult = rPolygon;
+ return;
+ }
+ else
+ {
+ a2DRange.intersect( rRectangle );
+ if( a2DRange.isEmpty() )
+ return;
+ }
+ }
+
+ std::vector< sal_Int32 > aResultPointCount;//per polygon index
+
+ //apply clipping:
+ drawing::Position3D aFrom;
+ drawing::Position3D aTo;
+
+ sal_Int32 nNewPolyIndex = 0;
+ sal_Int32 nOldPolyCount = rPolygon.size();
+ for(sal_Int32 nOldPolyIndex=0; nOldPolyIndex<nOldPolyCount; nOldPolyIndex++, nNewPolyIndex++ )
+ {
+ sal_Int32 nOldPointCount = rPolygon[nOldPolyIndex].size();
+
+ // set last point to a position outside the rectangle, such that the first
+ // time lcl_clip2d returns true, the comparison to last will always yield false
+ drawing::Position3D aLast(rRectangle.getMinX()-1.0,rRectangle.getMinY()-1.0, 0.0 );
+
+ for(sal_Int32 nOldPoint=1; nOldPoint<nOldPointCount; nOldPoint++)
+ {
+ aFrom = getPointFromPoly(rPolygon,nOldPoint-1,nOldPolyIndex);
+ aTo = getPointFromPoly(rPolygon,nOldPoint,nOldPolyIndex);
+ if( lcl_clip2d_(aFrom, aTo, rRectangle) )
+ {
+ // compose a Polygon of as many consecutive points as possible
+ if(aFrom == aLast)
+ {
+ if( aTo != aFrom )
+ {
+ lcl_addPointToPoly( aResult, aTo, nNewPolyIndex, aResultPointCount, nOldPointCount );
+ }
+ }
+ else
+ {
+ if( bSplitPiecesToDifferentPolygons && nOldPoint!=1 )
+ {
+ if( nNewPolyIndex < static_cast<sal_Int32>(aResult.size())
+ && aResultPointCount[nNewPolyIndex]>0 )
+ nNewPolyIndex++;
+ }
+ lcl_addPointToPoly( aResult, aFrom, nNewPolyIndex, aResultPointCount, nOldPointCount );
+ if( aTo != aFrom )
+ lcl_addPointToPoly( aResult, aTo, nNewPolyIndex, aResultPointCount, nOldPointCount );
+ }
+ aLast = aTo;
+ }
+ }
+ }
+ //free unused space
+ for( sal_Int32 nPolygonIndex = aResultPointCount.size(); nPolygonIndex--; )
+ {
+ std::vector<css::drawing::Position3D>* pOuterSequence = &aResult[nPolygonIndex];
+
+ sal_Int32 nUsedPointCount = aResultPointCount[nPolygonIndex];
+ pOuterSequence->resize(nUsedPointCount);
+ }
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/DataPointSymbolSupplier.cxx b/chart2/source/view/main/DataPointSymbolSupplier.cxx
new file mode 100644
index 000000000..ff7f8370a
--- /dev/null
+++ b/chart2/source/view/main/DataPointSymbolSupplier.cxx
@@ -0,0 +1,44 @@
+/* -*- 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 <chartview/DataPointSymbolSupplier.hxx>
+#include <ShapeFactory.hxx>
+#include <com/sun/star/drawing/Position3D.hpp>
+
+namespace chart
+{
+using namespace ::com::sun::star;
+
+rtl::Reference< SvxShapeGroup > DataPointSymbolSupplier::create2DSymbolList(
+ const rtl::Reference<SvxDrawPage>& xTarget
+ , const drawing::Direction3D& rSize )
+{
+ rtl::Reference< SvxShapeGroup > xGroupShapes = ShapeFactory::createGroup2D( xTarget );
+
+ drawing::Position3D aPos(0,0,0);
+ for(sal_Int32 nS=0;nS<ShapeFactory::getSymbolCount();nS++)
+ {
+ ShapeFactory::createSymbol2D( xGroupShapes, aPos, rSize, nS, 0, 0 );
+ }
+ return xGroupShapes;
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/DrawModelWrapper.cxx b/chart2/source/view/main/DrawModelWrapper.cxx
new file mode 100644
index 000000000..6a6488435
--- /dev/null
+++ b/chart2/source/view/main/DrawModelWrapper.cxx
@@ -0,0 +1,335 @@
+/* -*- 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 <chartview/DrawModelWrapper.hxx>
+#include <ShapeFactory.hxx>
+#include "ChartItemPool.hxx"
+#include <ObjectIdentifier.hxx>
+#include <svx/unomodel.hxx>
+#include <svl/itempool.hxx>
+#include <svx/objfac3d.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svx3ditems.hxx>
+#include <svx/xtable.hxx>
+#include <svx/svdoutl.hxx>
+#include <editeng/unolingu.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <libxml/xmlwriter.h>
+#include <osl/diagnose.h>
+
+namespace com::sun::star::linguistic2 { class XHyphenator; }
+namespace com::sun::star::linguistic2 { class XSpellChecker1; }
+
+using namespace ::com::sun::star;
+
+
+namespace chart
+{
+
+DrawModelWrapper::DrawModelWrapper()
+: SdrModel()
+{
+ m_xChartItemPool = ChartItemPool::CreateChartItemPool();
+
+ SetScaleUnit(MapUnit::Map100thMM);
+ SetScaleFraction(Fraction(1, 1));
+ SetDefaultFontHeight(423); // 12pt
+
+ SfxItemPool* pMasterPool = &GetItemPool();
+ pMasterPool->SetDefaultMetric(MapUnit::Map100thMM);
+ pMasterPool->SetPoolDefaultItem(SfxBoolItem(EE_PARA_HYPHENATE, true) );
+ pMasterPool->SetPoolDefaultItem(makeSvx3DPercentDiagonalItem (5));
+
+ // append chart pool to end of pool chain
+ pMasterPool->GetLastPoolInChain()->SetSecondaryPool(m_xChartItemPool.get());
+ pMasterPool->FreezeIdRanges();
+ SetTextDefaults();
+
+ //this factory needs to be created before first use of 3D scenes once upon an office runtime
+ //@todo in future this should be done by drawing engine itself on demand
+ static bool b3dFactoryInitialized = false;
+ if(!b3dFactoryInitialized)
+ {
+ E3dObjFactory aObjFactory;
+ b3dFactoryInitialized = true;
+ }
+
+ //Hyphenation and spellchecking
+ SdrOutliner& rOutliner = GetDrawOutliner();
+ try
+ {
+ uno::Reference< linguistic2::XHyphenator > xHyphenator( LinguMgr::GetHyphenator() );
+ if( xHyphenator.is() )
+ rOutliner.SetHyphenator( xHyphenator );
+
+ uno::Reference< linguistic2::XSpellChecker1 > xSpellChecker( LinguMgr::GetSpellChecker() );
+ if ( xSpellChecker.is() )
+ rOutliner.SetSpeller( xSpellChecker );
+ }
+ catch(...)
+ {
+ OSL_FAIL("Can't get Hyphenator or SpellChecker for chart");
+ }
+
+ //ref device for font rendering
+ OutputDevice* pDefaultDevice = rOutliner.GetRefDevice();
+ if( !pDefaultDevice )
+ pDefaultDevice = Application::GetDefaultDevice();
+ m_pRefDevice.disposeAndClear();
+ m_pRefDevice = VclPtr<VirtualDevice>::Create(*pDefaultDevice);
+ MapMode aMapMode = m_pRefDevice->GetMapMode();
+ aMapMode.SetMapUnit(MapUnit::Map100thMM);
+ m_pRefDevice->SetMapMode(aMapMode);
+ SetRefDevice(m_pRefDevice.get());
+ rOutliner.SetRefDevice(m_pRefDevice.get());
+}
+
+DrawModelWrapper::~DrawModelWrapper()
+{
+ //remove m_pChartItemPool from pool chain
+ if(m_xChartItemPool)
+ {
+ SfxItemPool* pPool = &GetItemPool();
+ for (;;)
+ {
+ SfxItemPool* pSecondary = pPool->GetSecondaryPool();
+ if(pSecondary == m_xChartItemPool.get())
+ {
+ pPool->SetSecondaryPool (nullptr);
+ break;
+ }
+ pPool = pSecondary;
+ }
+ m_xChartItemPool.clear();
+ }
+ m_pRefDevice.disposeAndClear();
+}
+
+uno::Reference< uno::XInterface > DrawModelWrapper::createUnoModel()
+{
+ uno::Reference< lang::XComponent > xComponent = new SvxUnoDrawingModel( this ); //tell Andreas Schluens if SvxUnoDrawingModel is not needed anymore -> remove export from svx to avoid link problems in writer
+ return uno::Reference< uno::XInterface >::query( xComponent );
+}
+
+uno::Reference< frame::XModel > DrawModelWrapper::getUnoModel()
+{
+ uno::Reference< uno::XInterface > xI = SdrModel::getUnoModel();
+ return uno::Reference<frame::XModel>::query( xI );
+}
+
+SdrModel& DrawModelWrapper::getSdrModel()
+{
+ return *this;
+}
+
+uno::Reference< lang::XMultiServiceFactory > DrawModelWrapper::getShapeFactory()
+{
+ uno::Reference< lang::XMultiServiceFactory > xShapeFactory( getUnoModel(), uno::UNO_QUERY );
+ return xShapeFactory;
+}
+
+const rtl::Reference<SvxDrawPage> & DrawModelWrapper::getMainDrawPage()
+{
+ if (m_xMainDrawPage.is())
+ return m_xMainDrawPage;
+
+ // Create draw page.
+ uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSuplier(getUnoModel(), uno::UNO_QUERY);
+ if (!xDrawPagesSuplier.is())
+ return m_xMainDrawPage;
+
+ uno::Reference<drawing::XDrawPages> xDrawPages = xDrawPagesSuplier->getDrawPages();
+ if (xDrawPages->getCount() > 1)
+ {
+ // Take the first page in case of multiple pages.
+ uno::Any aPage = xDrawPages->getByIndex(0);
+ uno::Reference<drawing::XDrawPage> xTmp;
+ aPage >>= xTmp;
+ m_xMainDrawPage = dynamic_cast<SvxDrawPage*>(xTmp.get());
+ assert(m_xMainDrawPage);
+ }
+
+ if (!m_xMainDrawPage.is())
+ {
+ m_xMainDrawPage = dynamic_cast<SvxDrawPage*>(xDrawPages->insertNewByIndex(0).get());
+ assert(m_xMainDrawPage);
+ }
+
+ //ensure that additional shapes are in front of the chart objects so create the chart root before
+ // let us disable this call for now
+ // TODO:moggi
+ // ShapeFactory::getOrCreateShapeFactory(getShapeFactory())->getOrCreateChartRootShape( m_xMainDrawPage );
+ return m_xMainDrawPage;
+}
+
+const rtl::Reference<SvxDrawPage> & DrawModelWrapper::getHiddenDrawPage()
+{
+ if( !m_xHiddenDrawPage.is() )
+ {
+ uno::Reference< drawing::XDrawPagesSupplier > xDrawPagesSuplier( getUnoModel(), uno::UNO_QUERY );
+ if( xDrawPagesSuplier.is() )
+ {
+ uno::Reference< drawing::XDrawPages > xDrawPages( xDrawPagesSuplier->getDrawPages () );
+ if( xDrawPages->getCount()>1 )
+ {
+ uno::Any aPage = xDrawPages->getByIndex( 1 ) ;
+ uno::Reference<drawing::XDrawPage> xTmp;
+ aPage >>= xTmp;
+ m_xHiddenDrawPage = dynamic_cast<SvxDrawPage*>(xTmp.get());
+ assert(m_xHiddenDrawPage);
+ }
+
+ if(!m_xHiddenDrawPage.is())
+ {
+ if( xDrawPages->getCount()==0 )
+ {
+ m_xMainDrawPage = dynamic_cast<SvxDrawPage*>(xDrawPages->insertNewByIndex( 0 ).get());
+ assert(m_xMainDrawPage);
+ }
+ m_xHiddenDrawPage = dynamic_cast<SvxDrawPage*>(xDrawPages->insertNewByIndex( 1 ).get());
+ assert(m_xHiddenDrawPage);
+ }
+ }
+ }
+ return m_xHiddenDrawPage;
+}
+void DrawModelWrapper::clearMainDrawPage()
+{
+ //uno::Reference<drawing::XShapes> xChartRoot( m_xMainDrawPage, uno::UNO_QUERY );
+ rtl::Reference<SvxShapeGroupAnyD> xChartRoot( ShapeFactory::getChartRootShape( m_xMainDrawPage ) );
+ if( xChartRoot.is() )
+ {
+ sal_Int32 nSubCount = xChartRoot->getCount();
+ uno::Reference< drawing::XShape > xShape;
+ for( sal_Int32 nS = nSubCount; nS--; )
+ {
+ if( xChartRoot->getByIndex( nS ) >>= xShape )
+ xChartRoot->remove( xShape );
+ }
+ }
+}
+
+rtl::Reference<SvxShapeGroupAnyD> DrawModelWrapper::getChartRootShape( const rtl::Reference<SvxDrawPage>& xDrawPage )
+{
+ return ShapeFactory::getChartRootShape( xDrawPage );
+}
+
+void DrawModelWrapper::lockControllers()
+{
+ uno::Reference< frame::XModel > xDrawModel( getUnoModel() );
+ if( xDrawModel.is())
+ xDrawModel->lockControllers();
+}
+void DrawModelWrapper::unlockControllers()
+{
+ uno::Reference< frame::XModel > xDrawModel( getUnoModel() );
+ if( xDrawModel.is())
+ xDrawModel->unlockControllers();
+}
+
+OutputDevice* DrawModelWrapper::getReferenceDevice() const
+{
+ return SdrModel::GetRefDevice();
+}
+
+SfxItemPool& DrawModelWrapper::GetItemPool()
+{
+ return SdrModel::GetItemPool();
+}
+XColorListRef DrawModelWrapper::GetColorList() const
+{
+ return SdrModel::GetColorList();
+}
+XDashListRef DrawModelWrapper::GetDashList() const
+{
+ return SdrModel::GetDashList();
+}
+XLineEndListRef DrawModelWrapper::GetLineEndList() const
+{
+ return SdrModel::GetLineEndList();
+}
+XGradientListRef DrawModelWrapper::GetGradientList() const
+{
+ return SdrModel::GetGradientList();
+}
+XHatchListRef DrawModelWrapper::GetHatchList() const
+{
+ return SdrModel::GetHatchList();
+}
+XBitmapListRef DrawModelWrapper::GetBitmapList() const
+{
+ return SdrModel::GetBitmapList();
+}
+
+XPatternListRef DrawModelWrapper::GetPatternList() const
+{
+ return SdrModel::GetPatternList();
+}
+
+SdrObject* DrawModelWrapper::getNamedSdrObject( const OUString& rName )
+{
+ if( rName.isEmpty() )
+ return nullptr;
+ return getNamedSdrObject( rName, GetPage(0) );
+}
+
+SdrObject* DrawModelWrapper::getNamedSdrObject( const OUString& rObjectCID, SdrObjList const * pSearchList )
+{
+ if(!pSearchList || rObjectCID.isEmpty())
+ return nullptr;
+ const size_t nCount = pSearchList->GetObjCount();
+ for( size_t nN=0; nN<nCount; ++nN )
+ {
+ SdrObject* pObj = pSearchList->GetObj(nN);
+ if(!pObj)
+ continue;
+ if( ObjectIdentifier::areIdenticalObjects( rObjectCID, pObj->GetName() ) )
+ return pObj;
+ pObj = DrawModelWrapper::getNamedSdrObject( rObjectCID, pObj->GetSubList() );
+ if(pObj)
+ return pObj;
+ }
+ return nullptr;
+}
+
+bool DrawModelWrapper::removeShape( const rtl::Reference<SvxShape>& xShape )
+{
+ uno::Reference<drawing::XShapes> xShapes( xShape->getParent(), uno::UNO_QUERY );
+ if( xShapes.is() )
+ {
+ xShapes->remove(xShape);
+ return true;
+ }
+ return false;
+}
+
+void DrawModelWrapper::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("DrawModelWrapper"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ SdrModel::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/ExplicitValueProvider.cxx b/chart2/source/view/main/ExplicitValueProvider.cxx
new file mode 100644
index 000000000..cf5a99974
--- /dev/null
+++ b/chart2/source/view/main/ExplicitValueProvider.cxx
@@ -0,0 +1,211 @@
+/* -*- 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 <chartview/ExplicitValueProvider.hxx>
+#include <AxisHelper.hxx>
+#include <ChartModel.hxx>
+#include <Diagram.hxx>
+#include <DiagramHelper.hxx>
+#include <unonames.hxx>
+#include <BaseCoordinateSystem.hxx>
+#include <TitleHelper.hxx>
+#include <ObjectIdentifier.hxx>
+
+#include <comphelper/servicehelper.hxx>
+#include <tools/diagnose_ex.h>
+
+namespace chart
+{
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::Any;
+
+namespace
+{
+constexpr sal_Int32 constDiagramTitleSpace = 200; //=0,2 cm spacing
+
+bool lcl_getPropertySwapXAndYAxis(const rtl::Reference<Diagram>& xDiagram)
+{
+ bool bSwapXAndY = false;
+
+ if (xDiagram.is())
+ {
+ const std::vector<rtl::Reference<BaseCoordinateSystem>>& aCooSysList(
+ xDiagram->getBaseCoordinateSystems());
+ if (!aCooSysList.empty())
+ {
+ try
+ {
+ aCooSysList[0]->getPropertyValue("SwapXAndYAxis") >>= bSwapXAndY;
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "");
+ }
+ }
+ }
+ return bSwapXAndY;
+}
+
+} // end anonymous namespace
+
+sal_Int32 ExplicitValueProvider::getExplicitNumberFormatKeyForAxis(
+ const Reference<chart2::XAxis>& xAxis,
+ const rtl::Reference<::chart::BaseCoordinateSystem>& xCorrespondingCoordinateSystem,
+ const rtl::Reference<::chart::ChartModel>& xChartDoc)
+{
+ return AxisHelper::getExplicitNumberFormatKeyForAxis(
+ xAxis, xCorrespondingCoordinateSystem, xChartDoc,
+ true /*bSearchForParallelAxisIfNothingIsFound*/);
+}
+
+const uno::Sequence<sal_Int8>& ExplicitValueProvider::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theExplicitValueProviderUnoTunnelId;
+ return theExplicitValueProviderUnoTunnelId.getSeq();
+}
+
+sal_Int32 ExplicitValueProvider::getExplicitNumberFormatKeyForDataLabel(
+ const uno::Reference<beans::XPropertySet>& xSeriesOrPointProp)
+{
+ sal_Int32 nFormat = 0;
+ if (!xSeriesOrPointProp.is())
+ return nFormat;
+
+ try
+ {
+ xSeriesOrPointProp->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nFormat;
+ }
+ catch (const beans::UnknownPropertyException&)
+ {
+ }
+
+ if (nFormat < 0)
+ nFormat = 0;
+ return nFormat;
+}
+
+sal_Int32 ExplicitValueProvider::getExplicitPercentageNumberFormatKeyForDataLabel(
+ const uno::Reference<beans::XPropertySet>& xSeriesOrPointProp,
+ const uno::Reference<util::XNumberFormatsSupplier>& xNumberFormatsSupplier)
+{
+ sal_Int32 nFormat = 0;
+ if (!xSeriesOrPointProp.is())
+ return nFormat;
+ if (!(xSeriesOrPointProp->getPropertyValue("PercentageNumberFormat") >>= nFormat))
+ {
+ nFormat = DiagramHelper::getPercentNumberFormat(xNumberFormatsSupplier);
+ }
+ if (nFormat < 0)
+ nFormat = 0;
+ return nFormat;
+}
+
+awt::Rectangle ExplicitValueProvider::AddSubtractAxisTitleSizes(
+ ChartModel& rModel, ExplicitValueProvider* pChartView, const awt::Rectangle& rPositionAndSize,
+ bool bSubtract)
+{
+ awt::Rectangle aRet(rPositionAndSize);
+
+ //add axis title sizes to the diagram size
+ uno::Reference<chart2::XTitle> xTitle_Height(
+ TitleHelper::getTitle(TitleHelper::TITLE_AT_STANDARD_X_AXIS_POSITION, rModel));
+ uno::Reference<chart2::XTitle> xTitle_Width(
+ TitleHelper::getTitle(TitleHelper::TITLE_AT_STANDARD_Y_AXIS_POSITION, rModel));
+ uno::Reference<chart2::XTitle> xSecondTitle_Height(
+ TitleHelper::getTitle(TitleHelper::SECONDARY_X_AXIS_TITLE, rModel));
+ uno::Reference<chart2::XTitle> xSecondTitle_Width(
+ TitleHelper::getTitle(TitleHelper::SECONDARY_Y_AXIS_TITLE, rModel));
+ if (xTitle_Height.is() || xTitle_Width.is() || xSecondTitle_Height.is()
+ || xSecondTitle_Width.is())
+ {
+ ExplicitValueProvider* pExplicitValueProvider = pChartView;
+ if (pExplicitValueProvider)
+ {
+ //detect whether x axis points into x direction or not
+ if (lcl_getPropertySwapXAndYAxis(rModel.getFirstChartDiagram()))
+ {
+ std::swap(xTitle_Height, xTitle_Width);
+ std::swap(xSecondTitle_Height, xSecondTitle_Width);
+ }
+
+ sal_Int32 nTitleSpaceWidth = 0;
+ sal_Int32 nTitleSpaceHeight = 0;
+ sal_Int32 nSecondTitleSpaceWidth = 0;
+ sal_Int32 nSecondTitleSpaceHeight = 0;
+
+ if (xTitle_Height.is())
+ {
+ OUString aCID_X(
+ ObjectIdentifier::createClassifiedIdentifierForObject(xTitle_Height, &rModel));
+ nTitleSpaceHeight
+ = pExplicitValueProvider->getRectangleOfObject(aCID_X, true).Height;
+ if (nTitleSpaceHeight)
+ nTitleSpaceHeight += constDiagramTitleSpace;
+ }
+ if (xTitle_Width.is())
+ {
+ OUString aCID_Y(
+ ObjectIdentifier::createClassifiedIdentifierForObject(xTitle_Width, &rModel));
+ nTitleSpaceWidth = pExplicitValueProvider->getRectangleOfObject(aCID_Y, true).Width;
+ if (nTitleSpaceWidth)
+ nTitleSpaceWidth += constDiagramTitleSpace;
+ }
+ if (xSecondTitle_Height.is())
+ {
+ OUString aCID_X(ObjectIdentifier::createClassifiedIdentifierForObject(
+ xSecondTitle_Height, &rModel));
+ nSecondTitleSpaceHeight
+ = pExplicitValueProvider->getRectangleOfObject(aCID_X, true).Height;
+ if (nSecondTitleSpaceHeight)
+ nSecondTitleSpaceHeight += constDiagramTitleSpace;
+ }
+ if (xSecondTitle_Width.is())
+ {
+ OUString aCID_Y(ObjectIdentifier::createClassifiedIdentifierForObject(
+ xSecondTitle_Width, &rModel));
+ nSecondTitleSpaceWidth
+ += pExplicitValueProvider->getRectangleOfObject(aCID_Y, true).Width;
+ if (nSecondTitleSpaceWidth)
+ nSecondTitleSpaceWidth += constDiagramTitleSpace;
+ }
+ if (bSubtract)
+ {
+ aRet.X += nTitleSpaceWidth;
+ aRet.Y += nSecondTitleSpaceHeight;
+ aRet.Width -= (nTitleSpaceWidth + nSecondTitleSpaceWidth);
+ aRet.Height -= (nTitleSpaceHeight + nSecondTitleSpaceHeight);
+ }
+ else
+ {
+ aRet.X -= nTitleSpaceWidth;
+ aRet.Y -= nSecondTitleSpaceHeight;
+ aRet.Width += nTitleSpaceWidth + nSecondTitleSpaceWidth;
+ aRet.Height += nTitleSpaceHeight + nSecondTitleSpaceHeight;
+ }
+ }
+ }
+ return aRet;
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/LabelPositionHelper.cxx b/chart2/source/view/main/LabelPositionHelper.cxx
new file mode 100644
index 000000000..e39bc0495
--- /dev/null
+++ b/chart2/source/view/main/LabelPositionHelper.cxx
@@ -0,0 +1,467 @@
+/* -*- 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 <LabelPositionHelper.hxx>
+#include <PlottingPositionHelper.hxx>
+#include <PropertyMapper.hxx>
+#include <RelativeSizeHelper.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/TextVerticalAdjust.hpp>
+#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
+
+#include <cmath>
+
+namespace chart
+{
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+
+LabelPositionHelper::LabelPositionHelper(
+ sal_Int32 nDimensionCount
+ , const rtl::Reference<SvxShapeGroupAnyD>& xLogicTarget)
+ : m_nDimensionCount(nDimensionCount)
+ , m_xLogicTarget(xLogicTarget)
+{
+}
+
+LabelPositionHelper::~LabelPositionHelper()
+{
+}
+
+awt::Point LabelPositionHelper::transformSceneToScreenPosition( const drawing::Position3D& rScenePosition3D ) const
+{
+ return PlottingPositionHelper::transformSceneToScreenPosition(
+ rScenePosition3D, m_xLogicTarget, m_nDimensionCount );
+}
+
+void LabelPositionHelper::changeTextAdjustment( tAnySequence& rPropValues, const tNameSequence& rPropNames, LabelAlignment eAlignment)
+{
+ //HorizontalAdjustment
+ {
+ drawing::TextHorizontalAdjust eHorizontalAdjust = drawing::TextHorizontalAdjust_CENTER;
+ if( eAlignment==LABEL_ALIGN_RIGHT || eAlignment==LABEL_ALIGN_RIGHT_TOP || eAlignment==LABEL_ALIGN_RIGHT_BOTTOM )
+ eHorizontalAdjust = drawing::TextHorizontalAdjust_LEFT;
+ else if( eAlignment==LABEL_ALIGN_LEFT || eAlignment==LABEL_ALIGN_LEFT_TOP || eAlignment==LABEL_ALIGN_LEFT_BOTTOM )
+ eHorizontalAdjust = drawing::TextHorizontalAdjust_RIGHT;
+ uno::Any* pHorizontalAdjustAny = PropertyMapper::getValuePointer(rPropValues,rPropNames,u"TextHorizontalAdjust");
+ if(pHorizontalAdjustAny)
+ *pHorizontalAdjustAny <<= eHorizontalAdjust;
+ }
+
+ //VerticalAdjustment
+ {
+ drawing::TextVerticalAdjust eVerticalAdjust = drawing::TextVerticalAdjust_CENTER;
+ if( eAlignment==LABEL_ALIGN_TOP || eAlignment==LABEL_ALIGN_RIGHT_TOP || eAlignment==LABEL_ALIGN_LEFT_TOP )
+ eVerticalAdjust = drawing::TextVerticalAdjust_BOTTOM;
+ else if( eAlignment==LABEL_ALIGN_BOTTOM || eAlignment==LABEL_ALIGN_RIGHT_BOTTOM || eAlignment==LABEL_ALIGN_LEFT_BOTTOM )
+ eVerticalAdjust = drawing::TextVerticalAdjust_TOP;
+ uno::Any* pVerticalAdjustAny = PropertyMapper::getValuePointer(rPropValues,rPropNames,u"TextVerticalAdjust");
+ if(pVerticalAdjustAny)
+ *pVerticalAdjustAny <<= eVerticalAdjust;
+ }
+}
+
+static void lcl_doDynamicFontResize( uno::Any* pAOldAndNewFontHeightAny
+ , const awt::Size& rOldReferenceSize
+ , const awt::Size& rNewReferenceSize )
+{
+ double fOldFontHeight = 0;
+ if( pAOldAndNewFontHeightAny && ( *pAOldAndNewFontHeightAny >>= fOldFontHeight ) )
+ {
+ double fNewFontHeight = RelativeSizeHelper::calculate( fOldFontHeight, rOldReferenceSize, rNewReferenceSize );
+ *pAOldAndNewFontHeightAny <<= fNewFontHeight;
+ }
+}
+
+void LabelPositionHelper::doDynamicFontResize( tAnySequence& rPropValues
+ , const tNameSequence& rPropNames
+ , const uno::Reference< beans::XPropertySet >& xAxisModelProps
+ , const awt::Size& rNewReferenceSize
+ )
+{
+ //handle dynamic font resize:
+ awt::Size aOldReferenceSize;
+ if( xAxisModelProps->getPropertyValue( "ReferencePageSize") >>= aOldReferenceSize )
+ {
+ uno::Any* pAOldAndNewFontHeightAny = PropertyMapper::getValuePointer( rPropValues, rPropNames, u"CharHeight" );
+ lcl_doDynamicFontResize( pAOldAndNewFontHeightAny, aOldReferenceSize, rNewReferenceSize );
+ pAOldAndNewFontHeightAny = PropertyMapper::getValuePointer( rPropValues, rPropNames, u"CharHeightAsian" );
+ lcl_doDynamicFontResize( pAOldAndNewFontHeightAny, aOldReferenceSize, rNewReferenceSize );
+ pAOldAndNewFontHeightAny = PropertyMapper::getValuePointer( rPropValues, rPropNames, u"CharHeightComplex" );
+ lcl_doDynamicFontResize( pAOldAndNewFontHeightAny, aOldReferenceSize, rNewReferenceSize );
+ }
+}
+
+namespace
+{
+
+void lcl_correctRotation_Left( double& rfXCorrection, double& rfYCorrection
+ , double fAnglePositiveDegree, const awt::Size& aSize, bool bRotateAroundCenter )
+{
+ //correct label positions for labels on a left side of something with a right centered alignment
+ double fAnglePi = basegfx::deg2rad(fAnglePositiveDegree);
+ if( fAnglePositiveDegree==0.0 )
+ {
+ }
+ else if( fAnglePositiveDegree<= 90.0 )
+ {
+ rfXCorrection = -aSize.Height*std::sin( fAnglePi )/2.0;
+ if( bRotateAroundCenter )
+ rfYCorrection = -aSize.Width*std::sin( fAnglePi )/2.0;
+ }
+ else if( fAnglePositiveDegree<= 180.0 )
+ {
+ double beta = fAnglePi-M_PI_2;
+ rfXCorrection = -aSize.Width *std::sin( beta )
+ -aSize.Height *std::cos( beta )/2.0;
+ if( bRotateAroundCenter )
+ rfYCorrection = -aSize.Width *std::cos( beta )/2.0;
+ else
+ rfYCorrection = -aSize.Width *std::cos( beta );
+ }
+ else if( fAnglePositiveDegree<= 270.0 )
+ {
+ double beta = fAnglePi - M_PI;
+ rfXCorrection = -aSize.Width *std::cos( beta )
+ -aSize.Height*std::sin( beta )/2.0;
+ if( bRotateAroundCenter )
+ rfYCorrection = aSize.Width *std::sin( beta )/2.0;
+ else
+ rfYCorrection = aSize.Width *std::sin( beta );
+ }
+ else
+ {
+ double beta = 2*M_PI - fAnglePi;
+ rfXCorrection = -aSize.Height*std::sin( beta )/2.0;
+ if( bRotateAroundCenter )
+ rfYCorrection = aSize.Width*std::sin( beta )/2.0;
+ }
+}
+
+void lcl_correctRotation_Right( double& rfXCorrection, double& rfYCorrection
+ , double fAnglePositiveDegree, const awt::Size& aSize, bool bRotateAroundCenter )
+{
+ //correct label positions for labels on a right side of something with a left centered alignment
+ double fAnglePi = basegfx::deg2rad(fAnglePositiveDegree);
+ if( fAnglePositiveDegree== 0.0 )
+ {
+ }
+ else if( fAnglePositiveDegree<= 90.0 )
+ {
+ rfXCorrection = aSize.Height*std::sin( fAnglePi )/2.0;
+ if( bRotateAroundCenter )
+ rfYCorrection = aSize.Width*std::sin( fAnglePi )/2.0;
+ }
+ else if( fAnglePositiveDegree<= 180.0 )
+ {
+ double beta = M_PI - fAnglePi;
+ rfXCorrection = aSize.Width *std::cos( beta )
+ + aSize.Height*std::sin( beta )/2.0;
+ if( bRotateAroundCenter )
+ rfYCorrection = aSize.Width *std::sin( beta )/2.0;
+ else
+ rfYCorrection = aSize.Width *std::sin( beta );
+ }
+ else if( fAnglePositiveDegree<= 270.0 )
+ {
+ double beta = 3*M_PI_2 - fAnglePi;
+ rfXCorrection = aSize.Width *std::sin( beta )
+ +aSize.Height*std::cos( beta )/2.0;
+ if( bRotateAroundCenter )
+ rfYCorrection = -aSize.Width *std::cos( beta )/2.0;
+ else
+ rfYCorrection = -aSize.Width *std::cos( beta );
+ }
+ else
+ {
+ rfXCorrection = aSize.Height*std::sin( 2*M_PI - fAnglePi )/2.0;
+ if( bRotateAroundCenter )
+ rfYCorrection = -aSize.Width*std::sin( 2*M_PI - fAnglePi )/2.0;
+ }
+}
+
+void lcl_correctRotation_Top( double& rfXCorrection, double& rfYCorrection
+ , double fAnglePositiveDegree, const awt::Size& aSize, bool bRotateAroundCenter )
+{
+ //correct label positions for labels on top of something with a bottom centered alignment
+ double fAnglePi = basegfx::deg2rad(fAnglePositiveDegree);
+ if( fAnglePositiveDegree== 0.0 )
+ {
+ }
+ else if( fAnglePositiveDegree<= 90.0 )
+ {
+ rfXCorrection = aSize.Height*std::sin( fAnglePi )/2.0;
+ if( !bRotateAroundCenter )
+ rfXCorrection += aSize.Width*std::cos( fAnglePi )/2.0;
+ rfYCorrection = -aSize.Width*std::sin( fAnglePi )/2.0;
+ }
+ else if( fAnglePositiveDegree<= 180.0 )
+ {
+ double beta = fAnglePi - M_PI_2;
+ rfXCorrection = aSize.Height*std::cos( beta )/2.0;
+ if( !bRotateAroundCenter )
+ rfXCorrection -= aSize.Width*std::sin( beta )/2.0;
+ rfYCorrection = -aSize.Width*std::cos( beta )/2.0
+ - aSize.Height*std::sin( beta );
+ }
+ else if( fAnglePositiveDegree<= 270.0 )
+ {
+ double beta = fAnglePi - M_PI;
+ rfXCorrection = -aSize.Height *std::sin( beta )/2.0;
+ if( !bRotateAroundCenter )
+ rfXCorrection += aSize.Width *std::cos( beta )/2.0;
+ rfYCorrection = -aSize.Width *std::sin( beta )/2.0
+ -aSize.Height *std::cos( beta );
+ }
+ else
+ {
+ rfXCorrection = aSize.Height*std::sin( fAnglePi )/2.0;
+ if( !bRotateAroundCenter )
+ rfXCorrection -= aSize.Width*std::cos( fAnglePi )/2.0;
+ rfYCorrection = aSize.Width*std::sin( fAnglePi )/2.0;
+ }
+}
+
+void lcl_correctRotation_Bottom( double& rfXCorrection, double& rfYCorrection
+ , double fAnglePositiveDegree, const awt::Size& aSize, bool bRotateAroundCenter )
+{
+ //correct label positions for labels below something with a top centered alignment
+ double fAnglePi = basegfx::deg2rad(fAnglePositiveDegree);
+ if( fAnglePositiveDegree==0.0 )
+ {
+ }
+ else if( fAnglePositiveDegree<= 90.0 )
+ {
+ rfXCorrection = -aSize.Height*std::sin( fAnglePi )/2.0;
+ if( !bRotateAroundCenter )
+ rfXCorrection -= aSize.Width *std::cos( fAnglePi )/2.0;
+ rfYCorrection = aSize.Width*std::sin( fAnglePi )/2.0;
+ }
+ else if( fAnglePositiveDegree<= 180.0 )
+ {
+ double beta = fAnglePi-M_PI_2;
+ rfXCorrection = -aSize.Height*std::cos( beta )/2.0;
+ if( !bRotateAroundCenter )
+ rfXCorrection += aSize.Width *std::sin( beta )/2.0;
+ rfYCorrection = aSize.Width *std::cos( beta )/2.0
+ +aSize.Height*std::sin( beta );
+ }
+ else if( fAnglePositiveDegree<= 270.0 )
+ {
+ double beta = 3*M_PI_2 - fAnglePi;
+ rfXCorrection = aSize.Height*std::cos( beta )/2.0;
+ if( !bRotateAroundCenter )
+ rfXCorrection -= aSize.Width *std::sin( beta )/2.0;
+ rfYCorrection = aSize.Height*std::sin( beta )
+ +aSize.Width*std::cos( beta )/2.0;
+ }
+ else
+ {
+ double beta = 2*M_PI - fAnglePi;
+ rfXCorrection = aSize.Height*std::sin( beta )/2.0;
+ if( !bRotateAroundCenter )
+ rfXCorrection += aSize.Width*std::cos( beta )/2.0;
+ rfYCorrection = aSize.Width*std::sin( beta )/2.0;
+ }
+}
+
+void lcl_correctRotation_Left_Top( double& rfXCorrection, double& rfYCorrection
+ , double fAnglePositiveDegree, const awt::Size& aSize )
+{
+ //correct position for labels at the left top corner of something with a bottom right alignment
+ double fAnglePi = basegfx::deg2rad(fAnglePositiveDegree);
+ if( fAnglePositiveDegree==0.0 )
+ {
+ }
+ else if( fAnglePositiveDegree<= 90.0 )
+ {
+ rfYCorrection = -aSize.Width*std::sin( fAnglePi );
+ }
+ else if( fAnglePositiveDegree<= 180.0 )
+ {
+ double beta = fAnglePi-M_PI_2;
+ rfXCorrection = -aSize.Width*std::sin( beta );
+ rfYCorrection = -aSize.Height*std::sin( beta )
+ -aSize.Width*std::cos( beta );
+ }
+ else if( fAnglePositiveDegree<= 270.0 )
+ {
+ double beta = 3*M_PI_2 - fAnglePi;
+ rfXCorrection = -aSize.Height*std::cos( beta )
+ -aSize.Width*std::sin( beta );
+ rfYCorrection = -aSize.Height*std::sin( beta );
+ }
+ else
+ {
+ rfXCorrection = aSize.Height*std::sin( fAnglePi );
+ }
+}
+
+void lcl_correctRotation_Left_Bottom( double& rfXCorrection, double& rfYCorrection
+ , double fAnglePositiveDegree, const awt::Size& aSize )
+{
+ //correct position for labels at the left bottom corner of something with a top right alignment
+ double fAnglePi = basegfx::deg2rad(fAnglePositiveDegree);
+ if( fAnglePositiveDegree==0.0 )
+ {
+ }
+ else if( fAnglePositiveDegree<= 90.0 )
+ {
+ rfXCorrection = -aSize.Height*std::sin( fAnglePi );
+ }
+ else if( fAnglePositiveDegree<= 180.0 )
+ {
+ double beta = fAnglePi-M_PI_2;
+ rfXCorrection = -aSize.Width*std::sin( beta )
+ -aSize.Height*std::cos( beta );
+ rfYCorrection = aSize.Height*std::sin( beta );
+ }
+ else if( fAnglePositiveDegree<= 270.0 )
+ {
+ double beta = 3*M_PI_2 - fAnglePi;
+ rfXCorrection = -aSize.Width*std::sin( beta );
+ rfYCorrection = aSize.Width*std::cos( beta )
+ +aSize.Height*std::sin( beta );
+ }
+ else
+ {
+ rfYCorrection = -aSize.Width*std::sin( fAnglePi );
+ }
+}
+
+void lcl_correctRotation_Right_Top( double& rfXCorrection, double& rfYCorrection
+ , double fAnglePositiveDegree, const awt::Size& aSize )
+{
+ //correct position for labels at the right top corner of something with a bottom left alignment
+ double fAnglePi = basegfx::deg2rad(fAnglePositiveDegree);
+ if( fAnglePositiveDegree==0.0 )
+ {
+ }
+ else if( fAnglePositiveDegree<= 90.0 )
+ {
+ rfXCorrection = aSize.Height*std::sin( fAnglePi );
+ }
+ else if( fAnglePositiveDegree<= 180.0 )
+ {
+ double beta = fAnglePi-M_PI_2;
+ rfXCorrection = aSize.Width*std::sin( beta )
+ +aSize.Height*std::cos( beta );
+ rfYCorrection = -aSize.Height*std::sin( beta );
+ }
+ else if( fAnglePositiveDegree<= 270.0 )
+ {
+ double beta = 3*M_PI_2 - fAnglePi;
+ rfXCorrection = aSize.Width*std::sin( beta );
+ rfYCorrection = -aSize.Width*std::cos( beta )
+ -aSize.Height*std::sin( beta );
+ }
+ else
+ {
+ rfYCorrection = aSize.Width*std::sin( fAnglePi );
+ }
+}
+
+void lcl_correctRotation_Right_Bottom( double& rfXCorrection, double& rfYCorrection
+ , double fAnglePositiveDegree, const awt::Size& aSize )
+{
+ //correct position for labels at the right bottom corner of something with a top left alignment
+ double fAnglePi = basegfx::deg2rad(fAnglePositiveDegree);
+ if( fAnglePositiveDegree==0.0 )
+ {
+ }
+ else if( fAnglePositiveDegree<= 90.0 )
+ {
+ rfYCorrection = aSize.Width*std::sin( fAnglePi );
+ }
+ else if( fAnglePositiveDegree<= 180.0 )
+ {
+ double beta = fAnglePi-M_PI_2;
+ rfXCorrection = aSize.Width*std::sin( beta );
+ rfYCorrection = aSize.Height*std::sin( beta )
+ +aSize.Width*std::cos( beta );
+ }
+ else if( fAnglePositiveDegree<= 270.0 )
+ {
+ double beta = 3*M_PI_2 - fAnglePi;
+ rfXCorrection = aSize.Height*std::cos( beta )
+ +aSize.Width*std::sin( beta );
+ rfYCorrection = aSize.Height*std::sin( beta );
+ }
+ else
+ {
+ rfXCorrection = -aSize.Height*std::sin( fAnglePi );
+ }
+}
+
+}//end anonymous namespace
+
+void LabelPositionHelper::correctPositionForRotation( const rtl::Reference<SvxShapeText>& xShape2DText
+ , LabelAlignment eLabelAlignment, const double fRotationAngle, bool bRotateAroundCenter )
+{
+ if( !xShape2DText.is() )
+ return;
+
+ awt::Point aOldPos = xShape2DText->getPosition();
+ awt::Size aSize = xShape2DText->getSize();
+
+ double fYCorrection = 0.0;
+ double fXCorrection = 0.0;
+
+ double fAnglePositiveDegree = fRotationAngle;
+ while(fAnglePositiveDegree<0.0)
+ fAnglePositiveDegree+=360.0;
+
+ switch(eLabelAlignment)
+ {
+ case LABEL_ALIGN_LEFT:
+ lcl_correctRotation_Left( fXCorrection, fYCorrection, fAnglePositiveDegree, aSize, bRotateAroundCenter );
+ break;
+ case LABEL_ALIGN_RIGHT:
+ lcl_correctRotation_Right( fXCorrection, fYCorrection, fAnglePositiveDegree, aSize, bRotateAroundCenter );
+ break;
+ case LABEL_ALIGN_TOP:
+ lcl_correctRotation_Top( fXCorrection, fYCorrection, fAnglePositiveDegree, aSize, bRotateAroundCenter );
+ break;
+ case LABEL_ALIGN_BOTTOM:
+ lcl_correctRotation_Bottom( fXCorrection, fYCorrection, fAnglePositiveDegree, aSize, bRotateAroundCenter );
+ break;
+ case LABEL_ALIGN_LEFT_TOP:
+ lcl_correctRotation_Left_Top( fXCorrection, fYCorrection, fAnglePositiveDegree, aSize );
+ break;
+ case LABEL_ALIGN_LEFT_BOTTOM:
+ lcl_correctRotation_Left_Bottom( fXCorrection, fYCorrection, fAnglePositiveDegree, aSize );
+ break;
+ case LABEL_ALIGN_RIGHT_TOP:
+ lcl_correctRotation_Right_Top( fXCorrection, fYCorrection, fAnglePositiveDegree, aSize );
+ break;
+ case LABEL_ALIGN_RIGHT_BOTTOM:
+ lcl_correctRotation_Right_Bottom( fXCorrection, fYCorrection, fAnglePositiveDegree, aSize );
+ break;
+ default: //LABEL_ALIGN_CENTER
+ break;
+ }
+
+ xShape2DText->setPosition( awt::Point(
+ static_cast<sal_Int32>(aOldPos.X + fXCorrection )
+ , static_cast<sal_Int32>(aOldPos.Y + fYCorrection ) ) );
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/Linear3DTransformation.cxx b/chart2/source/view/main/Linear3DTransformation.cxx
new file mode 100644
index 000000000..0d723ef34
--- /dev/null
+++ b/chart2/source/view/main/Linear3DTransformation.cxx
@@ -0,0 +1,124 @@
+/* -*- 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 <Linear3DTransformation.hxx>
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::uno::Sequence;
+
+namespace chart
+{
+
+ Linear3DTransformation::Linear3DTransformation( const drawing::HomogenMatrix& rHomMatrix, bool bSwapXAndY )
+ : m_Matrix(rHomMatrix)
+ , m_bSwapXAndY(bSwapXAndY)
+{}
+
+Linear3DTransformation::~Linear3DTransformation()
+{}
+
+// ____ XTransformation2 ____
+css::drawing::Position3D Linear3DTransformation::transform(
+ const Sequence< double >& rSourceValues ) const
+{
+ double fX = rSourceValues[0];
+ double fY = rSourceValues[1];
+ double fZ = rSourceValues[2];
+ if(m_bSwapXAndY)
+ std::swap(fX,fY);
+ css::drawing::Position3D aNewVec;
+ double fZwi;
+
+ fZwi = m_Matrix.Line1.Column1 * fX
+ + m_Matrix.Line1.Column2 * fY
+ + m_Matrix.Line1.Column3 * fZ
+ + m_Matrix.Line1.Column4;
+ aNewVec.PositionX = fZwi;
+
+ fZwi = m_Matrix.Line2.Column1 * fX
+ + m_Matrix.Line2.Column2 * fY
+ + m_Matrix.Line2.Column3 * fZ
+ + m_Matrix.Line2.Column4;
+ aNewVec.PositionY = fZwi;
+
+ fZwi = m_Matrix.Line3.Column1 * fX
+ + m_Matrix.Line3.Column2 * fY
+ + m_Matrix.Line3.Column3 * fZ
+ + m_Matrix.Line3.Column4;
+ aNewVec.PositionZ = fZwi;
+
+ fZwi = m_Matrix.Line4.Column1 * fX
+ + m_Matrix.Line4.Column2 * fY
+ + m_Matrix.Line4.Column3 * fZ
+ + m_Matrix.Line4.Column4;
+ if(fZwi != 1.0 && fZwi != 0.0)
+ {
+ aNewVec.PositionX /= fZwi;
+ aNewVec.PositionY /= fZwi;
+ aNewVec.PositionZ /= fZwi;
+ }
+ return aNewVec;
+}
+
+css::drawing::Position3D Linear3DTransformation::transform(
+ const css::drawing::Position3D& rSourceValues ) const
+{
+ double fX = rSourceValues.PositionX;
+ double fY = rSourceValues.PositionY;
+ double fZ = rSourceValues.PositionZ;
+ if(m_bSwapXAndY)
+ std::swap(fX,fY);
+ css::drawing::Position3D aNewVec;
+ double fZwi;
+
+ fZwi = m_Matrix.Line1.Column1 * fX
+ + m_Matrix.Line1.Column2 * fY
+ + m_Matrix.Line1.Column3 * fZ
+ + m_Matrix.Line1.Column4;
+ aNewVec.PositionX = fZwi;
+
+ fZwi = m_Matrix.Line2.Column1 * fX
+ + m_Matrix.Line2.Column2 * fY
+ + m_Matrix.Line2.Column3 * fZ
+ + m_Matrix.Line2.Column4;
+ aNewVec.PositionY = fZwi;
+
+ fZwi = m_Matrix.Line3.Column1 * fX
+ + m_Matrix.Line3.Column2 * fY
+ + m_Matrix.Line3.Column3 * fZ
+ + m_Matrix.Line3.Column4;
+ aNewVec.PositionZ = fZwi;
+
+ fZwi = m_Matrix.Line4.Column1 * fX
+ + m_Matrix.Line4.Column2 * fY
+ + m_Matrix.Line4.Column3 * fZ
+ + m_Matrix.Line4.Column4;
+ if(fZwi != 1.0 && fZwi != 0.0)
+ {
+ aNewVec.PositionX /= fZwi;
+ aNewVec.PositionY /= fZwi;
+ aNewVec.PositionZ /= fZwi;
+ }
+ return aNewVec;
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/PlotterBase.cxx b/chart2/source/view/main/PlotterBase.cxx
new file mode 100644
index 000000000..a706f1600
--- /dev/null
+++ b/chart2/source/view/main/PlotterBase.cxx
@@ -0,0 +1,106 @@
+/* -*- 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 <PlotterBase.hxx>
+#include <PlottingPositionHelper.hxx>
+#include <ShapeFactory.hxx>
+#include <osl/diagnose.h>
+
+namespace chart
+{
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+
+PlotterBase::PlotterBase( sal_Int32 nDimensionCount )
+ : m_nDimension(nDimensionCount)
+ , m_pPosHelper(nullptr)
+{
+}
+
+void PlotterBase::initPlotter( const rtl::Reference<SvxShapeGroupAnyD>& xLogicTarget
+ , const rtl::Reference<SvxShapeGroupAnyD>& xFinalTarget
+ , const OUString& rCID )
+{
+ OSL_PRECOND(xLogicTarget.is()&&xFinalTarget.is(),"no proper initialization parameters");
+ //is only allowed to be called once
+ m_xLogicTarget = xLogicTarget;
+ m_xFinalTarget = xFinalTarget;
+ m_aCID = rCID;
+}
+
+PlotterBase::~PlotterBase()
+{
+}
+
+void PlotterBase::setScales( std::vector< ExplicitScaleData >&& rScales, bool bSwapXAndYAxis )
+{
+ if (!m_pPosHelper)
+ return;
+
+ OSL_PRECOND(m_nDimension<=static_cast<sal_Int32>(rScales.size()),"Dimension of Plotter does not fit two dimension of given scale sequence");
+ m_pPosHelper->setScales( std::move(rScales), bSwapXAndYAxis );
+}
+
+void PlotterBase::setTransformationSceneToScreen( const drawing::HomogenMatrix& rMatrix)
+{
+ if (!m_pPosHelper)
+ return;
+
+ OSL_PRECOND(m_nDimension==2,"Set this transformation only in case of 2D");
+ if(m_nDimension!=2)
+ return;
+ m_pPosHelper->setTransformationSceneToScreen( rMatrix );
+}
+
+rtl::Reference<SvxShapeGroupAnyD> PlotterBase::createGroupShape(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const OUString& rName )
+{
+ if(m_nDimension==2)
+ {
+ //create and add to target
+ return ShapeFactory::createGroup2D( xTarget, rName );
+ }
+ else
+ {
+ //create and added to target
+ return ShapeFactory::createGroup3D( xTarget, rName );
+ }
+}
+
+bool PlotterBase::isValidPosition( const drawing::Position3D& rPos )
+{
+ if( std::isnan(rPos.PositionX) )
+ return false;
+ if( std::isnan(rPos.PositionY) )
+ return false;
+ if( std::isnan(rPos.PositionZ) )
+ return false;
+ if( std::isinf(rPos.PositionX) )
+ return false;
+ if( std::isinf(rPos.PositionY) )
+ return false;
+ if( std::isinf(rPos.PositionZ) )
+ return false;
+ return true;
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/PlottingPositionHelper.cxx b/chart2/source/view/main/PlottingPositionHelper.cxx
new file mode 100644
index 000000000..eab2f69e7
--- /dev/null
+++ b/chart2/source/view/main/PlottingPositionHelper.cxx
@@ -0,0 +1,708 @@
+/* -*- 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 <PlottingPositionHelper.hxx>
+#include <CommonConverters.hxx>
+#include <Linear3DTransformation.hxx>
+#include <VPolarTransformation.hxx>
+#include <ShapeFactory.hxx>
+#include <PropertyMapper.hxx>
+#include <defines.hxx>
+
+#include <com/sun/star/chart/TimeUnit.hpp>
+#include <com/sun/star/chart2/AxisType.hpp>
+#include <com/sun/star/drawing/Position3D.hpp>
+
+#include <rtl/math.hxx>
+
+namespace chart
+{
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+
+XTransformation2::~XTransformation2() {}
+
+PlottingPositionHelper::PlottingPositionHelper()
+ : m_bSwapXAndY( false )
+ , m_nXResolution( 1000 )
+ , m_nYResolution( 1000 )
+ , m_nZResolution( 1000 )
+ , m_bMaySkipPointsInRegressionCalculation( true )
+ , m_bDateAxis(false)
+ , m_nTimeResolution( css::chart::TimeUnit::DAY )
+ , m_aNullDate(30,12,1899)
+ , m_fScaledCategoryWidth(1.0)
+ , m_bAllowShiftXAxisPos(false)
+ , m_bAllowShiftZAxisPos(false)
+{
+}
+PlottingPositionHelper::PlottingPositionHelper( const PlottingPositionHelper& rSource )
+ : m_aScales( rSource.m_aScales )
+ , m_aMatrixScreenToScene( rSource.m_aMatrixScreenToScene )
+ // m_xTransformationLogicToScene( nullptr ) //should be recalculated
+ , m_bSwapXAndY( rSource.m_bSwapXAndY )
+ , m_nXResolution( rSource.m_nXResolution )
+ , m_nYResolution( rSource.m_nYResolution )
+ , m_nZResolution( rSource.m_nZResolution )
+ , m_bMaySkipPointsInRegressionCalculation( rSource.m_bMaySkipPointsInRegressionCalculation )
+ , m_bDateAxis( rSource.m_bDateAxis )
+ , m_nTimeResolution( rSource.m_nTimeResolution )
+ , m_aNullDate( rSource.m_aNullDate )
+ , m_fScaledCategoryWidth( rSource.m_fScaledCategoryWidth )
+ , m_bAllowShiftXAxisPos( rSource.m_bAllowShiftXAxisPos )
+ , m_bAllowShiftZAxisPos( rSource.m_bAllowShiftZAxisPos )
+{
+}
+
+PlottingPositionHelper::~PlottingPositionHelper()
+{
+
+}
+
+std::unique_ptr<PlottingPositionHelper> PlottingPositionHelper::clone() const
+{
+ return std::make_unique<PlottingPositionHelper>(*this);
+}
+
+std::unique_ptr<PlottingPositionHelper> PlottingPositionHelper::createSecondaryPosHelper( const ExplicitScaleData& rSecondaryScale )
+{
+ auto pRet = clone();
+ pRet->m_aScales[1]=rSecondaryScale;
+ return pRet;
+}
+
+void PlottingPositionHelper::setTransformationSceneToScreen( const drawing::HomogenMatrix& rMatrix)
+{
+ m_aMatrixScreenToScene = HomogenMatrixToB3DHomMatrix(rMatrix);
+ m_xTransformationLogicToScene = nullptr;
+}
+
+void PlottingPositionHelper::setScales( std::vector< ExplicitScaleData >&& rScales, bool bSwapXAndYAxis )
+{
+ m_aScales = std::move(rScales);
+ m_bSwapXAndY = bSwapXAndYAxis;
+ m_xTransformationLogicToScene = nullptr;
+}
+
+::chart::XTransformation2* PlottingPositionHelper::getTransformationScaledLogicToScene() const
+{
+ //this is a standard transformation for a cartesian coordinate system
+
+ //transformation from 2) to 4) //@todo 2) and 4) need an ink to a document
+
+ //we need to apply this transformation to each geometric object because of a bug/problem
+ //of the old drawing layer (the UNO_NAME_3D_EXTRUDE_DEPTH is an integer value instead of a double )
+ if(!m_xTransformationLogicToScene)
+ {
+ ::basegfx::B3DHomMatrix aMatrix;
+ double MinX = getLogicMinX();
+ double MinY = getLogicMinY();
+ double MinZ = getLogicMinZ();
+ double MaxX = getLogicMaxX();
+ double MaxY = getLogicMaxY();
+ double MaxZ = getLogicMaxZ();
+
+ AxisOrientation nXAxisOrientation = m_aScales[0].Orientation;
+ AxisOrientation nYAxisOrientation = m_aScales[1].Orientation;
+ AxisOrientation nZAxisOrientation = m_aScales[2].Orientation;
+
+ //apply scaling
+ doUnshiftedLogicScaling( &MinX, &MinY, &MinZ );
+ doUnshiftedLogicScaling( &MaxX, &MaxY, &MaxZ);
+
+ if(m_bSwapXAndY)
+ {
+ std::swap(MinX,MinY);
+ std::swap(MaxX,MaxY);
+ std::swap(nXAxisOrientation,nYAxisOrientation);
+ }
+
+ double fWidthX = MaxX - MinX;
+ double fWidthY = MaxY - MinY;
+ double fWidthZ = MaxZ - MinZ;
+
+ double fScaleDirectionX = nXAxisOrientation==AxisOrientation_MATHEMATICAL ? 1.0 : -1.0;
+ double fScaleDirectionY = nYAxisOrientation==AxisOrientation_MATHEMATICAL ? 1.0 : -1.0;
+ double fScaleDirectionZ = nZAxisOrientation==AxisOrientation_MATHEMATICAL ? -1.0 : 1.0;
+
+ double fScaleX = fScaleDirectionX*FIXED_SIZE_FOR_3D_CHART_VOLUME/fWidthX;
+ double fScaleY = fScaleDirectionY*FIXED_SIZE_FOR_3D_CHART_VOLUME/fWidthY;
+ double fScaleZ = fScaleDirectionZ*FIXED_SIZE_FOR_3D_CHART_VOLUME/fWidthZ;
+
+ aMatrix.scale(fScaleX, fScaleY, fScaleZ);
+
+ if( nXAxisOrientation==AxisOrientation_MATHEMATICAL )
+ aMatrix.translate(-MinX*fScaleX, 0.0, 0.0);
+ else
+ aMatrix.translate(-MaxX*fScaleX, 0.0, 0.0);
+ if( nYAxisOrientation==AxisOrientation_MATHEMATICAL )
+ aMatrix.translate(0.0, -MinY*fScaleY, 0.0);
+ else
+ aMatrix.translate(0.0, -MaxY*fScaleY, 0.0);
+ if( nZAxisOrientation==AxisOrientation_MATHEMATICAL )
+ aMatrix.translate(0.0, 0.0, -MaxZ*fScaleZ);//z direction in draw is reverse mathematical direction
+ else
+ aMatrix.translate(0.0, 0.0, -MinZ*fScaleZ);
+
+ aMatrix = m_aMatrixScreenToScene*aMatrix;
+
+ m_xTransformationLogicToScene.reset(new Linear3DTransformation(B3DHomMatrixToHomogenMatrix( aMatrix ), m_bSwapXAndY));
+ }
+ return m_xTransformationLogicToScene.get();
+}
+
+drawing::Position3D PlottingPositionHelper::transformLogicToScene(
+ double fX, double fY, double fZ, bool bClip ) const
+{
+ doLogicScaling( &fX,&fY,&fZ );
+ if(bClip)
+ clipScaledLogicValues( &fX,&fY,&fZ );
+
+ return transformScaledLogicToScene( fX, fY, fZ, false );
+}
+
+drawing::Position3D PlottingPositionHelper::transformScaledLogicToScene(
+ double fX, double fY, double fZ, bool bClip ) const
+{
+ if( bClip )
+ clipScaledLogicValues( &fX,&fY,&fZ );
+
+ drawing::Position3D aPos( fX, fY, fZ);
+
+ ::chart::XTransformation2* pTransformation =
+ getTransformationScaledLogicToScene();
+ return pTransformation->transform( aPos );
+}
+
+awt::Point PlottingPositionHelper::transformSceneToScreenPosition( const drawing::Position3D& rScenePosition3D
+ , const rtl::Reference<SvxShapeGroupAnyD>& xSceneTarget
+ , sal_Int32 nDimensionCount )
+{
+ //@todo would like to have a cheaper method to do this transformation
+ awt::Point aScreenPoint( static_cast<sal_Int32>(rScenePosition3D.PositionX), static_cast<sal_Int32>(rScenePosition3D.PositionY) );
+
+ //transformation from scene to screen (only necessary for 3D):
+ if(nDimensionCount==3)
+ {
+ //create 3D anchor shape
+ tPropertyNameMap aDummyPropertyNameMap;
+ rtl::Reference<Svx3DExtrudeObject> xShape3DAnchor = ShapeFactory::createCube( xSceneTarget
+ , rScenePosition3D,drawing::Direction3D(1,1,1)
+ , 0, nullptr, aDummyPropertyNameMap);
+ //get 2D position from xShape3DAnchor
+ aScreenPoint = xShape3DAnchor->getPosition();
+ xSceneTarget->remove(xShape3DAnchor);
+ }
+ return aScreenPoint;
+}
+
+void PlottingPositionHelper::transformScaledLogicToScene( drawing::PolyPolygonShape3D& rPolygon ) const
+{
+ drawing::Position3D aScenePosition;
+ auto SequenceXRange = asNonConstRange(rPolygon.SequenceX);
+ auto SequenceYRange = asNonConstRange(rPolygon.SequenceY);
+ auto SequenceZRange = asNonConstRange(rPolygon.SequenceZ);
+ for( sal_Int32 nS = rPolygon.SequenceX.getLength(); nS--;)
+ {
+ auto xValuesRange = asNonConstRange(SequenceXRange[nS]);
+ auto yValuesRange = asNonConstRange(SequenceYRange[nS]);
+ auto zValuesRange = asNonConstRange(SequenceZRange[nS]);
+ for( sal_Int32 nP = SequenceXRange[nS].getLength(); nP--; )
+ {
+ double& fX = xValuesRange[nP];
+ double& fY = yValuesRange[nP];
+ double& fZ = zValuesRange[nP];
+ aScenePosition = transformScaledLogicToScene( fX,fY,fZ,true );
+ fX = aScenePosition.PositionX;
+ fY = aScenePosition.PositionY;
+ fZ = aScenePosition.PositionZ;
+ }
+ }
+}
+
+void PlottingPositionHelper::transformScaledLogicToScene( std::vector<std::vector<css::drawing::Position3D>>& rPolygon ) const
+{
+ drawing::Position3D aScenePosition;
+ for( sal_Int32 nS = static_cast<sal_Int32>(rPolygon.size()); nS--;)
+ {
+ auto valuesRange = rPolygon[nS].data();
+ for( sal_Int32 nP = rPolygon[nS].size(); nP--; )
+ {
+ double& fX = valuesRange[nP].PositionX;
+ double& fY = valuesRange[nP].PositionY;
+ double& fZ = valuesRange[nP].PositionZ;
+ aScenePosition = transformScaledLogicToScene( fX,fY,fZ,true );
+ fX = aScenePosition.PositionX;
+ fY = aScenePosition.PositionY;
+ fZ = aScenePosition.PositionZ;
+ }
+ }
+}
+
+void PlottingPositionHelper::clipScaledLogicValues( double* pX, double* pY, double* pZ ) const
+{
+ //get logic clip values:
+ double MinX = getLogicMinX();
+ double MinY = getLogicMinY();
+ double MinZ = getLogicMinZ();
+ double MaxX = getLogicMaxX();
+ double MaxY = getLogicMaxY();
+ double MaxZ = getLogicMaxZ();
+
+ //apply scaling
+ doUnshiftedLogicScaling( &MinX, &MinY, &MinZ );
+ doUnshiftedLogicScaling( &MaxX, &MaxY, &MaxZ);
+
+ if(pX)
+ {
+ if( *pX < MinX )
+ *pX = MinX;
+ else if( *pX > MaxX )
+ *pX = MaxX;
+ }
+ if(pY)
+ {
+ if( *pY < MinY )
+ *pY = MinY;
+ else if( *pY > MaxY )
+ *pY = MaxY;
+ }
+ if(pZ)
+ {
+ if( *pZ < MinZ )
+ *pZ = MinZ;
+ else if( *pZ > MaxZ )
+ *pZ = MaxZ;
+ }
+}
+
+basegfx::B2DRectangle PlottingPositionHelper::getScaledLogicClipDoubleRect() const
+{
+ //get logic clip values:
+ double MinX = getLogicMinX();
+ double MinY = getLogicMinY();
+ double MinZ = getLogicMinZ();
+ double MaxX = getLogicMaxX();
+ double MaxY = getLogicMaxY();
+ double MaxZ = getLogicMaxZ();
+
+ //apply scaling
+ doUnshiftedLogicScaling( &MinX, &MinY, &MinZ );
+ doUnshiftedLogicScaling( &MaxX, &MaxY, &MaxZ);
+
+ basegfx::B2DRectangle aRet( MinX, MaxY, MaxX, MinY );
+ return aRet;
+}
+
+drawing::Direction3D PlottingPositionHelper::getScaledLogicWidth() const
+{
+ drawing::Direction3D aRet;
+
+ double MinX = getLogicMinX();
+ double MinY = getLogicMinY();
+ double MinZ = getLogicMinZ();
+ double MaxX = getLogicMaxX();
+ double MaxY = getLogicMaxY();
+ double MaxZ = getLogicMaxZ();
+
+ doLogicScaling( &MinX, &MinY, &MinZ );
+ doLogicScaling( &MaxX, &MaxY, &MaxZ);
+
+ aRet.DirectionX = MaxX - MinX;
+ aRet.DirectionY = MaxY - MinY;
+ aRet.DirectionZ = MaxZ - MinZ;
+ return aRet;
+}
+
+PolarPlottingPositionHelper::PolarPlottingPositionHelper()
+ : m_fRadiusOffset(0.0)
+ , m_fAngleDegreeOffset(90.0)
+{
+ m_bMaySkipPointsInRegressionCalculation = false;
+}
+
+PolarPlottingPositionHelper::PolarPlottingPositionHelper( const PolarPlottingPositionHelper& rSource )
+ : PlottingPositionHelper(rSource)
+ , m_fRadiusOffset( rSource.m_fRadiusOffset )
+ , m_fAngleDegreeOffset( rSource.m_fAngleDegreeOffset )
+ , m_aUnitCartesianToScene( rSource.m_aUnitCartesianToScene )
+{
+}
+
+PolarPlottingPositionHelper::~PolarPlottingPositionHelper()
+{
+}
+
+std::unique_ptr<PlottingPositionHelper> PolarPlottingPositionHelper::clone() const
+{
+ return std::make_unique<PolarPlottingPositionHelper>(*this);
+}
+
+void PolarPlottingPositionHelper::setTransformationSceneToScreen( const drawing::HomogenMatrix& rMatrix)
+{
+ PlottingPositionHelper::setTransformationSceneToScreen( rMatrix);
+ m_aUnitCartesianToScene =impl_calculateMatrixUnitCartesianToScene( m_aMatrixScreenToScene );
+}
+void PolarPlottingPositionHelper::setScales( std::vector< ExplicitScaleData >&& rScales, bool bSwapXAndYAxis )
+{
+ PlottingPositionHelper::setScales( std::move(rScales), bSwapXAndYAxis );
+ m_aUnitCartesianToScene =impl_calculateMatrixUnitCartesianToScene( m_aMatrixScreenToScene );
+}
+
+::basegfx::B3DHomMatrix PolarPlottingPositionHelper::impl_calculateMatrixUnitCartesianToScene( const ::basegfx::B3DHomMatrix& rMatrixScreenToScene ) const
+{
+ ::basegfx::B3DHomMatrix aRet;
+
+ if( m_aScales.empty() )
+ return aRet;
+
+ double fTranslate =1.0;
+ double fScale =FIXED_SIZE_FOR_3D_CHART_VOLUME/2.0;
+
+ double fTranslateLogicZ;
+ double fScaleLogicZ;
+ {
+ double fScaleDirectionZ = m_aScales[2].Orientation==AxisOrientation_MATHEMATICAL ? 1.0 : -1.0;
+ double MinZ = getLogicMinZ();
+ double MaxZ = getLogicMaxZ();
+ doLogicScaling( nullptr, nullptr, &MinZ );
+ doLogicScaling( nullptr, nullptr, &MaxZ );
+ double fWidthZ = MaxZ - MinZ;
+
+ if( m_aScales[2].Orientation==AxisOrientation_MATHEMATICAL )
+ fTranslateLogicZ=MinZ;
+ else
+ fTranslateLogicZ=MaxZ;
+ fScaleLogicZ = fScaleDirectionZ*FIXED_SIZE_FOR_3D_CHART_VOLUME/fWidthZ;
+ }
+
+ double fTranslateX = fTranslate;
+ double fTranslateY = fTranslate;
+ double fTranslateZ = fTranslateLogicZ;
+
+ double fScaleX = fScale;
+ double fScaleY = fScale;
+ double fScaleZ = fScaleLogicZ;
+
+ aRet.translate(fTranslateX, fTranslateY, fTranslateZ);//x first
+ aRet.scale(fScaleX, fScaleY, fScaleZ);//x first
+
+ aRet = rMatrixScreenToScene * aRet;
+ return aRet;
+}
+
+::chart::XTransformation2* PolarPlottingPositionHelper::getTransformationScaledLogicToScene() const
+{
+ if( !m_xTransformationLogicToScene )
+ m_xTransformationLogicToScene.reset(new VPolarTransformation(*this));
+ return m_xTransformationLogicToScene.get();
+}
+
+double PolarPlottingPositionHelper::getWidthAngleDegree( double& fStartLogicValueOnAngleAxis, double& fEndLogicValueOnAngleAxis ) const
+{
+ const ExplicitScaleData& rAngleScale = m_bSwapXAndY ? m_aScales[1] : m_aScales[0];
+ if( rAngleScale.Orientation != AxisOrientation_MATHEMATICAL )
+ {
+ double fHelp = fEndLogicValueOnAngleAxis;
+ fEndLogicValueOnAngleAxis = fStartLogicValueOnAngleAxis;
+ fStartLogicValueOnAngleAxis = fHelp;
+ }
+
+ double fStartAngleDegree = transformToAngleDegree( fStartLogicValueOnAngleAxis );
+ double fEndAngleDegree = transformToAngleDegree( fEndLogicValueOnAngleAxis );
+ double fWidthAngleDegree = fEndAngleDegree - fStartAngleDegree;
+
+ if( ::rtl::math::approxEqual( fStartAngleDegree, fEndAngleDegree )
+ && !::rtl::math::approxEqual( fStartLogicValueOnAngleAxis, fEndLogicValueOnAngleAxis ) )
+ fWidthAngleDegree = 360.0;
+
+ // tdf#123504: both 0 and 360 are valid and different values here!
+ while (fWidthAngleDegree < 0.0)
+ fWidthAngleDegree += 360.0;
+ while (fWidthAngleDegree > 360.0)
+ fWidthAngleDegree -= 360.0;
+
+ return fWidthAngleDegree;
+}
+
+//This method does a lot of computation for understanding which scale to
+//utilize and if reverse orientation should be used. Indeed, for a pie or donut,
+//the final result is as simple as multiplying by 360 and adding
+//`m_fAngleDegreeOffset`.
+double PolarPlottingPositionHelper::transformToAngleDegree( double fLogicValueOnAngleAxis, bool bDoScaling ) const
+{
+ double fRet=0.0;
+
+ double fAxisAngleScaleDirection = 1.0;
+ {
+ const ExplicitScaleData& rScale = m_bSwapXAndY ? m_aScales[1] : m_aScales[0];
+ if(rScale.Orientation != AxisOrientation_MATHEMATICAL)
+ fAxisAngleScaleDirection *= -1.0;
+ }
+
+ double MinAngleValue = 0.0;
+ double MaxAngleValue = 0.0;
+ {
+ double MinX = getLogicMinX();
+ double MinY = getLogicMinY();
+ double MaxX = getLogicMaxX();
+ double MaxY = getLogicMaxY();
+ double MinZ = getLogicMinZ();
+ double MaxZ = getLogicMaxZ();
+
+ doLogicScaling( &MinX, &MinY, &MinZ );
+ doLogicScaling( &MaxX, &MaxY, &MaxZ);
+
+ MinAngleValue = m_bSwapXAndY ? MinY : MinX;
+ MaxAngleValue = m_bSwapXAndY ? MaxY : MaxX;
+ }
+
+ double fScaledLogicAngleValue = 0.0;
+ if(bDoScaling)
+ {
+ double fX = m_bSwapXAndY ? getLogicMaxX() : fLogicValueOnAngleAxis;
+ double fY = m_bSwapXAndY ? fLogicValueOnAngleAxis : getLogicMaxY();
+ double fZ = getLogicMaxZ();
+ clipLogicValues( &fX, &fY, &fZ );
+ doLogicScaling( &fX, &fY, &fZ );
+ fScaledLogicAngleValue = m_bSwapXAndY ? fY : fX;
+ }
+ else
+ fScaledLogicAngleValue = fLogicValueOnAngleAxis;
+
+ fRet = m_fAngleDegreeOffset
+ + fAxisAngleScaleDirection*(fScaledLogicAngleValue-MinAngleValue)*360.0
+ /fabs(MaxAngleValue-MinAngleValue);
+ // tdf#123504: both 0 and 360 are valid and different values here!
+ while (fRet > 360.0)
+ fRet -= 360.0;
+ while (fRet < 0)
+ fRet += 360.0;
+ return fRet;
+}
+
+/**
+ * Given a value in the radius axis scale range, it returns, in the simplest
+ * case (that is when `m_fRadiusOffset` is zero), the normalized value; when
+ * `m_fRadiusOffset` is not zero (e.g. as in the case of a donut), the interval
+ * used for normalization is extended by `m_fRadiusOffset`: if the axis
+ * orientation is not reversed the new interval becomes
+ * [scale.Minimum - m_fRadiusOffset, scale.Maximum] else it becomes
+ * [scale.Minimum, scale.Maximum + m_fRadiusOffset].
+ * Pay attention here! For the latter case, since the axis orientation is
+ * reversed, the normalization is reversed too. Indeed, we have
+ * `transformToRadius(scale.Maximum + m_fRadiusOffset) = 0` and
+ * `transformToRadius(scale.Minimum) = 1`.
+ *
+ * For a pie chart the radius axis scale range is initialized by the
+ * `getMinimum` and `getMaximum` methods of the `PieChart` object (see notes
+ * for `VCoordinateSystem::prepareAutomaticAxisScaling`).
+ * So we have scale.Minimum = 0.5 (always constant!) and
+ * scale.Maximum = 0.5 + number_of_rings + max_offset
+ * (see notes for `PieChart::getMaxOffset`).
+ * Hence we get the following general formulas for computing normalized inner
+ * and outer radius:
+ *
+ * 1- transformToRadius(inner_radius) =
+ * (number_of_rings - (ring_index + 1) + m_fRadiusOffset)
+ * / (number_of_rings + max_offset + m_fRadiusOffset)
+ *
+ * 2- transformToRadius(outer_radius) =
+ * (1 + number_of_rings - (ring_index + 1) + m_fRadiusOffset)
+ * / (number_of_rings + max_offset + m_fRadiusOffset).
+ *
+ * Here you have to take into account that values for inner and outer radius
+ * are swapped since the radius axis is reversed (See notes for
+ * `PiePositionHelper::getInnerAndOuterRadius`). So indeed inner_radius is
+ * the outer and outer_radius is the inner. Anyway still because of the reverse
+ * orientation, the normalization performed by `transformToRadius` is reversed
+ * too, as we have seen above. Hence `transformToRadius(inner_radius)` is
+ * really the normalized inner radius and `transformToRadius(outer_radius)` is
+ * really the normalized outer radius.
+ *
+ * Some basic examples where we apply the above formulas:
+ * 1- For a non-exploded pie chart we have:
+ * `transformToRadius(inner_radius) = 0`,
+ * `transformToRadius(outer_radius) = 1`.
+ * 2- For a non-exploded donut with a single ring we have:
+ * `transformToRadius(inner_radius) =
+ * m_fRadiusOffset/(1 + m_fRadiusOffset)`,
+ * `transformToRadius(outer_radius) =
+ * (1 + m_fRadiusOffset)/(1 + m_fRadiusOffset) = 1`.
+ * 3- For an exploded pie chart we have:
+ * `transformToRadius(inner_radius) = 0/(1 + max_offset) = 0`,
+ * `transformToRadius(outer_radius) = 1/(1 + max_offset)`.
+ *
+ * The third example needs some remark. Both the logical inner and outer
+ * radius passed to `transformToRadius` are offset by `max_offset`.
+ * However the returned normalized values do not contain any (normalized)
+ * offset term at all, otherwise the returned values would be
+ * `max_offset/(1 + max_offset)` and `1`. Hence, for exploded pie/donut,
+ * `transformToRadius` returns the normalized value of radii without any
+ * offset term. These values are smaller than in the non-exploded case by an
+ * amount equals to the value of the normalized maximum offset
+ * (`max_offset/(1 + max_offset)` in the example above). That is due to the
+ * fact that the normalization keeps into account the space needed for the
+ * offset. This is the correct behavior, in fact the offset for the current
+ * slice could be different from the maximum offset.
+ * These remarks should clarify why the `PieChart::createDataPoint` and
+ * `PieChart::createTextLabelShape` methods add the normalized offset (for the
+ * current slice) to the normalized radii in order to achieve the correct
+ * placement of slice and text shapes.
+ */
+double PolarPlottingPositionHelper::transformToRadius( double fLogicValueOnRadiusAxis, bool bDoScaling ) const
+{
+ double fNormalRadius = 0.0;
+ {
+ double fScaledLogicRadiusValue = 0.0;
+ double fX = m_bSwapXAndY ? fLogicValueOnRadiusAxis: getLogicMaxX();
+ double fY = m_bSwapXAndY ? getLogicMaxY() : fLogicValueOnRadiusAxis;
+ if(bDoScaling)
+ doLogicScaling( &fX, &fY, nullptr );
+
+ fScaledLogicRadiusValue = m_bSwapXAndY ? fX : fY;
+
+ bool bMinIsInnerRadius = true;
+ const ExplicitScaleData& rScale = m_bSwapXAndY ? m_aScales[0] : m_aScales[1];
+ if(rScale.Orientation != AxisOrientation_MATHEMATICAL)
+ bMinIsInnerRadius = false;
+
+ double fInnerScaledLogicRadius=0.0;
+ double fOuterScaledLogicRadius=0.0;
+ {
+ double MinX = getLogicMinX();
+ double MinY = getLogicMinY();
+ doLogicScaling( &MinX, &MinY, nullptr );
+ double MaxX = getLogicMaxX();
+ double MaxY = getLogicMaxY();
+ doLogicScaling( &MaxX, &MaxY, nullptr );
+
+ double fMin = m_bSwapXAndY ? MinX : MinY;
+ double fMax = m_bSwapXAndY ? MaxX : MaxY;
+
+ fInnerScaledLogicRadius = bMinIsInnerRadius ? fMin : fMax;
+ fOuterScaledLogicRadius = bMinIsInnerRadius ? fMax : fMin;
+ }
+
+ if( bMinIsInnerRadius )
+ fInnerScaledLogicRadius -= fabs(m_fRadiusOffset);
+ else
+ fInnerScaledLogicRadius += fabs(m_fRadiusOffset);
+ fNormalRadius = (fScaledLogicRadiusValue-fInnerScaledLogicRadius)/(fOuterScaledLogicRadius-fInnerScaledLogicRadius);
+ }
+ return fNormalRadius;
+}
+
+drawing::Position3D PolarPlottingPositionHelper::transformLogicToScene( double fX, double fY, double fZ, bool bClip ) const
+{
+ if(bClip)
+ clipLogicValues( &fX,&fY,&fZ );
+ double fLogicValueOnAngleAxis = m_bSwapXAndY ? fY : fX;
+ double fLogicValueOnRadiusAxis = m_bSwapXAndY ? fX : fY;
+ return transformAngleRadiusToScene( fLogicValueOnAngleAxis, fLogicValueOnRadiusAxis, fZ );
+}
+
+drawing::Position3D PolarPlottingPositionHelper::transformScaledLogicToScene( double fX, double fY, double fZ, bool bClip ) const
+{
+ if(bClip)
+ clipScaledLogicValues( &fX,&fY,&fZ );
+ double fLogicValueOnAngleAxis = m_bSwapXAndY ? fY : fX;
+ double fLogicValueOnRadiusAxis = m_bSwapXAndY ? fX : fY;
+ return transformAngleRadiusToScene( fLogicValueOnAngleAxis, fLogicValueOnRadiusAxis, fZ, false );
+}
+drawing::Position3D PolarPlottingPositionHelper::transformUnitCircleToScene( double fUnitAngleDegree, double fUnitRadius
+ , double fLogicZ ) const
+{
+ double fAnglePi = basegfx::deg2rad(fUnitAngleDegree);
+
+ double fX=fUnitRadius*std::cos(fAnglePi);
+ double fY=fUnitRadius*std::sin(fAnglePi);
+ double fZ=fLogicZ;
+
+ //!! applying matrix to vector does ignore translation, so it is important to use a B3DPoint here instead of B3DVector
+ ::basegfx::B3DPoint aPoint(fX,fY,fZ);
+ ::basegfx::B3DPoint aRet = m_aUnitCartesianToScene * aPoint;
+ return B3DPointToPosition3D(aRet);
+}
+
+drawing::Position3D PolarPlottingPositionHelper::transformAngleRadiusToScene( double fLogicValueOnAngleAxis, double fLogicValueOnRadiusAxis, double fLogicZ, bool bDoScaling ) const
+{
+ double fUnitAngleDegree = transformToAngleDegree(fLogicValueOnAngleAxis,bDoScaling);
+ double fUnitRadius = transformToRadius(fLogicValueOnRadiusAxis,bDoScaling);
+
+ return transformUnitCircleToScene( fUnitAngleDegree, fUnitRadius, fLogicZ );
+}
+
+double PolarPlottingPositionHelper::getOuterLogicRadius() const
+{
+ const ExplicitScaleData& rScale = m_bSwapXAndY ? m_aScales[0] : m_aScales[1];
+ if( rScale.Orientation==AxisOrientation_MATHEMATICAL )
+ return rScale.Maximum;
+ else
+ return rScale.Minimum;
+}
+
+bool PlottingPositionHelper::isPercentY() const
+{
+ return m_aScales[1].AxisType==AxisType::PERCENT;
+}
+
+double PlottingPositionHelper::getBaseValueY() const
+{
+ return m_aScales[1].Origin;
+}
+
+void PlottingPositionHelper::setTimeResolution( tools::Long nTimeResolution, const Date& rNullDate )
+{
+ m_nTimeResolution = nTimeResolution;
+ m_aNullDate = rNullDate;
+
+ //adapt category width
+ double fCategoryWidth = 1.0;
+ if( !m_aScales.empty() )
+ {
+ if( m_aScales[0].AxisType == css::chart2::AxisType::DATE )
+ {
+ m_bDateAxis = true;
+ if( nTimeResolution == css::chart::TimeUnit::YEAR )
+ {
+ const double fMonthCount = 12.0;//todo: this depends on the DateScaling and must be adjusted in case we use more generic calendars in future
+ fCategoryWidth = fMonthCount;
+ }
+ }
+ }
+ setScaledCategoryWidth(fCategoryWidth);
+}
+
+void PlottingPositionHelper::setScaledCategoryWidth( double fScaledCategoryWidth )
+{
+ m_fScaledCategoryWidth = fScaledCategoryWidth;
+}
+void PlottingPositionHelper::AllowShiftXAxisPos( bool bAllowShift )
+{
+ m_bAllowShiftXAxisPos = bAllowShift;
+}
+void PlottingPositionHelper::AllowShiftZAxisPos( bool bAllowShift )
+{
+ m_bAllowShiftZAxisPos = bAllowShift;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/PolarLabelPositionHelper.cxx b/chart2/source/view/main/PolarLabelPositionHelper.cxx
new file mode 100644
index 000000000..d5c819954
--- /dev/null
+++ b/chart2/source/view/main/PolarLabelPositionHelper.cxx
@@ -0,0 +1,158 @@
+/* -*- 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 <PolarLabelPositionHelper.hxx>
+#include <PlottingPositionHelper.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <basegfx/vector/b2ivector.hxx>
+
+#include <com/sun/star/chart/DataLabelPlacement.hpp>
+
+namespace chart
+{
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+
+PolarLabelPositionHelper::PolarLabelPositionHelper(
+ PolarPlottingPositionHelper* pPosHelper
+ , sal_Int32 nDimensionCount
+ , const rtl::Reference<SvxShapeGroupAnyD>& xLogicTarget )
+ : LabelPositionHelper( nDimensionCount, xLogicTarget )
+ , m_pPosHelper(pPosHelper)
+{
+}
+
+PolarLabelPositionHelper::~PolarLabelPositionHelper()
+{
+}
+
+awt::Point PolarLabelPositionHelper::getLabelScreenPositionAndAlignmentForLogicValues(
+ LabelAlignment& rAlignment
+ , double fLogicValueOnAngleAxis
+ , double fLogicValueOnRadiusAxis
+ , double fLogicZ
+ , sal_Int32 nScreenValueOffsetInRadiusDirection ) const
+{
+ double fUnitCircleAngleDegree = m_pPosHelper->transformToAngleDegree( fLogicValueOnAngleAxis );
+ double fUnitCircleRadius = m_pPosHelper->transformToRadius( fLogicValueOnRadiusAxis );
+
+ return getLabelScreenPositionAndAlignmentForUnitCircleValues(
+ rAlignment, css::chart::DataLabelPlacement::OUTSIDE
+ , fUnitCircleAngleDegree, 0.0
+ , fUnitCircleRadius, fUnitCircleRadius, fLogicZ, nScreenValueOffsetInRadiusDirection );
+}
+
+awt::Point PolarLabelPositionHelper::getLabelScreenPositionAndAlignmentForUnitCircleValues(
+ LabelAlignment& rAlignment, sal_Int32 nLabelPlacement
+ , double fUnitCircleStartAngleDegree, double fUnitCircleWidthAngleDegree
+ , double fUnitCircleInnerRadius, double fUnitCircleOuterRadius
+ , double fLogicZ
+ , sal_Int32 nScreenValueOffsetInRadiusDirection ) const
+{
+ bool bCenter = (nLabelPlacement != css::chart::DataLabelPlacement::OUTSIDE)
+ && (nLabelPlacement != css::chart::DataLabelPlacement::INSIDE);
+
+ double fAngleDegree = fUnitCircleStartAngleDegree + fUnitCircleWidthAngleDegree/2.0;
+ double fRadius = 0.0;
+ if( !bCenter ) //e.g. for pure pie chart(one ring only) or for angle axis of polar coordinate system
+ fRadius = fUnitCircleOuterRadius;
+ else
+ fRadius = fUnitCircleInnerRadius + (fUnitCircleOuterRadius-fUnitCircleInnerRadius)/2.0 ;
+
+ awt::Point aRet( transformSceneToScreenPosition(
+ m_pPosHelper->transformUnitCircleToScene( fAngleDegree, fRadius, fLogicZ+0.5 ) ) );
+
+ if(m_nDimensionCount==3 && nLabelPlacement == css::chart::DataLabelPlacement::OUTSIDE)
+ {
+ //check whether the upper or the downer edge is more distant from the center
+ //take the farthest point to put the label to
+
+ awt::Point aP0( transformSceneToScreenPosition(
+ m_pPosHelper->transformUnitCircleToScene( 0, 0, fLogicZ ) ) );
+ awt::Point aP1(aRet);
+ awt::Point aP2( transformSceneToScreenPosition(
+ m_pPosHelper->transformUnitCircleToScene( fAngleDegree, fRadius, fLogicZ-0.5 ) ) );
+
+ ::basegfx::B2DVector aV0( aP0.X, aP0.Y );
+ ::basegfx::B2DVector aV1( aP1.X, aP1.Y );
+ ::basegfx::B2DVector aV2( aP2.X, aP2.Y );
+
+ double fL1 = ::basegfx::B2DVector(aV1-aV0).getLength();
+ double fL2 = ::basegfx::B2DVector(aV2-aV0).getLength();
+
+ if(fL2>fL1)
+ aRet = aP2;
+
+ //calculate new angle for alignment
+ double fDX = aRet.X-aP0.X;
+ double fDY = aRet.Y-aP0.Y;
+ fDY*=-1.0;//drawing layer has inverse y values
+
+ fAngleDegree = basegfx::rad2deg(atan2(fDY,fDX));
+ }
+ //set LabelAlignment
+ if( !bCenter )
+ {
+ // tdf#123504: both 0 and 360 are valid and different values here!
+ while (fAngleDegree > 360.0)
+ fAngleDegree -= 360.0;
+ while (fAngleDegree < 0.0)
+ fAngleDegree += 360.0;
+
+ bool bOutside = nLabelPlacement == css::chart::DataLabelPlacement::OUTSIDE;
+
+ if (fAngleDegree <= 5 || fAngleDegree >= 355)
+ rAlignment = bOutside ? LABEL_ALIGN_RIGHT : LABEL_ALIGN_LEFT;
+ else if (fAngleDegree < 85)
+ rAlignment = bOutside ? LABEL_ALIGN_RIGHT_TOP : LABEL_ALIGN_LEFT_BOTTOM;
+ else if (fAngleDegree <= 95)
+ rAlignment = bOutside ? LABEL_ALIGN_TOP : LABEL_ALIGN_BOTTOM;
+ else if (fAngleDegree < 175)
+ rAlignment = bOutside ? LABEL_ALIGN_LEFT_TOP : LABEL_ALIGN_RIGHT_BOTTOM;
+ else if (fAngleDegree <= 185)
+ rAlignment = bOutside ? LABEL_ALIGN_LEFT : LABEL_ALIGN_RIGHT;
+ else if (fAngleDegree < 265)
+ rAlignment = bOutside ? LABEL_ALIGN_LEFT_BOTTOM : LABEL_ALIGN_RIGHT_TOP;
+ else if (fAngleDegree <= 275)
+ rAlignment = bOutside ? LABEL_ALIGN_BOTTOM : LABEL_ALIGN_TOP;
+ else
+ rAlignment = bOutside ? LABEL_ALIGN_RIGHT_BOTTOM : LABEL_ALIGN_LEFT_TOP;
+ }
+ else
+ {
+ rAlignment = LABEL_ALIGN_CENTER;
+ }
+
+ //add a scaling independent Offset if requested
+ if( nScreenValueOffsetInRadiusDirection != 0)
+ {
+ awt::Point aOrigin( transformSceneToScreenPosition(
+ m_pPosHelper->transformUnitCircleToScene( 0.0, 0.0, fLogicZ+0.5 ) ) );
+ basegfx::B2IVector aDirection( aRet.X- aOrigin.X, aRet.Y- aOrigin.Y );
+ aDirection.setLength(nScreenValueOffsetInRadiusDirection);
+ aRet.X += aDirection.getX();
+ aRet.Y += aDirection.getY();
+ }
+
+ return aRet;
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/PropertyMapper.cxx b/chart2/source/view/main/PropertyMapper.cxx
new file mode 100644
index 000000000..0ee929bc7
--- /dev/null
+++ b/chart2/source/view/main/PropertyMapper.cxx
@@ -0,0 +1,561 @@
+/* -*- 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 <PropertyMapper.hxx>
+#include <unonames.hxx>
+
+#include <com/sun/star/beans/XMultiPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/TextVerticalAdjust.hpp>
+#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
+#include <com/sun/star/drawing/LineJoint.hpp>
+#include <com/sun/star/style/ParagraphAdjust.hpp>
+#include <tools/diagnose_ex.h>
+#include <svx/unoshape.hxx>
+
+namespace chart
+{
+using namespace ::com::sun::star;
+
+void PropertyMapper::setMappedProperties(
+ SvxShape& xTarget
+ , const uno::Reference< beans::XPropertySet >& xSource
+ , const tPropertyNameMap& rMap )
+{
+ if( !xSource.is() )
+ return;
+
+ sal_Int32 nPropertyCount = rMap.size();
+ tNameSequence aNames(nPropertyCount);
+ tAnySequence aValues(nPropertyCount);
+ auto pNames = aNames.getArray();
+ auto pValues = aValues.getArray();
+ sal_Int32 nN=0;
+
+ for (auto const& elem : rMap)
+ {
+ const OUString & rTarget = elem.first;
+ const OUString & rSource = elem.second;
+ try
+ {
+ uno::Any aAny( xSource->getPropertyValue(rSource) );
+ if( aAny.hasValue() )
+ {
+ //do not set empty anys because of performance (otherwise SdrAttrObj::ItemChange will take much longer)
+ pNames[nN] = rTarget;
+ pValues[nN] = std::move(aAny);
+ ++nN;
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ }
+ if (nN == 0)
+ return;
+ //reduce to real property count
+ aNames.realloc(nN);
+ aValues.realloc(nN);
+
+ uno::Reference< beans::XMultiPropertySet > xShapeMultiProp( xTarget, uno::UNO_QUERY_THROW );
+ try
+ {
+ xShapeMultiProp->setPropertyValues( aNames, aValues );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" ); //if this occurs more often think of removing the XMultiPropertySet completely for better performance
+ }
+}
+
+void PropertyMapper::setMappedProperties(
+ const uno::Reference< beans::XPropertySet >& xTarget
+ , const uno::Reference< beans::XPropertySet >& xSource
+ , const tPropertyNameMap& rMap )
+{
+ if( !xTarget.is() || !xSource.is() )
+ return;
+
+ tNameSequence aNames;
+ tAnySequence aValues;
+ sal_Int32 nN=0;
+ sal_Int32 nPropertyCount = rMap.size();
+ aNames.realloc(nPropertyCount);
+ auto pNames = aNames.getArray();
+ aValues.realloc(nPropertyCount);
+ auto pValues = aValues.getArray();
+
+ for (auto const& elem : rMap)
+ {
+ const OUString & rTarget = elem.first;
+ const OUString & rSource = elem.second;
+ try
+ {
+ uno::Any aAny( xSource->getPropertyValue(rSource) );
+ if( aAny.hasValue() )
+ {
+ //do not set empty anys because of performance (otherwise SdrAttrObj::ItemChange will take much longer)
+ pNames[nN] = rTarget;
+ pValues[nN] = aAny;
+ ++nN;
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ }
+ if (nN == 0)
+ return;
+
+ uno::Reference< beans::XMultiPropertySet > xShapeMultiProp( xTarget, uno::UNO_QUERY );
+ if (xShapeMultiProp)
+ try
+ {
+ //reduce to real property count
+ aNames.realloc(nN);
+ aValues.realloc(nN);
+ xShapeMultiProp->setPropertyValues( aNames, aValues );
+ return; // successful
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" ); //if this occurs more often think of removing the XMultiPropertySet completely for better performance
+ }
+
+ // fall back to one at a time
+ try
+ {
+ for( sal_Int32 i = 0; i < nN; i++ )
+ {
+ try
+ {
+ xTarget->setPropertyValue( aNames[i], aValues[i] );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+}
+
+void PropertyMapper::getValueMap(
+ tPropertyNameValueMap& rValueMap
+ , const tPropertyNameMap& rNameMap
+ , const uno::Reference< beans::XPropertySet >& xSourceProp
+ )
+{
+ uno::Reference< beans::XMultiPropertySet > xMultiPropSet(xSourceProp, uno::UNO_QUERY);
+ if((false) && xMultiPropSet.is())
+ {
+ uno::Sequence< OUString > aPropSourceNames(rNameMap.size());
+ auto aPropSourceNamesRange = asNonConstRange(aPropSourceNames);
+ uno::Sequence< OUString > aPropTargetNames(rNameMap.size());
+ auto aPropTargetNamesRange = asNonConstRange(aPropTargetNames);
+ sal_Int32 i = 0;
+ for (auto const& elem : rNameMap)
+ {
+ aPropTargetNamesRange[i] = elem.first;
+ aPropSourceNamesRange[i] = elem.second;
+ ++i;
+ }
+
+ uno::Sequence< uno::Any > xValues = xMultiPropSet->getPropertyValues(aPropSourceNames);
+ sal_Int32 n = rNameMap.size();
+ for(i = 0;i < n; ++i)
+ {
+ if( xValues[i].hasValue() )
+ rValueMap.emplace( aPropTargetNames[i], xValues[i] );
+ }
+ }
+ else
+ {
+ for (auto const& elem : rNameMap)
+ {
+ const OUString & rTarget = elem.first;
+ const OUString & rSource = elem.second;
+ try
+ {
+ uno::Any aAny( xSourceProp->getPropertyValue(rSource) );
+ if( aAny.hasValue() )
+ rValueMap.emplace( rTarget, aAny );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ }
+ }
+}
+
+void PropertyMapper::getMultiPropertyListsFromValueMap(
+ tNameSequence& rNames
+ , tAnySequence& rValues
+ , const tPropertyNameValueMap& rValueMap
+ )
+{
+ sal_Int32 nPropertyCount = rValueMap.size();
+ rNames.realloc(nPropertyCount);
+ auto pNames = rNames.getArray();
+ rValues.realloc(nPropertyCount);
+ auto pValues = rValues.getArray();
+
+ //fill sequences
+ sal_Int32 nN=0;
+ for (auto const& elem : rValueMap)
+ {
+ const uno::Any& rAny = elem.second;
+ if( rAny.hasValue() )
+ {
+ //do not set empty anys because of performance (otherwise SdrAttrObj::ItemChange will take much longer)
+ pNames[nN] = elem.first;
+ pValues[nN] = rAny;
+ ++nN;
+ }
+ }
+ //reduce to real property count
+ rNames.realloc(nN);
+ rValues.realloc(nN);
+}
+
+uno::Any* PropertyMapper::getValuePointer( tAnySequence& rPropValues
+ , const tNameSequence& rPropNames
+ , std::u16string_view rPropName )
+{
+ sal_Int32 nCount = rPropNames.getLength();
+ for( sal_Int32 nN = 0; nN < nCount; nN++ )
+ {
+ if(rPropNames[nN] == rPropName)
+ return &rPropValues.getArray()[nN];
+ }
+ return nullptr;
+}
+
+uno::Any* PropertyMapper::getValuePointerForLimitedSpace( tAnySequence& rPropValues
+ , const tNameSequence& rPropNames
+ , bool bLimitedHeight)
+{
+ return PropertyMapper::getValuePointer( rPropValues, rPropNames
+ , bLimitedHeight ? OUString("TextMaximumFrameHeight") : OUString("TextMaximumFrameWidth") );
+}
+
+const tPropertyNameMap& PropertyMapper::getPropertyNameMapForCharacterProperties()
+{
+ //shape property -- chart model object property
+ static tPropertyNameMap s_aShapePropertyMapForCharacterProperties{
+ {"CharColor", "CharColor"},
+ {"CharContoured", "CharContoured"},
+ {"CharEmphasis", "CharEmphasis"},//the service style::CharacterProperties describes a property called 'CharEmphasize' which is nowhere implemented
+
+ {"CharFontFamily", "CharFontFamily"},
+ {"CharFontFamilyAsian", "CharFontFamilyAsian"},
+ {"CharFontFamilyComplex", "CharFontFamilyComplex"},
+ {"CharFontCharSet", "CharFontCharSet"},
+ {"CharFontCharSetAsian", "CharFontCharSetAsian"},
+ {"CharFontCharSetComplex", "CharFontCharSetComplex"},
+ {"CharFontName", "CharFontName"},
+ {"CharFontNameAsian", "CharFontNameAsian"},
+ {"CharFontNameComplex", "CharFontNameComplex"},
+ {"CharFontPitch", "CharFontPitch"},
+ {"CharFontPitchAsian", "CharFontPitchAsian"},
+ {"CharFontPitchComplex", "CharFontPitchComplex"},
+ {"CharFontStyleName", "CharFontStyleName"},
+ {"CharFontStyleNameAsian", "CharFontStyleNameAsian"},
+ {"CharFontStyleNameComplex", "CharFontStyleNameComplex"},
+
+ {"CharHeight", "CharHeight"},
+ {"CharHeightAsian", "CharHeightAsian"},
+ {"CharHeightComplex", "CharHeightComplex"},
+ {"CharKerning", "CharKerning"},
+ {"CharLocale", "CharLocale"},
+ {"CharLocaleAsian", "CharLocaleAsian"},
+ {"CharLocaleComplex", "CharLocaleComplex"},
+ {"CharPosture", "CharPosture"},
+ {"CharPostureAsian", "CharPostureAsian"},
+ {"CharPostureComplex", "CharPostureComplex"},
+ {"CharRelief", "CharRelief"},
+ {"CharShadowed", "CharShadowed"},
+ {"CharStrikeout", "CharStrikeout"},
+ {"CharUnderline", "CharUnderline"},
+ {"CharUnderlineColor", "CharUnderlineColor"},
+ {"CharUnderlineHasColor", "CharUnderlineHasColor"},
+ {"CharOverline", "CharOverline"},
+ {"CharOverlineColor", "CharOverlineColor"},
+ {"CharOverlineHasColor", "CharOverlineHasColor"},
+ {"CharWeight", "CharWeight"},
+ {"CharWeightAsian", "CharWeightAsian"},
+ {"CharWeightComplex", "CharWeightComplex"},
+ {"CharWordMode", "CharWordMode"},
+
+ {"WritingMode", "WritingMode"},
+
+ {"ParaIsCharacterDistance", "ParaIsCharacterDistance"}};
+
+ return s_aShapePropertyMapForCharacterProperties;
+}
+
+const tPropertyNameMap& PropertyMapper::getPropertyNameMapForParagraphProperties()
+{
+ //shape property -- chart model object property
+ static tPropertyNameMap s_aShapePropertyMapForParagraphProperties{
+ {"ParaAdjust", "ParaAdjust"},
+ {"ParaBottomMargin", "ParaBottomMargin"},
+ {"ParaIsHyphenation", "ParaIsHyphenation"},
+ {"ParaLastLineAdjust", "ParaLastLineAdjust"},
+ {"ParaLeftMargin", "ParaLeftMargin"},
+ {"ParaRightMargin", "ParaRightMargin"},
+ {"ParaTopMargin", "ParaTopMargin"}};
+ return s_aShapePropertyMapForParagraphProperties;
+}
+
+const tPropertyNameMap& PropertyMapper::getPropertyNameMapForFillProperties()
+{
+ //shape property -- chart model object property
+ static tPropertyNameMap s_aShapePropertyMapForFillProperties{
+ {"FillBackground", "FillBackground"},
+ {"FillBitmapName", "FillBitmapName"},
+ {"FillColor", "FillColor"},
+ {"FillGradientName", "FillGradientName"},
+ {"FillGradientStepCount", "FillGradientStepCount"},
+ {"FillHatchName", "FillHatchName"},
+ {"FillStyle", "FillStyle"},
+ {"FillTransparence", "FillTransparence"},
+ {"FillTransparenceGradientName", "FillTransparenceGradientName"},
+ //bitmap properties
+ {"FillBitmapMode", "FillBitmapMode"},
+ {"FillBitmapSizeX", "FillBitmapSizeX"},
+ {"FillBitmapSizeY", "FillBitmapSizeY"},
+ {"FillBitmapLogicalSize", "FillBitmapLogicalSize"},
+ {"FillBitmapOffsetX", "FillBitmapOffsetX"},
+ {"FillBitmapOffsetY", "FillBitmapOffsetY"},
+ {"FillBitmapRectanglePoint", "FillBitmapRectanglePoint"},
+ {"FillBitmapPositionOffsetX", "FillBitmapPositionOffsetX"},
+ {"FillBitmapPositionOffsetY", "FillBitmapPositionOffsetY"}};
+ return s_aShapePropertyMapForFillProperties;
+}
+
+const tPropertyNameMap& PropertyMapper::getPropertyNameMapForLineProperties()
+{
+ //shape property -- chart model object property
+ static tPropertyNameMap s_aShapePropertyMapForLineProperties{
+ {"LineColor", "LineColor"},
+ {"LineDashName", "LineDashName"},
+ {"LineJoint", "LineJoint"},
+ {"LineStyle", "LineStyle"},
+ {"LineTransparence", "LineTransparence"},
+ {"LineWidth", "LineWidth"},
+ {"LineCap", "LineCap"}};
+ return s_aShapePropertyMapForLineProperties;
+}
+
+namespace {
+ tPropertyNameMap getPropertyNameMapForFillAndLineProperties_() {
+ auto map = PropertyMapper::getPropertyNameMapForFillProperties();
+ auto const & add
+ = PropertyMapper::getPropertyNameMapForLineProperties();
+ map.insert(add.begin(), add.end());
+ return map;
+ }
+}
+const tPropertyNameMap& PropertyMapper::getPropertyNameMapForFillAndLineProperties()
+{
+ static tPropertyNameMap s_aShapePropertyMapForFillAndLineProperties
+ = getPropertyNameMapForFillAndLineProperties_();
+ return s_aShapePropertyMapForFillAndLineProperties;
+}
+
+namespace {
+ tPropertyNameMap getPropertyNameMapForTextShapeProperties_() {
+ auto map = PropertyMapper::getPropertyNameMapForCharacterProperties();
+ auto const & add1
+ = PropertyMapper::getPropertyNameMapForFillProperties();
+ map.insert(add1.begin(), add1.end());
+ auto const & add2
+ = PropertyMapper::getPropertyNameMapForLineProperties();
+ map.insert(add2.begin(), add2.end());
+ return map;
+ }
+}
+const tPropertyNameMap& PropertyMapper::getPropertyNameMapForTextShapeProperties()
+{
+ static tPropertyNameMap s_aShapePropertyMapForTextShapeProperties
+ = getPropertyNameMapForTextShapeProperties_();
+ return s_aShapePropertyMapForTextShapeProperties;
+}
+
+const tPropertyNameMap& PropertyMapper::getPropertyNameMapForLineSeriesProperties()
+{
+ //shape property -- chart model object property
+ static tPropertyNameMap s_aShapePropertyMapForLineSeriesProperties{
+ {"LineColor", "Color"},
+ {"LineDashName", "LineDashName"},
+ {"LineStyle", "LineStyle"},
+ {"LineTransparence", "Transparency"},
+ {"LineWidth", "LineWidth"},
+ {"LineCap", "LineCap"}};
+ return s_aShapePropertyMapForLineSeriesProperties;
+}
+
+namespace {
+ tPropertyNameMap getPropertyNameMapForTextLabelProperties_() {
+ auto map = PropertyMapper::getPropertyNameMapForCharacterProperties();
+ map.insert({
+ {"LineStyle", CHART_UNONAME_LABEL_BORDER_STYLE},
+ {"LineWidth", CHART_UNONAME_LABEL_BORDER_WIDTH},
+ {"LineColor", CHART_UNONAME_LABEL_BORDER_COLOR},
+ {"LineTransparence", CHART_UNONAME_LABEL_BORDER_TRANS},
+ {"FillStyle", CHART_UNONAME_LABEL_FILL_STYLE},
+ {"FillColor", CHART_UNONAME_LABEL_FILL_COLOR},
+ {"FillBackground", CHART_UNONAME_LABEL_FILL_BACKGROUND},
+ {"FillHatchName", CHART_UNONAME_LABEL_FILL_HATCH_NAME}
+ });
+ // fix the spelling!
+ return map;
+ }
+}
+const tPropertyNameMap& PropertyMapper::getPropertyNameMapForTextLabelProperties()
+{
+ // target name (drawing layer) : source name (chart model)
+ static tPropertyNameMap aMap = getPropertyNameMapForTextLabelProperties_();
+ return aMap;
+}
+
+const tPropertyNameMap& PropertyMapper::getPropertyNameMapForFilledSeriesProperties()
+{
+ //shape property -- chart model object property
+ static tPropertyNameMap s_aShapePropertyMapForFilledSeriesProperties{
+ {"FillBackground", "FillBackground"},
+ {"FillBitmapName", "FillBitmapName"},
+ {"FillColor", "Color"},
+ {"FillGradientName", "GradientName"},
+ {"FillGradientStepCount", "GradientStepCount"},
+ {"FillHatchName", "HatchName"},
+ {"FillStyle", "FillStyle"},
+ {"FillTransparence", "Transparency"},
+ {"FillTransparenceGradientName", "TransparencyGradientName"},
+ //bitmap properties
+ {"FillBitmapMode", "FillBitmapMode"},
+ {"FillBitmapSizeX", "FillBitmapSizeX"},
+ {"FillBitmapSizeY", "FillBitmapSizeY"},
+ {"FillBitmapLogicalSize", "FillBitmapLogicalSize"},
+ {"FillBitmapOffsetX", "FillBitmapOffsetX"},
+ {"FillBitmapOffsetY", "FillBitmapOffsetY"},
+ {"FillBitmapRectanglePoint", "FillBitmapRectanglePoint"},
+ {"FillBitmapPositionOffsetX", "FillBitmapPositionOffsetX"},
+ {"FillBitmapPositionOffsetY", "FillBitmapPositionOffsetY"},
+ //line properties
+ {"LineColor", "BorderColor"},
+ {"LineDashName", "BorderDashName"},
+ {"LineStyle", "BorderStyle"},
+ {"LineTransparence", "BorderTransparency"},
+ {"LineWidth", "BorderWidth"},
+ {"LineCap", "LineCap"}};
+ return s_aShapePropertyMapForFilledSeriesProperties;
+}
+
+void PropertyMapper::setMultiProperties(
+ const tNameSequence& rNames
+ , const tAnySequence& rValues
+ , SvxShape& xTarget )
+{
+ try
+ {
+ xTarget.setPropertyValues( rNames, rValues );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" ); //if this occurs more often think of removing the XMultiPropertySet completely for better performance
+ }
+}
+
+void PropertyMapper::getTextLabelMultiPropertyLists(
+ const uno::Reference< beans::XPropertySet >& xSourceProp
+ , tNameSequence& rPropNames, tAnySequence& rPropValues
+ , bool bName
+ , sal_Int32 nLimitedSpace
+ , bool bLimitedHeight
+ , bool bSupportsLabelBorder)
+{
+ //fill character properties into the ValueMap
+ tPropertyNameValueMap aValueMap;
+ tPropertyNameMap const & aNameMap = bSupportsLabelBorder ? PropertyMapper::getPropertyNameMapForTextLabelProperties() : getPropertyNameMapForCharacterProperties();
+
+ PropertyMapper::getValueMap(aValueMap, aNameMap, xSourceProp);
+
+ //some more shape properties apart from character properties, position-matrix and label string
+ aValueMap.emplace( "TextHorizontalAdjust", uno::Any(drawing::TextHorizontalAdjust_CENTER) ); // drawing::TextHorizontalAdjust - needs to be overwritten
+ aValueMap.emplace( "TextVerticalAdjust", uno::Any(drawing::TextVerticalAdjust_CENTER) ); //drawing::TextVerticalAdjust - needs to be overwritten
+ aValueMap.emplace( "TextAutoGrowHeight", uno::Any(true) ); // sal_Bool
+ aValueMap.emplace( "TextAutoGrowWidth", uno::Any(true) ); // sal_Bool
+ aValueMap.emplace( "ParaAdjust", uno::Any(style::ParagraphAdjust_CENTER) ); // style::ParagraphAdjust_CENTER - needs to be overwritten
+ if( bName )
+ aValueMap.emplace( "Name", uno::Any( OUString() ) ); //CID OUString - needs to be overwritten for each point
+
+ if( nLimitedSpace > 0 )
+ {
+ if(bLimitedHeight)
+ aValueMap.emplace( "TextMaximumFrameHeight", uno::Any(nLimitedSpace) ); //sal_Int32
+ else
+ aValueMap.emplace( "TextMaximumFrameWidth", uno::Any(nLimitedSpace) ); //sal_Int32
+ aValueMap.emplace( "ParaIsHyphenation", uno::Any(true) );
+ }
+
+ PropertyMapper::getMultiPropertyListsFromValueMap( rPropNames, rPropValues, aValueMap );
+}
+
+void PropertyMapper::getPreparedTextShapePropertyLists(
+ const uno::Reference< beans::XPropertySet >& xSourceProp
+ , tNameSequence& rPropNames, tAnySequence& rPropValues )
+{
+ //fill character, line and fill properties into the ValueMap
+ tPropertyNameValueMap aValueMap;
+ PropertyMapper::getValueMap( aValueMap
+ , PropertyMapper::getPropertyNameMapForTextShapeProperties()
+ , xSourceProp );
+
+ // auto-grow makes sure the shape has the correct size after setting text
+ aValueMap.emplace( "TextHorizontalAdjust", uno::Any( drawing::TextHorizontalAdjust_CENTER ));
+ aValueMap.emplace( "TextVerticalAdjust", uno::Any( drawing::TextVerticalAdjust_CENTER ));
+ aValueMap.emplace( "TextAutoGrowHeight", uno::Any( true ));
+ aValueMap.emplace( "TextAutoGrowWidth", uno::Any( true ));
+
+ // set some distance to the border, in case it is shown
+ const sal_Int32 nWidthDist = 250;
+ const sal_Int32 nHeightDist = 125;
+ aValueMap.emplace( "TextLeftDistance", uno::Any( nWidthDist ));
+ aValueMap.emplace( "TextRightDistance", uno::Any( nWidthDist ));
+ aValueMap.emplace( "TextUpperDistance", uno::Any( nHeightDist ));
+ aValueMap.emplace( "TextLowerDistance", uno::Any( nHeightDist ));
+
+ // use a line-joint showing the border of thick lines like two rectangles
+ // filled in between.
+ aValueMap["LineJoint"] <<= drawing::LineJoint_ROUND;
+
+ PropertyMapper::getMultiPropertyListsFromValueMap( rPropNames, rPropValues, aValueMap );
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/SeriesPlotterContainer.cxx b/chart2/source/view/main/SeriesPlotterContainer.cxx
new file mode 100644
index 000000000..1eb6a64a4
--- /dev/null
+++ b/chart2/source/view/main/SeriesPlotterContainer.cxx
@@ -0,0 +1,739 @@
+/* -*- 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 <sal/config.h>
+
+#include <cstddef>
+
+#include "SeriesPlotterContainer.hxx"
+
+#include <ChartView.hxx>
+#include <Diagram.hxx>
+#include <ChartType.hxx>
+#include <DataSeries.hxx>
+#include <ChartModel.hxx>
+#include <ChartTypeHelper.hxx>
+#include <ObjectIdentifier.hxx>
+#include <DiagramHelper.hxx>
+#include <Axis.hxx>
+#include <AxisIndexDefines.hxx>
+#include <DataSeriesHelper.hxx>
+#include <ExplicitCategoriesProvider.hxx>
+#include <unonames.hxx>
+
+#include <com/sun/star/chart/ChartAxisPosition.hpp>
+#include <com/sun/star/chart2/AxisType.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+
+#include <comphelper/classids.hxx>
+#include <servicenames_charttypes.hxx>
+#include <tools/diagnose_ex.h>
+
+namespace chart
+{
+using namespace ::css;
+using namespace ::css::chart2;
+
+using ::css::uno::Reference;
+using ::css::uno::Sequence;
+using ::css::uno::Any;
+
+SeriesPlotterContainer::SeriesPlotterContainer(
+ std::vector<std::unique_ptr<VCoordinateSystem>>& rVCooSysList)
+ : m_rVCooSysList(rVCooSysList)
+ , m_nMaxAxisIndex(0)
+ , m_bChartTypeUsesShiftedCategoryPositionPerDefault(false)
+ , m_nDefaultDateNumberFormat(0)
+{
+}
+
+SeriesPlotterContainer::~SeriesPlotterContainer()
+{
+ // - remove plotter from coordinatesystems
+ for (auto& nC : m_rVCooSysList)
+ nC->clearMinimumAndMaximumSupplierList();
+}
+
+std::vector<LegendEntryProvider*> SeriesPlotterContainer::getLegendEntryProviderList()
+{
+ std::vector<LegendEntryProvider*> aRet(m_aSeriesPlotterList.size());
+ sal_Int32 nN = 0;
+ for (const std::unique_ptr<VSeriesPlotter>& aPlotter : m_aSeriesPlotterList)
+ aRet[nN++] = aPlotter.get();
+ return aRet;
+}
+
+VCoordinateSystem* SeriesPlotterContainer::findInCooSysList(
+ const std::vector<std::unique_ptr<VCoordinateSystem>>& rVCooSysList,
+ const rtl::Reference<BaseCoordinateSystem>& xCooSys)
+{
+ for (auto& pVCooSys : rVCooSysList)
+ {
+ if (pVCooSys->getModel() == xCooSys)
+ return pVCooSys.get();
+ }
+ return nullptr;
+}
+
+VCoordinateSystem* SeriesPlotterContainer::getCooSysForPlotter(
+ const std::vector<std::unique_ptr<VCoordinateSystem>>& rVCooSysList,
+ MinimumAndMaximumSupplier* pMinimumAndMaximumSupplier)
+{
+ if (!pMinimumAndMaximumSupplier)
+ return nullptr;
+ for (auto& pVCooSys : rVCooSysList)
+ {
+ if (pVCooSys->hasMinimumAndMaximumSupplier(pMinimumAndMaximumSupplier))
+ return pVCooSys.get();
+ }
+ return nullptr;
+}
+
+VCoordinateSystem* SeriesPlotterContainer::addCooSysToList(
+ std::vector<std::unique_ptr<VCoordinateSystem>>& rVCooSysList,
+ const rtl::Reference<BaseCoordinateSystem>& xCooSys, ChartModel& rChartModel)
+{
+ VCoordinateSystem* pExistingVCooSys
+ = SeriesPlotterContainer::findInCooSysList(rVCooSysList, xCooSys);
+ if (pExistingVCooSys)
+ return pExistingVCooSys;
+
+ std::unique_ptr<VCoordinateSystem> pVCooSys
+ = VCoordinateSystem::createCoordinateSystem(xCooSys);
+ if (!pVCooSys)
+ return nullptr;
+
+ OUString aCooSysParticle(
+ ObjectIdentifier::createParticleForCoordinateSystem(xCooSys, &rChartModel));
+ pVCooSys->setParticle(aCooSysParticle);
+
+ pVCooSys->setExplicitCategoriesProvider(new ExplicitCategoriesProvider(xCooSys, rChartModel));
+ rVCooSysList.push_back(std::move(pVCooSys));
+ return rVCooSysList.back().get();
+}
+
+void SeriesPlotterContainer::initializeCooSysAndSeriesPlotter(ChartModel& rChartModel)
+{
+ rtl::Reference<Diagram> xDiagram = rChartModel.getFirstChartDiagram();
+ if (!xDiagram.is())
+ return;
+
+ uno::Reference<util::XNumberFormatsSupplier> xNumberFormatsSupplier(&rChartModel);
+ if (rChartModel.hasInternalDataProvider() && DiagramHelper::isSupportingDateAxis(xDiagram))
+ m_nDefaultDateNumberFormat = DiagramHelper::getDateNumberFormat(xNumberFormatsSupplier);
+
+ sal_Int32 nDimensionCount = DiagramHelper::getDimension(xDiagram);
+ if (!nDimensionCount)
+ {
+ //@todo handle mixed dimension
+ nDimensionCount = 2;
+ }
+
+ bool bSortByXValues = false;
+ bool bConnectBars = false;
+ bool bGroupBarsPerAxis = true;
+ bool bIncludeHiddenCells = true;
+ bool bSecondaryYaxisVisible = true;
+ sal_Int32 nStartingAngle = 90;
+ sal_Int32 n3DRelativeHeight = 100;
+ try
+ {
+ xDiagram->getPropertyValue(CHART_UNONAME_SORT_BY_XVALUES) >>= bSortByXValues;
+ xDiagram->getPropertyValue("ConnectBars") >>= bConnectBars;
+ xDiagram->getPropertyValue("GroupBarsPerAxis") >>= bGroupBarsPerAxis;
+ xDiagram->getPropertyValue("IncludeHiddenCells") >>= bIncludeHiddenCells;
+ xDiagram->getPropertyValue("StartingAngle") >>= nStartingAngle;
+
+ if (nDimensionCount == 3)
+ {
+ xDiagram->getPropertyValue("3DRelativeHeight") >>= n3DRelativeHeight;
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ //prepare for autoscaling and shape creation
+ // - create plotter for charttypes (for each first scale group at each plotter, as they are independent)
+ // - add series to plotter (thus each charttype can provide minimum and maximum values for autoscaling)
+ // - add plotter to coordinate systems
+
+ //iterate through all coordinate systems
+ uno::Reference<XColorScheme> xColorScheme(xDiagram->getDefaultColorScheme());
+ auto& rCooSysList = xDiagram->getBaseCoordinateSystems();
+ sal_Int32 nGlobalSeriesIndex = 0; //for automatic symbols
+ for (std::size_t nCS = 0; nCS < rCooSysList.size(); ++nCS)
+ {
+ rtl::Reference<BaseCoordinateSystem> xCooSys(rCooSysList[nCS]);
+ VCoordinateSystem* pVCooSys
+ = SeriesPlotterContainer::addCooSysToList(m_rVCooSysList, xCooSys, rChartModel);
+ // Let's check whether the secondary Y axis is visible
+ try
+ {
+ if (xCooSys->getMaximumAxisIndexByDimension(1) > 0)
+ {
+ rtl::Reference<Axis> xAxisProp = xCooSys->getAxisByDimension2(1, 1);
+ xAxisProp->getPropertyValue("Show") >>= bSecondaryYaxisVisible;
+ }
+ }
+ catch (const lang::IndexOutOfBoundsException&)
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "");
+ }
+ //iterate through all chart types in the current coordinate system
+ std::vector<rtl::Reference<ChartType>> aChartTypeList(xCooSys->getChartTypes2());
+ for (std::size_t nT = 0; nT < aChartTypeList.size(); ++nT)
+ {
+ rtl::Reference<ChartType> xChartType(aChartTypeList[nT]);
+ if (nDimensionCount == 3
+ && xChartType->getChartType().equalsIgnoreAsciiCase(
+ CHART2_SERVICE_NAME_CHARTTYPE_PIE))
+ {
+ try
+ {
+ sal_Int32 n3DRelativeHeightOldValue(100);
+ uno::Any aAny = xChartType->getPropertyValue("3DRelativeHeight");
+ aAny >>= n3DRelativeHeightOldValue;
+ if (n3DRelativeHeightOldValue != n3DRelativeHeight)
+ xChartType->setPropertyValue("3DRelativeHeight",
+ uno::Any(n3DRelativeHeight));
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+
+ if (nT == 0)
+ m_bChartTypeUsesShiftedCategoryPositionPerDefault
+ = ChartTypeHelper::shiftCategoryPosAtXAxisPerDefault(xChartType);
+
+ bool bExcludingPositioning = DiagramHelper::getDiagramPositioningMode(xDiagram)
+ == DiagramPositioningMode_EXCLUDING;
+ VSeriesPlotter* pPlotter = VSeriesPlotter::createSeriesPlotter(
+ xChartType, nDimensionCount, bExcludingPositioning);
+ if (!pPlotter)
+ continue;
+
+ m_aSeriesPlotterList.push_back(std::unique_ptr<VSeriesPlotter>(pPlotter));
+ pPlotter->setNumberFormatsSupplier(xNumberFormatsSupplier);
+ pPlotter->setColorScheme(xColorScheme);
+ if (pVCooSys)
+ pPlotter->setExplicitCategoriesProvider(pVCooSys->getExplicitCategoriesProvider());
+ sal_Int32 nMissingValueTreatment
+ = DiagramHelper::getCorrectedMissingValueTreatment(xDiagram, xChartType);
+
+ if (pVCooSys)
+ pVCooSys->addMinimumAndMaximumSupplier(pPlotter);
+
+ sal_Int32 zSlot = -1;
+ sal_Int32 xSlot = -1;
+ sal_Int32 ySlot = -1;
+ const std::vector<rtl::Reference<DataSeries>>& aSeriesList
+ = xChartType->getDataSeries2();
+ for (std::size_t nS = 0; nS < aSeriesList.size(); ++nS)
+ {
+ rtl::Reference<DataSeries> const& xDataSeries = aSeriesList[nS];
+ if (!bIncludeHiddenCells && !DataSeriesHelper::hasUnhiddenData(xDataSeries))
+ continue;
+
+ std::unique_ptr<VDataSeries> pSeries(new VDataSeries(xDataSeries));
+
+ pSeries->setGlobalSeriesIndex(nGlobalSeriesIndex);
+ nGlobalSeriesIndex++;
+
+ if (bSortByXValues)
+ pSeries->doSortByXValues();
+
+ pSeries->setConnectBars(bConnectBars);
+ pSeries->setGroupBarsPerAxis(bGroupBarsPerAxis);
+ pSeries->setStartingAngle(nStartingAngle);
+
+ pSeries->setMissingValueTreatment(nMissingValueTreatment);
+
+ OUString aSeriesParticle(ObjectIdentifier::createParticleForSeries(0, nCS, nT, nS));
+ pSeries->setParticle(aSeriesParticle);
+
+ OUString aRole(ChartTypeHelper::getRoleOfSequenceForDataLabelNumberFormatDetection(
+ xChartType));
+ pSeries->setRoleOfSequenceForDataLabelNumberFormatDetection(aRole);
+
+ //ignore secondary axis for charttypes that do not support them
+ if (pSeries->getAttachedAxisIndex() != MAIN_AXIS_INDEX
+ && (!ChartTypeHelper::isSupportingSecondaryAxis(xChartType, nDimensionCount)
+ || !bSecondaryYaxisVisible))
+ {
+ pSeries->setAttachedAxisIndex(MAIN_AXIS_INDEX);
+ }
+
+ StackingDirection eDirection = pSeries->getStackingDirection();
+ switch (eDirection)
+ {
+ case StackingDirection_NO_STACKING:
+ xSlot++;
+ ySlot = -1;
+ if (zSlot < 0)
+ zSlot = 0;
+ break;
+ case StackingDirection_Y_STACKING:
+ ySlot++;
+ if (xSlot < 0)
+ xSlot = 0;
+ if (zSlot < 0)
+ zSlot = 0;
+ break;
+ case StackingDirection_Z_STACKING:
+ zSlot++;
+ xSlot = -1;
+ ySlot = -1;
+ break;
+ default:
+ // UNO enums have one additional auto-generated case
+ break;
+ }
+ pPlotter->addSeries(std::move(pSeries), zSlot, xSlot, ySlot);
+ }
+ }
+ }
+
+ //transport seriesnames to the coordinatesystems if needed
+ if (m_aSeriesPlotterList.empty())
+ return;
+
+ uno::Sequence<OUString> aSeriesNames;
+ bool bSeriesNamesInitialized = false;
+ for (auto& pVCooSys : m_rVCooSysList)
+ {
+ if (pVCooSys->needSeriesNamesForAxis())
+ {
+ if (!bSeriesNamesInitialized)
+ {
+ aSeriesNames = m_aSeriesPlotterList[0]->getSeriesNames();
+ bSeriesNamesInitialized = true;
+ }
+ pVCooSys->setSeriesNamesForAxis(aSeriesNames);
+ }
+ }
+}
+
+bool SeriesPlotterContainer::isCategoryPositionShifted(const chart2::ScaleData& rSourceScale,
+ bool bHasComplexCategories)
+{
+ if (rSourceScale.AxisType == AxisType::CATEGORY)
+ return bHasComplexCategories || rSourceScale.ShiftedCategoryPosition
+ || m_bChartTypeUsesShiftedCategoryPositionPerDefault;
+
+ if (rSourceScale.AxisType == AxisType::DATE)
+ return rSourceScale.ShiftedCategoryPosition;
+
+ return rSourceScale.AxisType == AxisType::SERIES;
+}
+
+void SeriesPlotterContainer::initAxisUsageList(const Date& rNullDate)
+{
+ m_aAxisUsageList.clear();
+
+ // Loop through coordinate systems in the diagram (though for now
+ // there should only be one coordinate system per diagram).
+ for (auto& pVCooSys : m_rVCooSysList)
+ {
+ rtl::Reference<BaseCoordinateSystem> xCooSys = pVCooSys->getModel();
+ sal_Int32 nDimCount = xCooSys->getDimension();
+ bool bComplexCategoryAllowed = ChartTypeHelper::isSupportingComplexCategory(
+ AxisHelper::getChartTypeByIndex(xCooSys, 0));
+
+ for (sal_Int32 nDimIndex = 0; nDimIndex < nDimCount; ++nDimIndex)
+ {
+ bool bDateAxisAllowed = ChartTypeHelper::isSupportingDateAxis(
+ AxisHelper::getChartTypeByIndex(xCooSys, 0), nDimIndex);
+
+ // Each dimension may have primary and secondary axes.
+ const sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nDimIndex);
+ for (sal_Int32 nAxisIndex = 0; nAxisIndex <= nMaxAxisIndex; ++nAxisIndex)
+ {
+ rtl::Reference<Axis> xAxis = xCooSys->getAxisByDimension2(nDimIndex, nAxisIndex);
+
+ if (!xAxis.is())
+ continue;
+
+ if (m_aAxisUsageList.find(xAxis) == m_aAxisUsageList.end())
+ {
+ // Create axis usage object for this axis.
+
+ chart2::ScaleData aSourceScale = xAxis->getScaleData();
+ ExplicitCategoriesProvider* pCatProvider
+ = pVCooSys->getExplicitCategoriesProvider();
+ if (nDimIndex == 0)
+ AxisHelper::checkDateAxis(aSourceScale, pCatProvider, bDateAxisAllowed);
+
+ bool bHasComplexCat = pCatProvider && pCatProvider->hasComplexCategories()
+ && bComplexCategoryAllowed;
+ aSourceScale.ShiftedCategoryPosition
+ = isCategoryPositionShifted(aSourceScale, bHasComplexCat);
+
+ m_aAxisUsageList[xAxis].aAutoScaling = ScaleAutomatism(aSourceScale, rNullDate);
+ }
+
+ AxisUsage& rAxisUsage = m_aAxisUsageList[xAxis];
+ rAxisUsage.addCoordinateSystem(pVCooSys.get(), nDimIndex, nAxisIndex);
+ }
+ }
+ }
+
+ // Determine the highest axis index of all dimensions.
+ m_nMaxAxisIndex = 0;
+ for (const auto& pVCooSys : m_rVCooSysList)
+ {
+ uno::Reference<XCoordinateSystem> xCooSys = pVCooSys->getModel();
+ sal_Int32 nDimCount = xCooSys->getDimension();
+
+ for (sal_Int32 nDimIndex = 0; nDimIndex < nDimCount; ++nDimIndex)
+ {
+ for (auto& axisUsage : m_aAxisUsageList)
+ {
+ sal_Int32 nLocalMax = axisUsage.second.getMaxAxisIndexForDimension(nDimIndex);
+ if (m_nMaxAxisIndex < nLocalMax)
+ m_nMaxAxisIndex = nLocalMax;
+ }
+ }
+ }
+}
+
+void SeriesPlotterContainer::setScalesFromCooSysToPlotter()
+{
+ //set scales to plotter to enable them to provide the preferred scene AspectRatio
+ for (const std::unique_ptr<VSeriesPlotter>& aPlotter : m_aSeriesPlotterList)
+ {
+ VSeriesPlotter* pSeriesPlotter = aPlotter.get();
+ VCoordinateSystem* pVCooSys
+ = SeriesPlotterContainer::getCooSysForPlotter(m_rVCooSysList, pSeriesPlotter);
+ if (pVCooSys)
+ {
+ pSeriesPlotter->setScales(pVCooSys->getExplicitScales(0, 0),
+ pVCooSys->getPropertySwapXAndYAxis());
+ sal_Int32 nMaxAxisIndex = pVCooSys->getMaximumAxisIndexByDimension(
+ 1); //only additional value axis are relevant for series plotter
+ for (sal_Int32 nI = 1; nI <= nMaxAxisIndex; nI++)
+ pSeriesPlotter->addSecondaryValueScale(pVCooSys->getExplicitScale(1, nI), nI);
+ }
+ }
+}
+
+void SeriesPlotterContainer::setNumberFormatsFromAxes()
+{
+ //set numberformats to plotter to enable them to display the data labels in the numberformat of the axis
+ for (const std::unique_ptr<VSeriesPlotter>& aPlotter : m_aSeriesPlotterList)
+ {
+ VSeriesPlotter* pSeriesPlotter = aPlotter.get();
+ VCoordinateSystem* pVCooSys
+ = SeriesPlotterContainer::getCooSysForPlotter(m_rVCooSysList, pSeriesPlotter);
+ if (pVCooSys)
+ {
+ AxesNumberFormats aAxesNumberFormats;
+ const rtl::Reference<BaseCoordinateSystem>& xCooSys = pVCooSys->getModel();
+ sal_Int32 nDimensionCount = xCooSys->getDimension();
+ for (sal_Int32 nDimensionIndex = 0; nDimensionIndex < nDimensionCount;
+ ++nDimensionIndex)
+ {
+ const sal_Int32 nMaximumAxisIndex
+ = xCooSys->getMaximumAxisIndexByDimension(nDimensionIndex);
+ for (sal_Int32 nAxisIndex = 0; nAxisIndex <= nMaximumAxisIndex; ++nAxisIndex)
+ {
+ try
+ {
+ rtl::Reference<Axis> xAxisProp
+ = xCooSys->getAxisByDimension2(nDimensionIndex, nAxisIndex);
+ if (xAxisProp.is())
+ {
+ sal_Int32 nNumberFormatKey(0);
+ if (xAxisProp->getPropertyValue(CHART_UNONAME_NUMFMT)
+ >>= nNumberFormatKey)
+ {
+ aAxesNumberFormats.setFormat(nNumberFormatKey, nDimensionIndex,
+ nAxisIndex);
+ }
+ else if (nDimensionIndex == 0)
+ {
+ //provide a default date format for date axis with own data
+ aAxesNumberFormats.setFormat(m_nDefaultDateNumberFormat,
+ nDimensionIndex, nAxisIndex);
+ }
+ }
+ }
+ catch (const lang::IndexOutOfBoundsException&)
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "");
+ }
+ }
+ }
+ }
+ }
+}
+
+void SeriesPlotterContainer::updateScalesAndIncrementsOnAxes()
+{
+ for (auto& nC : m_rVCooSysList)
+ nC->updateScalesAndIncrementsOnAxes();
+}
+
+void SeriesPlotterContainer::doAutoScaling(ChartModel& rChartModel)
+{
+ if (m_aSeriesPlotterList.empty() || m_aAxisUsageList.empty())
+ // We need these two containers populated to do auto-scaling. Bail out.
+ return;
+
+ //iterate over the main scales first than secondary axis
+ for (sal_Int32 nAxisIndex = 0; nAxisIndex <= m_nMaxAxisIndex; ++nAxisIndex)
+ {
+ // - first do autoscale for all x and z scales (because they are treated independent)
+ for (auto & [ rAxis, rAxisUsage ] : m_aAxisUsageList)
+ {
+ (void)rAxis;
+ rAxisUsage.prepareAutomaticAxisScaling(rAxisUsage.aAutoScaling, 0, nAxisIndex);
+ rAxisUsage.prepareAutomaticAxisScaling(rAxisUsage.aAutoScaling, 2, nAxisIndex);
+
+ ExplicitScaleData aExplicitScale;
+ ExplicitIncrementData aExplicitIncrement;
+ rAxisUsage.aAutoScaling.calculateExplicitScaleAndIncrement(aExplicitScale,
+ aExplicitIncrement);
+
+ rAxisUsage.setExplicitScaleAndIncrement(0, nAxisIndex, aExplicitScale,
+ aExplicitIncrement);
+ rAxisUsage.setExplicitScaleAndIncrement(2, nAxisIndex, aExplicitScale,
+ aExplicitIncrement);
+ }
+
+ // - second do autoscale for the dependent y scales (the coordinate systems are prepared with x and z scales already )
+ for (auto & [ rAxis, rAxisUsage ] : m_aAxisUsageList)
+ {
+ (void)rAxis;
+ rAxisUsage.prepareAutomaticAxisScaling(rAxisUsage.aAutoScaling, 1, nAxisIndex);
+
+ ExplicitScaleData aExplicitScale;
+ ExplicitIncrementData aExplicitIncrement;
+ rAxisUsage.aAutoScaling.calculateExplicitScaleAndIncrement(aExplicitScale,
+ aExplicitIncrement);
+
+ rAxisUsage.setExplicitScaleAndIncrement(0, nAxisIndex, aExplicitScale,
+ aExplicitIncrement);
+ rAxisUsage.setExplicitScaleAndIncrement(1, nAxisIndex, aExplicitScale,
+ aExplicitIncrement);
+ rAxisUsage.setExplicitScaleAndIncrement(2, nAxisIndex, aExplicitScale,
+ aExplicitIncrement);
+ }
+ }
+ AdaptScaleOfYAxisWithoutAttachedSeries(rChartModel);
+}
+
+void SeriesPlotterContainer::AdaptScaleOfYAxisWithoutAttachedSeries(ChartModel& rModel)
+{
+ //issue #i80518#
+ for (sal_Int32 nAxisIndex = 0; nAxisIndex <= m_nMaxAxisIndex; nAxisIndex++)
+ {
+ for (auto & [ rAxis, rAxisUsage ] : m_aAxisUsageList)
+ {
+ (void)rAxis;
+ std::vector<VCoordinateSystem*> aVCooSysList_Y
+ = rAxisUsage.getCoordinateSystems(1, nAxisIndex);
+ if (aVCooSysList_Y.empty())
+ continue;
+
+ rtl::Reference<Diagram> xDiagram(rModel.getFirstChartDiagram());
+ if (!xDiagram.is())
+ continue;
+
+ bool bSeriesAttachedToThisAxis = false;
+ sal_Int32 nAttachedAxisIndex = -1;
+ {
+ std::vector<rtl::Reference<DataSeries>> aSeriesVector
+ = DiagramHelper::getDataSeriesFromDiagram(xDiagram);
+ for (auto const& series : aSeriesVector)
+ {
+ sal_Int32 nCurrentIndex = DataSeriesHelper::getAttachedAxisIndex(series);
+ if (nAxisIndex == nCurrentIndex)
+ {
+ bSeriesAttachedToThisAxis = true;
+ break;
+ }
+ else if (nAttachedAxisIndex < 0 || nAttachedAxisIndex > nCurrentIndex)
+ nAttachedAxisIndex = nCurrentIndex;
+ }
+ }
+
+ if (bSeriesAttachedToThisAxis || nAttachedAxisIndex < 0)
+ continue;
+
+ for (VCoordinateSystem* nC : aVCooSysList_Y)
+ {
+ nC->prepareAutomaticAxisScaling(rAxisUsage.aAutoScaling, 1, nAttachedAxisIndex);
+
+ ExplicitScaleData aExplicitScaleSource
+ = nC->getExplicitScale(1, nAttachedAxisIndex);
+ ExplicitIncrementData aExplicitIncrementSource
+ = nC->getExplicitIncrement(1, nAttachedAxisIndex);
+
+ ExplicitScaleData aExplicitScaleDest = nC->getExplicitScale(1, nAxisIndex);
+ ExplicitIncrementData aExplicitIncrementDest
+ = nC->getExplicitIncrement(1, nAxisIndex);
+
+ aExplicitScaleDest.Orientation = aExplicitScaleSource.Orientation;
+ aExplicitScaleDest.Scaling = aExplicitScaleSource.Scaling;
+ aExplicitScaleDest.AxisType = aExplicitScaleSource.AxisType;
+
+ aExplicitIncrementDest.BaseValue = aExplicitIncrementSource.BaseValue;
+
+ ScaleData aScale(rAxisUsage.aAutoScaling.getScale());
+ if (!aScale.Minimum.hasValue())
+ {
+ bool bNewMinOK = true;
+ double fMax = 0.0;
+ if (aScale.Maximum >>= fMax)
+ bNewMinOK = (aExplicitScaleSource.Minimum <= fMax);
+ if (bNewMinOK)
+ aExplicitScaleDest.Minimum = aExplicitScaleSource.Minimum;
+ }
+ else
+ aExplicitIncrementDest.BaseValue = aExplicitScaleDest.Minimum;
+
+ if (!aScale.Maximum.hasValue())
+ {
+ bool bNewMaxOK = true;
+ double fMin = 0.0;
+ if (aScale.Minimum >>= fMin)
+ bNewMaxOK = (fMin <= aExplicitScaleSource.Maximum);
+ if (bNewMaxOK)
+ aExplicitScaleDest.Maximum = aExplicitScaleSource.Maximum;
+ }
+ if (!aScale.Origin.hasValue())
+ aExplicitScaleDest.Origin = aExplicitScaleSource.Origin;
+
+ if (!aScale.IncrementData.Distance.hasValue())
+ aExplicitIncrementDest.Distance = aExplicitIncrementSource.Distance;
+
+ bool bAutoMinorInterval = true;
+ if (aScale.IncrementData.SubIncrements.hasElements())
+ bAutoMinorInterval
+ = !(aScale.IncrementData.SubIncrements[0].IntervalCount.hasValue());
+ if (bAutoMinorInterval)
+ {
+ if (!aExplicitIncrementDest.SubIncrements.empty()
+ && !aExplicitIncrementSource.SubIncrements.empty())
+ aExplicitIncrementDest.SubIncrements[0].IntervalCount
+ = aExplicitIncrementSource.SubIncrements[0].IntervalCount;
+ }
+
+ nC->setExplicitScaleAndIncrement(1, nAxisIndex, aExplicitScaleDest,
+ aExplicitIncrementDest);
+ }
+ }
+ }
+
+ if (!AxisHelper::isAxisPositioningEnabled())
+ return;
+
+ //correct origin for y main axis (the origin is where the other main axis crosses)
+ sal_Int32 nAxisIndex = 0;
+ sal_Int32 nDimensionIndex = 1;
+ for (auto & [ rAxis, rAxisUsage ] : m_aAxisUsageList)
+ {
+ (void)rAxis;
+ std::vector<VCoordinateSystem*> aVCooSysList
+ = rAxisUsage.getCoordinateSystems(nDimensionIndex, nAxisIndex);
+ size_t nC;
+ for (nC = 0; nC < aVCooSysList.size(); nC++)
+ {
+ ExplicitScaleData aExplicitScale(
+ aVCooSysList[nC]->getExplicitScale(nDimensionIndex, nAxisIndex));
+ ExplicitIncrementData aExplicitIncrement(
+ aVCooSysList[nC]->getExplicitIncrement(nDimensionIndex, nAxisIndex));
+
+ rtl::Reference<BaseCoordinateSystem> xCooSys(aVCooSysList[nC]->getModel());
+ rtl::Reference<Axis> xAxis = xCooSys->getAxisByDimension2(nDimensionIndex, nAxisIndex);
+ rtl::Reference<Axis> xCrossingMainAxis
+ = AxisHelper::getCrossingMainAxis(xAxis, xCooSys);
+
+ if (xCrossingMainAxis.is())
+ {
+ css::chart::ChartAxisPosition eCrossingMainAxisPos(
+ css::chart::ChartAxisPosition_ZERO);
+ xCrossingMainAxis->getPropertyValue("CrossoverPosition") >>= eCrossingMainAxisPos;
+ if (eCrossingMainAxisPos == css::chart::ChartAxisPosition_VALUE)
+ {
+ double fValue = 0.0;
+ xCrossingMainAxis->getPropertyValue("CrossoverValue") >>= fValue;
+ aExplicitScale.Origin = fValue;
+ }
+ else if (eCrossingMainAxisPos == css::chart::ChartAxisPosition_ZERO)
+ aExplicitScale.Origin = 0.0;
+ else if (eCrossingMainAxisPos == css::chart::ChartAxisPosition_START)
+ aExplicitScale.Origin = aExplicitScale.Minimum;
+ else if (eCrossingMainAxisPos == css::chart::ChartAxisPosition_END)
+ aExplicitScale.Origin = aExplicitScale.Maximum;
+ }
+
+ aVCooSysList[nC]->setExplicitScaleAndIncrement(nDimensionIndex, nAxisIndex,
+ aExplicitScale, aExplicitIncrement);
+ }
+ }
+}
+
+drawing::Direction3D SeriesPlotterContainer::getPreferredAspectRatio()
+{
+ drawing::Direction3D aPreferredAspectRatio(1.0, 1.0, 1.0);
+
+ //get a list of all preferred aspect ratios and combine them
+ //first with special demands wins (less or equal zero <-> arbitrary)
+ double fx, fy, fz;
+ fx = fy = fz = -1.0;
+ for (const std::unique_ptr<VSeriesPlotter>& aPlotter : m_aSeriesPlotterList)
+ {
+ drawing::Direction3D aSingleRatio(aPlotter->getPreferredDiagramAspectRatio());
+ if (fx < 0 && aSingleRatio.DirectionX > 0)
+ fx = aSingleRatio.DirectionX;
+
+ if (fy < 0 && aSingleRatio.DirectionY > 0)
+ {
+ if (fx > 0 && aSingleRatio.DirectionX > 0)
+ fy = fx * aSingleRatio.DirectionY / aSingleRatio.DirectionX;
+ else if (fz > 0 && aSingleRatio.DirectionZ > 0)
+ fy = fz * aSingleRatio.DirectionY / aSingleRatio.DirectionZ;
+ else
+ fy = aSingleRatio.DirectionY;
+ }
+
+ if (fz < 0 && aSingleRatio.DirectionZ > 0)
+ {
+ if (fx > 0 && aSingleRatio.DirectionX > 0)
+ fz = fx * aSingleRatio.DirectionZ / aSingleRatio.DirectionX;
+ else if (fy > 0 && aSingleRatio.DirectionY > 0)
+ fz = fy * aSingleRatio.DirectionZ / aSingleRatio.DirectionY;
+ else
+ fz = aSingleRatio.DirectionZ;
+ }
+
+ if (fx > 0 && fy > 0 && fz > 0)
+ break;
+ }
+ aPreferredAspectRatio = drawing::Direction3D(fx, fy, fz);
+ return aPreferredAspectRatio;
+}
+
+} //end chart2 namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/SeriesPlotterContainer.hxx b/chart2/source/view/main/SeriesPlotterContainer.hxx
new file mode 100644
index 000000000..e34d07a96
--- /dev/null
+++ b/chart2/source/view/main/SeriesPlotterContainer.hxx
@@ -0,0 +1,159 @@
+/* -*- 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 <config_feature_desktop.h>
+#include <VSeriesPlotter.hxx>
+#include <BaseCoordinateSystem.hxx>
+#include "AxisUsage.hxx"
+
+namespace chart
+{
+/** This class is a container of `SeriesPlotter` objects (such as `PieChart`
+ * instances). It is used for initializing coordinate systems, axes and scales
+ * of all series plotters which belongs to the container.
+ */
+class SeriesPlotterContainer
+{
+public:
+ explicit SeriesPlotterContainer(std::vector<std::unique_ptr<VCoordinateSystem>>& rVCooSysList);
+ ~SeriesPlotterContainer();
+
+ /** It is used to set coordinate systems (`m_rVCooSysList`), this method
+ * is invoked by `ChartView::createShapes2D` before of
+ * `ChartView::impl_createDiagramAndContent`.
+ * Coordinate systems are retrieved through the `XCoordinateSystemContainer`
+ * interface implemented by a diagram object which is provided by the
+ * `ChartModel` object passed to the method (`rChartModel.getFirstDiagram()`).
+ *
+ * It is used for creating series plotters and appending them
+ * to `m_aSeriesPlotterList`. The created series plotters are initialized
+ * through data (number formats supplier, color scheme, data series),
+ * extracted from the chart model or the diagram objects. An exception is
+ * the explicit category provider that is retrieved through the
+ * `VCoordinateSystem` object used by the series plotter.
+ *
+ * It sets the minimum-maximum supplier for a coordinate system:
+ * this supplier is the series plotter itself which utilizes the given
+ * coordinate system. In fact `VSeriesPlotter` has `MinimumMaximumSupplier`
+ * as one of its base classes.
+ * Hence, for instance, a `PieChart`, which is a series plotter, is
+ * a `MinimumMaximumSupplier`, too.
+ */
+ void initializeCooSysAndSeriesPlotter(ChartModel& rModel);
+
+ /** This method is invoked by `ChartView::impl_createDiagramAndContent`.
+ * It iterates on every axis of every coordinate systems, and if the axis
+ * is not yet present in `m_aAxisUsageList` it creates a new `AxisUsage`
+ * object and initialize its `aAutoScaling` member to the `ScaleData`
+ * object of the current axis.
+ */
+ void initAxisUsageList(const Date& rNullDate);
+
+ /**
+ * Perform automatic axis scaling and determine the amount and spacing of
+ * increments. It assumes that the caller has determined the size of the
+ * largest axis label text object prior to calling this method.
+ *
+ * The new axis scaling data will be stored in the VCoordinateSystem
+ * objects. The label alignment direction for each axis will also get
+ * determined during this process, and stored in VAxis.
+ *
+ * This method is invoked by `ChartView::impl_createDiagramAndContent`
+ * soon after `initAxisUsageList`.
+ * It initializes explicit scale and increment objects for all coordinate
+ * systems in `m_rVCooSysList`.
+ * This action is achieved by iterating on the `m_aAxisUsageList` container,
+ * and performing 3 steps:
+ * 1- call `VCoordinateSystem::prepareAutomaticAxisScaling` for setting
+ * scaling parameters of the `aAutoScaling` member (a `ScaleAutomatism`
+ * object) for the current `AxisUsage` instance
+ * (see `VCoordinateSystem::prepareAutomaticAxisScaling`);
+ * 2- calculate the explicit scale and increment objects
+ * (see ScaleAutomatism::calculateExplicitScaleAndIncrement);
+ * 3- set the explicit scale and increment objects for each coordinate
+ * system.
+ */
+ void doAutoScaling(ChartModel& rModel);
+
+ /**
+ * After auto-scaling is performed, call this method to set the explicit
+ * scaling and increment data to all relevant VAxis objects.
+ */
+ void updateScalesAndIncrementsOnAxes();
+
+ /**
+ * After auto-scaling is performed, call this method to set the explicit
+ * scaling data to all the plotters.
+ */
+ void setScalesFromCooSysToPlotter();
+
+ void setNumberFormatsFromAxes();
+ css::drawing::Direction3D getPreferredAspectRatio();
+
+ std::vector<std::unique_ptr<VSeriesPlotter>>& getSeriesPlotterList()
+ {
+ return m_aSeriesPlotterList;
+ }
+ std::vector<std::unique_ptr<VCoordinateSystem>>& getCooSysList() { return m_rVCooSysList; }
+ std::vector<LegendEntryProvider*> getLegendEntryProviderList();
+
+ void AdaptScaleOfYAxisWithoutAttachedSeries(ChartModel& rModel);
+
+ bool isCategoryPositionShifted(const css::chart2::ScaleData& rSourceScale,
+ bool bHasComplexCategories);
+
+ static VCoordinateSystem*
+ getCooSysForPlotter(const std::vector<std::unique_ptr<VCoordinateSystem>>& rVCooSysList,
+ MinimumAndMaximumSupplier* pMinimumAndMaximumSupplier);
+ static VCoordinateSystem*
+ addCooSysToList(std::vector<std::unique_ptr<VCoordinateSystem>>& rVCooSysList,
+ const rtl::Reference<BaseCoordinateSystem>& xCooSys, ChartModel& rChartModel);
+ static VCoordinateSystem*
+ findInCooSysList(const std::vector<std::unique_ptr<VCoordinateSystem>>& rVCooSysList,
+ const rtl::Reference<BaseCoordinateSystem>& xCooSys);
+
+private:
+ /** A vector of series plotters.
+ */
+ std::vector<std::unique_ptr<VSeriesPlotter>> m_aSeriesPlotterList;
+
+ /** A vector of coordinate systems.
+ */
+ std::vector<std::unique_ptr<VCoordinateSystem>>& m_rVCooSysList;
+
+ /** A map whose key is a `XAxis` interface and the related value is
+ * an object of `AxisUsage` type.
+ */
+ std::map<rtl::Reference<Axis>, AxisUsage> m_aAxisUsageList;
+
+ /**
+ * Max axis index of all dimensions. Currently this can be either 0 or 1
+ * since we only support primary and secondary axes per dimension. The
+ * value of 0 means all dimensions have only primary axis, while 1 means
+ * at least one dimension has a secondary axis.
+ */
+ sal_Int32 m_nMaxAxisIndex;
+
+ bool m_bChartTypeUsesShiftedCategoryPositionPerDefault;
+ sal_Int32 m_nDefaultDateNumberFormat;
+};
+
+} //end chart2 namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/ShapeFactory.cxx b/chart2/source/view/main/ShapeFactory.cxx
new file mode 100644
index 000000000..7fddc5d01
--- /dev/null
+++ b/chart2/source/view/main/ShapeFactory.cxx
@@ -0,0 +1,2551 @@
+/* -*- 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 <ShapeFactory.hxx>
+#include <BaseGFXHelper.hxx>
+#include <ViewDefines.hxx>
+#include <Stripe.hxx>
+#include <CommonConverters.hxx>
+#include <RelativeSizeHelper.hxx>
+#include <PropertyMapper.hxx>
+#include <VLineProperties.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/chart2/XFormattedString.hpp>
+#include <com/sun/star/drawing/CircleKind.hpp>
+#include <com/sun/star/drawing/DoubleSequence.hpp>
+#include <com/sun/star/drawing/FlagSequence.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/drawing/NormalsKind.hpp>
+#include <com/sun/star/drawing/PointSequence.hpp>
+#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
+#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
+#include <com/sun/star/drawing/TextureProjectionMode.hpp>
+#include <com/sun/star/drawing/TextVerticalAdjust.hpp>
+#include <com/sun/star/drawing/XShapes2.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/style/ParagraphAdjust.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Any.hxx>
+
+#include <editeng/unoprnms.hxx>
+#include <rtl/math.hxx>
+
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdopath.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/helpers.hxx>
+#include <tools/UnitConversion.hxx>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <cstddef>
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Reference;
+
+namespace chart
+{
+
+namespace
+{
+
+void lcl_addProperty(uno::Sequence<OUString> & rPropertyNames, uno::Sequence<uno::Any> & rPropertyValues,
+ OUString const & rName, uno::Any const & rAny)
+{
+ rPropertyNames.realloc(rPropertyNames.getLength() + 1);
+ rPropertyNames.getArray()[rPropertyNames.getLength() - 1] = rName;
+
+ rPropertyValues.realloc(rPropertyValues.getLength() + 1);
+ rPropertyValues.getArray()[rPropertyValues.getLength() - 1] = rAny;
+}
+
+css::drawing::PolyPolygonShape3D toPolyPolygonShape3D(const std::vector<std::vector<css::drawing::Position3D>>& rPoints)
+{
+ css::drawing::PolyPolygonShape3D aUnoPoly;
+ aUnoPoly.SequenceX.realloc(rPoints.size());
+ aUnoPoly.SequenceY.realloc(rPoints.size());
+ aUnoPoly.SequenceZ.realloc(rPoints.size());
+ for (std::size_t nPolygonIndex=0; nPolygonIndex<rPoints.size(); ++nPolygonIndex)
+ {
+ drawing::DoubleSequence* pOuterSequenceX = &aUnoPoly.SequenceX.getArray()[nPolygonIndex];
+ drawing::DoubleSequence* pOuterSequenceY = &aUnoPoly.SequenceY.getArray()[nPolygonIndex];
+ drawing::DoubleSequence* pOuterSequenceZ = &aUnoPoly.SequenceZ.getArray()[nPolygonIndex];
+ pOuterSequenceX->realloc(rPoints[nPolygonIndex].size());
+ pOuterSequenceY->realloc(rPoints[nPolygonIndex].size());
+ pOuterSequenceZ->realloc(rPoints[nPolygonIndex].size());
+ double* pInnerSequenceX = pOuterSequenceX->getArray();
+ double* pInnerSequenceY = pOuterSequenceY->getArray();
+ double* pInnerSequenceZ = pOuterSequenceZ->getArray();
+ for (std::size_t nPointIndex=0; nPointIndex<rPoints[nPolygonIndex].size(); ++nPointIndex)
+ {
+ auto& rPos = rPoints[nPolygonIndex][nPointIndex];
+ pInnerSequenceX[nPointIndex] = rPos.PositionX;
+ pInnerSequenceY[nPointIndex] = rPos.PositionY;
+ pInnerSequenceZ[nPointIndex] = rPos.PositionZ;
+ }
+ }
+ return aUnoPoly;
+}
+
+} // end anonymous namespace
+
+rtl::Reference<SvxShapeGroupAnyD> ShapeFactory::getOrCreateChartRootShape(
+ const rtl::Reference<SvxDrawPage>& xDrawPage )
+{
+ rtl::Reference<SvxShapeGroupAnyD> xRet = ShapeFactory::getChartRootShape(xDrawPage);
+ if (xRet.is())
+ return xRet;
+
+ // Create a new root shape and set it to the bottom of the page. The root
+ // shape is identified by having the name com.sun.star.chart2.shapes.
+ rtl::Reference<SvxShapeGroup> xShapeGroup = new SvxShapeGroup(nullptr, nullptr);
+ xShapeGroup->setShapeKind(SdrObjKind::Group);
+ // cast to resolve ambiguity in converting to XShape
+ xDrawPage->addBottom(static_cast<SvxShape*>(xShapeGroup.get()));
+
+ setShapeName(xShapeGroup, "com.sun.star.chart2.shapes");
+ xShapeGroup->setSize(awt::Size(0,0));
+
+ return xShapeGroup;
+}
+
+void ShapeFactory::setPageSize(const rtl::Reference<SvxShapeGroupAnyD>&, const awt::Size&) {}
+
+// diverse tools::PolyPolygon create methods
+
+static uno::Any createPolyPolygon_Cube(
+ const drawing::Direction3D& rSize, double fRoundedEdge, bool bRounded )
+{
+ OSL_PRECOND(fRoundedEdge>=0, "fRoundedEdge needs to be >= 0");
+
+ // always use extra points, so set percent diagonal to 0.4 which is 0% in the UI (old Chart comment)
+ if( fRoundedEdge == 0.0 && bRounded)
+ fRoundedEdge = 0.4 / 200.0;
+ else if(!bRounded)
+ fRoundedEdge = 0.0;
+
+ //fWidthH stands for Half Width
+ const double fWidthH = rSize.DirectionX >=0.0? rSize.DirectionX/2.0 : -rSize.DirectionX/2.0;
+ const double fHeight = rSize.DirectionY;
+
+ const double fHeightSign = fHeight >= 0.0 ? 1.0 : -1.0;
+
+ const double fOffset = (fWidthH * fRoundedEdge) * 1.05; // increase by 5% for safety
+ const bool bRoundEdges = fRoundedEdge && fOffset < fWidthH && 2.0 * fOffset < fHeightSign*fHeight;
+ const sal_Int32 nPointCount = bRoundEdges ? 13 : 5;
+
+ drawing::PolyPolygonShape3D aPP;
+
+ aPP.SequenceX.realloc(1);
+ aPP.SequenceY.realloc(1);
+ aPP.SequenceZ.realloc(1);
+
+ drawing::DoubleSequence* pOuterSequenceX = aPP.SequenceX.getArray();
+ drawing::DoubleSequence* pOuterSequenceY = aPP.SequenceY.getArray();
+ drawing::DoubleSequence* pOuterSequenceZ = aPP.SequenceZ.getArray();
+
+ pOuterSequenceX->realloc(nPointCount);
+ pOuterSequenceY->realloc(nPointCount);
+ pOuterSequenceZ->realloc(nPointCount);
+
+ double* pInnerSequenceX = pOuterSequenceX->getArray();
+ double* pInnerSequenceY = pOuterSequenceY->getArray();
+ double* pInnerSequenceZ = pOuterSequenceZ->getArray();
+
+ for(sal_Int32 nN = nPointCount; nN--;)
+ *pInnerSequenceZ++ = 0.0;
+
+ if(nPointCount == 5)
+ {
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceY++ = fHeight;
+ *pInnerSequenceY++ = fHeight;
+ *pInnerSequenceY++ = 0.0;
+
+ *pInnerSequenceX++ = -fWidthH;
+ *pInnerSequenceX++ = fWidthH;
+ *pInnerSequenceX++ = fWidthH;
+ *pInnerSequenceX++ = -fWidthH;
+ *pInnerSequenceX++ = -fWidthH;
+ }
+ else
+ {
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceY++ = fHeightSign*fOffset;
+ *pInnerSequenceY++ = fHeight - fHeightSign*fOffset;
+ *pInnerSequenceY++ = fHeight;
+ *pInnerSequenceY++ = fHeight;
+ *pInnerSequenceY++ = fHeight;
+ *pInnerSequenceY++ = fHeight;
+ *pInnerSequenceY++ = fHeight - fHeightSign*fOffset;
+ *pInnerSequenceY++ = fHeightSign*fOffset;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceY++ = 0.0;
+
+ *pInnerSequenceX++ = -fWidthH + fOffset;
+ *pInnerSequenceX++ = fWidthH - fOffset;
+ *pInnerSequenceX++ = fWidthH;
+ *pInnerSequenceX++ = fWidthH;
+ *pInnerSequenceX++ = fWidthH;
+ *pInnerSequenceX++ = fWidthH;
+ *pInnerSequenceX++ = fWidthH - fOffset;
+ *pInnerSequenceX++ = -fWidthH + fOffset;
+ *pInnerSequenceX++ = -fWidthH;
+ *pInnerSequenceX++ = -fWidthH;
+ *pInnerSequenceX++ = -fWidthH;
+ *pInnerSequenceX++ = -fWidthH;
+ *pInnerSequenceX++ = -fWidthH + fOffset;
+ }
+ return uno::Any( &aPP, cppu::UnoType<drawing::PolyPolygonShape3D>::get());
+}
+
+static uno::Any createPolyPolygon_Cylinder(
+ double fHeight
+ , double fRadius
+ , sal_Int32& nVerticalSegmentCount )
+{
+ //fHeight may be negative
+ OSL_PRECOND(fRadius>0, "The radius of a cylinder needs to be > 0");
+
+ drawing::PolyPolygonShape3D aPP;
+
+ nVerticalSegmentCount=1;
+
+ aPP.SequenceX.realloc(3);
+ aPP.SequenceY.realloc(3);
+ aPP.SequenceZ.realloc(3);
+
+ drawing::DoubleSequence* pOuterSequenceX = aPP.SequenceX.getArray();
+ drawing::DoubleSequence* pOuterSequenceY = aPP.SequenceY.getArray();
+ drawing::DoubleSequence* pOuterSequenceZ = aPP.SequenceZ.getArray();
+
+ pOuterSequenceX->realloc(2);
+ pOuterSequenceY->realloc(2);
+ pOuterSequenceZ->realloc(2);
+
+ double* pInnerSequenceX = pOuterSequenceX->getArray();
+ double* pInnerSequenceY = pOuterSequenceY->getArray();
+ double* pInnerSequenceZ = pOuterSequenceZ->getArray();
+
+ double fY1 = 0.0;
+ double fY2 = fHeight;
+
+ if( fHeight<0.0 )
+ std::swap(fY1,fY2);
+
+ for(sal_Int32 nN = 2; nN--;)
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 0.0;
+ *pInnerSequenceY++ = fY1;
+
+ *pInnerSequenceX++ = fRadius;
+ *pInnerSequenceY++ = fY1;
+
+ pOuterSequenceX++;pOuterSequenceY++;pOuterSequenceZ++;
+ pOuterSequenceX->realloc(2);
+ pOuterSequenceY->realloc(2);
+ pOuterSequenceZ->realloc(2);
+
+ pInnerSequenceX = pOuterSequenceX->getArray();
+ pInnerSequenceY = pOuterSequenceY->getArray();
+ pInnerSequenceZ = pOuterSequenceZ->getArray();
+
+ for(sal_Int32 nN = 2; nN--;)
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = fRadius;
+ *pInnerSequenceY++ = fY1;
+
+ *pInnerSequenceX++ = fRadius;
+ *pInnerSequenceY++ = fY2;
+
+ pOuterSequenceX++;pOuterSequenceY++;pOuterSequenceZ++;
+ pOuterSequenceX->realloc(2);
+ pOuterSequenceY->realloc(2);
+ pOuterSequenceZ->realloc(2);
+
+ pInnerSequenceX = pOuterSequenceX->getArray();
+ pInnerSequenceY = pOuterSequenceY->getArray();
+ pInnerSequenceZ = pOuterSequenceZ->getArray();
+
+ for(sal_Int32 nN = 2; nN--;)
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = fRadius;
+ *pInnerSequenceY++ = fY2;
+
+ *pInnerSequenceX++ = 0.0;
+ *pInnerSequenceY++ = fY2;
+
+ return uno::Any( &aPP, cppu::UnoType<drawing::PolyPolygonShape3D>::get());
+}
+
+static uno::Any createPolyPolygon_Cone( double fHeight, double fRadius, double fTopHeight
+ , sal_Int32& nVerticalSegmentCount )
+{
+ OSL_PRECOND(fRadius>0, "The radius of a cone needs to be > 0");
+
+ //for stacked charts we need cones without top -> fTopHeight != 0 resp. bTopless == true
+ //fTopHeight indicates the high of the cutted top only (not the full height)
+ bool bTopless = !::rtl::math::approxEqual( fHeight, fHeight + fTopHeight );
+
+ double r1= 0.0, r2 = fRadius;
+ if(bTopless)
+ // #i63212# fHeight may be negative, fTopHeight is always positive -> use fabs(fHeight)
+ r1 = fRadius * fTopHeight/(fabs(fHeight)+fTopHeight);
+
+ nVerticalSegmentCount=1;
+ drawing::PolyPolygonShape3D aPP;
+
+ aPP.SequenceX.realloc(2);
+ aPP.SequenceY.realloc(2);
+ aPP.SequenceZ.realloc(2);
+
+ drawing::DoubleSequence* pOuterSequenceX = aPP.SequenceX.getArray();
+ drawing::DoubleSequence* pOuterSequenceY = aPP.SequenceY.getArray();
+ drawing::DoubleSequence* pOuterSequenceZ = aPP.SequenceZ.getArray();
+
+ pOuterSequenceX->realloc(2);
+ pOuterSequenceY->realloc(2);
+ pOuterSequenceZ->realloc(2);
+
+ double* pInnerSequenceX = pOuterSequenceX->getArray();
+ double* pInnerSequenceY = pOuterSequenceY->getArray();
+ double* pInnerSequenceZ = pOuterSequenceZ->getArray();
+
+ double fX1 = 0.0;
+ double fX2 = r2;
+ double fX3 = r1;
+
+ double fY1 = 0.0;
+ double fY2 = 0.0;
+ double fY3 = fHeight;
+
+ if( fHeight<0.0 )
+ {
+ std::swap(fX1,fX3);
+ std::swap(fY1,fY3);
+ }
+
+ for(sal_Int32 nN = 2; nN--;)
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceY++ = fY1;
+ *pInnerSequenceX++ = fX1;
+
+ *pInnerSequenceY++ = fY2;
+ *pInnerSequenceX++ = fX2;
+
+ pOuterSequenceX++;pOuterSequenceY++;pOuterSequenceZ++;
+ pOuterSequenceX->realloc(2);
+ pOuterSequenceY->realloc(2);
+ pOuterSequenceZ->realloc(2);
+
+ pInnerSequenceX = pOuterSequenceX->getArray();
+ pInnerSequenceY = pOuterSequenceY->getArray();
+ pInnerSequenceZ = pOuterSequenceZ->getArray();
+
+ for(sal_Int32 nN = 2; nN--;)
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceY++ = fY2;
+ *pInnerSequenceX++ = fX2;
+
+ *pInnerSequenceY++ = fY3;
+ *pInnerSequenceX++ = fX3;
+
+ return uno::Any( &aPP, cppu::UnoType<drawing::PolyPolygonShape3D>::get());
+}
+
+// methods for 3D shape creation
+
+rtl::Reference<Svx3DExtrudeObject>
+ ShapeFactory::createCube(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const drawing::Position3D& rPosition, const drawing::Direction3D& rSize
+ , sal_Int32 nRotateZAngleHundredthDegree
+ , const uno::Reference< beans::XPropertySet >& xSourceProp
+ , const tPropertyNameMap& rPropertyNameMap
+ , bool bRounded )
+{
+ if( !xTarget.is() )
+ return nullptr;
+ if( bRounded )
+ {
+ try
+ {
+ if( xSourceProp.is() )
+ {
+ drawing::LineStyle aLineStyle;
+ xSourceProp->getPropertyValue( "BorderStyle" ) >>= aLineStyle;
+ if( aLineStyle == drawing::LineStyle_SOLID )
+ bRounded = false;
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ }
+ rtl::Reference<Svx3DExtrudeObject> xShape = impl_createCube( xTarget, rPosition, rSize, nRotateZAngleHundredthDegree, bRounded );
+ if( xSourceProp.is())
+ PropertyMapper::setMappedProperties( *xShape, xSourceProp, rPropertyNameMap );
+ return xShape;
+}
+
+rtl::Reference<Svx3DExtrudeObject>
+ ShapeFactory::impl_createCube(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const drawing::Position3D& rPosition, const drawing::Direction3D& rSize
+ , sal_Int32 nRotateZAngleHundredthDegree
+ , bool bRounded )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ //create shape
+ rtl::Reference<Svx3DExtrudeObject> xShape = new Svx3DExtrudeObject(nullptr);
+ xShape->setShapeKind(SdrObjKind::E3D_Extrusion);
+ xTarget->addShape(*xShape);
+
+ //set properties
+ try
+ {
+ //depth
+ double fDepth = rSize.DirectionZ;
+ if (fDepth<0)
+ fDepth*=-1.0;
+
+ //PercentDiagonal
+ sal_Int16 nPercentDiagonal = bRounded ? 3 : 0;
+
+ //Matrix for position
+ basegfx::B3DHomMatrix aHomMatrix;
+ if (nRotateZAngleHundredthDegree != 0)
+ aHomMatrix.rotate(0.0, 0.0, -basegfx::deg2rad<100>(nRotateZAngleHundredthDegree));
+ aHomMatrix.translate(rPosition.PositionX, rPosition.PositionY,
+ rPosition.PositionZ - (fDepth / 2.0));
+
+ uno::Sequence<OUString> aPropertyNames {
+ UNO_NAME_3D_EXTRUDE_DEPTH,
+ UNO_NAME_3D_PERCENT_DIAGONAL,
+ UNO_NAME_3D_POLYPOLYGON3D,
+ UNO_NAME_3D_TRANSFORM_MATRIX,
+ };
+
+ uno::Sequence<uno::Any> aPropertyValues {
+ uno::Any(sal_Int32(fDepth)), // Depth
+ uno::Any(nPercentDiagonal), // PercentDiagonal
+ createPolyPolygon_Cube(rSize, double(nPercentDiagonal) / 200.0, bRounded),
+ uno::Any(B3DHomMatrixToHomogenMatrix(aHomMatrix))
+ };
+
+ xShape->setPropertyValues(aPropertyNames, aPropertyValues);
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<Svx3DLatheObject>
+ ShapeFactory::createCylinder(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const drawing::Position3D& rPosition, const drawing::Direction3D& rSize
+ , sal_Int32 nRotateZAngleHundredthDegree )
+{
+ return impl_createConeOrCylinder(
+ xTarget, rPosition, rSize, 0.0, nRotateZAngleHundredthDegree, true );
+}
+
+rtl::Reference<Svx3DSceneObject>
+ ShapeFactory::createPyramid(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const drawing::Position3D& rPosition, const drawing::Direction3D& rSize
+ , double fTopHeight, bool bRotateZ
+ , const uno::Reference< beans::XPropertySet >& xSourceProp
+ , const tPropertyNameMap& rPropertyNameMap )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ rtl::Reference<Svx3DSceneObject> xGroup( ShapeFactory::createGroup3D( xTarget ) );
+
+ bool bDoubleSided = false;
+ short nRotatedTexture = 0;
+
+ const double fWidth = rSize.DirectionX;
+ const double fDepth = rSize.DirectionZ;
+ const double fHeight = rSize.DirectionY;
+
+ drawing::Position3D aBottomP1( rPosition.PositionX, rPosition.PositionY, rPosition.PositionZ - fDepth/2.0 );
+ if(bRotateZ)
+ aBottomP1.PositionY -= fWidth/2.0;
+ else
+ aBottomP1.PositionX -= fWidth/2.0;
+ drawing::Position3D aBottomP2( aBottomP1 );
+ if(bRotateZ)
+ aBottomP2.PositionY += fWidth;
+ else
+ aBottomP2.PositionX += fWidth;
+ drawing::Position3D aBottomP3( aBottomP2 );
+ drawing::Position3D aBottomP4( aBottomP1 );
+ aBottomP3.PositionZ += fDepth;
+ aBottomP4.PositionZ += fDepth;
+
+ const double fTopFactor = fTopHeight/(fabs(fHeight)+fTopHeight);
+ drawing::Position3D aTopP1( rPosition.PositionX, rPosition.PositionY, rPosition.PositionZ - fDepth*fTopFactor/2.0 );
+ if(bRotateZ)
+ {
+ aTopP1.PositionY -= fWidth*fTopFactor/2.0;
+ aTopP1.PositionX += fHeight;
+ }
+ else
+ {
+ aTopP1.PositionX -= fWidth*fTopFactor/2.0;
+ aTopP1.PositionY += fHeight;
+ }
+ drawing::Position3D aTopP2( aTopP1 );
+ if(bRotateZ)
+ aTopP2.PositionY += fWidth*fTopFactor;
+ else
+ aTopP2.PositionX += fWidth*fTopFactor;
+ drawing::Position3D aTopP3( aTopP2 );
+ drawing::Position3D aTopP4( aTopP1 );
+ aTopP3.PositionZ += fDepth*fTopFactor;
+ aTopP4.PositionZ += fDepth*fTopFactor;
+
+ Stripe aStripeBottom( aBottomP1, aBottomP4, aBottomP3, aBottomP2 );
+
+ drawing::Position3D aNormalsBottomP1( aBottomP1 );
+ drawing::Position3D aNormalsBottomP2( aBottomP2 );
+ drawing::Position3D aNormalsBottomP3( aBottomP3 );
+ drawing::Position3D aNormalsBottomP4( aBottomP4 );
+ drawing::Position3D aNormalsTopP1( aBottomP1 );
+ drawing::Position3D aNormalsTopP2( aBottomP2 );
+ drawing::Position3D aNormalsTopP3( aBottomP3 );
+ drawing::Position3D aNormalsTopP4( aBottomP4 );
+ if( bRotateZ )
+ {
+ aNormalsTopP1.PositionX += fHeight;
+ aNormalsTopP2.PositionX += fHeight;
+ aNormalsTopP3.PositionX += fHeight;
+ aNormalsTopP4.PositionX += fHeight;
+ }
+ else
+ {
+ aNormalsTopP1.PositionY += fHeight;
+ aNormalsTopP2.PositionY += fHeight;
+ aNormalsTopP3.PositionY += fHeight;
+ aNormalsTopP4.PositionY += fHeight;
+ }
+
+ bool bInvertPolygon = false;
+ bool bInvertNormals = false;
+
+ if(bRotateZ)
+ {
+ //bars
+ if(fHeight>=0.0)
+ {
+ nRotatedTexture = 2;
+ bInvertNormals = true;
+ aStripeBottom = Stripe( aBottomP1, aBottomP4, aBottomP3, aBottomP2 );
+ }
+ else
+ {
+ bInvertPolygon = true;
+ nRotatedTexture = 1;
+ aStripeBottom = Stripe( aBottomP2, aBottomP3, aBottomP4, aBottomP1 );
+ }
+ }
+ else
+ {
+ //columns
+ if(fHeight>=0.0)
+ {
+ bInvertPolygon = true;
+ nRotatedTexture = 2;
+ aStripeBottom = Stripe( aBottomP2, aBottomP3, aBottomP4, aBottomP1 );
+ }
+ else
+ {
+ nRotatedTexture = 3;
+ bInvertNormals = true;
+ aStripeBottom = Stripe( aBottomP4, aBottomP3, aBottomP2, aBottomP1 );
+ }
+ }
+ aStripeBottom.InvertNormal(true);
+
+ Stripe aStripe1( aTopP2, aTopP1, aBottomP1, aBottomP2 );
+ Stripe aStripe2( aTopP3, aTopP2, aBottomP2, aBottomP3 );
+ Stripe aStripe3( aTopP4, aTopP3, aBottomP3, aBottomP4 );
+ Stripe aStripe4( aTopP1, aTopP4, aBottomP4, aBottomP1 );
+
+ if( bInvertPolygon )
+ {
+ aStripe1 = Stripe( aBottomP1, aTopP1, aTopP2, aBottomP2 );
+ aStripe2 = Stripe( aBottomP2, aTopP2, aTopP3, aBottomP3 );
+ aStripe3 = Stripe( aBottomP3, aTopP3, aTopP4, aBottomP4 );
+ aStripe4 = Stripe( aBottomP4, aTopP4, aTopP1, aBottomP1 );
+ }
+
+ Stripe aNormalsStripe1( aNormalsTopP1, aNormalsBottomP1, aNormalsBottomP2, aNormalsTopP2 );
+ Stripe aNormalsStripe2( aNormalsTopP2, aNormalsBottomP2, aNormalsBottomP3, aNormalsTopP3 );
+ Stripe aNormalsStripe3( aNormalsTopP3, aNormalsBottomP3, aNormalsBottomP4, aNormalsTopP4 );
+ Stripe aNormalsStripe4( aNormalsTopP4, aNormalsBottomP4, aNormalsBottomP1, aNormalsTopP1 );
+
+ if( bInvertNormals )
+ {
+ aNormalsStripe1 = Stripe( aNormalsTopP2, aNormalsBottomP2, aNormalsBottomP1, aNormalsTopP1 );
+ aNormalsStripe2 = Stripe( aNormalsTopP3, aNormalsBottomP3, aNormalsBottomP2, aNormalsTopP2 );
+ aNormalsStripe3 = Stripe( aNormalsTopP4, aNormalsBottomP4, aNormalsBottomP3, aNormalsTopP3 );
+ aNormalsStripe4 = Stripe( aNormalsTopP1, aNormalsBottomP1, aNormalsBottomP4, aNormalsTopP4 );
+ }
+
+ aStripe1.SetManualNormal( aNormalsStripe1.getNormal() );
+ aStripe2.SetManualNormal( aNormalsStripe2.getNormal() );
+ aStripe3.SetManualNormal( aNormalsStripe3.getNormal() );
+ aStripe4.SetManualNormal( aNormalsStripe4.getNormal() );
+
+ const bool bFlatNormals = false;
+ ShapeFactory::createStripe( xGroup, aStripe1, xSourceProp, rPropertyNameMap, bDoubleSided, nRotatedTexture, bFlatNormals );
+ ShapeFactory::createStripe( xGroup, aStripe2, xSourceProp, rPropertyNameMap, bDoubleSided, nRotatedTexture, bFlatNormals );
+ ShapeFactory::createStripe( xGroup, aStripe3, xSourceProp, rPropertyNameMap, bDoubleSided, nRotatedTexture, bFlatNormals );
+ ShapeFactory::createStripe( xGroup, aStripe4, xSourceProp, rPropertyNameMap, bDoubleSided, nRotatedTexture, bFlatNormals );
+ ShapeFactory::createStripe( xGroup, aStripeBottom, xSourceProp, rPropertyNameMap, bDoubleSided, nRotatedTexture, bFlatNormals );
+
+ return xGroup;
+}
+
+rtl::Reference<Svx3DLatheObject>
+ ShapeFactory::createCone(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const drawing::Position3D& rPosition, const drawing::Direction3D& rSize
+ , double fTopHeight, sal_Int32 nRotateZAngleHundredthDegree )
+{
+ return impl_createConeOrCylinder( xTarget, rPosition, rSize, fTopHeight, nRotateZAngleHundredthDegree, false );
+}
+
+rtl::Reference<Svx3DLatheObject>
+ ShapeFactory::impl_createConeOrCylinder(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const drawing::Position3D& rPosition, const drawing::Direction3D& rSize
+ , double fTopHeight, sal_Int32 nRotateZAngleHundredthDegree
+ , bool bCylinder )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ //create shape
+ rtl::Reference<Svx3DLatheObject> xShape = new Svx3DLatheObject(nullptr);
+ xShape->setShapeKind(SdrObjKind::E3D_Lathe);
+ xTarget->addShape(*xShape);
+
+ double fWidth = rSize.DirectionX/2.0; //The depth will be corrected within Matrix
+ double fRadius = fWidth; //!!!!!!!! problem in drawing layer: rotation object calculates wrong needed size -> wrong camera (it's a problem with bounding boxes)
+ double fHeight = rSize.DirectionY;
+
+ //set properties
+ try
+ {
+ //Polygon
+ sal_Int32 nVerticalSegmentCount = 0;
+ uno::Any aPPolygon = bCylinder
+ ? createPolyPolygon_Cylinder(fHeight, fRadius, nVerticalSegmentCount)
+ : createPolyPolygon_Cone(fHeight, fRadius, fTopHeight, nVerticalSegmentCount);
+
+ //Matrix for position
+ basegfx::B3DHomMatrix aHomMatrix;
+ if (nRotateZAngleHundredthDegree != 0)
+ aHomMatrix.rotate(0.0,0.0,-basegfx::deg2rad<100>(nRotateZAngleHundredthDegree));
+ //stretch the symmetric objects to given depth
+ aHomMatrix.scale(1.0,1.0,rSize.DirectionZ/rSize.DirectionX);
+ aHomMatrix.translate(rPosition.PositionX, rPosition.PositionY, rPosition.PositionZ);
+
+ uno::Sequence<OUString> aPropertyNames{
+ UNO_NAME_3D_PERCENT_DIAGONAL,
+ UNO_NAME_3D_POLYPOLYGON3D,
+ UNO_NAME_3D_TRANSFORM_MATRIX,
+ UNO_NAME_3D_HORZ_SEGS,
+ UNO_NAME_3D_VERT_SEGS,
+ UNO_NAME_3D_REDUCED_LINE_GEOMETRY
+ };
+
+ uno::Sequence<uno::Any> aPropertyValues {
+ uno::Any(sal_Int16(5)), // PercentDiagonal
+ aPPolygon, // Polygon
+ uno::Any(B3DHomMatrixToHomogenMatrix(aHomMatrix)), // Matrix
+ uno::Any(CHART_3DOBJECT_SEGMENTCOUNT), // Horizontal Segments
+ uno::Any(nVerticalSegmentCount), // Vertical Segments
+ uno::Any(true) // Reduced lines
+ };
+
+ xShape->setPropertyValues(aPropertyNames, aPropertyValues);
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+static void appendAndCloseBezierCoords( drawing::PolyPolygonBezierCoords& rReturn, const drawing::PolyPolygonBezierCoords& rAdd, bool bAppendInverse )
+{
+ if(!rAdd.Coordinates.hasElements())
+ return;
+ sal_Int32 nAddCount = rAdd.Coordinates[0].getLength();
+ if(!nAddCount)
+ return;
+
+ sal_Int32 nOldCount = rReturn.Coordinates[0].getLength();
+
+ auto pCoordinates = rReturn.Coordinates.getArray();
+ pCoordinates[0].realloc(nOldCount + nAddCount + 1);
+ auto pCoordinates0 = pCoordinates[0].getArray();
+ auto pFlags = rReturn.Flags.getArray();
+ pFlags[0].realloc(nOldCount+nAddCount+1);
+ auto pFlags0 = pFlags[0].getArray();
+
+ for(sal_Int32 nN=0;nN<nAddCount; nN++ )
+ {
+ sal_Int32 nAdd = bAppendInverse ? (nAddCount-1-nN) : nN;
+ pCoordinates0[nOldCount+nN] = rAdd.Coordinates[0][nAdd];
+ pFlags0[nOldCount+nN] = rAdd.Flags[0][nAdd];
+ }
+
+ //close
+ pCoordinates0[nOldCount+nAddCount] = rReturn.Coordinates[0][0];
+ pFlags0[nOldCount+nAddCount] = rReturn.Flags[0][0];
+}
+
+static drawing::PolyPolygonBezierCoords getCircularArcBezierCoords(
+ double fStartAngleRadian, double fWidthAngleRadian, double fUnitRadius
+ , const ::basegfx::B2DHomMatrix& rTransformationFromUnitCircle
+ , const double fAngleSubdivisionRadian )
+{
+ //at least one polygon is created using two normal and two control points
+ //if the angle is larger it is separated into multiple sub angles
+
+ drawing::PolyPolygonBezierCoords aReturn;
+ sal_Int32 nSegmentCount = static_cast< sal_Int32 >( fWidthAngleRadian/fAngleSubdivisionRadian );
+ if( fWidthAngleRadian > fAngleSubdivisionRadian*nSegmentCount )
+ nSegmentCount++;
+
+ double fFirstSegmentAngle = fAngleSubdivisionRadian;
+ double fLastSegmentAngle = fAngleSubdivisionRadian;
+ if(nSegmentCount==1)
+ {
+ fFirstSegmentAngle = fWidthAngleRadian;
+ fLastSegmentAngle = 0.0;
+ }
+ else
+ {
+ double fFirstAngleOnSubDivision = (static_cast<sal_Int32>(fStartAngleRadian/fAngleSubdivisionRadian)+1)*fAngleSubdivisionRadian;
+ if( !::rtl::math::approxEqual( fStartAngleRadian, fFirstAngleOnSubDivision ) )
+ fFirstSegmentAngle = fFirstAngleOnSubDivision-fStartAngleRadian;
+
+ if(nSegmentCount>1)
+ {
+ fLastSegmentAngle = fWidthAngleRadian-fFirstSegmentAngle-fAngleSubdivisionRadian*(nSegmentCount-2);
+ if( fLastSegmentAngle<0 )
+ nSegmentCount--;
+ if( fLastSegmentAngle>fAngleSubdivisionRadian )
+ {
+ fLastSegmentAngle-=fAngleSubdivisionRadian;
+ nSegmentCount++;
+ }
+ }
+ }
+
+ sal_Int32 nPointCount = 1 + 3*nSegmentCount; //first point of next segment equals last point of former segment
+
+ drawing::PointSequence aPoints(nPointCount);
+ auto pPoints = aPoints.getArray();
+ drawing::FlagSequence aFlags(nPointCount);
+ auto pFlags = aFlags.getArray();
+
+ //!! applying matrix to vector does ignore translation, so it is important to use a B2DPoint here instead of B2DVector
+ ::basegfx::B2DPoint P0,P1,P2,P3;
+
+ sal_Int32 nPoint=0;
+ double fCurrentRotateAngle = fStartAngleRadian;
+ for(sal_Int32 nSegment=0; nSegment<nSegmentCount; nSegment++)
+ {
+ double fCurrentSegmentAngle = fAngleSubdivisionRadian;
+ if(nSegment==0)//first segment gets only a smaller peace until the next subdivision
+ fCurrentSegmentAngle = fFirstSegmentAngle;
+ else if(nSegment==(nSegmentCount-1)) //the last segment gets the rest angle that does not fit into equal pieces
+ fCurrentSegmentAngle = fLastSegmentAngle;
+
+ //first create untransformed points for a unit circle arc:
+ const double fCos = cos(fCurrentSegmentAngle/2.0);
+ const double fSin = sin(fCurrentSegmentAngle/2.0);
+ P0.setX(fCos);
+ P3.setX(fCos);
+ P0.setY(-fSin);
+ P3.setY(-P0.getY());
+
+ P1.setX((4.0-fCos)/3.0);
+ P2.setX(P1.getX());
+ P1.setY((1.0-fCos)*(fCos-3.0)/(3.0*fSin));
+ P2.setY(-P1.getY());
+ //transform thus startangle equals NULL
+ ::basegfx::B2DHomMatrix aStart;
+ aStart.rotate(fCurrentSegmentAngle/2.0 + fCurrentRotateAngle );
+ fCurrentRotateAngle+=fCurrentSegmentAngle;
+
+ aStart.scale( fUnitRadius, fUnitRadius );
+
+ //apply given transformation to get final points
+ P0 = rTransformationFromUnitCircle*(aStart*P0);
+ P1 = rTransformationFromUnitCircle*(aStart*P1);
+ P2 = rTransformationFromUnitCircle*(aStart*P2);
+ P3 = rTransformationFromUnitCircle*(aStart*P3);
+
+ pPoints[nPoint].X = static_cast< sal_Int32 >( P0.getX());
+ pPoints[nPoint].Y = static_cast< sal_Int32 >( P0.getY());
+ pFlags [nPoint++] = drawing::PolygonFlags_NORMAL;
+
+ pPoints[nPoint].X = static_cast< sal_Int32 >( P1.getX());
+ pPoints[nPoint].Y = static_cast< sal_Int32 >( P1.getY());
+ pFlags[nPoint++] = drawing::PolygonFlags_CONTROL;
+
+ pPoints[nPoint].X = static_cast< sal_Int32 >( P2.getX());
+ pPoints[nPoint].Y = static_cast< sal_Int32 >( P2.getY());
+ pFlags [nPoint++] = drawing::PolygonFlags_CONTROL;
+
+ if(nSegment==(nSegmentCount-1))
+ {
+ pPoints[nPoint].X = static_cast< sal_Int32 >( P3.getX());
+ pPoints[nPoint].Y = static_cast< sal_Int32 >( P3.getY());
+ pFlags [nPoint++] = drawing::PolygonFlags_NORMAL;
+ }
+ }
+
+ aReturn.Coordinates = { aPoints };
+ aReturn.Flags = { aFlags };
+
+ return aReturn;
+}
+
+static drawing::PolyPolygonBezierCoords getRingBezierCoords(
+ double fUnitCircleInnerRadius
+ , double fUnitCircleOuterRadius
+ , double fStartAngleRadian, double fWidthAngleRadian
+ , const ::basegfx::B2DHomMatrix& aTransformationFromUnitCircle
+ , const double fAngleSubdivisionRadian )
+{
+ drawing::PolyPolygonBezierCoords aReturn;
+
+ drawing::PolyPolygonBezierCoords aOuterArc = getCircularArcBezierCoords(
+ fStartAngleRadian, fWidthAngleRadian, fUnitCircleOuterRadius, aTransformationFromUnitCircle, fAngleSubdivisionRadian );
+ aReturn.Coordinates = { aOuterArc.Coordinates[0] };
+ aReturn.Flags = { aOuterArc.Flags[0] };
+
+ drawing::PolyPolygonBezierCoords aInnerArc = getCircularArcBezierCoords(
+ fStartAngleRadian, fWidthAngleRadian, fUnitCircleInnerRadius, aTransformationFromUnitCircle, fAngleSubdivisionRadian );
+ appendAndCloseBezierCoords( aReturn, aInnerArc, true );
+
+ return aReturn;
+}
+
+rtl::Reference<SvxShapePolyPolygon>
+ ShapeFactory::createPieSegment2D(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , double fUnitCircleStartAngleDegree, double fUnitCircleWidthAngleDegree
+ , double fUnitCircleInnerRadius, double fUnitCircleOuterRadius
+ , const drawing::Direction3D& rOffset
+ , const drawing::HomogenMatrix& rUnitCircleToScene )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ // tdf#123504: both 0 and 360 are valid and different values here!
+ while (fUnitCircleWidthAngleDegree > 360)
+ fUnitCircleWidthAngleDegree -= 360.0;
+ while (fUnitCircleWidthAngleDegree < 0)
+ fUnitCircleWidthAngleDegree += 360.0;
+
+ //create shape
+ rtl::Reference<SvxShapePolyPolygon> xShape = new SvxShapePolyPolygon(nullptr);
+ xShape->setShapeKind(SdrObjKind::PathFill); // aka ClosedBezierShape
+ xTarget->addShape(*xShape); //need to add the shape before setting of properties
+
+ //set properties
+ try
+ {
+ ::basegfx::B2DHomMatrix aTransformationFromUnitCircle( IgnoreZ( HomogenMatrixToB3DHomMatrix(rUnitCircleToScene) ) );
+ aTransformationFromUnitCircle.translate(rOffset.DirectionX,rOffset.DirectionY);
+
+ const double fAngleSubdivisionRadian = M_PI/10.0;
+
+ drawing::PolyPolygonBezierCoords aCoords
+ = getRingBezierCoords(fUnitCircleInnerRadius, fUnitCircleOuterRadius,
+ basegfx::deg2rad(fUnitCircleStartAngleDegree),
+ basegfx::deg2rad(fUnitCircleWidthAngleDegree),
+ aTransformationFromUnitCircle, fAngleSubdivisionRadian);
+
+ xShape->SvxShape::setPropertyValue( "PolyPolygonBezier", uno::Any( aCoords ) );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+
+ return xShape;
+}
+
+rtl::Reference<Svx3DExtrudeObject>
+ ShapeFactory::createPieSegment(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , double fUnitCircleStartAngleDegree, double fUnitCircleWidthAngleDegree
+ , double fUnitCircleInnerRadius, double fUnitCircleOuterRadius
+ , const drawing::Direction3D& rOffset
+ , const drawing::HomogenMatrix& rUnitCircleToScene
+ , double fDepth )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ // tdf#123504: both 0 and 360 are valid and different values here!
+ while (fUnitCircleWidthAngleDegree > 360)
+ fUnitCircleWidthAngleDegree -= 360.0;
+ while (fUnitCircleWidthAngleDegree < 0)
+ fUnitCircleWidthAngleDegree += 360.0;
+
+ //create shape
+ rtl::Reference<Svx3DExtrudeObject> xShape = new Svx3DExtrudeObject(nullptr);
+ xShape->setShapeKind(SdrObjKind::E3D_Extrusion);
+ xTarget->addShape(*xShape); //need to add the shape before setting of properties
+
+ //set properties
+ try
+ {
+ ::basegfx::B2DHomMatrix aTransformationFromUnitCircle( IgnoreZ( HomogenMatrixToB3DHomMatrix(rUnitCircleToScene) ) );
+ aTransformationFromUnitCircle.translate(rOffset.DirectionX,rOffset.DirectionY);
+
+ const double fAngleSubdivisionRadian = M_PI/32.0;
+
+ drawing::PolyPolygonBezierCoords aCoords
+ = getRingBezierCoords(fUnitCircleInnerRadius, fUnitCircleOuterRadius,
+ basegfx::deg2rad(fUnitCircleStartAngleDegree),
+ basegfx::deg2rad(fUnitCircleWidthAngleDegree),
+ aTransformationFromUnitCircle, fAngleSubdivisionRadian);
+
+ //depth
+ xShape->setPropertyValue( UNO_NAME_3D_EXTRUDE_DEPTH
+ , uno::Any(static_cast<sal_Int32>(fDepth)) );
+
+ //PercentDiagonal
+ xShape->setPropertyValue( UNO_NAME_3D_PERCENT_DIAGONAL
+ , uno::Any( sal_Int16(0) ) );
+
+ //Polygon
+ drawing::PolyPolygonShape3D aPoly( BezierToPoly(aCoords) );
+ ShapeFactory::closePolygon( aPoly );
+ xShape->setPropertyValue( UNO_NAME_3D_POLYPOLYGON3D
+ , uno::Any( aPoly ) );
+
+ //DoubleSided
+ xShape->setPropertyValue( UNO_NAME_3D_DOUBLE_SIDED
+ , uno::Any( true ) );
+
+ //Reduced lines
+ xShape->setPropertyValue( UNO_NAME_3D_REDUCED_LINE_GEOMETRY
+ , uno::Any( true ) );
+
+ //TextureProjectionMode
+ xShape->setPropertyValue( UNO_NAME_3D_TEXTURE_PROJ_Y
+ , uno::Any( drawing::TextureProjectionMode_OBJECTSPECIFIC ) );
+
+ //TextureProjectionMode
+ xShape->setPropertyValue( UNO_NAME_3D_TEXTURE_PROJ_X
+ , uno::Any( drawing::TextureProjectionMode_PARALLEL ) );
+ xShape->setPropertyValue( UNO_NAME_3D_TEXTURE_PROJ_Y
+ , uno::Any( drawing::TextureProjectionMode_OBJECTSPECIFIC ) );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<Svx3DPolygonObject>
+ ShapeFactory::createStripe( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const Stripe& rStripe
+ , const uno::Reference< beans::XPropertySet >& xSourceProp
+ , const tPropertyNameMap& rPropertyNameMap
+ , bool bDoubleSided
+ , short nRotatedTexture
+ , bool bFlatNormals )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ //create shape
+ rtl::Reference<Svx3DPolygonObject> xShape = new Svx3DPolygonObject(nullptr);
+ xShape->setShapeKind(SdrObjKind::E3D_Polygon);
+ xTarget->addShape(*xShape);
+
+ //set properties
+ try
+ {
+ uno::Sequence<OUString> aPropertyNames{
+ UNO_NAME_3D_POLYPOLYGON3D,
+ UNO_NAME_3D_TEXTUREPOLYGON3D,
+ UNO_NAME_3D_NORMALSPOLYGON3D,
+ UNO_NAME_3D_LINEONLY,
+ UNO_NAME_3D_DOUBLE_SIDED
+ };
+
+ uno::Sequence<uno::Any> aPropertyValues {
+ rStripe.getPolyPolygonShape3D(), // Polygon
+ Stripe::getTexturePolygon(nRotatedTexture), // TexturePolygon
+ rStripe.getNormalsPolygon(), // Normals Polygon
+ uno::Any(false), // LineOnly
+ uno::Any(bDoubleSided) // DoubleSided
+ };
+
+ //NormalsKind
+ if (bFlatNormals)
+ lcl_addProperty(aPropertyNames, aPropertyValues,
+ UNO_NAME_3D_NORMALS_KIND, uno::Any(drawing::NormalsKind_FLAT));
+
+ xShape->setPropertyValues(aPropertyNames, aPropertyValues);
+
+ if (xSourceProp)
+ {
+ PropertyMapper::setMappedProperties(*xShape, xSourceProp, rPropertyNameMap);
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<Svx3DExtrudeObject>
+ ShapeFactory::createArea3D( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const std::vector<std::vector<css::drawing::Position3D>>& rPolyPolygon
+ , double fDepth )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ if( rPolyPolygon.empty() )
+ return nullptr;
+
+ //create shape
+ rtl::Reference<Svx3DExtrudeObject> xShape = new Svx3DExtrudeObject(nullptr);
+ xShape->setShapeKind(SdrObjKind::E3D_Extrusion);
+ xTarget->addShape(*xShape);
+
+ css::drawing::PolyPolygonShape3D aUnoPolyPolygon = toPolyPolygonShape3D(rPolyPolygon);
+
+ //set properties
+ try
+ {
+ uno::Sequence<OUString> aPropertyNames{
+ UNO_NAME_3D_EXTRUDE_DEPTH,
+ UNO_NAME_3D_PERCENT_DIAGONAL,
+ UNO_NAME_3D_POLYPOLYGON3D,
+ UNO_NAME_3D_DOUBLE_SIDED,
+ };
+
+ uno::Sequence<uno::Any> aPropertyValues {
+ uno::Any(sal_Int32(fDepth)), // depth
+ uno::Any(sal_Int16(0)), // PercentDiagonal
+ uno::Any(aUnoPolyPolygon), // Polygon
+ uno::Any(true) // DoubleSided
+ };
+
+ //the z component of the polygon is now ignored by the drawing layer,
+ //so we need to translate the object via transformation matrix
+
+ //Matrix for position
+ if (!rPolyPolygon.empty() && !rPolyPolygon[0].empty())
+ {
+ basegfx::B3DHomMatrix aM;
+ aM.translate(0, 0, rPolyPolygon[0][0].PositionZ);
+ drawing::HomogenMatrix aHM = B3DHomMatrixToHomogenMatrix(aM);
+ lcl_addProperty(aPropertyNames, aPropertyValues, UNO_NAME_3D_TRANSFORM_MATRIX, uno::Any(aHM));
+ }
+ xShape->setPropertyValues(aPropertyNames, aPropertyValues);
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<SvxShapePolyPolygon>
+ ShapeFactory::createArea2D( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const std::vector<std::vector<css::drawing::Position3D>>& rPolyPolygon )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ //create shape
+ SdrPathObj* pPath = new SdrPathObj(xTarget->GetSdrObject()->getSdrModelFromSdrObject(), SdrObjKind::Polygon);
+ xTarget->GetSdrObject()->GetSubList()->InsertObject(pPath);
+
+ //set properties
+ try
+ {
+ // Polygon
+ basegfx::B2DPolyPolygon aNewPolyPolygon( PolyToB2DPolyPolygon(rPolyPolygon) );
+ // tdf#117145 metric of SdrModel is app-specific, metric of UNO API is 100thmm
+ pPath->ForceMetricToItemPoolMetric(aNewPolyPolygon);
+ pPath->SetPathPoly(aNewPolyPolygon);
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return static_cast<SvxShapePolyPolygon*>(pPath->getUnoShape().get());
+}
+
+static drawing::PointSequenceSequence createPolyPolygon_Symbol( const drawing::Position3D& rPos
+ , const drawing::Direction3D& rSize
+ , sal_Int32 nStandardSymbol )
+{
+ if(nStandardSymbol<0)
+ nStandardSymbol*=-1;
+ nStandardSymbol = nStandardSymbol%ShapeFactory::getSymbolCount();
+ SymbolEnum eSymbolType=static_cast<SymbolEnum>(nStandardSymbol);
+
+ const double& fX = rPos.PositionX;
+ const double& fY = rPos.PositionY;
+
+ const double fWidthH = rSize.DirectionX/2.0; //fWidthH stands for Half Width
+ const double fHeightH = rSize.DirectionY/2.0; //fHeightH stands for Half Height
+
+ const sal_Int32 nQuarterCount = 35; // points inside a quadrant, used in case circle
+
+ sal_Int32 nPointCount = 4; //all arrow symbols only need 4 points
+ switch( eSymbolType )
+ {
+ case Symbol_Square:
+ case Symbol_Diamond:
+ case Symbol_Bowtie:
+ case Symbol_Sandglass:
+ case Symbol_HorizontalBar:
+ case Symbol_VerticalBar:
+ nPointCount = 5;
+ break;
+ case Symbol_X:
+ nPointCount = 13;
+ break;
+ case Symbol_Plus:
+ nPointCount = 13;
+ break;
+ case Symbol_Star:
+ nPointCount = 9;
+ break;
+ case Symbol_Asterisk:
+ nPointCount = 19;
+ break;
+ case Symbol_Circle:
+ nPointCount = 5 + 4 * nQuarterCount;
+ break;
+ default:
+ break;
+ }
+
+ drawing::PointSequenceSequence aPP;
+
+ aPP.realloc(1);
+
+ uno::Sequence<awt::Point>* pOuterSequence = aPP.getArray();
+
+ pOuterSequence->realloc(nPointCount);
+
+ awt::Point* pInnerSequence = pOuterSequence->getArray();
+
+ auto toPoint = [](double x, double y) -> awt::Point
+ {
+ return { static_cast<sal_Int32>(x), static_cast<sal_Int32>(y) };
+ };
+ switch(eSymbolType)
+ {
+ case Symbol_Square:
+ {
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+ break;
+ }
+ case Symbol_UpArrow:
+ {
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+fHeightH );
+ break;
+ }
+ case Symbol_DownArrow:
+ {
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+ break;
+ }
+ case Symbol_RightArrow:
+ {
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+ break;
+ }
+ case Symbol_LeftArrow:
+ {
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY );
+ break;
+ }
+ case Symbol_Bowtie:
+ {
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+ break;
+ }
+ case Symbol_Sandglass:
+ {
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+fHeightH );
+ break;
+ }
+ case Symbol_Diamond:
+ {
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY );
+
+ *pInnerSequence++ = toPoint( fX, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY );
+
+ *pInnerSequence++ = toPoint( fX, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY );
+ break;
+ }
+ case Symbol_HorizontalBar:
+ {
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-0.2*fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY-0.2*fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY+0.2*fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+0.2*fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-0.2*fHeightH );
+ break;
+ }
+ case Symbol_VerticalBar:
+ {
+ *pInnerSequence++ = toPoint( fX-0.2*fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+0.2*fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+0.2*fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-0.2*fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-0.2*fWidthH, fY-fHeightH );
+
+ break;
+ }
+ case Symbol_Circle:
+ {
+ double fOmega = 1.5707963267948966192 / (nQuarterCount + 1.0);
+ // one point in the middle of each edge to get full size bounding rectangle
+ *pInnerSequence++ = toPoint( fX + fWidthH, fY );
+ // 0 to PI/2
+ for (sal_Int32 i = 1; i <= nQuarterCount; ++i)
+ {
+ *pInnerSequence++ = toPoint( fX + fWidthH * cos( i * fOmega ), fY - fHeightH * sin( i * fOmega ) );
+ }
+ // PI/2 to PI
+ *pInnerSequence++ = toPoint( fX, fY - fHeightH );
+ for (sal_Int32 i = 1; i <= nQuarterCount; ++i)
+ {
+ *pInnerSequence++ = toPoint( fX - fWidthH * sin( i * fOmega), fY - fHeightH * cos( i * fOmega) );
+ }
+ // PI to 3/2*PI
+ *pInnerSequence++ = toPoint( fX - fWidthH, fY );
+ for (sal_Int32 i = 1; i <= nQuarterCount; ++i)
+ {
+ *pInnerSequence++ = toPoint( fX - fWidthH * cos( i * fOmega), fY + fHeightH * sin( i * fOmega) );
+ }
+ // 3/2*PI to 2*PI
+ *pInnerSequence++ = toPoint( fX, fY + fHeightH );
+ for (sal_Int32 i = 1; i <= nQuarterCount; ++i)
+ {
+ *pInnerSequence++ = toPoint( fX + fWidthH * sin(i * fOmega), fY + fHeightH * cos(i * fOmega) );
+ }
+ // close polygon
+ *pInnerSequence++ = toPoint( fX + fWidthH, fY );
+ break;
+ }
+ case Symbol_Star:
+ {
+ *pInnerSequence++ = toPoint( fX, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+0.2*fWidthH, fY-0.2*fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY );
+
+ *pInnerSequence++ = toPoint( fX+0.2*fWidthH, fY+0.2*fHeightH );
+
+ *pInnerSequence++ = toPoint( fX, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-0.2*fWidthH, fY+0.2*fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY );
+
+ *pInnerSequence++ = toPoint( fX-0.2*fWidthH, fY-0.2*fHeightH );
+
+ *pInnerSequence++ = toPoint( fX, fY-fHeightH );
+ break;
+ }
+ case Symbol_X:
+ {
+ const double fScaleX = fWidthH / 128.0;
+ const double fScaleY = fHeightH / 128.0;
+ const double fSmall = sqrt(200.0);
+ const double fLarge = 128.0 - fSmall;
+
+ *pInnerSequence++ = toPoint( fX, fY - fScaleY * fSmall );
+
+ *pInnerSequence++ = toPoint( fX - fScaleX * fLarge, fY - fHeightH );
+
+ *pInnerSequence++ = toPoint( fX - fWidthH, fY - fScaleY * fLarge );
+
+ *pInnerSequence++ = toPoint( fX - fScaleX * fSmall, fY );
+
+ *pInnerSequence++ = toPoint( fX - fWidthH, fY + fScaleY * fLarge );
+
+ *pInnerSequence++ = toPoint( fX - fScaleX * fLarge, fY + fHeightH );
+
+ *pInnerSequence++ = toPoint( fX, fY + fScaleY * fSmall );
+
+ *pInnerSequence++ = toPoint( fX + fScaleX * fLarge, fY + fHeightH );
+
+ *pInnerSequence++ = toPoint( fX + fWidthH, fY + fScaleY * fLarge );
+
+ *pInnerSequence++ = toPoint( fX + fScaleX * fSmall, fY );
+
+ *pInnerSequence++ = toPoint( fX + fWidthH, fY - fScaleY * fLarge );
+
+ *pInnerSequence++ = toPoint( fX + fScaleX * fLarge, fY - fHeightH );
+
+ *pInnerSequence++ = toPoint( fX, fY - fScaleY * fSmall );
+ break;
+
+ }
+ case Symbol_Plus:
+ {
+ const double fScaleX = fWidthH / 128.0;
+ const double fScaleY = fHeightH / 128.0;
+ const double fHalf = 10.0; //half line width on 256 size square
+ const double fdX = fScaleX * fHalf;
+ const double fdY = fScaleY * fHalf;
+
+ *pInnerSequence++ = toPoint( fX-fdX, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fdX, fY-fdY );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fdY );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+fdY );
+
+ *pInnerSequence++ = toPoint( fX-fdX, fY+fdY );
+
+ *pInnerSequence++ = toPoint( fX-fdX, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fdX, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fdX, fY+fdY );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY+fdY );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY-fdY );
+
+ *pInnerSequence++ = toPoint( fX+fdX, fY-fdY );
+
+ *pInnerSequence++ = toPoint( fX+fdY, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fdX, fY-fHeightH );
+ break;
+
+ }
+ case Symbol_Asterisk:
+ {
+ const double fHalf = 10.0; // half line width on 256 size square
+ const double fTwoY = fHalf * sqrt(3.0);
+ const double fFourY = (128.0 - 2.0 * fHalf ) / sqrt(3.0);
+ const double fThreeX = 128.0 - fHalf;
+ const double fThreeY = fHalf * sqrt(3.0) + fFourY;
+ const double fFiveX = 2.0 * fHalf;
+
+ const double fScaleX = fWidthH / 128.0;
+ const double fScaleY = fHeightH / 128.0;
+
+ //1
+ *pInnerSequence++ = toPoint( fX-fScaleX * fHalf, fY-fHeightH );
+ //2
+ *pInnerSequence++ = toPoint( fX-fScaleX * fHalf, fY-fScaleY * fTwoY );
+ //3
+ *pInnerSequence++ = toPoint( fX-fScaleX * fThreeX, fY-fScaleY * fThreeY );
+ //4
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fScaleY * fFourY );
+ //5
+ *pInnerSequence++ = toPoint( fX-fScaleX * fFiveX, fY );
+ //6 as 4
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+fScaleY * fFourY );
+ //7 as 3
+ *pInnerSequence++ = toPoint( fX-fScaleX * fThreeX, fY+fScaleY * fThreeY );
+ //8 as 2
+ *pInnerSequence++ = toPoint( fX-fScaleX * fHalf, fY+fScaleY * fTwoY );
+ //9 as 1
+ *pInnerSequence++ = toPoint( fX-fScaleX * fHalf, fY+fHeightH );
+ //10 as 1
+ *pInnerSequence++ = toPoint( fX+fScaleX * fHalf, fY+fHeightH );
+ //11 as 2
+ *pInnerSequence++ = toPoint( fX+fScaleX * fHalf, fY+fScaleY * fTwoY );
+ //12 as 3
+ *pInnerSequence++ = toPoint( fX+fScaleX * fThreeX, fY+fScaleY * fThreeY );
+ //13 as 4
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY+fScaleY * fFourY );
+ //14 as 5
+ *pInnerSequence++ = toPoint( fX+fScaleX * fFiveX, fY );
+ //15 as 4
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY-fScaleY * fFourY );
+ //16 as 3
+ *pInnerSequence++ = toPoint( fX+fScaleX * fThreeX, fY-fScaleY * fThreeY );
+ //17 as 2
+ *pInnerSequence++ = toPoint( fX+fScaleX * fHalf, fY-fScaleY * fTwoY );
+ // 18 as 1
+ *pInnerSequence++ = toPoint( fX+fScaleX * fHalf, fY-fHeightH );
+ // 19 = 1, closing
+ *pInnerSequence++ = toPoint( fX-fScaleX * fHalf, fY-fHeightH );
+ break;
+ }
+ default: //case Symbol_Square:
+ {
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+ break;
+ }
+ }
+
+ return aPP;
+}
+
+rtl::Reference<SvxShapePolyPolygon>
+ ShapeFactory::createSymbol2D(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const drawing::Position3D& rPosition
+ , const drawing::Direction3D& rSize
+ , sal_Int32 nStandardSymbol
+ , sal_Int32 nBorderColor
+ , sal_Int32 nFillColor )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ //create shape
+ rtl::Reference<SvxShapePolyPolygon> xShape = new SvxShapePolyPolygon(nullptr);
+ xShape->setShapeKind(SdrObjKind::Polygon);
+ xTarget->addShape(*xShape);
+
+ //set properties
+ try
+ {
+ drawing::PointSequenceSequence aPoints =
+ createPolyPolygon_Symbol( rPosition, rSize, nStandardSymbol );
+
+ //Polygon
+ xShape->SvxShape::setPropertyValue( UNO_NAME_POLYPOLYGON
+ , uno::Any( aPoints ) );
+
+ //LineColor
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINECOLOR
+ , uno::Any( nBorderColor ) );
+
+ //FillColor
+ xShape->SvxShape::setPropertyValue( UNO_NAME_FILLCOLOR
+ , uno::Any( nFillColor ) );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<SvxGraphicObject>
+ ShapeFactory::createGraphic2D(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const drawing::Position3D& rPosition
+ , const drawing::Direction3D& rSize
+ , const uno::Reference< graphic::XGraphic >& xGraphic )
+{
+ if( !xTarget.is() || !xGraphic.is() )
+ return nullptr;
+
+ // @todo: change this to a rectangle shape with a fill bitmap for
+ // performance reasons (ask AW, said CL)
+
+ //create shape
+ rtl::Reference<SvxGraphicObject> xShape = new SvxGraphicObject(nullptr);
+ xShape->setShapeKind(SdrObjKind::Graphic);
+ xTarget->addShape(*xShape);
+
+ try
+ {
+ // assume position is upper left corner. Transform to center.
+ drawing::Position3D aCenterPosition(
+ rPosition.PositionX - (rSize.DirectionX / 2.0),
+ rPosition.PositionY - (rSize.DirectionY / 2.0),
+ rPosition.PositionZ );
+ xShape->setPosition( Position3DToAWTPoint( aCenterPosition ));
+ xShape->setSize( Direction3DToAWTSize( rSize ));
+ }
+ catch( const uno::Exception & )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ try
+ {
+ xShape->SvxShape::setPropertyValue( "Graphic", uno::Any( xGraphic ));
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference< SvxShapeGroup >
+ ShapeFactory::createGroup2D( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const OUString& aName )
+{
+ if( !xTarget.is() )
+ return nullptr;
+ try
+ {
+ //create and add to target
+ rtl::Reference<SvxShapeGroup> xShapeGroup = new SvxShapeGroup(nullptr, nullptr);
+ xShapeGroup->setShapeKind(SdrObjKind::Group);
+ // cast to resolve ambiguity in converting to XShape
+ xTarget->addShape(*xShapeGroup);
+
+ //set name
+ if(!aName.isEmpty())
+ setShapeName( xShapeGroup, aName );
+
+ {//workaround
+ //need this null size as otherwise empty group shapes where painted with a gray border
+ xShapeGroup->setSize(awt::Size(0,0));
+ }
+
+ return xShapeGroup;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return nullptr;
+}
+
+rtl::Reference< SvxShapeGroup >
+ ShapeFactory::createGroup2D( const rtl::Reference<SvxDrawPage>& xTarget
+ , const OUString& aName )
+{
+ if( !xTarget.is() )
+ return nullptr;
+ try
+ {
+ //create and add to target
+ rtl::Reference<SvxShapeGroup> xShapeGroup = new SvxShapeGroup(nullptr, nullptr);
+ xShapeGroup->setShapeKind(SdrObjKind::Group);
+ // cast to resolve ambiguity in converting to XShape
+ xTarget->add(static_cast<SvxShape*>(xShapeGroup.get()));
+
+ //set name
+ if(!aName.isEmpty())
+ setShapeName( xShapeGroup, aName );
+
+ {//workaround
+ //need this null size as otherwise empty group shapes where painted with a gray border
+ xShapeGroup->setSize(awt::Size(0,0));
+ }
+
+ return xShapeGroup;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return nullptr;
+}
+
+rtl::Reference<Svx3DSceneObject>
+ ShapeFactory::createGroup3D( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const OUString& aName )
+{
+ if( !xTarget.is() )
+ return nullptr;
+ try
+ {
+ //create shape
+ rtl::Reference<Svx3DSceneObject> xShape = new Svx3DSceneObject(nullptr, nullptr);
+ xShape->setShapeKind(SdrObjKind::E3D_Scene);
+ xTarget->addShape(*xShape);
+
+ //it is necessary to set the transform matrix to initialize the scene properly
+ //otherwise all objects which are placed into this Group will not be visible
+ //the following should be unnecessary after the bug is fixed
+ //set properties
+ try
+ {
+ ::basegfx::B3DHomMatrix aM;
+ xShape->SvxShape::setPropertyValue( UNO_NAME_3D_TRANSFORM_MATRIX
+ , uno::Any(B3DHomMatrixToHomogenMatrix(aM)) );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+
+ //set name
+ if(!aName.isEmpty())
+ setShapeName( xShape , aName );
+
+ //return
+ return xShape;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return nullptr;
+}
+
+rtl::Reference<SvxShapeCircle>
+ ShapeFactory::createCircle2D( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const drawing::Position3D& rPosition
+ , const drawing::Direction3D& rSize )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ //create shape
+ rtl::Reference<SvxShapeCircle> xShape = new SvxShapeCircle(nullptr);
+ xShape->setShapeKind(SdrObjKind::CircleOrEllipse);
+ xTarget->addShape(*xShape);
+
+ try
+ {
+ drawing::Position3D aCenterPosition(
+ rPosition.PositionX - (rSize.DirectionX / 2.0),
+ rPosition.PositionY - (rSize.DirectionY / 2.0),
+ rPosition.PositionZ );
+ xShape->setPosition( Position3DToAWTPoint( aCenterPosition ));
+ xShape->setSize( Direction3DToAWTSize( rSize ));
+ }
+ catch( const uno::Exception & )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+
+ //set properties
+ try
+ {
+ xShape->SvxShape::setPropertyValue( UNO_NAME_CIRCKIND, uno::Any( drawing::CircleKind_FULL ) );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<SvxShapeCircle>
+ ShapeFactory::createCircle( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const awt::Size& rSize
+ , const awt::Point& rPosition )
+{
+ rtl::Reference<SvxShapeCircle> xShape = new SvxShapeCircle(nullptr);
+ xShape->setShapeKind(SdrObjKind::CircleOrEllipse);
+ xTarget->addShape(*xShape);
+ xShape->setSize( rSize );
+ xShape->setPosition( rPosition );
+
+ return xShape;
+}
+
+rtl::Reference<Svx3DPolygonObject>
+ ShapeFactory::createLine3D( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const std::vector<std::vector<css::drawing::Position3D>>& rPoints
+ , const VLineProperties& rLineProperties )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ if(rPoints.empty())
+ return nullptr;
+
+ //create shape
+ rtl::Reference<Svx3DPolygonObject> xShape = new Svx3DPolygonObject(nullptr);
+ xShape->setShapeKind(SdrObjKind::E3D_Polygon);
+ xTarget->addShape(*xShape);
+
+ css::drawing::PolyPolygonShape3D aUnoPoly = toPolyPolygonShape3D(rPoints);
+
+ //set properties
+ try
+ {
+ uno::Sequence<OUString> aPropertyNames {
+ UNO_NAME_3D_POLYPOLYGON3D,
+ UNO_NAME_3D_LINEONLY
+ };
+
+ uno::Sequence<uno::Any> aPropertyValues {
+ uno::Any(aUnoPoly), // Polygon
+ uno::Any(true) // LineOnly
+ };
+
+ //Transparency
+ if(rLineProperties.Transparence.hasValue())
+ {
+ lcl_addProperty(aPropertyNames, aPropertyValues,
+ UNO_NAME_LINETRANSPARENCE,
+ rLineProperties.Transparence);
+ }
+
+ //LineStyle
+ if(rLineProperties.LineStyle.hasValue())
+ {
+ lcl_addProperty(aPropertyNames, aPropertyValues,
+ UNO_NAME_LINESTYLE,
+ rLineProperties.LineStyle);
+ }
+
+ //LineWidth
+ if(rLineProperties.Width.hasValue())
+ {
+ lcl_addProperty(aPropertyNames, aPropertyValues,
+ UNO_NAME_LINEWIDTH,
+ rLineProperties.Width);
+ }
+
+ //LineColor
+ if(rLineProperties.Color.hasValue())
+ {
+ lcl_addProperty(aPropertyNames, aPropertyValues,
+ UNO_NAME_LINECOLOR,
+ rLineProperties.Color);
+ }
+ xShape->setPropertyValues(aPropertyNames, aPropertyValues);
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<SvxShapePolyPolygon>
+ ShapeFactory::createLine2D( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const drawing::PointSequenceSequence& rPoints
+ , const VLineProperties* pLineProperties )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ if(!rPoints.hasElements())
+ return nullptr;
+
+ //create shape
+ rtl::Reference<SvxShapePolyPolygon> xShape = new SvxShapePolyPolygon(nullptr);
+ xShape->setShapeKind(SdrObjKind::PolyLine);
+ xTarget->addShape(*xShape);
+
+ //set properties
+ try
+ {
+ //Polygon
+ xShape->SvxShape::setPropertyValue( UNO_NAME_POLYPOLYGON
+ , uno::Any( rPoints ) );
+
+ if(pLineProperties)
+ {
+ //Transparency
+ if(pLineProperties->Transparence.hasValue())
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINETRANSPARENCE
+ , pLineProperties->Transparence );
+
+ //LineStyle
+ if(pLineProperties->LineStyle.hasValue())
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINESTYLE
+ , pLineProperties->LineStyle );
+
+ //LineWidth
+ if(pLineProperties->Width.hasValue())
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINEWIDTH
+ , pLineProperties->Width );
+
+ //LineColor
+ if(pLineProperties->Color.hasValue())
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINECOLOR
+ , pLineProperties->Color );
+
+ //LineDashName
+ if(pLineProperties->DashName.hasValue())
+ xShape->SvxShape::setPropertyValue( "LineDashName"
+ , pLineProperties->DashName );
+
+ //LineCap
+ if(pLineProperties->LineCap.hasValue())
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINECAP
+ , pLineProperties->LineCap );
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<SvxShapePolyPolygon>
+ ShapeFactory::createLine2D( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const std::vector<std::vector<css::drawing::Position3D>>& rPoints
+ , const VLineProperties* pLineProperties )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ if(rPoints.empty())
+ return nullptr;
+
+ //create shape
+ rtl::Reference<SvxShapePolyPolygon> xShape = new SvxShapePolyPolygon(nullptr);
+ xShape->setShapeKind(SdrObjKind::PolyLine);
+ xTarget->addShape(*xShape);
+
+ drawing::PointSequenceSequence aAnyPoints = PolyToPointSequence(rPoints);
+
+ //set properties
+ try
+ {
+ //Polygon
+ xShape->SvxShape::setPropertyValue( UNO_NAME_POLYPOLYGON
+ , uno::Any( aAnyPoints ) );
+
+ if(pLineProperties)
+ {
+ //Transparency
+ if(pLineProperties->Transparence.hasValue())
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINETRANSPARENCE
+ , pLineProperties->Transparence );
+
+ //LineStyle
+ if(pLineProperties->LineStyle.hasValue())
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINESTYLE
+ , pLineProperties->LineStyle );
+
+ //LineWidth
+ if(pLineProperties->Width.hasValue())
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINEWIDTH
+ , pLineProperties->Width );
+
+ //LineColor
+ if(pLineProperties->Color.hasValue())
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINECOLOR
+ , pLineProperties->Color );
+
+ //LineDashName
+ if(pLineProperties->DashName.hasValue())
+ xShape->SvxShape::setPropertyValue( "LineDashName"
+ , pLineProperties->DashName );
+
+ //LineCap
+ if(pLineProperties->LineCap.hasValue())
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINECAP
+ , pLineProperties->LineCap );
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<SvxShapePolyPolygon>
+ ShapeFactory::createLine ( const rtl::Reference<SvxShapeGroupAnyD>& xTarget,
+ const awt::Size& rSize, const awt::Point& rPosition )
+{
+ //create shape
+ rtl::Reference<SvxShapePolyPolygon> xShape = new SvxShapePolyPolygon(nullptr);
+ xShape->setShapeKind(SdrObjKind::Line);
+ xTarget->addShape(*xShape);
+ xShape->setSize( rSize );
+ xShape->setPosition( rPosition );
+
+ return xShape;
+}
+
+rtl::Reference<SvxShapeRect> ShapeFactory::createInvisibleRectangle(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const awt::Size& rSize )
+{
+ try
+ {
+ if(!xTarget.is())
+ return nullptr;
+
+ rtl::Reference<SvxShapeRect> xShape = new SvxShapeRect(nullptr);
+ xShape->setShapeKind(SdrObjKind::Rectangle);
+ xTarget->addShape( *xShape );
+ ShapeFactory::makeShapeInvisible( xShape );
+ xShape->setSize( rSize );
+ return xShape;
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ return nullptr;
+}
+
+rtl::Reference<SvxShapeRect> ShapeFactory::createRectangle(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget,
+ const awt::Size& rSize,
+ const awt::Point& rPosition,
+ const tNameSequence& rPropNames,
+ const tAnySequence& rPropValues,
+ StackPosition ePos )
+{
+ rtl::Reference<SvxShapeRect> xShape = new SvxShapeRect(nullptr);
+ xShape->setShapeKind(SdrObjKind::Rectangle);
+ if (ePos == StackPosition::Bottom)
+ {
+ uno::Reference<drawing::XShapes2> xTarget2(static_cast<cppu::OWeakObject*>(xTarget.get()), uno::UNO_QUERY);
+ if (xTarget2.is())
+ xTarget2->addBottom(xShape);
+ }
+ else
+ xTarget->addShape(*xShape);
+
+ xShape->setPosition( rPosition );
+ xShape->setSize( rSize );
+ PropertyMapper::setMultiProperties( rPropNames, rPropValues, *xShape );
+
+ return xShape;
+}
+
+rtl::Reference<SvxShapeRect>
+ ShapeFactory::createRectangle(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget )
+{
+ rtl::Reference<SvxShapeRect> xShape = new SvxShapeRect(nullptr);
+ xShape->setShapeKind(SdrObjKind::Rectangle);
+ xTarget->addShape( *xShape );
+
+ return xShape;
+}
+
+rtl::Reference<SvxShapeText>
+ ShapeFactory::createText( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const OUString& rText
+ , const tNameSequence& rPropNames
+ , const tAnySequence& rPropValues
+ , const uno::Any& rATransformation )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ if(rText.isEmpty())
+ return nullptr;
+
+ //create shape and add to page
+ rtl::Reference<SvxShapeText> xShape = new SvxShapeText(nullptr);
+ xShape->setShapeKind(SdrObjKind::Text);
+ xTarget->addShape(*xShape);
+
+ //set text
+ xShape->setString( rText );
+
+ //set properties
+ PropertyMapper::setMultiProperties( rPropNames, rPropValues, *xShape );
+
+ //set position matrix
+ //the matrix needs to be set at the end behind autogrow and such position influencing properties
+ try
+ {
+ if (rATransformation.hasValue())
+ xShape->SvxShape::setPropertyValue( "Transformation", rATransformation );
+ else
+ SAL_INFO("chart2", "No rATransformation value is given to ShapeFactory::createText()");
+
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<SvxShapeText>
+ ShapeFactory::createText( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const uno::Sequence< uno::Reference< chart2::XFormattedString > >& xFormattedString
+ , const tNameSequence& rPropNames
+ , const tAnySequence& rPropValues
+ , const uno::Any& rATransformation )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ if( !xFormattedString.hasElements() )
+ return nullptr;
+
+ sal_Int32 nNumberOfParagraphs = xFormattedString.getLength();
+
+ bool bNotEmpty = false;
+ for( sal_Int32 nN = 0; nN < nNumberOfParagraphs; ++nN )
+ {
+ if( !xFormattedString[nN]->getString().isEmpty() )
+ {
+ bNotEmpty = true;
+ break;
+ }
+ }
+ if( !bNotEmpty )
+ return nullptr;
+
+ //create shape and add to page
+ rtl::Reference<SvxShapeText> xShape = new SvxShapeText(nullptr);
+ xShape->setShapeKind(SdrObjKind::Text);
+ xTarget->addShape(*xShape);
+
+ //set paragraph properties
+ bNotEmpty = false;
+ // the first cursor is used for appending the next paragraph,
+ // after a new string has been inserted the cursor is moved at the end
+ // of the inserted string
+ // the second cursor is used for selecting the paragraph and apply the
+ // passed text properties
+ Reference< text::XTextCursor > xInsertCursor = xShape->createTextCursor();
+ Reference< text::XTextCursor > xSelectionCursor = xShape->createTextCursor();
+ if( xInsertCursor.is() && xSelectionCursor.is() )
+ {
+ uno::Reference< beans::XPropertySet > xSelectionProp( xSelectionCursor, uno::UNO_QUERY );
+ if( xSelectionProp.is() )
+ {
+ for( sal_Int32 nN = 0; nN < nNumberOfParagraphs; ++nN )
+ {
+ if( !xFormattedString[nN]->getString().isEmpty() )
+ {
+ xInsertCursor->gotoEnd( false );
+ xSelectionCursor->gotoEnd( false );
+ xShape->insertString( xInsertCursor, xFormattedString[nN]->getString(), false );
+ bNotEmpty = true;
+ xSelectionCursor->gotoEnd( true ); // select current paragraph
+ uno::Reference< beans::XPropertySet > xStringProperties( xFormattedString[nN], uno::UNO_QUERY );
+ PropertyMapper::setMappedProperties( xSelectionProp, xStringProperties,
+ PropertyMapper::getPropertyNameMapForTextShapeProperties() );
+ }
+ }
+ }
+ }
+
+ if( !bNotEmpty )
+ return nullptr;
+
+ //set whole text shape properties
+ PropertyMapper::setMultiProperties( rPropNames, rPropValues, *xShape );
+
+ if( rATransformation.hasValue() )
+ {
+ //set position matrix
+ //the matrix needs to be set at the end behind autogrow and such position influencing properties
+ try
+ {
+ xShape->SvxShape::setPropertyValue( "Transformation", rATransformation );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ }
+ return xShape;
+}
+
+rtl::Reference<SvxShapeText>
+ ShapeFactory::createText( const rtl::Reference<SvxShapeGroupAnyD>& xTarget,
+ const awt::Size& rSize,
+ const awt::Point& rPos,
+ uno::Sequence< uno::Reference< chart2::XFormattedString > >& xFormattedString,
+ const uno::Reference<
+ beans::XPropertySet > & xTextProperties,
+ double nRotation, const OUString& aName, sal_Int32 nTextMaxWidth )
+{
+ //create shape and add to page
+ rtl::Reference<SvxShapeText> xShape = new SvxShapeText(nullptr);
+ xShape->setShapeKind(SdrObjKind::Text);
+ try
+ {
+ xTarget->addShape(*xShape);
+
+ //set text and text properties
+ uno::Reference< text::XTextCursor > xTextCursor( xShape->createTextCursor() );
+ if( !xTextCursor.is() )
+ return xShape;
+
+ tPropertyNameValueMap aValueMap;
+ //fill line-, fill- and paragraph-properties into the ValueMap
+ {
+ tPropertyNameMap aNameMap = PropertyMapper::getPropertyNameMapForParagraphProperties();
+ auto const & add = PropertyMapper::getPropertyNameMapForFillAndLineProperties();
+ aNameMap.insert(add.begin(), add.end());
+
+ PropertyMapper::getValueMap( aValueMap, aNameMap, xTextProperties );
+ }
+
+ //fill some more shape properties into the ValueMap
+ {
+ aValueMap.insert( { "TextHorizontalAdjust", uno::Any(drawing::TextHorizontalAdjust_CENTER) } ); // drawing::TextHorizontalAdjust
+ aValueMap.insert( { "TextVerticalAdjust", uno::Any(drawing::TextVerticalAdjust_CENTER) } ); //drawing::TextVerticalAdjust
+ aValueMap.insert( { "TextAutoGrowHeight", uno::Any(true) } ); // sal_Bool
+ aValueMap.insert( { "TextAutoGrowWidth", uno::Any(true) } ); // sal_Bool
+ aValueMap.insert( { "TextMaximumFrameWidth", uno::Any(nTextMaxWidth) } ); // sal_Int32
+
+ //set name/classified ObjectID (CID)
+ if( !aName.isEmpty() )
+ aValueMap.emplace( "Name", uno::Any( aName ) ); //CID OUString
+ }
+
+ //set global title properties
+ {
+ tNameSequence aPropNames;
+ tAnySequence aPropValues;
+ PropertyMapper::getMultiPropertyListsFromValueMap( aPropNames, aPropValues, aValueMap );
+ PropertyMapper::setMultiProperties( aPropNames, aPropValues, *xShape );
+ }
+
+ bool bStackCharacters(false);
+ try
+ {
+ xTextProperties->getPropertyValue( "StackCharacters" ) >>= bStackCharacters;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+
+ if(bStackCharacters)
+ {
+ //if the characters should be stacked we use only the first character properties for code simplicity
+ if( xFormattedString.hasElements() )
+ {
+ OUString aLabel;
+ for( const auto & i : std::as_const(xFormattedString) )
+ aLabel += i->getString();
+ aLabel = ShapeFactory::getStackedString( aLabel, bStackCharacters );
+
+ xTextCursor->gotoEnd(false);
+ xShape->insertString( xTextCursor, aLabel, false );
+ xTextCursor->gotoEnd(true);
+ uno::Reference< beans::XPropertySet > xSourceProps( xFormattedString[0], uno::UNO_QUERY );
+
+ PropertyMapper::setMappedProperties( *xShape, xSourceProps
+ , PropertyMapper::getPropertyNameMapForCharacterProperties() );
+
+ // adapt font size according to page size
+ awt::Size aOldRefSize;
+ if( xTextProperties->getPropertyValue( "ReferencePageSize") >>= aOldRefSize )
+ {
+ RelativeSizeHelper::adaptFontSizes( *xShape, aOldRefSize, rSize );
+ }
+ }
+ }
+ else
+ {
+ for( const uno::Reference< chart2::XFormattedString >& rxFS : std::as_const(xFormattedString) )
+ {
+ xTextCursor->gotoEnd(false);
+ xShape->insertString( xTextCursor, rxFS->getString(), false );
+ xTextCursor->gotoEnd(true);
+ }
+ awt::Size aOldRefSize;
+ bool bHasRefPageSize =
+ ( xTextProperties->getPropertyValue( "ReferencePageSize") >>= aOldRefSize );
+
+ if( xFormattedString.hasElements() )
+ {
+ uno::Reference< beans::XPropertySet > xSourceProps( xFormattedString[0], uno::UNO_QUERY );
+ PropertyMapper::setMappedProperties( *xShape, xSourceProps, PropertyMapper::getPropertyNameMapForCharacterProperties() );
+
+ // adapt font size according to page size
+ if( bHasRefPageSize )
+ {
+ RelativeSizeHelper::adaptFontSizes( *xShape, aOldRefSize, rSize );
+ }
+ }
+ }
+
+ // #i109336# Improve auto positioning in chart
+ float fFontHeight = 0.0;
+ if ( xShape->SvxShape::getPropertyValue( "CharHeight" ) >>= fFontHeight )
+ {
+ fFontHeight = convertPointToMm100(fFontHeight);
+ sal_Int32 nXDistance = static_cast< sal_Int32 >( ::rtl::math::round( fFontHeight * 0.18f ) );
+ sal_Int32 nYDistance = static_cast< sal_Int32 >( ::rtl::math::round( fFontHeight * 0.30f ) );
+ xShape->SvxShape::setPropertyValue( "TextLeftDistance", uno::Any( nXDistance ) );
+ xShape->SvxShape::setPropertyValue( "TextRightDistance", uno::Any( nXDistance ) );
+ xShape->SvxShape::setPropertyValue( "TextUpperDistance", uno::Any( nYDistance ) );
+ xShape->SvxShape::setPropertyValue( "TextLowerDistance", uno::Any( nYDistance ) );
+ }
+ sal_Int32 nXPos = rPos.X;
+ sal_Int32 nYPos = rPos.Y;
+
+ //set position matrix
+ //the matrix needs to be set at the end behind autogrow and such position influencing properties
+ ::basegfx::B2DHomMatrix aM;
+ aM.rotate( -basegfx::deg2rad(nRotation) );//#i78696#->#i80521#
+ aM.translate( nXPos, nYPos );
+ xShape->SvxShape::setPropertyValue( "Transformation", uno::Any( B2DHomMatrixToHomogenMatrix3(aM) ) );
+
+ xShape->SvxShape::setPropertyValue( "ParaAdjust", uno::Any( style::ParagraphAdjust_CENTER ) );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<SvxShapeGroupAnyD> ShapeFactory::getChartRootShape(
+ const rtl::Reference<SvxDrawPage>& xDrawPage )
+{
+ rtl::Reference<SvxShapeGroupAnyD> xRet;
+ const uno::Reference< drawing::XShapes > xShapes = xDrawPage;
+ if( xShapes.is() )
+ {
+ sal_Int32 nCount = xShapes->getCount();
+ uno::Reference< drawing::XShape > xShape;
+ for( sal_Int32 nN = nCount; nN--; )
+ {
+ if( xShapes->getByIndex( nN ) >>= xShape )
+ {
+ if( ShapeFactory::getShapeName( xShape ) == "com.sun.star.chart2.shapes" )
+ {
+ xRet = dynamic_cast<SvxShapeGroupAnyD*>(xShape.get());
+ assert(xRet);
+ break;
+ }
+ }
+ }
+ }
+ return xRet;
+}
+
+void ShapeFactory::makeShapeInvisible( const rtl::Reference< SvxShape >& xShape )
+{
+ try
+ {
+ xShape->setPropertyValue( "LineStyle", uno::Any( drawing::LineStyle_NONE ));
+ xShape->setPropertyValue( "FillStyle", uno::Any( drawing::FillStyle_NONE ));
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+}
+
+// set a name/CID at a shape (is used for selection handling)
+
+void ShapeFactory::setShapeName( const rtl::Reference< SvxShape >& xShape
+ , const OUString& rName )
+{
+ if(!xShape.is())
+ return;
+ try
+ {
+ xShape->setPropertyValue( UNO_NAME_MISC_OBJ_NAME
+ , uno::Any( rName ) );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+}
+
+OUString ShapeFactory::getShapeName( const uno::Reference< drawing::XShape >& xShape )
+{
+ OUString aRet;
+
+ uno::Reference< beans::XPropertySet > xProp( xShape, uno::UNO_QUERY );
+ OSL_ENSURE(xProp.is(), "shape offers no XPropertySet");
+ if( xProp.is())
+ {
+ try
+ {
+ xProp->getPropertyValue( UNO_NAME_MISC_OBJ_NAME ) >>= aRet;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ }
+
+ return aRet;
+}
+
+uno::Any ShapeFactory::makeTransformation( const awt::Point& rScreenPosition2D, double fRotationAnglePi )
+{
+ ::basegfx::B2DHomMatrix aM;
+ //As autogrow is active the rectangle is automatically expanded to that side
+ //to which the text is not adjusted.
+ // aM.scale( 1, 1 ); Oops? A scale with this parameters is neutral, line commented out
+ aM.rotate( fRotationAnglePi );
+ aM.translate( rScreenPosition2D.X, rScreenPosition2D.Y );
+ uno::Any aATransformation( B2DHomMatrixToHomogenMatrix3(aM) );
+ return aATransformation;
+}
+
+OUString ShapeFactory::getStackedString( const OUString& rString, bool bStacked )
+{
+ sal_Int32 nLen = rString.getLength();
+ if(!bStacked || !nLen)
+ return rString;
+
+ OUStringBuffer aStackStr;
+
+ //add a newline after each letter
+ //as we do not no letters here add a newline after each char
+ for( sal_Int32 nPosSrc=0; nPosSrc < nLen; nPosSrc++ )
+ {
+ if( nPosSrc )
+ aStackStr.append( '\r' );
+ aStackStr.append(rString[nPosSrc]);
+ }
+ return aStackStr.makeStringAndClear();
+}
+
+bool ShapeFactory::hasPolygonAnyLines( const std::vector<std::vector<css::drawing::Position3D>>& rPoly)
+{
+ // #i67757# check all contained polygons, if at least one polygon contains 2 or more points, return true
+ for( auto const & i : rPoly )
+ if( i.size() > 1 )
+ return true;
+ return false;
+}
+
+bool ShapeFactory::isPolygonEmptyOrSinglePoint( const drawing::PolyPolygonShape3D& rPoly)
+{
+ // true, if empty polypolygon or one polygon with one point
+ return !rPoly.SequenceX.hasElements() ||
+ ((rPoly.SequenceX.getLength() == 1) && (rPoly.SequenceX[0].getLength() <= 1));
+}
+
+bool ShapeFactory::isPolygonEmptyOrSinglePoint( const std::vector<std::vector<css::drawing::Position3D>>& rPoly)
+{
+ // true, if empty polypolygon or one polygon with one point
+ return rPoly.empty() || ((rPoly.size() == 1) && (rPoly[0].size() <= 1));
+}
+
+void ShapeFactory::closePolygon( drawing::PolyPolygonShape3D& rPoly)
+{
+ OSL_ENSURE( rPoly.SequenceX.getLength() <= 1, "ShapeFactory::closePolygon - single polygon expected" );
+ //add a last point == first point
+ if(isPolygonEmptyOrSinglePoint(rPoly))
+ return;
+ drawing::Position3D aFirst(rPoly.SequenceX[0][0],rPoly.SequenceY[0][0],rPoly.SequenceZ[0][0]);
+ AddPointToPoly( rPoly, aFirst );
+}
+
+void ShapeFactory::closePolygon( std::vector<std::vector<css::drawing::Position3D>>& rPoly)
+{
+ OSL_ENSURE( rPoly.size() <= 1, "ShapeFactory::closePolygon - single polygon expected" );
+ //add a last point == first point
+ if(isPolygonEmptyOrSinglePoint(rPoly))
+ return;
+ drawing::Position3D aFirst(rPoly[0][0]);
+ AddPointToPoly( rPoly, aFirst );
+}
+
+awt::Size ShapeFactory::calculateNewSizeRespectingAspectRatio(
+ const awt::Size& rTargetSize
+ , const awt::Size& rSourceSizeWithCorrectAspectRatio )
+{
+ awt::Size aNewSize;
+
+ double fFactorWidth = double(rTargetSize.Width)/double(rSourceSizeWithCorrectAspectRatio.Width);
+ double fFactorHeight = double(rTargetSize.Height)/double(rSourceSizeWithCorrectAspectRatio.Height);
+ double fFactor = std::min(fFactorWidth,fFactorHeight);
+ aNewSize.Width=static_cast<sal_Int32>(fFactor*rSourceSizeWithCorrectAspectRatio.Width);
+ aNewSize.Height=static_cast<sal_Int32>(fFactor*rSourceSizeWithCorrectAspectRatio.Height);
+
+ return aNewSize;
+}
+
+awt::Point ShapeFactory::calculateTopLeftPositionToCenterObject(
+ const awt::Point& rTargetAreaPosition
+ , const awt::Size& rTargetAreaSize
+ , const awt::Size& rObjectSize )
+{
+ awt::Point aNewPosition(rTargetAreaPosition);
+ aNewPosition.X += static_cast<sal_Int32>(double(rTargetAreaSize.Width-rObjectSize.Width)/2.0);
+ aNewPosition.Y += static_cast<sal_Int32>(double(rTargetAreaSize.Height-rObjectSize.Height)/2.0);
+ return aNewPosition;
+}
+
+::basegfx::B2IRectangle ShapeFactory::getRectangleOfShape( SvxShape& rShape )
+{
+ ::basegfx::B2IRectangle aRet;
+
+ awt::Point aPos = rShape.getPosition();
+ awt::Size aSize = rShape.getSize();
+ aRet = BaseGFXHelper::makeRectangle(aPos,aSize);
+
+ return aRet;
+}
+
+awt::Size ShapeFactory::getSizeAfterRotation(
+ SvxShape& rShape, double fRotationAngleDegree )
+{
+ awt::Size aRet(0,0);
+ const awt::Size aSize( rShape.getSize() );
+
+ if( fRotationAngleDegree == 0.0 )
+ aRet = aSize;
+ else
+ {
+ fRotationAngleDegree = NormAngle360(fRotationAngleDegree);
+ if(fRotationAngleDegree>270.0)
+ fRotationAngleDegree=360.0-fRotationAngleDegree;
+ else if(fRotationAngleDegree>180.0)
+ fRotationAngleDegree=fRotationAngleDegree-180.0;
+ else if(fRotationAngleDegree>90.0)
+ fRotationAngleDegree=180.0-fRotationAngleDegree;
+
+ const double fAnglePi = basegfx::deg2rad(fRotationAngleDegree);
+
+ aRet.Height = static_cast<sal_Int32>(
+ aSize.Width*std::sin( fAnglePi )
+ + aSize.Height*std::cos( fAnglePi ));
+ aRet.Width = static_cast<sal_Int32>(
+ aSize.Width*std::cos( fAnglePi )
+ + aSize.Height*std::sin( fAnglePi ));
+ }
+ return aRet;
+}
+
+void ShapeFactory::removeSubShapes( const rtl::Reference<SvxShapeGroupAnyD>& xShapes )
+{
+ if( xShapes.is() )
+ {
+ sal_Int32 nSubCount = xShapes->getCount();
+ uno::Reference< drawing::XShape > xShape;
+ for( sal_Int32 nS = nSubCount; nS--; )
+ {
+ if( xShapes->getByIndex( nS ) >>= xShape )
+ xShapes->remove( xShape );
+ }
+ }
+}
+
+rtl::Reference<SvxTableShape>
+ShapeFactory::createTable(rtl::Reference<SvxShapeGroupAnyD> const& xTarget)
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ //create table shape
+ rtl::Reference<SvxTableShape> xShape = new SvxTableShape(nullptr);
+ xShape->setShapeKind(SdrObjKind::Table);
+ xTarget->addShape(*xShape);
+
+ return xShape;
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/Stripe.cxx b/chart2/source/view/main/Stripe.cxx
new file mode 100644
index 000000000..74c8ad046
--- /dev/null
+++ b/chart2/source/view/main/Stripe.cxx
@@ -0,0 +1,347 @@
+/* -*- 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 <Stripe.hxx>
+#include <CommonConverters.hxx>
+#include <com/sun/star/drawing/PolyPolygonShape3D.hpp>
+#include <com/sun/star/drawing/DoubleSequence.hpp>
+#include <basegfx/polygon/b3dpolygon.hxx>
+
+using namespace ::com::sun::star;
+
+namespace chart
+{
+
+Stripe::Stripe( const drawing::Position3D& rPoint1
+ , const drawing::Direction3D& rDirectionToPoint2
+ , const drawing::Direction3D& rDirectionToPoint4 )
+ : m_aPoint1(rPoint1)
+ , m_aPoint2(rPoint1+rDirectionToPoint2)
+ , m_aPoint3(m_aPoint2+rDirectionToPoint4)
+ , m_aPoint4(rPoint1+rDirectionToPoint4)
+ , m_bInvertNormal(false)
+ , m_bManualNormalSet(false)
+{
+}
+
+Stripe::Stripe( const drawing::Position3D& rPoint1
+ , const drawing::Position3D& rPoint2
+ , double fDepth )
+ : m_aPoint1(rPoint1)
+ , m_aPoint2(rPoint2)
+ , m_aPoint3(rPoint2)
+ , m_aPoint4(rPoint1)
+ , m_bInvertNormal(false)
+ , m_bManualNormalSet(false)
+{
+ m_aPoint3.PositionZ += fDepth;
+ m_aPoint4.PositionZ += fDepth;
+}
+
+Stripe::Stripe( const drawing::Position3D& rPoint1
+ , const drawing::Position3D& rPoint2
+ , const drawing::Position3D& rPoint3
+ , const drawing::Position3D& rPoint4 )
+ : m_aPoint1(rPoint1)
+ , m_aPoint2(rPoint2)
+ , m_aPoint3(rPoint3)
+ , m_aPoint4(rPoint4)
+ , m_bInvertNormal(false)
+ , m_bManualNormalSet(false)
+{
+}
+
+void Stripe::SetManualNormal( const drawing::Direction3D& rNormal )
+{
+ m_aManualNormal = rNormal;
+ m_bManualNormalSet = true;
+}
+
+void Stripe::InvertNormal( bool bInvertNormal )
+{
+ m_bInvertNormal = bInvertNormal;
+}
+
+uno::Any Stripe::getPolyPolygonShape3D() const
+{
+ drawing::PolyPolygonShape3D aPP;
+
+ aPP.SequenceX.realloc(1);
+ aPP.SequenceY.realloc(1);
+ aPP.SequenceZ.realloc(1);
+
+ drawing::DoubleSequence* pOuterSequenceX = aPP.SequenceX.getArray();
+ drawing::DoubleSequence* pOuterSequenceY = aPP.SequenceY.getArray();
+ drawing::DoubleSequence* pOuterSequenceZ = aPP.SequenceZ.getArray();
+
+ pOuterSequenceX->realloc(4);
+ pOuterSequenceY->realloc(4);
+ pOuterSequenceZ->realloc(4);
+
+ double* pInnerSequenceX = pOuterSequenceX->getArray();
+ double* pInnerSequenceY = pOuterSequenceY->getArray();
+ double* pInnerSequenceZ = pOuterSequenceZ->getArray();
+
+ *pInnerSequenceX++ = m_aPoint1.PositionX;
+ *pInnerSequenceY++ = m_aPoint1.PositionY;
+ *pInnerSequenceZ++ = m_aPoint1.PositionZ;
+
+ *pInnerSequenceX++ = m_aPoint2.PositionX;
+ *pInnerSequenceY++ = m_aPoint2.PositionY;
+ *pInnerSequenceZ++ = m_aPoint2.PositionZ;
+
+ *pInnerSequenceX++ = m_aPoint3.PositionX;
+ *pInnerSequenceY++ = m_aPoint3.PositionY;
+ *pInnerSequenceZ++ = m_aPoint3.PositionZ;
+
+ *pInnerSequenceX++ = m_aPoint4.PositionX;
+ *pInnerSequenceY++ = m_aPoint4.PositionY;
+ *pInnerSequenceZ++ = m_aPoint4.PositionZ;
+
+ return uno::Any( &aPP, cppu::UnoType<drawing::PolyPolygonShape3D>::get());
+}
+
+drawing::Direction3D Stripe::getNormal() const
+{
+ drawing::Direction3D aRet(1.0,0.0,0.0);
+
+ if( m_bManualNormalSet )
+ aRet = m_aManualNormal;
+ else
+ {
+ ::basegfx::B3DPolygon aPolygon3D;
+ aPolygon3D.append(Position3DToB3DPoint( m_aPoint1 ));
+ aPolygon3D.append(Position3DToB3DPoint( m_aPoint2 ));
+ aPolygon3D.append(Position3DToB3DPoint( m_aPoint3 ));
+ aPolygon3D.append(Position3DToB3DPoint( m_aPoint4 ));
+ ::basegfx::B3DVector aNormal(aPolygon3D.getNormal());
+ aRet = B3DVectorToDirection3D(aNormal);
+ }
+
+ if( m_bInvertNormal )
+ {
+ aRet.DirectionX *= -1.0;
+ aRet.DirectionY *= -1.0;
+ aRet.DirectionZ *= -1.0;
+ }
+ return aRet;
+}
+
+uno::Any Stripe::getNormalsPolygon() const
+{
+ drawing::PolyPolygonShape3D aPP;
+
+ aPP.SequenceX.realloc(1);
+ aPP.SequenceY.realloc(1);
+ aPP.SequenceZ.realloc(1);
+
+ drawing::DoubleSequence* pOuterSequenceX = aPP.SequenceX.getArray();
+ drawing::DoubleSequence* pOuterSequenceY = aPP.SequenceY.getArray();
+ drawing::DoubleSequence* pOuterSequenceZ = aPP.SequenceZ.getArray();
+
+ pOuterSequenceX->realloc(4);
+ pOuterSequenceY->realloc(4);
+ pOuterSequenceZ->realloc(4);
+
+ double* pInnerSequenceX = pOuterSequenceX->getArray();
+ double* pInnerSequenceY = pOuterSequenceY->getArray();
+ double* pInnerSequenceZ = pOuterSequenceZ->getArray();
+
+ drawing::Direction3D aNormal( getNormal() );
+
+ for(sal_Int32 nN=4; --nN; )
+ {
+ *pInnerSequenceX++ = aNormal.DirectionX;
+ *pInnerSequenceY++ = aNormal.DirectionY;
+ *pInnerSequenceZ++ = aNormal.DirectionZ;
+ }
+ return uno::Any( &aPP, cppu::UnoType<drawing::PolyPolygonShape3D>::get());
+}
+
+uno::Any Stripe::getTexturePolygon( short nRotatedTexture )
+{
+ drawing::PolyPolygonShape3D aPP;
+
+ aPP.SequenceX.realloc(1);
+ aPP.SequenceY.realloc(1);
+ aPP.SequenceZ.realloc(1);
+
+ drawing::DoubleSequence* pOuterSequenceX = aPP.SequenceX.getArray();
+ drawing::DoubleSequence* pOuterSequenceY = aPP.SequenceY.getArray();
+ drawing::DoubleSequence* pOuterSequenceZ = aPP.SequenceZ.getArray();
+
+ pOuterSequenceX->realloc(4);
+ pOuterSequenceY->realloc(4);
+ pOuterSequenceZ->realloc(4);
+
+ double* pInnerSequenceX = pOuterSequenceX->getArray();
+ double* pInnerSequenceY = pOuterSequenceY->getArray();
+ double* pInnerSequenceZ = pOuterSequenceZ->getArray();
+
+ if( nRotatedTexture==0 )
+ {
+ *pInnerSequenceX++ = 0.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 0.0;
+ *pInnerSequenceY++ = 1.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 1.0;
+ *pInnerSequenceY++ = 1.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 1.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceZ++ = 0.0;
+ }
+ else if( nRotatedTexture==1 )
+ {
+ *pInnerSequenceX++ = 1.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 0.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 0.0;
+ *pInnerSequenceY++ = 1.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 1.0;
+ *pInnerSequenceY++ = 1.0;
+ *pInnerSequenceZ++ = 0.0;
+ }
+ else if( nRotatedTexture==2 )
+ {
+ *pInnerSequenceX++ = 1.0;
+ *pInnerSequenceY++ = 1.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 1.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 0.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 0.0;
+ *pInnerSequenceY++ = 1.0;
+ *pInnerSequenceZ++ = 0.0;
+ }
+ else if( nRotatedTexture==3 )
+ {
+ *pInnerSequenceX++ = 0.0;
+ *pInnerSequenceY++ = 1.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 1.0;
+ *pInnerSequenceY++ = 1.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 1.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 0.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceZ++ = 0.0;
+ }
+ else if( nRotatedTexture==4 )
+ {
+ *pInnerSequenceX++ = 1.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 1.0;
+ *pInnerSequenceY++ = 1.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 0.0;
+ *pInnerSequenceY++ = 1.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 0.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceZ++ = 0.0;
+ }
+ else if( nRotatedTexture==5 )
+ {
+ *pInnerSequenceX++ = 0.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 1.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 1.0;
+ *pInnerSequenceY++ = 1.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 0.0;
+ *pInnerSequenceY++ = 1.0;
+ *pInnerSequenceZ++ = 0.0;
+ }
+ else if( nRotatedTexture==6 )
+ {
+ *pInnerSequenceX++ = 0.0;
+ *pInnerSequenceY++ = 1.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 0.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 1.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 1.0;
+ *pInnerSequenceY++ = 1.0;
+ *pInnerSequenceZ++ = 0.0;
+ }
+ else if( nRotatedTexture==7 )
+ {
+ *pInnerSequenceX++ = 1.0;
+ *pInnerSequenceY++ = 1.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 0.0;
+ *pInnerSequenceY++ = 1.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 0.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 1.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceZ++ = 0.0;
+ }
+
+ return uno::Any( &aPP, cppu::UnoType<drawing::PolyPolygonShape3D>::get());
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/VButton.cxx b/chart2/source/view/main/VButton.cxx
new file mode 100644
index 000000000..25a770fb0
--- /dev/null
+++ b/chart2/source/view/main/VButton.cxx
@@ -0,0 +1,136 @@
+/* -*- 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/.
+ */
+
+#include "VButton.hxx"
+
+#include <ShapeFactory.hxx>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/style/ParagraphAdjust.hpp>
+#include <com/sun/star/drawing/TextVerticalAdjust.hpp>
+#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
+
+#include <CommonConverters.hxx>
+#include <editeng/unoprnms.hxx>
+
+namespace chart
+{
+using namespace css;
+
+VButton::VButton()
+ : m_bShowArrow(true)
+ , m_nArrowColor(0x00000000)
+ , m_nBGColor(0x00E6E6E6)
+{
+}
+
+void VButton::init(const rtl::Reference<SvxShapeGroupAnyD>& xTargetPage)
+{
+ m_xTarget = xTargetPage;
+}
+
+rtl::Reference<SvxShapePolyPolygon> VButton::createTriangle(awt::Size aSize)
+{
+ rtl::Reference<SvxShapePolyPolygon> xShape = new SvxShapePolyPolygon(nullptr);
+ xShape->setShapeKind(SdrObjKind::Polygon);
+
+ drawing::PolyPolygonShape3D aPolyPolygon;
+ aPolyPolygon.SequenceX.realloc(1);
+ aPolyPolygon.SequenceY.realloc(1);
+ aPolyPolygon.SequenceZ.realloc(1);
+
+ drawing::DoubleSequence* pOuterSequenceX = aPolyPolygon.SequenceX.getArray();
+ drawing::DoubleSequence* pOuterSequenceY = aPolyPolygon.SequenceY.getArray();
+ drawing::DoubleSequence* pOuterSequenceZ = aPolyPolygon.SequenceZ.getArray();
+
+ pOuterSequenceX->realloc(3);
+ pOuterSequenceY->realloc(3);
+ pOuterSequenceZ->realloc(3);
+
+ double* pInnerSequenceX = pOuterSequenceX->getArray();
+ double* pInnerSequenceY = pOuterSequenceY->getArray();
+ double* pInnerSequenceZ = pOuterSequenceZ->getArray();
+
+ pInnerSequenceX[0] = 0.0;
+ pInnerSequenceY[0] = 0.0;
+ pInnerSequenceZ[0] = 0.0;
+
+ pInnerSequenceX[1] = aSize.Width / 2.0;
+ pInnerSequenceY[1] = aSize.Height;
+ pInnerSequenceZ[1] = 0.0;
+
+ pInnerSequenceX[2] = aSize.Width;
+ pInnerSequenceY[2] = 0.0;
+ pInnerSequenceZ[2] = 0.0;
+
+ xShape->SvxShape::setPropertyValue("Name", uno::Any(m_sCID));
+ xShape->SvxShape::setPropertyValue(UNO_NAME_POLYPOLYGON,
+ uno::Any(PolyToPointSequence(aPolyPolygon)));
+ xShape->SvxShape::setPropertyValue("LineStyle", uno::Any(drawing::LineStyle_NONE));
+ xShape->SvxShape::setPropertyValue("FillColor", uno::Any(m_nArrowColor));
+
+ return xShape;
+}
+
+void VButton::createShapes(const uno::Reference<beans::XPropertySet>& xTextProp)
+{
+ tNameSequence aPropNames;
+ tAnySequence aPropValues;
+
+ PropertyMapper::getTextLabelMultiPropertyLists(xTextProp, aPropNames, aPropValues);
+
+ m_xShape = ShapeFactory::createGroup2D(m_xTarget, m_sCID);
+ m_xShape->setPosition(m_aPosition);
+ m_xShape->setSize(m_aSize);
+
+ rtl::Reference<SvxShapeGroupAnyD> xContainer = m_xShape;
+
+ tPropertyNameValueMap aTextValueMap;
+ aTextValueMap["CharHeight"] <<= 10.0f;
+ aTextValueMap["CharHeightAsian"] <<= 10.0f;
+ aTextValueMap["CharHeightComplex"] <<= 10.0f;
+ aTextValueMap["FillColor"] <<= m_nBGColor;
+ aTextValueMap["FillStyle"] <<= drawing::FillStyle_SOLID;
+ aTextValueMap["LineColor"] <<= sal_Int32(0xcccccc);
+ aTextValueMap["LineStyle"] <<= drawing::LineStyle_SOLID;
+ aTextValueMap["ParaAdjust"] <<= style::ParagraphAdjust_CENTER;
+ aTextValueMap["TextHorizontalAdjust"] <<= drawing::TextHorizontalAdjust_LEFT;
+ aTextValueMap["TextVerticalAdjust"] <<= drawing::TextVerticalAdjust_CENTER;
+ aTextValueMap["ParaLeftMargin"] <<= sal_Int32(100);
+ aTextValueMap["ParaRightMargin"] <<= sal_Int32(600);
+
+ aTextValueMap["Name"] <<= m_sCID; //CID OUString
+
+ PropertyMapper::getMultiPropertyListsFromValueMap(aPropNames, aPropValues, aTextValueMap);
+
+ rtl::Reference<SvxShapeText> xEntry
+ = ShapeFactory::createText(xContainer, m_sLabel, aPropNames, aPropValues, uno::Any());
+
+ if (xEntry.is())
+ {
+ xEntry->setPosition(m_aPosition);
+ xEntry->setSize(m_aSize);
+ }
+
+ if (!m_bShowArrow)
+ return;
+
+ awt::Size aPolySize{ 280, 180 };
+
+ rtl::Reference<SvxShapePolyPolygon> xPoly = createTriangle(aPolySize);
+ xPoly->setSize(aPolySize);
+ xPoly->setPosition(
+ { sal_Int32(m_aPosition.X + m_aSize.Width - aPolySize.Width - 100),
+ sal_Int32(m_aPosition.Y + (m_aSize.Height / 2.0) - (aPolySize.Height / 2.0)) });
+ xContainer->add(xPoly);
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/VButton.hxx b/chart2/source/view/main/VButton.hxx
new file mode 100644
index 000000000..87017f369
--- /dev/null
+++ b/chart2/source/view/main/VButton.hxx
@@ -0,0 +1,86 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <tools/color.hxx>
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <rtl/ref.hxx>
+#include <svx/unoshape.hxx>
+
+namespace com::sun::star::beans { class XPropertySet; }
+namespace com::sun::star::drawing { class XShape; }
+namespace com::sun::star::drawing { class XShapes; }
+namespace com::sun::star::lang { class XMultiServiceFactory; }
+
+namespace chart
+{
+
+class VButton final
+{
+private:
+ rtl::Reference<SvxShapeGroupAnyD> m_xTarget;
+ rtl::Reference<SvxShapeGroup> m_xShape;
+ OUString m_sLabel;
+ OUString m_sCID;
+ css::awt::Point m_aPosition;
+ css::awt::Size m_aSize;
+ bool m_bShowArrow;
+ Color m_nArrowColor;
+ Color m_nBGColor;
+
+ rtl::Reference<SvxShapePolyPolygon>
+ createTriangle(css::awt::Size aSize);
+
+public:
+ VButton();
+
+ void init(const rtl::Reference<SvxShapeGroupAnyD>& xTargetPage);
+
+ void createShapes(const css::uno::Reference<css::beans::XPropertySet>& xTextProp);
+
+ void showArrow(bool bShowArrow)
+ {
+ m_bShowArrow = bShowArrow;
+ }
+ void setArrowColor(Color nArrowColor)
+ {
+ m_nArrowColor = nArrowColor;
+ }
+ void setBGColor(Color nBGColor)
+ {
+ m_nBGColor = nBGColor;
+ }
+ void setLabel(OUString const & rLabel)
+ {
+ m_sLabel = rLabel;
+ }
+ void setCID(OUString const & rCID)
+ {
+ m_sCID = rCID;
+ }
+ void setPosition(css::awt::Point const & rPosition)
+ {
+ m_aPosition = rPosition;
+ }
+ css::awt::Size const & getSize() const
+ {
+ return m_aSize;
+ }
+ void setSize(css::awt::Size const & rSize)
+ {
+ m_aSize = rSize;
+ }
+};
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/VDataSeries.cxx b/chart2/source/view/main/VDataSeries.cxx
new file mode 100644
index 000000000..3fb12ebca
--- /dev/null
+++ b/chart2/source/view/main/VDataSeries.cxx
@@ -0,0 +1,1118 @@
+/* -*- 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 <limits>
+#include <memory>
+#include <VDataSeries.hxx>
+#include <DataSeries.hxx>
+#include <ObjectIdentifier.hxx>
+#include <CommonConverters.hxx>
+#include <LabelPositionHelper.hxx>
+#include <ChartType.hxx>
+#include <ChartTypeHelper.hxx>
+#include <RegressionCurveHelper.hxx>
+#include <unonames.hxx>
+
+#include <com/sun/star/chart/MissingValueTreatment.hpp>
+#include <com/sun/star/chart2/DataPointLabel.hpp>
+#include <com/sun/star/chart2/Symbol.hpp>
+#include <com/sun/star/chart2/XRegressionCurveCalculator.hpp>
+#include <com/sun/star/chart2/RelativePosition.hpp>
+
+#include <osl/diagnose.h>
+#include <tools/color.hxx>
+#include <tools/diagnose_ex.h>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
+
+namespace chart {
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+using ::com::sun::star::uno::Reference;
+
+void VDataSequence::init( const uno::Reference< data::XDataSequence >& xModel )
+{
+ m_xModel = xModel;
+ m_aValues = DataSequenceToDoubleSequence( xModel );
+}
+
+bool VDataSequence::is() const
+{
+ return m_xModel.is();
+}
+void VDataSequence::clear()
+{
+ m_xModel = nullptr;
+ m_aValues.realloc(0);
+}
+
+double VDataSequence::getValue( sal_Int32 index ) const
+{
+ if( 0<=index && index<m_aValues.getLength() )
+ return m_aValues[index];
+ return std::numeric_limits<double>::quiet_NaN();
+}
+
+sal_Int32 VDataSequence::detectNumberFormatKey( sal_Int32 index ) const
+{
+ sal_Int32 nNumberFormatKey = -1;
+
+ // -1 is allowed and means a key for the whole sequence
+ if( -1<=index && index<m_aValues.getLength() && m_xModel.is())
+ {
+ nNumberFormatKey = m_xModel->getNumberFormatKeyByIndex( index );
+ }
+
+ return nNumberFormatKey;
+}
+
+sal_Int32 VDataSequence::getLength() const
+{
+ return m_aValues.getLength();
+}
+
+namespace
+{
+struct lcl_LessXOfPoint
+{
+ bool operator() ( const std::vector< double >& first,
+ const std::vector< double >& second )
+ {
+ if( !first.empty() && !second.empty() )
+ {
+ return first[0]<second[0];
+ }
+ return false;
+ }
+};
+
+void lcl_clearIfNoValuesButTextIsContained( VDataSequence& rData, const uno::Reference<data::XDataSequence>& xDataSequence )
+{
+ //#i71686#, #i101968#, #i102428#
+ sal_Int32 nCount = rData.m_aValues.getLength();
+ for( sal_Int32 i = 0; i < nCount; ++i )
+ {
+ if( !std::isnan( rData.m_aValues[i] ) )
+ return;
+ }
+ //no double value is contained
+ //is there any text?
+ uno::Sequence< OUString > aStrings( DataSequenceToStringSequence( xDataSequence ) );
+ sal_Int32 nTextCount = aStrings.getLength();
+ for( sal_Int32 j = 0; j < nTextCount; ++j )
+ {
+ if( !aStrings[j].isEmpty() )
+ {
+ rData.clear();
+ return;
+ }
+ }
+ //no content at all
+}
+
+void lcl_maybeReplaceNanWithZero( double& rfValue, sal_Int32 nMissingValueTreatment )
+{
+ if( nMissingValueTreatment == css::chart::MissingValueTreatment::USE_ZERO
+ && (std::isnan(rfValue) || std::isinf(rfValue)) )
+ rfValue = 0.0;
+}
+
+}
+
+VDataSeries::VDataSeries( const rtl::Reference< DataSeries >& xDataSeries )
+ : m_nPolygonIndex(0)
+ , m_fLogicMinX(0.0)
+ , m_fLogicMaxX(0.0)
+ , m_fLogicZPos(0.0)
+ , m_xDataSeries(xDataSeries)
+ , m_nPointCount(0)
+ , m_pValueSequenceForDataLabelNumberFormatDetection(&m_aValues_Y)
+ , m_fXMeanValue(std::numeric_limits<double>::quiet_NaN())
+ , m_fYMeanValue(std::numeric_limits<double>::quiet_NaN())
+ , m_eStackingDirection(StackingDirection_NO_STACKING)
+ , m_nAxisIndex(0)
+ , m_bConnectBars(false)
+ , m_bGroupBarsPerAxis(true)
+ , m_nStartingAngle(90)
+ , m_nGlobalSeriesIndex(0)
+ , m_nCurrentAttributedPoint(-1)
+ , m_nMissingValueTreatment(css::chart::MissingValueTreatment::LEAVE_GAP)
+ , m_bAllowPercentValueInDataLabel(false)
+ , mpOldSeries(nullptr)
+ , mnPercent(0.0)
+{
+ m_xDataSeriesProps = m_xDataSeries;
+
+ const std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > & aDataSequences =
+ m_xDataSeries->getDataSequences2();
+
+ for(sal_Int32 nN = aDataSequences.size();nN--;)
+ {
+ uno::Reference<data::XDataSequence> xDataSequence( aDataSequences[nN]->getValues());
+ uno::Reference<beans::XPropertySet> xProp(xDataSequence, uno::UNO_QUERY );
+ if( xProp.is())
+ {
+ try
+ {
+ uno::Any aARole = xProp->getPropertyValue("Role");
+ OUString aRole;
+ aARole >>= aRole;
+
+ if (aRole == "values-x")
+ {
+ m_aValues_X.init( xDataSequence );
+ lcl_clearIfNoValuesButTextIsContained( m_aValues_X, xDataSequence );
+ }
+ else if (aRole =="values-y")
+ m_aValues_Y.init( xDataSequence );
+ else if (aRole == "values-min")
+ m_aValues_Y_Min.init( xDataSequence );
+ else if (aRole == "values-max")
+ m_aValues_Y_Max.init( xDataSequence );
+ else if (aRole == "values-first")
+ m_aValues_Y_First.init( xDataSequence );
+ else if (aRole == "values-last")
+ m_aValues_Y_Last.init( xDataSequence );
+ else if (aRole == "values-size")
+ m_aValues_Bubble_Size.init( xDataSequence );
+ else
+ {
+ VDataSequence aSequence;
+ aSequence.init(xDataSequence);
+ m_PropertyMap.insert(std::make_pair(aRole, aSequence));
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ }
+ }
+
+ //determine the point count
+ m_nPointCount = m_aValues_Y.getLength();
+ {
+ if( m_nPointCount < m_aValues_Bubble_Size.getLength() )
+ m_nPointCount = m_aValues_Bubble_Size.getLength();
+ if( m_nPointCount < m_aValues_Y_Min.getLength() )
+ m_nPointCount = m_aValues_Y_Min.getLength();
+ if( m_nPointCount < m_aValues_Y_Max.getLength() )
+ m_nPointCount = m_aValues_Y_Max.getLength();
+ if( m_nPointCount < m_aValues_Y_First.getLength() )
+ m_nPointCount = m_aValues_Y_First.getLength();
+ if( m_nPointCount < m_aValues_Y_Last.getLength() )
+ m_nPointCount = m_aValues_Y_Last.getLength();
+ }
+
+ if( !xDataSeries.is())
+ return;
+
+ try
+ {
+ //get AttributedDataPoints
+ xDataSeries->getPropertyValue("AttributedDataPoints") >>= m_aAttributedDataPointIndexList;
+
+ xDataSeries->getPropertyValue("StackingDirection") >>= m_eStackingDirection;
+
+ xDataSeries->getPropertyValue("AttachedAxisIndex") >>= m_nAxisIndex;
+ if(m_nAxisIndex<0)
+ m_nAxisIndex=0;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+}
+
+VDataSeries::~VDataSeries()
+{
+}
+
+void VDataSeries::doSortByXValues()
+{
+ if( !(m_aValues_X.is() && m_aValues_X.m_aValues.hasElements()) )
+ return;
+
+ //prepare a vector for sorting
+ std::vector< std::vector< double > > aTmp;//outer vector are points, inner vector are the different values of the point
+ sal_Int32 nPointIndex = 0;
+ for( nPointIndex=0; nPointIndex < m_nPointCount; nPointIndex++ )
+ {
+ aTmp.push_back(
+ { ((nPointIndex < m_aValues_X.m_aValues.getLength()) ? m_aValues_X.m_aValues[nPointIndex]
+ : std::numeric_limits<double>::quiet_NaN()),
+ ((nPointIndex < m_aValues_Y.m_aValues.getLength()) ? m_aValues_Y.m_aValues[nPointIndex]
+ : std::numeric_limits<double>::quiet_NaN())
+ }
+ );
+ }
+
+ //do sort
+ std::stable_sort( aTmp.begin(), aTmp.end(), lcl_LessXOfPoint() );
+
+ //fill the sorted points back to the members
+ m_aValues_X.m_aValues.realloc( m_nPointCount );
+ auto pDoublesX = m_aValues_X.m_aValues.getArray();
+ m_aValues_Y.m_aValues.realloc( m_nPointCount );
+ auto pDoublesY = m_aValues_Y.m_aValues.getArray();
+
+ for( nPointIndex=0; nPointIndex < m_nPointCount; nPointIndex++ )
+ {
+ pDoublesX[nPointIndex]=aTmp[nPointIndex][0];
+ pDoublesY[nPointIndex]=aTmp[nPointIndex][1];
+ }
+}
+
+void VDataSeries::releaseShapes()
+{
+ m_xGroupShape.set(nullptr);
+ m_xLabelsGroupShape.set(nullptr);
+ m_xErrorXBarsGroupShape.set(nullptr);
+ m_xErrorYBarsGroupShape.set(nullptr);
+ m_xFrontSubGroupShape.set(nullptr);
+ m_xBackSubGroupShape.set(nullptr);
+
+ m_aPolyPolygonShape3D.clear();
+ m_nPolygonIndex = 0;
+}
+
+const rtl::Reference<::chart::DataSeries>& VDataSeries::getModel() const
+{
+ return m_xDataSeries;
+}
+
+void VDataSeries::setCategoryXAxis()
+{
+ m_aValues_X.clear();
+ m_bAllowPercentValueInDataLabel = true;
+}
+
+void VDataSeries::setXValues( const Reference< chart2::data::XDataSequence >& xValues )
+{
+ m_aValues_X.clear();
+ m_aValues_X.init( xValues );
+ m_bAllowPercentValueInDataLabel = true;
+}
+
+void VDataSeries::setXValuesIfNone( const Reference< chart2::data::XDataSequence >& xValues )
+{
+ if( m_aValues_X.is() )
+ return;
+
+ m_aValues_X.init( xValues );
+ lcl_clearIfNoValuesButTextIsContained( m_aValues_X, xValues );
+}
+
+void VDataSeries::setGlobalSeriesIndex( sal_Int32 nGlobalSeriesIndex )
+{
+ m_nGlobalSeriesIndex = nGlobalSeriesIndex;
+}
+
+void VDataSeries::setParticle( const OUString& rSeriesParticle )
+{
+ m_aSeriesParticle = rSeriesParticle;
+
+ //get CID
+ m_aCID = ObjectIdentifier::createClassifiedIdentifierForParticle( m_aSeriesParticle );
+ m_aPointCID_Stub = ObjectIdentifier::createSeriesSubObjectStub( OBJECTTYPE_DATA_POINT, m_aSeriesParticle );
+
+ m_aLabelCID_Stub = ObjectIdentifier::createClassifiedIdentifierWithParent(
+ OBJECTTYPE_DATA_LABEL, u"", getLabelsCID() );
+}
+OUString VDataSeries::getErrorBarsCID(bool bYError) const
+{
+ OUString aChildParticle( ObjectIdentifier::getStringForType(
+ bYError ? OBJECTTYPE_DATA_ERRORS_Y : OBJECTTYPE_DATA_ERRORS_X )
+ + "=" );
+
+ return ObjectIdentifier::createClassifiedIdentifierForParticles(
+ m_aSeriesParticle, aChildParticle );
+}
+OUString VDataSeries::getLabelsCID() const
+{
+ OUString aChildParticle( ObjectIdentifier::getStringForType( OBJECTTYPE_DATA_LABELS ) + "=" );
+
+ return ObjectIdentifier::createClassifiedIdentifierForParticles(
+ m_aSeriesParticle, aChildParticle );
+}
+OUString VDataSeries::getDataCurveCID( sal_Int32 nCurveIndex, bool bAverageLine ) const
+{
+ return ObjectIdentifier::createDataCurveCID( m_aSeriesParticle, nCurveIndex, bAverageLine );
+}
+
+OUString VDataSeries::getDataCurveEquationCID( sal_Int32 nCurveIndex ) const
+{
+ return ObjectIdentifier::createDataCurveEquationCID( m_aSeriesParticle, nCurveIndex );
+}
+void VDataSeries::setPageReferenceSize( const awt::Size & rPageRefSize )
+{
+ m_aReferenceSize = rPageRefSize;
+}
+
+void VDataSeries::setConnectBars( bool bConnectBars )
+{
+ m_bConnectBars = bConnectBars;
+}
+
+bool VDataSeries::getConnectBars() const
+{
+ return m_bConnectBars;
+}
+
+void VDataSeries::setGroupBarsPerAxis( bool bGroupBarsPerAxis )
+{
+ m_bGroupBarsPerAxis = bGroupBarsPerAxis;
+}
+
+bool VDataSeries::getGroupBarsPerAxis() const
+{
+ return m_bGroupBarsPerAxis;
+}
+
+void VDataSeries::setStartingAngle( sal_Int32 nStartingAngle )
+{
+ m_nStartingAngle = nStartingAngle;
+}
+
+sal_Int32 VDataSeries::getStartingAngle() const
+{
+ return m_nStartingAngle;
+}
+
+chart2::StackingDirection VDataSeries::getStackingDirection() const
+{
+ return m_eStackingDirection;
+}
+
+sal_Int32 VDataSeries::getAttachedAxisIndex() const
+{
+ return m_nAxisIndex;
+}
+
+void VDataSeries::setAttachedAxisIndex( sal_Int32 nAttachedAxisIndex )
+{
+ if( nAttachedAxisIndex < 0 )
+ nAttachedAxisIndex = 0;
+ m_nAxisIndex = nAttachedAxisIndex;
+}
+
+double VDataSeries::getXValue( sal_Int32 index ) const
+{
+ double fRet = std::numeric_limits<double>::quiet_NaN();
+ if(m_aValues_X.is())
+ {
+ if( 0<=index && index<m_aValues_X.getLength() )
+ {
+ fRet = m_aValues_X.m_aValues[index];
+ if(mpOldSeries && index < mpOldSeries->m_aValues_X.getLength())
+ {
+ double nOldVal = mpOldSeries->m_aValues_X.m_aValues[index];
+ fRet = nOldVal + (fRet - nOldVal) * mnPercent;
+ }
+ }
+ }
+ else
+ {
+ // #i70133# always return correct X position - needed for short data series
+ if( 0<=index /*&& index < m_nPointCount*/ )
+ fRet = index+1;//first category (index 0) matches with real number 1.0
+ }
+ lcl_maybeReplaceNanWithZero( fRet, getMissingValueTreatment() );
+ return fRet;
+}
+
+double VDataSeries::getYValue( sal_Int32 index ) const
+{
+ double fRet = std::numeric_limits<double>::quiet_NaN();
+ if(m_aValues_Y.is())
+ {
+ if( 0<=index && index<m_aValues_Y.getLength() )
+ {
+ fRet = m_aValues_Y.m_aValues[index];
+ if(mpOldSeries && index < mpOldSeries->m_aValues_Y.getLength())
+ {
+ double nOldVal = mpOldSeries->m_aValues_Y.m_aValues[index];
+ fRet = nOldVal + (fRet - nOldVal) * mnPercent;
+ }
+ }
+ }
+ else
+ {
+ // #i70133# always return correct X position - needed for short data series
+ if( 0<=index /*&& index < m_nPointCount*/ )
+ fRet = index+1;//first category (index 0) matches with real number 1.0
+ }
+ lcl_maybeReplaceNanWithZero( fRet, getMissingValueTreatment() );
+ return fRet;
+}
+
+void VDataSeries::getMinMaxXValue(double& fMin, double& fMax) const
+{
+ fMax = std::numeric_limits<double>::quiet_NaN();
+ fMin = std::numeric_limits<double>::quiet_NaN();
+
+ uno::Sequence< double > aValuesX = getAllX();
+
+ if(!aValuesX.hasElements())
+ return;
+
+ sal_Int32 i = 0;
+ while ( i < aValuesX.getLength() && std::isnan(aValuesX[i]) )
+ i++;
+ if ( i < aValuesX.getLength() )
+ fMax = fMin = aValuesX[i++];
+
+ for ( ; i < aValuesX.getLength(); i++)
+ {
+ const double aValue = aValuesX[i];
+ if ( aValue > fMax)
+ {
+ fMax = aValue;
+ }
+ else if ( aValue < fMin)
+ {
+ fMin = aValue;
+ }
+ }
+}
+double VDataSeries::getY_Min( sal_Int32 index ) const
+{
+ return m_aValues_Y_Min.getValue( index );
+}
+double VDataSeries::getY_Max( sal_Int32 index ) const
+{
+ return m_aValues_Y_Max.getValue( index );
+}
+double VDataSeries::getY_First( sal_Int32 index ) const
+{
+ return m_aValues_Y_First.getValue( index );
+}
+double VDataSeries::getY_Last( sal_Int32 index ) const
+{
+ return m_aValues_Y_Last.getValue( index );
+}
+double VDataSeries::getBubble_Size( sal_Int32 index ) const
+{
+ double nNewVal = m_aValues_Bubble_Size.getValue( index );
+ if(mpOldSeries && index < mpOldSeries->m_aValues_Bubble_Size.getLength())
+ {
+ double nOldVal = mpOldSeries->m_aValues_Bubble_Size.getValue( index );
+ nNewVal = nOldVal + (nNewVal - nOldVal) * mnPercent;
+ }
+
+ return nNewVal;
+}
+
+bool VDataSeries::hasExplicitNumberFormat( sal_Int32 nPointIndex, bool bForPercentage ) const
+{
+ OUString aPropName = bForPercentage ? OUString("PercentageNumberFormat") : OUString(CHART_UNONAME_NUMFMT);
+ bool bHasNumberFormat = false;
+ bool bLinkToSource = true;
+ uno::Reference< beans::XPropertySet > xPointProp( getPropertiesOfPoint( nPointIndex ));
+ if( xPointProp.is() && (xPointProp->getPropertyValue(CHART_UNONAME_LINK_TO_SRC_NUMFMT) >>= bLinkToSource))
+ {
+ sal_Int32 nNumberFormat = -1;
+ if( !bLinkToSource && (xPointProp->getPropertyValue(aPropName) >>= nNumberFormat))
+ bHasNumberFormat = true;
+ }
+ return bHasNumberFormat;
+}
+sal_Int32 VDataSeries::getExplicitNumberFormat( sal_Int32 nPointIndex, bool bForPercentage ) const
+{
+ OUString aPropName = bForPercentage ? OUString("PercentageNumberFormat") : OUString(CHART_UNONAME_NUMFMT);
+ sal_Int32 nNumberFormat = -1;
+ uno::Reference< beans::XPropertySet > xPointProp( getPropertiesOfPoint( nPointIndex ));
+ if( xPointProp.is() )
+ xPointProp->getPropertyValue(aPropName) >>= nNumberFormat;
+ return nNumberFormat;
+}
+void VDataSeries::setRoleOfSequenceForDataLabelNumberFormatDetection( std::u16string_view rRole )
+{
+ if (rRole == u"values-y")
+ m_pValueSequenceForDataLabelNumberFormatDetection = &m_aValues_Y;
+ else if (rRole == u"values-size")
+ m_pValueSequenceForDataLabelNumberFormatDetection = &m_aValues_Bubble_Size;
+ else if (rRole == u"values-min")
+ m_pValueSequenceForDataLabelNumberFormatDetection = &m_aValues_Y_Min;
+ else if (rRole == u"values-max")
+ m_pValueSequenceForDataLabelNumberFormatDetection = &m_aValues_Y_Max;
+ else if (rRole == u"values-first")
+ m_pValueSequenceForDataLabelNumberFormatDetection = &m_aValues_Y_First;
+ else if (rRole == u"values-last")
+ m_pValueSequenceForDataLabelNumberFormatDetection = &m_aValues_Y_Last;
+ else if (rRole == u"values-x")
+ m_pValueSequenceForDataLabelNumberFormatDetection = &m_aValues_X;
+}
+sal_Int32 VDataSeries::detectNumberFormatKey( sal_Int32 index ) const
+{
+ sal_Int32 nRet = 0;
+ if( m_pValueSequenceForDataLabelNumberFormatDetection )
+ nRet = m_pValueSequenceForDataLabelNumberFormatDetection->detectNumberFormatKey( index );
+ return nRet;
+}
+
+sal_Int32 VDataSeries::getLabelPlacement( sal_Int32 nPointIndex, const rtl::Reference< ChartType >& xChartType, bool bSwapXAndY ) const
+{
+ sal_Int32 nLabelPlacement=0;
+ try
+ {
+ uno::Reference< beans::XPropertySet > xPointProps( getPropertiesOfPoint( nPointIndex ) );
+ if( xPointProps.is() )
+ xPointProps->getPropertyValue("LabelPlacement") >>= nLabelPlacement;
+
+ const uno::Sequence < sal_Int32 > aAvailablePlacements( ChartTypeHelper::getSupportedLabelPlacements(
+ xChartType, bSwapXAndY, m_xDataSeries ) );
+
+ for( sal_Int32 n : aAvailablePlacements )
+ if( n == nLabelPlacement )
+ return nLabelPlacement; //ok
+
+ //otherwise use the first supported one
+ if( aAvailablePlacements.hasElements() )
+ {
+ nLabelPlacement = aAvailablePlacements[0];
+ if( xPointProps.is() )
+ xPointProps->setPropertyValue("LabelPlacement", uno::Any(nLabelPlacement));
+ return nLabelPlacement;
+ }
+
+ OSL_FAIL("no label placement supported");
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return nLabelPlacement;
+}
+
+awt::Point VDataSeries::getLabelPosition( awt::Point aTextShapePos, sal_Int32 nPointIndex ) const
+{
+ awt::Point aPos(-1, -1);
+ try
+ {
+ RelativePosition aCustomLabelPosition;
+ uno::Reference< beans::XPropertySet > xPointProps(getPropertiesOfPoint(nPointIndex));
+ if( xPointProps.is() && (xPointProps->getPropertyValue("CustomLabelPosition") >>= aCustomLabelPosition))
+ {
+ aPos.X = static_cast<sal_Int32>(aCustomLabelPosition.Primary * m_aReferenceSize.Width) + aTextShapePos.X;
+ aPos.Y = static_cast<sal_Int32>(aCustomLabelPosition.Secondary * m_aReferenceSize.Height) + aTextShapePos.Y;
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "");
+ }
+ return aPos;
+}
+
+bool VDataSeries::isLabelCustomPos(sal_Int32 nPointIndex) const
+{
+ bool bCustom = false;
+ try
+ {
+ if( isAttributedDataPoint(nPointIndex) )
+ {
+ uno::Reference< beans::XPropertySet > xPointProps(m_xDataSeries->getDataPointByIndex(nPointIndex));
+ RelativePosition aCustomLabelPosition;
+ if( xPointProps.is() && (xPointProps->getPropertyValue("CustomLabelPosition") >>= aCustomLabelPosition) )
+ bCustom = true;
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "");
+ }
+ return bCustom;
+}
+
+double VDataSeries::getMinimumofAllDifferentYValues( sal_Int32 index ) const
+{
+ double fMin = std::numeric_limits<double>::infinity();
+
+ if( !m_aValues_Y.is() &&
+ (m_aValues_Y_Min.is() || m_aValues_Y_Max.is()
+ || m_aValues_Y_First.is() || m_aValues_Y_Last.is() ) )
+ {
+ double fY_Min = getY_Min( index );
+ double fY_Max = getY_Max( index );
+ double fY_First = getY_First( index );
+ double fY_Last = getY_Last( index );
+
+ if(fMin>fY_First)
+ fMin=fY_First;
+ if(fMin>fY_Last)
+ fMin=fY_Last;
+ if(fMin>fY_Min)
+ fMin=fY_Min;
+ if(fMin>fY_Max)
+ fMin=fY_Max;
+ }
+ else
+ {
+ double fY = getYValue( index );
+ if(fMin>fY)
+ fMin=fY;
+ }
+
+ if( std::isinf(fMin) )
+ return std::numeric_limits<double>::quiet_NaN();
+
+ return fMin;
+}
+
+double VDataSeries::getMaximumofAllDifferentYValues( sal_Int32 index ) const
+{
+ double fMax = -std::numeric_limits<double>::infinity();
+
+ if( !m_aValues_Y.is() &&
+ (m_aValues_Y_Min.is() || m_aValues_Y_Max.is()
+ || m_aValues_Y_First.is() || m_aValues_Y_Last.is() ) )
+ {
+ double fY_Min = getY_Min( index );
+ double fY_Max = getY_Max( index );
+ double fY_First = getY_First( index );
+ double fY_Last = getY_Last( index );
+
+ if(fMax<fY_First)
+ fMax=fY_First;
+ if(fMax<fY_Last)
+ fMax=fY_Last;
+ if(fMax<fY_Min)
+ fMax=fY_Min;
+ if(fMax<fY_Max)
+ fMax=fY_Max;
+ }
+ else
+ {
+ double fY = getYValue( index );
+ if(fMax<fY)
+ fMax=fY;
+ }
+
+ if( std::isinf(fMax) )
+ return std::numeric_limits<double>::quiet_NaN();
+
+ return fMax;
+}
+
+uno::Sequence< double > const & VDataSeries::getAllX() const
+{
+ if(!m_aValues_X.is() && !m_aValues_X.getLength() && m_nPointCount)
+ {
+ //init x values from category indexes
+ //first category (index 0) matches with real number 1.0
+ m_aValues_X.m_aValues.realloc( m_nPointCount );
+ auto pDoubles = m_aValues_X.m_aValues.getArray();
+ for(sal_Int32 nN=m_aValues_X.getLength();nN--;)
+ pDoubles[nN] = nN+1;
+ }
+ return m_aValues_X.m_aValues;
+}
+
+uno::Sequence< double > const & VDataSeries::getAllY() const
+{
+ if(!m_aValues_Y.is() && !m_aValues_Y.getLength() && m_nPointCount)
+ {
+ //init y values from indexes
+ //first y-value (index 0) matches with real number 1.0
+ m_aValues_Y.m_aValues.realloc( m_nPointCount );
+ auto pDoubles = m_aValues_Y.m_aValues.getArray();
+ for(sal_Int32 nN=m_aValues_Y.getLength();nN--;)
+ pDoubles[nN] = nN+1;
+ }
+ return m_aValues_Y.m_aValues;
+}
+
+double VDataSeries::getXMeanValue() const
+{
+ if( std::isnan( m_fXMeanValue ) )
+ {
+ uno::Reference< XRegressionCurveCalculator > xCalculator( RegressionCurveHelper::createRegressionCurveCalculatorByServiceName( u"com.sun.star.chart2.MeanValueRegressionCurve" ) );
+ uno::Sequence< double > aXValuesDummy;
+ xCalculator->recalculateRegression( aXValuesDummy, getAllX() );
+ m_fXMeanValue = xCalculator->getCurveValue( 1.0 );
+ }
+ return m_fXMeanValue;
+}
+
+double VDataSeries::getYMeanValue() const
+{
+ if( std::isnan( m_fYMeanValue ) )
+ {
+ uno::Reference< XRegressionCurveCalculator > xCalculator(
+ RegressionCurveHelper::createRegressionCurveCalculatorByServiceName(u"com.sun.star.chart2.MeanValueRegressionCurve"));
+ uno::Sequence< double > aXValuesDummy;
+ xCalculator->recalculateRegression( aXValuesDummy, getAllY() );
+ m_fYMeanValue = xCalculator->getCurveValue( 1.0 );
+ }
+ return m_fYMeanValue;
+}
+
+static std::unique_ptr<Symbol> getSymbolPropertiesFromPropertySet( const uno::Reference< beans::XPropertySet >& xProp )
+{
+ std::unique_ptr< Symbol > apSymbolProps( new Symbol() );
+ try
+ {
+ if( xProp->getPropertyValue("Symbol") >>= *apSymbolProps )
+ {
+ //use main color to fill symbols
+ xProp->getPropertyValue("Color") >>= apSymbolProps->FillColor;
+ // border of symbols always same as fill color
+ apSymbolProps->BorderColor = apSymbolProps->FillColor;
+ }
+ else
+ apSymbolProps.reset();
+ }
+ catch(const uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return apSymbolProps;
+}
+
+Symbol* VDataSeries::getSymbolProperties( sal_Int32 index ) const
+{
+ Symbol* pRet=nullptr;
+ if( isAttributedDataPoint( index ) )
+ {
+ adaptPointCache( index );
+ if (!m_apSymbolProperties_AttributedPoint)
+ m_apSymbolProperties_AttributedPoint
+ = getSymbolPropertiesFromPropertySet(getPropertiesOfPoint(index));
+ pRet = m_apSymbolProperties_AttributedPoint.get();
+ //if a single data point does not have symbols but the dataseries itself has symbols
+ //we create an invisible symbol shape to enable selection of that point
+ if( !pRet || pRet->Style == SymbolStyle_NONE )
+ {
+ if (!m_apSymbolProperties_Series)
+ m_apSymbolProperties_Series
+ = getSymbolPropertiesFromPropertySet(getPropertiesOfSeries());
+ if( m_apSymbolProperties_Series && m_apSymbolProperties_Series->Style != SymbolStyle_NONE )
+ {
+ if (!m_apSymbolProperties_InvisibleSymbolForSelection)
+ {
+ m_apSymbolProperties_InvisibleSymbolForSelection.reset(new Symbol);
+ m_apSymbolProperties_InvisibleSymbolForSelection->Style = SymbolStyle_STANDARD;
+ m_apSymbolProperties_InvisibleSymbolForSelection->StandardSymbol = 0;//square
+ m_apSymbolProperties_InvisibleSymbolForSelection->Size = com::sun::star::awt::Size(0, 0);//tdf#126033
+ m_apSymbolProperties_InvisibleSymbolForSelection->BorderColor = 0xff000000;//invisible
+ m_apSymbolProperties_InvisibleSymbolForSelection->FillColor = 0xff000000;//invisible
+ }
+ pRet = m_apSymbolProperties_InvisibleSymbolForSelection.get();
+ }
+ }
+ }
+ else
+ {
+ if (!m_apSymbolProperties_Series)
+ m_apSymbolProperties_Series
+ = getSymbolPropertiesFromPropertySet(getPropertiesOfSeries());
+ pRet = m_apSymbolProperties_Series.get();
+ }
+
+ if( pRet && pRet->Style == SymbolStyle_AUTO )
+ {
+ pRet->Style = SymbolStyle_STANDARD;
+
+ sal_Int32 nIndex = m_nGlobalSeriesIndex;
+ if(m_aValues_X.is())
+ nIndex++;
+ pRet->StandardSymbol = nIndex;
+ }
+
+ return pRet;
+}
+
+uno::Reference< beans::XPropertySet > VDataSeries::getXErrorBarProperties( sal_Int32 index ) const
+{
+ uno::Reference< beans::XPropertySet > xErrorBarProp;
+
+ uno::Reference< beans::XPropertySet > xPointProp( getPropertiesOfPoint( index ));
+ if( xPointProp.is() )
+ xPointProp->getPropertyValue(CHART_UNONAME_ERRORBAR_X) >>= xErrorBarProp;
+ return xErrorBarProp;
+}
+
+uno::Reference< beans::XPropertySet > VDataSeries::getYErrorBarProperties( sal_Int32 index ) const
+{
+ uno::Reference< beans::XPropertySet > xErrorBarProp;
+
+ uno::Reference< beans::XPropertySet > xPointProp( getPropertiesOfPoint( index ));
+ if( xPointProp.is() )
+ xPointProp->getPropertyValue(CHART_UNONAME_ERRORBAR_Y) >>= xErrorBarProp;
+ return xErrorBarProp;
+}
+
+bool VDataSeries::hasPointOwnColor( sal_Int32 index ) const
+{
+ if( !isAttributedDataPoint(index) )
+ return false;
+
+ try
+ {
+ uno::Reference< beans::XPropertyState > xPointState( getPropertiesOfPoint(index), uno::UNO_QUERY_THROW );
+ return (xPointState->getPropertyState("Color") != beans::PropertyState_DEFAULT_VALUE );
+ }
+ catch(const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return false;
+}
+
+bool VDataSeries::isAttributedDataPoint( sal_Int32 index ) const
+{
+ //returns true if the data point assigned by the given index has set its own properties
+ if( index>=m_nPointCount || m_nPointCount==0)
+ return false;
+ for(sal_Int32 n : m_aAttributedDataPointIndexList)
+ {
+ if(index == n)
+ return true;
+ }
+ return false;
+}
+
+bool VDataSeries::isVaryColorsByPoint() const
+{
+ bool bVaryColorsByPoint = false;
+ Reference< beans::XPropertySet > xSeriesProp( getPropertiesOfSeries() );
+ if( xSeriesProp.is() )
+ xSeriesProp->getPropertyValue("VaryColorsByPoint") >>= bVaryColorsByPoint;
+ return bVaryColorsByPoint;
+}
+
+uno::Reference< beans::XPropertySet > VDataSeries::getPropertiesOfPoint( sal_Int32 index ) const
+{
+ if( isAttributedDataPoint( index ) )
+ return m_xDataSeries->getDataPointByIndex(index);
+ return getPropertiesOfSeries();
+}
+
+const uno::Reference<beans::XPropertySet> & VDataSeries::getPropertiesOfSeries() const
+{
+ return m_xDataSeriesProps;
+}
+
+static std::unique_ptr<DataPointLabel> getDataPointLabelFromPropertySet( const uno::Reference< beans::XPropertySet >& xProp )
+{
+ std::unique_ptr< DataPointLabel > apLabel( new DataPointLabel() );
+ try
+ {
+ if( !(xProp->getPropertyValue(CHART_UNONAME_LABEL) >>= *apLabel) )
+ apLabel.reset();
+ }
+ catch(const uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return apLabel;
+}
+
+void VDataSeries::adaptPointCache( sal_Int32 nNewPointIndex ) const
+{
+ if( m_nCurrentAttributedPoint != nNewPointIndex )
+ {
+ m_apLabel_AttributedPoint.reset();
+ m_apLabelPropNames_AttributedPoint.reset();
+ m_apLabelPropValues_AttributedPoint.reset();
+ m_apSymbolProperties_AttributedPoint.reset();
+ m_nCurrentAttributedPoint = nNewPointIndex;
+ }
+}
+
+DataPointLabel* VDataSeries::getDataPointLabel( sal_Int32 index ) const
+{
+ DataPointLabel* pRet = nullptr;
+ if( isAttributedDataPoint( index ) )
+ {
+ adaptPointCache( index );
+ if (!m_apLabel_AttributedPoint)
+ m_apLabel_AttributedPoint
+ = getDataPointLabelFromPropertySet(getPropertiesOfPoint(index));
+ pRet = m_apLabel_AttributedPoint.get();
+ }
+ else
+ {
+ if (!m_apLabel_Series)
+ m_apLabel_Series
+ = getDataPointLabelFromPropertySet(getPropertiesOfPoint(index));
+ pRet = m_apLabel_Series.get();
+ }
+ if( !m_bAllowPercentValueInDataLabel )
+ {
+ if( pRet )
+ pRet->ShowNumberInPercent = false;
+ }
+ return pRet;
+}
+
+DataPointLabel* VDataSeries::getDataPointLabelIfLabel( sal_Int32 index ) const
+{
+ DataPointLabel* pLabel = getDataPointLabel( index );
+ if( !pLabel || (!pLabel->ShowNumber && !pLabel->ShowNumberInPercent
+ && !pLabel->ShowCategoryName && !pLabel->ShowCustomLabel && !pLabel->ShowSeriesName ) )
+ return nullptr;
+ return pLabel;
+}
+
+bool VDataSeries::getTextLabelMultiPropertyLists( sal_Int32 index
+ , tNameSequence*& pPropNames
+ , tAnySequence*& pPropValues ) const
+{
+ pPropNames = nullptr; pPropValues = nullptr;
+ uno::Reference< beans::XPropertySet > xTextProp;
+ bool bDoDynamicFontResize = false;
+ if( isAttributedDataPoint( index ) )
+ {
+ adaptPointCache( index );
+ if (!m_apLabelPropValues_AttributedPoint)
+ {
+ // Cache these properties for this point.
+ m_apLabelPropNames_AttributedPoint.reset(new tNameSequence);
+ m_apLabelPropValues_AttributedPoint.reset(new tAnySequence);
+ xTextProp.set( getPropertiesOfPoint( index ));
+ PropertyMapper::getTextLabelMultiPropertyLists(
+ xTextProp, *m_apLabelPropNames_AttributedPoint, *m_apLabelPropValues_AttributedPoint);
+ bDoDynamicFontResize = true;
+ }
+ pPropNames = m_apLabelPropNames_AttributedPoint.get();
+ pPropValues = m_apLabelPropValues_AttributedPoint.get();
+ }
+ else
+ {
+ if (!m_apLabelPropValues_Series)
+ {
+ // Cache these properties for the whole series.
+ m_apLabelPropNames_Series.reset(new tNameSequence);
+ m_apLabelPropValues_Series.reset(new tAnySequence);
+ xTextProp.set( getPropertiesOfPoint( index ));
+ PropertyMapper::getTextLabelMultiPropertyLists(
+ xTextProp, *m_apLabelPropNames_Series, *m_apLabelPropValues_Series);
+ bDoDynamicFontResize = true;
+ }
+ pPropNames = m_apLabelPropNames_Series.get();
+ pPropValues = m_apLabelPropValues_Series.get();
+ }
+
+ if( bDoDynamicFontResize &&
+ pPropNames && pPropValues &&
+ xTextProp.is())
+ {
+ LabelPositionHelper::doDynamicFontResize( *pPropValues, *pPropNames, xTextProp, m_aReferenceSize );
+ }
+
+ return (pPropNames && pPropValues);
+}
+
+void VDataSeries::setMissingValueTreatment( sal_Int32 nMissingValueTreatment )
+{
+ m_nMissingValueTreatment = nMissingValueTreatment;
+}
+
+sal_Int32 VDataSeries::getMissingValueTreatment() const
+{
+ return m_nMissingValueTreatment;
+}
+
+VDataSeries::VDataSeries()
+ : m_nPolygonIndex(0)
+ , m_fLogicMinX(0)
+ , m_fLogicMaxX(0)
+ , m_fLogicZPos(0)
+ , m_nPointCount(0)
+ , m_pValueSequenceForDataLabelNumberFormatDetection(nullptr)
+ , m_fXMeanValue(0)
+ , m_fYMeanValue(0)
+ , m_eStackingDirection(chart2::StackingDirection_NO_STACKING)
+ , m_nAxisIndex(0)
+ , m_bConnectBars(false)
+ , m_bGroupBarsPerAxis(false)
+ , m_nStartingAngle(0)
+ , m_nGlobalSeriesIndex(0)
+ , m_nCurrentAttributedPoint(0)
+ , m_nMissingValueTreatment(0)
+ , m_bAllowPercentValueInDataLabel(false)
+ , mpOldSeries(nullptr)
+ , mnPercent(0)
+{
+}
+
+void VDataSeries::setOldTimeBased( VDataSeries* pOldSeries, double nPercent )
+{
+ mnPercent = nPercent;
+ mpOldSeries = pOldSeries;
+ mpOldSeries->mpOldSeries = nullptr;
+}
+
+VDataSeries* VDataSeries::createCopyForTimeBased() const
+{
+ VDataSeries* pNew = new VDataSeries();
+ pNew->m_aValues_X = m_aValues_X;
+ pNew->m_aValues_Y = m_aValues_Y;
+ pNew->m_aValues_Z = m_aValues_Z;
+ pNew->m_aValues_Y_Min = m_aValues_Y_Min;
+ pNew->m_aValues_Y_Max = m_aValues_Y_Max;
+ pNew->m_aValues_Y_First = m_aValues_Y_First;
+ pNew->m_aValues_Y_Last = m_aValues_Y_Last;
+ pNew->m_aValues_Bubble_Size = m_aValues_Bubble_Size;
+ pNew->m_PropertyMap = m_PropertyMap;
+
+ pNew->m_nPointCount = m_nPointCount;
+
+ return pNew;
+}
+
+double VDataSeries::getValueByProperty( sal_Int32 nIndex, const OUString& rPropName ) const
+{
+ auto const itr = m_PropertyMap.find(rPropName);
+ if (itr == m_PropertyMap.end())
+ return std::numeric_limits<double>::quiet_NaN();
+
+ const VDataSequence* pData = &itr->second;
+ double fValue = pData->getValue(nIndex);
+ if(mpOldSeries && mpOldSeries->hasPropertyMapping(rPropName))
+ {
+ double fOldValue = mpOldSeries->getValueByProperty( nIndex, rPropName );
+ if(rPropName.endsWith("Color"))
+ {
+ //optimized interpolation for color values
+ Color aColor(ColorTransparency, static_cast<sal_uInt32>(fValue));
+ Color aOldColor(ColorTransparency, static_cast<sal_uInt32>(fOldValue));
+ sal_uInt8 r = aOldColor.GetRed() + (aColor.GetRed() - aOldColor.GetRed()) * mnPercent;
+ sal_uInt8 g = aOldColor.GetGreen() + (aColor.GetGreen() - aOldColor.GetGreen()) * mnPercent;
+ sal_uInt8 b = aOldColor.GetBlue() + (aColor.GetBlue() - aOldColor.GetBlue()) * mnPercent;
+ sal_uInt8 a = aOldColor.GetAlpha() + (aColor.GetAlpha() - aOldColor.GetAlpha()) * mnPercent;
+ Color aRet(ColorAlpha, a, r, g, b);
+ return sal_uInt32(aRet);
+ }
+ return fOldValue + (fValue - fOldValue) * mnPercent;
+ }
+ return fValue;
+}
+
+bool VDataSeries::hasPropertyMapping(const OUString& rPropName ) const
+{
+ return m_PropertyMap.find(rPropName) != m_PropertyMap.end();
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/VLegend.cxx b/chart2/source/view/main/VLegend.cxx
new file mode 100644
index 000000000..794d8d41e
--- /dev/null
+++ b/chart2/source/view/main/VLegend.cxx
@@ -0,0 +1,1099 @@
+/* -*- 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 <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/XFormattedString2.hpp>
+#include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp>
+#include <com/sun/star/chart2/data/PivotTableFieldEntry.hpp>
+#include <rtl/math.hxx>
+#include <svl/ctloptions.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/UnitConversion.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;
+
+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( "CharHeight") >>= fFontHeight ))
+ {
+ fResult = fFontHeight;
+ try
+ {
+ awt::Size aPropRefSize;
+ if( (xProp->getPropertyValue( "ReferencePageSize") >>= 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[ "LineJoint" ] <<= 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[ "TextAutoGrowHeight" ] <<= true;
+ aTextValueMap[ "TextAutoGrowWidth" ] <<= true;
+ aTextValueMap[ "TextHorizontalAdjust" ] <<= drawing::TextHorizontalAdjust_LEFT;
+ aTextValueMap[ "TextMaximumFrameWidth" ] <<= 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( "ReferencePageSize") >>= aPropRefSize) &&
+ (aPropRefSize.Height > 0) &&
+ (aTextValueMap[ "CharHeight" ] >>= fFontHeight) )
+ {
+ aTextValueMap[ "CharHeight" ] <<=
+ static_cast< float >(
+ ::chart::RelativeSizeHelper::calculate( fFontHeight, aPropRefSize, rReferenceSize ));
+
+ if( aTextValueMap[ "CharHeightAsian" ] >>= fFontHeight )
+ {
+ aTextValueMap[ "CharHeightAsian" ] <<=
+ static_cast< float >(
+ ::chart::RelativeSizeHelper::calculate( fFontHeight, aPropRefSize, rReferenceSize ));
+ }
+ if( aTextValueMap[ "CharHeightComplex" ] >>= fFontHeight )
+ {
+ aTextValueMap[ "CharHeightComplex" ] <<=
+ 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;
+ Sequence< Reference< XFormattedString2 > > aLabelSeq = rEntry.aLabel;
+ for( sal_Int32 i = 0; i < aLabelSeq.getLength(); ++i )
+ {
+ // todo: support more than one text range
+ if( i == 1 )
+ break;
+
+ aLabelString += aLabelSeq[i]->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++ )
+ {
+ 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].aLabel[0]->getString();
+ static const OUStringLiteral sDots = u"...";
+ for (sal_Int32 nNewLen = aLabelString.getLength() - sDots.getLength(); nNewLen > 0; nNewLen--)
+ {
+ 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].aLabel[0]->setString(aNewLabel);
+ aRowHeights[0] = nSumHeight;
+ aColumnWidths[0] = nWidth;
+ break;
+ }
+ }
+ DrawModelWrapper::removeShape(xEntry);
+ }
+ if (aTextShapes.size() == 0)
+ {
+ 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
+ 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( "WritingMode" ) >>= 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;
+
+ uno::Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider(rModel.getDataProvider(), uno::UNO_QUERY);
+ if (!xPivotTableDataProvider.is())
+ return aButtons;
+
+ if (!xPivotTableDataProvider->getColumnFields().hasElements())
+ return aButtons;
+
+ awt::Size aSize(2000, 700);
+ int x = 100;
+ int y = 100;
+
+ const css::uno::Sequence<chart2::data::PivotTableFieldEntry> aPivotFieldEntries = xPivotTableDataProvider->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(
+ const rtl::Reference< Legend > & xLegend,
+ const Reference< uno::XComponentContext > & xContext,
+ std::vector< LegendEntryProvider* >&& rLegendEntryProviderList,
+ const rtl::Reference<SvxShapeGroupAnyD>& xTargetPage,
+ ChartModel& rModel )
+ : m_xTarget(xTargetPage)
+ , m_xLegend(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( "Show") >>= 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("Expansion") >>= eExpansion;
+ if( eExpansion == css::chart::ChartLegendExpansion_CUSTOM )
+ {
+ RelativeSize aRelativeSize;
+ if (m_xLegend->getPropertyValue("RelativeSize") >>= 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("AnchorPosition") >>= 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 );
+
+ uno::Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider( mrModel.getDataProvider(), uno::UNO_QUERY );
+ bool bIsPivotChart = xPivotTableDataProvider.is();
+
+ 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, "MarkHandles");
+ }
+ }
+ }
+ 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( "RelativePosition") >>= aRelativePosition);
+
+ LegendPosition ePos = LegendPosition_LINE_END;
+ m_xLegend->getPropertyValue( "AnchorPosition") >>= ePos;
+
+ bool bOverlay = false;
+ m_xLegend->getPropertyValue("Overlay") >>= 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: */
diff --git a/chart2/source/view/main/VLegend.hxx b/chart2/source/view/main/VLegend.hxx
new file mode 100644
index 000000000..f47b21d26
--- /dev/null
+++ b/chart2/source/view/main/VLegend.hxx
@@ -0,0 +1,91 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#include <Legend.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <rtl/ref.hxx>
+#include <svx/unoshape.hxx>
+#include <vector>
+
+namespace chart { class ChartModel; }
+namespace com::sun::star::awt { struct Rectangle; }
+namespace com::sun::star::awt { struct Size; }
+namespace com::sun::star::chart2 { class XLegend; }
+namespace com::sun::star::drawing { class XShape; }
+namespace com::sun::star::drawing { class XShapes; }
+namespace com::sun::star::lang { class XMultiServiceFactory; }
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace chart
+{
+
+class Legend;
+class LegendEntryProvider;
+
+class VLegend
+{
+public:
+ VLegend( const rtl::Reference< ::chart::Legend > & xLegend,
+ const css::uno::Reference< css::uno::XComponentContext > & xContext,
+ std::vector< LegendEntryProvider* >&& rLegendEntryProviderList,
+ const rtl::Reference<SvxShapeGroupAnyD>& xTargetPage,
+ ChartModel& rModel );
+
+ void setDefaultWritingMode( sal_Int16 nDefaultWritingMode );
+
+ void createShapes( const css::awt::Size & rAvailableSpace,
+ const css::awt::Size & rPageSize,
+ css::awt::Size & rDefaultLegendSize );
+
+ /** Sets the position according to its internal anchor.
+
+ @param rOutAvailableSpace
+ is modified by the method, if the legend is in a standard position,
+ such that the space allocated by the legend is removed from it.
+
+ @param rReferenceSize
+ is used to calculate the offset (default 2%) from the edge.
+ */
+ void changePosition(
+ css::awt::Rectangle & rOutAvailableSpace,
+ const css::awt::Size & rReferenceSize,
+ const css::awt::Size & rDefaultLegendSize );
+
+ static bool isVisible(
+ const rtl::Reference< ::chart::Legend > & xLegend );
+
+private:
+ rtl::Reference<SvxShapeGroupAnyD> m_xTarget;
+ rtl::Reference<::chart::Legend> m_xLegend;
+ rtl::Reference< SvxShapeGroup > m_xShape;
+
+ ChartModel& mrModel;
+
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ std::vector< LegendEntryProvider* > m_aLegendEntryProviderList;
+
+ sal_Int16 m_nDefaultWritingMode;//to be used when writing mode is set to page
+};
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/VLegendSymbolFactory.cxx b/chart2/source/view/main/VLegendSymbolFactory.cxx
new file mode 100644
index 000000000..451e32e45
--- /dev/null
+++ b/chart2/source/view/main/VLegendSymbolFactory.cxx
@@ -0,0 +1,188 @@
+/* -*- 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 <VLegendSymbolFactory.hxx>
+#include <PropertyMapper.hxx>
+#include <ShapeFactory.hxx>
+#include <com/sun/star/drawing/Position3D.hpp>
+#include <com/sun/star/chart2/Symbol.hpp>
+#include <com/sun/star/drawing/Direction3D.hpp>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Reference;
+
+namespace
+{
+
+void getPropNamesAndValues( const Reference< beans::XPropertySet >& xProp,
+ ::chart::tNameSequence& rNames,
+ ::chart::tAnySequence& rValues,
+ ::chart::VLegendSymbolFactory::PropertyType ePropertyType,
+ const awt::Size& aMaxSymbolExtent)
+{
+ const ::chart::tPropertyNameMap & aFilledSeriesNameMap( ::chart::PropertyMapper::getPropertyNameMapForFilledSeriesProperties());
+ const ::chart::tPropertyNameMap & aLineSeriesNameMap( ::chart::PropertyMapper::getPropertyNameMapForLineSeriesProperties());
+ const ::chart::tPropertyNameMap & aLineNameMap( ::chart::PropertyMapper::getPropertyNameMapForLineProperties());
+
+ ::chart::tPropertyNameValueMap aValueMap;
+ switch( ePropertyType )
+ {
+ case ::chart::VLegendSymbolFactory::PropertyType::FilledSeries:
+ ::chart::PropertyMapper::getValueMap( aValueMap, aFilledSeriesNameMap, xProp );
+ break;
+ case ::chart::VLegendSymbolFactory::PropertyType::LineSeries:
+ ::chart::PropertyMapper::getValueMap( aValueMap, aLineSeriesNameMap, xProp );
+ break;
+ case ::chart::VLegendSymbolFactory::PropertyType::Line:
+ ::chart::PropertyMapper::getValueMap( aValueMap, aLineNameMap, xProp );
+ break;
+ }
+
+ ::chart::PropertyMapper::getMultiPropertyListsFromValueMap( rNames, rValues, aValueMap );
+
+ uno::Any* pLineWidthAny = ::chart::PropertyMapper::getValuePointer(rValues,rNames,u"LineWidth");
+ sal_Int32 nLineWidth = 0;
+ if( pLineWidthAny && (*pLineWidthAny>>=nLineWidth) )
+ {
+ // use legend entry height as upper limit for line width
+ sal_Int32 nMaxLineWidthForLegend = aMaxSymbolExtent.Height;
+ if( nLineWidth>nMaxLineWidthForLegend )
+ *pLineWidthAny <<= nMaxLineWidthForLegend;
+ }
+}
+
+void lcl_setPropertiesToShape(
+ const Reference< beans::XPropertySet > & xProp,
+ const rtl::Reference< SvxShape > & xShape,
+ ::chart::VLegendSymbolFactory::PropertyType ePropertyType,
+ const awt::Size& aMaxSymbolExtent)
+{
+ ::chart::tNameSequence aPropNames;
+ ::chart::tAnySequence aPropValues;
+ getPropNamesAndValues( xProp, aPropNames, aPropValues,
+ ePropertyType, aMaxSymbolExtent );
+
+ ::chart::PropertyMapper::setMultiProperties( aPropNames, aPropValues, *xShape );
+}
+
+} // anonymous namespace
+
+namespace chart
+{
+
+rtl::Reference< SvxShapeGroup > VLegendSymbolFactory::createSymbol(
+ const awt::Size& rEntryKeyAspectRatio,
+ const rtl::Reference<SvxShapeGroupAnyD>& rSymbolContainer,
+ LegendSymbolStyle eStyle,
+ const Reference< beans::XPropertySet > & xLegendEntryProperties,
+ PropertyType ePropertyType, const uno::Any& rExplicitSymbol )
+{
+ rtl::Reference< SvxShapeGroup > xResult;
+
+ if( !rSymbolContainer)
+ return xResult;
+
+ xResult = ShapeFactory::createGroup2D( rSymbolContainer );
+ if( ! xResult)
+ return xResult;
+
+ rtl::Reference<SvxShapeGroupAnyD> xResultGroup = xResult;
+
+ // add an invisible square box to maintain aspect ratio
+ ShapeFactory::createInvisibleRectangle( xResultGroup, rEntryKeyAspectRatio );
+
+ // create symbol
+ try
+ {
+ if( eStyle == LegendSymbolStyle::Line )
+ {
+ rtl::Reference<SvxShapePolyPolygon> xLine =
+ ShapeFactory::createLine( xResultGroup, awt::Size( rEntryKeyAspectRatio.Width, 0 ),
+ awt::Point( 0, rEntryKeyAspectRatio.Height/2 ));
+ lcl_setPropertiesToShape( xLegendEntryProperties, xLine, ePropertyType, rEntryKeyAspectRatio );
+
+ const sal_Int32 nSize = std::min(rEntryKeyAspectRatio.Width,rEntryKeyAspectRatio.Height);
+ chart2::Symbol aSymbol;
+ if( rExplicitSymbol >>= aSymbol )
+ {
+ drawing::Direction3D aSymbolSize( nSize, nSize, 0 );
+ drawing::Position3D aPos( rEntryKeyAspectRatio.Width/2.0, rEntryKeyAspectRatio.Height/2.0, 0 );
+ if( aSymbol.Style == chart2::SymbolStyle_STANDARD )
+ {
+ // take series color as fill color
+ xLegendEntryProperties->getPropertyValue( "Color") >>= aSymbol.FillColor;
+ // border of symbols always same as fill color
+ aSymbol.BorderColor = aSymbol.FillColor;
+
+ ShapeFactory::createSymbol2D(
+ xResultGroup,
+ aPos,
+ aSymbolSize,
+ aSymbol.StandardSymbol,
+ aSymbol.BorderColor,
+ aSymbol.FillColor );
+ }
+ else if( aSymbol.Style == chart2::SymbolStyle_GRAPHIC )
+ {
+ ShapeFactory::createGraphic2D(
+ xResultGroup,
+ aPos,
+ aSymbolSize,
+ aSymbol.Graphic );
+ }
+ else if( aSymbol.Style == chart2::SymbolStyle_AUTO )
+ {
+ SAL_WARN("chart2", "the given parameter is not allowed to contain an automatic symbol style");
+ }
+ }
+ }
+ else if( eStyle == LegendSymbolStyle::Circle )
+ {
+ sal_Int32 nSize = std::min( rEntryKeyAspectRatio.Width, rEntryKeyAspectRatio.Height );
+ rtl::Reference<SvxShapeCircle> xShape =
+ ShapeFactory::createCircle( xResultGroup, awt::Size( nSize, nSize ),
+ awt::Point( rEntryKeyAspectRatio.Width/2-nSize/2, rEntryKeyAspectRatio.Height/2-nSize/2 ));
+ lcl_setPropertiesToShape( xLegendEntryProperties, xShape, ePropertyType, awt::Size(0,0) ); // PropertyType::FilledSeries );
+ }
+ else // eStyle == LegendSymbolStyle::Box
+ {
+ tNameSequence aPropNames;
+ tAnySequence aPropValues;
+
+ getPropNamesAndValues( xLegendEntryProperties, aPropNames, aPropValues,
+ ePropertyType, awt::Size(0,0) );// PropertyType::FilledSeries
+
+ ShapeFactory::createRectangle( xResultGroup,
+ rEntryKeyAspectRatio, awt::Point( 0, 0 ),
+ aPropNames, aPropValues );
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ return xResult;
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/VLineProperties.cxx b/chart2/source/view/main/VLineProperties.cxx
new file mode 100644
index 000000000..49a3b4e77
--- /dev/null
+++ b/chart2/source/view/main/VLineProperties.cxx
@@ -0,0 +1,85 @@
+/* -*- 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 <VLineProperties.hxx>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/drawing/LineCap.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <tools/diagnose_ex.h>
+
+namespace chart
+{
+using namespace ::com::sun::star;
+
+// get line properties from a propertyset
+
+VLineProperties::VLineProperties()
+{
+ Color <<= sal_Int32(0x000000); //type sal_Int32 UNO_NAME_LINECOLOR
+ LineStyle
+ <<= drawing::LineStyle_SOLID; //type drawing::LineStyle for property UNO_NAME_LINESTYLE
+ Transparence <<= sal_Int16(0); //type sal_Int16 for property UNO_NAME_LINETRANSPARENCE
+ Width <<= sal_Int32(0); //type sal_Int32 for property UNO_NAME_LINEWIDTH
+ LineCap <<= drawing::LineCap_BUTT; //type drawing::LineCap for property UNO_NAME_LINECAP
+}
+
+void VLineProperties::initFromPropertySet(const uno::Reference<beans::XPropertySet>& xProp)
+{
+ if (xProp.is())
+ {
+ try
+ {
+ Color = xProp->getPropertyValue("LineColor");
+ LineStyle = xProp->getPropertyValue("LineStyle");
+ Transparence = xProp->getPropertyValue("LineTransparence");
+ Width = xProp->getPropertyValue("LineWidth");
+ DashName = xProp->getPropertyValue("LineDashName");
+ LineCap = xProp->getPropertyValue("LineCap");
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "");
+ }
+ }
+ else
+ LineStyle <<= drawing::LineStyle_NONE;
+}
+
+bool VLineProperties::isLineVisible() const
+{
+ bool bRet = false;
+
+ drawing::LineStyle aLineStyle(drawing::LineStyle_SOLID);
+ LineStyle >>= aLineStyle;
+ if (aLineStyle != drawing::LineStyle_NONE)
+ {
+ sal_Int16 nLineTransparence = 0;
+ Transparence >>= nLineTransparence;
+ if (nLineTransparence != 100)
+ {
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/VPolarTransformation.cxx b/chart2/source/view/main/VPolarTransformation.cxx
new file mode 100644
index 000000000..9ec2eea3f
--- /dev/null
+++ b/chart2/source/view/main/VPolarTransformation.cxx
@@ -0,0 +1,88 @@
+/* -*- 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 <VPolarTransformation.hxx>
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::uno::Sequence;
+
+namespace chart
+{
+
+VPolarTransformation::VPolarTransformation( const PolarPlottingPositionHelper& rPositionHelper )
+ : m_aPositionHelper(rPositionHelper)
+ , m_aUnitCartesianToScene( rPositionHelper.getUnitCartesianToScene() )
+{
+}
+
+VPolarTransformation::~VPolarTransformation()
+{
+}
+
+// ____ XTransformation2 ____
+css::drawing::Position3D VPolarTransformation::transform(
+ const Sequence< double >& rSourceValues ) const
+{
+ double fScaledLogicAngle = rSourceValues[0];
+ double fScaledLogicRadius = rSourceValues[1];
+
+ if( m_aPositionHelper.isSwapXAndY() )
+ std::swap(fScaledLogicAngle,fScaledLogicRadius);
+
+ double fAngleDegree = m_aPositionHelper.transformToAngleDegree( fScaledLogicAngle, false );
+ double fAnglePi = basegfx::deg2rad(fAngleDegree);
+ double fRadius = m_aPositionHelper.transformToRadius( fScaledLogicRadius, false);
+
+ double fX=fRadius*cos(fAnglePi);
+ double fY=fRadius*sin(fAnglePi);
+ double fZ=rSourceValues[2];
+
+ //!! applying matrix to vector does ignore translation, so it is important to use a B3DPoint here instead of B3DVector
+ ::basegfx::B3DPoint aPoint(fX,fY,fZ);
+ ::basegfx::B3DPoint aRet = m_aUnitCartesianToScene * aPoint;
+ return css::drawing::Position3D(aRet.getX(), aRet.getY(), aRet.getZ());
+}
+
+css::drawing::Position3D VPolarTransformation::transform(
+ const css::drawing::Position3D& rSourceValues ) const
+{
+ double fScaledLogicAngle = rSourceValues.PositionX;
+ double fScaledLogicRadius = rSourceValues.PositionY;
+
+ if( m_aPositionHelper.isSwapXAndY() )
+ std::swap(fScaledLogicAngle,fScaledLogicRadius);
+
+ double fAngleDegree = m_aPositionHelper.transformToAngleDegree( fScaledLogicAngle, false );
+ double fAnglePi = basegfx::deg2rad(fAngleDegree);
+ double fRadius = m_aPositionHelper.transformToRadius( fScaledLogicRadius, false);
+
+ double fX=fRadius*cos(fAnglePi);
+ double fY=fRadius*sin(fAnglePi);
+ double fZ=rSourceValues.PositionZ;
+
+ //!! applying matrix to vector does ignore translation, so it is important to use a B3DPoint here instead of B3DVector
+ ::basegfx::B3DPoint aPoint(fX,fY,fZ);
+ ::basegfx::B3DPoint aRet = m_aUnitCartesianToScene * aPoint;
+ return css::drawing::Position3D(aRet.getX(), aRet.getY(), aRet.getZ());
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/VTitle.cxx b/chart2/source/view/main/VTitle.cxx
new file mode 100644
index 000000000..74d276742
--- /dev/null
+++ b/chart2/source/view/main/VTitle.cxx
@@ -0,0 +1,158 @@
+/* -*- 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 "VTitle.hxx"
+#include <CommonConverters.hxx>
+#include <ShapeFactory.hxx>
+#include <com/sun/star/chart2/XTitle.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <tools/diagnose_ex.h>
+
+namespace chart
+{
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+
+VTitle::VTitle( const uno::Reference< XTitle > & xTitle )
+ : m_xTitle(xTitle)
+ , m_fRotationAngleDegree(0.0)
+ , m_nXPos(0)
+ , m_nYPos(0)
+{
+}
+
+VTitle::~VTitle()
+{
+}
+
+void VTitle::init(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTargetPage
+ , const OUString& rCID )
+{
+ m_xTarget = xTargetPage;
+ m_aCID = rCID;
+}
+
+double VTitle::getRotationAnglePi() const
+{
+ return basegfx::deg2rad(m_fRotationAngleDegree);
+}
+
+awt::Size VTitle::getUnrotatedSize() const //size before rotation
+{
+ awt::Size aRet;
+ if(m_xShape.is())
+ aRet = m_xShape->getSize();
+ return aRet;
+}
+
+awt::Size VTitle::getFinalSize() const //size after rotation
+{
+ return ShapeFactory::getSizeAfterRotation(
+ *m_xShape, m_fRotationAngleDegree );
+}
+
+void VTitle::changePosition( const awt::Point& rPos )
+{
+ if(!m_xShape.is())
+ return;
+ try
+ {
+ m_nXPos = rPos.X;
+ m_nYPos = rPos.Y;
+
+ //set position matrix
+ //the matrix needs to be set at the end behind autogrow and such position influencing properties
+ ::basegfx::B2DHomMatrix aM;
+ aM.rotate( basegfx::deg2rad(-m_fRotationAngleDegree) );//#i78696#->#i80521#
+ aM.translate( m_nXPos, m_nYPos);
+ m_xShape->SvxShape::setPropertyValue( "Transformation", uno::Any( B2DHomMatrixToHomogenMatrix3(aM) ) );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+}
+
+bool VTitle::isVisible(const uno::Reference< XTitle >& xTitle) {
+ if (!xTitle.is()) {
+ return false;
+ }
+ bool bShow = true;
+ try {
+ uno::Reference< beans::XPropertySet > xTitleProps(xTitle, uno::UNO_QUERY_THROW);
+ xTitleProps->getPropertyValue("Visible") >>= bShow;
+ } catch (const uno::Exception &) {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ return bShow;
+}
+
+
+void VTitle::createShapes(
+ const awt::Point& rPos
+ , const awt::Size& rReferenceSize
+ , const awt::Size& rTextMaxWidth
+ , bool bYAxisTitle )
+{
+ if(!m_xTitle.is())
+ return;
+
+ uno::Sequence< uno::Reference< XFormattedString > > aStringList = m_xTitle->getText();
+ if(!aStringList.hasElements())
+ return;
+
+ m_nXPos = rPos.X;
+ m_nYPos = rPos.Y;
+
+ uno::Reference< beans::XPropertySet > xTitleProperties( m_xTitle, uno::UNO_QUERY );
+
+ try
+ {
+ double fAngleDegree = 0;
+ xTitleProperties->getPropertyValue( "TextRotation" ) >>= fAngleDegree;
+ m_fRotationAngleDegree += fAngleDegree;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+
+ sal_Int32 nTextMaxWidth;
+ if (bYAxisTitle)
+ {
+ if (m_fRotationAngleDegree < 75.0 || m_fRotationAngleDegree > 285.0
+ || (m_fRotationAngleDegree > 105.0 && m_fRotationAngleDegree < 255.0))
+ nTextMaxWidth = rTextMaxWidth.Width;
+ else
+ nTextMaxWidth = rTextMaxWidth.Height;
+ }
+ else if (m_fRotationAngleDegree <= 15.0 || m_fRotationAngleDegree >= 345.0
+ || (m_fRotationAngleDegree >= 165.0 && m_fRotationAngleDegree <= 195.0))
+ nTextMaxWidth = rTextMaxWidth.Width;
+ else
+ nTextMaxWidth = rTextMaxWidth.Height;
+
+ m_xShape = ShapeFactory::createText( m_xTarget, rReferenceSize, rPos, aStringList, xTitleProperties,
+ m_fRotationAngleDegree, m_aCID, nTextMaxWidth );
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/view/main/VTitle.hxx b/chart2/source/view/main/VTitle.hxx
new file mode 100644
index 000000000..566128b95
--- /dev/null
+++ b/chart2/source/view/main/VTitle.hxx
@@ -0,0 +1,72 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/uno/Reference.h>
+#include <rtl/ustring.hxx>
+#include <rtl/ref.hxx>
+#include <sal/types.h>
+#include <svx/unoshape.hxx>
+
+namespace com::sun::star::awt { struct Point; }
+namespace com::sun::star::chart2 { class XTitle; }
+namespace com::sun::star::drawing { class XShape; }
+namespace com::sun::star::drawing { class XShapes; }
+namespace com::sun::star::lang { class XMultiServiceFactory; }
+class SvxShapeText;
+
+namespace chart
+{
+
+class VTitle final
+{
+public:
+ explicit VTitle( const css::uno::Reference< css::chart2::XTitle > & xTitle );
+ ~VTitle();
+
+ void init( const rtl::Reference<SvxShapeGroupAnyD>& xTargetPage
+ , const OUString& rCID );
+
+ void createShapes( const css::awt::Point& rPos
+ , const css::awt::Size& rReferenceSize
+ , const css::awt::Size& nTextMaxWidth
+ , bool bYAxisTitle );
+
+ double getRotationAnglePi() const;
+ css::awt::Size getUnrotatedSize() const;
+ css::awt::Size getFinalSize() const;
+ void changePosition( const css::awt::Point& rPos );
+ static bool isVisible(
+ const css::uno::Reference< css::chart2::XTitle > & xTitle);
+
+private:
+ rtl::Reference<SvxShapeGroupAnyD> m_xTarget;
+ css::uno::Reference< css::chart2::XTitle > m_xTitle;
+ rtl::Reference<SvxShapeText> m_xShape;
+ OUString m_aCID;
+
+ double m_fRotationAngleDegree;
+ sal_Int32 m_nXPos;
+ sal_Int32 m_nYPos;
+};
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */