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 <scitems.hxx>
21 #include <editeng/borderline.hxx>
22 #include <editeng/boxitem.hxx>
23 #include <editeng/wghtitem.hxx>
24 #include <editeng/justifyitem.hxx>
25 #include <o3tl/safeint.hxx>
26 #include <osl/diagnose.h>
27 #include <svl/itemset.hxx>
28 
29 #include <dpoutput.hxx>
30 #include <document.hxx>
31 #include <attrib.hxx>
32 #include <formula/errorcodes.hxx>
33 #include <miscuno.hxx>
34 #include <globstr.hrc>
35 #include <stlpool.hxx>
36 #include <stlsheet.hxx>
37 #include <scresid.hxx>
38 #include <unonames.hxx>
39 #include <strings.hrc>
40 #include <stringutil.hxx>
41 #include <dputil.hxx>
42 
43 #include <com/sun/star/beans/XPropertySet.hpp>
44 #include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
45 #include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
46 #include <com/sun/star/sheet/DataPilotTablePositionData.hpp>
47 #include <com/sun/star/sheet/DataPilotTableResultData.hpp>
48 #include <com/sun/star/sheet/MemberResultFlags.hpp>
49 #include <com/sun/star/sheet/DataResultFlags.hpp>
50 #include <com/sun/star/sheet/DataPilotTablePositionType.hpp>
51 #include <com/sun/star/sheet/GeneralFunction2.hpp>
52 #include <com/sun/star/sheet/MemberResult.hpp>
53 #include <com/sun/star/sheet/XDataPilotMemberResults.hpp>
54 #include <com/sun/star/sheet/XDataPilotResults.hpp>
55 #include <com/sun/star/sheet/XDimensionsSupplier.hpp>
56 #include <com/sun/star/sheet/XHierarchiesSupplier.hpp>
57 #include <com/sun/star/sheet/XLevelsSupplier.hpp>
58 #include <com/sun/star/sheet/XMembersAccess.hpp>
59 #include <com/sun/star/sheet/XMembersSupplier.hpp>
60 
61 #include <string_view>
62 #include <vector>
63 
64 using namespace com::sun::star;
65 using ::std::vector;
66 using ::com::sun::star::beans::XPropertySet;
67 using ::com::sun::star::uno::Sequence;
68 using ::com::sun::star::uno::UNO_QUERY;
69 using ::com::sun::star::uno::Reference;
70 using ::com::sun::star::sheet::DataPilotTablePositionData;
71 using ::com::sun::star::sheet::DataPilotTableResultData;
72 
73 #define SC_DP_FRAME_INNER_BOLD      20
74 #define SC_DP_FRAME_OUTER_BOLD      40
75 
76 #define SC_DP_FRAME_COLOR           Color(0,0,0) //( 0x20, 0x40, 0x68 )
77 
78 struct ScDPOutLevelData
79 {
80     tools::Long                                mnDim;
81     tools::Long                                mnHier;
82     tools::Long                                mnLevel;
83     tools::Long                                mnDimPos;
84     sal_uInt32 mnSrcNumFmt; /// Prevailing number format used in the source data.
85     uno::Sequence<sheet::MemberResult>  maResult;
86     OUString                            maName;     /// Name is the internal field name.
87     OUString                            maCaption;  /// Caption is the name visible in the output table.
88     bool                                mbHasHiddenMember:1;
89     bool                                mbDataLayout:1;
90     bool                                mbPageDim:1;
91 
ScDPOutLevelDataScDPOutLevelData92     ScDPOutLevelData(tools::Long nDim, tools::Long nHier, tools::Long nLevel, tools::Long nDimPos, sal_uInt32 nSrcNumFmt, const uno::Sequence<sheet::MemberResult>  &aResult,
93                        const OUString &aName, const OUString &aCaption, bool bHasHiddenMember, bool bDataLayout, bool bPageDim) :
94         mnDim(nDim), mnHier(nHier), mnLevel(nLevel), mnDimPos(nDimPos), mnSrcNumFmt(nSrcNumFmt), maResult(aResult),
95         maName(aName), maCaption(aCaption), mbHasHiddenMember(bHasHiddenMember), mbDataLayout(bDataLayout),
96         mbPageDim(bPageDim)
97     {
98     }
99 
100     // bug (73840) in uno::Sequence - copy and then assign doesn't work!
101 };
102 
103 
104 
105 namespace {
106     struct ScDPOutLevelDataComparator
107     {
operator ()__anon2ffb30190111::ScDPOutLevelDataComparator108         bool operator()(const ScDPOutLevelData & rA, const ScDPOutLevelData & rB)
109         {
110             return rA.mnDimPos<rB.mnDimPos || ( rA.mnDimPos==rB.mnDimPos && rA.mnHier<rB.mnHier ) ||
111             ( rA.mnDimPos==rB.mnDimPos && rA.mnHier==rB.mnHier && rA.mnLevel<rB.mnLevel );
112         }
113     };
114 
115 
116 class ScDPOutputImpl
117 {
118     ScDocument*         mpDoc;
119     sal_uInt16          mnTab;
120     ::std::vector< bool > mbNeedLineCols;
121     ::std::vector< SCCOL > mnCols;
122 
123     ::std::vector< bool > mbNeedLineRows;
124     ::std::vector< SCROW > mnRows;
125 
126     SCCOL   mnTabStartCol;
127     SCROW   mnTabStartRow;
128 
129     SCCOL   mnDataStartCol;
130     SCROW   mnDataStartRow;
131     SCCOL   mnTabEndCol;
132     SCROW   mnTabEndRow;
133 
134 public:
135     ScDPOutputImpl( ScDocument* pDoc, sal_uInt16 nTab,
136         SCCOL   nTabStartCol,
137         SCROW   nTabStartRow,
138         SCCOL nDataStartCol,
139         SCROW nDataStartRow,
140         SCCOL nTabEndCol,
141         SCROW nTabEndRow );
142     bool AddRow( SCROW nRow );
143     bool AddCol( SCCOL nCol );
144 
145     void OutputDataArea();
146     void OutputBlockFrame ( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, bool bHori = false );
147 
148 };
149 
OutputDataArea()150 void ScDPOutputImpl::OutputDataArea()
151 {
152     AddRow( mnDataStartRow );
153     AddCol( mnDataStartCol );
154 
155     mnCols.push_back( mnTabEndCol+1); //set last row bottom
156     mnRows.push_back( mnTabEndRow+1); //set last col bottom
157 
158     bool bAllRows = ( ( mnTabEndRow - mnDataStartRow + 2 ) == static_cast<SCROW>(mnRows.size()) );
159 
160     std::sort( mnCols.begin(), mnCols.end());
161     std::sort( mnRows.begin(), mnRows.end());
162 
163     for( SCCOL nCol = 0; nCol < static_cast<SCCOL>(mnCols.size())-1; nCol ++ )
164     {
165         if ( !bAllRows )
166         {
167             if ( nCol < static_cast<SCCOL>(mnCols.size())-2)
168             {
169                 for ( SCROW i = nCol%2; i < static_cast<SCROW>(mnRows.size())-2; i +=2 )
170                     OutputBlockFrame( mnCols[nCol], mnRows[i], mnCols[nCol+1]-1, mnRows[i+1]-1 );
171                 if ( mnRows.size()>=2 )
172                     OutputBlockFrame(  mnCols[nCol], mnRows[mnRows.size()-2], mnCols[nCol+1]-1, mnRows[mnRows.size()-1]-1 );
173             }
174             else
175             {
176                 for ( SCROW i = 0 ; i < static_cast<SCROW>(mnRows.size())-1; i++ )
177                     OutputBlockFrame(  mnCols[nCol], mnRows[i], mnCols[nCol+1]-1,  mnRows[i+1]-1 );
178             }
179         }
180         else
181             OutputBlockFrame( mnCols[nCol], mnRows.front(), mnCols[nCol+1]-1, mnRows.back()-1, bAllRows );
182     }
183     //out put rows area outer framer
184     if ( mnTabStartCol != mnDataStartCol )
185     {
186         if ( mnTabStartRow != mnDataStartRow )
187             OutputBlockFrame( mnTabStartCol, mnTabStartRow, mnDataStartCol-1, mnDataStartRow-1 );
188         OutputBlockFrame( mnTabStartCol, mnDataStartRow, mnDataStartCol-1, mnTabEndRow );
189     }
190     //out put cols area outer framer
191     OutputBlockFrame( mnDataStartCol, mnTabStartRow, mnTabEndCol, mnDataStartRow-1 );
192 }
193 
ScDPOutputImpl(ScDocument * pDoc,sal_uInt16 nTab,SCCOL nTabStartCol,SCROW nTabStartRow,SCCOL nDataStartCol,SCROW nDataStartRow,SCCOL nTabEndCol,SCROW nTabEndRow)194 ScDPOutputImpl::ScDPOutputImpl( ScDocument* pDoc, sal_uInt16 nTab,
195         SCCOL   nTabStartCol,
196         SCROW   nTabStartRow,
197         SCCOL nDataStartCol,
198         SCROW nDataStartRow,
199         SCCOL nTabEndCol,
200         SCROW nTabEndRow ):
201     mpDoc( pDoc ),
202     mnTab( nTab ),
203     mnTabStartCol( nTabStartCol ),
204     mnTabStartRow( nTabStartRow ),
205     mnDataStartCol ( nDataStartCol ),
206     mnDataStartRow ( nDataStartRow ),
207     mnTabEndCol(  nTabEndCol ),
208     mnTabEndRow(  nTabEndRow )
209 {
210     mbNeedLineCols.resize( nTabEndCol-nDataStartCol+1, false );
211     mbNeedLineRows.resize( nTabEndRow-nDataStartRow+1, false );
212 
213 }
214 
AddRow(SCROW nRow)215 bool ScDPOutputImpl::AddRow( SCROW nRow )
216 {
217     if ( !mbNeedLineRows[ nRow - mnDataStartRow ] )
218     {
219         mbNeedLineRows[ nRow - mnDataStartRow ] = true;
220         mnRows.push_back( nRow );
221         return true;
222     }
223     else
224         return false;
225 }
226 
AddCol(SCCOL nCol)227 bool ScDPOutputImpl::AddCol( SCCOL nCol )
228 {
229 
230     if ( !mbNeedLineCols[ nCol - mnDataStartCol ] )
231     {
232         mbNeedLineCols[ nCol - mnDataStartCol ] = true;
233         mnCols.push_back( nCol );
234         return true;
235     }
236     else
237         return false;
238 }
239 
OutputBlockFrame(SCCOL nStartCol,SCROW nStartRow,SCCOL nEndCol,SCROW nEndRow,bool bHori)240 void ScDPOutputImpl::OutputBlockFrame ( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, bool bHori )
241 {
242     Color color = SC_DP_FRAME_COLOR;
243     ::editeng::SvxBorderLine aLine( &color, SC_DP_FRAME_INNER_BOLD );
244     ::editeng::SvxBorderLine aOutLine( &color, SC_DP_FRAME_OUTER_BOLD );
245 
246     SvxBoxItem aBox( ATTR_BORDER );
247 
248     if ( nStartCol == mnTabStartCol )
249         aBox.SetLine(&aOutLine, SvxBoxItemLine::LEFT);
250     else
251         aBox.SetLine(&aLine, SvxBoxItemLine::LEFT);
252 
253     if ( nStartRow == mnTabStartRow )
254         aBox.SetLine(&aOutLine, SvxBoxItemLine::TOP);
255     else
256         aBox.SetLine(&aLine, SvxBoxItemLine::TOP);
257 
258     if ( nEndCol == mnTabEndCol ) //bottom row
259         aBox.SetLine(&aOutLine, SvxBoxItemLine::RIGHT);
260     else
261         aBox.SetLine(&aLine,  SvxBoxItemLine::RIGHT);
262 
263     if ( nEndRow == mnTabEndRow ) //bottom
264         aBox.SetLine(&aOutLine,  SvxBoxItemLine::BOTTOM);
265     else
266         aBox.SetLine(&aLine,  SvxBoxItemLine::BOTTOM);
267 
268     SvxBoxInfoItem aBoxInfo( ATTR_BORDER_INNER );
269     aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::VERT,false );
270     if ( bHori )
271     {
272         aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI);
273         aBoxInfo.SetLine( &aLine, SvxBoxInfoItemLine::HORI );
274     }
275     else
276         aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI,false );
277 
278     aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::DISTANCE,false);
279 
280     mpDoc->ApplyFrameAreaTab(ScRange(nStartCol, nStartRow, mnTab, nEndCol, nEndRow , mnTab), aBox, aBoxInfo);
281 
282 }
283 
lcl_SetStyleById(ScDocument * pDoc,SCTAB nTab,SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2,const char * pStrId)284 void lcl_SetStyleById(ScDocument* pDoc, SCTAB nTab,
285                       SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
286                       const char* pStrId)
287 {
288     if ( nCol1 > nCol2 || nRow1 > nRow2 )
289     {
290         OSL_FAIL("SetStyleById: invalid range");
291         return;
292     }
293 
294     OUString aStyleName = ScResId(pStrId);
295     ScStyleSheetPool* pStlPool = pDoc->GetStyleSheetPool();
296     ScStyleSheet* pStyle = static_cast<ScStyleSheet*>( pStlPool->Find( aStyleName, SfxStyleFamily::Para ) );
297     if (!pStyle)
298     {
299         //  create new style (was in ScPivot::SetStyle)
300 
301         pStyle = static_cast<ScStyleSheet*>( &pStlPool->Make( aStyleName, SfxStyleFamily::Para,
302                                                     SfxStyleSearchBits::UserDefined ) );
303         pStyle->SetParent( ScResId(STR_STYLENAME_STANDARD) );
304         SfxItemSet& rSet = pStyle->GetItemSet();
305         if (strcmp(pStrId, STR_PIVOT_STYLENAME_RESULT) == 0 || strcmp(pStrId, STR_PIVOT_STYLENAME_TITLE) == 0){
306             rSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
307             rSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_CJK_FONT_WEIGHT ) );
308             rSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_CTL_FONT_WEIGHT ) );
309         }
310         if (strcmp(pStrId, STR_PIVOT_STYLENAME_CATEGORY) == 0 || strcmp(pStrId, STR_PIVOT_STYLENAME_TITLE) == 0)
311             rSet.Put( SvxHorJustifyItem( SvxCellHorJustify::Left, ATTR_HOR_JUSTIFY ) );
312     }
313 
314     pDoc->ApplyStyleAreaTab( nCol1, nRow1, nCol2, nRow2, nTab, *pStyle );
315 }
316 
lcl_SetFrame(ScDocument * pDoc,SCTAB nTab,SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2,sal_uInt16 nWidth)317 void lcl_SetFrame( ScDocument* pDoc, SCTAB nTab,
318                     SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
319                     sal_uInt16 nWidth )
320 {
321     ::editeng::SvxBorderLine aLine(nullptr, nWidth, SvxBorderLineStyle::SOLID);
322     SvxBoxItem aBox( ATTR_BORDER );
323     aBox.SetLine(&aLine, SvxBoxItemLine::LEFT);
324     aBox.SetLine(&aLine, SvxBoxItemLine::TOP);
325     aBox.SetLine(&aLine, SvxBoxItemLine::RIGHT);
326     aBox.SetLine(&aLine, SvxBoxItemLine::BOTTOM);
327     SvxBoxInfoItem aBoxInfo( ATTR_BORDER_INNER );
328     aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI,false);
329     aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::VERT,false);
330     aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::DISTANCE,false);
331 
332     pDoc->ApplyFrameAreaTab(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab), aBox, aBoxInfo);
333 }
334 
lcl_FillNumberFormats(std::unique_ptr<sal_uInt32[]> & rFormats,sal_Int32 & rCount,const uno::Reference<sheet::XDataPilotMemberResults> & xLevRes,const uno::Reference<container::XIndexAccess> & xDims)335 void lcl_FillNumberFormats( std::unique_ptr<sal_uInt32[]>& rFormats, sal_Int32& rCount,
336                             const uno::Reference<sheet::XDataPilotMemberResults>& xLevRes,
337                             const uno::Reference<container::XIndexAccess>& xDims )
338 {
339     if ( rFormats )
340         return;                         // already set
341 
342     //  xLevRes is from the data layout dimension
343     //TODO: use result sequence from ScDPOutLevelData!
344 
345     uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults();
346 
347     tools::Long nSize = aResult.getLength();
348     if (!nSize)
349         return;
350 
351     //  get names/formats for all data dimensions
352     //TODO: merge this with the loop to collect ScDPOutLevelData?
353 
354     std::vector <OUString> aDataNames;
355     std::vector <sal_uInt32> aDataFormats;
356     sal_Int32 nDimCount = xDims->getCount();
357     sal_Int32 nDim = 0;
358     for ( ; nDim < nDimCount ; nDim++)
359     {
360         uno::Reference<uno::XInterface> xDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
361         uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
362         uno::Reference<container::XNamed> xDimName( xDim, uno::UNO_QUERY );
363         if ( xDimProp.is() && xDimName.is() )
364         {
365             sheet::DataPilotFieldOrientation eDimOrient =
366                 ScUnoHelpFunctions::GetEnumProperty(
367                     xDimProp, SC_UNO_DP_ORIENTATION,
368                     sheet::DataPilotFieldOrientation_HIDDEN );
369             if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
370             {
371                 aDataNames.push_back(xDimName->getName());
372                 tools::Long nFormat = ScUnoHelpFunctions::GetLongProperty(
373                                         xDimProp,
374                                         SC_UNONAME_NUMFMT );
375                 aDataFormats.push_back(nFormat);
376             }
377         }
378     }
379 
380     if (aDataFormats.empty())
381         return;
382 
383     const sheet::MemberResult* pArray = aResult.getConstArray();
384 
385     OUString aName;
386     sal_uInt32* pNumFmt = new sal_uInt32[nSize];
387     if (aDataFormats.size() == 1)
388     {
389         //  only one data dimension -> use its numberformat everywhere
390         tools::Long nFormat = aDataFormats[0];
391         for (tools::Long nPos=0; nPos<nSize; nPos++)
392             pNumFmt[nPos] = nFormat;
393     }
394     else
395     {
396         for (tools::Long nPos=0; nPos<nSize; nPos++)
397         {
398             //  if CONTINUE bit is set, keep previous name
399             //TODO: keep number format instead!
400             if ( !(pArray[nPos].Flags & sheet::MemberResultFlags::CONTINUE) )
401                 aName = pArray[nPos].Name;
402 
403             sal_uInt32 nFormat = 0;
404             for (size_t i=0; i<aDataFormats.size(); i++)
405                 if (aName == aDataNames[i])         //TODO: search more efficiently?
406                 {
407                     nFormat = aDataFormats[i];
408                     break;
409                 }
410             pNumFmt[nPos] = nFormat;
411         }
412     }
413 
414     rFormats.reset( pNumFmt );
415     rCount = nSize;
416 }
417 
lcl_GetFirstNumberFormat(const uno::Reference<container::XIndexAccess> & xDims)418 sal_uInt32 lcl_GetFirstNumberFormat( const uno::Reference<container::XIndexAccess>& xDims )
419 {
420     tools::Long nDimCount = xDims->getCount();
421     for (tools::Long nDim=0; nDim<nDimCount; nDim++)
422     {
423         uno::Reference<beans::XPropertySet> xDimProp(xDims->getByIndex(nDim), uno::UNO_QUERY);
424         if ( xDimProp.is() )
425         {
426             sheet::DataPilotFieldOrientation eDimOrient =
427                 ScUnoHelpFunctions::GetEnumProperty(
428                     xDimProp, SC_UNO_DP_ORIENTATION,
429                     sheet::DataPilotFieldOrientation_HIDDEN );
430             if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
431             {
432                 tools::Long nFormat = ScUnoHelpFunctions::GetLongProperty(
433                                         xDimProp,
434                                         SC_UNONAME_NUMFMT );
435 
436                 return nFormat;     // use format from first found data dimension
437             }
438         }
439     }
440 
441     return 0;       // none found
442 }
443 
lcl_MemberEmpty(const uno::Sequence<sheet::MemberResult> & rSeq)444 bool lcl_MemberEmpty( const uno::Sequence<sheet::MemberResult>& rSeq )
445 {
446     //  used to skip levels that have no members
447 
448     return std::none_of(rSeq.begin(), rSeq.end(),
449         [](const sheet::MemberResult& rMem) {
450             return rMem.Flags & sheet::MemberResultFlags::HASMEMBER; });
451 }
452 
453 /**
454  * Get visible page dimension members as results, except that, if all
455  * members are visible, then this function returns empty result.
456  */
getVisiblePageMembersAsResults(const uno::Reference<uno::XInterface> & xLevel)457 uno::Sequence<sheet::MemberResult> getVisiblePageMembersAsResults( const uno::Reference<uno::XInterface>& xLevel )
458 {
459     if (!xLevel.is())
460         return uno::Sequence<sheet::MemberResult>();
461 
462     uno::Reference<sheet::XMembersSupplier> xMSupplier(xLevel, UNO_QUERY);
463     if (!xMSupplier.is())
464         return uno::Sequence<sheet::MemberResult>();
465 
466     uno::Reference<sheet::XMembersAccess> xNA = xMSupplier->getMembers();
467     if (!xNA.is())
468         return uno::Sequence<sheet::MemberResult>();
469 
470     std::vector<sheet::MemberResult> aRes;
471     const uno::Sequence<OUString> aNames = xNA->getElementNames();
472     for (const OUString& rName : aNames)
473     {
474         xNA->getByName(rName);
475 
476         uno::Reference<beans::XPropertySet> xMemPS(xNA->getByName(rName), UNO_QUERY);
477         if (!xMemPS.is())
478             continue;
479 
480         OUString aCaption = ScUnoHelpFunctions::GetStringProperty(xMemPS, SC_UNO_DP_LAYOUTNAME, OUString());
481         if (aCaption.isEmpty())
482             aCaption = rName;
483 
484         bool bVisible = ScUnoHelpFunctions::GetBoolProperty(xMemPS, SC_UNO_DP_ISVISIBLE);
485 
486         if (bVisible)
487         {
488             /* TODO: any numeric value to obtain? */
489             double fValue;
490             rtl::math::setNan(&fValue);
491             aRes.emplace_back(rName, aCaption, 0, fValue);
492         }
493     }
494 
495     if (aNames.getLength() == static_cast<sal_Int32>(aRes.size()))
496         // All members are visible.  Return empty result.
497         return uno::Sequence<sheet::MemberResult>();
498 
499     return ScUnoHelpFunctions::VectorToSequence(aRes);
500 }
501 
502 }
503 
ScDPOutput(ScDocument * pD,const uno::Reference<sheet::XDimensionsSupplier> & xSrc,const ScAddress & rPos,bool bFilter)504 ScDPOutput::ScDPOutput( ScDocument* pD, const uno::Reference<sheet::XDimensionsSupplier>& xSrc,
505                         const ScAddress& rPos, bool bFilter ) :
506     pDoc( pD ),
507     xSource( xSrc ),
508     aStartPos( rPos ),
509     nColFmtCount( 0 ),
510     nRowFmtCount( 0 ),
511     nSingleNumFmt( 0 ),
512     nColCount(0),
513     nRowCount(0),
514     nHeaderSize(0),
515     bDoFilter(bFilter),
516     bResultsError(false),
517     bSizesValid(false),
518     bSizeOverflow(false),
519     mbHeaderLayout(false)
520 {
521     nTabStartCol = nMemberStartCol = nDataStartCol = nTabEndCol = 0;
522     nTabStartRow = nMemberStartRow = nDataStartRow = nTabEndRow = 0;
523 
524     uno::Reference<sheet::XDataPilotResults> xResult( xSource, uno::UNO_QUERY );
525     if ( xSource.is() && xResult.is() )
526     {
527         //  get dimension results:
528 
529         uno::Reference<container::XIndexAccess> xDims =
530                 new ScNameToIndexAccess( xSource->getDimensions() );
531         tools::Long nDimCount = xDims->getCount();
532         for (tools::Long nDim=0; nDim<nDimCount; nDim++)
533         {
534             uno::Reference<uno::XInterface> xDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
535             uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
536             uno::Reference<sheet::XHierarchiesSupplier> xDimSupp( xDim, uno::UNO_QUERY );
537             if ( xDimProp.is() && xDimSupp.is() )
538             {
539                 sheet::DataPilotFieldOrientation eDimOrient =
540                     ScUnoHelpFunctions::GetEnumProperty(
541                         xDimProp, SC_UNO_DP_ORIENTATION,
542                         sheet::DataPilotFieldOrientation_HIDDEN );
543                 tools::Long nDimPos = ScUnoHelpFunctions::GetLongProperty( xDimProp,
544                         SC_UNO_DP_POSITION );
545                 bool bIsDataLayout = ScUnoHelpFunctions::GetBoolProperty(
546                     xDimProp, SC_UNO_DP_ISDATALAYOUT);
547                 bool bHasHiddenMember = ScUnoHelpFunctions::GetBoolProperty(
548                     xDimProp, SC_UNO_DP_HAS_HIDDEN_MEMBER);
549                 sal_Int32 nNumFmt = ScUnoHelpFunctions::GetLongProperty(
550                     xDimProp, SC_UNO_DP_NUMBERFO);
551 
552                 if ( eDimOrient != sheet::DataPilotFieldOrientation_HIDDEN )
553                 {
554                     uno::Reference<container::XIndexAccess> xHiers =
555                             new ScNameToIndexAccess( xDimSupp->getHierarchies() );
556                     tools::Long nHierarchy = ScUnoHelpFunctions::GetLongProperty(
557                                             xDimProp,
558                                             SC_UNO_DP_USEDHIERARCHY );
559                     if ( nHierarchy >= xHiers->getCount() )
560                         nHierarchy = 0;
561 
562                     uno::Reference<sheet::XLevelsSupplier> xHierSupp(xHiers->getByIndex(nHierarchy),
563                                                                      uno::UNO_QUERY);
564                     if ( xHierSupp.is() )
565                     {
566                         uno::Reference<container::XIndexAccess> xLevels =
567                                 new ScNameToIndexAccess( xHierSupp->getLevels() );
568                         tools::Long nLevCount = xLevels->getCount();
569                         for (tools::Long nLev=0; nLev<nLevCount; nLev++)
570                         {
571                             uno::Reference<uno::XInterface> xLevel(xLevels->getByIndex(nLev),
572                                                                    uno::UNO_QUERY);
573                             uno::Reference<container::XNamed> xLevNam( xLevel, uno::UNO_QUERY );
574                             uno::Reference<sheet::XDataPilotMemberResults> xLevRes(
575                                     xLevel, uno::UNO_QUERY );
576                             if ( xLevNam.is() && xLevRes.is() )
577                             {
578                                 OUString aName = xLevNam->getName();
579                                 Reference<XPropertySet> xPropSet(xLevel, UNO_QUERY);
580                                 // Caption equals the field name by default.
581                                 // #i108948# use ScUnoHelpFunctions::GetStringProperty, because
582                                 // LayoutName is new and may not be present in external implementation
583                                 OUString aCaption = ScUnoHelpFunctions::GetStringProperty( xPropSet,
584                                     SC_UNO_DP_LAYOUTNAME, aName );
585 
586                                 switch ( eDimOrient )
587                                 {
588                                     case sheet::DataPilotFieldOrientation_COLUMN:
589                                     {
590                                         uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults();
591                                         if (!lcl_MemberEmpty(aResult))
592                                         {
593                                             ScDPOutLevelData tmp(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName,
594                                                                    aCaption, bHasHiddenMember, bIsDataLayout, false);
595                                             pColFields.push_back(tmp);
596                                         }
597                                     }
598                                     break;
599                                     case sheet::DataPilotFieldOrientation_ROW:
600                                     {
601                                         uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults();
602                                         if (!lcl_MemberEmpty(aResult))
603                                         {
604                                             ScDPOutLevelData tmp(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName,
605                                                                    aCaption, bHasHiddenMember, bIsDataLayout, false);
606                                             pRowFields.push_back(tmp);
607                                         }
608                                     }
609                                     break;
610                                     case sheet::DataPilotFieldOrientation_PAGE:
611                                     {
612                                         uno::Sequence<sheet::MemberResult> aResult = getVisiblePageMembersAsResults(xLevel);
613                                         // no check on results for page fields
614                                         ScDPOutLevelData tmp(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName,
615                                                                aCaption, bHasHiddenMember, false, true);
616                                         pPageFields.push_back(tmp);
617                                     }
618                                     break;
619                                     default:
620                                     {
621                                         // added to avoid warnings
622                                     }
623                                 }
624 
625                                 // get number formats from data dimensions
626                                 if ( bIsDataLayout )
627                                 {
628                                     OSL_ENSURE( nLevCount == 1, "data layout: multiple levels?" );
629                                     if ( eDimOrient == sheet::DataPilotFieldOrientation_COLUMN )
630                                         lcl_FillNumberFormats( pColNumFmt, nColFmtCount, xLevRes, xDims );
631                                     else if ( eDimOrient == sheet::DataPilotFieldOrientation_ROW )
632                                         lcl_FillNumberFormats( pRowNumFmt, nRowFmtCount, xLevRes, xDims );
633                                 }
634                             }
635                         }
636                     }
637                 }
638                 else if ( bIsDataLayout )
639                 {
640                     // data layout dimension is hidden (allowed if there is only one data dimension)
641                     // -> use the number format from the first data dimension for all results
642 
643                     nSingleNumFmt = lcl_GetFirstNumberFormat( xDims );
644                 }
645             }
646         }
647         std::sort(pColFields.begin(), pColFields.end(), ScDPOutLevelDataComparator());
648         std::sort(pRowFields.begin(), pRowFields.end(), ScDPOutLevelDataComparator());
649         std::sort(pPageFields.begin(), pPageFields.end(), ScDPOutLevelDataComparator());
650 
651         //  get data results:
652 
653         try
654         {
655             aData = xResult->getResults();
656         }
657         catch (const uno::RuntimeException&)
658         {
659             bResultsError = true;
660         }
661     }
662 
663     // get "DataDescription" property (may be missing in external sources)
664 
665     uno::Reference<beans::XPropertySet> xSrcProp( xSource, uno::UNO_QUERY );
666     if ( !xSrcProp.is() )
667         return;
668 
669     try
670     {
671         uno::Any aAny = xSrcProp->getPropertyValue( SC_UNO_DP_DATADESC );
672         OUString aUStr;
673         aAny >>= aUStr;
674         aDataDescription = aUStr;
675     }
676     catch(const uno::Exception&)
677     {
678     }
679 }
680 
~ScDPOutput()681 ScDPOutput::~ScDPOutput()
682 {
683 }
684 
SetPosition(const ScAddress & rPos)685 void ScDPOutput::SetPosition( const ScAddress& rPos )
686 {
687     aStartPos = rPos;
688     bSizesValid = bSizeOverflow = false;
689 }
690 
DataCell(SCCOL nCol,SCROW nRow,SCTAB nTab,const sheet::DataResult & rData)691 void ScDPOutput::DataCell( SCCOL nCol, SCROW nRow, SCTAB nTab, const sheet::DataResult& rData )
692 {
693     tools::Long nFlags = rData.Flags;
694     if ( nFlags & sheet::DataResultFlags::ERROR )
695     {
696         pDoc->SetError( nCol, nRow, nTab, FormulaError::NoValue );
697     }
698     else if ( nFlags & sheet::DataResultFlags::HASDATA )
699     {
700         pDoc->SetValue( nCol, nRow, nTab, rData.Value );
701 
702         //  use number formats from source
703 
704         OSL_ENSURE( bSizesValid, "DataCell: !bSizesValid" );
705         sal_uInt32 nFormat = 0;
706         bool bApplyFormat = false;
707         if ( pColNumFmt )
708         {
709             if ( nCol >= nDataStartCol )
710             {
711                 tools::Long nIndex = nCol - nDataStartCol;
712                 if ( nIndex < nColFmtCount )
713                 {
714                     nFormat = pColNumFmt[nIndex];
715                     bApplyFormat = true;
716                 }
717             }
718         }
719         else if ( pRowNumFmt )
720         {
721             if ( nRow >= nDataStartRow )
722             {
723                 tools::Long nIndex = nRow - nDataStartRow;
724                 if ( nIndex < nRowFmtCount )
725                 {
726                     nFormat = pRowNumFmt[nIndex];
727                     bApplyFormat = true;
728                 }
729             }
730         }
731         else if ( nSingleNumFmt != 0 )
732         {
733             nFormat = nSingleNumFmt;        // single format is used everywhere
734             bApplyFormat = true;
735         }
736 
737         if (bApplyFormat)
738             pDoc->ApplyAttr(nCol, nRow, nTab, SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
739     }
740     //  SubTotal formatting is controlled by headers
741 }
742 
HeaderCell(SCCOL nCol,SCROW nRow,SCTAB nTab,const sheet::MemberResult & rData,bool bColHeader,tools::Long nLevel)743 void ScDPOutput::HeaderCell( SCCOL nCol, SCROW nRow, SCTAB nTab,
744                              const sheet::MemberResult& rData, bool bColHeader, tools::Long nLevel )
745 {
746     tools::Long nFlags = rData.Flags;
747 
748     if ( nFlags & sheet::MemberResultFlags::HASMEMBER )
749     {
750         bool bNumeric = (nFlags & sheet::MemberResultFlags::NUMERIC) != 0;
751         if (bNumeric && std::isfinite( rData.Value))
752         {
753             pDoc->SetValue( nCol, nRow, nTab, rData.Value);
754         }
755         else
756         {
757             ScSetStringParam aParam;
758             if (bNumeric)
759                 aParam.setNumericInput();
760             else
761                 aParam.setTextInput();
762 
763             pDoc->SetString(nCol, nRow, nTab, rData.Caption, &aParam);
764         }
765     }
766 
767     if ( !(nFlags & sheet::MemberResultFlags::SUBTOTAL) )
768         return;
769 
770     ScDPOutputImpl outputimp( pDoc, nTab,
771         nTabStartCol, nTabStartRow,
772         nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow );
773     //TODO: limit frames to horizontal or vertical?
774     if (bColHeader)
775     {
776         outputimp.OutputBlockFrame( nCol,nMemberStartRow+static_cast<SCROW>(nLevel), nCol,nDataStartRow-1 );
777 
778         lcl_SetStyleById( pDoc,nTab, nCol,nMemberStartRow+static_cast<SCROW>(nLevel), nCol,nDataStartRow-1,
779                                 STR_PIVOT_STYLENAME_TITLE );
780         lcl_SetStyleById( pDoc,nTab, nCol,nDataStartRow, nCol,nTabEndRow,
781                                 STR_PIVOT_STYLENAME_RESULT );
782     }
783     else
784     {
785         outputimp.OutputBlockFrame( nMemberStartCol+static_cast<SCCOL>(nLevel),nRow, nDataStartCol-1,nRow );
786         lcl_SetStyleById( pDoc,nTab, nMemberStartCol+static_cast<SCCOL>(nLevel),nRow, nDataStartCol-1,nRow,
787                                 STR_PIVOT_STYLENAME_TITLE );
788         lcl_SetStyleById( pDoc,nTab, nDataStartCol,nRow, nTabEndCol,nRow,
789                                 STR_PIVOT_STYLENAME_RESULT );
790     }
791 }
792 
FieldCell(SCCOL nCol,SCROW nRow,SCTAB nTab,const ScDPOutLevelData & rData,bool bInTable)793 void ScDPOutput::FieldCell(
794     SCCOL nCol, SCROW nRow, SCTAB nTab, const ScDPOutLevelData& rData, bool bInTable)
795 {
796     // Avoid unwanted automatic format detection.
797     ScSetStringParam aParam;
798     aParam.mbDetectNumberFormat = false;
799     aParam.meSetTextNumFormat = ScSetStringParam::Always;
800     aParam.mbHandleApostrophe = false;
801     pDoc->SetString(nCol, nRow, nTab, rData.maCaption, &aParam);
802 
803     if (bInTable)
804         lcl_SetFrame( pDoc,nTab, nCol,nRow, nCol,nRow, 20 );
805 
806     // For field button drawing
807     ScMF nMergeFlag = ScMF::NONE;
808     if (rData.mbHasHiddenMember)
809         nMergeFlag |= ScMF::HiddenMember;
810 
811     if (rData.mbPageDim)
812     {
813         nMergeFlag |= ScMF::ButtonPopup;
814         pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, ScMF::Button);
815         pDoc->ApplyFlagsTab(nCol+1, nRow, nCol+1, nRow, nTab, nMergeFlag);
816     }
817     else
818     {
819         nMergeFlag |= ScMF::Button;
820         if (!rData.mbDataLayout)
821             nMergeFlag |= ScMF::ButtonPopup;
822         pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, nMergeFlag);
823     }
824 
825     lcl_SetStyleById( pDoc,nTab, nCol,nRow, nCol,nRow, STR_PIVOT_STYLENAME_FIELDNAME );
826 }
827 
lcl_DoFilterButton(ScDocument * pDoc,SCCOL nCol,SCROW nRow,SCTAB nTab)828 static void lcl_DoFilterButton( ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
829 {
830     pDoc->SetString( nCol, nRow, nTab, ScResId(STR_CELL_FILTER) );
831     pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, ScMF::Button);
832 }
833 
CalcSizes()834 void ScDPOutput::CalcSizes()
835 {
836     if (bSizesValid)
837         return;
838 
839     //  get column size of data from first row
840     //TODO: allow different sizes (and clear following areas) ???
841 
842     nRowCount = aData.getLength();
843     const uno::Sequence<sheet::DataResult>* pRowAry = aData.getConstArray();
844     nColCount = nRowCount ? ( pRowAry[0].getLength() ) : 0;
845 
846     nHeaderSize = 1;
847     if (GetHeaderLayout() && pColFields.empty())
848         // Insert an extra header row only when there is no column field.
849         nHeaderSize = 2;
850 
851     //  calculate output positions and sizes
852 
853     tools::Long nPageSize = 0;     // use page fields!
854     if ( bDoFilter || !pPageFields.empty() )
855     {
856         nPageSize += pPageFields.size() + 1;   // plus one empty row
857         if ( bDoFilter )
858             ++nPageSize;        //  filter button above the page fields
859     }
860 
861     if ( aStartPos.Col() + static_cast<tools::Long>(pRowFields.size()) + nColCount - 1 > MAXCOL ||
862          aStartPos.Row() + nPageSize + nHeaderSize + pColFields.size() + nRowCount > MAXROW )
863     {
864         bSizeOverflow = true;
865     }
866 
867     nTabStartCol = aStartPos.Col();
868     nTabStartRow = aStartPos.Row() + static_cast<SCROW>(nPageSize);          // below page fields
869     nMemberStartCol = nTabStartCol;
870     nMemberStartRow = nTabStartRow + static_cast<SCROW>(nHeaderSize);
871     nDataStartCol = nMemberStartCol + static_cast<SCCOL>(pRowFields.size());
872     nDataStartRow = nMemberStartRow + static_cast<SCROW>(pColFields.size());
873     if ( nColCount > 0 )
874         nTabEndCol = nDataStartCol + static_cast<SCCOL>(nColCount) - 1;
875     else
876         nTabEndCol = nDataStartCol;     // single column will remain empty
877     // if page fields are involved, include the page selection cells
878     if ( !pPageFields.empty() && nTabEndCol < nTabStartCol + 1 )
879         nTabEndCol = nTabStartCol + 1;
880     if ( nRowCount > 0 )
881         nTabEndRow = nDataStartRow + static_cast<SCROW>(nRowCount) - 1;
882     else
883         nTabEndRow = nDataStartRow;     // single row will remain empty
884     bSizesValid = true;
885 }
886 
GetPositionType(const ScAddress & rPos)887 sal_Int32 ScDPOutput::GetPositionType(const ScAddress& rPos)
888 {
889     using namespace ::com::sun::star::sheet;
890 
891     SCCOL nCol = rPos.Col();
892     SCROW nRow = rPos.Row();
893     SCTAB nTab = rPos.Tab();
894     if ( nTab != aStartPos.Tab() )
895         return DataPilotTablePositionType::NOT_IN_TABLE;
896 
897     CalcSizes();
898 
899     // Make sure the cursor is within the table.
900     if (nCol < nTabStartCol || nRow < nTabStartRow || nCol > nTabEndCol || nRow > nTabEndRow)
901         return DataPilotTablePositionType::NOT_IN_TABLE;
902 
903     // test for result data area.
904     if (nCol >= nDataStartCol && nCol <= nTabEndCol && nRow >= nDataStartRow && nRow <= nTabEndRow)
905         return DataPilotTablePositionType::RESULT;
906 
907     bool bInColHeader = (nRow >= nTabStartRow && nRow < nDataStartRow);
908     bool bInRowHeader = (nCol >= nTabStartCol && nCol < nDataStartCol);
909 
910     if (bInColHeader && bInRowHeader)
911         // probably in that ugly little box at the upper-left corner of the table.
912         return DataPilotTablePositionType::OTHER;
913 
914     if (bInColHeader)
915     {
916         if (nRow == nTabStartRow)
917             // first row in the column header area is always used for column
918             // field buttons.
919             return DataPilotTablePositionType::OTHER;
920 
921         return DataPilotTablePositionType::COLUMN_HEADER;
922     }
923 
924     if (bInRowHeader)
925         return DataPilotTablePositionType::ROW_HEADER;
926 
927     return DataPilotTablePositionType::OTHER;
928 }
929 
Output()930 void ScDPOutput::Output()
931 {
932     SCTAB nTab = aStartPos.Tab();
933     const uno::Sequence<sheet::DataResult>* pRowAry = aData.getConstArray();
934 
935     //  calculate output positions and sizes
936 
937     CalcSizes();
938     if ( bSizeOverflow || bResultsError )   // does output area exceed sheet limits?
939         return;                             // nothing
940 
941     //  clear whole (new) output area
942     // when modifying table, clear old area !
943     //TODO: include InsertDeleteFlags::OBJECTS ???
944     pDoc->DeleteAreaTab( aStartPos.Col(), aStartPos.Row(), nTabEndCol, nTabEndRow, nTab, InsertDeleteFlags::ALL );
945 
946     if ( bDoFilter )
947         lcl_DoFilterButton( pDoc, aStartPos.Col(), aStartPos.Row(), nTab );
948 
949     //  output page fields:
950 
951     for (size_t nField=0; nField<pPageFields.size(); ++nField)
952     {
953         SCCOL nHdrCol = aStartPos.Col();
954         SCROW nHdrRow = aStartPos.Row() + nField + ( bDoFilter ? 1 : 0 );
955         // draw without frame for consistency with filter button:
956         FieldCell(nHdrCol, nHdrRow, nTab, pPageFields[nField], false);
957         SCCOL nFldCol = nHdrCol + 1;
958 
959         OUString aPageValue = ScResId(SCSTR_ALL);
960         const uno::Sequence<sheet::MemberResult>& rRes = pPageFields[nField].maResult;
961         sal_Int32 n = rRes.getLength();
962         if (n == 1)
963         {
964             if (rRes[0].Caption.isEmpty())
965                 aPageValue = ScResId(STR_EMPTYDATA);
966             else
967                 aPageValue = rRes[0].Caption;
968         }
969         else if (n > 1)
970             aPageValue = ScResId(SCSTR_MULTIPLE);
971 
972         ScSetStringParam aParam;
973         aParam.setTextInput();
974         pDoc->SetString(nFldCol, nHdrRow, nTab, aPageValue, &aParam);
975 
976         lcl_SetFrame( pDoc,nTab, nFldCol,nHdrRow, nFldCol,nHdrRow, 20 );
977     }
978 
979     //  data description
980     //  (may get overwritten by first row field)
981 
982     if (aDataDescription.isEmpty())
983     {
984         //TODO: use default string ("result") ?
985     }
986     pDoc->SetString(nTabStartCol, nTabStartRow, nTab, aDataDescription);
987 
988     //  set STR_PIVOT_STYLENAME_INNER for whole data area (subtotals are overwritten)
989 
990     if ( nDataStartRow > nTabStartRow )
991         lcl_SetStyleById( pDoc, nTab, nTabStartCol, nTabStartRow, nTabEndCol, nDataStartRow-1,
992                             STR_PIVOT_STYLENAME_TOP );
993     lcl_SetStyleById( pDoc, nTab, nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow,
994                         STR_PIVOT_STYLENAME_INNER  );
995 
996     //  output column headers:
997     ScDPOutputImpl outputimp( pDoc, nTab,
998         nTabStartCol, nTabStartRow,
999         nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow );
1000     for (size_t nField=0; nField<pColFields.size(); nField++)
1001     {
1002         SCCOL nHdrCol = nDataStartCol + static_cast<SCCOL>(nField);              //TODO: check for overflow
1003         FieldCell(nHdrCol, nTabStartRow, nTab, pColFields[nField], true);
1004 
1005         SCROW nRowPos = nMemberStartRow + static_cast<SCROW>(nField);                //TODO: check for overflow
1006         const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nField].maResult;
1007         const sheet::MemberResult* pArray = rSequence.getConstArray();
1008         tools::Long nThisColCount = rSequence.getLength();
1009         OSL_ENSURE( nThisColCount == nColCount, "count mismatch" );     //TODO: ???
1010         for (tools::Long nCol=0; nCol<nThisColCount; nCol++)
1011         {
1012             SCCOL nColPos = nDataStartCol + static_cast<SCCOL>(nCol);                //TODO: check for overflow
1013             HeaderCell( nColPos, nRowPos, nTab, pArray[nCol], true, nField );
1014             if ( ( pArray[nCol].Flags & sheet::MemberResultFlags::HASMEMBER ) &&
1015                 !( pArray[nCol].Flags & sheet::MemberResultFlags::SUBTOTAL ) )
1016             {
1017                 tools::Long nEnd = nCol;
1018                 while ( nEnd+1 < nThisColCount && ( pArray[nEnd+1].Flags & sheet::MemberResultFlags::CONTINUE ) )
1019                     ++nEnd;
1020                 SCCOL nEndColPos = nDataStartCol + static_cast<SCCOL>(nEnd);     //TODO: check for overflow
1021                 if ( nField+1 < pColFields.size())
1022                 {
1023                     if ( nField == pColFields.size() - 2 )
1024                     {
1025                         outputimp.AddCol( nColPos );
1026                         if ( nColPos + 1 == nEndColPos  )
1027                             outputimp.OutputBlockFrame( nColPos,nRowPos, nEndColPos,nRowPos+1, true );
1028                     }
1029                     else
1030                         outputimp.OutputBlockFrame( nColPos,nRowPos, nEndColPos,nRowPos );
1031 
1032                     lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nEndColPos,nDataStartRow-1, STR_PIVOT_STYLENAME_CATEGORY );
1033                 }
1034                 else
1035                     lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nColPos,nDataStartRow-1, STR_PIVOT_STYLENAME_CATEGORY );
1036             }
1037             else if (  pArray[nCol].Flags & sheet::MemberResultFlags::SUBTOTAL )
1038                 outputimp.AddCol( nColPos );
1039 
1040             // Apply the same number format as in data source.
1041             pDoc->ApplyAttr(nColPos, nRowPos, nTab, SfxUInt32Item(ATTR_VALUE_FORMAT, pColFields[nField].mnSrcNumFmt));
1042         }
1043         if ( nField== 0 && pColFields.size() == 1 )
1044             outputimp.OutputBlockFrame( nDataStartCol,nTabStartRow, nTabEndCol,nRowPos-1 );
1045     }
1046 
1047     //  output row headers:
1048     std::vector<bool> vbSetBorder;
1049     vbSetBorder.resize( nTabEndRow - nDataStartRow + 1, false );
1050     for (size_t nField=0; nField<pRowFields.size(); nField++)
1051     {
1052         SCCOL nHdrCol = nTabStartCol + static_cast<SCCOL>(nField);                   //TODO: check for overflow
1053         SCROW nHdrRow = nDataStartRow - 1;
1054         FieldCell(nHdrCol, nHdrRow, nTab, pRowFields[nField], true);
1055 
1056         SCCOL nColPos = nMemberStartCol + static_cast<SCCOL>(nField);                //TODO: check for overflow
1057         const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].maResult;
1058         const sheet::MemberResult* pArray = rSequence.getConstArray();
1059         sal_Int32 nThisRowCount = rSequence.getLength();
1060         OSL_ENSURE( nThisRowCount == nRowCount, "count mismatch" );     //TODO: ???
1061         for (sal_Int32 nRow=0; nRow<nThisRowCount; nRow++)
1062         {
1063             SCROW nRowPos = nDataStartRow + static_cast<SCROW>(nRow);                //TODO: check for overflow
1064             HeaderCell( nColPos, nRowPos, nTab, pArray[nRow], false, nField );
1065             if ( ( pArray[nRow].Flags & sheet::MemberResultFlags::HASMEMBER ) &&
1066                 !( pArray[nRow].Flags & sheet::MemberResultFlags::SUBTOTAL ) )
1067             {
1068                 if ( nField+1 < pRowFields.size() )
1069                 {
1070                     tools::Long nEnd = nRow;
1071                     while ( nEnd+1 < nThisRowCount && ( pArray[nEnd+1].Flags & sheet::MemberResultFlags::CONTINUE ) )
1072                         ++nEnd;
1073                     SCROW nEndRowPos = nDataStartRow + static_cast<SCROW>(nEnd);     //TODO: check for overflow
1074                     outputimp.AddRow( nRowPos );
1075                     if ( !vbSetBorder[ nRow ] )
1076                     {
1077                         outputimp.OutputBlockFrame( nColPos, nRowPos, nTabEndCol, nEndRowPos );
1078                         vbSetBorder[ nRow ]  = true;
1079                     }
1080                     outputimp.OutputBlockFrame( nColPos, nRowPos, nColPos, nEndRowPos );
1081 
1082                     if ( nField == pRowFields.size() - 2 )
1083                         outputimp.OutputBlockFrame( nColPos+1, nRowPos, nColPos+1, nEndRowPos );
1084 
1085                     lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nDataStartCol-1,nEndRowPos, STR_PIVOT_STYLENAME_CATEGORY );
1086                 }
1087                 else
1088                     lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nDataStartCol-1,nRowPos, STR_PIVOT_STYLENAME_CATEGORY );
1089             }
1090             else if (  pArray[nRow].Flags & sheet::MemberResultFlags::SUBTOTAL )
1091                 outputimp.AddRow( nRowPos );
1092 
1093             // Apply the same number format as in data source.
1094             pDoc->ApplyAttr(nColPos, nRowPos, nTab, SfxUInt32Item(ATTR_VALUE_FORMAT, pRowFields[nField].mnSrcNumFmt));
1095         }
1096     }
1097 
1098     if (nColCount == 1 && nRowCount > 0 && pColFields.empty())
1099     {
1100         // the table contains exactly one data field and no column fields.
1101         // Display data description at top right corner.
1102         ScSetStringParam aParam;
1103         aParam.setTextInput();
1104         pDoc->SetString(nDataStartCol, nDataStartRow-1, nTab, aDataDescription, &aParam);
1105     }
1106 
1107     //  output data results:
1108 
1109     for (sal_Int32 nRow=0; nRow<nRowCount; nRow++)
1110     {
1111         SCROW nRowPos = nDataStartRow + static_cast<SCROW>(nRow);                    //TODO: check for overflow
1112         const sheet::DataResult* pColAry = pRowAry[nRow].getConstArray();
1113         sal_Int32 nThisColCount = pRowAry[nRow].getLength();
1114         OSL_ENSURE( nThisColCount == nColCount, "count mismatch" );     //TODO: ???
1115         for (sal_Int32 nCol=0; nCol<nThisColCount; nCol++)
1116         {
1117             SCCOL nColPos = nDataStartCol + static_cast<SCCOL>(nCol);                //TODO: check for overflow
1118             DataCell( nColPos, nRowPos, nTab, pColAry[nCol] );
1119         }
1120     }
1121 
1122     outputimp.OutputDataArea();
1123 }
1124 
GetOutputRange(sal_Int32 nRegionType)1125 ScRange ScDPOutput::GetOutputRange( sal_Int32 nRegionType )
1126 {
1127     using namespace ::com::sun::star::sheet;
1128 
1129     CalcSizes();
1130 
1131     SCTAB nTab = aStartPos.Tab();
1132     switch (nRegionType)
1133     {
1134         case DataPilotOutputRangeType::RESULT:
1135             return ScRange(nDataStartCol, nDataStartRow, nTab, nTabEndCol, nTabEndRow, nTab);
1136         case DataPilotOutputRangeType::TABLE:
1137             return ScRange(aStartPos.Col(), nTabStartRow, nTab, nTabEndCol, nTabEndRow, nTab);
1138         default:
1139             OSL_ENSURE(nRegionType == DataPilotOutputRangeType::WHOLE, "ScDPOutput::GetOutputRange: unknown region type");
1140         break;
1141     }
1142     return ScRange(aStartPos.Col(), aStartPos.Row(), nTab, nTabEndCol, nTabEndRow, nTab);
1143 }
1144 
HasError()1145 bool ScDPOutput::HasError()
1146 {
1147     CalcSizes();
1148 
1149     return bSizeOverflow || bResultsError;
1150 }
1151 
GetHeaderRows() const1152 sal_Int32 ScDPOutput::GetHeaderRows() const
1153 {
1154     return pPageFields.size() + ( bDoFilter ? 1 : 0 );
1155 }
1156 
1157 namespace
1158 {
insertNames(ScDPUniqueStringSet & rNames,const uno::Sequence<sheet::MemberResult> & rMemberResults)1159     void insertNames(ScDPUniqueStringSet& rNames, const uno::Sequence<sheet::MemberResult>& rMemberResults)
1160     {
1161         for (const sheet::MemberResult& rMemberResult : rMemberResults)
1162         {
1163             if (rMemberResult.Flags & sheet::MemberResultFlags::HASMEMBER)
1164                 rNames.insert(rMemberResult.Name);
1165         }
1166     }
1167 }
1168 
GetMemberResultNames(ScDPUniqueStringSet & rNames,tools::Long nDimension)1169 void ScDPOutput::GetMemberResultNames(ScDPUniqueStringSet& rNames, tools::Long nDimension)
1170 {
1171     //  Return the list of all member names in a dimension's MemberResults.
1172     //  Only the dimension has to be compared because this is only used with table data,
1173     //  where each dimension occurs only once.
1174 
1175     auto lFindDimension = [nDimension](const ScDPOutLevelData& rField) { return rField.mnDim == nDimension; };
1176 
1177     // look in column fields
1178     auto colit = std::find_if(pColFields.begin(), pColFields.end(), lFindDimension);
1179     if (colit != pColFields.end())
1180     {
1181         // collect the member names
1182         insertNames(rNames, colit->maResult);
1183         return;
1184     }
1185 
1186     // look in row fields
1187     auto rowit = std::find_if(pRowFields.begin(), pRowFields.end(), lFindDimension);
1188     if (rowit != pRowFields.end())
1189     {
1190         // collect the member names
1191         insertNames(rNames, rowit->maResult);
1192     }
1193 }
1194 
SetHeaderLayout(bool bUseGrid)1195 void ScDPOutput::SetHeaderLayout(bool bUseGrid)
1196 {
1197     mbHeaderLayout = bUseGrid;
1198     bSizesValid = false;
1199 }
1200 
1201 namespace {
1202 
lcl_GetTableVars(sal_Int32 & rGrandTotalCols,sal_Int32 & rGrandTotalRows,sal_Int32 & rDataLayoutIndex,std::vector<OUString> & rDataNames,std::vector<OUString> & rGivenNames,sheet::DataPilotFieldOrientation & rDataOrient,const uno::Reference<sheet::XDimensionsSupplier> & xSource)1203 void lcl_GetTableVars( sal_Int32& rGrandTotalCols, sal_Int32& rGrandTotalRows, sal_Int32& rDataLayoutIndex,
1204                        std::vector<OUString>& rDataNames, std::vector<OUString>& rGivenNames,
1205                        sheet::DataPilotFieldOrientation& rDataOrient,
1206                        const uno::Reference<sheet::XDimensionsSupplier>& xSource )
1207 {
1208     rDataLayoutIndex = -1;  // invalid
1209     rGrandTotalCols = 0;
1210     rGrandTotalRows = 0;
1211     rDataOrient = sheet::DataPilotFieldOrientation_HIDDEN;
1212 
1213     uno::Reference<beans::XPropertySet> xSrcProp( xSource, uno::UNO_QUERY );
1214     bool bColGrand = ScUnoHelpFunctions::GetBoolProperty(
1215         xSrcProp, SC_UNO_DP_COLGRAND);
1216     if ( bColGrand )
1217         rGrandTotalCols = 1;    // default if data layout not in columns
1218 
1219     bool bRowGrand = ScUnoHelpFunctions::GetBoolProperty(
1220         xSrcProp, SC_UNO_DP_ROWGRAND);
1221     if ( bRowGrand )
1222         rGrandTotalRows = 1;    // default if data layout not in rows
1223 
1224     if ( !xSource.is() )
1225         return;
1226 
1227     // find index and orientation of "data layout" dimension, count data dimensions
1228 
1229     sal_Int32 nDataCount = 0;
1230 
1231     uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xSource->getDimensions() );
1232     tools::Long nDimCount = xDims->getCount();
1233     for (tools::Long nDim=0; nDim<nDimCount; nDim++)
1234     {
1235         uno::Reference<uno::XInterface> xDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
1236         uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
1237         if ( xDimProp.is() )
1238         {
1239             sheet::DataPilotFieldOrientation eDimOrient =
1240                 ScUnoHelpFunctions::GetEnumProperty(
1241                     xDimProp, SC_UNO_DP_ORIENTATION,
1242                     sheet::DataPilotFieldOrientation_HIDDEN );
1243             if ( ScUnoHelpFunctions::GetBoolProperty( xDimProp,
1244                                      SC_UNO_DP_ISDATALAYOUT ) )
1245             {
1246                 rDataLayoutIndex = nDim;
1247                 rDataOrient = eDimOrient;
1248             }
1249             if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
1250             {
1251                 OUString aSourceName;
1252                 OUString aGivenName;
1253                 ScDPOutput::GetDataDimensionNames( aSourceName, aGivenName, xDim );
1254                 try
1255                 {
1256                     uno::Any aValue = xDimProp->getPropertyValue( SC_UNO_DP_LAYOUTNAME );
1257 
1258                     if( aValue.hasValue() )
1259                     {
1260                         OUString strLayoutName;
1261 
1262                         if( ( aValue >>= strLayoutName ) && !strLayoutName.isEmpty() )
1263                             aGivenName = strLayoutName;
1264                     }
1265                 }
1266                 catch(const uno::Exception&)
1267                 {
1268                 }
1269                 rDataNames.push_back( aSourceName );
1270                 rGivenNames.push_back( aGivenName );
1271 
1272                 ++nDataCount;
1273             }
1274         }
1275     }
1276 
1277     if ( ( rDataOrient == sheet::DataPilotFieldOrientation_COLUMN ) && bColGrand )
1278         rGrandTotalCols = nDataCount;
1279     else if ( ( rDataOrient == sheet::DataPilotFieldOrientation_ROW ) && bRowGrand )
1280         rGrandTotalRows = nDataCount;
1281 }
1282 
1283 }
1284 
GetPositionData(const ScAddress & rPos,DataPilotTablePositionData & rPosData)1285 void ScDPOutput::GetPositionData(const ScAddress& rPos, DataPilotTablePositionData& rPosData)
1286 {
1287     using namespace ::com::sun::star::sheet;
1288 
1289     SCCOL nCol = rPos.Col();
1290     SCROW nRow = rPos.Row();
1291     SCTAB nTab = rPos.Tab();
1292     if ( nTab != aStartPos.Tab() )
1293         return;                                     // wrong sheet
1294 
1295     //  calculate output positions and sizes
1296 
1297     CalcSizes();
1298 
1299     rPosData.PositionType = GetPositionType(rPos);
1300     switch (rPosData.PositionType)
1301     {
1302         case DataPilotTablePositionType::RESULT:
1303         {
1304             vector<DataPilotFieldFilter> aFilters;
1305             GetDataResultPositionData(aFilters, rPos);
1306             sal_Int32 nSize = aFilters.size();
1307 
1308             DataPilotTableResultData aResData;
1309             aResData.FieldFilters.realloc(nSize);
1310             for (sal_Int32 i = 0; i < nSize; ++i)
1311                 aResData.FieldFilters[i] = aFilters[i];
1312 
1313             aResData.DataFieldIndex = 0;
1314             Reference<beans::XPropertySet> xPropSet(xSource, UNO_QUERY);
1315             if (xPropSet.is())
1316             {
1317                 sal_Int32 nDataFieldCount = ScUnoHelpFunctions::GetLongProperty( xPropSet,
1318                                             SC_UNO_DP_DATAFIELDCOUNT );
1319                 if (nDataFieldCount > 0)
1320                     aResData.DataFieldIndex = (nRow - nDataStartRow) % nDataFieldCount;
1321             }
1322 
1323             // Copy appropriate DataResult object from the cached sheet::DataResult table.
1324             if (aData.getLength() > nRow - nDataStartRow &&
1325                 aData[nRow-nDataStartRow].getLength() > nCol-nDataStartCol)
1326                 aResData.Result = aData[nRow-nDataStartRow][nCol-nDataStartCol];
1327 
1328             rPosData.PositionData <<= aResData;
1329             return;
1330         }
1331         case DataPilotTablePositionType::COLUMN_HEADER:
1332         {
1333             tools::Long nField = nRow - nTabStartRow - 1; // 1st line is used for the buttons
1334             if (nField < 0)
1335                 break;
1336 
1337             const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nField].maResult;
1338             if (!rSequence.hasElements())
1339                 break;
1340             const sheet::MemberResult* pArray = rSequence.getConstArray();
1341 
1342             tools::Long nItem = nCol - nDataStartCol;
1343             //  get origin of "continue" fields
1344             while (nItem > 0 && ( pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1345                 --nItem;
1346 
1347             if (nItem < 0)
1348                 break;
1349 
1350             DataPilotTableHeaderData aHeaderData;
1351             aHeaderData.MemberName = pArray[nItem].Name;
1352             aHeaderData.Flags = pArray[nItem].Flags;
1353             aHeaderData.Dimension = static_cast<sal_Int32>(pColFields[nField].mnDim);
1354             aHeaderData.Hierarchy = static_cast<sal_Int32>(pColFields[nField].mnHier);
1355             aHeaderData.Level     = static_cast<sal_Int32>(pColFields[nField].mnLevel);
1356 
1357             rPosData.PositionData <<= aHeaderData;
1358             return;
1359         }
1360         case DataPilotTablePositionType::ROW_HEADER:
1361         {
1362             tools::Long nField = nCol - nTabStartCol;
1363             if (nField < 0)
1364                 break;
1365 
1366             const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].maResult;
1367             if (!rSequence.hasElements())
1368                 break;
1369             const sheet::MemberResult* pArray = rSequence.getConstArray();
1370 
1371             tools::Long nItem = nRow - nDataStartRow;
1372             //  get origin of "continue" fields
1373             while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1374                 --nItem;
1375 
1376             if (nItem < 0)
1377                 break;
1378 
1379             DataPilotTableHeaderData aHeaderData;
1380             aHeaderData.MemberName = pArray[nItem].Name;
1381             aHeaderData.Flags = pArray[nItem].Flags;
1382             aHeaderData.Dimension = static_cast<sal_Int32>(pRowFields[nField].mnDim);
1383             aHeaderData.Hierarchy = static_cast<sal_Int32>(pRowFields[nField].mnHier);
1384             aHeaderData.Level     = static_cast<sal_Int32>(pRowFields[nField].mnLevel);
1385 
1386             rPosData.PositionData <<= aHeaderData;
1387             return;
1388         }
1389     }
1390 }
1391 
GetDataResultPositionData(vector<sheet::DataPilotFieldFilter> & rFilters,const ScAddress & rPos)1392 bool ScDPOutput::GetDataResultPositionData(vector<sheet::DataPilotFieldFilter>& rFilters, const ScAddress& rPos)
1393 {
1394     // Check to make sure there is at least one data field.
1395     Reference<beans::XPropertySet> xPropSet(xSource, UNO_QUERY);
1396     if (!xPropSet.is())
1397         return false;
1398 
1399     sal_Int32 nDataFieldCount = ScUnoHelpFunctions::GetLongProperty( xPropSet,
1400                                 SC_UNO_DP_DATAFIELDCOUNT );
1401     if (nDataFieldCount == 0)
1402         // No data field is present in this datapilot table.
1403         return false;
1404 
1405     // #i111421# use lcl_GetTableVars for correct size of totals and data layout position
1406     sal_Int32 nGrandTotalCols;
1407     sal_Int32 nGrandTotalRows;
1408     sal_Int32 nDataLayoutIndex;
1409     std::vector<OUString> aDataNames;
1410     std::vector<OUString> aGivenNames;
1411     sheet::DataPilotFieldOrientation eDataOrient;
1412     lcl_GetTableVars( nGrandTotalCols, nGrandTotalRows, nDataLayoutIndex, aDataNames, aGivenNames, eDataOrient, xSource );
1413 
1414     SCCOL nCol = rPos.Col();
1415     SCROW nRow = rPos.Row();
1416     SCTAB nTab = rPos.Tab();
1417     if ( nTab != aStartPos.Tab() )
1418         return false;                                     // wrong sheet
1419 
1420     CalcSizes();
1421 
1422     // test for data area.
1423     if (nCol < nDataStartCol || nCol > nTabEndCol || nRow < nDataStartRow || nRow > nTabEndRow)
1424     {
1425         // Cell is outside the data field area.
1426         return false;
1427     }
1428 
1429     bool bFilterByCol = (nCol <= static_cast<SCCOL>(nTabEndCol - nGrandTotalCols));
1430     bool bFilterByRow = (nRow <= static_cast<SCROW>(nTabEndRow - nGrandTotalRows));
1431 
1432     // column fields
1433     for (size_t nColField = 0; nColField < pColFields.size() && bFilterByCol; ++nColField)
1434     {
1435         if (pColFields[nColField].mnDim == nDataLayoutIndex)
1436             // There is no sense including the data layout field for filtering.
1437             continue;
1438 
1439         sheet::DataPilotFieldFilter filter;
1440         filter.FieldName = pColFields[nColField].maName;
1441 
1442         const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nColField].maResult;
1443         const sheet::MemberResult* pArray = rSequence.getConstArray();
1444 
1445         OSL_ENSURE(nDataStartCol + rSequence.getLength() - 1 == nTabEndCol, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
1446 
1447         tools::Long nItem = nCol - nDataStartCol;
1448                 //  get origin of "continue" fields
1449         while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1450             --nItem;
1451 
1452         filter.MatchValueName = pArray[nItem].Name;
1453         rFilters.push_back(filter);
1454     }
1455 
1456     // row fields
1457     for (size_t nRowField = 0; nRowField < pRowFields.size() && bFilterByRow; ++nRowField)
1458     {
1459         if (pRowFields[nRowField].mnDim == nDataLayoutIndex)
1460             // There is no sense including the data layout field for filtering.
1461             continue;
1462 
1463         sheet::DataPilotFieldFilter filter;
1464         filter.FieldName = pRowFields[nRowField].maName;
1465 
1466         const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nRowField].maResult;
1467         const sheet::MemberResult* pArray = rSequence.getConstArray();
1468 
1469         OSL_ENSURE(nDataStartRow + rSequence.getLength() - 1 == nTabEndRow, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
1470 
1471         tools::Long nItem = nRow - nDataStartRow;
1472             //  get origin of "continue" fields
1473         while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1474             --nItem;
1475 
1476         filter.MatchValueName = pArray[nItem].Name;
1477         rFilters.push_back(filter);
1478     }
1479 
1480     return true;
1481 }
1482 
1483 namespace {
1484 
lcl_GetDataFieldName(std::u16string_view rSourceName,sal_Int16 eFunc)1485 OUString lcl_GetDataFieldName( std::u16string_view rSourceName, sal_Int16 eFunc )
1486 {
1487     const char* pStrId = nullptr;
1488     switch ( eFunc )
1489     {
1490         case sheet::GeneralFunction2::SUM:        pStrId = STR_FUN_TEXT_SUM;      break;
1491         case sheet::GeneralFunction2::COUNT:
1492         case sheet::GeneralFunction2::COUNTNUMS:  pStrId = STR_FUN_TEXT_COUNT;    break;
1493         case sheet::GeneralFunction2::AVERAGE:    pStrId = STR_FUN_TEXT_AVG;      break;
1494         case sheet::GeneralFunction2::MEDIAN:     pStrId = STR_FUN_TEXT_MEDIAN;   break;
1495         case sheet::GeneralFunction2::MAX:        pStrId = STR_FUN_TEXT_MAX;      break;
1496         case sheet::GeneralFunction2::MIN:        pStrId = STR_FUN_TEXT_MIN;      break;
1497         case sheet::GeneralFunction2::PRODUCT:    pStrId = STR_FUN_TEXT_PRODUCT;  break;
1498         case sheet::GeneralFunction2::STDEV:
1499         case sheet::GeneralFunction2::STDEVP:     pStrId = STR_FUN_TEXT_STDDEV;   break;
1500         case sheet::GeneralFunction2::VAR:
1501         case sheet::GeneralFunction2::VARP:       pStrId = STR_FUN_TEXT_VAR;      break;
1502         case sheet::GeneralFunction2::NONE:
1503         case sheet::GeneralFunction2::AUTO:                                       break;
1504         default:
1505         {
1506             assert(false);
1507         }
1508     }
1509     if (!pStrId)
1510         return OUString();
1511 
1512     return ScResId(pStrId) + " - " + rSourceName;
1513 }
1514 
1515 }
1516 
GetDataDimensionNames(OUString & rSourceName,OUString & rGivenName,const uno::Reference<uno::XInterface> & xDim)1517 void ScDPOutput::GetDataDimensionNames(
1518     OUString& rSourceName, OUString& rGivenName, const uno::Reference<uno::XInterface>& xDim )
1519 {
1520     uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
1521     uno::Reference<container::XNamed> xDimName( xDim, uno::UNO_QUERY );
1522     if ( !(xDimProp.is() && xDimName.is()) )
1523         return;
1524 
1525     // Asterisks are added in ScDPSaveData::WriteToSource to create unique names.
1526     //TODO: preserve original name there?
1527     rSourceName = ScDPUtil::getSourceDimensionName(xDimName->getName());
1528 
1529     // Generate "given name" the same way as in dptabres.
1530     //TODO: Should use a stored name when available
1531 
1532     sal_Int16 eFunc = ScUnoHelpFunctions::GetShortProperty(
1533                       xDimProp, SC_UNO_DP_FUNCTION2,
1534                       sheet::GeneralFunction2::NONE );
1535     rGivenName = lcl_GetDataFieldName( rSourceName, eFunc );
1536 }
1537 
IsFilterButton(const ScAddress & rPos)1538 bool ScDPOutput::IsFilterButton( const ScAddress& rPos )
1539 {
1540     SCCOL nCol = rPos.Col();
1541     SCROW nRow = rPos.Row();
1542     SCTAB nTab = rPos.Tab();
1543     if ( nTab != aStartPos.Tab() || !bDoFilter )
1544         return false;                               // wrong sheet or no button at all
1545 
1546     //  filter button is at top left
1547     return ( nCol == aStartPos.Col() && nRow == aStartPos.Row() );
1548 }
1549 
GetHeaderDim(const ScAddress & rPos,sheet::DataPilotFieldOrientation & rOrient)1550 tools::Long ScDPOutput::GetHeaderDim( const ScAddress& rPos, sheet::DataPilotFieldOrientation& rOrient )
1551 {
1552     SCCOL nCol = rPos.Col();
1553     SCROW nRow = rPos.Row();
1554     SCTAB nTab = rPos.Tab();
1555     if ( nTab != aStartPos.Tab() )
1556         return -1;                                      // wrong sheet
1557 
1558     //  calculate output positions and sizes
1559 
1560     CalcSizes();
1561 
1562     //  test for column header
1563 
1564     if ( nRow == nTabStartRow && nCol >= nDataStartCol && o3tl::make_unsigned(nCol) < nDataStartCol + pColFields.size())
1565     {
1566         rOrient = sheet::DataPilotFieldOrientation_COLUMN;
1567         tools::Long nField = nCol - nDataStartCol;
1568         return pColFields[nField].mnDim;
1569     }
1570 
1571     //  test for row header
1572 
1573     if ( nRow+1 == nDataStartRow && nCol >= nTabStartCol && o3tl::make_unsigned(nCol) < nTabStartCol + pRowFields.size() )
1574     {
1575         rOrient = sheet::DataPilotFieldOrientation_ROW;
1576         tools::Long nField = nCol - nTabStartCol;
1577         return pRowFields[nField].mnDim;
1578     }
1579 
1580     //  test for page field
1581 
1582     SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 );
1583     if ( nCol == aStartPos.Col() && nRow >= nPageStartRow && o3tl::make_unsigned(nRow) < nPageStartRow + pPageFields.size() )
1584     {
1585         rOrient = sheet::DataPilotFieldOrientation_PAGE;
1586         tools::Long nField = nRow - nPageStartRow;
1587         return pPageFields[nField].mnDim;
1588     }
1589 
1590     //TODO: single data field (?)
1591 
1592     rOrient = sheet::DataPilotFieldOrientation_HIDDEN;
1593     return -1;      // invalid
1594 }
1595 
GetHeaderDrag(const ScAddress & rPos,bool bMouseLeft,bool bMouseTop,tools::Long nDragDim,tools::Rectangle & rPosRect,sheet::DataPilotFieldOrientation & rOrient,tools::Long & rDimPos)1596 bool ScDPOutput::GetHeaderDrag( const ScAddress& rPos, bool bMouseLeft, bool bMouseTop,
1597                                 tools::Long nDragDim,
1598                                 tools::Rectangle& rPosRect, sheet::DataPilotFieldOrientation& rOrient, tools::Long& rDimPos )
1599 {
1600     //  Rectangle instead of ScRange for rPosRect to allow for negative values
1601 
1602     SCCOL nCol = rPos.Col();
1603     SCROW nRow = rPos.Row();
1604     SCTAB nTab = rPos.Tab();
1605     if ( nTab != aStartPos.Tab() )
1606         return false;                                       // wrong sheet
1607 
1608     //  calculate output positions and sizes
1609 
1610     CalcSizes();
1611 
1612     //  test for column header
1613 
1614     if ( nCol >= nDataStartCol && nCol <= nTabEndCol &&
1615             nRow + 1 >= nMemberStartRow && o3tl::make_unsigned(nRow) < nMemberStartRow + pColFields.size())
1616     {
1617         tools::Long nField = nRow - nMemberStartRow;
1618         if (nField < 0)
1619         {
1620             nField = 0;
1621             bMouseTop = true;
1622         }
1623         //TODO: find start of dimension
1624 
1625         rPosRect = tools::Rectangle( nDataStartCol, nMemberStartRow + nField,
1626                               nTabEndCol, nMemberStartRow + nField -1 );
1627 
1628         bool bFound = false;            // is this within the same orientation?
1629         bool bBeforeDrag = false;
1630         bool bAfterDrag = false;
1631         for (tools::Long nPos=0; o3tl::make_unsigned(nPos)<pColFields.size() && !bFound; nPos++)
1632         {
1633             if (pColFields[nPos].mnDim == nDragDim)
1634             {
1635                 bFound = true;
1636                 if ( nField < nPos )
1637                     bBeforeDrag = true;
1638                 else if ( nField > nPos )
1639                     bAfterDrag = true;
1640             }
1641         }
1642 
1643         if ( bFound )
1644         {
1645             if (!bBeforeDrag)
1646             {
1647                 rPosRect.AdjustBottom( 1 );
1648                 if (bAfterDrag)
1649                     rPosRect.AdjustTop( 1 );
1650             }
1651         }
1652         else
1653         {
1654             if ( !bMouseTop )
1655             {
1656                 rPosRect.AdjustTop( 1 );
1657                 rPosRect.AdjustBottom( 1 );
1658                 ++nField;
1659             }
1660         }
1661 
1662         rOrient = sheet::DataPilotFieldOrientation_COLUMN;
1663         rDimPos = nField;                       //!...
1664         return true;
1665     }
1666 
1667     //  test for row header
1668 
1669     //  special case if no row fields
1670     bool bSpecial = ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow &&
1671                         pRowFields.empty() && nCol == nTabStartCol && bMouseLeft );
1672 
1673     if ( bSpecial || ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow &&
1674                         nCol + 1 >= nTabStartCol && o3tl::make_unsigned(nCol) < nTabStartCol + pRowFields.size() ) )
1675     {
1676         tools::Long nField = nCol - nTabStartCol;
1677         //TODO: find start of dimension
1678 
1679         rPosRect = tools::Rectangle( nTabStartCol + nField, nDataStartRow - 1,
1680                               nTabStartCol + nField - 1, nTabEndRow );
1681 
1682         bool bFound = false;            // is this within the same orientation?
1683         bool bBeforeDrag = false;
1684         bool bAfterDrag = false;
1685         for (tools::Long nPos=0; o3tl::make_unsigned(nPos)<pRowFields.size() && !bFound; nPos++)
1686         {
1687             if (pRowFields[nPos].mnDim == nDragDim)
1688             {
1689                 bFound = true;
1690                 if ( nField < nPos )
1691                     bBeforeDrag = true;
1692                 else if ( nField > nPos )
1693                     bAfterDrag = true;
1694             }
1695         }
1696 
1697         if ( bFound )
1698         {
1699             if (!bBeforeDrag)
1700             {
1701                 rPosRect.AdjustRight( 1 );
1702                 if (bAfterDrag)
1703                     rPosRect.AdjustLeft( 1 );
1704             }
1705         }
1706         else
1707         {
1708             if ( !bMouseLeft )
1709             {
1710                 rPosRect.AdjustLeft( 1 );
1711                 rPosRect.AdjustRight( 1 );
1712                 ++nField;
1713             }
1714         }
1715 
1716         rOrient = sheet::DataPilotFieldOrientation_ROW;
1717         rDimPos = nField;                       //!...
1718         return true;
1719     }
1720 
1721     //  test for page fields
1722 
1723     SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 );
1724     if ( nCol >= aStartPos.Col() && nCol <= nTabEndCol &&
1725             nRow + 1 >= nPageStartRow && o3tl::make_unsigned(nRow) < nPageStartRow + pPageFields.size() )
1726     {
1727         tools::Long nField = nRow - nPageStartRow;
1728         if (nField < 0)
1729         {
1730             nField = 0;
1731             bMouseTop = true;
1732         }
1733         //TODO: find start of dimension
1734 
1735         rPosRect = tools::Rectangle( aStartPos.Col(), nPageStartRow + nField,
1736                               nTabEndCol, nPageStartRow + nField - 1 );
1737 
1738         bool bFound = false;            // is this within the same orientation?
1739         bool bBeforeDrag = false;
1740         bool bAfterDrag = false;
1741         for (tools::Long nPos=0; o3tl::make_unsigned(nPos)<pPageFields.size() && !bFound; nPos++)
1742         {
1743             if (pPageFields[nPos].mnDim == nDragDim)
1744             {
1745                 bFound = true;
1746                 if ( nField < nPos )
1747                     bBeforeDrag = true;
1748                 else if ( nField > nPos )
1749                     bAfterDrag = true;
1750             }
1751         }
1752 
1753         if ( bFound )
1754         {
1755             if (!bBeforeDrag)
1756             {
1757                 rPosRect.AdjustBottom( 1 );
1758                 if (bAfterDrag)
1759                     rPosRect.AdjustTop( 1 );
1760             }
1761         }
1762         else
1763         {
1764             if ( !bMouseTop )
1765             {
1766                 rPosRect.AdjustTop( 1 );
1767                 rPosRect.AdjustBottom( 1 );
1768                 ++nField;
1769             }
1770         }
1771 
1772         rOrient = sheet::DataPilotFieldOrientation_PAGE;
1773         rDimPos = nField;                       //!...
1774         return true;
1775     }
1776 
1777     return false;
1778 }
1779 
1780 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1781