summaryrefslogtreecommitdiffstats
path: root/sc/source/core/data/dpoutput.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/core/data/dpoutput.cxx')
-rw-r--r--sc/source/core/data/dpoutput.cxx1781
1 files changed, 1781 insertions, 0 deletions
diff --git a/sc/source/core/data/dpoutput.cxx b/sc/source/core/data/dpoutput.cxx
new file mode 100644
index 000000000..bf2109b30
--- /dev/null
+++ b/sc/source/core/data/dpoutput.cxx
@@ -0,0 +1,1781 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+
+#include <comphelper/sequence.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <svl/itemset.hxx>
+
+#include <dpoutput.hxx>
+#include <document.hxx>
+#include <attrib.hxx>
+#include <formula/errorcodes.hxx>
+#include <miscuno.hxx>
+#include <globstr.hrc>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <scresid.hxx>
+#include <unonames.hxx>
+#include <strings.hrc>
+#include <stringutil.hxx>
+#include <dputil.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <com/sun/star/sheet/DataPilotTablePositionData.hpp>
+#include <com/sun/star/sheet/DataPilotTableResultData.hpp>
+#include <com/sun/star/sheet/MemberResultFlags.hpp>
+#include <com/sun/star/sheet/DataResultFlags.hpp>
+#include <com/sun/star/sheet/DataPilotTablePositionType.hpp>
+#include <com/sun/star/sheet/GeneralFunction2.hpp>
+#include <com/sun/star/sheet/MemberResult.hpp>
+#include <com/sun/star/sheet/XDataPilotMemberResults.hpp>
+#include <com/sun/star/sheet/XDataPilotResults.hpp>
+#include <com/sun/star/sheet/XDimensionsSupplier.hpp>
+#include <com/sun/star/sheet/XHierarchiesSupplier.hpp>
+#include <com/sun/star/sheet/XLevelsSupplier.hpp>
+#include <com/sun/star/sheet/XMembersAccess.hpp>
+#include <com/sun/star/sheet/XMembersSupplier.hpp>
+
+#include <limits>
+#include <string_view>
+#include <vector>
+
+using namespace com::sun::star;
+using ::std::vector;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::sheet::DataPilotTablePositionData;
+using ::com::sun::star::sheet::DataPilotTableResultData;
+
+#define SC_DP_FRAME_INNER_BOLD 20
+#define SC_DP_FRAME_OUTER_BOLD 40
+
+#define SC_DP_FRAME_COLOR Color(0,0,0) //( 0x20, 0x40, 0x68 )
+
+struct ScDPOutLevelData
+{
+ tools::Long mnDim;
+ tools::Long mnHier;
+ tools::Long mnLevel;
+ tools::Long mnDimPos;
+ sal_uInt32 mnSrcNumFmt; /// Prevailing number format used in the source data.
+ uno::Sequence<sheet::MemberResult> maResult;
+ OUString maName; /// Name is the internal field name.
+ OUString maCaption; /// Caption is the name visible in the output table.
+ bool mbHasHiddenMember:1;
+ bool mbDataLayout:1;
+ bool mbPageDim:1;
+
+ ScDPOutLevelData(tools::Long nDim, tools::Long nHier, tools::Long nLevel, tools::Long nDimPos, sal_uInt32 nSrcNumFmt, const uno::Sequence<sheet::MemberResult> &aResult,
+ const OUString &aName, const OUString &aCaption, bool bHasHiddenMember, bool bDataLayout, bool bPageDim) :
+ mnDim(nDim), mnHier(nHier), mnLevel(nLevel), mnDimPos(nDimPos), mnSrcNumFmt(nSrcNumFmt), maResult(aResult),
+ maName(aName), maCaption(aCaption), mbHasHiddenMember(bHasHiddenMember), mbDataLayout(bDataLayout),
+ mbPageDim(bPageDim)
+ {
+ }
+
+ // bug (73840) in uno::Sequence - copy and then assign doesn't work!
+};
+
+
+
+namespace {
+ struct ScDPOutLevelDataComparator
+ {
+ bool operator()(const ScDPOutLevelData & rA, const ScDPOutLevelData & rB)
+ {
+ return rA.mnDimPos<rB.mnDimPos || ( rA.mnDimPos==rB.mnDimPos && rA.mnHier<rB.mnHier ) ||
+ ( rA.mnDimPos==rB.mnDimPos && rA.mnHier==rB.mnHier && rA.mnLevel<rB.mnLevel );
+ }
+ };
+
+
+class ScDPOutputImpl
+{
+ ScDocument* mpDoc;
+ sal_uInt16 mnTab;
+ ::std::vector< bool > mbNeedLineCols;
+ ::std::vector< SCCOL > mnCols;
+
+ ::std::vector< bool > mbNeedLineRows;
+ ::std::vector< SCROW > mnRows;
+
+ SCCOL mnTabStartCol;
+ SCROW mnTabStartRow;
+
+ SCCOL mnDataStartCol;
+ SCROW mnDataStartRow;
+ SCCOL mnTabEndCol;
+ SCROW mnTabEndRow;
+
+public:
+ ScDPOutputImpl( ScDocument* pDoc, sal_uInt16 nTab,
+ SCCOL nTabStartCol,
+ SCROW nTabStartRow,
+ SCCOL nDataStartCol,
+ SCROW nDataStartRow,
+ SCCOL nTabEndCol,
+ SCROW nTabEndRow );
+ bool AddRow( SCROW nRow );
+ bool AddCol( SCCOL nCol );
+
+ void OutputDataArea();
+ void OutputBlockFrame ( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, bool bHori = false );
+
+};
+
+void ScDPOutputImpl::OutputDataArea()
+{
+ AddRow( mnDataStartRow );
+ AddCol( mnDataStartCol );
+
+ mnCols.push_back( mnTabEndCol+1); //set last row bottom
+ mnRows.push_back( mnTabEndRow+1); //set last col bottom
+
+ bool bAllRows = ( ( mnTabEndRow - mnDataStartRow + 2 ) == static_cast<SCROW>(mnRows.size()) );
+
+ std::sort( mnCols.begin(), mnCols.end());
+ std::sort( mnRows.begin(), mnRows.end());
+
+ for( SCCOL nCol = 0; nCol < static_cast<SCCOL>(mnCols.size())-1; nCol ++ )
+ {
+ if ( !bAllRows )
+ {
+ if ( nCol < static_cast<SCCOL>(mnCols.size())-2)
+ {
+ for ( SCROW i = nCol%2; i < static_cast<SCROW>(mnRows.size())-2; i +=2 )
+ OutputBlockFrame( mnCols[nCol], mnRows[i], mnCols[nCol+1]-1, mnRows[i+1]-1 );
+ if ( mnRows.size()>=2 )
+ OutputBlockFrame( mnCols[nCol], mnRows[mnRows.size()-2], mnCols[nCol+1]-1, mnRows[mnRows.size()-1]-1 );
+ }
+ else
+ {
+ for ( SCROW i = 0 ; i < static_cast<SCROW>(mnRows.size())-1; i++ )
+ OutputBlockFrame( mnCols[nCol], mnRows[i], mnCols[nCol+1]-1, mnRows[i+1]-1 );
+ }
+ }
+ else
+ OutputBlockFrame( mnCols[nCol], mnRows.front(), mnCols[nCol+1]-1, mnRows.back()-1, bAllRows );
+ }
+ //out put rows area outer framer
+ if ( mnTabStartCol != mnDataStartCol )
+ {
+ if ( mnTabStartRow != mnDataStartRow )
+ OutputBlockFrame( mnTabStartCol, mnTabStartRow, mnDataStartCol-1, mnDataStartRow-1 );
+ OutputBlockFrame( mnTabStartCol, mnDataStartRow, mnDataStartCol-1, mnTabEndRow );
+ }
+ //out put cols area outer framer
+ OutputBlockFrame( mnDataStartCol, mnTabStartRow, mnTabEndCol, mnDataStartRow-1 );
+}
+
+ScDPOutputImpl::ScDPOutputImpl( ScDocument* pDoc, sal_uInt16 nTab,
+ SCCOL nTabStartCol,
+ SCROW nTabStartRow,
+ SCCOL nDataStartCol,
+ SCROW nDataStartRow,
+ SCCOL nTabEndCol,
+ SCROW nTabEndRow ):
+ mpDoc( pDoc ),
+ mnTab( nTab ),
+ mnTabStartCol( nTabStartCol ),
+ mnTabStartRow( nTabStartRow ),
+ mnDataStartCol ( nDataStartCol ),
+ mnDataStartRow ( nDataStartRow ),
+ mnTabEndCol( nTabEndCol ),
+ mnTabEndRow( nTabEndRow )
+{
+ mbNeedLineCols.resize( nTabEndCol-nDataStartCol+1, false );
+ mbNeedLineRows.resize( nTabEndRow-nDataStartRow+1, false );
+
+}
+
+bool ScDPOutputImpl::AddRow( SCROW nRow )
+{
+ if ( !mbNeedLineRows[ nRow - mnDataStartRow ] )
+ {
+ mbNeedLineRows[ nRow - mnDataStartRow ] = true;
+ mnRows.push_back( nRow );
+ return true;
+ }
+ else
+ return false;
+}
+
+bool ScDPOutputImpl::AddCol( SCCOL nCol )
+{
+
+ if ( !mbNeedLineCols[ nCol - mnDataStartCol ] )
+ {
+ mbNeedLineCols[ nCol - mnDataStartCol ] = true;
+ mnCols.push_back( nCol );
+ return true;
+ }
+ else
+ return false;
+}
+
+void ScDPOutputImpl::OutputBlockFrame ( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, bool bHori )
+{
+ Color color = SC_DP_FRAME_COLOR;
+ ::editeng::SvxBorderLine aLine( &color, SC_DP_FRAME_INNER_BOLD );
+ ::editeng::SvxBorderLine aOutLine( &color, SC_DP_FRAME_OUTER_BOLD );
+
+ SvxBoxItem aBox( ATTR_BORDER );
+
+ if ( nStartCol == mnTabStartCol )
+ aBox.SetLine(&aOutLine, SvxBoxItemLine::LEFT);
+ else
+ aBox.SetLine(&aLine, SvxBoxItemLine::LEFT);
+
+ if ( nStartRow == mnTabStartRow )
+ aBox.SetLine(&aOutLine, SvxBoxItemLine::TOP);
+ else
+ aBox.SetLine(&aLine, SvxBoxItemLine::TOP);
+
+ if ( nEndCol == mnTabEndCol ) //bottom row
+ aBox.SetLine(&aOutLine, SvxBoxItemLine::RIGHT);
+ else
+ aBox.SetLine(&aLine, SvxBoxItemLine::RIGHT);
+
+ if ( nEndRow == mnTabEndRow ) //bottom
+ aBox.SetLine(&aOutLine, SvxBoxItemLine::BOTTOM);
+ else
+ aBox.SetLine(&aLine, SvxBoxItemLine::BOTTOM);
+
+ SvxBoxInfoItem aBoxInfo( ATTR_BORDER_INNER );
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::VERT,false );
+ if ( bHori )
+ {
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI);
+ aBoxInfo.SetLine( &aLine, SvxBoxInfoItemLine::HORI );
+ }
+ else
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI,false );
+
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::DISTANCE,false);
+
+ mpDoc->ApplyFrameAreaTab(ScRange(nStartCol, nStartRow, mnTab, nEndCol, nEndRow , mnTab), aBox, aBoxInfo);
+
+}
+
+void lcl_SetStyleById(ScDocument* pDoc, SCTAB nTab,
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ TranslateId pStrId)
+{
+ if ( nCol1 > nCol2 || nRow1 > nRow2 )
+ {
+ OSL_FAIL("SetStyleById: invalid range");
+ return;
+ }
+
+ OUString aStyleName = ScResId(pStrId);
+ ScStyleSheetPool* pStlPool = pDoc->GetStyleSheetPool();
+ ScStyleSheet* pStyle = static_cast<ScStyleSheet*>( pStlPool->Find( aStyleName, SfxStyleFamily::Para ) );
+ if (!pStyle)
+ {
+ // create new style (was in ScPivot::SetStyle)
+
+ pStyle = static_cast<ScStyleSheet*>( &pStlPool->Make( aStyleName, SfxStyleFamily::Para,
+ SfxStyleSearchBits::UserDefined ) );
+ pStyle->SetParent( ScResId(STR_STYLENAME_STANDARD) );
+ SfxItemSet& rSet = pStyle->GetItemSet();
+ if (pStrId == STR_PIVOT_STYLENAME_RESULT || pStrId == STR_PIVOT_STYLENAME_TITLE){
+ rSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
+ rSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_CJK_FONT_WEIGHT ) );
+ rSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_CTL_FONT_WEIGHT ) );
+ }
+ if (pStrId == STR_PIVOT_STYLENAME_CATEGORY || pStrId == STR_PIVOT_STYLENAME_TITLE)
+ rSet.Put( SvxHorJustifyItem( SvxCellHorJustify::Left, ATTR_HOR_JUSTIFY ) );
+ }
+
+ pDoc->ApplyStyleAreaTab( nCol1, nRow1, nCol2, nRow2, nTab, *pStyle );
+}
+
+void lcl_SetFrame( ScDocument* pDoc, SCTAB nTab,
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ sal_uInt16 nWidth )
+{
+ ::editeng::SvxBorderLine aLine(nullptr, nWidth, SvxBorderLineStyle::SOLID);
+ SvxBoxItem aBox( ATTR_BORDER );
+ aBox.SetLine(&aLine, SvxBoxItemLine::LEFT);
+ aBox.SetLine(&aLine, SvxBoxItemLine::TOP);
+ aBox.SetLine(&aLine, SvxBoxItemLine::RIGHT);
+ aBox.SetLine(&aLine, SvxBoxItemLine::BOTTOM);
+ SvxBoxInfoItem aBoxInfo( ATTR_BORDER_INNER );
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI,false);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::VERT,false);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::DISTANCE,false);
+
+ pDoc->ApplyFrameAreaTab(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab), aBox, aBoxInfo);
+}
+
+void lcl_FillNumberFormats( std::unique_ptr<sal_uInt32[]>& rFormats, sal_Int32& rCount,
+ const uno::Reference<sheet::XDataPilotMemberResults>& xLevRes,
+ const uno::Reference<container::XIndexAccess>& xDims )
+{
+ if ( rFormats )
+ return; // already set
+
+ // xLevRes is from the data layout dimension
+ //TODO: use result sequence from ScDPOutLevelData!
+
+ uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults();
+
+ tools::Long nSize = aResult.getLength();
+ if (!nSize)
+ return;
+
+ // get names/formats for all data dimensions
+ //TODO: merge this with the loop to collect ScDPOutLevelData?
+
+ std::vector <OUString> aDataNames;
+ std::vector <sal_uInt32> aDataFormats;
+ sal_Int32 nDimCount = xDims->getCount();
+ sal_Int32 nDim = 0;
+ for ( ; nDim < nDimCount ; nDim++)
+ {
+ uno::Reference<uno::XInterface> xDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
+ uno::Reference<container::XNamed> xDimName( xDim, uno::UNO_QUERY );
+ if ( xDimProp.is() && xDimName.is() )
+ {
+ sheet::DataPilotFieldOrientation eDimOrient =
+ ScUnoHelpFunctions::GetEnumProperty(
+ xDimProp, SC_UNO_DP_ORIENTATION,
+ sheet::DataPilotFieldOrientation_HIDDEN );
+ if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
+ {
+ aDataNames.push_back(xDimName->getName());
+ tools::Long nFormat = ScUnoHelpFunctions::GetLongProperty(
+ xDimProp,
+ SC_UNONAME_NUMFMT );
+ aDataFormats.push_back(nFormat);
+ }
+ }
+ }
+
+ if (aDataFormats.empty())
+ return;
+
+ const sheet::MemberResult* pArray = aResult.getConstArray();
+
+ OUString aName;
+ sal_uInt32* pNumFmt = new sal_uInt32[nSize];
+ if (aDataFormats.size() == 1)
+ {
+ // only one data dimension -> use its numberformat everywhere
+ tools::Long nFormat = aDataFormats[0];
+ for (tools::Long nPos=0; nPos<nSize; nPos++)
+ pNumFmt[nPos] = nFormat;
+ }
+ else
+ {
+ for (tools::Long nPos=0; nPos<nSize; nPos++)
+ {
+ // if CONTINUE bit is set, keep previous name
+ //TODO: keep number format instead!
+ if ( !(pArray[nPos].Flags & sheet::MemberResultFlags::CONTINUE) )
+ aName = pArray[nPos].Name;
+
+ sal_uInt32 nFormat = 0;
+ for (size_t i=0; i<aDataFormats.size(); i++)
+ if (aName == aDataNames[i]) //TODO: search more efficiently?
+ {
+ nFormat = aDataFormats[i];
+ break;
+ }
+ pNumFmt[nPos] = nFormat;
+ }
+ }
+
+ rFormats.reset( pNumFmt );
+ rCount = nSize;
+}
+
+sal_uInt32 lcl_GetFirstNumberFormat( const uno::Reference<container::XIndexAccess>& xDims )
+{
+ tools::Long nDimCount = xDims->getCount();
+ for (tools::Long nDim=0; nDim<nDimCount; nDim++)
+ {
+ uno::Reference<beans::XPropertySet> xDimProp(xDims->getByIndex(nDim), uno::UNO_QUERY);
+ if ( xDimProp.is() )
+ {
+ sheet::DataPilotFieldOrientation eDimOrient =
+ ScUnoHelpFunctions::GetEnumProperty(
+ xDimProp, SC_UNO_DP_ORIENTATION,
+ sheet::DataPilotFieldOrientation_HIDDEN );
+ if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
+ {
+ tools::Long nFormat = ScUnoHelpFunctions::GetLongProperty(
+ xDimProp,
+ SC_UNONAME_NUMFMT );
+
+ return nFormat; // use format from first found data dimension
+ }
+ }
+ }
+
+ return 0; // none found
+}
+
+bool lcl_MemberEmpty( const uno::Sequence<sheet::MemberResult>& rSeq )
+{
+ // used to skip levels that have no members
+
+ return std::none_of(rSeq.begin(), rSeq.end(),
+ [](const sheet::MemberResult& rMem) {
+ return rMem.Flags & sheet::MemberResultFlags::HASMEMBER; });
+}
+
+/**
+ * Get visible page dimension members as results, except that, if all
+ * members are visible, then this function returns empty result.
+ */
+uno::Sequence<sheet::MemberResult> getVisiblePageMembersAsResults( const uno::Reference<uno::XInterface>& xLevel )
+{
+ if (!xLevel.is())
+ return uno::Sequence<sheet::MemberResult>();
+
+ uno::Reference<sheet::XMembersSupplier> xMSupplier(xLevel, UNO_QUERY);
+ if (!xMSupplier.is())
+ return uno::Sequence<sheet::MemberResult>();
+
+ uno::Reference<sheet::XMembersAccess> xNA = xMSupplier->getMembers();
+ if (!xNA.is())
+ return uno::Sequence<sheet::MemberResult>();
+
+ std::vector<sheet::MemberResult> aRes;
+ const uno::Sequence<OUString> aNames = xNA->getElementNames();
+ for (const OUString& rName : aNames)
+ {
+ xNA->getByName(rName);
+
+ uno::Reference<beans::XPropertySet> xMemPS(xNA->getByName(rName), UNO_QUERY);
+ if (!xMemPS.is())
+ continue;
+
+ OUString aCaption = ScUnoHelpFunctions::GetStringProperty(xMemPS, SC_UNO_DP_LAYOUTNAME, OUString());
+ if (aCaption.isEmpty())
+ aCaption = rName;
+
+ bool bVisible = ScUnoHelpFunctions::GetBoolProperty(xMemPS, SC_UNO_DP_ISVISIBLE);
+
+ if (bVisible)
+ {
+ /* TODO: any numeric value to obtain? */
+ aRes.emplace_back(rName, aCaption, 0, std::numeric_limits<double>::quiet_NaN());
+ }
+ }
+
+ if (o3tl::make_unsigned(aNames.getLength()) == aRes.size())
+ // All members are visible. Return empty result.
+ return uno::Sequence<sheet::MemberResult>();
+
+ return ScUnoHelpFunctions::VectorToSequence(aRes);
+}
+
+}
+
+ScDPOutput::ScDPOutput( ScDocument* pD, const uno::Reference<sheet::XDimensionsSupplier>& xSrc,
+ const ScAddress& rPos, bool bFilter ) :
+ pDoc( pD ),
+ xSource( xSrc ),
+ aStartPos( rPos ),
+ nColFmtCount( 0 ),
+ nRowFmtCount( 0 ),
+ nSingleNumFmt( 0 ),
+ nColCount(0),
+ nRowCount(0),
+ nHeaderSize(0),
+ bDoFilter(bFilter),
+ bResultsError(false),
+ bSizesValid(false),
+ bSizeOverflow(false),
+ mbHeaderLayout(false)
+{
+ nTabStartCol = nMemberStartCol = nDataStartCol = nTabEndCol = 0;
+ nTabStartRow = nMemberStartRow = nDataStartRow = nTabEndRow = 0;
+
+ uno::Reference<sheet::XDataPilotResults> xResult( xSource, uno::UNO_QUERY );
+ if ( xSource.is() && xResult.is() )
+ {
+ // get dimension results:
+
+ uno::Reference<container::XIndexAccess> xDims =
+ new ScNameToIndexAccess( xSource->getDimensions() );
+ tools::Long nDimCount = xDims->getCount();
+ for (tools::Long nDim=0; nDim<nDimCount; nDim++)
+ {
+ uno::Reference<uno::XInterface> xDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
+ uno::Reference<sheet::XHierarchiesSupplier> xDimSupp( xDim, uno::UNO_QUERY );
+ if ( xDimProp.is() && xDimSupp.is() )
+ {
+ sheet::DataPilotFieldOrientation eDimOrient =
+ ScUnoHelpFunctions::GetEnumProperty(
+ xDimProp, SC_UNO_DP_ORIENTATION,
+ sheet::DataPilotFieldOrientation_HIDDEN );
+ tools::Long nDimPos = ScUnoHelpFunctions::GetLongProperty( xDimProp,
+ SC_UNO_DP_POSITION );
+ bool bIsDataLayout = ScUnoHelpFunctions::GetBoolProperty(
+ xDimProp, SC_UNO_DP_ISDATALAYOUT);
+ bool bHasHiddenMember = ScUnoHelpFunctions::GetBoolProperty(
+ xDimProp, SC_UNO_DP_HAS_HIDDEN_MEMBER);
+ sal_Int32 nNumFmt = ScUnoHelpFunctions::GetLongProperty(
+ xDimProp, SC_UNO_DP_NUMBERFO);
+
+ if ( eDimOrient != sheet::DataPilotFieldOrientation_HIDDEN )
+ {
+ uno::Reference<container::XIndexAccess> xHiers =
+ new ScNameToIndexAccess( xDimSupp->getHierarchies() );
+ tools::Long nHierarchy = ScUnoHelpFunctions::GetLongProperty(
+ xDimProp,
+ SC_UNO_DP_USEDHIERARCHY );
+ if ( nHierarchy >= xHiers->getCount() )
+ nHierarchy = 0;
+
+ uno::Reference<sheet::XLevelsSupplier> xHierSupp(xHiers->getByIndex(nHierarchy),
+ uno::UNO_QUERY);
+ if ( xHierSupp.is() )
+ {
+ uno::Reference<container::XIndexAccess> xLevels =
+ new ScNameToIndexAccess( xHierSupp->getLevels() );
+ tools::Long nLevCount = xLevels->getCount();
+ for (tools::Long nLev=0; nLev<nLevCount; nLev++)
+ {
+ uno::Reference<uno::XInterface> xLevel(xLevels->getByIndex(nLev),
+ uno::UNO_QUERY);
+ uno::Reference<container::XNamed> xLevNam( xLevel, uno::UNO_QUERY );
+ uno::Reference<sheet::XDataPilotMemberResults> xLevRes(
+ xLevel, uno::UNO_QUERY );
+ if ( xLevNam.is() && xLevRes.is() )
+ {
+ OUString aName = xLevNam->getName();
+ Reference<XPropertySet> xPropSet(xLevel, UNO_QUERY);
+ // Caption equals the field name by default.
+ // #i108948# use ScUnoHelpFunctions::GetStringProperty, because
+ // LayoutName is new and may not be present in external implementation
+ OUString aCaption = ScUnoHelpFunctions::GetStringProperty( xPropSet,
+ SC_UNO_DP_LAYOUTNAME, aName );
+
+ switch ( eDimOrient )
+ {
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ {
+ uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults();
+ if (!lcl_MemberEmpty(aResult))
+ {
+ ScDPOutLevelData tmp(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName,
+ aCaption, bHasHiddenMember, bIsDataLayout, false);
+ pColFields.push_back(tmp);
+ }
+ }
+ break;
+ case sheet::DataPilotFieldOrientation_ROW:
+ {
+ uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults();
+ if (!lcl_MemberEmpty(aResult))
+ {
+ ScDPOutLevelData tmp(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName,
+ aCaption, bHasHiddenMember, bIsDataLayout, false);
+ pRowFields.push_back(tmp);
+ }
+ }
+ break;
+ case sheet::DataPilotFieldOrientation_PAGE:
+ {
+ uno::Sequence<sheet::MemberResult> aResult = getVisiblePageMembersAsResults(xLevel);
+ // no check on results for page fields
+ ScDPOutLevelData tmp(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName,
+ aCaption, bHasHiddenMember, false, true);
+ pPageFields.push_back(tmp);
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ // get number formats from data dimensions
+ if ( bIsDataLayout )
+ {
+ OSL_ENSURE( nLevCount == 1, "data layout: multiple levels?" );
+ if ( eDimOrient == sheet::DataPilotFieldOrientation_COLUMN )
+ lcl_FillNumberFormats( pColNumFmt, nColFmtCount, xLevRes, xDims );
+ else if ( eDimOrient == sheet::DataPilotFieldOrientation_ROW )
+ lcl_FillNumberFormats( pRowNumFmt, nRowFmtCount, xLevRes, xDims );
+ }
+ }
+ }
+ }
+ }
+ else if ( bIsDataLayout )
+ {
+ // data layout dimension is hidden (allowed if there is only one data dimension)
+ // -> use the number format from the first data dimension for all results
+
+ nSingleNumFmt = lcl_GetFirstNumberFormat( xDims );
+ }
+ }
+ }
+ std::sort(pColFields.begin(), pColFields.end(), ScDPOutLevelDataComparator());
+ std::sort(pRowFields.begin(), pRowFields.end(), ScDPOutLevelDataComparator());
+ std::sort(pPageFields.begin(), pPageFields.end(), ScDPOutLevelDataComparator());
+
+ // get data results:
+
+ try
+ {
+ aData = xResult->getResults();
+ }
+ catch (const uno::RuntimeException&)
+ {
+ bResultsError = true;
+ }
+ }
+
+ // get "DataDescription" property (may be missing in external sources)
+
+ uno::Reference<beans::XPropertySet> xSrcProp( xSource, uno::UNO_QUERY );
+ if ( !xSrcProp.is() )
+ return;
+
+ try
+ {
+ uno::Any aAny = xSrcProp->getPropertyValue( SC_UNO_DP_DATADESC );
+ OUString aUStr;
+ aAny >>= aUStr;
+ aDataDescription = aUStr;
+ }
+ catch(const uno::Exception&)
+ {
+ }
+}
+
+ScDPOutput::~ScDPOutput()
+{
+}
+
+void ScDPOutput::SetPosition( const ScAddress& rPos )
+{
+ aStartPos = rPos;
+ bSizesValid = bSizeOverflow = false;
+}
+
+void ScDPOutput::DataCell( SCCOL nCol, SCROW nRow, SCTAB nTab, const sheet::DataResult& rData )
+{
+ tools::Long nFlags = rData.Flags;
+ if ( nFlags & sheet::DataResultFlags::ERROR )
+ {
+ pDoc->SetError( nCol, nRow, nTab, FormulaError::NoValue );
+ }
+ else if ( nFlags & sheet::DataResultFlags::HASDATA )
+ {
+ pDoc->SetValue( nCol, nRow, nTab, rData.Value );
+
+ // use number formats from source
+
+ OSL_ENSURE( bSizesValid, "DataCell: !bSizesValid" );
+ sal_uInt32 nFormat = 0;
+ bool bApplyFormat = false;
+ if ( pColNumFmt )
+ {
+ if ( nCol >= nDataStartCol )
+ {
+ tools::Long nIndex = nCol - nDataStartCol;
+ if ( nIndex < nColFmtCount )
+ {
+ nFormat = pColNumFmt[nIndex];
+ bApplyFormat = true;
+ }
+ }
+ }
+ else if ( pRowNumFmt )
+ {
+ if ( nRow >= nDataStartRow )
+ {
+ tools::Long nIndex = nRow - nDataStartRow;
+ if ( nIndex < nRowFmtCount )
+ {
+ nFormat = pRowNumFmt[nIndex];
+ bApplyFormat = true;
+ }
+ }
+ }
+ else if ( nSingleNumFmt != 0 )
+ {
+ nFormat = nSingleNumFmt; // single format is used everywhere
+ bApplyFormat = true;
+ }
+
+ if (bApplyFormat)
+ pDoc->ApplyAttr(nCol, nRow, nTab, SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
+ }
+ // SubTotal formatting is controlled by headers
+}
+
+void ScDPOutput::HeaderCell( SCCOL nCol, SCROW nRow, SCTAB nTab,
+ const sheet::MemberResult& rData, bool bColHeader, tools::Long nLevel )
+{
+ tools::Long nFlags = rData.Flags;
+
+ if ( nFlags & sheet::MemberResultFlags::HASMEMBER )
+ {
+ bool bNumeric = (nFlags & sheet::MemberResultFlags::NUMERIC) != 0;
+ if (bNumeric && std::isfinite( rData.Value))
+ {
+ pDoc->SetValue( nCol, nRow, nTab, rData.Value);
+ }
+ else
+ {
+ ScSetStringParam aParam;
+ if (bNumeric)
+ aParam.setNumericInput();
+ else
+ aParam.setTextInput();
+
+ pDoc->SetString(nCol, nRow, nTab, rData.Caption, &aParam);
+ }
+ }
+
+ if ( !(nFlags & sheet::MemberResultFlags::SUBTOTAL) )
+ return;
+
+ ScDPOutputImpl outputimp( pDoc, nTab,
+ nTabStartCol, nTabStartRow,
+ nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow );
+ //TODO: limit frames to horizontal or vertical?
+ if (bColHeader)
+ {
+ outputimp.OutputBlockFrame( nCol,nMemberStartRow+static_cast<SCROW>(nLevel), nCol,nDataStartRow-1 );
+
+ lcl_SetStyleById( pDoc,nTab, nCol,nMemberStartRow+static_cast<SCROW>(nLevel), nCol,nDataStartRow-1,
+ STR_PIVOT_STYLENAME_TITLE );
+ lcl_SetStyleById( pDoc,nTab, nCol,nDataStartRow, nCol,nTabEndRow,
+ STR_PIVOT_STYLENAME_RESULT );
+ }
+ else
+ {
+ outputimp.OutputBlockFrame( nMemberStartCol+static_cast<SCCOL>(nLevel),nRow, nDataStartCol-1,nRow );
+ lcl_SetStyleById( pDoc,nTab, nMemberStartCol+static_cast<SCCOL>(nLevel),nRow, nDataStartCol-1,nRow,
+ STR_PIVOT_STYLENAME_TITLE );
+ lcl_SetStyleById( pDoc,nTab, nDataStartCol,nRow, nTabEndCol,nRow,
+ STR_PIVOT_STYLENAME_RESULT );
+ }
+}
+
+void ScDPOutput::FieldCell(
+ SCCOL nCol, SCROW nRow, SCTAB nTab, const ScDPOutLevelData& rData, bool bInTable)
+{
+ // Avoid unwanted automatic format detection.
+ ScSetStringParam aParam;
+ aParam.mbDetectNumberFormat = false;
+ aParam.meSetTextNumFormat = ScSetStringParam::Always;
+ aParam.mbHandleApostrophe = false;
+ pDoc->SetString(nCol, nRow, nTab, rData.maCaption, &aParam);
+
+ if (bInTable)
+ lcl_SetFrame( pDoc,nTab, nCol,nRow, nCol,nRow, 20 );
+
+ // For field button drawing
+ ScMF nMergeFlag = ScMF::NONE;
+ if (rData.mbHasHiddenMember)
+ nMergeFlag |= ScMF::HiddenMember;
+
+ if (rData.mbPageDim)
+ {
+ nMergeFlag |= ScMF::ButtonPopup;
+ pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, ScMF::Button);
+ pDoc->ApplyFlagsTab(nCol+1, nRow, nCol+1, nRow, nTab, nMergeFlag);
+ }
+ else
+ {
+ nMergeFlag |= ScMF::Button;
+ if (!rData.mbDataLayout)
+ nMergeFlag |= ScMF::ButtonPopup;
+ pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, nMergeFlag);
+ }
+
+ lcl_SetStyleById( pDoc,nTab, nCol,nRow, nCol,nRow, STR_PIVOT_STYLENAME_FIELDNAME );
+}
+
+static void lcl_DoFilterButton( ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ pDoc->SetString( nCol, nRow, nTab, ScResId(STR_CELL_FILTER) );
+ pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, ScMF::Button);
+}
+
+void ScDPOutput::CalcSizes()
+{
+ if (bSizesValid)
+ return;
+
+ // get column size of data from first row
+ //TODO: allow different sizes (and clear following areas) ???
+
+ nRowCount = aData.getLength();
+ const uno::Sequence<sheet::DataResult>* pRowAry = aData.getConstArray();
+ nColCount = nRowCount ? ( pRowAry[0].getLength() ) : 0;
+
+ nHeaderSize = 1;
+ if (GetHeaderLayout() && pColFields.empty())
+ // Insert an extra header row only when there is no column field.
+ nHeaderSize = 2;
+
+ // calculate output positions and sizes
+
+ tools::Long nPageSize = 0; // use page fields!
+ if ( bDoFilter || !pPageFields.empty() )
+ {
+ nPageSize += pPageFields.size() + 1; // plus one empty row
+ if ( bDoFilter )
+ ++nPageSize; // filter button above the page fields
+ }
+
+ if ( aStartPos.Col() + static_cast<tools::Long>(pRowFields.size()) + nColCount - 1 > pDoc->MaxCol() ||
+ aStartPos.Row() + nPageSize + nHeaderSize + static_cast<tools::Long>(pColFields.size()) + nRowCount > pDoc->MaxRow())
+ {
+ bSizeOverflow = true;
+ }
+
+ nTabStartCol = aStartPos.Col();
+ nTabStartRow = aStartPos.Row() + static_cast<SCROW>(nPageSize); // below page fields
+ nMemberStartCol = nTabStartCol;
+ nMemberStartRow = nTabStartRow + static_cast<SCROW>(nHeaderSize);
+ nDataStartCol = nMemberStartCol + static_cast<SCCOL>(pRowFields.size());
+ nDataStartRow = nMemberStartRow + static_cast<SCROW>(pColFields.size());
+ if ( nColCount > 0 )
+ nTabEndCol = nDataStartCol + static_cast<SCCOL>(nColCount) - 1;
+ else
+ nTabEndCol = nDataStartCol; // single column will remain empty
+ // if page fields are involved, include the page selection cells
+ if ( !pPageFields.empty() && nTabEndCol < nTabStartCol + 1 )
+ nTabEndCol = nTabStartCol + 1;
+ if ( nRowCount > 0 )
+ nTabEndRow = nDataStartRow + static_cast<SCROW>(nRowCount) - 1;
+ else
+ nTabEndRow = nDataStartRow; // single row will remain empty
+ bSizesValid = true;
+}
+
+sal_Int32 ScDPOutput::GetPositionType(const ScAddress& rPos)
+{
+ using namespace ::com::sun::star::sheet;
+
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+ if ( nTab != aStartPos.Tab() )
+ return DataPilotTablePositionType::NOT_IN_TABLE;
+
+ CalcSizes();
+
+ // Make sure the cursor is within the table.
+ if (nCol < nTabStartCol || nRow < nTabStartRow || nCol > nTabEndCol || nRow > nTabEndRow)
+ return DataPilotTablePositionType::NOT_IN_TABLE;
+
+ // test for result data area.
+ if (nCol >= nDataStartCol && nCol <= nTabEndCol && nRow >= nDataStartRow && nRow <= nTabEndRow)
+ return DataPilotTablePositionType::RESULT;
+
+ bool bInColHeader = (nRow >= nTabStartRow && nRow < nDataStartRow);
+ bool bInRowHeader = (nCol >= nTabStartCol && nCol < nDataStartCol);
+
+ if (bInColHeader && bInRowHeader)
+ // probably in that ugly little box at the upper-left corner of the table.
+ return DataPilotTablePositionType::OTHER;
+
+ if (bInColHeader)
+ {
+ if (nRow == nTabStartRow)
+ // first row in the column header area is always used for column
+ // field buttons.
+ return DataPilotTablePositionType::OTHER;
+
+ return DataPilotTablePositionType::COLUMN_HEADER;
+ }
+
+ if (bInRowHeader)
+ return DataPilotTablePositionType::ROW_HEADER;
+
+ return DataPilotTablePositionType::OTHER;
+}
+
+void ScDPOutput::Output()
+{
+ SCTAB nTab = aStartPos.Tab();
+ const uno::Sequence<sheet::DataResult>* pRowAry = aData.getConstArray();
+
+ // calculate output positions and sizes
+
+ CalcSizes();
+ if ( bSizeOverflow || bResultsError ) // does output area exceed sheet limits?
+ return; // nothing
+
+ // clear whole (new) output area
+ // when modifying table, clear old area !
+ //TODO: include InsertDeleteFlags::OBJECTS ???
+ pDoc->DeleteAreaTab( aStartPos.Col(), aStartPos.Row(), nTabEndCol, nTabEndRow, nTab, InsertDeleteFlags::ALL );
+
+ if ( bDoFilter )
+ lcl_DoFilterButton( pDoc, aStartPos.Col(), aStartPos.Row(), nTab );
+
+ // output page fields:
+
+ for (size_t nField=0; nField<pPageFields.size(); ++nField)
+ {
+ SCCOL nHdrCol = aStartPos.Col();
+ SCROW nHdrRow = aStartPos.Row() + nField + ( bDoFilter ? 1 : 0 );
+ // draw without frame for consistency with filter button:
+ FieldCell(nHdrCol, nHdrRow, nTab, pPageFields[nField], false);
+ SCCOL nFldCol = nHdrCol + 1;
+
+ OUString aPageValue = ScResId(SCSTR_ALL);
+ const uno::Sequence<sheet::MemberResult>& rRes = pPageFields[nField].maResult;
+ sal_Int32 n = rRes.getLength();
+ if (n == 1)
+ {
+ if (rRes[0].Caption.isEmpty())
+ aPageValue = ScResId(STR_EMPTYDATA);
+ else
+ aPageValue = rRes[0].Caption;
+ }
+ else if (n > 1)
+ aPageValue = ScResId(SCSTR_MULTIPLE);
+
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ pDoc->SetString(nFldCol, nHdrRow, nTab, aPageValue, &aParam);
+
+ lcl_SetFrame( pDoc,nTab, nFldCol,nHdrRow, nFldCol,nHdrRow, 20 );
+ }
+
+ // data description
+ // (may get overwritten by first row field)
+
+ if (aDataDescription.isEmpty())
+ {
+ //TODO: use default string ("result") ?
+ }
+ pDoc->SetString(nTabStartCol, nTabStartRow, nTab, aDataDescription);
+
+ // set STR_PIVOT_STYLENAME_INNER for whole data area (subtotals are overwritten)
+
+ if ( nDataStartRow > nTabStartRow )
+ lcl_SetStyleById( pDoc, nTab, nTabStartCol, nTabStartRow, nTabEndCol, nDataStartRow-1,
+ STR_PIVOT_STYLENAME_TOP );
+ lcl_SetStyleById( pDoc, nTab, nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow,
+ STR_PIVOT_STYLENAME_INNER );
+
+ // output column headers:
+ ScDPOutputImpl outputimp( pDoc, nTab,
+ nTabStartCol, nTabStartRow,
+ nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow );
+ for (size_t nField=0; nField<pColFields.size(); nField++)
+ {
+ SCCOL nHdrCol = nDataStartCol + static_cast<SCCOL>(nField); //TODO: check for overflow
+ FieldCell(nHdrCol, nTabStartRow, nTab, pColFields[nField], true);
+
+ SCROW nRowPos = nMemberStartRow + static_cast<SCROW>(nField); //TODO: check for overflow
+ const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nField].maResult;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+ tools::Long nThisColCount = rSequence.getLength();
+ OSL_ENSURE( nThisColCount == nColCount, "count mismatch" ); //TODO: ???
+ for (tools::Long nCol=0; nCol<nThisColCount; nCol++)
+ {
+ SCCOL nColPos = nDataStartCol + static_cast<SCCOL>(nCol); //TODO: check for overflow
+ HeaderCell( nColPos, nRowPos, nTab, pArray[nCol], true, nField );
+ if ( ( pArray[nCol].Flags & sheet::MemberResultFlags::HASMEMBER ) &&
+ !( pArray[nCol].Flags & sheet::MemberResultFlags::SUBTOTAL ) )
+ {
+ tools::Long nEnd = nCol;
+ while ( nEnd+1 < nThisColCount && ( pArray[nEnd+1].Flags & sheet::MemberResultFlags::CONTINUE ) )
+ ++nEnd;
+ SCCOL nEndColPos = nDataStartCol + static_cast<SCCOL>(nEnd); //TODO: check for overflow
+ if ( nField+1 < pColFields.size())
+ {
+ if ( nField == pColFields.size() - 2 )
+ {
+ outputimp.AddCol( nColPos );
+ if ( nColPos + 1 == nEndColPos )
+ outputimp.OutputBlockFrame( nColPos,nRowPos, nEndColPos,nRowPos+1, true );
+ }
+ else
+ outputimp.OutputBlockFrame( nColPos,nRowPos, nEndColPos,nRowPos );
+
+ lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nEndColPos,nDataStartRow-1, STR_PIVOT_STYLENAME_CATEGORY );
+ }
+ else
+ lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nColPos,nDataStartRow-1, STR_PIVOT_STYLENAME_CATEGORY );
+ }
+ else if ( pArray[nCol].Flags & sheet::MemberResultFlags::SUBTOTAL )
+ outputimp.AddCol( nColPos );
+
+ // Apply the same number format as in data source.
+ pDoc->ApplyAttr(nColPos, nRowPos, nTab, SfxUInt32Item(ATTR_VALUE_FORMAT, pColFields[nField].mnSrcNumFmt));
+ }
+ if ( nField== 0 && pColFields.size() == 1 )
+ outputimp.OutputBlockFrame( nDataStartCol,nTabStartRow, nTabEndCol,nRowPos-1 );
+ }
+
+ // output row headers:
+ std::vector<bool> vbSetBorder;
+ vbSetBorder.resize( nTabEndRow - nDataStartRow + 1, false );
+ for (size_t nField=0; nField<pRowFields.size(); nField++)
+ {
+ SCCOL nHdrCol = nTabStartCol + static_cast<SCCOL>(nField); //TODO: check for overflow
+ SCROW nHdrRow = nDataStartRow - 1;
+ FieldCell(nHdrCol, nHdrRow, nTab, pRowFields[nField], true);
+
+ SCCOL nColPos = nMemberStartCol + static_cast<SCCOL>(nField); //TODO: check for overflow
+ const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].maResult;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+ sal_Int32 nThisRowCount = rSequence.getLength();
+ OSL_ENSURE( nThisRowCount == nRowCount, "count mismatch" ); //TODO: ???
+ for (sal_Int32 nRow=0; nRow<nThisRowCount; nRow++)
+ {
+ SCROW nRowPos = nDataStartRow + static_cast<SCROW>(nRow); //TODO: check for overflow
+ HeaderCell( nColPos, nRowPos, nTab, pArray[nRow], false, nField );
+ if ( ( pArray[nRow].Flags & sheet::MemberResultFlags::HASMEMBER ) &&
+ !( pArray[nRow].Flags & sheet::MemberResultFlags::SUBTOTAL ) )
+ {
+ if ( nField+1 < pRowFields.size() )
+ {
+ tools::Long nEnd = nRow;
+ while ( nEnd+1 < nThisRowCount && ( pArray[nEnd+1].Flags & sheet::MemberResultFlags::CONTINUE ) )
+ ++nEnd;
+ SCROW nEndRowPos = nDataStartRow + static_cast<SCROW>(nEnd); //TODO: check for overflow
+ outputimp.AddRow( nRowPos );
+ if ( !vbSetBorder[ nRow ] )
+ {
+ outputimp.OutputBlockFrame( nColPos, nRowPos, nTabEndCol, nEndRowPos );
+ vbSetBorder[ nRow ] = true;
+ }
+ outputimp.OutputBlockFrame( nColPos, nRowPos, nColPos, nEndRowPos );
+
+ if ( nField == pRowFields.size() - 2 )
+ outputimp.OutputBlockFrame( nColPos+1, nRowPos, nColPos+1, nEndRowPos );
+
+ lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nDataStartCol-1,nEndRowPos, STR_PIVOT_STYLENAME_CATEGORY );
+ }
+ else
+ lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nDataStartCol-1,nRowPos, STR_PIVOT_STYLENAME_CATEGORY );
+ }
+ else if ( pArray[nRow].Flags & sheet::MemberResultFlags::SUBTOTAL )
+ outputimp.AddRow( nRowPos );
+
+ // Apply the same number format as in data source.
+ pDoc->ApplyAttr(nColPos, nRowPos, nTab, SfxUInt32Item(ATTR_VALUE_FORMAT, pRowFields[nField].mnSrcNumFmt));
+ }
+ }
+
+ if (nColCount == 1 && nRowCount > 0 && pColFields.empty())
+ {
+ // the table contains exactly one data field and no column fields.
+ // Display data description at top right corner.
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ pDoc->SetString(nDataStartCol, nDataStartRow-1, nTab, aDataDescription, &aParam);
+ }
+
+ // output data results:
+
+ for (sal_Int32 nRow=0; nRow<nRowCount; nRow++)
+ {
+ SCROW nRowPos = nDataStartRow + static_cast<SCROW>(nRow); //TODO: check for overflow
+ const sheet::DataResult* pColAry = pRowAry[nRow].getConstArray();
+ sal_Int32 nThisColCount = pRowAry[nRow].getLength();
+ OSL_ENSURE( nThisColCount == nColCount, "count mismatch" ); //TODO: ???
+ for (sal_Int32 nCol=0; nCol<nThisColCount; nCol++)
+ {
+ SCCOL nColPos = nDataStartCol + static_cast<SCCOL>(nCol); //TODO: check for overflow
+ DataCell( nColPos, nRowPos, nTab, pColAry[nCol] );
+ }
+ }
+
+ outputimp.OutputDataArea();
+}
+
+ScRange ScDPOutput::GetOutputRange( sal_Int32 nRegionType )
+{
+ using namespace ::com::sun::star::sheet;
+
+ CalcSizes();
+
+ SCTAB nTab = aStartPos.Tab();
+ switch (nRegionType)
+ {
+ case DataPilotOutputRangeType::RESULT:
+ return ScRange(nDataStartCol, nDataStartRow, nTab, nTabEndCol, nTabEndRow, nTab);
+ case DataPilotOutputRangeType::TABLE:
+ return ScRange(aStartPos.Col(), nTabStartRow, nTab, nTabEndCol, nTabEndRow, nTab);
+ default:
+ OSL_ENSURE(nRegionType == DataPilotOutputRangeType::WHOLE, "ScDPOutput::GetOutputRange: unknown region type");
+ break;
+ }
+ return ScRange(aStartPos.Col(), aStartPos.Row(), nTab, nTabEndCol, nTabEndRow, nTab);
+}
+
+bool ScDPOutput::HasError()
+{
+ CalcSizes();
+
+ return bSizeOverflow || bResultsError;
+}
+
+sal_Int32 ScDPOutput::GetHeaderRows() const
+{
+ return pPageFields.size() + ( bDoFilter ? 1 : 0 );
+}
+
+namespace
+{
+ void insertNames(ScDPUniqueStringSet& rNames, const uno::Sequence<sheet::MemberResult>& rMemberResults)
+ {
+ for (const sheet::MemberResult& rMemberResult : rMemberResults)
+ {
+ if (rMemberResult.Flags & sheet::MemberResultFlags::HASMEMBER)
+ rNames.insert(rMemberResult.Name);
+ }
+ }
+}
+
+void ScDPOutput::GetMemberResultNames(ScDPUniqueStringSet& rNames, tools::Long nDimension)
+{
+ // Return the list of all member names in a dimension's MemberResults.
+ // Only the dimension has to be compared because this is only used with table data,
+ // where each dimension occurs only once.
+
+ auto lFindDimension = [nDimension](const ScDPOutLevelData& rField) { return rField.mnDim == nDimension; };
+
+ // look in column fields
+ auto colit = std::find_if(pColFields.begin(), pColFields.end(), lFindDimension);
+ if (colit != pColFields.end())
+ {
+ // collect the member names
+ insertNames(rNames, colit->maResult);
+ return;
+ }
+
+ // look in row fields
+ auto rowit = std::find_if(pRowFields.begin(), pRowFields.end(), lFindDimension);
+ if (rowit != pRowFields.end())
+ {
+ // collect the member names
+ insertNames(rNames, rowit->maResult);
+ }
+}
+
+void ScDPOutput::SetHeaderLayout(bool bUseGrid)
+{
+ mbHeaderLayout = bUseGrid;
+ bSizesValid = false;
+}
+
+namespace {
+
+void lcl_GetTableVars( sal_Int32& rGrandTotalCols, sal_Int32& rGrandTotalRows, sal_Int32& rDataLayoutIndex,
+ std::vector<OUString>& rDataNames, std::vector<OUString>& rGivenNames,
+ sheet::DataPilotFieldOrientation& rDataOrient,
+ const uno::Reference<sheet::XDimensionsSupplier>& xSource )
+{
+ rDataLayoutIndex = -1; // invalid
+ rGrandTotalCols = 0;
+ rGrandTotalRows = 0;
+ rDataOrient = sheet::DataPilotFieldOrientation_HIDDEN;
+
+ uno::Reference<beans::XPropertySet> xSrcProp( xSource, uno::UNO_QUERY );
+ bool bColGrand = ScUnoHelpFunctions::GetBoolProperty(
+ xSrcProp, SC_UNO_DP_COLGRAND);
+ if ( bColGrand )
+ rGrandTotalCols = 1; // default if data layout not in columns
+
+ bool bRowGrand = ScUnoHelpFunctions::GetBoolProperty(
+ xSrcProp, SC_UNO_DP_ROWGRAND);
+ if ( bRowGrand )
+ rGrandTotalRows = 1; // default if data layout not in rows
+
+ if ( !xSource.is() )
+ return;
+
+ // find index and orientation of "data layout" dimension, count data dimensions
+
+ sal_Int32 nDataCount = 0;
+
+ uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xSource->getDimensions() );
+ tools::Long nDimCount = xDims->getCount();
+ for (tools::Long nDim=0; nDim<nDimCount; nDim++)
+ {
+ uno::Reference<uno::XInterface> xDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
+ if ( xDimProp.is() )
+ {
+ sheet::DataPilotFieldOrientation eDimOrient =
+ ScUnoHelpFunctions::GetEnumProperty(
+ xDimProp, SC_UNO_DP_ORIENTATION,
+ sheet::DataPilotFieldOrientation_HIDDEN );
+ if ( ScUnoHelpFunctions::GetBoolProperty( xDimProp,
+ SC_UNO_DP_ISDATALAYOUT ) )
+ {
+ rDataLayoutIndex = nDim;
+ rDataOrient = eDimOrient;
+ }
+ if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
+ {
+ OUString aSourceName;
+ OUString aGivenName;
+ ScDPOutput::GetDataDimensionNames( aSourceName, aGivenName, xDim );
+ try
+ {
+ uno::Any aValue = xDimProp->getPropertyValue( SC_UNO_DP_LAYOUTNAME );
+
+ if( aValue.hasValue() )
+ {
+ OUString strLayoutName;
+
+ if( ( aValue >>= strLayoutName ) && !strLayoutName.isEmpty() )
+ aGivenName = strLayoutName;
+ }
+ }
+ catch(const uno::Exception&)
+ {
+ }
+ rDataNames.push_back( aSourceName );
+ rGivenNames.push_back( aGivenName );
+
+ ++nDataCount;
+ }
+ }
+ }
+
+ if ( ( rDataOrient == sheet::DataPilotFieldOrientation_COLUMN ) && bColGrand )
+ rGrandTotalCols = nDataCount;
+ else if ( ( rDataOrient == sheet::DataPilotFieldOrientation_ROW ) && bRowGrand )
+ rGrandTotalRows = nDataCount;
+}
+
+}
+
+void ScDPOutput::GetPositionData(const ScAddress& rPos, DataPilotTablePositionData& rPosData)
+{
+ using namespace ::com::sun::star::sheet;
+
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+ if ( nTab != aStartPos.Tab() )
+ return; // wrong sheet
+
+ // calculate output positions and sizes
+
+ CalcSizes();
+
+ rPosData.PositionType = GetPositionType(rPos);
+ switch (rPosData.PositionType)
+ {
+ case DataPilotTablePositionType::RESULT:
+ {
+ vector<DataPilotFieldFilter> aFilters;
+ GetDataResultPositionData(aFilters, rPos);
+
+ DataPilotTableResultData aResData;
+ aResData.FieldFilters = comphelper::containerToSequence(aFilters);
+ aResData.DataFieldIndex = 0;
+ Reference<beans::XPropertySet> xPropSet(xSource, UNO_QUERY);
+ if (xPropSet.is())
+ {
+ sal_Int32 nDataFieldCount = ScUnoHelpFunctions::GetLongProperty( xPropSet,
+ SC_UNO_DP_DATAFIELDCOUNT );
+ if (nDataFieldCount > 0)
+ aResData.DataFieldIndex = (nRow - nDataStartRow) % nDataFieldCount;
+ }
+
+ // Copy appropriate DataResult object from the cached sheet::DataResult table.
+ if (aData.getLength() > nRow - nDataStartRow &&
+ aData[nRow-nDataStartRow].getLength() > nCol-nDataStartCol)
+ aResData.Result = aData[nRow-nDataStartRow][nCol-nDataStartCol];
+
+ rPosData.PositionData <<= aResData;
+ return;
+ }
+ case DataPilotTablePositionType::COLUMN_HEADER:
+ {
+ tools::Long nField = nRow - nTabStartRow - 1; // 1st line is used for the buttons
+ if (nField < 0)
+ break;
+
+ if (pColFields.size() < o3tl::make_unsigned(nField) + 1 )
+ break;
+ const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nField].maResult;
+ if (!rSequence.hasElements())
+ break;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+
+ tools::Long nItem = nCol - nDataStartCol;
+ // get origin of "continue" fields
+ while (nItem > 0 && ( pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
+ --nItem;
+
+ if (nItem < 0)
+ break;
+
+ DataPilotTableHeaderData aHeaderData;
+ aHeaderData.MemberName = pArray[nItem].Name;
+ aHeaderData.Flags = pArray[nItem].Flags;
+ aHeaderData.Dimension = static_cast<sal_Int32>(pColFields[nField].mnDim);
+ aHeaderData.Hierarchy = static_cast<sal_Int32>(pColFields[nField].mnHier);
+ aHeaderData.Level = static_cast<sal_Int32>(pColFields[nField].mnLevel);
+
+ rPosData.PositionData <<= aHeaderData;
+ return;
+ }
+ case DataPilotTablePositionType::ROW_HEADER:
+ {
+ tools::Long nField = nCol - nTabStartCol;
+ if (nField < 0)
+ break;
+
+ if (pRowFields.size() < o3tl::make_unsigned(nField) + 1 )
+ break;
+ const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].maResult;
+ if (!rSequence.hasElements())
+ break;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+
+ tools::Long nItem = nRow - nDataStartRow;
+ // get origin of "continue" fields
+ while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
+ --nItem;
+
+ if (nItem < 0)
+ break;
+
+ DataPilotTableHeaderData aHeaderData;
+ aHeaderData.MemberName = pArray[nItem].Name;
+ aHeaderData.Flags = pArray[nItem].Flags;
+ aHeaderData.Dimension = static_cast<sal_Int32>(pRowFields[nField].mnDim);
+ aHeaderData.Hierarchy = static_cast<sal_Int32>(pRowFields[nField].mnHier);
+ aHeaderData.Level = static_cast<sal_Int32>(pRowFields[nField].mnLevel);
+
+ rPosData.PositionData <<= aHeaderData;
+ return;
+ }
+ }
+}
+
+bool ScDPOutput::GetDataResultPositionData(vector<sheet::DataPilotFieldFilter>& rFilters, const ScAddress& rPos)
+{
+ // Check to make sure there is at least one data field.
+ Reference<beans::XPropertySet> xPropSet(xSource, UNO_QUERY);
+ if (!xPropSet.is())
+ return false;
+
+ sal_Int32 nDataFieldCount = ScUnoHelpFunctions::GetLongProperty( xPropSet,
+ SC_UNO_DP_DATAFIELDCOUNT );
+ if (nDataFieldCount == 0)
+ // No data field is present in this datapilot table.
+ return false;
+
+ // #i111421# use lcl_GetTableVars for correct size of totals and data layout position
+ sal_Int32 nGrandTotalCols;
+ sal_Int32 nGrandTotalRows;
+ sal_Int32 nDataLayoutIndex;
+ std::vector<OUString> aDataNames;
+ std::vector<OUString> aGivenNames;
+ sheet::DataPilotFieldOrientation eDataOrient;
+ lcl_GetTableVars( nGrandTotalCols, nGrandTotalRows, nDataLayoutIndex, aDataNames, aGivenNames, eDataOrient, xSource );
+
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+ if ( nTab != aStartPos.Tab() )
+ return false; // wrong sheet
+
+ CalcSizes();
+
+ // test for data area.
+ if (nCol < nDataStartCol || nCol > nTabEndCol || nRow < nDataStartRow || nRow > nTabEndRow)
+ {
+ // Cell is outside the data field area.
+ return false;
+ }
+
+ bool bFilterByCol = (nCol <= static_cast<SCCOL>(nTabEndCol - nGrandTotalCols));
+ bool bFilterByRow = (nRow <= static_cast<SCROW>(nTabEndRow - nGrandTotalRows));
+
+ // column fields
+ for (size_t nColField = 0; nColField < pColFields.size() && bFilterByCol; ++nColField)
+ {
+ if (pColFields[nColField].mnDim == nDataLayoutIndex)
+ // There is no sense including the data layout field for filtering.
+ continue;
+
+ sheet::DataPilotFieldFilter filter;
+ filter.FieldName = pColFields[nColField].maName;
+
+ const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nColField].maResult;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+
+ OSL_ENSURE(nDataStartCol + rSequence.getLength() - 1 == nTabEndCol, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
+
+ tools::Long nItem = nCol - nDataStartCol;
+ // get origin of "continue" fields
+ while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
+ --nItem;
+
+ filter.MatchValueName = pArray[nItem].Name;
+ rFilters.push_back(filter);
+ }
+
+ // row fields
+ for (size_t nRowField = 0; nRowField < pRowFields.size() && bFilterByRow; ++nRowField)
+ {
+ if (pRowFields[nRowField].mnDim == nDataLayoutIndex)
+ // There is no sense including the data layout field for filtering.
+ continue;
+
+ sheet::DataPilotFieldFilter filter;
+ filter.FieldName = pRowFields[nRowField].maName;
+
+ const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nRowField].maResult;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+
+ OSL_ENSURE(nDataStartRow + rSequence.getLength() - 1 == nTabEndRow, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
+
+ tools::Long nItem = nRow - nDataStartRow;
+ // get origin of "continue" fields
+ while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
+ --nItem;
+
+ filter.MatchValueName = pArray[nItem].Name;
+ rFilters.push_back(filter);
+ }
+
+ return true;
+}
+
+namespace {
+
+OUString lcl_GetDataFieldName( std::u16string_view rSourceName, sal_Int16 eFunc )
+{
+ TranslateId pStrId;
+ switch ( eFunc )
+ {
+ case sheet::GeneralFunction2::SUM: pStrId = STR_FUN_TEXT_SUM; break;
+ case sheet::GeneralFunction2::COUNT:
+ case sheet::GeneralFunction2::COUNTNUMS: pStrId = STR_FUN_TEXT_COUNT; break;
+ case sheet::GeneralFunction2::AVERAGE: pStrId = STR_FUN_TEXT_AVG; break;
+ case sheet::GeneralFunction2::MEDIAN: pStrId = STR_FUN_TEXT_MEDIAN; break;
+ case sheet::GeneralFunction2::MAX: pStrId = STR_FUN_TEXT_MAX; break;
+ case sheet::GeneralFunction2::MIN: pStrId = STR_FUN_TEXT_MIN; break;
+ case sheet::GeneralFunction2::PRODUCT: pStrId = STR_FUN_TEXT_PRODUCT; break;
+ case sheet::GeneralFunction2::STDEV:
+ case sheet::GeneralFunction2::STDEVP: pStrId = STR_FUN_TEXT_STDDEV; break;
+ case sheet::GeneralFunction2::VAR:
+ case sheet::GeneralFunction2::VARP: pStrId = STR_FUN_TEXT_VAR; break;
+ case sheet::GeneralFunction2::NONE:
+ case sheet::GeneralFunction2::AUTO: break;
+ default:
+ {
+ assert(false);
+ }
+ }
+ if (!pStrId)
+ return OUString();
+
+ return ScResId(pStrId) + " - " + rSourceName;
+}
+
+}
+
+void ScDPOutput::GetDataDimensionNames(
+ OUString& rSourceName, OUString& rGivenName, const uno::Reference<uno::XInterface>& xDim )
+{
+ uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
+ uno::Reference<container::XNamed> xDimName( xDim, uno::UNO_QUERY );
+ if ( !(xDimProp.is() && xDimName.is()) )
+ return;
+
+ // Asterisks are added in ScDPSaveData::WriteToSource to create unique names.
+ //TODO: preserve original name there?
+ rSourceName = ScDPUtil::getSourceDimensionName(xDimName->getName());
+
+ // Generate "given name" the same way as in dptabres.
+ //TODO: Should use a stored name when available
+
+ sal_Int16 eFunc = ScUnoHelpFunctions::GetShortProperty(
+ xDimProp, SC_UNO_DP_FUNCTION2,
+ sheet::GeneralFunction2::NONE );
+ rGivenName = lcl_GetDataFieldName( rSourceName, eFunc );
+}
+
+bool ScDPOutput::IsFilterButton( const ScAddress& rPos )
+{
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+ if ( nTab != aStartPos.Tab() || !bDoFilter )
+ return false; // wrong sheet or no button at all
+
+ // filter button is at top left
+ return ( nCol == aStartPos.Col() && nRow == aStartPos.Row() );
+}
+
+tools::Long ScDPOutput::GetHeaderDim( const ScAddress& rPos, sheet::DataPilotFieldOrientation& rOrient )
+{
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+ if ( nTab != aStartPos.Tab() )
+ return -1; // wrong sheet
+
+ // calculate output positions and sizes
+
+ CalcSizes();
+
+ // test for column header
+
+ if ( nRow == nTabStartRow && nCol >= nDataStartCol && o3tl::make_unsigned(nCol) < nDataStartCol + pColFields.size())
+ {
+ rOrient = sheet::DataPilotFieldOrientation_COLUMN;
+ tools::Long nField = nCol - nDataStartCol;
+ return pColFields[nField].mnDim;
+ }
+
+ // test for row header
+
+ if ( nRow+1 == nDataStartRow && nCol >= nTabStartCol && o3tl::make_unsigned(nCol) < nTabStartCol + pRowFields.size() )
+ {
+ rOrient = sheet::DataPilotFieldOrientation_ROW;
+ tools::Long nField = nCol - nTabStartCol;
+ return pRowFields[nField].mnDim;
+ }
+
+ // test for page field
+
+ SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 );
+ if ( nCol == aStartPos.Col() && nRow >= nPageStartRow && o3tl::make_unsigned(nRow) < nPageStartRow + pPageFields.size() )
+ {
+ rOrient = sheet::DataPilotFieldOrientation_PAGE;
+ tools::Long nField = nRow - nPageStartRow;
+ return pPageFields[nField].mnDim;
+ }
+
+ //TODO: single data field (?)
+
+ rOrient = sheet::DataPilotFieldOrientation_HIDDEN;
+ return -1; // invalid
+}
+
+bool ScDPOutput::GetHeaderDrag( const ScAddress& rPos, bool bMouseLeft, bool bMouseTop,
+ tools::Long nDragDim,
+ tools::Rectangle& rPosRect, sheet::DataPilotFieldOrientation& rOrient, tools::Long& rDimPos )
+{
+ // Rectangle instead of ScRange for rPosRect to allow for negative values
+
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+ if ( nTab != aStartPos.Tab() )
+ return false; // wrong sheet
+
+ // calculate output positions and sizes
+
+ CalcSizes();
+
+ // test for column header
+
+ if ( nCol >= nDataStartCol && nCol <= nTabEndCol &&
+ nRow + 1 >= nMemberStartRow && o3tl::make_unsigned(nRow) < nMemberStartRow + pColFields.size())
+ {
+ tools::Long nField = nRow - nMemberStartRow;
+ if (nField < 0)
+ {
+ nField = 0;
+ bMouseTop = true;
+ }
+ //TODO: find start of dimension
+
+ rPosRect = tools::Rectangle( nDataStartCol, nMemberStartRow + nField,
+ nTabEndCol, nMemberStartRow + nField -1 );
+
+ bool bFound = false; // is this within the same orientation?
+ bool bBeforeDrag = false;
+ bool bAfterDrag = false;
+ for (tools::Long nPos=0; o3tl::make_unsigned(nPos)<pColFields.size() && !bFound; nPos++)
+ {
+ if (pColFields[nPos].mnDim == nDragDim)
+ {
+ bFound = true;
+ if ( nField < nPos )
+ bBeforeDrag = true;
+ else if ( nField > nPos )
+ bAfterDrag = true;
+ }
+ }
+
+ if ( bFound )
+ {
+ if (!bBeforeDrag)
+ {
+ rPosRect.AdjustBottom( 1 );
+ if (bAfterDrag)
+ rPosRect.AdjustTop( 1 );
+ }
+ }
+ else
+ {
+ if ( !bMouseTop )
+ {
+ rPosRect.AdjustTop( 1 );
+ rPosRect.AdjustBottom( 1 );
+ ++nField;
+ }
+ }
+
+ rOrient = sheet::DataPilotFieldOrientation_COLUMN;
+ rDimPos = nField; //!...
+ return true;
+ }
+
+ // test for row header
+
+ // special case if no row fields
+ bool bSpecial = ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow &&
+ pRowFields.empty() && nCol == nTabStartCol && bMouseLeft );
+
+ if ( bSpecial || ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow &&
+ nCol + 1 >= nTabStartCol && o3tl::make_unsigned(nCol) < nTabStartCol + pRowFields.size() ) )
+ {
+ tools::Long nField = nCol - nTabStartCol;
+ //TODO: find start of dimension
+
+ rPosRect = tools::Rectangle( nTabStartCol + nField, nDataStartRow - 1,
+ nTabStartCol + nField - 1, nTabEndRow );
+
+ bool bFound = false; // is this within the same orientation?
+ bool bBeforeDrag = false;
+ bool bAfterDrag = false;
+ for (tools::Long nPos=0; o3tl::make_unsigned(nPos)<pRowFields.size() && !bFound; nPos++)
+ {
+ if (pRowFields[nPos].mnDim == nDragDim)
+ {
+ bFound = true;
+ if ( nField < nPos )
+ bBeforeDrag = true;
+ else if ( nField > nPos )
+ bAfterDrag = true;
+ }
+ }
+
+ if ( bFound )
+ {
+ if (!bBeforeDrag)
+ {
+ rPosRect.AdjustRight( 1 );
+ if (bAfterDrag)
+ rPosRect.AdjustLeft( 1 );
+ }
+ }
+ else
+ {
+ if ( !bMouseLeft )
+ {
+ rPosRect.AdjustLeft( 1 );
+ rPosRect.AdjustRight( 1 );
+ ++nField;
+ }
+ }
+
+ rOrient = sheet::DataPilotFieldOrientation_ROW;
+ rDimPos = nField; //!...
+ return true;
+ }
+
+ // test for page fields
+
+ SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 );
+ if ( nCol >= aStartPos.Col() && nCol <= nTabEndCol &&
+ nRow + 1 >= nPageStartRow && o3tl::make_unsigned(nRow) < nPageStartRow + pPageFields.size() )
+ {
+ tools::Long nField = nRow - nPageStartRow;
+ if (nField < 0)
+ {
+ nField = 0;
+ bMouseTop = true;
+ }
+ //TODO: find start of dimension
+
+ rPosRect = tools::Rectangle( aStartPos.Col(), nPageStartRow + nField,
+ nTabEndCol, nPageStartRow + nField - 1 );
+
+ bool bFound = false; // is this within the same orientation?
+ bool bBeforeDrag = false;
+ bool bAfterDrag = false;
+ for (tools::Long nPos=0; o3tl::make_unsigned(nPos)<pPageFields.size() && !bFound; nPos++)
+ {
+ if (pPageFields[nPos].mnDim == nDragDim)
+ {
+ bFound = true;
+ if ( nField < nPos )
+ bBeforeDrag = true;
+ else if ( nField > nPos )
+ bAfterDrag = true;
+ }
+ }
+
+ if ( bFound )
+ {
+ if (!bBeforeDrag)
+ {
+ rPosRect.AdjustBottom( 1 );
+ if (bAfterDrag)
+ rPosRect.AdjustTop( 1 );
+ }
+ }
+ else
+ {
+ if ( !bMouseTop )
+ {
+ rPosRect.AdjustTop( 1 );
+ rPosRect.AdjustBottom( 1 );
+ ++nField;
+ }
+ }
+
+ rOrient = sheet::DataPilotFieldOrientation_PAGE;
+ rDimPos = nField; //!...
+ return true;
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */