1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <scitems.hxx>
22 #include <comphelper/sequence.hxx>
23 #include <editeng/borderline.hxx>
24 #include <editeng/boxitem.hxx>
25 #include <editeng/wghtitem.hxx>
26 #include <editeng/justifyitem.hxx>
27 #include <o3tl/safeint.hxx>
28 #include <osl/diagnose.h>
29 #include <svl/itemset.hxx>
31 #include <dpoutput.hxx>
32 #include <dpobject.hxx>
33 #include <document.hxx>
35 #include <formula/errorcodes.hxx>
36 #include <miscuno.hxx>
37 #include <globstr.hrc>
38 #include <stlpool.hxx>
39 #include <stlsheet.hxx>
40 #include <scresid.hxx>
41 #include <unonames.hxx>
42 #include <strings.hrc>
43 #include <stringutil.hxx>
45 #include <pivot/DPOutLevelData.hxx>
47 #include <com/sun/star/beans/XPropertySet.hpp>
48 #include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
49 #include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
50 #include <com/sun/star/sheet/DataPilotTablePositionData.hpp>
51 #include <com/sun/star/sheet/DataPilotTableResultData.hpp>
52 #include <com/sun/star/sheet/MemberResultFlags.hpp>
53 #include <com/sun/star/sheet/DataResultFlags.hpp>
54 #include <com/sun/star/sheet/DataPilotTablePositionType.hpp>
55 #include <com/sun/star/sheet/GeneralFunction2.hpp>
56 #include <com/sun/star/sheet/MemberResult.hpp>
57 #include <com/sun/star/sheet/XDataPilotMemberResults.hpp>
58 #include <com/sun/star/sheet/XDataPilotResults.hpp>
59 #include <com/sun/star/sheet/XDimensionsSupplier.hpp>
60 #include <com/sun/star/sheet/XHierarchiesSupplier.hpp>
61 #include <com/sun/star/sheet/XLevelsSupplier.hpp>
62 #include <com/sun/star/sheet/XMembersAccess.hpp>
63 #include <com/sun/star/sheet/XMembersSupplier.hpp>
64 #include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
65 #include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>
68 #include <string_view>
73 using namespace com::sun::star
;
75 using ::com::sun::star::beans::XPropertySet
;
76 using ::com::sun::star::uno::Sequence
;
77 using ::com::sun::star::uno::UNO_QUERY
;
78 using ::com::sun::star::uno::Reference
;
79 using ::com::sun::star::sheet::DataPilotTablePositionData
;
80 using ::com::sun::star::sheet::DataPilotTableResultData
;
82 #define SC_DP_FRAME_INNER_BOLD 20
83 #define SC_DP_FRAME_OUTER_BOLD 40
85 #define SC_DP_FRAME_COLOR Color(0,0,0) //( 0x20, 0x40, 0x68 )
89 struct ScDPOutLevelDataComparator
91 bool operator()(const ScDPOutLevelData
& rA
, const ScDPOutLevelData
& rB
)
93 return rA
.mnDimPos
<rB
.mnDimPos
|| ( rA
.mnDimPos
==rB
.mnDimPos
&& rA
.mnHier
<rB
.mnHier
) ||
94 ( rA
.mnDimPos
==rB
.mnDimPos
&& rA
.mnHier
==rB
.mnHier
&& rA
.mnLevel
<rB
.mnLevel
);
97 } // end anonymous namespace
103 ::std::vector
< bool > mbNeedLineCols
;
104 ::std::vector
< SCCOL
> mnCols
;
106 ::std::vector
< bool > mbNeedLineRows
;
107 ::std::vector
< SCROW
> mnRows
;
112 SCCOL mnDataStartCol
;
113 SCROW mnDataStartRow
;
118 ScDPOutputImpl( ScDocument
* pDoc
, sal_uInt16 nTab
,
125 bool AddRow( SCROW nRow
);
126 bool AddCol( SCCOL nCol
);
128 void OutputDataArea();
129 void OutputBlockFrame ( SCCOL nStartCol
, SCROW nStartRow
, SCCOL nEndCol
, SCROW nEndRow
, bool bHori
= false );
133 void ScDPOutputImpl::OutputDataArea()
135 AddRow( mnDataStartRow
);
136 AddCol( mnDataStartCol
);
138 mnCols
.push_back( mnTabEndCol
+1); //set last row bottom
139 mnRows
.push_back( mnTabEndRow
+1); //set last col bottom
141 bool bAllRows
= ( ( mnTabEndRow
- mnDataStartRow
+ 2 ) == static_cast<SCROW
>(mnRows
.size()) );
143 std::sort( mnCols
.begin(), mnCols
.end());
144 std::sort( mnRows
.begin(), mnRows
.end());
146 for( SCCOL nCol
= 0; nCol
< static_cast<SCCOL
>(mnCols
.size())-1; nCol
++ )
150 if ( nCol
< static_cast<SCCOL
>(mnCols
.size())-2)
152 for ( SCROW i
= nCol
%2; i
< static_cast<SCROW
>(mnRows
.size())-2; i
+=2 )
153 OutputBlockFrame( mnCols
[nCol
], mnRows
[i
], mnCols
[nCol
+1]-1, mnRows
[i
+1]-1 );
154 if ( mnRows
.size()>=2 )
155 OutputBlockFrame( mnCols
[nCol
], mnRows
[mnRows
.size()-2], mnCols
[nCol
+1]-1, mnRows
[mnRows
.size()-1]-1 );
159 for ( SCROW i
= 0 ; i
< static_cast<SCROW
>(mnRows
.size())-1; i
++ )
160 OutputBlockFrame( mnCols
[nCol
], mnRows
[i
], mnCols
[nCol
+1]-1, mnRows
[i
+1]-1 );
164 OutputBlockFrame( mnCols
[nCol
], mnRows
.front(), mnCols
[nCol
+1]-1, mnRows
.back()-1, bAllRows
);
166 //out put rows area outer framer
167 if ( mnTabStartCol
!= mnDataStartCol
)
169 if ( mnTabStartRow
!= mnDataStartRow
)
170 OutputBlockFrame( mnTabStartCol
, mnTabStartRow
, mnDataStartCol
-1, mnDataStartRow
-1 );
171 OutputBlockFrame( mnTabStartCol
, mnDataStartRow
, mnDataStartCol
-1, mnTabEndRow
);
173 //out put cols area outer framer
174 OutputBlockFrame( mnDataStartCol
, mnTabStartRow
, mnTabEndCol
, mnDataStartRow
-1 );
177 ScDPOutputImpl::ScDPOutputImpl( ScDocument
* pDoc
, sal_uInt16 nTab
,
186 mnTabStartCol( nTabStartCol
),
187 mnTabStartRow( nTabStartRow
),
188 mnDataStartCol ( nDataStartCol
),
189 mnDataStartRow ( nDataStartRow
),
190 mnTabEndCol( nTabEndCol
),
191 mnTabEndRow( nTabEndRow
)
193 mbNeedLineCols
.resize( nTabEndCol
-nDataStartCol
+1, false );
194 mbNeedLineRows
.resize( nTabEndRow
-nDataStartRow
+1, false );
198 bool ScDPOutputImpl::AddRow( SCROW nRow
)
200 if ( !mbNeedLineRows
[ nRow
- mnDataStartRow
] )
202 mbNeedLineRows
[ nRow
- mnDataStartRow
] = true;
203 mnRows
.push_back( nRow
);
210 bool ScDPOutputImpl::AddCol( SCCOL nCol
)
213 if ( !mbNeedLineCols
[ nCol
- mnDataStartCol
] )
215 mbNeedLineCols
[ nCol
- mnDataStartCol
] = true;
216 mnCols
.push_back( nCol
);
223 void ScDPOutputImpl::OutputBlockFrame ( SCCOL nStartCol
, SCROW nStartRow
, SCCOL nEndCol
, SCROW nEndRow
, bool bHori
)
225 Color color
= SC_DP_FRAME_COLOR
;
226 ::editeng::SvxBorderLine
aLine( &color
, SC_DP_FRAME_INNER_BOLD
);
227 ::editeng::SvxBorderLine
aOutLine( &color
, SC_DP_FRAME_OUTER_BOLD
);
229 SvxBoxItem
aBox( ATTR_BORDER
);
231 if ( nStartCol
== mnTabStartCol
)
232 aBox
.SetLine(&aOutLine
, SvxBoxItemLine::LEFT
);
234 aBox
.SetLine(&aLine
, SvxBoxItemLine::LEFT
);
236 if ( nStartRow
== mnTabStartRow
)
237 aBox
.SetLine(&aOutLine
, SvxBoxItemLine::TOP
);
239 aBox
.SetLine(&aLine
, SvxBoxItemLine::TOP
);
241 if ( nEndCol
== mnTabEndCol
) //bottom row
242 aBox
.SetLine(&aOutLine
, SvxBoxItemLine::RIGHT
);
244 aBox
.SetLine(&aLine
, SvxBoxItemLine::RIGHT
);
246 if ( nEndRow
== mnTabEndRow
) //bottom
247 aBox
.SetLine(&aOutLine
, SvxBoxItemLine::BOTTOM
);
249 aBox
.SetLine(&aLine
, SvxBoxItemLine::BOTTOM
);
251 SvxBoxInfoItem
aBoxInfo( ATTR_BORDER_INNER
);
252 aBoxInfo
.SetValid(SvxBoxInfoItemValidFlags::VERT
,false );
255 aBoxInfo
.SetValid(SvxBoxInfoItemValidFlags::HORI
);
256 aBoxInfo
.SetLine( &aLine
, SvxBoxInfoItemLine::HORI
);
259 aBoxInfo
.SetValid(SvxBoxInfoItemValidFlags::HORI
,false );
261 aBoxInfo
.SetValid(SvxBoxInfoItemValidFlags::DISTANCE
,false);
263 mpDoc
->ApplyFrameAreaTab(ScRange(nStartCol
, nStartRow
, mnTab
, nEndCol
, nEndRow
, mnTab
), aBox
, aBoxInfo
);
270 void lcl_SetStyleById(ScDocument
* pDoc
, SCTAB nTab
,
271 SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
,
274 if ( nCol1
> nCol2
|| nRow1
> nRow2
)
276 OSL_FAIL("SetStyleById: invalid range");
280 OUString aStyleName
= ScResId(pStrId
);
281 ScStyleSheetPool
* pStlPool
= pDoc
->GetStyleSheetPool();
282 ScStyleSheet
* pStyle
= static_cast<ScStyleSheet
*>( pStlPool
->Find( aStyleName
, SfxStyleFamily::Para
) );
285 // create new style (was in ScPivot::SetStyle)
287 pStyle
= static_cast<ScStyleSheet
*>( &pStlPool
->Make( aStyleName
, SfxStyleFamily::Para
,
288 SfxStyleSearchBits::UserDefined
) );
289 pStyle
->SetParent( ScResId(STR_STYLENAME_STANDARD
) );
290 SfxItemSet
& rSet
= pStyle
->GetItemSet();
291 if (pStrId
== STR_PIVOT_STYLENAME_RESULT
|| pStrId
== STR_PIVOT_STYLENAME_TITLE
){
292 rSet
.Put( SvxWeightItem( WEIGHT_BOLD
, ATTR_FONT_WEIGHT
) );
293 rSet
.Put( SvxWeightItem( WEIGHT_BOLD
, ATTR_CJK_FONT_WEIGHT
) );
294 rSet
.Put( SvxWeightItem( WEIGHT_BOLD
, ATTR_CTL_FONT_WEIGHT
) );
296 if (pStrId
== STR_PIVOT_STYLENAME_CATEGORY
|| pStrId
== STR_PIVOT_STYLENAME_TITLE
)
297 rSet
.Put( SvxHorJustifyItem( SvxCellHorJustify::Left
, ATTR_HOR_JUSTIFY
) );
300 pDoc
->ApplyStyleAreaTab( nCol1
, nRow1
, nCol2
, nRow2
, nTab
, *pStyle
);
303 void lcl_SetFrame( ScDocument
* pDoc
, SCTAB nTab
,
304 SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
,
307 ::editeng::SvxBorderLine
aLine(nullptr, nWidth
, SvxBorderLineStyle::SOLID
);
308 SvxBoxItem
aBox( ATTR_BORDER
);
309 aBox
.SetLine(&aLine
, SvxBoxItemLine::LEFT
);
310 aBox
.SetLine(&aLine
, SvxBoxItemLine::TOP
);
311 aBox
.SetLine(&aLine
, SvxBoxItemLine::RIGHT
);
312 aBox
.SetLine(&aLine
, SvxBoxItemLine::BOTTOM
);
313 SvxBoxInfoItem
aBoxInfo( ATTR_BORDER_INNER
);
314 aBoxInfo
.SetValid(SvxBoxInfoItemValidFlags::HORI
,false);
315 aBoxInfo
.SetValid(SvxBoxInfoItemValidFlags::VERT
,false);
316 aBoxInfo
.SetValid(SvxBoxInfoItemValidFlags::DISTANCE
,false);
318 pDoc
->ApplyFrameAreaTab(ScRange(nCol1
, nRow1
, nTab
, nCol2
, nRow2
, nTab
), aBox
, aBoxInfo
);
321 void lcl_FillNumberFormats( std::unique_ptr
<sal_uInt32
[]>& rFormats
, sal_Int32
& rCount
,
322 const uno::Reference
<sheet::XDataPilotMemberResults
>& xLevRes
,
323 const uno::Reference
<container::XIndexAccess
>& xDims
)
326 return; // already set
328 // xLevRes is from the data layout dimension
329 //TODO: use result sequence from ScDPOutLevelData!
331 uno::Sequence
<sheet::MemberResult
> aResult
= xLevRes
->getResults();
333 tools::Long nSize
= aResult
.getLength();
337 // get names/formats for all data dimensions
338 //TODO: merge this with the loop to collect ScDPOutLevelData?
340 std::vector
<OUString
> aDataNames
;
341 std::vector
<sal_uInt32
> aDataFormats
;
342 sal_Int32 nDimCount
= xDims
->getCount();
344 for ( ; nDim
< nDimCount
; nDim
++)
346 uno::Reference
<uno::XInterface
> xDim(xDims
->getByIndex(nDim
), uno::UNO_QUERY
);
347 uno::Reference
<beans::XPropertySet
> xDimProp( xDim
, uno::UNO_QUERY
);
348 uno::Reference
<container::XNamed
> xDimName( xDim
, uno::UNO_QUERY
);
349 if ( xDimProp
.is() && xDimName
.is() )
351 sheet::DataPilotFieldOrientation eDimOrient
=
352 ScUnoHelpFunctions::GetEnumProperty(
353 xDimProp
, SC_UNO_DP_ORIENTATION
,
354 sheet::DataPilotFieldOrientation_HIDDEN
);
355 if ( eDimOrient
== sheet::DataPilotFieldOrientation_DATA
)
357 aDataNames
.push_back(xDimName
->getName());
358 tools::Long nFormat
= ScUnoHelpFunctions::GetLongProperty(
361 aDataFormats
.push_back(nFormat
);
366 if (aDataFormats
.empty())
369 const sheet::MemberResult
* pArray
= aResult
.getConstArray();
372 sal_uInt32
* pNumFmt
= new sal_uInt32
[nSize
];
373 if (aDataFormats
.size() == 1)
375 // only one data dimension -> use its numberformat everywhere
376 tools::Long nFormat
= aDataFormats
[0];
377 for (tools::Long nPos
=0; nPos
<nSize
; nPos
++)
378 pNumFmt
[nPos
] = nFormat
;
382 for (tools::Long nPos
=0; nPos
<nSize
; nPos
++)
384 // if CONTINUE bit is set, keep previous name
385 //TODO: keep number format instead!
386 if ( !(pArray
[nPos
].Flags
& sheet::MemberResultFlags::CONTINUE
) )
387 aName
= pArray
[nPos
].Name
;
389 sal_uInt32 nFormat
= 0;
390 for (size_t i
=0; i
<aDataFormats
.size(); i
++)
391 if (aName
== aDataNames
[i
]) //TODO: search more efficiently?
393 nFormat
= aDataFormats
[i
];
396 pNumFmt
[nPos
] = nFormat
;
400 rFormats
.reset( pNumFmt
);
404 sal_uInt32
lcl_GetFirstNumberFormat( const uno::Reference
<container::XIndexAccess
>& xDims
)
406 tools::Long nDimCount
= xDims
->getCount();
407 for (tools::Long nDim
=0; nDim
<nDimCount
; nDim
++)
409 uno::Reference
<beans::XPropertySet
> xDimProp(xDims
->getByIndex(nDim
), uno::UNO_QUERY
);
412 sheet::DataPilotFieldOrientation eDimOrient
=
413 ScUnoHelpFunctions::GetEnumProperty(
414 xDimProp
, SC_UNO_DP_ORIENTATION
,
415 sheet::DataPilotFieldOrientation_HIDDEN
);
416 if ( eDimOrient
== sheet::DataPilotFieldOrientation_DATA
)
418 tools::Long nFormat
= ScUnoHelpFunctions::GetLongProperty(
422 return nFormat
; // use format from first found data dimension
427 return 0; // none found
430 bool lcl_MemberEmpty( const uno::Sequence
<sheet::MemberResult
>& rSeq
)
432 // used to skip levels that have no members
434 return std::none_of(rSeq
.begin(), rSeq
.end(),
435 [](const sheet::MemberResult
& rMem
) {
436 return rMem
.Flags
& sheet::MemberResultFlags::HASMEMBER
; });
440 * Get visible page dimension members as results, except that, if all
441 * members are visible, then this function returns empty result.
443 uno::Sequence
<sheet::MemberResult
> getVisiblePageMembersAsResults( const uno::Reference
<uno::XInterface
>& xLevel
)
446 return uno::Sequence
<sheet::MemberResult
>();
448 uno::Reference
<sheet::XMembersSupplier
> xMSupplier(xLevel
, UNO_QUERY
);
449 if (!xMSupplier
.is())
450 return uno::Sequence
<sheet::MemberResult
>();
452 uno::Reference
<sheet::XMembersAccess
> xNA
= xMSupplier
->getMembers();
454 return uno::Sequence
<sheet::MemberResult
>();
456 std::vector
<sheet::MemberResult
> aRes
;
457 const uno::Sequence
<OUString
> aNames
= xNA
->getElementNames();
458 for (const OUString
& rName
: aNames
)
460 xNA
->getByName(rName
);
462 uno::Reference
<beans::XPropertySet
> xMemPS(xNA
->getByName(rName
), UNO_QUERY
);
466 OUString aCaption
= ScUnoHelpFunctions::GetStringProperty(xMemPS
, SC_UNO_DP_LAYOUTNAME
, OUString());
467 if (aCaption
.isEmpty())
470 bool bVisible
= ScUnoHelpFunctions::GetBoolProperty(xMemPS
, SC_UNO_DP_ISVISIBLE
);
474 /* TODO: any numeric value to obtain? */
475 aRes
.emplace_back(rName
, aCaption
, 0, std::numeric_limits
<double>::quiet_NaN());
479 if (o3tl::make_unsigned(aNames
.getLength()) == aRes
.size())
480 // All members are visible. Return empty result.
481 return uno::Sequence
<sheet::MemberResult
>();
483 return comphelper::containerToSequence(aRes
);
486 } // end anonymous namespace
488 ScDPOutput::ScDPOutput(ScDocument
* pDocument
, uno::Reference
<sheet::XDimensionsSupplier
> xSource
,
489 const ScAddress
& rPosition
, bool bFilter
, bool bExpandCollapse
, ScDPObject
& rObject
, bool bHideHeader
)
490 : mpDocument(pDocument
)
491 , maFormatOutput(rObject
)
492 , mxSource(std::move(xSource
))
493 , maStartPos(rPosition
)
494 , mnColFormatCount(0)
495 , mnRowFormatCount(0)
496 , mnSingleNumberFormat(0)
501 , mbDoFilter(bFilter
)
502 , mbResultsError(false)
503 , mbSizesValid(false)
504 , mbSizeOverflow(false)
505 , mbHeaderLayout(false)
506 , mbHasCompactRowField(false)
507 , mbExpandCollapse(bExpandCollapse
)
508 , mbHideHeader(bHideHeader
)
510 mnTabStartCol
= mnMemberStartCol
= mnDataStartCol
= mnTabEndCol
= 0;
511 mnTabStartRow
= mnMemberStartRow
= mnDataStartRow
= mnTabEndRow
= 0;
513 uno::Reference
<sheet::XDataPilotResults
> xResult(mxSource
, uno::UNO_QUERY
);
514 if (mxSource
.is() && xResult
.is())
516 // get dimension results:
518 uno::Reference
<container::XIndexAccess
> xDims
=
519 new ScNameToIndexAccess(mxSource
->getDimensions());
520 tools::Long nDimCount
= xDims
->getCount();
521 for (tools::Long nDim
=0; nDim
<nDimCount
; nDim
++)
523 uno::Reference
<uno::XInterface
> xDim(xDims
->getByIndex(nDim
), uno::UNO_QUERY
);
524 uno::Reference
<beans::XPropertySet
> xDimProp( xDim
, uno::UNO_QUERY
);
525 uno::Reference
<sheet::XHierarchiesSupplier
> xDimSupp( xDim
, uno::UNO_QUERY
);
526 if ( xDimProp
.is() && xDimSupp
.is() )
528 sheet::DataPilotFieldOrientation eDimOrient
=
529 ScUnoHelpFunctions::GetEnumProperty(
530 xDimProp
, SC_UNO_DP_ORIENTATION
,
531 sheet::DataPilotFieldOrientation_HIDDEN
);
532 tools::Long nDimPos
= ScUnoHelpFunctions::GetLongProperty( xDimProp
,
533 SC_UNO_DP_POSITION
);
534 bool bIsDataLayout
= ScUnoHelpFunctions::GetBoolProperty(
535 xDimProp
, SC_UNO_DP_ISDATALAYOUT
);
536 bool bHasHiddenMember
= ScUnoHelpFunctions::GetBoolProperty(
537 xDimProp
, SC_UNO_DP_HAS_HIDDEN_MEMBER
);
538 sal_Int32 nNumFmt
= ScUnoHelpFunctions::GetLongProperty(
539 xDimProp
, SC_UNO_DP_NUMBERFO
);
541 if ( eDimOrient
!= sheet::DataPilotFieldOrientation_HIDDEN
)
543 uno::Reference
<container::XIndexAccess
> xHiers
=
544 new ScNameToIndexAccess( xDimSupp
->getHierarchies() );
545 tools::Long nHierarchy
= ScUnoHelpFunctions::GetLongProperty(
547 SC_UNO_DP_USEDHIERARCHY
);
548 if ( nHierarchy
>= xHiers
->getCount() )
551 uno::Reference
<sheet::XLevelsSupplier
> xHierSupp(xHiers
->getByIndex(nHierarchy
),
553 if ( xHierSupp
.is() )
555 uno::Reference
<container::XIndexAccess
> xLevels
=
556 new ScNameToIndexAccess( xHierSupp
->getLevels() );
557 tools::Long nLevCount
= xLevels
->getCount();
558 for (tools::Long nLev
=0; nLev
<nLevCount
; nLev
++)
560 uno::Reference
<uno::XInterface
> xLevel(xLevels
->getByIndex(nLev
),
562 uno::Reference
<container::XNamed
> xLevNam( xLevel
, uno::UNO_QUERY
);
563 uno::Reference
<sheet::XDataPilotMemberResults
> xLevRes(
564 xLevel
, uno::UNO_QUERY
);
565 if ( xLevNam
.is() && xLevRes
.is() )
567 OUString aName
= xLevNam
->getName();
568 Reference
<XPropertySet
> xPropSet(xLevel
, UNO_QUERY
);
569 // Caption equals the field name by default.
570 // #i108948# use ScUnoHelpFunctions::GetStringProperty, because
571 // LayoutName is new and may not be present in external implementation
572 OUString aCaption
= ScUnoHelpFunctions::GetStringProperty( xPropSet
,
573 SC_UNO_DP_LAYOUTNAME
, aName
);
575 switch ( eDimOrient
)
577 case sheet::DataPilotFieldOrientation_COLUMN
:
579 uno::Sequence
<sheet::MemberResult
> aResult
= xLevRes
->getResults();
580 if (!lcl_MemberEmpty(aResult
))
582 mpColFields
.emplace_back(nDim
, nHierarchy
, nLev
, nDimPos
, nNumFmt
, aResult
, aName
,
583 aCaption
, bHasHiddenMember
, bIsDataLayout
, false);
587 case sheet::DataPilotFieldOrientation_ROW
:
589 uno::Sequence
<sheet::MemberResult
> aResult
= xLevRes
->getResults();
591 // We want only to remove the DATA column if it is empty
592 // and not any other empty columns (to still show the
594 bool bSkip
= lcl_MemberEmpty(aResult
) && bIsDataLayout
;
597 bool bFieldCompact
= false;
600 sheet::DataPilotFieldLayoutInfo aLayoutInfo
;
601 xPropSet
->getPropertyValue( SC_UNO_DP_LAYOUT
) >>= aLayoutInfo
;
602 bFieldCompact
= (aLayoutInfo
.LayoutMode
== sheet::DataPilotFieldLayoutMode::COMPACT_LAYOUT
);
604 catch (uno::Exception
&)
607 mpRowFields
.emplace_back(nDim
, nHierarchy
, nLev
, nDimPos
, nNumFmt
, aResult
, aName
,
608 aCaption
, bHasHiddenMember
, bIsDataLayout
, false);
609 maRowCompactFlags
.push_back(bFieldCompact
);
610 mbHasCompactRowField
|= bFieldCompact
;
615 case sheet::DataPilotFieldOrientation_PAGE
:
617 uno::Sequence
<sheet::MemberResult
> aResult
= getVisiblePageMembersAsResults(xLevel
);
618 // no check on results for page fields
619 mpPageFields
.emplace_back(nDim
, nHierarchy
, nLev
, nDimPos
, nNumFmt
, aResult
, aName
,
620 aCaption
, bHasHiddenMember
, false, true);
625 // added to avoid warnings
629 // get number formats from data dimensions
632 OSL_ENSURE( nLevCount
== 1, "data layout: multiple levels?" );
633 if ( eDimOrient
== sheet::DataPilotFieldOrientation_COLUMN
)
634 lcl_FillNumberFormats(mpColNumberFormat
, mnColFormatCount
, xLevRes
, xDims
);
635 else if ( eDimOrient
== sheet::DataPilotFieldOrientation_ROW
)
636 lcl_FillNumberFormats(mpRowNumberFormat
, mnRowFormatCount
, xLevRes
, xDims
);
642 else if ( bIsDataLayout
)
644 // data layout dimension is hidden (allowed if there is only one data dimension)
645 // -> use the number format from the first data dimension for all results
647 mnSingleNumberFormat
= lcl_GetFirstNumberFormat( xDims
);
651 std::sort(mpColFields
.begin(), mpColFields
.end(), ScDPOutLevelDataComparator());
652 std::sort(mpRowFields
.begin(), mpRowFields
.end(), ScDPOutLevelDataComparator());
653 std::sort(mpPageFields
.begin(), mpPageFields
.end(), ScDPOutLevelDataComparator());
659 maData
= xResult
->getResults();
661 catch (const uno::RuntimeException
&)
663 mbResultsError
= true;
667 // get "DataDescription" property (may be missing in external sources)
669 uno::Reference
<beans::XPropertySet
> xSrcProp(mxSource
, uno::UNO_QUERY
);
670 if ( !xSrcProp
.is() )
675 uno::Any aAny
= xSrcProp
->getPropertyValue( SC_UNO_DP_DATADESC
);
678 maDataDescription
= aUStr
;
680 catch(const uno::Exception
&)
685 ScDPOutput::~ScDPOutput()
689 void ScDPOutput::SetPosition(const ScAddress
& rPosition
)
691 maStartPos
= rPosition
;
692 mbSizesValid
= mbSizeOverflow
= false;
695 void ScDPOutput::DataCell( SCCOL nCol
, SCROW nRow
, SCTAB nTab
, const sheet::DataResult
& rData
)
697 tools::Long nFlags
= rData
.Flags
;
698 if ( nFlags
& sheet::DataResultFlags::ERROR
)
700 mpDocument
->SetError( nCol
, nRow
, nTab
, FormulaError::NoValue
);
702 else if ( nFlags
& sheet::DataResultFlags::HASDATA
)
704 mpDocument
->SetValue( nCol
, nRow
, nTab
, rData
.Value
);
706 // use number formats from source
708 OSL_ENSURE(mbSizesValid
, "DataCell: !bSizesValid");
709 sal_uInt32 nFormat
= 0;
710 bool bApplyFormat
= false;
711 if (mpColNumberFormat
)
713 if (nCol
>= mnDataStartCol
)
715 tools::Long nIndex
= nCol
- mnDataStartCol
;
716 if (nIndex
< mnColFormatCount
)
718 nFormat
= mpColNumberFormat
[nIndex
];
723 else if (mpRowNumberFormat
)
725 if (nRow
>= mnDataStartRow
)
727 tools::Long nIndex
= nRow
- mnDataStartRow
;
728 if (nIndex
< mnRowFormatCount
)
730 nFormat
= mpRowNumberFormat
[nIndex
];
735 else if (mnSingleNumberFormat
!= 0)
737 nFormat
= mnSingleNumberFormat
; // single format is used everywhere
742 mpDocument
->ApplyAttr(nCol
, nRow
, nTab
, SfxUInt32Item(ATTR_VALUE_FORMAT
, nFormat
));
744 // SubTotal formatting is controlled by headers
747 void ScDPOutput::HeaderCell( SCCOL nCol
, SCROW nRow
, SCTAB nTab
,
748 const sheet::MemberResult
& rData
, bool bColHeader
, tools::Long nLevel
)
750 tools::Long nFlags
= rData
.Flags
;
752 if ( nFlags
& sheet::MemberResultFlags::HASMEMBER
)
754 bool bNumeric
= (nFlags
& sheet::MemberResultFlags::NUMERIC
) != 0;
755 if (bNumeric
&& std::isfinite( rData
.Value
))
757 mpDocument
->SetValue( nCol
, nRow
, nTab
, rData
.Value
);
761 ScSetStringParam aParam
;
763 aParam
.setNumericInput();
765 aParam
.setTextInput();
767 mpDocument
->SetString(nCol
, nRow
, nTab
, rData
.Caption
, &aParam
);
771 if ( !(nFlags
& sheet::MemberResultFlags::SUBTOTAL
) )
774 ScDPOutputImpl
outputimp(mpDocument
, nTab
,
775 mnTabStartCol
, mnTabStartRow
,
776 mnDataStartCol
, mnDataStartRow
, mnTabEndCol
, mnTabEndRow
);
777 //TODO: limit frames to horizontal or vertical?
780 outputimp
.OutputBlockFrame(nCol
, mnMemberStartRow
+static_cast<SCROW
>(nLevel
), nCol
, mnDataStartRow
- 1);
782 lcl_SetStyleById(mpDocument
, nTab
, nCol
, mnMemberStartRow
+ static_cast<SCROW
>(nLevel
), nCol
, mnDataStartRow
- 1, STR_PIVOT_STYLENAME_TITLE
);
783 lcl_SetStyleById(mpDocument
, nTab
, nCol
, mnDataStartRow
, nCol
, mnTabEndRow
, STR_PIVOT_STYLENAME_RESULT
);
787 outputimp
.OutputBlockFrame(mnMemberStartCol
+ static_cast<SCCOL
>(nLevel
), nRow
, mnDataStartCol
- 1, nRow
);
788 lcl_SetStyleById(mpDocument
, nTab
, mnMemberStartCol
+ static_cast<SCCOL
>(nLevel
), nRow
, mnDataStartCol
- 1, nRow
, STR_PIVOT_STYLENAME_TITLE
);
789 lcl_SetStyleById(mpDocument
, nTab
, mnDataStartCol
, nRow
, mnTabEndCol
, nRow
, STR_PIVOT_STYLENAME_RESULT
);
793 void ScDPOutput::MultiFieldCell(SCCOL nCol
, SCROW nRow
, SCTAB nTab
, bool bRowField
)
795 mpDocument
->SetString(nCol
, nRow
, nTab
, ScResId(bRowField
? STR_PIVOT_ROW_LABELS
: STR_PIVOT_COL_LABELS
));
797 ScMF nMergeFlag
= ScMF::Button
;
798 for (auto& rData
: mpRowFields
)
800 if (rData
.mbHasHiddenMember
)
802 nMergeFlag
|= ScMF::HiddenMember
;
807 nMergeFlag
|= ScMF::ButtonPopup2
;
809 mpDocument
->ApplyFlagsTab(nCol
, nRow
, nCol
, nRow
, nTab
, nMergeFlag
);
810 lcl_SetStyleById(mpDocument
, nTab
, nCol
, nRow
, nCol
, nRow
, STR_PIVOT_STYLENAME_FIELDNAME
);
813 void ScDPOutput::FieldCell(
814 SCCOL nCol
, SCROW nRow
, SCTAB nTab
, const ScDPOutLevelData
& rData
, bool bInTable
)
816 // Avoid unwanted automatic format detection.
817 ScSetStringParam aParam
;
818 aParam
.mbDetectNumberFormat
= false;
819 aParam
.meSetTextNumFormat
= ScSetStringParam::Always
;
820 aParam
.mbHandleApostrophe
= false;
821 mpDocument
->SetString(nCol
, nRow
, nTab
, rData
.maCaption
, &aParam
);
824 lcl_SetFrame(mpDocument
, nTab
, nCol
,nRow
, nCol
,nRow
, 20);
826 // For field button drawing
827 ScMF nMergeFlag
= ScMF::NONE
;
828 if (rData
.mbHasHiddenMember
)
829 nMergeFlag
|= ScMF::HiddenMember
;
833 nMergeFlag
|= ScMF::ButtonPopup
;
834 mpDocument
->ApplyFlagsTab(nCol
, nRow
, nCol
, nRow
, nTab
, ScMF::Button
);
835 mpDocument
->ApplyFlagsTab(nCol
+1, nRow
, nCol
+1, nRow
, nTab
, nMergeFlag
);
839 nMergeFlag
|= ScMF::Button
;
840 if (!rData
.mbDataLayout
)
841 nMergeFlag
|= ScMF::ButtonPopup
;
842 mpDocument
->ApplyFlagsTab(nCol
, nRow
, nCol
, nRow
, nTab
, nMergeFlag
);
845 lcl_SetStyleById(mpDocument
, nTab
, nCol
,nRow
, nCol
,nRow
, STR_PIVOT_STYLENAME_FIELDNAME
);
848 static void lcl_DoFilterButton( ScDocument
* pDoc
, SCCOL nCol
, SCROW nRow
, SCTAB nTab
)
850 pDoc
->SetString( nCol
, nRow
, nTab
, ScResId(STR_CELL_FILTER
) );
851 pDoc
->ApplyFlagsTab(nCol
, nRow
, nCol
, nRow
, nTab
, ScMF::Button
);
854 SCCOL
ScDPOutput::GetColumnsForRowFields() const
856 if (!mbHasCompactRowField
)
857 return static_cast<SCCOL
>(mpRowFields
.size());
860 for (const auto bCompact
: maRowCompactFlags
)
864 if (maRowCompactFlags
.back())
870 void ScDPOutput::CalcSizes()
875 // get column size of data from first row
876 //TODO: allow different sizes (and clear following areas) ???
878 mnRowCount
= maData
.getLength();
879 const uno::Sequence
<sheet::DataResult
>* pRowAry
= maData
.getConstArray();
880 mnColCount
= mnRowCount
? ( pRowAry
[0].getLength() ) : 0;
885 else if (GetHeaderLayout() && mpColFields
.empty())
886 // Insert an extra header row only when there is no column field.
889 // calculate output positions and sizes
891 tools::Long nPageSize
= 0; // use page fields!
892 if (mbDoFilter
|| !mpPageFields
.empty())
894 nPageSize
+= mpPageFields
.size() + 1; // plus one empty row
896 ++nPageSize
; // filter button above the page fields
899 if (maStartPos
.Col() + static_cast<tools::Long
>(mpRowFields
.size()) + mnColCount
- 1 > mpDocument
->MaxCol() ||
900 maStartPos
.Row() + nPageSize
+ mnHeaderSize
+ static_cast<tools::Long
>(mpColFields
.size()) + mnRowCount
> mpDocument
->MaxRow())
902 mbSizeOverflow
= true;
905 mnTabStartCol
= maStartPos
.Col();
906 mnTabStartRow
= maStartPos
.Row() + static_cast<SCROW
>(nPageSize
); // below page fields
907 mnMemberStartCol
= mnTabStartCol
;
908 mnMemberStartRow
= mnTabStartRow
+ static_cast<SCROW
>(mnHeaderSize
);
909 mnDataStartCol
= mnMemberStartCol
+ GetColumnsForRowFields();
910 mnDataStartRow
= mnMemberStartRow
+ static_cast<SCROW
>(mpColFields
.size());
912 mnTabEndCol
= mnDataStartCol
+ static_cast<SCCOL
>(mnColCount
) - 1;
914 mnTabEndCol
= mnDataStartCol
; // single column will remain empty
915 // if page fields are involved, include the page selection cells
916 if (!mpPageFields
.empty() && mnTabEndCol
< mnTabStartCol
+ 1)
917 mnTabEndCol
= mnTabStartCol
+ 1;
919 mnTabEndRow
= mnDataStartRow
+ static_cast<SCROW
>(mnRowCount
) - 1;
921 mnTabEndRow
= mnDataStartRow
; // single row will remain empty
925 sal_Int32
ScDPOutput::GetPositionType(const ScAddress
& rPos
)
927 using namespace ::com::sun::star::sheet
;
929 SCCOL nCol
= rPos
.Col();
930 SCROW nRow
= rPos
.Row();
931 SCTAB nTab
= rPos
.Tab();
932 if ( nTab
!= maStartPos
.Tab() )
933 return DataPilotTablePositionType::NOT_IN_TABLE
;
937 // Make sure the cursor is within the table.
938 if (nCol
< mnTabStartCol
|| nRow
< mnTabStartRow
|| nCol
> mnTabEndCol
|| nRow
> mnTabEndRow
)
939 return DataPilotTablePositionType::NOT_IN_TABLE
;
941 // test for result data area.
942 if (nCol
>= mnDataStartCol
&& nCol
<= mnTabEndCol
&& nRow
>= mnDataStartRow
&& nRow
<= mnTabEndRow
)
943 return DataPilotTablePositionType::RESULT
;
945 bool bInColHeader
= (nRow
>= mnTabStartRow
&& nRow
< mnDataStartRow
);
946 bool bInRowHeader
= (nCol
>= mnTabStartCol
&& nCol
< mnDataStartCol
);
948 if (bInColHeader
&& bInRowHeader
)
949 // probably in that ugly little box at the upper-left corner of the table.
950 return DataPilotTablePositionType::OTHER
;
954 if (nRow
== mnTabStartRow
)
955 // first row in the column header area is always used for column
957 return DataPilotTablePositionType::OTHER
;
959 return DataPilotTablePositionType::COLUMN_HEADER
;
963 return DataPilotTablePositionType::ROW_HEADER
;
965 return DataPilotTablePositionType::OTHER
;
968 void ScDPOutput::outputPageFields(SCTAB nTab
)
970 for (size_t nField
= 0; nField
< mpPageFields
.size(); ++nField
)
972 SCCOL nHeaderCol
= maStartPos
.Col();
973 SCROW nHeaderRow
= maStartPos
.Row() + nField
+ (mbDoFilter
? 1 : 0);
974 // draw without frame for consistency with filter button:
975 FieldCell(nHeaderCol
, nHeaderRow
, nTab
, mpPageFields
[nField
], false);
976 SCCOL nFieldCol
= nHeaderCol
+ 1;
978 OUString aPageValue
= ScResId(SCSTR_ALL
);
979 const uno::Sequence
<sheet::MemberResult
>& rRes
= mpPageFields
[nField
].maResult
;
980 sal_Int32 n
= rRes
.getLength();
983 if (rRes
[0].Caption
.isEmpty())
984 aPageValue
= ScResId(STR_EMPTYDATA
);
986 aPageValue
= rRes
[0].Caption
;
990 aPageValue
= ScResId(SCSTR_MULTIPLE
);
993 ScSetStringParam aParam
;
994 aParam
.setTextInput();
995 mpDocument
->SetString(nFieldCol
, nHeaderRow
, nTab
, aPageValue
, &aParam
);
997 lcl_SetFrame(mpDocument
, nTab
, nFieldCol
, nHeaderRow
, nFieldCol
, nHeaderRow
, 20);
1001 void ScDPOutput::outputColumnHeaders(SCTAB nTab
, ScDPOutputImpl
& rOutputImpl
)
1003 size_t nNumColFields
= mpColFields
.size();
1005 for (size_t nField
= 0; nField
< nNumColFields
; nField
++)
1007 SCCOL nHeaderCol
= mnDataStartCol
+ SCCOL(nField
); //TODO: check for overflow
1009 if (mnMemberStartRow
> mnTabStartRow
)
1011 if (!mbHasCompactRowField
|| nNumColFields
== 1)
1012 FieldCell(nHeaderCol
, mnTabStartRow
, nTab
, mpColFields
[nField
], true);
1014 MultiFieldCell(nHeaderCol
, mnTabStartRow
, nTab
, false /* bRowField */);
1017 SCROW nRowPos
= mnMemberStartRow
+ SCROW(nField
); //TODO: check for overflow
1018 const uno::Sequence
<sheet::MemberResult
> rMemberSequence
= mpColFields
[nField
].maResult
;
1019 const sheet::MemberResult
* pMemberArray
= rMemberSequence
.getConstArray();
1020 tools::Long nThisColCount
= rMemberSequence
.getLength();
1021 OSL_ENSURE(nThisColCount
== mnColCount
, "count mismatch"); //TODO: ???
1023 for (tools::Long nColumn
= 0; nColumn
< nThisColCount
; nColumn
++)
1025 sheet::MemberResult
const& rMember
= rMemberSequence
[nColumn
];
1027 SCCOL nColPos
= mnDataStartCol
+ SCCOL(nColumn
); //TODO: check for overflow
1029 HeaderCell(nColPos
, nRowPos
, nTab
, rMember
, true, nField
);
1031 if ((rMember
.Flags
& sheet::MemberResultFlags::HASMEMBER
) &&
1032 !(rMember
.Flags
& sheet::MemberResultFlags::SUBTOTAL
))
1034 // Check the number of columns this spreads
1035 tools::Long nEnd
= nColumn
;
1036 while (nEnd
+ 1 < nThisColCount
&& (pMemberArray
[nEnd
+ 1].Flags
& sheet::MemberResultFlags::CONTINUE
))
1039 SCCOL nEndColPos
= mnDataStartCol
+ SCCOL(nEnd
); //TODO: check for overflow
1040 if (nField
+ 1 < mpColFields
.size())
1042 if (nField
+ 2 == mpColFields
.size())
1044 rOutputImpl
.AddCol( nColPos
);
1045 if (nColPos
+ 1 == nEndColPos
)
1046 rOutputImpl
.OutputBlockFrame(nColPos
, nRowPos
, nEndColPos
, nRowPos
+ 1, true);
1049 rOutputImpl
.OutputBlockFrame(nColPos
, nRowPos
, nEndColPos
, nRowPos
);
1051 lcl_SetStyleById(mpDocument
, nTab
, nColPos
, nRowPos
, nEndColPos
, mnDataStartRow
- 1, STR_PIVOT_STYLENAME_CATEGORY
);
1055 lcl_SetStyleById(mpDocument
, nTab
, nColPos
, nRowPos
, nColPos
, mnDataStartRow
- 1, STR_PIVOT_STYLENAME_CATEGORY
);
1058 else if (rMember
.Flags
& sheet::MemberResultFlags::SUBTOTAL
)
1060 rOutputImpl
.AddCol(nColPos
);
1064 maFormatOutput
.insertFieldMember(nField
, mpColFields
[nField
], nColumn
, rMember
, nColPos
, nRowPos
, sc::FormatResultDirection::COLUMN
);
1066 // Apply the same number format as in data source.
1067 mpDocument
->ApplyAttr(nColPos
, nRowPos
, nTab
, SfxUInt32Item(ATTR_VALUE_FORMAT
, mpColFields
[nField
].mnSrcNumFmt
));
1069 if (nField
== 0 && mpColFields
.size() == 1 && mnMemberStartRow
> mnTabStartRow
)
1070 rOutputImpl
.OutputBlockFrame(mnDataStartCol
, mnTabStartRow
, mnTabEndCol
, nRowPos
- 1);
1074 void ScDPOutput::outputRowHeader(SCTAB nTab
, ScDPOutputImpl
& rOutputImpl
)
1076 std::vector
<bool> vbSetBorder
;
1077 vbSetBorder
.resize(mnTabEndRow
- mnDataStartRow
+ 1, false);
1078 size_t nFieldColOffset
= 0;
1079 size_t nFieldIndentLevel
= 0; // To calculate indent level for fields packed in a column.
1080 size_t nNumRowFields
= mpRowFields
.size();
1081 for (size_t nField
= 0; nField
< nNumRowFields
; nField
++)
1083 const bool bCompactField
= maRowCompactFlags
[nField
];
1084 SCCOL nHdrCol
= mnTabStartCol
+ SCCOL(nField
); //TODO: check for overflow
1085 SCROW nHdrRow
= mnDataStartRow
- 1;
1086 if (!mbHasCompactRowField
|| nNumRowFields
== 1)
1087 FieldCell(nHdrCol
, nHdrRow
, nTab
, mpRowFields
[nField
], true);
1089 MultiFieldCell(nHdrCol
, nHdrRow
, nTab
, true /* bRowField */);
1091 SCCOL nColPos
= mnMemberStartCol
+ SCCOL(nFieldColOffset
); //TODO: check for overflow
1092 const uno::Sequence
<sheet::MemberResult
> rMemberSequence
= mpRowFields
[nField
].maResult
;
1093 const sheet::MemberResult
* pMemberArray
= rMemberSequence
.getConstArray();
1094 sal_Int32 nThisRowCount
= rMemberSequence
.getLength();
1095 OSL_ENSURE(nThisRowCount
== mnRowCount
, "count mismatch"); //TODO: ???
1096 for (sal_Int32 nRow
= 0; nRow
< nThisRowCount
; nRow
++)
1098 sheet::MemberResult
const& rMember
= rMemberSequence
[nRow
];
1099 const sheet::MemberResult
& rData
= rMember
;
1100 const bool bHasMember
= rData
.Flags
& sheet::MemberResultFlags::HASMEMBER
;
1101 const bool bSubtotal
= rData
.Flags
& sheet::MemberResultFlags::SUBTOTAL
;
1102 SCROW nRowPos
= mnDataStartRow
+ SCROW(nRow
); //TODO: check for overflow
1103 HeaderCell( nColPos
, nRowPos
, nTab
, rData
, false, nFieldColOffset
);
1104 if (bHasMember
&& !bSubtotal
)
1106 if (nField
+ 1 < mpRowFields
.size())
1108 tools::Long nEnd
= nRow
;
1109 while (nEnd
+ 1 < nThisRowCount
&& (pMemberArray
[nEnd
+ 1].Flags
& sheet::MemberResultFlags::CONTINUE
))
1113 SCROW nEndRowPos
= mnDataStartRow
+ SCROW(nEnd
); //TODO: check for overflow
1114 rOutputImpl
.AddRow(nRowPos
);
1115 if (!vbSetBorder
[nRow
] )
1117 rOutputImpl
.OutputBlockFrame(nColPos
, nRowPos
, mnTabEndCol
, nEndRowPos
);
1118 vbSetBorder
[nRow
] = true;
1120 rOutputImpl
.OutputBlockFrame(nColPos
, nRowPos
, nColPos
, nEndRowPos
);
1122 if (nField
== mpRowFields
.size() - 2)
1123 rOutputImpl
.OutputBlockFrame(nColPos
+ 1, nRowPos
, nColPos
+ 1, nEndRowPos
);
1125 lcl_SetStyleById(mpDocument
, nTab
, nColPos
, nRowPos
, mnDataStartCol
- 1, nEndRowPos
, STR_PIVOT_STYLENAME_CATEGORY
);
1129 lcl_SetStyleById(mpDocument
, nTab
, nColPos
, nRowPos
, mnDataStartCol
- 1, nRowPos
, STR_PIVOT_STYLENAME_CATEGORY
);
1132 // Set flags for collapse/expand buttons and indent field header text
1134 bool bLast
= mnRowDims
== (nField
+ 1);
1135 size_t nMinIndentLevel
= mbExpandCollapse
? 1 : 0;
1136 tools::Long nIndent
= o3tl::convert(13 * (bLast
? nFieldIndentLevel
: nMinIndentLevel
+ nFieldIndentLevel
), o3tl::Length::px
, o3tl::Length::twip
);
1137 bool bHasContinue
= !bLast
&& nRow
+ 1 < nThisRowCount
&& (pMemberArray
[nRow
+ 1].Flags
& sheet::MemberResultFlags::CONTINUE
);
1139 mpDocument
->ApplyAttr(nColPos
, nRowPos
, nTab
, ScIndentItem(nIndent
));
1140 if (mbExpandCollapse
&& !bLast
)
1142 mpDocument
->ApplyFlagsTab(nColPos
, nRowPos
, nColPos
, nRowPos
, nTab
,
1143 bHasContinue
? ScMF::DpCollapse
: ScMF::DpExpand
);
1149 rOutputImpl
.AddRow(nRowPos
);
1153 maFormatOutput
.insertFieldMember(nField
, mpRowFields
[nField
], nRow
, rMember
, nColPos
, nRowPos
, sc::FormatResultDirection::ROW
);
1155 // Apply the same number format as in data source.
1156 mpDocument
->ApplyAttr(nColPos
, nRowPos
, nTab
, SfxUInt32Item(ATTR_VALUE_FORMAT
, mpRowFields
[nField
].mnSrcNumFmt
));
1161 // Next field should be placed in next column only if current field has a non-compact layout.
1163 nFieldIndentLevel
= 0; // Reset indent level.
1167 ++nFieldIndentLevel
;
1172 void ScDPOutput::outputDataResults(SCTAB nTab
)
1174 const uno::Sequence
<sheet::DataResult
>* pRowAry
= maData
.getConstArray();
1176 for (sal_Int32 nRow
= 0; nRow
< mnRowCount
; nRow
++)
1178 SCROW nRowPos
= mnDataStartRow
+ SCROW(nRow
); //TODO: check for overflow
1179 const sheet::DataResult
* pColAry
= pRowAry
[nRow
].getConstArray();
1180 sal_Int32 nThisColCount
= pRowAry
[nRow
].getLength();
1181 OSL_ENSURE(nThisColCount
== mnColCount
, "count mismatch"); //TODO: ???
1182 for (sal_Int32 nCol
= 0; nCol
< nThisColCount
; nCol
++)
1184 SCCOL nColPos
= mnDataStartCol
+ SCCOL(nCol
); //TODO: check for overflow
1185 DataCell(nColPos
, nRowPos
, nTab
, pColAry
[nCol
]);
1189 maFormatOutput
.apply(*mpDocument
);
1192 void ScDPOutput::Output()
1194 SCTAB nTab
= maStartPos
.Tab();
1196 // calculate output positions and sizes
1199 if (mbSizeOverflow
|| mbResultsError
) // does output area exceed sheet limits?
1202 // Prepare format output
1203 bool bColumnFieldIsDataOnly
= mnColCount
== 1 && mnRowCount
> 0 && mpColFields
.empty();
1204 maFormatOutput
.prepare(nTab
, mpColFields
, mpRowFields
, bColumnFieldIsDataOnly
);
1206 // clear whole (new) output area
1207 // when modifying table, clear old area !
1208 //TODO: include InsertDeleteFlags::OBJECTS ???
1209 mpDocument
->DeleteAreaTab(maStartPos
.Col(), maStartPos
.Row(), mnTabEndCol
, mnTabEndRow
, nTab
, InsertDeleteFlags::ALL
);
1212 lcl_DoFilterButton(mpDocument
, maStartPos
.Col(), maStartPos
.Row(), nTab
);
1214 outputPageFields(nTab
);
1217 // (may get overwritten by first row field)
1219 if (maDataDescription
.isEmpty())
1221 //TODO: use default string ("result") ?
1223 mpDocument
->SetString(mnTabStartCol
, mnTabStartRow
, nTab
, maDataDescription
);
1225 // set STR_PIVOT_STYLENAME_INNER for whole data area (subtotals are overwritten)
1227 if (mnDataStartRow
> mnTabStartRow
)
1228 lcl_SetStyleById(mpDocument
, nTab
, mnTabStartCol
, mnTabStartRow
, mnTabEndCol
, mnDataStartRow
- 1, STR_PIVOT_STYLENAME_TOP
);
1229 lcl_SetStyleById(mpDocument
, nTab
, mnDataStartCol
, mnDataStartRow
, mnTabEndCol
, mnTabEndRow
, STR_PIVOT_STYLENAME_INNER
);
1231 ScDPOutputImpl
aOutputImpl(mpDocument
, nTab
, mnTabStartCol
, mnTabStartRow
,
1232 mnDataStartCol
, mnDataStartRow
, mnTabEndCol
, mnTabEndRow
);
1234 outputColumnHeaders(nTab
, aOutputImpl
);
1236 outputRowHeader(nTab
, aOutputImpl
);
1238 if (bColumnFieldIsDataOnly
)
1240 // the table contains exactly one data field and no column fields.
1241 // Display data description at top right corner.
1242 ScSetStringParam aParam
;
1243 aParam
.setTextInput();
1244 SCCOL nCol
= mnDataStartCol
;
1245 SCCOL nRow
= mnDataStartRow
- 1;
1246 mpDocument
->SetString(nCol
, nRow
, nTab
, maDataDescription
, &aParam
);
1247 maFormatOutput
.insertEmptyDataColumn(nCol
, nRow
);
1250 outputDataResults(nTab
);
1252 aOutputImpl
.OutputDataArea();
1255 ScRange
ScDPOutput::GetOutputRange( sal_Int32 nRegionType
)
1257 using namespace ::com::sun::star::sheet
;
1261 SCTAB nTab
= maStartPos
.Tab();
1262 switch (nRegionType
)
1264 case DataPilotOutputRangeType::RESULT
:
1265 return ScRange(mnDataStartCol
, mnDataStartRow
, nTab
, mnTabEndCol
, mnTabEndRow
, nTab
);
1266 case DataPilotOutputRangeType::TABLE
:
1267 return ScRange(maStartPos
.Col(), mnTabStartRow
, nTab
, mnTabEndCol
, mnTabEndRow
, nTab
);
1269 OSL_ENSURE(nRegionType
== DataPilotOutputRangeType::WHOLE
, "ScDPOutput::GetOutputRange: unknown region type");
1272 return ScRange(maStartPos
.Col(), maStartPos
.Row(), nTab
, mnTabEndCol
, mnTabEndRow
, nTab
);
1275 bool ScDPOutput::HasError()
1279 return mbSizeOverflow
|| mbResultsError
;
1282 sal_Int32
ScDPOutput::GetHeaderRows() const
1284 return mpPageFields
.size() + (mbDoFilter
? 1 : 0);
1289 void insertNames(ScDPUniqueStringSet
& rNames
, const uno::Sequence
<sheet::MemberResult
>& rMemberResults
)
1291 for (const sheet::MemberResult
& rMemberResult
: rMemberResults
)
1293 if (rMemberResult
.Flags
& sheet::MemberResultFlags::HASMEMBER
)
1294 rNames
.insert(rMemberResult
.Name
);
1299 void ScDPOutput::GetMemberResultNames(ScDPUniqueStringSet
& rNames
, tools::Long nDimension
)
1301 // Return the list of all member names in a dimension's MemberResults.
1302 // Only the dimension has to be compared because this is only used with table data,
1303 // where each dimension occurs only once.
1305 auto lFindDimension
= [nDimension
](const ScDPOutLevelData
& rField
) { return rField
.mnDim
== nDimension
; };
1307 // look in column fields
1308 auto colit
= std::find_if(mpColFields
.begin(), mpColFields
.end(), lFindDimension
);
1309 if (colit
!= mpColFields
.end())
1311 // collect the member names
1312 insertNames(rNames
, colit
->maResult
);
1316 // look in row fields
1317 auto rowit
= std::find_if(mpRowFields
.begin(), mpRowFields
.end(), lFindDimension
);
1318 if (rowit
!= mpRowFields
.end())
1320 // collect the member names
1321 insertNames(rNames
, rowit
->maResult
);
1325 void ScDPOutput::SetHeaderLayout(bool bUseGrid
)
1327 mbHeaderLayout
= bUseGrid
;
1328 mbSizesValid
= false;
1333 void lcl_GetTableVars( sal_Int32
& rGrandTotalCols
, sal_Int32
& rGrandTotalRows
, sal_Int32
& rDataLayoutIndex
,
1334 std::vector
<OUString
>& rDataNames
, std::vector
<OUString
>& rGivenNames
,
1335 sheet::DataPilotFieldOrientation
& rDataOrient
,
1336 const uno::Reference
<sheet::XDimensionsSupplier
>& xSource
)
1338 rDataLayoutIndex
= -1; // invalid
1339 rGrandTotalCols
= 0;
1340 rGrandTotalRows
= 0;
1341 rDataOrient
= sheet::DataPilotFieldOrientation_HIDDEN
;
1343 uno::Reference
<beans::XPropertySet
> xSrcProp( xSource
, uno::UNO_QUERY
);
1344 bool bColGrand
= ScUnoHelpFunctions::GetBoolProperty(
1345 xSrcProp
, SC_UNO_DP_COLGRAND
);
1347 rGrandTotalCols
= 1; // default if data layout not in columns
1349 bool bRowGrand
= ScUnoHelpFunctions::GetBoolProperty(
1350 xSrcProp
, SC_UNO_DP_ROWGRAND
);
1352 rGrandTotalRows
= 1; // default if data layout not in rows
1354 if ( !xSource
.is() )
1357 // find index and orientation of "data layout" dimension, count data dimensions
1359 sal_Int32 nDataCount
= 0;
1361 uno::Reference
<container::XIndexAccess
> xDims
= new ScNameToIndexAccess( xSource
->getDimensions() );
1362 tools::Long nDimCount
= xDims
->getCount();
1363 for (tools::Long nDim
=0; nDim
<nDimCount
; nDim
++)
1365 uno::Reference
<uno::XInterface
> xDim(xDims
->getByIndex(nDim
), uno::UNO_QUERY
);
1366 uno::Reference
<beans::XPropertySet
> xDimProp( xDim
, uno::UNO_QUERY
);
1367 if ( xDimProp
.is() )
1369 sheet::DataPilotFieldOrientation eDimOrient
=
1370 ScUnoHelpFunctions::GetEnumProperty(
1371 xDimProp
, SC_UNO_DP_ORIENTATION
,
1372 sheet::DataPilotFieldOrientation_HIDDEN
);
1373 if ( ScUnoHelpFunctions::GetBoolProperty( xDimProp
,
1374 SC_UNO_DP_ISDATALAYOUT
) )
1376 rDataLayoutIndex
= nDim
;
1377 rDataOrient
= eDimOrient
;
1379 if ( eDimOrient
== sheet::DataPilotFieldOrientation_DATA
)
1381 OUString aSourceName
;
1382 OUString aGivenName
;
1383 ScDPOutput::GetDataDimensionNames( aSourceName
, aGivenName
, xDim
);
1386 uno::Any aValue
= xDimProp
->getPropertyValue( SC_UNO_DP_LAYOUTNAME
);
1388 if( aValue
.hasValue() )
1390 OUString strLayoutName
;
1392 if( ( aValue
>>= strLayoutName
) && !strLayoutName
.isEmpty() )
1393 aGivenName
= strLayoutName
;
1396 catch(const uno::Exception
&)
1399 rDataNames
.push_back( aSourceName
);
1400 rGivenNames
.push_back( aGivenName
);
1407 if ( ( rDataOrient
== sheet::DataPilotFieldOrientation_COLUMN
) && bColGrand
)
1408 rGrandTotalCols
= nDataCount
;
1409 else if ( ( rDataOrient
== sheet::DataPilotFieldOrientation_ROW
) && bRowGrand
)
1410 rGrandTotalRows
= nDataCount
;
1415 void ScDPOutput::GetRowFieldRange(SCCOL nCol
, sal_Int32
& nRowFieldStart
, sal_Int32
& nRowFieldEnd
) const
1417 if (!mbHasCompactRowField
)
1419 nRowFieldStart
= nCol
;
1420 nRowFieldEnd
= nCol
+ 1;
1424 if (nCol
>= static_cast<SCCOL
>(maRowCompactFlags
.size()))
1426 nRowFieldStart
= nRowFieldEnd
= 0;
1430 nRowFieldStart
= -1;
1433 sal_Int32 nField
= 0;
1435 for (const auto bCompact
: maRowCompactFlags
)
1437 if (nCurCol
== nCol
&& nRowFieldStart
== -1)
1438 nRowFieldStart
= nField
;
1445 if (nCurCol
== (nCol
+ 1) && nRowFieldStart
!= -1 && nRowFieldEnd
== -1)
1447 nRowFieldEnd
= nField
;
1452 if (nRowFieldStart
!= -1 && nRowFieldEnd
== -1 && nCurCol
== nCol
)
1453 nRowFieldEnd
= static_cast<sal_Int32
>(maRowCompactFlags
.size());
1455 if (nRowFieldStart
== -1 || nRowFieldEnd
== -1)
1457 SAL_WARN("sc.core", "ScDPOutput::GetRowFieldRange : unable to find field range for nCol = " << nCol
);
1458 nRowFieldStart
= nRowFieldEnd
= 0;
1462 sal_Int32
ScDPOutput::GetRowFieldCompact(SCCOL nColQuery
, SCROW nRowQuery
) const
1464 if (!mbHasCompactRowField
)
1465 return nColQuery
- mnTabStartCol
;
1467 SCCOL nCol
= nColQuery
- mnTabStartCol
;
1468 sal_Int32 nStartField
= 0;
1469 sal_Int32 nEndField
= 0;
1470 GetRowFieldRange(nCol
, nStartField
, nEndField
);
1472 for (sal_Int32 nField
= nEndField
- 1; nField
>= nStartField
; --nField
)
1474 const uno::Sequence
<sheet::MemberResult
> rSequence
= mpRowFields
[nField
].maResult
;
1475 const sheet::MemberResult
* pArray
= rSequence
.getConstArray();
1476 sal_Int32 nThisRowCount
= rSequence
.getLength();
1477 SCROW nRow
= nRowQuery
- mnDataStartRow
;
1478 if (nRow
>= 0 && nRow
< nThisRowCount
)
1480 const sheet::MemberResult
& rData
= pArray
[nRow
];
1481 if ((rData
.Flags
& sheet::MemberResultFlags::HASMEMBER
)
1482 && !(rData
.Flags
& sheet::MemberResultFlags::SUBTOTAL
))
1492 void ScDPOutput::GetPositionData(const ScAddress
& rPos
, DataPilotTablePositionData
& rPosData
)
1494 using namespace ::com::sun::star::sheet
;
1496 SCCOL nCol
= rPos
.Col();
1497 SCROW nRow
= rPos
.Row();
1498 SCTAB nTab
= rPos
.Tab();
1499 if (nTab
!= maStartPos
.Tab())
1500 return; // wrong sheet
1502 // calculate output positions and sizes
1506 rPosData
.PositionType
= GetPositionType(rPos
);
1507 switch (rPosData
.PositionType
)
1509 case DataPilotTablePositionType::RESULT
:
1511 vector
<DataPilotFieldFilter
> aFilters
;
1512 GetDataResultPositionData(aFilters
, rPos
);
1514 DataPilotTableResultData aResData
;
1515 aResData
.FieldFilters
= comphelper::containerToSequence(aFilters
);
1516 aResData
.DataFieldIndex
= 0;
1517 Reference
<beans::XPropertySet
> xPropSet(mxSource
, UNO_QUERY
);
1520 sal_Int32 nDataFieldCount
= ScUnoHelpFunctions::GetLongProperty( xPropSet
,
1521 SC_UNO_DP_DATAFIELDCOUNT
);
1522 if (nDataFieldCount
> 0)
1523 aResData
.DataFieldIndex
= (nRow
- mnDataStartRow
) % nDataFieldCount
;
1526 // Copy appropriate DataResult object from the cached sheet::DataResult table.
1527 if (maData
.getLength() > nRow
- mnDataStartRow
&&
1528 maData
[nRow
- mnDataStartRow
].getLength() > nCol
- mnDataStartCol
)
1529 aResData
.Result
= maData
[nRow
- mnDataStartRow
][nCol
- mnDataStartCol
];
1531 rPosData
.PositionData
<<= aResData
;
1534 case DataPilotTablePositionType::COLUMN_HEADER
:
1536 tools::Long nField
= nRow
- mnTabStartRow
- 1; // 1st line is used for the buttons
1540 if (mpColFields
.size() < o3tl::make_unsigned(nField
) + 1 )
1542 const uno::Sequence
<sheet::MemberResult
> rSequence
= mpColFields
[nField
].maResult
;
1543 if (!rSequence
.hasElements())
1545 const sheet::MemberResult
* pArray
= rSequence
.getConstArray();
1547 tools::Long nItem
= nCol
- mnDataStartCol
;
1548 // get origin of "continue" fields
1549 while (nItem
> 0 && ( pArray
[nItem
].Flags
& sheet::MemberResultFlags::CONTINUE
) )
1555 DataPilotTableHeaderData aHeaderData
;
1556 aHeaderData
.MemberName
= pArray
[nItem
].Name
;
1557 aHeaderData
.Flags
= pArray
[nItem
].Flags
;
1558 aHeaderData
.Dimension
= static_cast<sal_Int32
>(mpColFields
[nField
].mnDim
);
1559 aHeaderData
.Hierarchy
= static_cast<sal_Int32
>(mpColFields
[nField
].mnHier
);
1560 aHeaderData
.Level
= static_cast<sal_Int32
>(mpColFields
[nField
].mnLevel
);
1562 rPosData
.PositionData
<<= aHeaderData
;
1565 case DataPilotTablePositionType::ROW_HEADER
:
1567 tools::Long nField
= GetRowFieldCompact(nCol
, nRow
);
1571 if (mpRowFields
.size() < o3tl::make_unsigned(nField
) + 1 )
1573 const uno::Sequence
<sheet::MemberResult
> rSequence
= mpRowFields
[nField
].maResult
;
1574 if (!rSequence
.hasElements())
1576 const sheet::MemberResult
* pArray
= rSequence
.getConstArray();
1578 tools::Long nItem
= nRow
- mnDataStartRow
;
1579 // get origin of "continue" fields
1580 while ( nItem
> 0 && (pArray
[nItem
].Flags
& sheet::MemberResultFlags::CONTINUE
) )
1586 DataPilotTableHeaderData aHeaderData
;
1587 aHeaderData
.MemberName
= pArray
[nItem
].Name
;
1588 aHeaderData
.Flags
= pArray
[nItem
].Flags
;
1589 aHeaderData
.Dimension
= static_cast<sal_Int32
>(mpRowFields
[nField
].mnDim
);
1590 aHeaderData
.Hierarchy
= static_cast<sal_Int32
>(mpRowFields
[nField
].mnHier
);
1591 aHeaderData
.Level
= static_cast<sal_Int32
>(mpRowFields
[nField
].mnLevel
);
1593 rPosData
.PositionData
<<= aHeaderData
;
1599 bool ScDPOutput::GetDataResultPositionData(vector
<sheet::DataPilotFieldFilter
>& rFilters
, const ScAddress
& rPos
)
1601 // Check to make sure there is at least one data field.
1602 Reference
<beans::XPropertySet
> xPropSet(mxSource
, UNO_QUERY
);
1606 sal_Int32 nDataFieldCount
= ScUnoHelpFunctions::GetLongProperty( xPropSet
,
1607 SC_UNO_DP_DATAFIELDCOUNT
);
1608 if (nDataFieldCount
== 0)
1609 // No data field is present in this datapilot table.
1612 // #i111421# use lcl_GetTableVars for correct size of totals and data layout position
1613 sal_Int32 nGrandTotalCols
;
1614 sal_Int32 nGrandTotalRows
;
1615 sal_Int32 nDataLayoutIndex
;
1616 std::vector
<OUString
> aDataNames
;
1617 std::vector
<OUString
> aGivenNames
;
1618 sheet::DataPilotFieldOrientation eDataOrient
;
1619 lcl_GetTableVars( nGrandTotalCols
, nGrandTotalRows
, nDataLayoutIndex
, aDataNames
, aGivenNames
, eDataOrient
, mxSource
);
1621 SCCOL nCol
= rPos
.Col();
1622 SCROW nRow
= rPos
.Row();
1623 SCTAB nTab
= rPos
.Tab();
1624 if (nTab
!= maStartPos
.Tab())
1625 return false; // wrong sheet
1629 // test for data area.
1630 if (nCol
< mnDataStartCol
|| nCol
> mnTabEndCol
|| nRow
< mnDataStartRow
|| nRow
> mnTabEndRow
)
1632 // Cell is outside the data field area.
1636 bool bFilterByCol
= (nCol
<= static_cast<SCCOL
>(mnTabEndCol
- nGrandTotalCols
));
1637 bool bFilterByRow
= (nRow
<= static_cast<SCROW
>(mnTabEndRow
- nGrandTotalRows
));
1640 for (size_t nColField
= 0; nColField
< mpColFields
.size() && bFilterByCol
; ++nColField
)
1642 if (mpColFields
[nColField
].mnDim
== nDataLayoutIndex
)
1643 // There is no sense including the data layout field for filtering.
1646 sheet::DataPilotFieldFilter filter
;
1647 filter
.FieldName
= mpColFields
[nColField
].maName
;
1649 const uno::Sequence
<sheet::MemberResult
> rSequence
= mpColFields
[nColField
].maResult
;
1650 const sheet::MemberResult
* pArray
= rSequence
.getConstArray();
1652 OSL_ENSURE(mnDataStartCol
+ rSequence
.getLength() - 1 == mnTabEndCol
, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
1654 tools::Long nItem
= nCol
- mnDataStartCol
;
1655 // get origin of "continue" fields
1656 while ( nItem
> 0 && (pArray
[nItem
].Flags
& sheet::MemberResultFlags::CONTINUE
) )
1659 filter
.MatchValueName
= pArray
[nItem
].Name
;
1660 rFilters
.push_back(filter
);
1664 for (size_t nRowField
= 0; nRowField
< mpRowFields
.size() && bFilterByRow
; ++nRowField
)
1666 if (mpRowFields
[nRowField
].mnDim
== nDataLayoutIndex
)
1667 // There is no sense including the data layout field for filtering.
1670 sheet::DataPilotFieldFilter filter
;
1671 filter
.FieldName
= mpRowFields
[nRowField
].maName
;
1673 const uno::Sequence
<sheet::MemberResult
> rSequence
= mpRowFields
[nRowField
].maResult
;
1674 const sheet::MemberResult
* pArray
= rSequence
.getConstArray();
1676 OSL_ENSURE(mnDataStartRow
+ rSequence
.getLength() - 1 == mnTabEndRow
, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
1678 tools::Long nItem
= nRow
- mnDataStartRow
;
1679 // get origin of "continue" fields
1680 while ( nItem
> 0 && (pArray
[nItem
].Flags
& sheet::MemberResultFlags::CONTINUE
) )
1683 filter
.MatchValueName
= pArray
[nItem
].Name
;
1684 rFilters
.push_back(filter
);
1692 OUString
lcl_GetDataFieldName( std::u16string_view rSourceName
, sal_Int16 eFunc
)
1697 case sheet::GeneralFunction2::SUM
: pStrId
= STR_FUN_TEXT_SUM
; break;
1698 case sheet::GeneralFunction2::COUNT
:
1699 case sheet::GeneralFunction2::COUNTNUMS
: pStrId
= STR_FUN_TEXT_COUNT
; break;
1700 case sheet::GeneralFunction2::AVERAGE
: pStrId
= STR_FUN_TEXT_AVG
; break;
1701 case sheet::GeneralFunction2::MEDIAN
: pStrId
= STR_FUN_TEXT_MEDIAN
; break;
1702 case sheet::GeneralFunction2::MAX
: pStrId
= STR_FUN_TEXT_MAX
; break;
1703 case sheet::GeneralFunction2::MIN
: pStrId
= STR_FUN_TEXT_MIN
; break;
1704 case sheet::GeneralFunction2::PRODUCT
: pStrId
= STR_FUN_TEXT_PRODUCT
; break;
1705 case sheet::GeneralFunction2::STDEV
:
1706 case sheet::GeneralFunction2::STDEVP
: pStrId
= STR_FUN_TEXT_STDDEV
; break;
1707 case sheet::GeneralFunction2::VAR
:
1708 case sheet::GeneralFunction2::VARP
: pStrId
= STR_FUN_TEXT_VAR
; break;
1709 case sheet::GeneralFunction2::NONE
:
1710 case sheet::GeneralFunction2::AUTO
: break;
1719 return ScResId(pStrId
) + " - " + rSourceName
;
1724 void ScDPOutput::GetDataDimensionNames(
1725 OUString
& rSourceName
, OUString
& rGivenName
, const uno::Reference
<uno::XInterface
>& xDim
)
1727 uno::Reference
<beans::XPropertySet
> xDimProp( xDim
, uno::UNO_QUERY
);
1728 uno::Reference
<container::XNamed
> xDimName( xDim
, uno::UNO_QUERY
);
1729 if ( !(xDimProp
.is() && xDimName
.is()) )
1732 // Asterisks are added in ScDPSaveData::WriteToSource to create unique names.
1733 //TODO: preserve original name there?
1734 rSourceName
= ScDPUtil::getSourceDimensionName(xDimName
->getName());
1736 // Generate "given name" the same way as in dptabres.
1737 //TODO: Should use a stored name when available
1739 sal_Int16 eFunc
= ScUnoHelpFunctions::GetShortProperty(
1740 xDimProp
, SC_UNO_DP_FUNCTION2
,
1741 sheet::GeneralFunction2::NONE
);
1742 rGivenName
= lcl_GetDataFieldName( rSourceName
, eFunc
);
1745 bool ScDPOutput::IsFilterButton( const ScAddress
& rPos
)
1747 SCCOL nCol
= rPos
.Col();
1748 SCROW nRow
= rPos
.Row();
1749 SCTAB nTab
= rPos
.Tab();
1750 if (nTab
!= maStartPos
.Tab() || !mbDoFilter
)
1751 return false; // wrong sheet or no button at all
1753 // filter button is at top left
1754 return nCol
== maStartPos
.Col() && nRow
== maStartPos
.Row();
1757 tools::Long
ScDPOutput::GetHeaderDim( const ScAddress
& rPos
, sheet::DataPilotFieldOrientation
& rOrient
)
1759 SCCOL nCol
= rPos
.Col();
1760 SCROW nRow
= rPos
.Row();
1761 SCTAB nTab
= rPos
.Tab();
1762 if (nTab
!= maStartPos
.Tab())
1763 return -1; // wrong sheet
1765 // calculate output positions and sizes
1769 // test for column header
1771 if ( nRow
== mnTabStartRow
&& nCol
>= mnDataStartCol
&& o3tl::make_unsigned(nCol
) < mnDataStartCol
+ mpColFields
.size())
1773 rOrient
= sheet::DataPilotFieldOrientation_COLUMN
;
1774 tools::Long nField
= nCol
- mnDataStartCol
;
1775 return mpColFields
[nField
].mnDim
;
1778 // test for row header
1780 if ( nRow
+1 == mnDataStartRow
&& nCol
>= mnTabStartCol
&& o3tl::make_unsigned(nCol
) < mnTabStartCol
+ mpRowFields
.size() )
1782 rOrient
= sheet::DataPilotFieldOrientation_ROW
;
1783 tools::Long nField
= nCol
- mnTabStartCol
;
1784 return mpRowFields
[nField
].mnDim
;
1787 // test for page field
1789 SCROW nPageStartRow
= maStartPos
.Row() + (mbDoFilter
? 1 : 0);
1790 if ( nCol
== maStartPos
.Col() && nRow
>= nPageStartRow
&& o3tl::make_unsigned(nRow
) < nPageStartRow
+ mpPageFields
.size() )
1792 rOrient
= sheet::DataPilotFieldOrientation_PAGE
;
1793 tools::Long nField
= nRow
- nPageStartRow
;
1794 return mpPageFields
[nField
].mnDim
;
1797 //TODO: single data field (?)
1799 rOrient
= sheet::DataPilotFieldOrientation_HIDDEN
;
1800 return -1; // invalid
1803 bool ScDPOutput::GetHeaderDrag( const ScAddress
& rPos
, bool bMouseLeft
, bool bMouseTop
,
1804 tools::Long nDragDim
,
1805 tools::Rectangle
& rPosRect
, sheet::DataPilotFieldOrientation
& rOrient
, tools::Long
& rDimPos
)
1807 // Rectangle instead of ScRange for rPosRect to allow for negative values
1809 SCCOL nCol
= rPos
.Col();
1810 SCROW nRow
= rPos
.Row();
1811 SCTAB nTab
= rPos
.Tab();
1812 if ( nTab
!= maStartPos
.Tab() )
1813 return false; // wrong sheet
1815 // calculate output positions and sizes
1819 // test for column header
1821 if ( nCol
>= mnDataStartCol
&& nCol
<= mnTabEndCol
&&
1822 nRow
+ 1 >= mnMemberStartRow
&& o3tl::make_unsigned(nRow
) < mnMemberStartRow
+ mpColFields
.size())
1824 tools::Long nField
= nRow
- mnMemberStartRow
;
1830 //TODO: find start of dimension
1832 rPosRect
= tools::Rectangle(mnDataStartCol
, mnMemberStartRow
+ nField
,
1833 mnTabEndCol
, mnMemberStartRow
+ nField
- 1);
1835 bool bFound
= false; // is this within the same orientation?
1836 bool bBeforeDrag
= false;
1837 bool bAfterDrag
= false;
1838 for (tools::Long nPos
=0; o3tl::make_unsigned(nPos
)<mpColFields
.size() && !bFound
; nPos
++)
1840 if (mpColFields
[nPos
].mnDim
== nDragDim
)
1843 if ( nField
< nPos
)
1845 else if ( nField
> nPos
)
1854 rPosRect
.AdjustBottom( 1 );
1856 rPosRect
.AdjustTop( 1 );
1863 rPosRect
.AdjustTop( 1 );
1864 rPosRect
.AdjustBottom( 1 );
1869 rOrient
= sheet::DataPilotFieldOrientation_COLUMN
;
1870 rDimPos
= nField
; //!...
1874 // test for row header
1876 // special case if no row fields
1877 bool bSpecial
= ( nRow
+1 >= mnDataStartRow
&& nRow
<= mnTabEndRow
&&
1878 mpRowFields
.empty() && nCol
== mnTabStartCol
&& bMouseLeft
);
1880 if ( bSpecial
|| ( nRow
+1 >= mnDataStartRow
&& nRow
<= mnTabEndRow
&&
1881 nCol
+ 1 >= mnTabStartCol
&& o3tl::make_unsigned(nCol
) < mnTabStartCol
+ mpRowFields
.size() ) )
1883 tools::Long nField
= nCol
- mnTabStartCol
;
1884 //TODO: find start of dimension
1886 rPosRect
= tools::Rectangle(mnTabStartCol
+ nField
, mnDataStartRow
- 1,
1887 mnTabStartCol
+ nField
- 1, mnTabEndRow
);
1889 bool bFound
= false; // is this within the same orientation?
1890 bool bBeforeDrag
= false;
1891 bool bAfterDrag
= false;
1892 for (tools::Long nPos
= 0; o3tl::make_unsigned(nPos
) < mpRowFields
.size() && !bFound
; nPos
++)
1894 if (mpRowFields
[nPos
].mnDim
== nDragDim
)
1897 if ( nField
< nPos
)
1899 else if ( nField
> nPos
)
1908 rPosRect
.AdjustRight( 1 );
1910 rPosRect
.AdjustLeft( 1 );
1917 rPosRect
.AdjustLeft( 1 );
1918 rPosRect
.AdjustRight( 1 );
1923 rOrient
= sheet::DataPilotFieldOrientation_ROW
;
1924 rDimPos
= nField
; //!...
1928 // test for page fields
1930 SCROW nPageStartRow
= maStartPos
.Row() + (mbDoFilter
? 1 : 0);
1931 if (nCol
>= maStartPos
.Col() && nCol
<= mnTabEndCol
&&
1932 nRow
+ 1 >= nPageStartRow
&& o3tl::make_unsigned(nRow
) < nPageStartRow
+ mpPageFields
.size())
1934 tools::Long nField
= nRow
- nPageStartRow
;
1940 //TODO: find start of dimension
1942 rPosRect
= tools::Rectangle(maStartPos
.Col(), nPageStartRow
+ nField
,
1943 mnTabEndCol
, nPageStartRow
+ nField
- 1);
1945 bool bFound
= false; // is this within the same orientation?
1946 bool bBeforeDrag
= false;
1947 bool bAfterDrag
= false;
1948 for (tools::Long nPos
= 0; o3tl::make_unsigned(nPos
) < mpPageFields
.size() && !bFound
; nPos
++)
1950 if (mpPageFields
[nPos
].mnDim
== nDragDim
)
1953 if ( nField
< nPos
)
1955 else if ( nField
> nPos
)
1964 rPosRect
.AdjustBottom( 1 );
1966 rPosRect
.AdjustTop( 1 );
1973 rPosRect
.AdjustTop( 1 );
1974 rPosRect
.AdjustBottom( 1 );
1979 rOrient
= sheet::DataPilotFieldOrientation_PAGE
;
1980 rDimPos
= nField
; //!...
1987 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */