Coverage Report

Created: 2026-05-16 09:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/chart2/source/view/main/VLegend.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 "VLegend.hxx"
21
#include "VButton.hxx"
22
#include <Legend.hxx>
23
#include <PropertyMapper.hxx>
24
#include <ChartModel.hxx>
25
#include <ObjectIdentifier.hxx>
26
#include <FormattedString.hxx>
27
#include <RelativePositionHelper.hxx>
28
#include <ShapeFactory.hxx>
29
#include <RelativeSizeHelper.hxx>
30
#include <LegendEntryProvider.hxx>
31
#include <chartview/DrawModelWrapper.hxx>
32
#include <com/sun/star/text/WritingMode2.hpp>
33
#include <com/sun/star/beans/XPropertySet.hpp>
34
#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
35
#include <com/sun/star/drawing/LineJoint.hpp>
36
#include <com/sun/star/chart/ChartLegendExpansion.hpp>
37
#include <com/sun/star/chart2/LegendPosition.hpp>
38
#include <com/sun/star/chart2/RelativePosition.hpp>
39
#include <com/sun/star/chart2/RelativeSize.hpp>
40
#include <com/sun/star/chart2/data/PivotTableFieldEntry.hpp>
41
#include <chart2/AbstractPivotTableDataProvider.hxx>
42
#include <rtl/math.hxx>
43
#include <svl/ctloptions.hxx>
44
#include <comphelper/diagnose_ex.hxx>
45
#include <tools/UnitConversion.hxx>
46
47
#include <utility>
48
#include <vector>
49
#include <algorithm>
50
51
using namespace ::com::sun::star;
52
using namespace ::com::sun::star::chart2;
53
54
using ::com::sun::star::uno::Reference;
55
using ::com::sun::star::uno::Sequence;
56
57
namespace chart
58
{
59
60
namespace
61
{
62
63
typedef std::pair< ::chart::tNameSequence, ::chart::tAnySequence > tPropertyValues;
64
65
double lcl_CalcViewFontSize(
66
    const Reference< beans::XPropertySet > & xProp,
67
    const awt::Size & rReferenceSize )
68
0
{
69
0
    double fResult = 10.0;
70
71
0
    float fFontHeight( 0.0 );
72
0
    if( xProp.is() && ( xProp->getPropertyValue( u"CharHeight"_ustr) >>= fFontHeight ))
73
0
    {
74
0
        fResult = fFontHeight;
75
0
        try
76
0
        {
77
0
            awt::Size aPropRefSize;
78
0
            if( (xProp->getPropertyValue( u"ReferencePageSize"_ustr) >>= aPropRefSize) &&
79
0
                (aPropRefSize.Height > 0))
80
0
            {
81
0
                fResult = ::chart::RelativeSizeHelper::calculate( fFontHeight, aPropRefSize, rReferenceSize );
82
0
            }
83
0
        }
84
0
        catch( const uno::Exception & )
85
0
        {
86
0
            DBG_UNHANDLED_EXCEPTION("chart2");
87
0
        }
88
0
    }
89
90
0
    return convertPointToMm100(fResult);
91
0
}
92
93
void lcl_getProperties(
94
    const Reference< beans::XPropertySet > & xLegendProp,
95
    tPropertyValues & rOutLineFillProperties,
96
    tPropertyValues & rOutTextProperties,
97
    const awt::Size & rReferenceSize )
98
0
{
99
    // Get Line- and FillProperties from model legend
100
0
    if( !xLegendProp.is())
101
0
        return;
102
103
    // set rOutLineFillProperties
104
0
    ::chart::tPropertyNameValueMap aLineFillValueMap;
105
0
    ::chart::PropertyMapper::getValueMap( aLineFillValueMap, ::chart::PropertyMapper::getPropertyNameMapForFillAndLineProperties(), xLegendProp );
106
107
0
    aLineFillValueMap[ u"LineJoint"_ustr ] <<= drawing::LineJoint_ROUND;
108
109
0
    ::chart::PropertyMapper::getMultiPropertyListsFromValueMap(
110
0
        rOutLineFillProperties.first, rOutLineFillProperties.second, aLineFillValueMap );
111
112
    // set rOutTextProperties
113
0
    ::chart::tPropertyNameValueMap aTextValueMap;
114
0
    ::chart::PropertyMapper::getValueMap( aTextValueMap, ::chart::PropertyMapper::getPropertyNameMapForCharacterProperties(), xLegendProp );
115
116
0
    aTextValueMap[ u"TextAutoGrowHeight"_ustr ] <<= true;
117
0
    aTextValueMap[ u"TextAutoGrowWidth"_ustr ] <<= true;
118
0
    aTextValueMap[ u"TextHorizontalAdjust"_ustr ] <<= drawing::TextHorizontalAdjust_LEFT;
119
0
    aTextValueMap[ u"TextMaximumFrameWidth"_ustr ] <<= rReferenceSize.Width; //needs to be overwritten by actual available space in the legend
120
121
    // recalculate font size
122
0
    awt::Size aPropRefSize;
123
0
    float fFontHeight( 0.0 );
124
0
    if( (xLegendProp->getPropertyValue( u"ReferencePageSize"_ustr) >>= aPropRefSize) &&
125
0
        (aPropRefSize.Height > 0) &&
126
0
        (aTextValueMap[ u"CharHeight"_ustr ] >>= fFontHeight) )
127
0
    {
128
0
        aTextValueMap[ u"CharHeight"_ustr ] <<=
129
0
            static_cast< float >(
130
0
                ::chart::RelativeSizeHelper::calculate( fFontHeight, aPropRefSize, rReferenceSize ));
131
132
0
        if( aTextValueMap[ u"CharHeightAsian"_ustr ] >>= fFontHeight )
133
0
        {
134
0
            aTextValueMap[ u"CharHeightAsian"_ustr ] <<=
135
0
                static_cast< float >(
136
0
                    ::chart::RelativeSizeHelper::calculate( fFontHeight, aPropRefSize, rReferenceSize ));
137
0
        }
138
0
        if( aTextValueMap[ u"CharHeightComplex"_ustr ] >>= fFontHeight )
139
0
        {
140
0
            aTextValueMap[ u"CharHeightComplex"_ustr ] <<=
141
0
                static_cast< float >(
142
0
                    ::chart::RelativeSizeHelper::calculate( fFontHeight, aPropRefSize, rReferenceSize ));
143
0
        }
144
0
    }
145
146
0
    ::chart::PropertyMapper::getMultiPropertyListsFromValueMap(
147
0
        rOutTextProperties.first, rOutTextProperties.second, aTextValueMap );
148
0
}
149
150
awt::Size lcl_createTextShapes(
151
    const std::vector<ViewLegendEntry> & rEntries,
152
    const rtl::Reference<SvxShapeGroupAnyD> & xTarget,
153
    std::vector< rtl::Reference<SvxShapeText> > & rOutTextShapes,
154
    const tPropertyValues & rTextProperties )
155
0
{
156
0
    awt::Size aResult;
157
158
0
    for (ViewLegendEntry const & rEntry : rEntries)
159
0
    {
160
0
        try
161
0
        {
162
0
            OUString aLabelString;
163
0
            if (rEntry.xLabel)
164
0
            {
165
                // tdf#150034 limit legend label text
166
0
                if (rEntry.xLabel->getString().getLength() > 520)
167
0
                {
168
0
                    sal_Int32 nIndex = rEntry.xLabel->getString().indexOf(' ', 500);
169
0
                    rEntry.xLabel->setString(
170
0
                        rEntry.xLabel->getString().copy(0, nIndex > 500 ? nIndex : 500));
171
0
                }
172
173
0
                aLabelString += rEntry.xLabel->getString();
174
                // workaround for Issue #i67540#
175
0
                if( aLabelString.isEmpty())
176
0
                    aLabelString = " ";
177
0
            }
178
179
0
            rtl::Reference<SvxShapeText> xEntry =
180
0
                ShapeFactory::createText( xTarget, aLabelString,
181
0
                        rTextProperties.first, rTextProperties.second, uno::Any() );
182
183
            // adapt max-extent
184
0
            awt::Size aCurrSize( xEntry->getSize());
185
0
            aResult.Width  = std::max( aResult.Width,  aCurrSize.Width  );
186
0
            aResult.Height = std::max( aResult.Height, aCurrSize.Height );
187
188
0
            rOutTextShapes.push_back( xEntry );
189
0
        }
190
0
        catch( const uno::Exception & )
191
0
        {
192
0
            DBG_UNHANDLED_EXCEPTION("chart2");
193
0
        }
194
0
    }
195
196
0
    return aResult;
197
0
}
198
199
void lcl_collectColumnWidths( std::vector< sal_Int32 >& rColumnWidths, const sal_Int32 nNumberOfRows, const sal_Int32 nNumberOfColumns,
200
                              const std::vector< rtl::Reference<SvxShapeText> >& rTextShapes, sal_Int32 nSymbolPlusDistanceWidth )
201
0
{
202
0
    rColumnWidths.clear();
203
0
    sal_Int32 nNumberOfEntries = rTextShapes.size();
204
0
    for (sal_Int32 nRow = 0; nRow < nNumberOfRows; ++nRow )
205
0
    {
206
0
        for (sal_Int32 nColumn = 0; nColumn < nNumberOfColumns; ++nColumn )
207
0
        {
208
0
            sal_Int32 nEntry = nColumn + nRow * nNumberOfColumns;
209
0
            if( nEntry < nNumberOfEntries )
210
0
            {
211
0
                awt::Size aTextSize( rTextShapes[ nEntry ]->getSize() );
212
0
                sal_Int32 nWidth = nSymbolPlusDistanceWidth + aTextSize.Width;
213
0
                if( nRow==0 )
214
0
                    rColumnWidths.push_back( nWidth );
215
0
                else
216
0
                    rColumnWidths[nColumn] = std::max( nWidth, rColumnWidths[nColumn] );
217
0
            }
218
0
        }
219
0
    }
220
0
}
221
222
void lcl_collectRowHeighs( std::vector< sal_Int32 >& rRowHeights, const sal_Int32 nNumberOfRows, const sal_Int32 nNumberOfColumns,
223
                           const std::vector< rtl::Reference<SvxShapeText> >& rTextShapes )
224
0
{
225
    // calculate maximum height for each row
226
    // and collect column widths
227
0
    rRowHeights.clear();
228
0
    sal_Int32 nNumberOfEntries = rTextShapes.size();
229
0
    for (sal_Int32 nRow = 0; nRow < nNumberOfRows; ++nRow)
230
0
    {
231
0
        sal_Int32 nCurrentRowHeight = 0;
232
0
        for (sal_Int32 nColumn = 0; nColumn < nNumberOfColumns; ++nColumn)
233
0
        {
234
0
            sal_Int32 nEntry = nColumn + nRow * nNumberOfColumns;
235
0
            if( nEntry < nNumberOfEntries )
236
0
            {
237
0
                awt::Size aTextSize( rTextShapes[ nEntry ]->getSize() );
238
0
                nCurrentRowHeight = std::max( nCurrentRowHeight, aTextSize.Height );
239
0
            }
240
0
        }
241
0
        rRowHeights.push_back( nCurrentRowHeight );
242
0
    }
243
0
}
244
245
sal_Int32 lcl_getTextLineHeight( const std::vector< sal_Int32 >& aRowHeights, const sal_Int32 nNumberOfRows, double fViewFontSize )
246
0
{
247
0
    const sal_Int32 nFontHeight = static_cast< sal_Int32 >( fViewFontSize );
248
0
    if (!nFontHeight)
249
0
        return 0;
250
0
    sal_Int32 nTextLineHeight = nFontHeight;
251
0
    for (sal_Int32 nRow = 0; nRow < nNumberOfRows; ++nRow)
252
0
    {
253
0
        sal_Int32 nFullTextHeight = aRowHeights[nRow];
254
0
        if( ( nFullTextHeight / nFontHeight ) <= 1 )
255
0
        {
256
0
            nTextLineHeight = nFullTextHeight;//found an entry with one line-> have real text height
257
0
            break;
258
0
        }
259
0
    }
260
0
    return nTextLineHeight;
261
0
}
262
263
//returns resulting legend size
264
awt::Size lcl_placeLegendEntries(
265
    std::vector<ViewLegendEntry> & rEntries,
266
    css::chart::ChartLegendExpansion eExpansion,
267
    bool bSymbolsLeftSide,
268
    double fViewFontSize,
269
    const awt::Size& rMaxSymbolExtent,
270
    tPropertyValues & rTextProperties,
271
    const rtl::Reference<SvxShapeGroupAnyD> & xTarget,
272
    const awt::Size& rRemainingSpace,
273
    sal_Int32 nYStartPosition,
274
    const awt::Size& rPageSize,
275
    bool bIsPivotChart,
276
    awt::Size& rDefaultLegendSize)
277
0
{
278
0
    bool bIsCustomSize = (eExpansion == css::chart::ChartLegendExpansion_CUSTOM);
279
0
    awt::Size aResultingLegendSize(0,0);
280
    // For Pivot charts set the *minimum* legend size as a function of page size.
281
0
    if ( bIsPivotChart )
282
0
        aResultingLegendSize = awt::Size((rPageSize.Width * 13) / 80, (rPageSize.Height * 31) / 90);
283
0
    if( bIsCustomSize )
284
0
        aResultingLegendSize = awt::Size(rRemainingSpace.Width, rRemainingSpace.Height + nYStartPosition);
285
286
    // #i109336# Improve auto positioning in chart
287
0
    sal_Int32 nXPadding = static_cast< sal_Int32 >( std::max( 100.0, fViewFontSize * 0.33 ) );
288
0
    sal_Int32 nXOffset  = static_cast< sal_Int32 >( std::max( 100.0, fViewFontSize * 0.66 ) );
289
0
    sal_Int32 nYPadding = static_cast< sal_Int32 >( std::max( 100.0, fViewFontSize * 0.2 ) );
290
0
    sal_Int32 nYOffset  = static_cast< sal_Int32 >( std::max( 100.0, fViewFontSize * 0.2 ) );
291
292
0
    const sal_Int32 nSymbolToTextDistance = static_cast< sal_Int32 >( std::max( 100.0, fViewFontSize * 0.22 ) );//minimum 1mm
293
0
    const sal_Int32 nSymbolPlusDistanceWidth = rMaxSymbolExtent.Width + nSymbolToTextDistance;
294
0
    sal_Int32 nMaxTextWidth = rRemainingSpace.Width - nSymbolPlusDistanceWidth;
295
0
    uno::Any* pFrameWidthAny = PropertyMapper::getValuePointer( rTextProperties.second, rTextProperties.first, u"TextMaximumFrameWidth");
296
0
    if(pFrameWidthAny)
297
0
    {
298
0
        if( eExpansion == css::chart::ChartLegendExpansion_HIGH )
299
0
        {
300
            // limit the width of texts to 30% of the total available width
301
            // #i109336# Improve auto positioning in chart
302
0
            nMaxTextWidth = rRemainingSpace.Width * 3 / 10;
303
0
        }
304
0
        *pFrameWidthAny <<= nMaxTextWidth;
305
0
    }
306
307
0
    std::vector< rtl::Reference<SvxShapeText> > aTextShapes;
308
0
    awt::Size aMaxEntryExtent = lcl_createTextShapes( rEntries, xTarget, aTextShapes, rTextProperties );
309
0
    OSL_ASSERT( aTextShapes.size() == rEntries.size());
310
311
0
    sal_Int32 nMaxEntryWidth = nXOffset + nSymbolPlusDistanceWidth + aMaxEntryExtent.Width;
312
0
    sal_Int32 nMaxEntryHeight = nYOffset + aMaxEntryExtent.Height;
313
0
    sal_Int32 nNumberOfEntries = rEntries.size();
314
315
0
    rDefaultLegendSize.Width = nMaxEntryWidth;
316
0
    rDefaultLegendSize.Height = nMaxEntryHeight + nYPadding;
317
318
0
    sal_Int32 nNumberOfColumns = 0, nNumberOfRows = 0;
319
0
    std::vector< sal_Int32 > aColumnWidths;
320
0
    std::vector< sal_Int32 > aRowHeights;
321
322
0
    sal_Int32 nTextLineHeight = static_cast< sal_Int32 >( fViewFontSize );
323
324
    // determine layout depending on LegendExpansion
325
0
    if( eExpansion == css::chart::ChartLegendExpansion_CUSTOM )
326
0
    {
327
0
        sal_Int32 nCurrentRow=0;
328
0
        sal_Int32 nCurrentColumn=-1;
329
0
        sal_Int32 nMaxColumnCount=-1;
330
0
        for( sal_Int32 nN=0; nN<static_cast<sal_Int32>(aTextShapes.size()); nN++ )
331
0
        {
332
0
            const rtl::Reference<SvxShapeText>& xShape( aTextShapes[nN] );
333
0
            if( !xShape.is() )
334
0
                continue;
335
0
            awt::Size aSize( xShape->getSize() );
336
0
            sal_Int32 nNewWidth = aSize.Width + nSymbolPlusDistanceWidth;
337
0
            sal_Int32 nCurrentColumnCount = aColumnWidths.size();
338
339
            //are we allowed to add a new column?
340
0
            if( nMaxColumnCount==-1 || (nCurrentColumn+1) < nMaxColumnCount )
341
0
            {
342
                //try add a new column
343
0
                nCurrentColumn++;
344
0
                if( nCurrentColumn < nCurrentColumnCount )
345
0
                {
346
                    //check whether the current column width is sufficient for the new entry
347
0
                    if( aColumnWidths[nCurrentColumn]>=nNewWidth )
348
0
                    {
349
                        //all good proceed with next entry
350
0
                        continue;
351
0
                    }
352
353
0
                    aColumnWidths[nCurrentColumn] = std::max( nNewWidth, aColumnWidths[nCurrentColumn] );
354
0
                } else
355
0
                    aColumnWidths.push_back(nNewWidth);
356
357
                //do the columns still fit into the given size?
358
0
                nCurrentColumnCount = aColumnWidths.size();//update count
359
0
                sal_Int32 nSumWidth = 0;
360
0
                for (sal_Int32 nColumn = 0; nColumn < nCurrentColumnCount; nColumn++)
361
0
                    nSumWidth += aColumnWidths[nColumn];
362
363
0
                if( nSumWidth <= rRemainingSpace.Width || nCurrentColumnCount==1 )
364
0
                {
365
                    //all good proceed with next entry
366
0
                    continue;
367
0
                }
368
0
                else
369
0
                {
370
                    //not enough space for the current amount of columns
371
                    //try again with less columns
372
0
                    nMaxColumnCount = nCurrentColumnCount-1;
373
0
                    nN=-1;
374
0
                    nCurrentRow=0;
375
0
                    nCurrentColumn=-1;
376
0
                    aColumnWidths.clear();
377
0
                }
378
0
            }
379
0
            else
380
0
            {
381
                //add a new row and try the same entry again
382
0
                nCurrentRow++;
383
0
                nCurrentColumn=-1;
384
0
                nN--;
385
0
            }
386
0
        }
387
0
        nNumberOfColumns = aColumnWidths.size();
388
0
        nNumberOfRows = nCurrentRow+1;
389
390
        //check if there is not enough space so that some entries must be removed
391
0
        lcl_collectRowHeighs( aRowHeights, nNumberOfRows, nNumberOfColumns, aTextShapes );
392
0
        nTextLineHeight = lcl_getTextLineHeight( aRowHeights, nNumberOfRows, fViewFontSize );
393
0
        sal_Int32 nSumHeight = 0;
394
0
        for (sal_Int32 nRow=0; nRow < nNumberOfRows; nRow++)
395
0
            nSumHeight += aRowHeights[nRow];
396
0
        sal_Int32 nRemainingSpace = rRemainingSpace.Height - nSumHeight;
397
398
0
        if( nRemainingSpace < -100 ) // 1mm tolerance for OOXML interop tdf#90404
399
0
        {
400
            //remove entries that are too big
401
0
            for (sal_Int32 nRow = nNumberOfRows; nRow--; )
402
0
            {
403
0
                for (sal_Int32 nColumn = nNumberOfColumns; nColumn--; )
404
0
                {
405
0
                    sal_Int32 nEntry = nColumn + nRow * nNumberOfColumns;
406
0
                    if( nEntry < static_cast<sal_Int32>(aTextShapes.size()) )
407
0
                    {
408
0
                        DrawModelWrapper::removeShape( aTextShapes[nEntry] );
409
0
                        aTextShapes.pop_back();
410
0
                    }
411
0
                    if( nEntry < nNumberOfEntries && ( nEntry != 0 || nNumberOfColumns != 1 ) )
412
0
                    {
413
0
                        DrawModelWrapper::removeShape( rEntries[ nEntry ].xSymbol );
414
0
                        rEntries.pop_back();
415
0
                        nNumberOfEntries--;
416
0
                    }
417
0
                }
418
0
                if (nRow == 0 && nNumberOfColumns == 1)
419
0
                {
420
0
                    try
421
0
                    {
422
0
                        OUString aLabelString = rEntries[0].xLabel->getString();
423
0
                        static constexpr OUString sDots = u"..."_ustr;
424
0
                        for (sal_Int32 nNewLen = aLabelString.getLength() - sDots.getLength(); nNewLen > 0; )
425
0
                        {
426
0
                            OUString aNewLabel = aLabelString.subView(0, nNewLen) + sDots;
427
0
                            rtl::Reference<SvxShapeText> xEntry = ShapeFactory::createText(
428
0
                                xTarget, aNewLabel, rTextProperties.first, rTextProperties.second, uno::Any());
429
0
                            nSumHeight = xEntry->getSize().Height;
430
0
                            nRemainingSpace = rRemainingSpace.Height - nSumHeight;
431
0
                            if (nRemainingSpace >= 0)
432
0
                            {
433
0
                                sal_Int32 nWidth = xEntry->getSize().Width + nSymbolPlusDistanceWidth;
434
0
                                if (rRemainingSpace.Width - nWidth >= 0)
435
0
                                {
436
0
                                    aTextShapes.push_back(xEntry);
437
0
                                    rEntries[0].xLabel->setString(aNewLabel);
438
0
                                    aRowHeights[0] = nSumHeight;
439
0
                                    aColumnWidths[0] = nWidth;
440
0
                                    break;
441
0
                                }
442
0
                            }
443
0
                            DrawModelWrapper::removeShape(xEntry);
444
                            // The intention here is to make pathological cases with extremely large labels
445
                            // converge a little faster
446
0
                            if (nNewLen > 10 && std::abs(nRemainingSpace) > nSumHeight / 10)
447
0
                                nNewLen -= nNewLen / 10;
448
0
                            else
449
0
                                --nNewLen;
450
0
                        }
451
0
                        if (aTextShapes.empty())
452
0
                        {
453
0
                            DrawModelWrapper::removeShape(rEntries[0].xSymbol);
454
0
                            rEntries.pop_back();
455
0
                            nNumberOfEntries--;
456
0
                            aRowHeights.pop_back();
457
0
                        }
458
0
                    }
459
0
                    catch (const uno::Exception&)
460
0
                    {
461
0
                        DBG_UNHANDLED_EXCEPTION("chart2");
462
0
                    }
463
0
                }
464
0
                else
465
0
                {
466
0
                    nSumHeight -= aRowHeights[nRow];
467
0
                    aRowHeights.pop_back();
468
0
                    nRemainingSpace = rRemainingSpace.Height - nSumHeight;
469
0
                    if (nRemainingSpace >= 0)
470
0
                        break;
471
0
                }
472
0
            }
473
0
            nNumberOfRows = static_cast<sal_Int32>(aRowHeights.size());
474
0
        }
475
0
        if( nRemainingSpace >= -100 ) // 1mm tolerance for OOXML interop tdf#90404
476
0
        {
477
0
            sal_Int32 nNormalSpacingHeight = 2*nYPadding+(nNumberOfRows-1)*nYOffset;
478
0
            if( nRemainingSpace < nNormalSpacingHeight )
479
0
            {
480
                //reduce spacing between the entries
481
0
                nYPadding = nYOffset = nRemainingSpace/(nNumberOfRows+1);
482
0
            }
483
0
            else
484
0
            {
485
                //we have some space left that should be spread equally between all rows
486
0
                sal_Int32 nRemainingSingleSpace = (nRemainingSpace-nNormalSpacingHeight)/(nNumberOfRows+1);
487
0
                nYPadding += nRemainingSingleSpace;
488
0
                nYOffset += nRemainingSingleSpace;
489
0
            }
490
0
        }
491
492
        //check spacing between columns
493
0
        sal_Int32 nSumWidth = 0;
494
0
        for (sal_Int32 nColumn = 0; nColumn < nNumberOfColumns; nColumn++)
495
0
            nSumWidth += aColumnWidths[nColumn];
496
0
        nRemainingSpace = rRemainingSpace.Width - nSumWidth;
497
0
        if( nRemainingSpace>=0 )
498
0
        {
499
0
            sal_Int32 nNormalSpacingWidth = 2*nXPadding+(nNumberOfColumns-1)*nXOffset;
500
0
            if( nRemainingSpace < nNormalSpacingWidth )
501
0
            {
502
                //reduce spacing between the entries
503
0
                nXPadding = nXOffset = nRemainingSpace/(nNumberOfColumns+1);
504
0
            }
505
0
            else
506
0
            {
507
                //we have some space left that should be spread equally between all columns
508
0
                sal_Int32 nRemainingSingleSpace = (nRemainingSpace-nNormalSpacingWidth)/(nNumberOfColumns+1);
509
0
                nXPadding += nRemainingSingleSpace;
510
0
                nXOffset += nRemainingSingleSpace;
511
0
            }
512
0
        }
513
0
    }
514
0
    else if( eExpansion == css::chart::ChartLegendExpansion_HIGH )
515
0
    {
516
0
        sal_Int32 nMaxNumberOfRows = nMaxEntryHeight
517
0
            ? (rRemainingSpace.Height - 2*nYPadding ) / nMaxEntryHeight
518
0
            : 0;
519
520
0
        nNumberOfColumns = nMaxNumberOfRows
521
0
            ? static_cast< sal_Int32 >(
522
0
                ceil( static_cast< double >( nNumberOfEntries ) /
523
0
                      static_cast< double >( nMaxNumberOfRows ) ))
524
0
            : 0;
525
0
        nNumberOfRows =  nNumberOfColumns
526
0
            ? static_cast< sal_Int32 >(
527
0
                ceil( static_cast< double >( nNumberOfEntries ) /
528
0
                      static_cast< double >( nNumberOfColumns ) ))
529
0
            : 0;
530
0
    }
531
0
    else if( eExpansion == css::chart::ChartLegendExpansion_WIDE )
532
0
    {
533
0
        sal_Int32 nMaxNumberOfColumns = nMaxEntryWidth
534
0
            ? (rRemainingSpace.Width - 2*nXPadding ) / nMaxEntryWidth
535
0
            : 0;
536
537
0
        nNumberOfRows = nMaxNumberOfColumns
538
0
            ? static_cast< sal_Int32 >(
539
0
                ceil( static_cast< double >( nNumberOfEntries ) /
540
0
                      static_cast< double >( nMaxNumberOfColumns ) ))
541
0
            : 0;
542
0
        nNumberOfColumns = nNumberOfRows
543
0
            ? static_cast< sal_Int32 >(
544
0
                ceil( static_cast< double >( nNumberOfEntries ) /
545
0
                      static_cast< double >( nNumberOfRows ) ))
546
0
            : 0;
547
0
    }
548
0
    else // css::chart::ChartLegendExpansion_BALANCED
549
0
    {
550
0
        double fAspect = nMaxEntryHeight
551
0
            ? static_cast< double >( nMaxEntryWidth ) / static_cast< double >( nMaxEntryHeight )
552
0
            : 0.0;
553
554
0
        nNumberOfRows = static_cast< sal_Int32 >(
555
0
            ceil( sqrt( static_cast< double >( nNumberOfEntries ) * fAspect )));
556
0
        nNumberOfColumns = nNumberOfRows
557
0
            ? static_cast< sal_Int32 >(
558
0
                ceil( static_cast< double >( nNumberOfEntries ) /
559
0
                      static_cast< double >( nNumberOfRows ) ))
560
0
            : 0;
561
0
    }
562
563
0
    if(nNumberOfRows<=0)
564
0
        return aResultingLegendSize;
565
566
0
    if( eExpansion != css::chart::ChartLegendExpansion_CUSTOM )
567
0
    {
568
0
        lcl_collectColumnWidths( aColumnWidths, nNumberOfRows, nNumberOfColumns, aTextShapes, nSymbolPlusDistanceWidth );
569
0
        lcl_collectRowHeighs( aRowHeights, nNumberOfRows, nNumberOfColumns, aTextShapes );
570
0
        nTextLineHeight = lcl_getTextLineHeight( aRowHeights, nNumberOfRows, fViewFontSize );
571
0
    }
572
573
0
    sal_Int32 nCurrentXPos = bSymbolsLeftSide ? nXPadding : -nXPadding;
574
575
    // place entries into column and rows
576
0
    sal_Int32 nMaxYPos = 0;
577
578
0
    for (sal_Int32 nColumn = 0; nColumn < nNumberOfColumns; ++nColumn)
579
0
    {
580
0
        sal_Int32 nCurrentYPos = nYPadding + nYStartPosition;
581
0
        for (sal_Int32 nRow = 0; nRow < nNumberOfRows; ++nRow)
582
0
        {
583
0
            sal_Int32 nEntry = nColumn + nRow * nNumberOfColumns;
584
0
            if( nEntry >= nNumberOfEntries )
585
0
                break;
586
587
            // text shape
588
0
            const rtl::Reference<SvxShapeText>& xTextShape( aTextShapes[nEntry] );
589
0
            if( xTextShape.is() )
590
0
            {
591
0
                awt::Size aTextSize( xTextShape->getSize() );
592
0
                sal_Int32 nTextXPos = nCurrentXPos + nSymbolPlusDistanceWidth;
593
0
                if( !bSymbolsLeftSide )
594
0
                    nTextXPos = nCurrentXPos - nSymbolPlusDistanceWidth - aTextSize.Width;
595
0
                xTextShape->setPosition( awt::Point( nTextXPos, nCurrentYPos ));
596
0
            }
597
598
            // symbol
599
0
            rtl::Reference<SvxShapeGroup> & xSymbol( rEntries[ nEntry ].xSymbol );
600
0
            if( xSymbol.is() )
601
0
            {
602
0
                awt::Size aSymbolSize( rMaxSymbolExtent );
603
0
                sal_Int32 nSymbolXPos = nCurrentXPos;
604
0
                if( !bSymbolsLeftSide )
605
0
                    nSymbolXPos = nCurrentXPos - rMaxSymbolExtent.Width;
606
0
                sal_Int32 nSymbolYPos = nCurrentYPos + ( ( nTextLineHeight - aSymbolSize.Height ) / 2 );
607
0
                xSymbol->setPosition( awt::Point( nSymbolXPos, nSymbolYPos ) );
608
0
            }
609
610
0
            nCurrentYPos += aRowHeights[ nRow ];
611
0
            if( nRow+1 < nNumberOfRows )
612
0
                nCurrentYPos += nYOffset;
613
0
            nMaxYPos = std::max( nMaxYPos, nCurrentYPos );
614
0
        }
615
0
        if( bSymbolsLeftSide )
616
0
        {
617
0
            nCurrentXPos += aColumnWidths[nColumn];
618
0
            if( nColumn+1 < nNumberOfColumns )
619
0
                nCurrentXPos += nXOffset;
620
0
        }
621
0
        else
622
0
        {
623
0
            nCurrentXPos -= aColumnWidths[nColumn];
624
0
            if( nColumn+1 < nNumberOfColumns )
625
0
                nCurrentXPos -= nXOffset;
626
0
        }
627
0
    }
628
629
0
    if( !bIsCustomSize )
630
0
    {
631
0
        if( bSymbolsLeftSide )
632
0
            aResultingLegendSize.Width  = std::max( aResultingLegendSize.Width, nCurrentXPos + nXPadding );
633
0
        else
634
0
        {
635
0
            sal_Int32 nLegendWidth = -(nCurrentXPos-nXPadding);
636
0
            aResultingLegendSize.Width  = std::max( aResultingLegendSize.Width, nLegendWidth );
637
0
        }
638
0
        aResultingLegendSize.Height = std::max( aResultingLegendSize.Height, nMaxYPos + nYPadding );
639
0
    }
640
641
0
    if( !bSymbolsLeftSide )
642
0
    {
643
0
        sal_Int32 nLegendWidth = aResultingLegendSize.Width;
644
0
        awt::Point aPos(0,0);
645
0
        for( sal_Int32 nEntry=0; nEntry<nNumberOfEntries; nEntry++ )
646
0
        {
647
0
            rtl::Reference<SvxShapeGroup> & xSymbol( rEntries[ nEntry ].xSymbol );
648
0
            aPos = xSymbol->getPosition();
649
0
            aPos.X += nLegendWidth;
650
0
            xSymbol->setPosition( aPos );
651
0
            rtl::Reference<SvxShapeText> & xText( aTextShapes[ nEntry ] );
652
0
            aPos = xText->getPosition();
653
0
            aPos.X += nLegendWidth;
654
0
            xText->setPosition( aPos );
655
0
        }
656
0
    }
657
658
0
    return aResultingLegendSize;
659
0
}
660
661
// #i109336# Improve auto positioning in chart
662
sal_Int32 lcl_getLegendLeftRightMargin()
663
0
{
664
0
    return 210;  // 1/100 mm
665
0
}
666
667
// #i109336# Improve auto positioning in chart
668
sal_Int32 lcl_getLegendTopBottomMargin()
669
0
{
670
0
    return 185;  // 1/100 mm
671
0
}
672
673
chart2::RelativePosition lcl_getDefaultPosition( LegendPosition ePos, const awt::Rectangle& rOutAvailableSpace, const awt::Size & rPageSize )
674
0
{
675
0
    chart2::RelativePosition aResult;
676
677
0
    switch( ePos )
678
0
    {
679
0
        case LegendPosition_LINE_START:
680
0
            {
681
                // #i109336# Improve auto positioning in chart
682
0
                const double fDefaultDistance = static_cast< double >( lcl_getLegendLeftRightMargin() ) /
683
0
                    static_cast< double >( rPageSize.Width );
684
0
                aResult = chart2::RelativePosition(
685
0
                    fDefaultDistance, 0.5, drawing::Alignment_LEFT );
686
0
            }
687
0
            break;
688
0
        case LegendPosition_LINE_END:
689
0
            {
690
                // #i109336# Improve auto positioning in chart
691
0
                const double fDefaultDistance = static_cast< double >( lcl_getLegendLeftRightMargin() ) /
692
0
                    static_cast< double >( rPageSize.Width );
693
0
                aResult = chart2::RelativePosition(
694
0
                    1.0 - fDefaultDistance, 0.5, drawing::Alignment_RIGHT );
695
0
            }
696
0
            break;
697
0
        case LegendPosition_PAGE_START:
698
0
            {
699
                // #i109336# Improve auto positioning in chart
700
0
                const double fDefaultDistance = static_cast< double >( lcl_getLegendTopBottomMargin() ) /
701
0
                    static_cast< double >( rPageSize.Height );
702
0
                double fDistance = (static_cast<double>(rOutAvailableSpace.Y)/static_cast<double>(rPageSize.Height)) + fDefaultDistance;
703
0
                aResult = chart2::RelativePosition(
704
0
                    0.5, fDistance, drawing::Alignment_TOP );
705
0
            }
706
0
            break;
707
0
        case LegendPosition_PAGE_END:
708
0
            {
709
                // #i109336# Improve auto positioning in chart
710
0
                const double fDefaultDistance = static_cast< double >( lcl_getLegendTopBottomMargin() ) /
711
0
                    static_cast< double >( rPageSize.Height );
712
713
0
                double fDistance = double(rPageSize.Height - (rOutAvailableSpace.Y + rOutAvailableSpace.Height));
714
0
                fDistance += fDefaultDistance;
715
0
                fDistance /= double(rPageSize.Height);
716
717
0
                aResult = chart2::RelativePosition(
718
0
                    0.5, 1.0 - fDistance, drawing::Alignment_BOTTOM );
719
0
            }
720
0
            break;
721
0
        case LegendPosition::LegendPosition_MAKE_FIXED_SIZE:
722
0
        default:
723
            // nothing to be set
724
0
            break;
725
0
    }
726
727
0
    return aResult;
728
0
}
729
730
/**  @return
731
         a point relative to the upper left corner that can be used for
732
         XShape::setPosition()
733
*/
734
awt::Point lcl_calculatePositionAndRemainingSpace(
735
    awt::Rectangle & rRemainingSpace,
736
    const awt::Size & rPageSize,
737
    const chart2::RelativePosition& rRelPos,
738
    LegendPosition ePos,
739
    const awt::Size& aLegendSize,
740
    bool bOverlay )
741
0
{
742
    // calculate position
743
0
    awt::Point aResult(
744
0
        static_cast< sal_Int32 >( rRelPos.Primary * rPageSize.Width ),
745
0
        static_cast< sal_Int32 >( rRelPos.Secondary * rPageSize.Height ));
746
747
0
    aResult = RelativePositionHelper::getUpperLeftCornerOfAnchoredObject(
748
0
        aResult, aLegendSize, rRelPos.Anchor );
749
750
    // adapt rRemainingSpace if LegendPosition is not CUSTOM
751
    // #i109336# Improve auto positioning in chart
752
0
    sal_Int32 nXDistance = lcl_getLegendLeftRightMargin();
753
0
    sal_Int32 nYDistance = lcl_getLegendTopBottomMargin();
754
0
    if (!bOverlay) switch( ePos )
755
0
    {
756
0
        case LegendPosition_LINE_START:
757
0
        {
758
0
            sal_Int32 nExtent = aLegendSize.Width;
759
0
            rRemainingSpace.Width -= ( nExtent + nXDistance );
760
0
            rRemainingSpace.X += ( nExtent + nXDistance );
761
0
        }
762
0
        break;
763
0
        case LegendPosition_LINE_END:
764
0
        {
765
0
            rRemainingSpace.Width -= ( aLegendSize.Width + nXDistance );
766
0
        }
767
0
        break;
768
0
        case LegendPosition_PAGE_START:
769
0
        {
770
0
            sal_Int32 nExtent = aLegendSize.Height;
771
0
            rRemainingSpace.Height -= ( nExtent + nYDistance );
772
0
            rRemainingSpace.Y += ( nExtent + nYDistance );
773
0
        }
774
0
        break;
775
0
        case LegendPosition_PAGE_END:
776
0
        {
777
0
            rRemainingSpace.Height -= ( aLegendSize.Height + nYDistance );
778
0
        }
779
0
        break;
780
781
0
        default:
782
            // nothing
783
0
            break;
784
0
    }
785
786
    // adjust the legend position. Esp. for old files that had slightly smaller legends
787
0
    const sal_Int32 nEdgeDistance( 30 );
788
0
    if( aResult.X + aLegendSize.Width > rPageSize.Width )
789
0
    {
790
0
        sal_Int32 nNewX( (rPageSize.Width - aLegendSize.Width) - nEdgeDistance );
791
0
        if( nNewX > rPageSize.Width / 4 )
792
0
            aResult.X = nNewX;
793
0
    }
794
0
    if( aResult.Y + aLegendSize.Height > rPageSize.Height )
795
0
    {
796
0
        sal_Int32 nNewY( (rPageSize.Height - aLegendSize.Height) - nEdgeDistance );
797
0
        if( nNewY > rPageSize.Height / 4 )
798
0
            aResult.Y = nNewY;
799
0
    }
800
801
0
    return aResult;
802
0
}
803
804
bool lcl_shouldSymbolsBePlacedOnTheLeftSide( const Reference< beans::XPropertySet >& xLegendProp, sal_Int16 nDefaultWritingMode )
805
0
{
806
0
    bool bSymbolsLeftSide = true;
807
0
    try
808
0
    {
809
0
        if( SvtCTLOptions::IsCTLFontEnabled() )
810
0
        {
811
0
            if(xLegendProp.is())
812
0
            {
813
0
                sal_Int16 nWritingMode=-1;
814
0
                if( xLegendProp->getPropertyValue( u"WritingMode"_ustr ) >>= nWritingMode )
815
0
                {
816
0
                    if( nWritingMode == text::WritingMode2::PAGE )
817
0
                        nWritingMode = nDefaultWritingMode;
818
0
                    if( nWritingMode == text::WritingMode2::RL_TB )
819
0
                        bSymbolsLeftSide=false;
820
0
                }
821
0
            }
822
0
        }
823
0
    }
824
0
    catch( const uno::Exception & )
825
0
    {
826
0
        DBG_UNHANDLED_EXCEPTION("chart2");
827
0
    }
828
0
    return bSymbolsLeftSide;
829
0
}
830
831
std::vector<std::shared_ptr<VButton>> lcl_createButtons(
832
                       rtl::Reference<SvxShapeGroupAnyD> const & xLegendContainer,
833
                       ChartModel& rModel, bool bPlaceButtonsVertically, tools::Long & nUsedHeight)
834
0
{
835
0
    std::vector<std::shared_ptr<VButton>> aButtons;
836
837
0
    chart2api::AbstractPivotTableDataProvider* pPivotTableDataProvider =
838
0
        dynamic_cast<chart2api::AbstractPivotTableDataProvider*>(rModel.getDataProvider().get());
839
0
    if (!pPivotTableDataProvider)
840
0
        return aButtons;
841
842
0
    if (pPivotTableDataProvider->getColumnFields().empty())
843
0
        return aButtons;
844
845
0
    awt::Size aSize(2000, 700);
846
0
    int x = 100;
847
0
    int y = 100;
848
849
0
    const std::vector<chart2::data::PivotTableFieldEntry>& aPivotFieldEntries = pPivotTableDataProvider->getColumnFields();
850
0
    for (chart2::data::PivotTableFieldEntry const & sColumnFieldEntry : aPivotFieldEntries)
851
0
    {
852
0
        auto pButton = std::make_shared<VButton>();
853
0
        aButtons.push_back(pButton);
854
0
        pButton->init(xLegendContainer);
855
0
        awt::Point aNewPosition(x, y);
856
0
        pButton->setLabel(sColumnFieldEntry.Name);
857
0
        pButton->setCID("FieldButton.Column." + OUString::number(sColumnFieldEntry.DimensionIndex));
858
0
        pButton->setPosition(aNewPosition);
859
0
        pButton->setSize(aSize);
860
0
        if (sColumnFieldEntry.Name == "Data")
861
0
        {
862
0
            pButton->showArrow(false);
863
0
            pButton->setBGColor(Color(0x00F6F6F6));
864
0
        }
865
0
        if (sColumnFieldEntry.HasHiddenMembers)
866
0
            pButton->setArrowColor(Color(0x0000FF));
867
868
0
        if (bPlaceButtonsVertically)
869
0
            y += aSize.Height + 100;
870
0
        else
871
0
            x += aSize.Width + 100;
872
0
    }
873
0
    if (bPlaceButtonsVertically)
874
0
        nUsedHeight += y + 100;
875
0
    else
876
0
        nUsedHeight += aSize.Height + 100;
877
878
0
    return aButtons;
879
0
}
880
881
} // anonymous namespace
882
883
VLegend::VLegend(
884
    rtl::Reference< Legend > xLegend,
885
    const Reference< uno::XComponentContext > & xContext,
886
    std::vector< LegendEntryProvider* >&& rLegendEntryProviderList,
887
    rtl::Reference<SvxShapeGroupAnyD> xTargetPage,
888
    ChartModel& rModel )
889
0
        : m_xTarget(std::move(xTargetPage))
890
0
        , m_xLegend(std::move(xLegend))
891
0
        , mrModel(rModel)
892
0
        , m_xContext(xContext)
893
0
        , m_aLegendEntryProviderList(std::move(rLegendEntryProviderList))
894
0
        , m_nDefaultWritingMode(text::WritingMode2::LR_TB)
895
0
{
896
0
}
897
898
void VLegend::setDefaultWritingMode( sal_Int16 nDefaultWritingMode )
899
0
{
900
0
    m_nDefaultWritingMode = nDefaultWritingMode;
901
0
}
902
903
bool VLegend::isVisible( const rtl::Reference< Legend > & xLegend )
904
0
{
905
0
    if( ! xLegend.is())
906
0
        return false;
907
908
0
    bool bShow = false;
909
0
    try
910
0
    {
911
0
        xLegend->getPropertyValue( u"Show"_ustr) >>= bShow;
912
0
    }
913
0
    catch( const uno::Exception & )
914
0
    {
915
0
        DBG_UNHANDLED_EXCEPTION("chart2");
916
0
    }
917
918
0
    return bShow;
919
0
}
920
921
void VLegend::createShapes(
922
    const awt::Size & rAvailableSpace,
923
    const awt::Size & rPageSize,
924
    awt::Size & rDefaultLegendSize )
925
0
{
926
0
    if(! (m_xLegend.is() && m_xTarget.is()))
927
0
        return;
928
929
0
    try
930
0
    {
931
        //create shape and add to page
932
0
        OUString aLegendParticle( ObjectIdentifier::createParticleForLegend( &mrModel ) );
933
0
        m_xShape = ShapeFactory::createGroup2D( m_xTarget,
934
0
                    ObjectIdentifier::createClassifiedIdentifierForParticle( aLegendParticle ) );
935
936
        // create and insert sub-shapes
937
0
        rtl::Reference<SvxShapeGroupAnyD> xLegendContainer = m_xShape;
938
0
        if( xLegendContainer.is() )
939
0
        {
940
            // for quickly setting properties
941
0
            tPropertyValues aLineFillProperties;
942
0
            tPropertyValues aTextProperties;
943
944
0
            css::chart::ChartLegendExpansion eExpansion = css::chart::ChartLegendExpansion_HIGH;
945
0
            awt::Size aLegendSize( rAvailableSpace );
946
947
0
            bool bCustom = false;
948
0
            LegendPosition eLegendPosition = LegendPosition_LINE_END;
949
            // get Expansion property
950
0
            m_xLegend->getPropertyValue(u"Expansion"_ustr) >>= eExpansion;
951
0
            if( eExpansion == css::chart::ChartLegendExpansion_CUSTOM )
952
0
            {
953
0
                RelativeSize aRelativeSize;
954
0
                if (m_xLegend->getPropertyValue(u"RelativeSize"_ustr) >>= aRelativeSize)
955
0
                {
956
0
                    aLegendSize.Width = static_cast<sal_Int32>(::rtl::math::approxCeil( aRelativeSize.Primary * rPageSize.Width ));
957
0
                    aLegendSize.Height = static_cast<sal_Int32>(::rtl::math::approxCeil( aRelativeSize.Secondary * rPageSize.Height ));
958
0
                    bCustom = true;
959
0
                }
960
0
                else
961
0
                {
962
0
                    eExpansion = css::chart::ChartLegendExpansion_HIGH;
963
0
                }
964
0
            }
965
0
            m_xLegend->getPropertyValue(u"AnchorPosition"_ustr) >>= eLegendPosition;
966
0
            lcl_getProperties( m_xLegend, aLineFillProperties, aTextProperties, rPageSize );
967
968
            // create entries
969
0
            double fViewFontSize = lcl_CalcViewFontSize( m_xLegend, rPageSize );//todo
970
            // #i109336# Improve auto positioning in chart
971
0
            sal_Int32 nSymbolHeight = static_cast< sal_Int32 >( fViewFontSize * 0.6  );
972
0
            sal_Int32 nSymbolWidth = nSymbolHeight;
973
974
0
            for (LegendEntryProvider* pLegendEntryProvider : m_aLegendEntryProviderList)
975
0
            {
976
0
                if (pLegendEntryProvider)
977
0
                {
978
0
                    awt::Size aCurrentRatio = pLegendEntryProvider->getPreferredLegendKeyAspectRatio();
979
0
                    sal_Int32 nCurrentWidth = aCurrentRatio.Width;
980
0
                    if( aCurrentRatio.Height > 0 )
981
0
                    {
982
0
                        nCurrentWidth = nSymbolHeight* aCurrentRatio.Width/aCurrentRatio.Height;
983
0
                    }
984
0
                    nSymbolWidth = std::max( nSymbolWidth, nCurrentWidth );
985
0
                }
986
0
            }
987
0
            awt::Size aMaxSymbolExtent( nSymbolWidth, nSymbolHeight );
988
989
0
            std::vector<ViewLegendEntry> aViewEntries;
990
0
            for(LegendEntryProvider* pLegendEntryProvider : m_aLegendEntryProviderList)
991
0
            {
992
0
                if (pLegendEntryProvider)
993
0
                {
994
0
                    std::vector<ViewLegendEntry> aNewEntries = pLegendEntryProvider->createLegendEntries(
995
0
                                                                    aMaxSymbolExtent, eLegendPosition, m_xLegend,
996
0
                                                                    xLegendContainer, m_xContext, mrModel);
997
0
                    aViewEntries.insert( aViewEntries.end(), aNewEntries.begin(), aNewEntries.end() );
998
0
                }
999
0
            }
1000
1001
0
            bool bSymbolsLeftSide = lcl_shouldSymbolsBePlacedOnTheLeftSide( m_xLegend, m_nDefaultWritingMode );
1002
1003
0
            chart2api::AbstractPivotTableDataProvider* pPivotTableDataProvider =
1004
0
                dynamic_cast<chart2api::AbstractPivotTableDataProvider*>(mrModel.getDataProvider().get());
1005
0
            bool bIsPivotChart = pPivotTableDataProvider != nullptr;
1006
1007
0
            if ( !aViewEntries.empty() || bIsPivotChart )
1008
0
            {
1009
                // create buttons
1010
0
                tools::Long nUsedButtonHeight = 0;
1011
0
                bool bPlaceButtonsVertically = (eLegendPosition != LegendPosition_PAGE_START &&
1012
0
                                                eLegendPosition != LegendPosition_PAGE_END &&
1013
0
                                                eExpansion != css::chart::ChartLegendExpansion_WIDE);
1014
1015
0
                std::vector<std::shared_ptr<VButton>> aButtons = lcl_createButtons(xLegendContainer, mrModel, bPlaceButtonsVertically, nUsedButtonHeight);
1016
1017
                // A custom size includes the size we used for buttons already, so we need to
1018
                // subtract that from the size that is available for the legend
1019
0
                if (bCustom)
1020
0
                    aLegendSize.Height -= nUsedButtonHeight;
1021
1022
                // place the legend entries
1023
0
                aLegendSize = lcl_placeLegendEntries(aViewEntries, eExpansion, bSymbolsLeftSide, fViewFontSize,
1024
0
                                                     aMaxSymbolExtent, aTextProperties, xLegendContainer,
1025
0
                                                     aLegendSize, nUsedButtonHeight, rPageSize, bIsPivotChart, rDefaultLegendSize);
1026
1027
0
                uno::Reference<beans::XPropertySet> xModelPage(mrModel.getPageBackground());
1028
1029
0
                for (std::shared_ptr<VButton> const & pButton : aButtons)
1030
0
                {
1031
                    // adjust the width of the buttons if we place them vertically
1032
0
                    if (bPlaceButtonsVertically)
1033
0
                        pButton->setSize({aLegendSize.Width - 200, pButton->getSize().Height});
1034
1035
                    // create the buttons
1036
0
                    pButton->createShapes(xModelPage);
1037
0
                }
1038
1039
0
                rtl::Reference<SvxShapeRect> xBorder = ShapeFactory::createRectangle(
1040
0
                    xLegendContainer, aLegendSize, awt::Point(0, 0), aLineFillProperties.first,
1041
0
                    aLineFillProperties.second, ShapeFactory::StackPosition::Bottom);
1042
1043
                //because of this name this border will be used for marking the legend
1044
0
                ShapeFactory::setShapeName(xBorder, u"MarkHandles"_ustr);
1045
0
            }
1046
0
        }
1047
0
    }
1048
0
    catch( const uno::Exception & )
1049
0
    {
1050
0
        DBG_UNHANDLED_EXCEPTION("chart2" );
1051
0
    }
1052
0
}
1053
1054
void VLegend::changePosition(
1055
    awt::Rectangle & rOutAvailableSpace,
1056
    const awt::Size & rPageSize,
1057
    const css::awt::Size & rDefaultLegendSize )
1058
0
{
1059
0
    if(! m_xShape.is())
1060
0
        return;
1061
1062
0
    try
1063
0
    {
1064
        // determine position and alignment depending on default position
1065
0
        awt::Size aLegendSize = m_xShape->getSize();
1066
0
        chart2::RelativePosition aRelativePosition;
1067
1068
0
        bool bDefaultLegendSize = rDefaultLegendSize.Width != 0 || rDefaultLegendSize.Height != 0;
1069
0
        bool bAutoPosition =
1070
0
            ! (m_xLegend->getPropertyValue( u"RelativePosition"_ustr) >>= aRelativePosition);
1071
1072
0
        LegendPosition ePos = LegendPosition_LINE_END;
1073
0
        m_xLegend->getPropertyValue( u"AnchorPosition"_ustr) >>= ePos;
1074
1075
0
        bool bOverlay = false;
1076
0
        m_xLegend->getPropertyValue(u"Overlay"_ustr) >>= bOverlay;
1077
        //calculate position
1078
0
        if( bAutoPosition )
1079
0
        {
1080
            // auto position: relative to remaining space
1081
0
            aRelativePosition = lcl_getDefaultPosition( ePos, rOutAvailableSpace, rPageSize );
1082
0
            awt::Point aPos = lcl_calculatePositionAndRemainingSpace(
1083
0
                rOutAvailableSpace, rPageSize, aRelativePosition, ePos, aLegendSize, bOverlay );
1084
0
            m_xShape->setPosition( aPos );
1085
0
        }
1086
0
        else
1087
0
        {
1088
            // manual position: relative to whole page
1089
0
            awt::Rectangle aAvailableSpace( 0, 0, rPageSize.Width, rPageSize.Height );
1090
0
            awt::Point aPos = lcl_calculatePositionAndRemainingSpace(
1091
0
                aAvailableSpace, rPageSize, aRelativePosition, ePos, bDefaultLegendSize ? rDefaultLegendSize : aLegendSize, bOverlay );
1092
0
            m_xShape->setPosition( aPos );
1093
1094
0
            if (!bOverlay)
1095
0
            {
1096
                // calculate remaining space as if having autoposition:
1097
0
                aRelativePosition = lcl_getDefaultPosition( ePos, rOutAvailableSpace, rPageSize );
1098
0
                lcl_calculatePositionAndRemainingSpace(
1099
0
                    rOutAvailableSpace, rPageSize, aRelativePosition, ePos, bDefaultLegendSize ? rDefaultLegendSize : aLegendSize, bOverlay );
1100
0
            }
1101
0
        }
1102
0
    }
1103
0
    catch( const uno::Exception & )
1104
0
    {
1105
0
        DBG_UNHANDLED_EXCEPTION("chart2" );
1106
0
    }
1107
0
}
1108
1109
} //namespace chart
1110
1111
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */