Gtk-WARNING gtktreestore.c:1047: Invalid column number 1 added to iter
[LibreOffice.git] / sc / source / core / data / dpoutput.cxx
blob1c6a0c102438fe259d69b02901c2d14b36a4b8bf
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
34 #include <attrib.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>
44 #include <dputil.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>
67 #include <limits>
68 #include <string_view>
69 #include <utility>
70 #include <vector>
71 #include <iostream>
73 using namespace com::sun::star;
74 using ::std::vector;
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 )
87 namespace
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
99 class ScDPOutputImpl
101 ScDocument* mpDoc;
102 sal_uInt16 mnTab;
103 ::std::vector< bool > mbNeedLineCols;
104 ::std::vector< SCCOL > mnCols;
106 ::std::vector< bool > mbNeedLineRows;
107 ::std::vector< SCROW > mnRows;
109 SCCOL mnTabStartCol;
110 SCROW mnTabStartRow;
112 SCCOL mnDataStartCol;
113 SCROW mnDataStartRow;
114 SCCOL mnTabEndCol;
115 SCROW mnTabEndRow;
117 public:
118 ScDPOutputImpl( ScDocument* pDoc, sal_uInt16 nTab,
119 SCCOL nTabStartCol,
120 SCROW nTabStartRow,
121 SCCOL nDataStartCol,
122 SCROW nDataStartRow,
123 SCCOL nTabEndCol,
124 SCROW nTabEndRow );
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 ++ )
148 if ( !bAllRows )
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 );
157 else
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 );
163 else
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,
178 SCCOL nTabStartCol,
179 SCROW nTabStartRow,
180 SCCOL nDataStartCol,
181 SCROW nDataStartRow,
182 SCCOL nTabEndCol,
183 SCROW nTabEndRow ):
184 mpDoc( pDoc ),
185 mnTab( 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 );
204 return true;
206 else
207 return false;
210 bool ScDPOutputImpl::AddCol( SCCOL nCol )
213 if ( !mbNeedLineCols[ nCol - mnDataStartCol ] )
215 mbNeedLineCols[ nCol - mnDataStartCol ] = true;
216 mnCols.push_back( nCol );
217 return true;
219 else
220 return false;
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);
233 else
234 aBox.SetLine(&aLine, SvxBoxItemLine::LEFT);
236 if ( nStartRow == mnTabStartRow )
237 aBox.SetLine(&aOutLine, SvxBoxItemLine::TOP);
238 else
239 aBox.SetLine(&aLine, SvxBoxItemLine::TOP);
241 if ( nEndCol == mnTabEndCol ) //bottom row
242 aBox.SetLine(&aOutLine, SvxBoxItemLine::RIGHT);
243 else
244 aBox.SetLine(&aLine, SvxBoxItemLine::RIGHT);
246 if ( nEndRow == mnTabEndRow ) //bottom
247 aBox.SetLine(&aOutLine, SvxBoxItemLine::BOTTOM);
248 else
249 aBox.SetLine(&aLine, SvxBoxItemLine::BOTTOM);
251 SvxBoxInfoItem aBoxInfo( ATTR_BORDER_INNER );
252 aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::VERT,false );
253 if ( bHori )
255 aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI);
256 aBoxInfo.SetLine( &aLine, SvxBoxInfoItemLine::HORI );
258 else
259 aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI,false );
261 aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::DISTANCE,false);
263 mpDoc->ApplyFrameAreaTab(ScRange(nStartCol, nStartRow, mnTab, nEndCol, nEndRow , mnTab), aBox, aBoxInfo);
267 namespace
270 void lcl_SetStyleById(ScDocument* pDoc, SCTAB nTab,
271 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
272 TranslateId pStrId)
274 if ( nCol1 > nCol2 || nRow1 > nRow2 )
276 OSL_FAIL("SetStyleById: invalid range");
277 return;
280 OUString aStyleName = ScResId(pStrId);
281 ScStyleSheetPool* pStlPool = pDoc->GetStyleSheetPool();
282 ScStyleSheet* pStyle = static_cast<ScStyleSheet*>( pStlPool->Find( aStyleName, SfxStyleFamily::Para ) );
283 if (!pStyle)
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,
305 sal_uInt16 nWidth )
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 )
325 if ( rFormats )
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();
334 if (!nSize)
335 return;
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();
343 sal_Int32 nDim = 0;
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(
359 xDimProp,
360 SC_UNONAME_NUMFMT );
361 aDataFormats.push_back(nFormat);
366 if (aDataFormats.empty())
367 return;
369 const sheet::MemberResult* pArray = aResult.getConstArray();
371 OUString aName;
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;
380 else
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];
394 break;
396 pNumFmt[nPos] = nFormat;
400 rFormats.reset( pNumFmt );
401 rCount = nSize;
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);
410 if ( xDimProp.is() )
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(
419 xDimProp,
420 SC_UNONAME_NUMFMT );
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 )
445 if (!xLevel.is())
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();
453 if (!xNA.is())
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);
463 if (!xMemPS.is())
464 continue;
466 OUString aCaption = ScUnoHelpFunctions::GetStringProperty(xMemPS, SC_UNO_DP_LAYOUTNAME, OUString());
467 if (aCaption.isEmpty())
468 aCaption = rName;
470 bool bVisible = ScUnoHelpFunctions::GetBoolProperty(xMemPS, SC_UNO_DP_ISVISIBLE);
472 if (bVisible)
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)
497 , mnRowDims(0)
498 , mnColCount(0)
499 , mnRowCount(0)
500 , mnHeaderSize(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(
546 xDimProp,
547 SC_UNO_DP_USEDHIERARCHY );
548 if ( nHierarchy >= xHiers->getCount() )
549 nHierarchy = 0;
551 uno::Reference<sheet::XLevelsSupplier> xHierSupp(xHiers->getByIndex(nHierarchy),
552 uno::UNO_QUERY);
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),
561 uno::UNO_QUERY);
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);
586 break;
587 case sheet::DataPilotFieldOrientation_ROW:
589 uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults();
590 ++mnRowDims;
591 // We want only to remove the DATA column if it is empty
592 // and not any other empty columns (to still show the
593 // header columns)
594 bool bSkip = lcl_MemberEmpty(aResult) && bIsDataLayout;
595 if (!bSkip)
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;
614 break;
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);
622 break;
623 default:
625 // added to avoid warnings
629 // get number formats from data dimensions
630 if ( bIsDataLayout )
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());
655 // get data results:
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() )
671 return;
675 uno::Any aAny = xSrcProp->getPropertyValue( SC_UNO_DP_DATADESC );
676 OUString aUStr;
677 aAny >>= aUStr;
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];
719 bApplyFormat = true;
723 else if (mpRowNumberFormat)
725 if (nRow >= mnDataStartRow)
727 tools::Long nIndex = nRow - mnDataStartRow;
728 if (nIndex < mnRowFormatCount)
730 nFormat = mpRowNumberFormat[nIndex];
731 bApplyFormat = true;
735 else if (mnSingleNumberFormat != 0)
737 nFormat = mnSingleNumberFormat; // single format is used everywhere
738 bApplyFormat = true;
741 if (bApplyFormat)
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);
759 else
761 ScSetStringParam aParam;
762 if (bNumeric)
763 aParam.setNumericInput();
764 else
765 aParam.setTextInput();
767 mpDocument->SetString(nCol, nRow, nTab, rData.Caption, &aParam);
771 if ( !(nFlags & sheet::MemberResultFlags::SUBTOTAL) )
772 return;
774 ScDPOutputImpl outputimp(mpDocument, nTab,
775 mnTabStartCol, mnTabStartRow,
776 mnDataStartCol, mnDataStartRow, mnTabEndCol, mnTabEndRow);
777 //TODO: limit frames to horizontal or vertical?
778 if (bColHeader)
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 );
785 else
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;
803 break;
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);
823 if (bInTable)
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;
831 if (rData.mbPageDim)
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);
837 else
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());
859 SCCOL nNum = 0;
860 for (const auto bCompact: maRowCompactFlags)
861 if (!bCompact)
862 ++nNum;
864 if (maRowCompactFlags.back())
865 ++nNum;
867 return nNum;
870 void ScDPOutput::CalcSizes()
872 if (mbSizesValid)
873 return;
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;
882 mnHeaderSize = 1;
883 if (mbHideHeader)
884 mnHeaderSize = 0;
885 else if (GetHeaderLayout() && mpColFields.empty())
886 // Insert an extra header row only when there is no column field.
887 mnHeaderSize = 2;
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
895 if (mbDoFilter)
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());
911 if (mnColCount > 0)
912 mnTabEndCol = mnDataStartCol + static_cast<SCCOL>(mnColCount) - 1;
913 else
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;
918 if (mnRowCount > 0)
919 mnTabEndRow = mnDataStartRow + static_cast<SCROW>(mnRowCount) - 1;
920 else
921 mnTabEndRow = mnDataStartRow; // single row will remain empty
922 mbSizesValid = true;
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;
935 CalcSizes();
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;
952 if (bInColHeader)
954 if (nRow == mnTabStartRow)
955 // first row in the column header area is always used for column
956 // field buttons.
957 return DataPilotTablePositionType::OTHER;
959 return DataPilotTablePositionType::COLUMN_HEADER;
962 if (bInRowHeader)
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();
981 if (n == 1)
983 if (rRes[0].Caption.isEmpty())
984 aPageValue = ScResId(STR_EMPTYDATA);
985 else
986 aPageValue = rRes[0].Caption;
988 else if (n > 1)
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);
1013 else if (!nField)
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))
1037 ++nEnd;
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);
1048 else
1049 rOutputImpl.OutputBlockFrame(nColPos, nRowPos, nEndColPos, nRowPos);
1051 lcl_SetStyleById(mpDocument, nTab, nColPos, nRowPos, nEndColPos, mnDataStartRow - 1, STR_PIVOT_STYLENAME_CATEGORY);
1053 else
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);
1063 // Resolve formats
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);
1088 else if (!nField)
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))
1111 ++nEnd;
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);
1127 else
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);
1138 if (nIndent)
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);
1147 else if (bSubtotal)
1149 rOutputImpl.AddRow(nRowPos);
1152 // Resolve formats
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));
1159 if (!bCompactField)
1161 // Next field should be placed in next column only if current field has a non-compact layout.
1162 ++nFieldColOffset;
1163 nFieldIndentLevel = 0; // Reset indent level.
1165 else
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
1197 CalcSizes();
1199 if (mbSizeOverflow || mbResultsError) // does output area exceed sheet limits?
1200 return; // nothing
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 );
1211 if (mbDoFilter)
1212 lcl_DoFilterButton(mpDocument, maStartPos.Col(), maStartPos.Row(), nTab);
1214 outputPageFields(nTab);
1216 // data description
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;
1259 CalcSizes();
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);
1268 default:
1269 OSL_ENSURE(nRegionType == DataPilotOutputRangeType::WHOLE, "ScDPOutput::GetOutputRange: unknown region type");
1270 break;
1272 return ScRange(maStartPos.Col(), maStartPos.Row(), nTab, mnTabEndCol, mnTabEndRow, nTab);
1275 bool ScDPOutput::HasError()
1277 CalcSizes();
1279 return mbSizeOverflow || mbResultsError;
1282 sal_Int32 ScDPOutput::GetHeaderRows() const
1284 return mpPageFields.size() + (mbDoFilter ? 1 : 0);
1287 namespace
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);
1313 return;
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;
1331 namespace {
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);
1346 if ( bColGrand )
1347 rGrandTotalCols = 1; // default if data layout not in columns
1349 bool bRowGrand = ScUnoHelpFunctions::GetBoolProperty(
1350 xSrcProp, SC_UNO_DP_ROWGRAND);
1351 if ( bRowGrand )
1352 rGrandTotalRows = 1; // default if data layout not in rows
1354 if ( !xSource.is() )
1355 return;
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 );
1402 ++nDataCount;
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;
1421 return;
1424 if (nCol >= static_cast<SCCOL>(maRowCompactFlags.size()))
1426 nRowFieldStart = nRowFieldEnd = 0;
1427 return;
1430 nRowFieldStart = -1;
1431 nRowFieldEnd = -1;
1432 SCCOL nCurCol = 0;
1433 sal_Int32 nField = 0;
1435 for (const auto bCompact: maRowCompactFlags)
1437 if (nCurCol == nCol && nRowFieldStart == -1)
1438 nRowFieldStart = nField;
1440 if (!bCompact)
1441 ++nCurCol;
1443 ++nField;
1445 if (nCurCol == (nCol + 1) && nRowFieldStart != -1 && nRowFieldEnd == -1)
1447 nRowFieldEnd = nField;
1448 break;
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))
1484 return nField;
1489 return -1;
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
1504 CalcSizes();
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);
1518 if (xPropSet.is())
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;
1532 return;
1534 case DataPilotTablePositionType::COLUMN_HEADER:
1536 tools::Long nField = nRow - mnTabStartRow - 1; // 1st line is used for the buttons
1537 if (nField < 0)
1538 break;
1540 if (mpColFields.size() < o3tl::make_unsigned(nField) + 1 )
1541 break;
1542 const uno::Sequence<sheet::MemberResult> rSequence = mpColFields[nField].maResult;
1543 if (!rSequence.hasElements())
1544 break;
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) )
1550 --nItem;
1552 if (nItem < 0)
1553 break;
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;
1563 return;
1565 case DataPilotTablePositionType::ROW_HEADER:
1567 tools::Long nField = GetRowFieldCompact(nCol, nRow);
1568 if (nField < 0)
1569 break;
1571 if (mpRowFields.size() < o3tl::make_unsigned(nField) + 1 )
1572 break;
1573 const uno::Sequence<sheet::MemberResult> rSequence = mpRowFields[nField].maResult;
1574 if (!rSequence.hasElements())
1575 break;
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) )
1581 --nItem;
1583 if (nItem < 0)
1584 break;
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;
1594 return;
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);
1603 if (!xPropSet.is())
1604 return false;
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.
1610 return false;
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
1627 CalcSizes();
1629 // test for data area.
1630 if (nCol < mnDataStartCol || nCol > mnTabEndCol || nRow < mnDataStartRow || nRow > mnTabEndRow)
1632 // Cell is outside the data field area.
1633 return false;
1636 bool bFilterByCol = (nCol <= static_cast<SCCOL>(mnTabEndCol - nGrandTotalCols));
1637 bool bFilterByRow = (nRow <= static_cast<SCROW>(mnTabEndRow - nGrandTotalRows));
1639 // column fields
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.
1644 continue;
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) )
1657 --nItem;
1659 filter.MatchValueName = pArray[nItem].Name;
1660 rFilters.push_back(filter);
1663 // row fields
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.
1668 continue;
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) )
1681 --nItem;
1683 filter.MatchValueName = pArray[nItem].Name;
1684 rFilters.push_back(filter);
1687 return true;
1690 namespace {
1692 OUString lcl_GetDataFieldName( std::u16string_view rSourceName, sal_Int16 eFunc )
1694 TranslateId pStrId;
1695 switch ( 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;
1711 default:
1713 assert(false);
1716 if (!pStrId)
1717 return OUString();
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()) )
1730 return;
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
1767 CalcSizes();
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
1817 CalcSizes();
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;
1825 if (nField < 0)
1827 nField = 0;
1828 bMouseTop = true;
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)
1842 bFound = true;
1843 if ( nField < nPos )
1844 bBeforeDrag = true;
1845 else if ( nField > nPos )
1846 bAfterDrag = true;
1850 if ( bFound )
1852 if (!bBeforeDrag)
1854 rPosRect.AdjustBottom( 1 );
1855 if (bAfterDrag)
1856 rPosRect.AdjustTop( 1 );
1859 else
1861 if ( !bMouseTop )
1863 rPosRect.AdjustTop( 1 );
1864 rPosRect.AdjustBottom( 1 );
1865 ++nField;
1869 rOrient = sheet::DataPilotFieldOrientation_COLUMN;
1870 rDimPos = nField; //!...
1871 return true;
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)
1896 bFound = true;
1897 if ( nField < nPos )
1898 bBeforeDrag = true;
1899 else if ( nField > nPos )
1900 bAfterDrag = true;
1904 if ( bFound )
1906 if (!bBeforeDrag)
1908 rPosRect.AdjustRight( 1 );
1909 if (bAfterDrag)
1910 rPosRect.AdjustLeft( 1 );
1913 else
1915 if ( !bMouseLeft )
1917 rPosRect.AdjustLeft( 1 );
1918 rPosRect.AdjustRight( 1 );
1919 ++nField;
1923 rOrient = sheet::DataPilotFieldOrientation_ROW;
1924 rDimPos = nField; //!...
1925 return true;
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;
1935 if (nField < 0)
1937 nField = 0;
1938 bMouseTop = true;
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)
1952 bFound = true;
1953 if ( nField < nPos )
1954 bBeforeDrag = true;
1955 else if ( nField > nPos )
1956 bAfterDrag = true;
1960 if ( bFound )
1962 if (!bBeforeDrag)
1964 rPosRect.AdjustBottom( 1 );
1965 if (bAfterDrag)
1966 rPosRect.AdjustTop( 1 );
1969 else
1971 if ( !bMouseTop )
1973 rPosRect.AdjustTop( 1 );
1974 rPosRect.AdjustBottom( 1 );
1975 ++nField;
1979 rOrient = sheet::DataPilotFieldOrientation_PAGE;
1980 rDimPos = nField; //!...
1981 return true;
1984 return false;
1987 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */