Coverage Report

Created: 2025-11-16 09:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/chart2/source/view/main/ChartView.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <config_feature_desktop.h>
21
22
#include "SeriesPlotterContainer.hxx"
23
24
#include <ChartView.hxx>
25
#include <chartview/DrawModelWrapper.hxx>
26
#include <Diagram.hxx>
27
#include <ChartType.hxx>
28
#include <DataSeries.hxx>
29
#include <NumberFormatterWrapper.hxx>
30
#include <VDiagram.hxx>
31
#include "VTitle.hxx"
32
#include "VButton.hxx"
33
#include <ShapeFactory.hxx>
34
#include <BaseCoordinateSystem.hxx>
35
#include <VCoordinateSystem.hxx>
36
#include <VSeriesPlotter.hxx>
37
#include <CommonConverters.hxx>
38
#include <TitleHelper.hxx>
39
#include <Legend.hxx>
40
#include <LegendHelper.hxx>
41
#include "VLegend.hxx"
42
#include <PropertyMapper.hxx>
43
#include <ChartModel.hxx>
44
#include <ChartTypeHelper.hxx>
45
#include <ScaleAutomatism.hxx>
46
#include <ObjectIdentifier.hxx>
47
#include <DiagramHelper.hxx>
48
#include <RelativePositionHelper.hxx>
49
#include <servicenames.hxx>
50
#include <Axis.hxx>
51
#include <AxisHelper.hxx>
52
#include "AxisUsage.hxx"
53
#include <AxisIndexDefines.hxx>
54
#include <BaseGFXHelper.hxx>
55
#include <DataSeriesHelper.hxx>
56
#include <DateHelper.hxx>
57
#include <ExplicitCategoriesProvider.hxx>
58
#include <defines.hxx>
59
#include <comphelper/dumpxmltostring.hxx>
60
#include <unonames.hxx>
61
#include <editeng/frmdiritem.hxx>
62
#include <editeng/eeitem.hxx>
63
#include <tools/globname.hxx>
64
#include <comphelper/fileformat.h>
65
#include <comphelper/propertyvalue.hxx>
66
#include <comphelper/scopeguard.hxx>
67
#include <comphelper/servicehelper.hxx>
68
#include <cppuhelper/supportsservice.hxx>
69
#include <rtl/math.hxx>
70
#include <unotools/streamwrap.hxx>
71
#include <svx/svdpage.hxx>
72
#include <svx/unopage.hxx>
73
#include <utility>
74
#include <vcl/svapp.hxx>
75
#include <osl/mutex.hxx>
76
#include <svx/unofill.hxx>
77
#include <drawinglayer/XShapeDumper.hxx>
78
#include <sfx2/objsh.hxx>
79
80
#include <time.h>
81
82
#include <com/sun/star/awt/Point.hpp>
83
#include <com/sun/star/chart/ChartAxisPosition.hpp>
84
#include <com/sun/star/chart/TimeUnit.hpp>
85
#include <com/sun/star/chart2/AxisType.hpp>
86
#include <com/sun/star/chart2/StackingDirection.hpp>
87
#include <com/sun/star/chart2/RelativePosition.hpp>
88
#include <com/sun/star/chart2/RelativeSize.hpp>
89
#include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp>
90
#include <com/sun/star/chart2/data/PivotTableFieldEntry.hpp>
91
#include <com/sun/star/drawing/GraphicExportFilter.hpp>
92
#include <com/sun/star/embed/Aspects.hpp>
93
#include <com/sun/star/io/XSeekable.hpp>
94
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
95
#include <com/sun/star/util/XRefreshable.hpp>
96
#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
97
#include <com/sun/star/text/XText.hpp>
98
#include <com/sun/star/text/XTextDocument.hpp>
99
#include <com/sun/star/text/WritingMode2.hpp>
100
#include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp>
101
#include <com/sun/star/view/XSelectionSupplier.hpp>
102
#include <svl/itempool.hxx>
103
#include <svl/ctloptions.hxx>
104
#include <comphelper/classids.hxx>
105
#include <servicenames_charttypes.hxx>
106
107
108
#include <rtl/ustring.hxx>
109
110
#include <comphelper/diagnose_ex.hxx>
111
#include <tools/stream.hxx>
112
113
#include <memory>
114
#include <libxml/xmlwriter.h>
115
116
namespace chart {
117
118
using namespace ::com::sun::star;
119
using ::com::sun::star::uno::Reference;
120
using ::com::sun::star::uno::Sequence;
121
using ::com::sun::star::uno::Any;
122
123
struct CreateShapeParam2D
124
{
125
    css::awt::Rectangle maRemainingSpace;
126
127
    std::shared_ptr<SeriesPlotterContainer> mpSeriesPlotterContainer;
128
129
    std::shared_ptr<VTitle> mpVTitleX;
130
    std::shared_ptr<VTitle> mpVTitleY;
131
    std::shared_ptr<VTitle> mpVTitleZ;
132
133
    std::shared_ptr<VTitle> mpVTitleSecondX;
134
    std::shared_ptr<VTitle> mpVTitleSecondY;
135
136
    rtl::Reference<SvxShapeRect> mxMarkHandles;
137
    rtl::Reference<SvxShapeRect> mxPlotAreaWithAxes;
138
139
    rtl::Reference<SvxShapeGroup> mxDiagramWithAxesShapes;
140
141
    bool mbAutoPosTitleX;
142
    bool mbAutoPosTitleY;
143
    bool mbAutoPosTitleZ;
144
145
    bool mbAutoPosSecondTitleX;
146
    bool mbAutoPosSecondTitleY;
147
148
    bool mbUseFixedInnerSize;
149
150
    CreateShapeParam2D() :
151
0
        mbAutoPosTitleX(true),
152
0
        mbAutoPosTitleY(true),
153
0
        mbAutoPosTitleZ(true),
154
0
        mbAutoPosSecondTitleX(true),
155
0
        mbAutoPosSecondTitleY(true),
156
0
        mbUseFixedInnerSize(false) {}
157
};
158
159
160
161
ChartView::ChartView(
162
        uno::Reference<uno::XComponentContext> xContext,
163
        ChartModel& rModel)
164
0
    : m_xCC(std::move(xContext))
165
0
    , mrChartModel(rModel)
166
0
    , m_bViewDirty(true)
167
0
    , m_bInViewUpdate(false)
168
0
    , m_bViewUpdatePending(false)
169
0
    , m_bRefreshAddIn(true)
170
0
    , m_aPageResolution(1000,1000)
171
0
    , m_bPointsWereSkipped(false)
172
0
    , m_nScaleXNumerator(1)
173
0
    , m_nScaleXDenominator(1)
174
0
    , m_nScaleYNumerator(1)
175
0
    , m_nScaleYDenominator(1)
176
0
    , m_bSdrViewIsInEditMode(false)
177
0
    , m_aResultingDiagramRectangleExcludingAxes(0,0,0,0)
178
0
{
179
0
    init();
180
0
}
181
182
void ChartView::init()
183
0
{
184
0
    if( !m_pDrawModelWrapper )
185
0
    {
186
0
        SolarMutexGuard aSolarGuard;
187
0
        m_pDrawModelWrapper = std::make_shared< DrawModelWrapper >();
188
0
        m_xShapeFactory = m_pDrawModelWrapper->getShapeFactory();
189
0
        m_xDrawPage = m_pDrawModelWrapper->getMainDrawPage();
190
0
        StartListening( m_pDrawModelWrapper->getSdrModel() );
191
0
    }
192
0
}
193
194
void SAL_CALL ChartView::initialize( const uno::Sequence< uno::Any >& )
195
0
{
196
0
    init();
197
0
}
198
199
ChartView::~ChartView()
200
0
{
201
0
    maTimeBased.maTimer.Stop();
202
    // #i120831#. In ChartView::initialize(), m_xShapeFactory is created from SdrModel::getUnoModel() and indirectly
203
    //   from SfxBaseModel, it needs call dispose() to make sure SfxBaseModel object is freed correctly.
204
0
    uno::Reference< lang::XComponent > xComp( m_xShapeFactory, uno::UNO_QUERY);
205
0
    if ( xComp.is() )
206
0
        xComp->dispose();
207
208
0
    if( m_pDrawModelWrapper )
209
0
    {
210
0
        SolarMutexGuard aSolarGuard;
211
0
        EndListening( m_pDrawModelWrapper->getSdrModel() );
212
0
        m_pDrawModelWrapper.reset();
213
0
    }
214
0
    m_xDrawPage = nullptr;
215
0
    impl_deleteCoordinateSystems();
216
0
}
217
218
void ChartView::impl_deleteCoordinateSystems()
219
0
{
220
    //delete all coordinate systems
221
0
    m_aVCooSysList.clear();
222
0
}
223
224
// datatransfer::XTransferable
225
namespace
226
{
227
constexpr OUString lcl_aGDIMetaFileMIMEType(
228
    u"application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\""_ustr );
229
constexpr OUString lcl_aGDIMetaFileMIMETypeHighContrast(
230
    u"application/x-openoffice-highcontrast-gdimetafile;windows_formatname=\"GDIMetaFile\""_ustr );
231
} // anonymous namespace
232
233
void ChartView::getMetaFile( const uno::Reference< io::XOutputStream >& xOutStream
234
                           , bool bUseHighContrast )
235
0
{
236
0
    if( !m_xDrawPage.is() )
237
0
        return;
238
239
    // creating the graphic exporter
240
0
    uno::Reference< drawing::XGraphicExportFilter > xExporter = drawing::GraphicExportFilter::create( m_xCC );
241
242
0
    uno::Sequence< beans::PropertyValue > aFilterData{
243
0
        comphelper::makePropertyValue(u"ExportOnlyBackground"_ustr, false),
244
0
        comphelper::makePropertyValue(u"HighContrast"_ustr, bUseHighContrast),
245
0
        comphelper::makePropertyValue(u"Version"_ustr, sal_Int32(SOFFICE_FILEFORMAT_50)),
246
0
        comphelper::makePropertyValue(u"CurrentPage"_ustr, uno::Reference< uno::XInterface >( static_cast<cppu::OWeakObject*>(m_xDrawPage.get()), uno::UNO_QUERY )),
247
        //#i75867# poor quality of ole's alternative view with 3D scenes and zoomfactors besides 100%
248
0
        comphelper::makePropertyValue(u"ScaleXNumerator"_ustr, m_nScaleXNumerator),
249
0
        comphelper::makePropertyValue(u"ScaleXDenominator"_ustr, m_nScaleXDenominator),
250
0
        comphelper::makePropertyValue(u"ScaleYNumerator"_ustr, m_nScaleYNumerator),
251
0
        comphelper::makePropertyValue(u"ScaleYDenominator"_ustr, m_nScaleYDenominator)
252
0
    };
253
254
0
    uno::Sequence< beans::PropertyValue > aProps{
255
0
        comphelper::makePropertyValue(u"FilterName"_ustr, u"SVM"_ustr),
256
0
        comphelper::makePropertyValue(u"OutputStream"_ustr, xOutStream),
257
0
        comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData)
258
0
    };
259
260
0
    xExporter->setSourceDocument( m_xDrawPage );
261
0
    if( xExporter->filter( aProps ) )
262
0
    {
263
0
        xOutStream->flush();
264
0
        xOutStream->closeOutput();
265
0
        uno::Reference< io::XSeekable > xSeekable( xOutStream, uno::UNO_QUERY );
266
0
        if( xSeekable.is() )
267
0
            xSeekable->seek(0);
268
0
    }
269
0
}
270
271
uno::Any SAL_CALL ChartView::getTransferData( const datatransfer::DataFlavor& aFlavor )
272
0
{
273
0
    bool bHighContrastMetaFile( aFlavor.MimeType == lcl_aGDIMetaFileMIMETypeHighContrast);
274
0
    uno::Any aRet;
275
0
    if( ! (bHighContrastMetaFile || aFlavor.MimeType == lcl_aGDIMetaFileMIMEType) )
276
0
        return aRet;
277
278
0
    update();
279
280
0
    SvMemoryStream aStream( 1024, 1024 );
281
0
    rtl::Reference<utl::OStreamWrapper> pStreamWrapper = new utl::OStreamWrapper( aStream );
282
283
0
    this->getMetaFile( pStreamWrapper, bHighContrastMetaFile );
284
285
0
    pStreamWrapper->seek(0);
286
0
    sal_Int32 nBytesToRead = pStreamWrapper->available();
287
0
    uno::Sequence< sal_Int8 > aSeq( nBytesToRead );
288
0
    pStreamWrapper->readBytes( aSeq, nBytesToRead);
289
0
    aRet <<= aSeq;
290
0
    pStreamWrapper->closeInput();
291
292
0
    return aRet;
293
0
}
294
uno::Sequence< datatransfer::DataFlavor > SAL_CALL ChartView::getTransferDataFlavors()
295
0
{
296
0
    return
297
0
    {
298
0
        { lcl_aGDIMetaFileMIMEType, u"GDIMetaFile"_ustr, cppu::UnoType<uno::Sequence< sal_Int8 >>::get() },
299
0
        { lcl_aGDIMetaFileMIMETypeHighContrast, u"GDIMetaFile"_ustr, cppu::UnoType<uno::Sequence< sal_Int8 >>::get() }
300
0
    };
301
0
}
302
sal_Bool SAL_CALL ChartView::isDataFlavorSupported( const datatransfer::DataFlavor& aFlavor )
303
0
{
304
0
    return ( aFlavor.MimeType == lcl_aGDIMetaFileMIMEType ||
305
0
             aFlavor.MimeType == lcl_aGDIMetaFileMIMETypeHighContrast );
306
0
}
307
308
// lang::XServiceInfo
309
310
OUString SAL_CALL ChartView::getImplementationName()
311
0
{
312
0
    return CHART_VIEW_SERVICE_IMPLEMENTATION_NAME;
313
0
}
314
315
sal_Bool SAL_CALL ChartView::supportsService( const OUString& rServiceName )
316
0
{
317
0
    return cppu::supportsService(this, rServiceName);
318
0
}
319
320
css::uno::Sequence< OUString > SAL_CALL ChartView::getSupportedServiceNames()
321
0
{
322
0
    return { CHART_VIEW_SERVICE_NAME };
323
0
}
324
325
static ::basegfx::B3DHomMatrix createTransformationSceneToScreen(
326
    const ::basegfx::B2IRectangle& rDiagramRectangleWithoutAxes )
327
0
{
328
0
    ::basegfx::B3DHomMatrix aM;
329
0
    aM.scale(double(rDiagramRectangleWithoutAxes.getWidth())/FIXED_SIZE_FOR_3D_CHART_VOLUME
330
0
            , -double(rDiagramRectangleWithoutAxes.getHeight())/FIXED_SIZE_FOR_3D_CHART_VOLUME, 1.0 );
331
0
    aM.translate(double(rDiagramRectangleWithoutAxes.getMinX())
332
0
        , double(rDiagramRectangleWithoutAxes.getMinY()+rDiagramRectangleWithoutAxes.getHeight()-1), 0);
333
0
    return aM;
334
0
}
335
336
namespace
337
{
338
339
bool lcl_IsPieOrDonut( const rtl::Reference< Diagram >& xDiagram )
340
0
{
341
    //special treatment for pie charts
342
    //the size is checked after complete creation to get the datalabels into the given space
343
344
    //todo: this is just a workaround at the moment for pie and donut labels
345
0
    return xDiagram->isPieOrDonutChart();
346
0
}
347
348
void lcl_setDefaultWritingMode( const std::shared_ptr< DrawModelWrapper >& pDrawModelWrapper, ChartModel& rModel)
349
0
{
350
    //get writing mode from parent document:
351
0
    if( !SvtCTLOptions::IsCTLFontEnabled() )
352
0
        return;
353
354
0
    try
355
0
    {
356
0
        sal_Int16 nWritingMode=-1;
357
0
        uno::Reference< beans::XPropertySet > xParentProps( rModel.getParent(), uno::UNO_QUERY );
358
0
        uno::Reference< style::XStyleFamiliesSupplier > xStyleFamiliesSupplier( xParentProps, uno::UNO_QUERY );
359
0
        if( xStyleFamiliesSupplier.is() )
360
0
        {
361
0
            uno::Reference< container::XNameAccess > xStylesFamilies( xStyleFamiliesSupplier->getStyleFamilies() );
362
0
            if( xStylesFamilies.is() )
363
0
            {
364
0
                if( !xStylesFamilies->hasByName( u"PageStyles"_ustr ) )
365
0
                {
366
                    //draw/impress is parent document
367
0
                    uno::Reference< lang::XMultiServiceFactory > xFatcory( xParentProps, uno::UNO_QUERY );
368
0
                    if( xFatcory.is() )
369
0
                    {
370
0
                        uno::Reference< beans::XPropertySet > xDrawDefaults( xFatcory->createInstance( u"com.sun.star.drawing.Defaults"_ustr ), uno::UNO_QUERY );
371
0
                        if( xDrawDefaults.is() )
372
0
                            xDrawDefaults->getPropertyValue( u"WritingMode"_ustr ) >>= nWritingMode;
373
0
                    }
374
0
                }
375
0
                else
376
0
                {
377
0
                    uno::Reference< container::XNameAccess > xPageStyles( xStylesFamilies->getByName( u"PageStyles"_ustr ), uno::UNO_QUERY );
378
0
                    if( xPageStyles.is() )
379
0
                    {
380
0
                        OUString aPageStyle;
381
382
0
                        uno::Reference< text::XTextDocument > xTextDocument( xParentProps, uno::UNO_QUERY );
383
0
                        if( xTextDocument.is() )
384
0
                        {
385
                            //writer is parent document
386
                            //retrieve the current page style from the text cursor property PageStyleName
387
388
0
                            uno::Reference< text::XTextEmbeddedObjectsSupplier > xTextEmbeddedObjectsSupplier( xTextDocument, uno::UNO_QUERY );
389
0
                            if( xTextEmbeddedObjectsSupplier.is() )
390
0
                            {
391
0
                                uno::Reference< container::XNameAccess > xEmbeddedObjects( xTextEmbeddedObjectsSupplier->getEmbeddedObjects() );
392
0
                                if( xEmbeddedObjects.is() )
393
0
                                {
394
0
                                    uno::Sequence< OUString > aNames( xEmbeddedObjects->getElementNames() );
395
396
0
                                    sal_Int32 nCount = aNames.getLength();
397
0
                                    for( sal_Int32 nN=0; nN<nCount; nN++ )
398
0
                                    {
399
0
                                        uno::Reference< beans::XPropertySet > xEmbeddedProps( xEmbeddedObjects->getByName( aNames[nN] ), uno::UNO_QUERY );
400
0
                                        if( xEmbeddedProps.is() )
401
0
                                        {
402
0
                                            static OUString aChartCLSID = SvGlobalName( SO3_SCH_CLASSID ).GetHexName();
403
0
                                            OUString aCLSID;
404
0
                                            xEmbeddedProps->getPropertyValue( u"CLSID"_ustr ) >>= aCLSID;
405
0
                                            if( aCLSID == aChartCLSID )
406
0
                                            {
407
0
                                                uno::Reference< text::XTextContent > xEmbeddedObject( xEmbeddedProps, uno::UNO_QUERY );
408
0
                                                if( xEmbeddedObject.is() )
409
0
                                                {
410
0
                                                    uno::Reference< text::XTextRange > xAnchor( xEmbeddedObject->getAnchor() );
411
0
                                                    if( xAnchor.is() )
412
0
                                                    {
413
0
                                                        uno::Reference< beans::XPropertySet > xAnchorProps( xAnchor, uno::UNO_QUERY );
414
0
                                                        if( xAnchorProps.is() )
415
0
                                                        {
416
0
                                                            xAnchorProps->getPropertyValue( u"WritingMode"_ustr ) >>= nWritingMode;
417
0
                                                        }
418
0
                                                        uno::Reference< text::XText > xText( xAnchor->getText() );
419
0
                                                        if( xText.is() )
420
0
                                                        {
421
0
                                                            uno::Reference< beans::XPropertySet > xTextCursorProps( xText->createTextCursor(), uno::UNO_QUERY );
422
0
                                                            if( xTextCursorProps.is() )
423
0
                                                                xTextCursorProps->getPropertyValue( u"PageStyleName"_ustr ) >>= aPageStyle;
424
0
                                                        }
425
0
                                                    }
426
0
                                                }
427
0
                                                break;
428
0
                                            }
429
0
                                        }
430
0
                                    }
431
0
                                }
432
0
                            }
433
0
                            if( aPageStyle.isEmpty() )
434
0
                            {
435
0
                                uno::Reference< text::XText > xText( xTextDocument->getText() );
436
0
                                if( xText.is() )
437
0
                                {
438
0
                                    uno::Reference< beans::XPropertySet > xTextCursorProps( xText->createTextCursor(), uno::UNO_QUERY );
439
0
                                    if( xTextCursorProps.is() )
440
0
                                        xTextCursorProps->getPropertyValue( u"PageStyleName"_ustr ) >>= aPageStyle;
441
0
                                }
442
0
                            }
443
0
                            if(aPageStyle.isEmpty())
444
0
                                aPageStyle = "Standard";
445
0
                        }
446
0
                        else
447
0
                        {
448
                            //Calc is parent document
449
0
                            Reference< css::beans::XPropertySetInfo > xInfo = xParentProps->getPropertySetInfo();
450
0
                            if (xInfo->hasPropertyByName(u"PageStyle"_ustr))
451
0
                            {
452
0
                                xParentProps->getPropertyValue( u"PageStyle"_ustr ) >>= aPageStyle;
453
0
                            }
454
0
                            if(aPageStyle.isEmpty())
455
0
                                aPageStyle = "Default";
456
0
                        }
457
0
                        if( nWritingMode == -1 || nWritingMode == text::WritingMode2::PAGE )
458
0
                        {
459
0
                            uno::Reference< beans::XPropertySet > xPageStyle( xPageStyles->getByName( aPageStyle ), uno::UNO_QUERY );
460
0
                            Reference< css::beans::XPropertySetInfo > xInfo = xPageStyle->getPropertySetInfo();
461
0
                            if (xInfo->hasPropertyByName(u"WritingMode"_ustr))
462
0
                            {
463
0
                                if( xPageStyle.is() )
464
0
                                    xPageStyle->getPropertyValue( u"WritingMode"_ustr ) >>= nWritingMode;
465
0
                            }
466
0
                        }
467
0
                    }
468
0
                }
469
0
            }
470
0
        }
471
0
        if( nWritingMode != -1 && nWritingMode != text::WritingMode2::PAGE )
472
0
        {
473
0
            if( pDrawModelWrapper )
474
0
                pDrawModelWrapper->GetItemPool().SetUserDefaultItem(SvxFrameDirectionItem(static_cast<SvxFrameDirection>(nWritingMode), EE_PARA_WRITINGDIR) );
475
0
        }
476
0
    }
477
0
    catch( const uno::Exception& )
478
0
    {
479
0
        DBG_UNHANDLED_EXCEPTION("chart2" );
480
0
    }
481
0
}
482
483
sal_Int16 lcl_getDefaultWritingModeFromPool( const std::shared_ptr<DrawModelWrapper>& pDrawModelWrapper )
484
0
{
485
0
    sal_Int16 nWritingMode = text::WritingMode2::LR_TB;
486
0
    if(!pDrawModelWrapper)
487
0
        return nWritingMode;
488
489
0
    const SfxPoolItem& rItem = pDrawModelWrapper->GetItemPool().GetUserOrPoolDefaultItem(EE_PARA_WRITINGDIR);
490
0
    nWritingMode
491
0
        = static_cast<sal_Int16>(static_cast<const SvxFrameDirectionItem&>(rItem).GetValue());
492
0
    return nWritingMode;
493
0
}
494
495
} //end anonymous namespace
496
497
awt::Rectangle ChartView::impl_createDiagramAndContent( const CreateShapeParam2D& rParam, const awt::Size& rPageSize )
498
0
{
499
    //return the used rectangle
500
0
    awt::Rectangle aUsedOuterRect(rParam.maRemainingSpace.X, rParam.maRemainingSpace.Y, 0, 0);
501
502
0
    rtl::Reference< Diagram > xDiagram( mrChartModel.getFirstChartDiagram() );
503
0
    if( !xDiagram.is())
504
0
        return aUsedOuterRect;
505
506
0
    sal_Int32 nDimensionCount = xDiagram->getDimension();
507
0
    if(!nDimensionCount)
508
0
    {
509
        //@todo handle mixed dimension
510
0
        nDimensionCount = 2;
511
0
    }
512
513
0
    basegfx::B2IRectangle aAvailableOuterRect = BaseGFXHelper::makeRectangle(rParam.maRemainingSpace);
514
515
0
    const std::vector< std::unique_ptr<VCoordinateSystem> >& rVCooSysList( rParam.mpSeriesPlotterContainer->getCooSysList() );
516
0
    auto& rSeriesPlotterList = rParam.mpSeriesPlotterContainer->getSeriesPlotterList();
517
518
    //create VAxis, so they can give necessary information for automatic scaling
519
0
    uno::Reference<util::XNumberFormatsSupplier> const xNumberFormatsSupplier(
520
0
            mrChartModel.getNumberFormatsSupplier());
521
522
0
    for (auto& rpVCooSys : rVCooSysList)
523
0
    {
524
0
        if (nDimensionCount == 3)
525
0
        {
526
0
            CuboidPlanePosition eLeftWallPos( ThreeDHelper::getAutomaticCuboidPlanePositionForStandardLeftWall( xDiagram ) );
527
0
            CuboidPlanePosition eBackWallPos( ThreeDHelper::getAutomaticCuboidPlanePositionForStandardBackWall( xDiagram ) );
528
0
            CuboidPlanePosition eBottomPos( ThreeDHelper::getAutomaticCuboidPlanePositionForStandardBottom( xDiagram ) );
529
0
            rpVCooSys->set3DWallPositions( eLeftWallPos, eBackWallPos, eBottomPos );
530
0
        }
531
0
        rpVCooSys->createVAxisList(&mrChartModel, rPageSize, rParam.maRemainingSpace,
532
0
            rParam.mbUseFixedInnerSize, rSeriesPlotterList, getComponentContext());
533
0
    }
534
535
    // - prepare list of all axis and how they are used
536
0
    Date aNullDate = NumberFormatterWrapper( xNumberFormatsSupplier ).getNullDate();
537
0
    rParam.mpSeriesPlotterContainer->initAxisUsageList(aNullDate);
538
0
    rParam.mpSeriesPlotterContainer->doAutoScaling( mrChartModel );
539
0
    rParam.mpSeriesPlotterContainer->setScalesFromCooSysToPlotter();
540
0
    rParam.mpSeriesPlotterContainer->setNumberFormatsFromAxes();
541
542
    //create shapes
543
544
    //aspect ratio
545
0
    drawing::Direction3D aPreferredAspectRatio =
546
0
        rParam.mpSeriesPlotterContainer->getPreferredAspectRatio();
547
548
0
    rtl::Reference<SvxShapeGroupAnyD> xSeriesTargetInFrontOfAxis;
549
0
    rtl::Reference<SvxShapeGroupAnyD> xSeriesTargetBehindAxis;
550
0
    VDiagram aVDiagram(xDiagram, aPreferredAspectRatio, nDimensionCount);
551
0
    {//create diagram
552
0
        aVDiagram.init(rParam.mxDiagramWithAxesShapes);
553
0
        aVDiagram.createShapes(
554
0
            awt::Point(rParam.maRemainingSpace.X, rParam.maRemainingSpace.Y),
555
0
            awt::Size(rParam.maRemainingSpace.Width, rParam.maRemainingSpace.Height));
556
557
0
        xSeriesTargetInFrontOfAxis = aVDiagram.getCoordinateRegion();
558
        // It is preferable to use full size than minimum for pie charts
559
0
        if (!rParam.mbUseFixedInnerSize)
560
0
            aVDiagram.reduceToMinimumSize();
561
0
    }
562
563
0
    rtl::Reference<SvxShapeGroup> xTextTargetShapes =
564
0
        ShapeFactory::createGroup2D(rParam.mxDiagramWithAxesShapes);
565
566
    // - create axis and grids for all coordinate systems
567
568
    //init all coordinate systems
569
0
    for (auto& rpVCooSys : rVCooSysList)
570
0
    {
571
0
        rpVCooSys->initPlottingTargets(xSeriesTargetInFrontOfAxis, xTextTargetShapes, xSeriesTargetBehindAxis);
572
573
0
        rpVCooSys->setTransformationSceneToScreen( B3DHomMatrixToHomogenMatrix(
574
0
            createTransformationSceneToScreen( aVDiagram.getCurrentRectangle() ) ));
575
576
0
        rpVCooSys->initVAxisInList();
577
0
    }
578
579
    //calculate resulting size respecting axis label layout and fontscaling
580
581
0
    rtl::Reference<SvxShapeGroup> xBoundingShape(rParam.mxDiagramWithAxesShapes);
582
0
    ::basegfx::B2IRectangle aConsumedOuterRect;
583
584
    //use first coosys only so far; todo: calculate for more than one coosys if we have more in future
585
    //todo: this is just a workaround at the moment for pie and donut labels
586
0
    bool bIsPieOrDonut = lcl_IsPieOrDonut(xDiagram);
587
0
    if( !bIsPieOrDonut && (!rVCooSysList.empty()) )
588
0
    {
589
0
        VCoordinateSystem* pVCooSys = rVCooSysList[0].get();
590
0
        pVCooSys->createMaximumAxesLabels();
591
592
0
        aConsumedOuterRect = ShapeFactory::getRectangleOfShape(*xBoundingShape);
593
0
        ::basegfx::B2IRectangle aNewInnerRect( aVDiagram.getCurrentRectangle() );
594
0
        if (!rParam.mbUseFixedInnerSize)
595
0
            aNewInnerRect = aVDiagram.adjustInnerSize( aConsumedOuterRect );
596
597
0
        pVCooSys->setTransformationSceneToScreen( B3DHomMatrixToHomogenMatrix(
598
0
            createTransformationSceneToScreen( aNewInnerRect ) ));
599
600
        //redo autoscaling to get size and text dependent automatic main increment count
601
0
        rParam.mpSeriesPlotterContainer->doAutoScaling( mrChartModel );
602
0
        rParam.mpSeriesPlotterContainer->updateScalesAndIncrementsOnAxes();
603
0
        rParam.mpSeriesPlotterContainer->setScalesFromCooSysToPlotter();
604
605
0
        pVCooSys->createAxesLabels();
606
607
0
        bool bLessSpaceConsumedThanExpected = false;
608
0
        {
609
0
            aConsumedOuterRect = ShapeFactory::getRectangleOfShape(*xBoundingShape);
610
0
            if( aConsumedOuterRect.getMinX() > aAvailableOuterRect.getMinX()
611
0
                || aConsumedOuterRect.getMaxX() < aAvailableOuterRect.getMaxX()
612
0
                || aConsumedOuterRect.getMinY() > aAvailableOuterRect.getMinY()
613
0
                || aConsumedOuterRect.getMinY() < aAvailableOuterRect.getMaxY() )
614
0
            {
615
0
                bLessSpaceConsumedThanExpected = true;
616
0
            }
617
0
        }
618
619
0
        if (bLessSpaceConsumedThanExpected && !rParam.mbUseFixedInnerSize)
620
0
        {
621
0
            aVDiagram.adjustInnerSize( aConsumedOuterRect );
622
0
            pVCooSys->setTransformationSceneToScreen( B3DHomMatrixToHomogenMatrix(
623
0
                createTransformationSceneToScreen( aVDiagram.getCurrentRectangle() ) ));
624
625
            // Need to re-adjust again if the labels have changed height because of
626
            // text can break. Ideally this shouldn't be needed, but the chart height
627
            // isn't readjusted otherwise.
628
0
            pVCooSys->createAxesLabels();
629
0
            aConsumedOuterRect = ShapeFactory::getRectangleOfShape(*xBoundingShape);
630
0
            aVDiagram.adjustInnerSize(aConsumedOuterRect);
631
0
            pVCooSys->setTransformationSceneToScreen(B3DHomMatrixToHomogenMatrix(
632
0
                createTransformationSceneToScreen(aVDiagram.getCurrentRectangle())));
633
634
0
        }
635
0
        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
636
0
    }
637
638
    //create axes and grids for the final size
639
0
    for (auto& rpVCooSys : rVCooSysList)
640
0
    {
641
0
        rpVCooSys->setTransformationSceneToScreen( B3DHomMatrixToHomogenMatrix(
642
0
            createTransformationSceneToScreen( aVDiagram.getCurrentRectangle() ) ));
643
644
0
        rpVCooSys->createAxesShapes();
645
0
        rpVCooSys->createGridShapes();
646
0
    }
647
648
    // - create data series for all charttypes
649
0
    m_bPointsWereSkipped = false;
650
0
    for( const std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
651
0
    {
652
0
        VSeriesPlotter* pSeriesPlotter = aPlotter.get();
653
0
        rtl::Reference<SvxShapeGroupAnyD> xSeriesTarget;
654
0
        if( pSeriesPlotter->WantToPlotInFrontOfAxisLine() )
655
0
            xSeriesTarget = xSeriesTargetInFrontOfAxis;
656
0
        else
657
0
        {
658
0
            xSeriesTarget = xSeriesTargetBehindAxis;
659
0
            OSL_ENSURE( !bIsPieOrDonut, "not implemented yet! - during a complete recreation this shape is destroyed so no series can be created anymore" );
660
0
        }
661
0
        pSeriesPlotter->initPlotter( xSeriesTarget,xTextTargetShapes,OUString() );
662
0
        pSeriesPlotter->setPageReferenceSize( rPageSize );
663
0
        VCoordinateSystem* pVCooSys = SeriesPlotterContainer::getCooSysForPlotter( rVCooSysList, pSeriesPlotter );
664
0
        if(nDimensionCount==2)
665
0
            pSeriesPlotter->setTransformationSceneToScreen( pVCooSys->getTransformationSceneToScreen() );
666
        //better performance for big data
667
0
        {
668
            //calculate resolution for coordinate system
669
0
            Sequence<sal_Int32> aCoordinateSystemResolution = pVCooSys->getCoordinateSystemResolution( rPageSize, m_aPageResolution );
670
0
            pSeriesPlotter->setCoordinateSystemResolution( aCoordinateSystemResolution );
671
0
        }
672
        // Do not allow to move data labels in case of pie or donut chart, yet!
673
0
        pSeriesPlotter->setPieLabelsAllowToMove(!bIsPieOrDonut);
674
        // use the pagesize as remaining space if we have a fixed inner size
675
0
        if( rParam.mbUseFixedInnerSize )
676
0
            aAvailableOuterRect = BaseGFXHelper::makeRectangle(awt::Rectangle(0, 0, rPageSize.Width, rPageSize.Height));
677
        // set the available space for data labels to avoid moving out from chart area
678
0
        pSeriesPlotter->setAvailableOuterRect(aAvailableOuterRect);
679
0
        pSeriesPlotter->createShapes();
680
0
        m_bPointsWereSkipped = m_bPointsWereSkipped || pSeriesPlotter->PointsWereSkipped();
681
0
    }
682
683
    //recreate all with corrected sizes if requested
684
0
    if( bIsPieOrDonut )
685
0
    {
686
0
        m_bPointsWereSkipped = false;
687
688
0
        aConsumedOuterRect = ShapeFactory::getRectangleOfShape(*xBoundingShape);
689
0
        ::basegfx::B2IRectangle aNewInnerRect( aVDiagram.getCurrentRectangle() );
690
0
        if (!rParam.mbUseFixedInnerSize)
691
0
            aNewInnerRect = aVDiagram.adjustInnerSize( aConsumedOuterRect );
692
693
0
        for( std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
694
0
        {
695
0
            aPlotter->releaseShapes();
696
0
        }
697
698
        //clear and recreate
699
0
        ShapeFactory::removeSubShapes( xSeriesTargetInFrontOfAxis ); //xSeriesTargetBehindAxis is a sub shape of xSeriesTargetInFrontOfAxis and will be removed here
700
0
        xSeriesTargetBehindAxis.clear();
701
0
        ShapeFactory::removeSubShapes( xTextTargetShapes );
702
703
        //set new transformation
704
0
        for (auto& rpVCooSys : rVCooSysList)
705
0
        {
706
0
            auto aMatrix = createTransformationSceneToScreen(aNewInnerRect);
707
0
            rpVCooSys->setTransformationSceneToScreen(B3DHomMatrixToHomogenMatrix(aMatrix));
708
0
        }
709
710
        // - create data series for all charttypes
711
0
        for( std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
712
0
        {
713
0
            VCoordinateSystem* pVCooSys = SeriesPlotterContainer::getCooSysForPlotter( rVCooSysList, aPlotter.get() );
714
0
            if(nDimensionCount==2)
715
0
                aPlotter->setTransformationSceneToScreen( pVCooSys->getTransformationSceneToScreen() );
716
            // Now we can move data labels in case of pie or donut chart!
717
0
            aPlotter->setPieLabelsAllowToMove(bIsPieOrDonut);
718
0
            aPlotter->createShapes();
719
0
            m_bPointsWereSkipped = m_bPointsWereSkipped || aPlotter->PointsWereSkipped();
720
0
        }
721
722
0
        for( std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
723
0
            aPlotter->rearrangeLabelToAvoidOverlapIfRequested(rPageSize);
724
0
    }
725
726
0
    if (rParam.mbUseFixedInnerSize)
727
0
    {
728
0
        aUsedOuterRect = awt::Rectangle( aConsumedOuterRect.getMinX(), aConsumedOuterRect.getMinY(), aConsumedOuterRect.getWidth(), aConsumedOuterRect.getHeight() );
729
0
    }
730
0
    else
731
0
        aUsedOuterRect = rParam.maRemainingSpace;
732
733
0
    bool bSnapRectToUsedArea = false;
734
0
    for( std::unique_ptr<VSeriesPlotter>& aPlotter : rSeriesPlotterList )
735
0
    {
736
0
        bSnapRectToUsedArea = aPlotter->shouldSnapRectToUsedArea();
737
0
        if(bSnapRectToUsedArea)
738
0
            break;
739
0
    }
740
0
    if(bSnapRectToUsedArea)
741
0
    {
742
0
        if (rParam.mbUseFixedInnerSize)
743
0
            m_aResultingDiagramRectangleExcludingAxes = getRectangleOfObject( u"PlotAreaExcludingAxes"_ustr );
744
0
        else
745
0
        {
746
0
            ::basegfx::B2IRectangle aConsumedInnerRect = aVDiagram.getCurrentRectangle();
747
0
            m_aResultingDiagramRectangleExcludingAxes = BaseGFXHelper::toAwtRectangle(aConsumedInnerRect);
748
0
        }
749
0
    }
750
0
    else
751
0
    {
752
0
        if (rParam.mbUseFixedInnerSize)
753
0
            m_aResultingDiagramRectangleExcludingAxes = rParam.maRemainingSpace;
754
0
        else
755
0
        {
756
0
            ::basegfx::B2IRectangle aConsumedInnerRect = aVDiagram.getCurrentRectangle();
757
0
            m_aResultingDiagramRectangleExcludingAxes = BaseGFXHelper::toAwtRectangle(aConsumedInnerRect);
758
0
        }
759
0
    }
760
761
0
    if (rParam.mxMarkHandles.is())
762
0
    {
763
0
        awt::Point aPos(rParam.maRemainingSpace.X, rParam.maRemainingSpace.Y);
764
0
        awt::Size  aSize(rParam.maRemainingSpace.Width, rParam.maRemainingSpace.Height);
765
766
0
        bool bPosSizeExcludeAxesProperty = true;
767
0
        xDiagram->getPropertyValue(u"PosSizeExcludeAxes"_ustr) >>= bPosSizeExcludeAxesProperty;
768
0
        if (rParam.mbUseFixedInnerSize || bPosSizeExcludeAxesProperty)
769
0
        {
770
0
            aPos = awt::Point( m_aResultingDiagramRectangleExcludingAxes.X, m_aResultingDiagramRectangleExcludingAxes.Y );
771
0
            aSize = awt::Size( m_aResultingDiagramRectangleExcludingAxes.Width, m_aResultingDiagramRectangleExcludingAxes.Height );
772
0
        }
773
0
        rParam.mxMarkHandles->setPosition(aPos);
774
0
        rParam.mxMarkHandles->setSize(aSize);
775
0
    }
776
777
0
    return aUsedOuterRect;
778
0
}
779
780
bool ChartView::getExplicitValuesForAxis(
781
                     const rtl::Reference< Axis >& xAxis
782
                     , ExplicitScaleData&  rExplicitScale
783
                     , ExplicitIncrementData& rExplicitIncrement )
784
0
{
785
0
    SolarMutexGuard aSolarGuard;
786
787
0
    impl_updateView();
788
789
0
    if(!xAxis.is())
790
0
        return false;
791
792
0
    rtl::Reference< BaseCoordinateSystem > xCooSys = AxisHelper::getCoordinateSystemOfAxis(xAxis, mrChartModel.getFirstChartDiagram() );
793
0
    const VCoordinateSystem* pVCooSys = SeriesPlotterContainer::findInCooSysList(m_aVCooSysList, xCooSys);
794
0
    if(!pVCooSys)
795
0
        return false;
796
797
0
    sal_Int32 nDimensionIndex=-1;
798
0
    sal_Int32 nAxisIndex=-1;
799
0
    if( !AxisHelper::getIndicesForAxis( xAxis, xCooSys, nDimensionIndex, nAxisIndex ) )
800
0
        return false;
801
802
0
    rExplicitScale = pVCooSys->getExplicitScale(nDimensionIndex,nAxisIndex);
803
0
    rExplicitIncrement = pVCooSys->getExplicitIncrement(nDimensionIndex,nAxisIndex);
804
0
    if( !rExplicitScale.m_bShiftedCategoryPosition )
805
0
        return true;
806
807
    //remove 'one' from max
808
0
    if( rExplicitScale.AxisType == css::chart2::AxisType::DATE )
809
0
    {
810
0
        Date aMaxDate(rExplicitScale.NullDate); aMaxDate.AddDays(::rtl::math::approxFloor(rExplicitScale.Maximum));
811
        //for explicit scales with shifted categories we need one interval more
812
0
        switch( rExplicitScale.TimeResolution )
813
0
        {
814
0
        case css::chart::TimeUnit::DAY:
815
0
            --aMaxDate;
816
0
            break;
817
0
        case css::chart::TimeUnit::MONTH:
818
0
            aMaxDate = DateHelper::GetDateSomeMonthsAway(aMaxDate,-1);
819
0
            break;
820
0
        case css::chart::TimeUnit::YEAR:
821
0
            aMaxDate = DateHelper::GetDateSomeYearsAway(aMaxDate,-1);
822
0
            break;
823
0
        }
824
0
        rExplicitScale.Maximum = aMaxDate - rExplicitScale.NullDate;
825
0
    }
826
0
    else if( rExplicitScale.AxisType == css::chart2::AxisType::CATEGORY )
827
0
        rExplicitScale.Maximum -= 1.0;
828
0
    else if( rExplicitScale.AxisType == css::chart2::AxisType::SERIES )
829
0
        rExplicitScale.Maximum -= 1.0;
830
0
    return true;
831
0
}
832
833
SdrPage* ChartView::getSdrPage()
834
0
{
835
0
    if(m_xDrawPage)
836
0
        return m_xDrawPage->GetSdrPage();
837
838
0
    return nullptr;
839
0
}
840
841
rtl::Reference< SvxShape > ChartView::getShapeForCID( const OUString& rObjectCID )
842
0
{
843
0
    SolarMutexGuard aSolarGuard;
844
0
    SdrObject* pObj = DrawModelWrapper::getNamedSdrObject( rObjectCID, this->getSdrPage() );
845
0
    if( !pObj )
846
0
        return nullptr;
847
848
0
    uno::Reference< drawing::XShape > xShape = pObj->getUnoShape();
849
0
    rtl::Reference<SvxShape> xShape2 = dynamic_cast<SvxShape*>(xShape.get());
850
0
    assert(xShape2 || !xShape);
851
0
    return xShape2;
852
0
}
853
854
awt::Rectangle ChartView::getDiagramRectangleExcludingAxes()
855
0
{
856
0
    impl_updateView();
857
0
    return m_aResultingDiagramRectangleExcludingAxes;
858
0
}
859
860
awt::Rectangle ChartView::getRectangleOfObject( const OUString& rObjectCID, bool bSnapRect )
861
0
{
862
0
    impl_updateView();
863
864
0
    awt::Rectangle aRet;
865
0
    rtl::Reference< SvxShape > xShape = getShapeForCID(rObjectCID);
866
0
    if(xShape.is())
867
0
    {
868
        //special handling for axis for old api:
869
        //same special handling for diagram
870
0
        ObjectType eObjectType( ObjectIdentifier::getObjectType( rObjectCID ) );
871
0
        if( eObjectType == OBJECTTYPE_AXIS || eObjectType == OBJECTTYPE_DIAGRAM )
872
0
        {
873
0
            SolarMutexGuard aSolarGuard;
874
0
            SdrObject* pRootSdrObject = xShape->GetSdrObject();
875
0
            if( pRootSdrObject )
876
0
            {
877
0
                SdrObjList* pRootList = pRootSdrObject->GetSubList();
878
0
                if( pRootList )
879
0
                {
880
0
                    OUString aShapeName = u"MarkHandles"_ustr;
881
0
                    if( eObjectType == OBJECTTYPE_DIAGRAM )
882
0
                        aShapeName = "PlotAreaIncludingAxes";
883
0
                    SdrObject* pShape = DrawModelWrapper::getNamedSdrObject( aShapeName, pRootList );
884
0
                    if( pShape )
885
0
                    {
886
0
                        xShape = dynamic_cast<SvxShape*>(pShape->getUnoShape().get());
887
0
                        assert(xShape);
888
0
                    }
889
0
                }
890
0
            }
891
0
        }
892
893
0
        awt::Size aSize( xShape->getSize() );
894
0
        awt::Point aPoint( xShape->getPosition() );
895
0
        aRet = awt::Rectangle( aPoint.X, aPoint.Y, aSize.Width, aSize.Height );
896
0
        if( bSnapRect )
897
0
        {
898
            //for rotated objects the shape size and position differs from the visible rectangle
899
0
            SdrObject* pSdrObject = xShape->GetSdrObject();
900
0
            if( pSdrObject )
901
0
            {
902
0
                tools::Rectangle aSnapRect( pSdrObject->GetSnapRect() );
903
0
                aRet = awt::Rectangle(aSnapRect.Left(),aSnapRect.Top(),aSnapRect.GetWidth(),aSnapRect.GetHeight());
904
0
            }
905
0
        }
906
0
    }
907
0
    return aRet;
908
0
}
909
910
std::shared_ptr< DrawModelWrapper > ChartView::getDrawModelWrapper()
911
0
{
912
0
    return m_pDrawModelWrapper;
913
0
}
914
915
namespace
916
{
917
918
constexpr double constPageLayoutDistancePercentage = 0.02;
919
constexpr sal_Int32 constPageLayoutFixedDistance = 350;
920
921
bool getAvailablePosAndSizeForDiagram(
922
    CreateShapeParam2D& rParam, const awt::Size & rPageSize, rtl::Reference<Diagram> const& xDiagram)
923
0
{
924
0
    rParam.mbUseFixedInnerSize = false;
925
926
    //@todo: we need a size dependent on the axis labels
927
0
    rtl::Reference<ChartType> xChartType;
928
0
    if (xDiagram)
929
0
        xChartType = xDiagram->getChartTypeByIndex(0);
930
931
0
    sal_Int32 nXDistance = sal_Int32(rPageSize.Width * constPageLayoutDistancePercentage);
932
0
    sal_Int32 nYDistance = sal_Int32(rPageSize.Height * constPageLayoutDistancePercentage);
933
934
    // Only pie chart uses fixed size margins
935
0
    if (xChartType.is() && xChartType->getChartType() == CHART2_SERVICE_NAME_CHARTTYPE_PIE)
936
0
    {
937
0
        nXDistance = constPageLayoutFixedDistance;
938
0
        nYDistance = constPageLayoutFixedDistance;
939
0
    }
940
941
0
    rParam.maRemainingSpace.X += nXDistance;
942
0
    rParam.maRemainingSpace.Width -= 2*nXDistance;
943
0
    rParam.maRemainingSpace.Y += nYDistance;
944
0
    rParam.maRemainingSpace.Height -= 2*nYDistance;
945
946
0
    bool bPosSizeExcludeAxes = false;
947
0
    if( xDiagram.is() )
948
0
        xDiagram->getPropertyValue( u"PosSizeExcludeAxes"_ustr ) >>= bPosSizeExcludeAxes;
949
950
    //size:
951
0
    css::chart2::RelativeSize aRelativeSize;
952
0
    if( xDiagram.is() && (xDiagram->getPropertyValue( u"RelativeSize"_ustr )>>=aRelativeSize) )
953
0
    {
954
0
        rParam.maRemainingSpace.Height = static_cast<sal_Int32>(aRelativeSize.Secondary*rPageSize.Height);
955
0
        rParam.maRemainingSpace.Width = static_cast<sal_Int32>(aRelativeSize.Primary*rPageSize.Width);
956
0
        rParam.mbUseFixedInnerSize = bPosSizeExcludeAxes;
957
0
    }
958
959
0
    if (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0)
960
0
        return false;
961
962
    //position:
963
0
    chart2::RelativePosition aRelativePosition;
964
0
    if( xDiagram.is() && (xDiagram->getPropertyValue( u"RelativePosition"_ustr )>>=aRelativePosition) )
965
0
    {
966
        //@todo decide whether x is primary or secondary
967
968
        //the coordinates re relative to the page
969
0
        double fX = aRelativePosition.Primary*rPageSize.Width;
970
0
        double fY = aRelativePosition.Secondary*rPageSize.Height;
971
972
0
        awt::Point aPos = RelativePositionHelper::getUpperLeftCornerOfAnchoredObject(
973
0
            awt::Point(static_cast<sal_Int32>(fX),static_cast<sal_Int32>(fY)),
974
0
            awt::Size(rParam.maRemainingSpace.Width, rParam.maRemainingSpace.Height),
975
0
            aRelativePosition.Anchor);
976
977
0
        rParam.maRemainingSpace.X = aPos.X;
978
0
        rParam.maRemainingSpace.Y = aPos.Y;
979
980
0
        rParam.mbUseFixedInnerSize = bPosSizeExcludeAxes;
981
0
    }
982
983
    //ensure that the diagram does not lap out right side or out of bottom
984
0
    if (rParam.maRemainingSpace.Y + rParam.maRemainingSpace.Height > rPageSize.Height)
985
0
        rParam.maRemainingSpace.Height = rPageSize.Height - rParam.maRemainingSpace.Y;
986
987
0
    if (rParam.maRemainingSpace.X + rParam.maRemainingSpace.Width > rPageSize.Width)
988
0
        rParam.maRemainingSpace.Width = rPageSize.Width - rParam.maRemainingSpace.X;
989
990
0
    return true;
991
0
}
992
993
enum class TitleAlignment { ALIGN_LEFT, ALIGN_TOP, ALIGN_RIGHT, ALIGN_BOTTOM, ALIGN_Z };
994
995
void changePositionOfAxisTitle( VTitle* pVTitle, TitleAlignment eAlignment
996
                               , awt::Rectangle const & rDiagramPlusAxesRect, const awt::Size & rPageSize )
997
0
{
998
0
    if(!pVTitle)
999
0
        return;
1000
1001
0
    awt::Point aNewPosition(0,0);
1002
0
    awt::Size aTitleSize = pVTitle->getFinalSize();
1003
0
    sal_Int32 nYDistance = static_cast<sal_Int32>(rPageSize.Height * constPageLayoutDistancePercentage);
1004
0
    sal_Int32 nXDistance = static_cast<sal_Int32>(rPageSize.Width * constPageLayoutDistancePercentage);
1005
0
    switch (eAlignment)
1006
0
    {
1007
0
    case TitleAlignment::ALIGN_TOP:
1008
0
        aNewPosition = awt::Point( rDiagramPlusAxesRect.X + rDiagramPlusAxesRect.Width/2
1009
0
                                    , rDiagramPlusAxesRect.Y - aTitleSize.Height/2  - nYDistance );
1010
0
        break;
1011
0
    case TitleAlignment::ALIGN_BOTTOM:
1012
0
        aNewPosition = awt::Point( rDiagramPlusAxesRect.X + rDiagramPlusAxesRect.Width/2
1013
0
                                    , rDiagramPlusAxesRect.Y + rDiagramPlusAxesRect.Height + aTitleSize.Height/2  + nYDistance );
1014
0
        break;
1015
0
    case TitleAlignment::ALIGN_LEFT:
1016
0
        aNewPosition = awt::Point( rDiagramPlusAxesRect.X - aTitleSize.Width/2 - nXDistance
1017
0
                                    , rDiagramPlusAxesRect.Y + rDiagramPlusAxesRect.Height/2 );
1018
0
        break;
1019
0
    case TitleAlignment::ALIGN_RIGHT:
1020
0
        aNewPosition = awt::Point( rDiagramPlusAxesRect.X + rDiagramPlusAxesRect.Width + aTitleSize.Width/2 + nXDistance
1021
0
                                    , rDiagramPlusAxesRect.Y + rDiagramPlusAxesRect.Height/2 );
1022
0
        break;
1023
0
    case TitleAlignment::ALIGN_Z:
1024
0
        aNewPosition = awt::Point( rDiagramPlusAxesRect.X + rDiagramPlusAxesRect.Width + aTitleSize.Width/2 + nXDistance
1025
0
                                    , rDiagramPlusAxesRect.Y + rDiagramPlusAxesRect.Height - aTitleSize.Height/2 );
1026
0
       break;
1027
0
    }
1028
1029
0
    sal_Int32 nMaxY = rPageSize.Height - aTitleSize.Height/2;
1030
0
    sal_Int32 nMaxX = rPageSize.Width - aTitleSize.Width/2;
1031
0
    sal_Int32 nMinX = aTitleSize.Width/2;
1032
0
    sal_Int32 nMinY = aTitleSize.Height/2;
1033
0
    if( aNewPosition.Y > nMaxY )
1034
0
        aNewPosition.Y = nMaxY;
1035
0
    if( aNewPosition.X > nMaxX )
1036
0
        aNewPosition.X = nMaxX;
1037
0
    if( aNewPosition.Y < nMinY )
1038
0
        aNewPosition.Y = nMinY;
1039
0
    if( aNewPosition.X < nMinX )
1040
0
        aNewPosition.X = nMinX;
1041
1042
0
    pVTitle->changePosition( aNewPosition );
1043
0
}
1044
1045
std::shared_ptr<VTitle> lcl_createTitle( TitleHelper::eTitleType eType
1046
                , const rtl::Reference<SvxShapeGroupAnyD>& xPageShapes
1047
                , ChartModel& rModel
1048
                , awt::Rectangle& rRemainingSpace
1049
                , const awt::Size & rPageSize
1050
                , TitleAlignment eAlignment
1051
                , bool& rbAutoPosition )
1052
0
{
1053
0
    std::shared_ptr<VTitle> apVTitle;
1054
1055
    // #i109336# Improve auto positioning in chart
1056
0
    double fPercentage = constPageLayoutDistancePercentage;
1057
0
    sal_Int32 nXDistance = static_cast< sal_Int32 >( rPageSize.Width * fPercentage );
1058
0
    sal_Int32 nYDistance = static_cast< sal_Int32 >( rPageSize.Height * fPercentage );
1059
0
    if ( eType == TitleHelper::MAIN_TITLE )
1060
0
    {
1061
0
        nYDistance += 135; // 1/100 mm
1062
0
    }
1063
0
    else if ( eType == TitleHelper::TITLE_AT_STANDARD_X_AXIS_POSITION )
1064
0
    {
1065
0
        nYDistance = 420; // 1/100 mm
1066
0
    }
1067
0
    else if ( eType == TitleHelper::TITLE_AT_STANDARD_Y_AXIS_POSITION )
1068
0
    {
1069
0
        nXDistance = 450; // 1/100 mm
1070
0
    }
1071
1072
0
    rtl::Reference< Title > xTitle( TitleHelper::getTitle( eType, rModel ) );
1073
0
    OUString aCompleteString = TitleHelper::getCompleteString(xTitle);
1074
0
    if (aCompleteString.isEmpty() || !VTitle::isVisible(xTitle))
1075
0
        return apVTitle;
1076
1077
    //create title
1078
0
    awt::Size aTextMaxWidth(rPageSize.Width, rPageSize.Height);
1079
0
    bool bYAxisTitle = false;
1080
0
    if (eType == TitleHelper::MAIN_TITLE || eType == TitleHelper::SUB_TITLE)
1081
0
    {
1082
0
        aTextMaxWidth.Width = static_cast<sal_Int32>(rPageSize.Width * 0.8);
1083
0
        aTextMaxWidth.Height = static_cast<sal_Int32>(rPageSize.Height * 0.5);
1084
0
    }
1085
0
    else if (eType == TitleHelper::X_AXIS_TITLE || eType == TitleHelper::SECONDARY_X_AXIS_TITLE
1086
0
             || eType == TitleHelper::TITLE_AT_STANDARD_X_AXIS_POSITION)
1087
0
    {
1088
0
        aTextMaxWidth.Width = static_cast<sal_Int32>(rPageSize.Width * 0.8);
1089
0
        aTextMaxWidth.Height = static_cast<sal_Int32>(rPageSize.Height * 0.2);
1090
0
    }
1091
0
    else if (eType == TitleHelper::Y_AXIS_TITLE || eType == TitleHelper::SECONDARY_Y_AXIS_TITLE
1092
0
             || eType == TitleHelper::TITLE_AT_STANDARD_Y_AXIS_POSITION)
1093
0
    {
1094
0
        aTextMaxWidth.Width = static_cast<sal_Int32>(rPageSize.Width * 0.2);
1095
0
        aTextMaxWidth.Height = static_cast<sal_Int32>(rPageSize.Height * 0.8);
1096
0
        bYAxisTitle = true;
1097
0
    }
1098
0
    apVTitle = std::make_shared<VTitle>(xTitle);
1099
0
    OUString aCID = ObjectIdentifier::createClassifiedIdentifierForObject(xTitle, &rModel);
1100
0
    apVTitle->init(xPageShapes, aCID);
1101
0
    apVTitle->createShapes(awt::Point(0, 0), rPageSize, aTextMaxWidth, bYAxisTitle);
1102
0
    awt::Size aTitleUnrotatedSize = apVTitle->getUnrotatedSize();
1103
0
    awt::Size aTitleSize = apVTitle->getFinalSize();
1104
1105
    //position
1106
0
    rbAutoPosition = true;
1107
0
    awt::Point aNewPosition(0,0);
1108
0
    chart2::RelativePosition aRelativePosition;
1109
0
    if (xTitle.is() && (xTitle->getPropertyValue(u"RelativePosition"_ustr) >>= aRelativePosition))
1110
0
    {
1111
0
        rbAutoPosition = false;
1112
1113
        //@todo decide whether x is primary or secondary
1114
0
        double fX = aRelativePosition.Primary*rPageSize.Width;
1115
0
        double fY = aRelativePosition.Secondary*rPageSize.Height;
1116
1117
0
        double fAnglePi = apVTitle->getRotationAnglePi();
1118
0
        aNewPosition = RelativePositionHelper::getCenterOfAnchoredObject(
1119
0
                awt::Point(static_cast<sal_Int32>(fX),static_cast<sal_Int32>(fY))
1120
0
                , aTitleUnrotatedSize, aRelativePosition.Anchor, fAnglePi );
1121
1122
0
        const bool bInfiniteY = std::isinf(aRelativePosition.Secondary);
1123
0
        if (bInfiniteY)
1124
0
        {
1125
0
            SAL_WARN("chart2", "infinite aRelativePosition.Secondary position, using ALIGN_BOTTOM");
1126
0
            aNewPosition.Y = rRemainingSpace.Y + rRemainingSpace.Height - aTitleSize.Height/2 - nYDistance;
1127
0
        }
1128
1129
0
        const bool bInfiniteX = std::isinf(aRelativePosition.Primary);
1130
0
        if (bInfiniteX)
1131
0
        {
1132
0
            SAL_WARN("chart2", "infinite aRelativePosition.Primary position, using ALIGN_RIGHT");
1133
0
            aNewPosition.X = rRemainingSpace.X + rRemainingSpace.Width - aTitleSize.Width/2 - nXDistance;
1134
0
        }
1135
0
    }
1136
0
    else //auto position
1137
0
    {
1138
0
        switch( eAlignment )
1139
0
        {
1140
0
        case TitleAlignment::ALIGN_TOP:
1141
0
            aNewPosition = awt::Point( rRemainingSpace.X + rRemainingSpace.Width/2
1142
0
                                     , rRemainingSpace.Y + aTitleSize.Height/2 + nYDistance );
1143
0
            break;
1144
0
        case TitleAlignment::ALIGN_BOTTOM:
1145
0
            aNewPosition = awt::Point( rRemainingSpace.X + rRemainingSpace.Width/2
1146
0
                                     , rRemainingSpace.Y + rRemainingSpace.Height - aTitleSize.Height/2 - nYDistance );
1147
0
            break;
1148
0
        case TitleAlignment::ALIGN_LEFT:
1149
0
            aNewPosition = awt::Point( rRemainingSpace.X + aTitleSize.Width/2 + nXDistance
1150
0
                                     , rRemainingSpace.Y + rRemainingSpace.Height/2 );
1151
0
            break;
1152
0
        case TitleAlignment::ALIGN_RIGHT:
1153
0
            aNewPosition = awt::Point( rRemainingSpace.X + rRemainingSpace.Width - aTitleSize.Width/2 - nXDistance
1154
0
                                     , rRemainingSpace.Y + rRemainingSpace.Height/2 );
1155
0
            break;
1156
0
        case TitleAlignment::ALIGN_Z:
1157
0
            break;
1158
1159
0
        }
1160
0
    }
1161
0
    apVTitle->changePosition( aNewPosition );
1162
1163
    //remaining space
1164
0
    switch( eAlignment )
1165
0
    {
1166
0
        case TitleAlignment::ALIGN_TOP:
1167
            // Push the remaining space down from top.
1168
0
            rRemainingSpace.Y += ( aTitleSize.Height + nYDistance );
1169
0
            rRemainingSpace.Height -= ( aTitleSize.Height + nYDistance );
1170
0
            break;
1171
0
        case TitleAlignment::ALIGN_BOTTOM:
1172
            // Push the remaining space up from bottom.
1173
0
            rRemainingSpace.Height -= ( aTitleSize.Height + nYDistance );
1174
0
            break;
1175
0
        case TitleAlignment::ALIGN_LEFT:
1176
            // Push the remaining space to the right from left edge.
1177
0
            rRemainingSpace.X += ( aTitleSize.Width + nXDistance );
1178
0
            rRemainingSpace.Width -= ( aTitleSize.Width + nXDistance );
1179
0
            break;
1180
0
        case TitleAlignment::ALIGN_RIGHT:
1181
            // Push the remaining space to the left from right edge.
1182
0
            rRemainingSpace.Width -= ( aTitleSize.Width + nXDistance );
1183
0
            break;
1184
0
        case TitleAlignment::ALIGN_Z:
1185
0
            break;
1186
0
    }
1187
1188
0
    return apVTitle;
1189
0
}
1190
1191
bool lcl_createLegend( const rtl::Reference< Legend > & xLegend
1192
                   , const rtl::Reference<SvxShapeGroupAnyD>& xPageShapes
1193
                   , const uno::Reference< uno::XComponentContext > & xContext
1194
                   , awt::Rectangle & rRemainingSpace
1195
                   , const awt::Size & rPageSize
1196
                   , ChartModel& rModel
1197
                   , std::vector< LegendEntryProvider* >&& rLegendEntryProviderList
1198
                   , sal_Int16 nDefaultWritingMode )
1199
0
{
1200
0
    if (!VLegend::isVisible(xLegend))
1201
0
        return false;
1202
1203
0
    awt::Size rDefaultLegendSize;
1204
0
    VLegend aVLegend( xLegend, xContext, std::move(rLegendEntryProviderList),
1205
0
            xPageShapes, rModel);
1206
0
    aVLegend.setDefaultWritingMode( nDefaultWritingMode );
1207
0
    aVLegend.createShapes( awt::Size( rRemainingSpace.Width, rRemainingSpace.Height ),
1208
0
                           rPageSize, rDefaultLegendSize );
1209
0
    aVLegend.changePosition( rRemainingSpace, rPageSize, rDefaultLegendSize );
1210
0
    return true;
1211
0
}
1212
1213
void lcl_createButtons(const rtl::Reference<SvxShapeGroupAnyD>& xPageShapes,
1214
                       ChartModel& rModel,
1215
                       awt::Rectangle& rRemainingSpace)
1216
0
{
1217
0
    uno::Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider(rModel.getDataProvider(), uno::UNO_QUERY);
1218
0
    if (!xPivotTableDataProvider.is())
1219
0
        return;
1220
1221
0
    uno::Reference<beans::XPropertySet> xModelPage(rModel.getPageBackground());
1222
1223
0
    awt::Size aSize(4000, 700); // size of the button
1224
1225
0
    tools::Long x = 0;
1226
1227
0
    if (xPivotTableDataProvider->getPageFields().hasElements())
1228
0
    {
1229
0
        x = 0;
1230
1231
0
        const css::uno::Sequence<chart2::data::PivotTableFieldEntry> aPivotFieldEntries = xPivotTableDataProvider->getPageFields();
1232
0
        for (css::chart2::data::PivotTableFieldEntry const & rPageFieldEntry : aPivotFieldEntries)
1233
0
        {
1234
0
            VButton aButton;
1235
0
            aButton.init(xPageShapes);
1236
0
            awt::Point aNewPosition(rRemainingSpace.X + x + 100, rRemainingSpace.Y + 100);
1237
0
            sal_Int32 nDimensionIndex = rPageFieldEntry.DimensionIndex;
1238
0
            OUString aFieldOutputDescription = xPivotTableDataProvider->getFieldOutputDescription(nDimensionIndex);
1239
0
            aButton.setLabel(rPageFieldEntry.Name + " | " + aFieldOutputDescription);
1240
0
            aButton.setCID("FieldButton.Page." + OUString::number(nDimensionIndex));
1241
0
            aButton.setPosition(aNewPosition);
1242
0
            aButton.setSize(aSize);
1243
0
            if (rPageFieldEntry.HasHiddenMembers)
1244
0
                aButton.setArrowColor(Color(0x0000FF));
1245
1246
0
            aButton.createShapes(xModelPage);
1247
0
            x += aSize.Width + 100;
1248
0
        }
1249
0
        rRemainingSpace.Y += (aSize.Height + 100 + 100);
1250
0
        rRemainingSpace.Height -= (aSize.Height + 100 + 100);
1251
0
    }
1252
1253
0
    aSize = awt::Size(3000, 700); // size of the button
1254
1255
0
    if (!xPivotTableDataProvider->getRowFields().hasElements())
1256
0
        return;
1257
1258
0
    x = 200;
1259
0
    const css::uno::Sequence<chart2::data::PivotTableFieldEntry> aPivotFieldEntries = xPivotTableDataProvider->getRowFields();
1260
0
    for (css::chart2::data::PivotTableFieldEntry const & rRowFieldEntry : aPivotFieldEntries)
1261
0
    {
1262
0
        VButton aButton;
1263
0
        aButton.init(xPageShapes);
1264
0
        awt::Point aNewPosition(rRemainingSpace.X + x + 100,
1265
0
                                rRemainingSpace.Y + rRemainingSpace.Height - aSize.Height - 100);
1266
0
        aButton.setLabel(rRowFieldEntry.Name);
1267
0
        aButton.setCID("FieldButton.Row." + OUString::number(rRowFieldEntry.DimensionIndex));
1268
0
        aButton.setPosition(aNewPosition);
1269
0
        aButton.setSize(aSize);
1270
0
        if ( rRowFieldEntry.Name == "Data" )
1271
0
        {
1272
0
            aButton.setBGColor( Color(0x00F6F6F6) );
1273
0
            aButton.showArrow( false );
1274
0
        }
1275
0
        else if (rRowFieldEntry.HasHiddenMembers)
1276
0
            aButton.setArrowColor(Color(0x0000FF));
1277
0
        aButton.createShapes(xModelPage);
1278
0
        x += aSize.Width + 100;
1279
0
    }
1280
0
    rRemainingSpace.Height -= (aSize.Height + 100 + 100);
1281
0
}
1282
1283
void formatPage(
1284
      ChartModel& rChartModel
1285
    , const awt::Size& rPageSize
1286
    , const rtl::Reference<SvxShapeGroupAnyD>& xTarget
1287
    )
1288
0
{
1289
0
    try
1290
0
    {
1291
0
        uno::Reference< beans::XPropertySet > xModelPage( rChartModel.getPageBackground());
1292
0
        if( ! xModelPage.is())
1293
0
            return;
1294
1295
        //format page
1296
0
        tPropertyNameValueMap aNameValueMap;
1297
0
        PropertyMapper::getValueMap( aNameValueMap, PropertyMapper::getPropertyNameMapForFillAndLineProperties(), xModelPage );
1298
1299
0
        OUString aCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, u"" ) );
1300
0
        aNameValueMap.emplace( "Name", uno::Any( aCID ) ); //CID OUString
1301
1302
0
        tNameSequence aNames;
1303
0
        tAnySequence aValues;
1304
0
        PropertyMapper::getMultiPropertyListsFromValueMap( aNames, aValues, aNameValueMap );
1305
1306
0
        ShapeFactory::createRectangle(
1307
0
            xTarget, rPageSize, awt::Point(0, 0), aNames, aValues);
1308
0
    }
1309
0
    catch( const uno::Exception & )
1310
0
    {
1311
0
        DBG_UNHANDLED_EXCEPTION("chart2" );
1312
0
    }
1313
0
}
1314
1315
void lcl_removeEmptyGroupShapes( const SdrObject& rParent )
1316
0
{
1317
0
    SdrObjList* pObjList = rParent.getChildrenOfSdrObject();
1318
0
    if (!pObjList || pObjList->GetObjCount() == 0)
1319
0
        return;
1320
1321
    //iterate from back!
1322
0
    for(auto nIdx = static_cast<sal_Int32>(pObjList->GetObjCount() - 1); nIdx >= 0; --nIdx)
1323
0
    {
1324
0
        SdrObject* pChildSdrObject = pObjList->GetObj(nIdx);
1325
0
        SdrObjList* pChildObjList = pChildSdrObject->getChildrenOfSdrObject();
1326
0
        if (!pChildObjList)
1327
0
            continue;
1328
0
        if (pChildObjList->GetObjCount() == 0)
1329
0
        {
1330
            //remove empty group shape
1331
0
            pObjList->NbcRemoveObject(nIdx);
1332
0
        }
1333
0
        else
1334
0
            lcl_removeEmptyGroupShapes(*pChildSdrObject);
1335
0
    }
1336
0
}
1337
1338
}
1339
1340
void ChartView::impl_refreshAddIn()
1341
0
{
1342
0
    if( !m_bRefreshAddIn )
1343
0
        return;
1344
1345
0
    uno::Reference< beans::XPropertySet > xProp( static_cast< ::cppu::OWeakObject* >( &mrChartModel ), uno::UNO_QUERY );
1346
0
    if( !xProp.is())
1347
0
        return;
1348
1349
0
    try
1350
0
    {
1351
0
        uno::Reference< util::XRefreshable > xAddIn;
1352
0
        xProp->getPropertyValue( u"AddIn"_ustr ) >>= xAddIn;
1353
0
        if( xAddIn.is() )
1354
0
        {
1355
0
            bool bRefreshAddInAllowed = true;
1356
0
            xProp->getPropertyValue( u"RefreshAddInAllowed"_ustr ) >>= bRefreshAddInAllowed;
1357
0
            if( bRefreshAddInAllowed )
1358
0
                xAddIn->refresh();
1359
0
        }
1360
0
    }
1361
0
    catch( const uno::Exception& )
1362
0
    {
1363
0
        TOOLS_WARN_EXCEPTION("chart2", "" );
1364
0
    }
1365
0
}
1366
1367
void ChartView::createShapes()
1368
0
{
1369
0
    SolarMutexGuard aSolarGuard;
1370
1371
0
    std::unique_lock aTimedGuard(maTimeMutex);
1372
0
    if(mrChartModel.isTimeBased())
1373
0
    {
1374
0
        maTimeBased.bTimeBased = true;
1375
0
    }
1376
1377
    //make sure add-in is refreshed after creating the shapes
1378
0
    const ::comphelper::ScopeGuard aGuard( [this]() { this->impl_refreshAddIn(); } );
1379
1380
0
    m_aResultingDiagramRectangleExcludingAxes = awt::Rectangle(0,0,0,0);
1381
0
    impl_deleteCoordinateSystems();
1382
0
    if( m_pDrawModelWrapper )
1383
0
    {
1384
        // #i12587# support for shapes in chart
1385
0
        m_pDrawModelWrapper->getSdrModel().EnableUndo( false );
1386
0
        m_pDrawModelWrapper->clearMainDrawPage();
1387
0
    }
1388
1389
0
    lcl_setDefaultWritingMode( m_pDrawModelWrapper, mrChartModel );
1390
1391
0
    awt::Size aPageSize = mrChartModel.getVisualAreaSize( embed::Aspects::MSOLE_CONTENT );
1392
1393
0
    if(!mxRootShape.is())
1394
0
        mxRootShape = ShapeFactory::getOrCreateChartRootShape( m_xDrawPage );
1395
1396
0
    SdrPage* pPage = getSdrPage();
1397
1398
0
    if (pPage) //it is necessary to use the implementation here as the uno page does not provide a propertyset
1399
0
    {
1400
0
        pPage->SetSize(Size(aPageSize.Width,aPageSize.Height));
1401
0
    }
1402
0
    else
1403
0
    {
1404
0
        OSL_FAIL("could not set page size correctly");
1405
0
    }
1406
0
    ShapeFactory::setPageSize(mxRootShape, aPageSize);
1407
1408
0
    createShapes2D(aPageSize);
1409
1410
    // #i12587# support for shapes in chart
1411
0
    if ( m_pDrawModelWrapper )
1412
0
    {
1413
0
        m_pDrawModelWrapper->getSdrModel().EnableUndo( true );
1414
0
    }
1415
1416
0
    if(maTimeBased.bTimeBased)
1417
0
    {
1418
0
        maTimeBased.nFrame++;
1419
0
    }
1420
0
}
1421
1422
// util::XEventListener (base of XCloseListener)
1423
void SAL_CALL ChartView::disposing( const lang::EventObject& /* rSource */ )
1424
0
{
1425
0
}
1426
1427
namespace
1428
{
1429
// Disables setting the chart's modified state, as well as its parent's (if exists).
1430
// Painting a chart must not set these states.
1431
struct ChartModelDisableSetModified
1432
{
1433
    ChartModel& mrChartModel;
1434
    SfxObjectShell* mpParentShell;
1435
    bool mbWasUnmodified;
1436
    ChartModelDisableSetModified(ChartModel& rChartModel)
1437
0
        : mrChartModel(rChartModel)
1438
0
        , mpParentShell(SfxObjectShell::GetShellFromComponent(rChartModel.getParent()))
1439
0
        , mbWasUnmodified(!rChartModel.isModified())
1440
0
    {
1441
0
        if (mpParentShell && mpParentShell->IsEnableSetModified())
1442
0
            mpParentShell->EnableSetModified(false);
1443
0
        else
1444
0
            mpParentShell = nullptr;
1445
0
    }
1446
    ~ChartModelDisableSetModified()
1447
0
    {
1448
0
        if (mbWasUnmodified && mrChartModel.isModified())
1449
0
            mrChartModel.setModified(false);
1450
0
        if (mpParentShell)
1451
0
            mpParentShell->EnableSetModified(true);
1452
0
    }
1453
};
1454
}
1455
1456
void ChartView::impl_updateView( bool bCheckLockedCtrler )
1457
0
{
1458
0
    if( !m_pDrawModelWrapper )
1459
0
        return;
1460
1461
    // #i12587# support for shapes in chart
1462
0
    if ( m_bSdrViewIsInEditMode )
1463
0
    {
1464
0
        return;
1465
0
    }
1466
1467
0
    if (bCheckLockedCtrler && mrChartModel.hasControllersLocked())
1468
0
        return;
1469
1470
0
    if( !m_bViewDirty || m_bInViewUpdate )
1471
0
        return;
1472
1473
0
    m_bInViewUpdate = true;
1474
    //bool bOldRefreshAddIn = m_bRefreshAddIn;
1475
    //m_bRefreshAddIn = false;
1476
0
    try
1477
0
    {
1478
0
        impl_notifyModeChangeListener(u"invalid"_ustr);
1479
1480
        //prepare draw model
1481
0
        {
1482
0
            SolarMutexGuard aSolarGuard;
1483
0
            m_pDrawModelWrapper->lockControllers();
1484
0
        }
1485
1486
        // Rendering the chart must not set its (or its parent) modified status
1487
0
        ChartModelDisableSetModified dontSetModified(mrChartModel);
1488
1489
        //create chart view
1490
0
        {
1491
0
            m_bViewDirty = false;
1492
0
            m_bViewUpdatePending = false;
1493
0
            createShapes();
1494
1495
0
            if( m_bViewDirty )
1496
0
            {
1497
                //avoid recursions due to add-in
1498
0
                m_bRefreshAddIn = false;
1499
0
                m_bViewDirty = false;
1500
0
                m_bViewUpdatePending = false;
1501
                //delete old chart view
1502
0
                createShapes();
1503
0
                m_bRefreshAddIn = true;
1504
0
            }
1505
0
        }
1506
1507
0
        m_bViewDirty = m_bViewUpdatePending;
1508
0
        m_bViewUpdatePending = false;
1509
0
        m_bInViewUpdate = false;
1510
0
    }
1511
0
    catch( const uno::Exception& )
1512
0
    {
1513
0
        DBG_UNHANDLED_EXCEPTION("chart2" );
1514
0
        m_bViewDirty = m_bViewUpdatePending;
1515
0
        m_bViewUpdatePending = false;
1516
0
        m_bInViewUpdate = false;
1517
0
    }
1518
1519
0
    {
1520
0
        SolarMutexGuard aSolarGuard;
1521
0
        m_pDrawModelWrapper->unlockControllers();
1522
0
    }
1523
1524
0
    impl_notifyModeChangeListener(u"valid"_ustr);
1525
1526
    //m_bRefreshAddIn = bOldRefreshAddIn;
1527
0
}
1528
1529
// ____ XModifyListener ____
1530
void SAL_CALL ChartView::modified( const lang::EventObject& /* aEvent */ )
1531
0
{
1532
0
    m_bViewDirty = true;
1533
0
    if( m_bInViewUpdate )
1534
0
        m_bViewUpdatePending = true;
1535
1536
0
    impl_notifyModeChangeListener(u"dirty"_ustr);
1537
0
}
1538
1539
//SfxListener
1540
void ChartView::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
1541
0
{
1542
    //#i77362 change notification for changes on additional shapes are missing
1543
0
    if( m_bInViewUpdate )
1544
0
        return;
1545
1546
    // #i12587# support for shapes in chart
1547
0
    if ( m_bSdrViewIsInEditMode )
1548
0
    {
1549
0
        uno::Reference< view::XSelectionSupplier > xSelectionSupplier( mrChartModel.getCurrentController(), uno::UNO_QUERY );
1550
0
        if ( xSelectionSupplier.is() )
1551
0
        {
1552
0
            OUString aSelObjCID;
1553
0
            uno::Any aSelObj( xSelectionSupplier->getSelection() );
1554
0
            aSelObj >>= aSelObjCID;
1555
0
            if ( !aSelObjCID.isEmpty() )
1556
0
            {
1557
0
                return;
1558
0
            }
1559
0
        }
1560
0
    }
1561
1562
0
    if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
1563
0
        return;
1564
0
    const SdrHint* pSdrHint = static_cast< const SdrHint* >(&rHint);
1565
1566
0
    bool bShapeChanged = false;
1567
0
    switch( pSdrHint->GetKind() )
1568
0
    {
1569
0
         case SdrHintKind::ObjectChange:
1570
0
            bShapeChanged = true;
1571
0
            break;
1572
0
        case SdrHintKind::ObjectInserted:
1573
0
            bShapeChanged = true;
1574
0
            break;
1575
0
        case SdrHintKind::ObjectRemoved:
1576
0
            bShapeChanged = true;
1577
0
            break;
1578
0
        case SdrHintKind::ModelCleared:
1579
0
            bShapeChanged = true;
1580
0
            break;
1581
0
        case SdrHintKind::EndEdit:
1582
0
            bShapeChanged = true;
1583
0
            break;
1584
0
        default:
1585
0
            break;
1586
0
    }
1587
1588
0
    if(bShapeChanged)
1589
0
    {
1590
        //#i76053# do not send view modified notifications for changes on the hidden page which contains e.g. the symbols for the dialogs
1591
0
        if( ChartView::getSdrPage() != pSdrHint->GetPage() )
1592
0
            bShapeChanged=false;
1593
0
    }
1594
1595
0
    if(!bShapeChanged)
1596
0
        return;
1597
1598
0
    mrChartModel.setModified(true);
1599
0
}
1600
1601
void ChartView::impl_notifyModeChangeListener( const OUString& rNewMode )
1602
0
{
1603
0
    try
1604
0
    {
1605
0
        std::unique_lock g(m_aMutex);
1606
0
        if( m_aModeChangeListeners.getLength(g) )
1607
0
        {
1608
0
            util::ModeChangeEvent aEvent( static_cast< uno::XWeak* >( this ), rNewMode );
1609
0
            m_aModeChangeListeners.notifyEach( g, &css::util::XModeChangeListener::modeChanged, aEvent);
1610
0
        }
1611
0
    }
1612
0
    catch( const uno::Exception& )
1613
0
    {
1614
0
        DBG_UNHANDLED_EXCEPTION("chart2");
1615
0
    }
1616
0
}
1617
1618
// ____ XModeChangeBroadcaster ____
1619
1620
void SAL_CALL ChartView::addModeChangeListener( const uno::Reference< util::XModeChangeListener >& xListener )
1621
0
{
1622
0
    std::unique_lock g(m_aMutex);
1623
0
    m_aModeChangeListeners.addInterface(g, xListener );
1624
0
}
1625
void SAL_CALL ChartView::removeModeChangeListener( const uno::Reference< util::XModeChangeListener >& xListener )
1626
0
{
1627
0
    std::unique_lock g(m_aMutex);
1628
0
    m_aModeChangeListeners.removeInterface(g, xListener );
1629
0
}
1630
void SAL_CALL ChartView::addModeChangeApproveListener( const uno::Reference< util::XModeChangeApproveListener >& /* _rxListener */ )
1631
0
{
1632
1633
0
}
1634
void SAL_CALL ChartView::removeModeChangeApproveListener( const uno::Reference< util::XModeChangeApproveListener >& /* _rxListener */ )
1635
0
{
1636
1637
0
}
1638
1639
// ____ XUpdatable ____
1640
void SAL_CALL ChartView::update()
1641
0
{
1642
0
    impl_updateView();
1643
1644
    //#i100778# migrate all imported or old documents to a plot area sizing exclusive axes (in case the save settings allow for this):
1645
    //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.
1646
    //When a view update is requested (what happens for creating the metafile or displaying
1647
    //the chart in edit mode or printing) it is most likely that all necessary information is available - like the underlying spreadsheet data for example.
1648
    //Those data are important for the correct axis label sizes which are needed during conversion.
1649
0
    if( DiagramHelper::switchDiagramPositioningToExcludingPositioning( mrChartModel, true, false ) )
1650
0
        impl_updateView();
1651
0
}
1652
1653
void SAL_CALL ChartView::updateSoft()
1654
0
{
1655
0
    update();
1656
0
}
1657
1658
void SAL_CALL ChartView::updateHard()
1659
0
{
1660
0
    impl_updateView(false);
1661
0
}
1662
1663
// ____ XPropertySet ____
1664
Reference< beans::XPropertySetInfo > SAL_CALL ChartView::getPropertySetInfo()
1665
0
{
1666
0
    OSL_FAIL("not implemented");
1667
0
    return nullptr;
1668
0
}
1669
1670
void SAL_CALL ChartView::setPropertyValue( const OUString& rPropertyName
1671
                                                     , const Any& rValue )
1672
0
{
1673
0
    if( rPropertyName == "Resolution" )
1674
0
    {
1675
0
        awt::Size aNewResolution;
1676
0
        if( ! (rValue >>= aNewResolution) )
1677
0
            throw lang::IllegalArgumentException( u"Property 'Resolution' requires value of type awt::Size"_ustr, nullptr, 0 );
1678
1679
0
        if( m_aPageResolution.Width!=aNewResolution.Width || m_aPageResolution.Height!=aNewResolution.Height )
1680
0
        {
1681
            //set modified only when the new resolution is higher and points were skipped before
1682
0
            bool bSetModified = m_bPointsWereSkipped && (m_aPageResolution.Width<aNewResolution.Width || m_aPageResolution.Height<aNewResolution.Height);
1683
1684
0
            m_aPageResolution = aNewResolution;
1685
1686
0
            if( bSetModified )
1687
0
                this->modified( lang::EventObject(  static_cast< uno::XWeak* >( this )  ) );
1688
0
        }
1689
0
    }
1690
0
    else if( rPropertyName == "ZoomFactors" )
1691
0
    {
1692
        //#i75867# poor quality of ole's alternative view with 3D scenes and zoomfactors besides 100%
1693
0
        uno::Sequence< beans::PropertyValue > aZoomFactors;
1694
0
        if( ! (rValue >>= aZoomFactors) )
1695
0
            throw lang::IllegalArgumentException( u"Property 'ZoomFactors' requires value of type Sequence< PropertyValue >"_ustr, nullptr, 0 );
1696
1697
0
        for (auto& propval : aZoomFactors)
1698
0
        {
1699
0
            if (propval.Name == "ScaleXNumerator")
1700
0
                propval.Value >>= m_nScaleXNumerator;
1701
0
            else if (propval.Name == "ScaleXDenominator")
1702
0
                propval.Value >>= m_nScaleXDenominator;
1703
0
            else if (propval.Name == "ScaleYNumerator")
1704
0
                propval.Value >>= m_nScaleYNumerator;
1705
0
            else if (propval.Name == "ScaleYDenominator")
1706
0
                propval.Value >>= m_nScaleYDenominator;
1707
0
        }
1708
0
    }
1709
0
    else if( rPropertyName == "SdrViewIsInEditMode" )
1710
0
    {
1711
        //#i77362 change notification for changes on additional shapes are missing
1712
0
        if( ! (rValue >>= m_bSdrViewIsInEditMode) )
1713
0
            throw lang::IllegalArgumentException( u"Property 'SdrViewIsInEditMode' requires value of type sal_Bool"_ustr, nullptr, 0 );
1714
0
    }
1715
0
    else
1716
0
        throw beans::UnknownPropertyException( "unknown property was tried to set to chart wizard " + rPropertyName, nullptr );
1717
0
}
1718
1719
Any SAL_CALL ChartView::getPropertyValue( const OUString& rPropertyName )
1720
0
{
1721
0
    if( rPropertyName != "Resolution" )
1722
0
        throw beans::UnknownPropertyException( "unknown property was tried to get from chart wizard " + rPropertyName, nullptr );
1723
1724
0
    return Any(m_aPageResolution);
1725
0
}
1726
1727
void SAL_CALL ChartView::addPropertyChangeListener(
1728
    const OUString& /* aPropertyName */, const Reference< beans::XPropertyChangeListener >& /* xListener */ )
1729
0
{
1730
0
    OSL_FAIL("not implemented");
1731
0
}
1732
void SAL_CALL ChartView::removePropertyChangeListener(
1733
    const OUString& /* aPropertyName */, const Reference< beans::XPropertyChangeListener >& /* aListener */ )
1734
0
{
1735
0
    OSL_FAIL("not implemented");
1736
0
}
1737
1738
void SAL_CALL ChartView::addVetoableChangeListener( const OUString& /* PropertyName */, const Reference< beans::XVetoableChangeListener >& /* aListener */ )
1739
0
{
1740
0
    OSL_FAIL("not implemented");
1741
0
}
1742
1743
void SAL_CALL ChartView::removeVetoableChangeListener( const OUString& /* PropertyName */, const Reference< beans::XVetoableChangeListener >& /* aListener */ )
1744
0
{
1745
0
    OSL_FAIL("not implemented");
1746
0
}
1747
1748
// ____ XMultiServiceFactory ____
1749
1750
Reference< uno::XInterface > ChartView::createInstance( const OUString& aServiceSpecifier )
1751
0
{
1752
0
    SolarMutexGuard aSolarGuard;
1753
1754
0
    SdrModel* pModel = ( m_pDrawModelWrapper ? &m_pDrawModelWrapper->getSdrModel() : nullptr );
1755
0
    if ( pModel )
1756
0
    {
1757
0
        if ( aServiceSpecifier == "com.sun.star.drawing.DashTable" )
1758
0
        {
1759
0
            if ( !m_xDashTable.is() )
1760
0
            {
1761
0
                m_xDashTable = SvxUnoDashTable_createInstance( pModel );
1762
0
            }
1763
0
            return m_xDashTable;
1764
0
        }
1765
0
        else if ( aServiceSpecifier == "com.sun.star.drawing.GradientTable" )
1766
0
        {
1767
0
            if ( !m_xGradientTable.is() )
1768
0
            {
1769
0
                m_xGradientTable = SvxUnoGradientTable_createInstance( pModel );
1770
0
            }
1771
0
            return m_xGradientTable;
1772
0
        }
1773
0
        else if ( aServiceSpecifier == "com.sun.star.drawing.HatchTable" )
1774
0
        {
1775
0
            if ( !m_xHatchTable.is() )
1776
0
            {
1777
0
                m_xHatchTable = SvxUnoHatchTable_createInstance( pModel );
1778
0
            }
1779
0
            return m_xHatchTable;
1780
0
        }
1781
0
        else if ( aServiceSpecifier == "com.sun.star.drawing.BitmapTable" )
1782
0
        {
1783
0
            if ( !m_xBitmapTable.is() )
1784
0
            {
1785
0
                m_xBitmapTable = SvxUnoBitmapTable_createInstance( pModel );
1786
0
            }
1787
0
            return m_xBitmapTable;
1788
0
        }
1789
0
        else if ( aServiceSpecifier == "com.sun.star.drawing.TransparencyGradientTable" )
1790
0
        {
1791
0
            if ( !m_xTransGradientTable.is() )
1792
0
            {
1793
0
                m_xTransGradientTable = SvxUnoTransGradientTable_createInstance( pModel );
1794
0
            }
1795
0
            return m_xTransGradientTable;
1796
0
        }
1797
0
        else if ( aServiceSpecifier == "com.sun.star.drawing.MarkerTable" )
1798
0
        {
1799
0
            if ( !m_xMarkerTable.is() )
1800
0
            {
1801
0
                m_xMarkerTable = SvxUnoMarkerTable_createInstance( pModel );
1802
0
            }
1803
0
            return m_xMarkerTable;
1804
0
        }
1805
0
    }
1806
1807
0
    return nullptr;
1808
0
}
1809
1810
Reference< uno::XInterface > ChartView::createInstanceWithArguments( const OUString& ServiceSpecifier, const uno::Sequence< uno::Any >& Arguments )
1811
0
{
1812
0
    OSL_ENSURE( Arguments.hasElements(), "ChartView::createInstanceWithArguments: arguments are ignored" );
1813
0
    return createInstance( ServiceSpecifier );
1814
0
}
1815
1816
uno::Sequence< OUString > ChartView::getAvailableServiceNames()
1817
0
{
1818
0
    uno::Sequence< OUString > aServiceNames{ u"com.sun.star.drawing.DashTable"_ustr,
1819
0
                                             u"com.sun.star.drawing.GradientTable"_ustr,
1820
0
                                             u"com.sun.star.drawing.HatchTable"_ustr,
1821
0
                                             u"com.sun.star.drawing.BitmapTable"_ustr,
1822
0
                                             u"com.sun.star.drawing.TransparencyGradientTable"_ustr,
1823
0
                                             u"com.sun.star.drawing.MarkerTable"_ustr };
1824
1825
0
    return aServiceNames;
1826
0
}
1827
1828
OUString ChartView::dump(OUString const & kind)
1829
0
{
1830
0
    if (kind.isEmpty()) {
1831
0
        return comphelper::dumpXmlToString([this](auto writer) { return dumpAsXml(writer); });
1832
0
    }
1833
1834
    // kind == "shapes":
1835
#if HAVE_FEATURE_DESKTOP
1836
    // Used for unit tests and in chartcontroller only, no need to drag in this when cross-compiling
1837
    // for non-desktop
1838
    impl_updateView();
1839
    sal_Int32 n = m_xDrawPage->getCount();
1840
    OUStringBuffer aBuffer;
1841
    for(sal_Int32 i = 0; i < n; ++i)
1842
    {
1843
        uno::Reference< drawing::XShapes > xShape(m_xDrawPage->getByIndex(i), uno::UNO_QUERY);
1844
        if(xShape.is())
1845
        {
1846
            OUString aString = XShapeDumper::dump(uno::Reference<drawing::XShapes>(mxRootShape));
1847
            aBuffer.append(aString);
1848
        }
1849
        else
1850
        {
1851
            uno::Reference< drawing::XShape > xSingleShape(m_xDrawPage->getByIndex(i), uno::UNO_QUERY);
1852
            if(!xSingleShape.is())
1853
                continue;
1854
            OUString aString = XShapeDumper::dump(xSingleShape);
1855
            aBuffer.append(aString);
1856
        }
1857
        aBuffer.append("\n\n");
1858
    }
1859
1860
    return aBuffer.makeStringAndClear();
1861
#else
1862
0
    return OUString();
1863
0
#endif
1864
0
}
1865
1866
void ChartView::dumpAsXml(xmlTextWriterPtr pWriter) const
1867
0
{
1868
0
    (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ChartView"));
1869
0
    (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
1870
1871
0
    if (m_pDrawModelWrapper)
1872
0
    {
1873
0
        m_pDrawModelWrapper->dumpAsXml(pWriter);
1874
0
    }
1875
1876
0
    (void)xmlTextWriterEndElement(pWriter);
1877
0
}
1878
1879
void ChartView::setViewDirty()
1880
0
{
1881
0
    std::unique_lock aGuard(maTimeMutex);
1882
0
    m_bViewDirty = true;
1883
0
}
1884
1885
IMPL_LINK_NOARG(ChartView, UpdateTimeBased, Timer *, void)
1886
0
{
1887
0
    setViewDirty();
1888
0
    update();
1889
0
}
1890
1891
void ChartView::createShapes2D( const awt::Size& rPageSize )
1892
0
{
1893
    // todo: it would be nicer to just pass the page m_xDrawPage and format it,
1894
    // but the draw page does not support XPropertySet
1895
0
    formatPage( mrChartModel, rPageSize, mxRootShape );
1896
1897
0
    CreateShapeParam2D aParam;
1898
0
    aParam.maRemainingSpace.X = 0;
1899
0
    aParam.maRemainingSpace.Y = 0;
1900
0
    aParam.maRemainingSpace.Width = rPageSize.Width;
1901
0
    aParam.maRemainingSpace.Height = rPageSize.Height;
1902
1903
    //create the group shape for diagram and axes first to have title and legends on top of it
1904
0
    rtl::Reference< Diagram > xDiagram( mrChartModel.getFirstChartDiagram() );
1905
0
    bool bHasRelativeSize = false;
1906
0
    if( xDiagram.is() && xDiagram->getPropertyValue(u"RelativeSize"_ustr).hasValue() )
1907
0
        bHasRelativeSize = true;
1908
1909
0
    OUString aDiagramCID( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_DIAGRAM, OUString::number( 0 ) ) );//todo: other index if more than one diagram is possible
1910
0
    rtl::Reference<SvxShapeGroup> xDiagramPlusAxesPlusMarkHandlesGroup_Shapes =
1911
0
            ShapeFactory::createGroup2D(mxRootShape,aDiagramCID);
1912
1913
0
    aParam.mxMarkHandles = ShapeFactory::createInvisibleRectangle(
1914
0
        xDiagramPlusAxesPlusMarkHandlesGroup_Shapes, awt::Size(0,0));
1915
0
    ShapeFactory::setShapeName(aParam.mxMarkHandles, u"MarkHandles"_ustr);
1916
1917
0
    aParam.mxPlotAreaWithAxes = ShapeFactory::createInvisibleRectangle(
1918
0
        xDiagramPlusAxesPlusMarkHandlesGroup_Shapes, awt::Size(0, 0));
1919
0
    ShapeFactory::setShapeName(aParam.mxPlotAreaWithAxes, u"PlotAreaIncludingAxes"_ustr);
1920
1921
0
    aParam.mxDiagramWithAxesShapes = ShapeFactory::createGroup2D(xDiagramPlusAxesPlusMarkHandlesGroup_Shapes);
1922
1923
0
    bool bAutoPositionDummy = true;
1924
1925
    // create buttons
1926
0
    lcl_createButtons(mxRootShape, mrChartModel, aParam.maRemainingSpace);
1927
1928
0
    lcl_createTitle(
1929
0
        TitleHelper::MAIN_TITLE, mxRootShape, mrChartModel,
1930
0
        aParam.maRemainingSpace, rPageSize, TitleAlignment::ALIGN_TOP, bAutoPositionDummy);
1931
0
    if (!bHasRelativeSize && (aParam.maRemainingSpace.Width <= 0 || aParam.maRemainingSpace.Height <= 0))
1932
0
        return;
1933
1934
0
    lcl_createTitle(
1935
0
        TitleHelper::SUB_TITLE, mxRootShape, mrChartModel,
1936
0
        aParam.maRemainingSpace, rPageSize, TitleAlignment::ALIGN_TOP, bAutoPositionDummy );
1937
0
    if (!bHasRelativeSize && (aParam.maRemainingSpace.Width <= 0 || aParam.maRemainingSpace.Height <= 0))
1938
0
        return;
1939
1940
0
    aParam.mpSeriesPlotterContainer = std::make_shared<SeriesPlotterContainer>(m_aVCooSysList);
1941
0
    aParam.mpSeriesPlotterContainer->initializeCooSysAndSeriesPlotter( mrChartModel );
1942
0
    if(maTimeBased.bTimeBased && maTimeBased.nFrame != 0)
1943
0
    {
1944
0
        auto& rSeriesPlotter = aParam.mpSeriesPlotterContainer->getSeriesPlotterList();
1945
0
        size_t n = rSeriesPlotter.size();
1946
0
        for(size_t i = 0; i < n; ++i)
1947
0
        {
1948
0
            std::vector<VDataSeries*> aAllNewDataSeries = rSeriesPlotter[i]->getAllSeries();
1949
0
            std::vector< VDataSeries* >& rAllOldDataSeries =
1950
0
                maTimeBased.m_aDataSeriesList[i];
1951
0
            size_t m = std::min(aAllNewDataSeries.size(), rAllOldDataSeries.size());
1952
0
            for(size_t j = 0; j < m; ++j)
1953
0
            {
1954
0
                aAllNewDataSeries[j]->setOldTimeBased(
1955
0
                        rAllOldDataSeries[j], (maTimeBased.nFrame % 60)/60.0);
1956
0
            }
1957
0
        }
1958
0
    }
1959
1960
0
    lcl_createLegend(
1961
0
        LegendHelper::getLegend( mrChartModel ), mxRootShape, getComponentContext(),
1962
0
        aParam.maRemainingSpace, rPageSize, mrChartModel, aParam.mpSeriesPlotterContainer->getLegendEntryProviderList(),
1963
0
        lcl_getDefaultWritingModeFromPool( m_pDrawModelWrapper ) );
1964
1965
0
    if (!bHasRelativeSize && (aParam.maRemainingSpace.Width <= 0 || aParam.maRemainingSpace.Height <= 0))
1966
0
        return;
1967
1968
0
    if (!createAxisTitleShapes2D(aParam, rPageSize, bHasRelativeSize))
1969
0
        return;
1970
1971
0
    bool bDummy = false;
1972
0
    bool bIsVertical = xDiagram && xDiagram->getVertical(bDummy, bDummy);
1973
1974
0
    if (getAvailablePosAndSizeForDiagram(aParam, rPageSize, xDiagram))
1975
0
    {
1976
0
        awt::Rectangle aUsedOuterRect = impl_createDiagramAndContent(aParam, rPageSize);
1977
1978
0
        if (aParam.mxPlotAreaWithAxes.is())
1979
0
        {
1980
0
            aParam.mxPlotAreaWithAxes->setPosition(awt::Point(aUsedOuterRect.X, aUsedOuterRect.Y));
1981
0
            aParam.mxPlotAreaWithAxes->setSize(awt::Size(aUsedOuterRect.Width, aUsedOuterRect.Height));
1982
0
        }
1983
1984
        //correct axis title position
1985
0
        awt::Rectangle aDiagramPlusAxesRect( aUsedOuterRect );
1986
0
        if (aParam.mbAutoPosTitleX)
1987
0
            changePositionOfAxisTitle(aParam.mpVTitleX.get(), TitleAlignment::ALIGN_BOTTOM, aDiagramPlusAxesRect, rPageSize);
1988
0
        if (aParam.mbAutoPosTitleY)
1989
0
            changePositionOfAxisTitle(aParam.mpVTitleY.get(), TitleAlignment::ALIGN_LEFT, aDiagramPlusAxesRect, rPageSize);
1990
0
        if (aParam.mbAutoPosTitleZ)
1991
0
            changePositionOfAxisTitle(aParam.mpVTitleZ.get(), TitleAlignment::ALIGN_Z, aDiagramPlusAxesRect, rPageSize);
1992
0
        if (aParam.mbAutoPosSecondTitleX)
1993
0
            changePositionOfAxisTitle(aParam.mpVTitleSecondX.get(), bIsVertical? TitleAlignment::ALIGN_RIGHT : TitleAlignment::ALIGN_TOP, aDiagramPlusAxesRect, rPageSize);
1994
0
        if (aParam.mbAutoPosSecondTitleY)
1995
0
            changePositionOfAxisTitle(aParam.mpVTitleSecondY.get(), bIsVertical? TitleAlignment::ALIGN_TOP : TitleAlignment::ALIGN_RIGHT, aDiagramPlusAxesRect, rPageSize);
1996
0
    }
1997
1998
    //cleanup: remove all empty group shapes to avoid grey border lines:
1999
0
    lcl_removeEmptyGroupShapes( *mxRootShape->GetSdrObject() );
2000
2001
0
    if(maTimeBased.bTimeBased && maTimeBased.nFrame % 60 == 0)
2002
0
    {
2003
        // create copy of the data for next frame
2004
0
        auto& rSeriesPlotter = aParam.mpSeriesPlotterContainer->getSeriesPlotterList();
2005
0
        size_t n = rSeriesPlotter.size();
2006
0
        maTimeBased.m_aDataSeriesList.clear();
2007
0
        maTimeBased.m_aDataSeriesList.resize(n);
2008
0
        for(size_t i = 0; i < n; ++i)
2009
0
        {
2010
0
            std::vector<VDataSeries*> aAllNewDataSeries = rSeriesPlotter[i]->getAllSeries();
2011
0
            std::vector<VDataSeries*>& rAllOldDataSeries = maTimeBased.m_aDataSeriesList[i];
2012
0
            size_t m = aAllNewDataSeries.size();
2013
0
            for(size_t j = 0; j < m; ++j)
2014
0
            {
2015
0
                rAllOldDataSeries.push_back( aAllNewDataSeries[j]->
2016
0
                        createCopyForTimeBased() );
2017
0
            }
2018
0
        }
2019
2020
0
        maTimeBased.maTimer.Stop();
2021
0
    }
2022
2023
0
    if(maTimeBased.bTimeBased && !maTimeBased.maTimer.IsActive())
2024
0
    {
2025
0
        maTimeBased.maTimer.SetTimeout(15);
2026
0
        maTimeBased.maTimer.SetInvokeHandler(LINK(this, ChartView, UpdateTimeBased));
2027
0
        maTimeBased.maTimer.Start();
2028
0
    }
2029
0
}
2030
2031
bool ChartView::createAxisTitleShapes2D( CreateShapeParam2D& rParam, const css::awt::Size& rPageSize, bool bHasRelativeSize )
2032
0
{
2033
0
    rtl::Reference<Diagram> xDiagram = mrChartModel.getFirstChartDiagram();
2034
2035
0
    rtl::Reference< ChartType > xChartType;
2036
0
    sal_Int32 nDimension = 0;
2037
0
    if (xDiagram)
2038
0
    {
2039
0
        xChartType = xDiagram->getChartTypeByIndex( 0 );
2040
0
        nDimension = xDiagram->getDimension();
2041
0
    }
2042
2043
0
    if (xChartType.is() ? xChartType->isSupportingMainAxis(nDimension, 0) : true)
2044
0
        rParam.mpVTitleX = lcl_createTitle( TitleHelper::TITLE_AT_STANDARD_X_AXIS_POSITION, mxRootShape, mrChartModel
2045
0
                , rParam.maRemainingSpace, rPageSize, TitleAlignment::ALIGN_BOTTOM, rParam.mbAutoPosTitleX );
2046
0
    if (!bHasRelativeSize && (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0))
2047
0
        return false;
2048
2049
0
    if (xChartType.is() ? xChartType->isSupportingMainAxis(nDimension, 1) : true)
2050
0
        rParam.mpVTitleY = lcl_createTitle( TitleHelper::TITLE_AT_STANDARD_Y_AXIS_POSITION, mxRootShape, mrChartModel
2051
0
                , rParam.maRemainingSpace, rPageSize, TitleAlignment::ALIGN_LEFT, rParam.mbAutoPosTitleY );
2052
0
    if (!bHasRelativeSize && (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0))
2053
0
        return false;
2054
2055
0
    if (xChartType.is() ? xChartType->isSupportingMainAxis(nDimension, 2) : true)
2056
0
        rParam.mpVTitleZ = lcl_createTitle( TitleHelper::Z_AXIS_TITLE, mxRootShape, mrChartModel
2057
0
                , rParam.maRemainingSpace, rPageSize, TitleAlignment::ALIGN_RIGHT, rParam.mbAutoPosTitleZ );
2058
0
    if (!bHasRelativeSize && (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0))
2059
0
        return false;
2060
2061
0
    bool bDummy = false;
2062
0
    bool bIsVertical = xDiagram && xDiagram->getVertical( bDummy, bDummy );
2063
2064
0
    if (xChartType.is() ? xChartType->isSupportingSecondaryAxis(nDimension) : true)
2065
0
        rParam.mpVTitleSecondX = lcl_createTitle( TitleHelper::SECONDARY_X_AXIS_TITLE, mxRootShape, mrChartModel
2066
0
                , rParam.maRemainingSpace, rPageSize, bIsVertical? TitleAlignment::ALIGN_RIGHT : TitleAlignment::ALIGN_TOP, rParam.mbAutoPosSecondTitleX );
2067
0
    if (!bHasRelativeSize && (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0))
2068
0
        return false;
2069
2070
0
    if (xChartType.is() ? xChartType->isSupportingSecondaryAxis(nDimension) : true)
2071
0
        rParam.mpVTitleSecondY = lcl_createTitle( TitleHelper::SECONDARY_Y_AXIS_TITLE, mxRootShape, mrChartModel
2072
0
                , rParam.maRemainingSpace, rPageSize, bIsVertical? TitleAlignment::ALIGN_TOP : TitleAlignment::ALIGN_RIGHT, rParam.mbAutoPosSecondTitleY );
2073
0
    if (!bHasRelativeSize && (rParam.maRemainingSpace.Width <= 0 || rParam.maRemainingSpace.Height <= 0))
2074
0
        return false;
2075
2076
0
    return true;
2077
0
}
2078
2079
namespace
2080
{
2081
constexpr sal_Int32 constDiagramTitleSpace = 200; //=0,2 cm spacing
2082
2083
bool lcl_getPropertySwapXAndYAxis(const rtl::Reference<Diagram>& xDiagram)
2084
0
{
2085
0
    bool bSwapXAndY = false;
2086
2087
0
    if (xDiagram.is())
2088
0
    {
2089
0
        const std::vector<rtl::Reference<BaseCoordinateSystem>> aCooSysList(
2090
0
            xDiagram->getBaseCoordinateSystems());
2091
0
        if (!aCooSysList.empty())
2092
0
        {
2093
0
            try
2094
0
            {
2095
0
                aCooSysList[0]->getPropertyValue(u"SwapXAndYAxis"_ustr) >>= bSwapXAndY;
2096
0
            }
2097
0
            catch (const uno::Exception&)
2098
0
            {
2099
0
                TOOLS_WARN_EXCEPTION("chart2", "");
2100
0
            }
2101
0
        }
2102
0
    }
2103
0
    return bSwapXAndY;
2104
0
}
2105
2106
} // end anonymous namespace
2107
2108
sal_Int32 ChartView::getExplicitNumberFormatKeyForAxis(
2109
    const rtl::Reference<::chart::Axis>& xAxis,
2110
    const rtl::Reference<::chart::BaseCoordinateSystem>& xCorrespondingCoordinateSystem,
2111
    const rtl::Reference<::chart::ChartModel>& xChartDoc)
2112
0
{
2113
0
    return AxisHelper::getExplicitNumberFormatKeyForAxis(
2114
0
        xAxis, xCorrespondingCoordinateSystem, xChartDoc,
2115
0
        true /*bSearchForParallelAxisIfNothingIsFound*/);
2116
0
}
2117
2118
sal_Int32 ChartView::getExplicitPercentageNumberFormatKeyForDataLabel(
2119
    const uno::Reference<beans::XPropertySet>& xSeriesOrPointProp,
2120
    const uno::Reference<util::XNumberFormatsSupplier>& xNumberFormatsSupplier)
2121
0
{
2122
0
    sal_Int32 nFormat = 0;
2123
0
    if (!xSeriesOrPointProp.is())
2124
0
        return nFormat;
2125
0
    if (!(xSeriesOrPointProp->getPropertyValue(u"PercentageNumberFormat"_ustr) >>= nFormat))
2126
0
    {
2127
0
        nFormat = DiagramHelper::getPercentNumberFormat(xNumberFormatsSupplier);
2128
0
    }
2129
0
    if (nFormat < 0)
2130
0
        nFormat = 0;
2131
0
    return nFormat;
2132
0
}
2133
2134
awt::Rectangle ChartView::AddSubtractAxisTitleSizes(
2135
    ChartModel& rModel, ChartView* pChartView, const awt::Rectangle& rPositionAndSize,
2136
    bool bSubtract)
2137
0
{
2138
0
    awt::Rectangle aRet(rPositionAndSize);
2139
2140
    //add axis title sizes to the diagram size
2141
0
    rtl::Reference<::chart::Title> xTitle_Height(
2142
0
        TitleHelper::getTitle(TitleHelper::TITLE_AT_STANDARD_X_AXIS_POSITION, rModel));
2143
0
    rtl::Reference<::chart::Title> xTitle_Width(
2144
0
        TitleHelper::getTitle(TitleHelper::TITLE_AT_STANDARD_Y_AXIS_POSITION, rModel));
2145
0
    rtl::Reference<::chart::Title> xSecondTitle_Height(
2146
0
        TitleHelper::getTitle(TitleHelper::SECONDARY_X_AXIS_TITLE, rModel));
2147
0
    rtl::Reference<::chart::Title> xSecondTitle_Width(
2148
0
        TitleHelper::getTitle(TitleHelper::SECONDARY_Y_AXIS_TITLE, rModel));
2149
0
    if (xTitle_Height.is() || xTitle_Width.is() || xSecondTitle_Height.is()
2150
0
        || xSecondTitle_Width.is())
2151
0
    {
2152
0
        if (pChartView)
2153
0
        {
2154
            //detect whether x axis points into x direction or not
2155
0
            if (lcl_getPropertySwapXAndYAxis(rModel.getFirstChartDiagram()))
2156
0
            {
2157
0
                std::swap(xTitle_Height, xTitle_Width);
2158
0
                std::swap(xSecondTitle_Height, xSecondTitle_Width);
2159
0
            }
2160
2161
0
            sal_Int32 nTitleSpaceWidth = 0;
2162
0
            sal_Int32 nTitleSpaceHeight = 0;
2163
0
            sal_Int32 nSecondTitleSpaceWidth = 0;
2164
0
            sal_Int32 nSecondTitleSpaceHeight = 0;
2165
2166
0
            if (xTitle_Height.is())
2167
0
            {
2168
0
                OUString aCID_X(
2169
0
                    ObjectIdentifier::createClassifiedIdentifierForObject(xTitle_Height, &rModel));
2170
0
                nTitleSpaceHeight
2171
0
                    = pChartView->getRectangleOfObject(aCID_X, true).Height;
2172
0
                if (nTitleSpaceHeight)
2173
0
                    nTitleSpaceHeight += constDiagramTitleSpace;
2174
0
            }
2175
0
            if (xTitle_Width.is())
2176
0
            {
2177
0
                OUString aCID_Y(
2178
0
                    ObjectIdentifier::createClassifiedIdentifierForObject(xTitle_Width, &rModel));
2179
0
                nTitleSpaceWidth = pChartView->getRectangleOfObject(aCID_Y, true).Width;
2180
0
                if (nTitleSpaceWidth)
2181
0
                    nTitleSpaceWidth += constDiagramTitleSpace;
2182
0
            }
2183
0
            if (xSecondTitle_Height.is())
2184
0
            {
2185
0
                OUString aCID_X(ObjectIdentifier::createClassifiedIdentifierForObject(
2186
0
                    xSecondTitle_Height, &rModel));
2187
0
                nSecondTitleSpaceHeight
2188
0
                    = pChartView->getRectangleOfObject(aCID_X, true).Height;
2189
0
                if (nSecondTitleSpaceHeight)
2190
0
                    nSecondTitleSpaceHeight += constDiagramTitleSpace;
2191
0
            }
2192
0
            if (xSecondTitle_Width.is())
2193
0
            {
2194
0
                OUString aCID_Y(ObjectIdentifier::createClassifiedIdentifierForObject(
2195
0
                    xSecondTitle_Width, &rModel));
2196
0
                nSecondTitleSpaceWidth
2197
0
                    += pChartView->getRectangleOfObject(aCID_Y, true).Width;
2198
0
                if (nSecondTitleSpaceWidth)
2199
0
                    nSecondTitleSpaceWidth += constDiagramTitleSpace;
2200
0
            }
2201
0
            if (bSubtract)
2202
0
            {
2203
0
                aRet.X += nTitleSpaceWidth;
2204
0
                aRet.Y += nSecondTitleSpaceHeight;
2205
0
                aRet.Width -= (nTitleSpaceWidth + nSecondTitleSpaceWidth);
2206
0
                aRet.Height -= (nTitleSpaceHeight + nSecondTitleSpaceHeight);
2207
0
            }
2208
0
            else
2209
0
            {
2210
0
                aRet.X -= nTitleSpaceWidth;
2211
0
                aRet.Y -= nSecondTitleSpaceHeight;
2212
0
                aRet.Width += nTitleSpaceWidth + nSecondTitleSpaceWidth;
2213
0
                aRet.Height += nTitleSpaceHeight + nSecondTitleSpaceHeight;
2214
0
            }
2215
0
        }
2216
0
    }
2217
0
    return aRet;
2218
0
}
2219
2220
} //namespace chart
2221
2222
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
2223
com_sun_star_comp_chart2_ChartView_get_implementation(css::uno::XComponentContext *context,
2224
                                                         css::uno::Sequence<css::uno::Any> const &)
2225
0
{
2226
0
    rtl::Reference<::chart::ChartModel> pChartModel = new ::chart::ChartModel(context);
2227
0
    return cppu::acquire(new ::chart::ChartView(context, *pChartModel));
2228
0
}
2229
2230
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */