summaryrefslogtreecommitdiff
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.cxx2001
1 files changed, 2001 insertions, 0 deletions
diff --git a/sc/source/core/data/dpoutput.cxx b/sc/source/core/data/dpoutput.cxx
new file mode 100644
index 000000000000..c0455819285c
--- /dev/null
+++ b/sc/source/core/data/dpoutput.cxx
@@ -0,0 +1,2001 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sc.hxx"
+
+// INCLUDE ---------------------------------------------------------------
+
+#include "scitems.hxx"
+#include <svx/algitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/brshitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <unotools/transliterationwrapper.hxx>
+
+#include "dpoutput.hxx"
+#include "dptabsrc.hxx"
+#include "dpcachetable.hxx"
+#include "document.hxx"
+#include "patattr.hxx"
+#include "docpool.hxx"
+#include "markdata.hxx"
+#include "attrib.hxx"
+#include "formula/errorcodes.hxx" // errNoValue
+#include "miscuno.hxx"
+#include "globstr.hrc"
+#include "stlpool.hxx"
+#include "stlsheet.hxx"
+#include "collect.hxx"
+#include "scresid.hxx"
+#include "unonames.hxx"
+#include "sc.hrc"
+#include "scdpoutputimpl.hxx"
+#include "dpglobal.hxx"
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#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;
+using ::com::sun::star::uno::makeAny;
+using ::com::sun::star::uno::Any;
+using ::rtl::OUString;
+
+// -----------------------------------------------------------------------
+
+//! move to a header file
+//! use names from unonames.hxx?
+#define DP_PROP_FUNCTION "Function"
+#define DP_PROP_ORIENTATION "Orientation"
+#define DP_PROP_POSITION "Position"
+#define DP_PROP_USEDHIERARCHY "UsedHierarchy"
+#define DP_PROP_ISDATALAYOUT "IsDataLayoutDimension"
+#define DP_PROP_NUMBERFORMAT "NumberFormat"
+#define DP_PROP_FILTER "Filter"
+#define DP_PROP_COLUMNGRAND "ColumnGrand"
+#define DP_PROP_ROWGRAND "RowGrand"
+#define DP_PROP_SUBTOTALS "SubTotals"
+
+// -----------------------------------------------------------------------
+
+//! dynamic!!!
+#define SC_DPOUT_MAXLEVELS 256
+
+struct ScDPOutLevelData
+{
+ long nDim;
+ long nHier;
+ long nLevel;
+ long nDimPos;
+ uno::Sequence<sheet::MemberResult> aResult;
+ String maName; /// Name is the internal field name.
+ String aCaption; /// Caption is the name visible in the output table.
+ bool mbHasHiddenMember;
+
+ ScDPOutLevelData()
+ {
+ nDim = nHier = nLevel = nDimPos = -1;
+ mbHasHiddenMember = false;
+ }
+
+ sal_Bool operator<(const ScDPOutLevelData& r) const
+ { return nDimPos<r.nDimPos || ( nDimPos==r.nDimPos && nHier<r.nHier ) ||
+ ( nDimPos==r.nDimPos && nHier==r.nHier && nLevel<r.nLevel ); }
+
+ void Swap(ScDPOutLevelData& r)
+ { ScDPOutLevelData aTemp; aTemp = r; r = *this; *this = aTemp; }
+
+ //! bug (73840) in uno::Sequence - copy and then assign doesn't work!
+};
+
+// -----------------------------------------------------------------------
+
+void lcl_SetStyleById( ScDocument* pDoc, SCTAB nTab,
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ sal_uInt16 nStrId )
+{
+ if ( nCol1 > nCol2 || nRow1 > nRow2 )
+ {
+ OSL_FAIL("SetStyleById: invalid range");
+ return;
+ }
+
+ String aStyleName = ScGlobal::GetRscString( nStrId );
+ ScStyleSheetPool* pStlPool = pDoc->GetStyleSheetPool();
+ ScStyleSheet* pStyle = (ScStyleSheet*) pStlPool->Find( aStyleName, SFX_STYLE_FAMILY_PARA );
+ if (!pStyle)
+ {
+ // create new style (was in ScPivot::SetStyle)
+
+ pStyle = (ScStyleSheet*) &pStlPool->Make( aStyleName, SFX_STYLE_FAMILY_PARA,
+ SFXSTYLEBIT_USERDEF );
+ pStyle->SetParent( ScGlobal::GetRscString(STR_STYLENAME_STANDARD) );
+ SfxItemSet& rSet = pStyle->GetItemSet();
+ if ( nStrId==STR_PIVOT_STYLE_RESULT || nStrId==STR_PIVOT_STYLE_TITLE )
+ rSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
+ if ( nStrId==STR_PIVOT_STYLE_CATEGORY || nStrId==STR_PIVOT_STYLE_TITLE )
+ rSet.Put( SvxHorJustifyItem( SVX_HOR_JUSTIFY_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( NULL, nWidth, ::editeng::SOLID );
+ SvxBoxItem aBox( ATTR_BORDER );
+ aBox.SetLine(&aLine, BOX_LINE_LEFT);
+ aBox.SetLine(&aLine, BOX_LINE_TOP);
+ aBox.SetLine(&aLine, BOX_LINE_RIGHT);
+ aBox.SetLine(&aLine, BOX_LINE_BOTTOM);
+ SvxBoxInfoItem aBoxInfo( ATTR_BORDER_INNER );
+ aBoxInfo.SetValid(VALID_HORI,false);
+ aBoxInfo.SetValid(VALID_VERT,false);
+ aBoxInfo.SetValid(VALID_DISTANCE,false);
+
+ pDoc->ApplyFrameAreaTab( ScRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab ), &aBox, &aBoxInfo );
+}
+
+// -----------------------------------------------------------------------
+
+void lcl_FillNumberFormats( sal_uInt32*& rFormats, long& 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
+ //! use result sequence from ScDPOutLevelData!
+
+ uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults();
+
+ long nSize = aResult.getLength();
+ if (nSize)
+ {
+ // get names/formats for all data dimensions
+ //! merge this with the loop to collect ScDPOutLevelData?
+
+ String aDataNames[SC_DPOUT_MAXLEVELS];
+ sal_uInt32 nDataFormats[SC_DPOUT_MAXLEVELS];
+ long nDataCount = 0;
+ sal_Bool bAnySet = false;
+
+ long nDimCount = xDims->getCount();
+ for (long nDim=0; nDim<nDimCount; nDim++)
+ {
+ uno::Reference<uno::XInterface> xDim =
+ ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) );
+ 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 =
+ (sheet::DataPilotFieldOrientation) ScUnoHelpFunctions::GetEnumProperty(
+ xDimProp, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(DP_PROP_ORIENTATION)),
+ sheet::DataPilotFieldOrientation_HIDDEN );
+ if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
+ {
+ aDataNames[nDataCount] = String( xDimName->getName() );
+ long nFormat = ScUnoHelpFunctions::GetLongProperty(
+ xDimProp,
+ rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(DP_PROP_NUMBERFORMAT)) );
+ nDataFormats[nDataCount] = nFormat;
+ if ( nFormat != 0 )
+ bAnySet = sal_True;
+ ++nDataCount;
+ }
+ }
+ }
+
+ if ( bAnySet ) // forget everything if all formats are 0 (or no data dimensions)
+ {
+ const sheet::MemberResult* pArray = aResult.getConstArray();
+
+ String aName;
+ sal_uInt32* pNumFmt = new sal_uInt32[nSize];
+ if (nDataCount == 1)
+ {
+ // only one data dimension -> use its numberformat everywhere
+ long nFormat = nDataFormats[0];
+ for (long nPos=0; nPos<nSize; nPos++)
+ pNumFmt[nPos] = nFormat;
+ }
+ else
+ {
+ for (long nPos=0; nPos<nSize; nPos++)
+ {
+ // if CONTINUE bit is set, keep previous name
+ //! keep number format instead!
+ if ( !(pArray[nPos].Flags & sheet::MemberResultFlags::CONTINUE) )
+ aName = String( pArray[nPos].Name );
+
+ sal_uInt32 nFormat = 0;
+ for (long i=0; i<nDataCount; i++)
+ if (aName == aDataNames[i]) //! search more efficiently?
+ {
+ nFormat = nDataFormats[i];
+ break;
+ }
+ pNumFmt[nPos] = nFormat;
+ }
+ }
+
+ rFormats = pNumFmt;
+ rCount = nSize;
+ }
+ }
+}
+
+sal_uInt32 lcl_GetFirstNumberFormat( const uno::Reference<container::XIndexAccess>& xDims )
+{
+ long nDimCount = xDims->getCount();
+ for (long nDim=0; nDim<nDimCount; nDim++)
+ {
+ uno::Reference<uno::XInterface> xDim =
+ ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) );
+ uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
+ if ( xDimProp.is() )
+ {
+ sheet::DataPilotFieldOrientation eDimOrient =
+ (sheet::DataPilotFieldOrientation) ScUnoHelpFunctions::GetEnumProperty(
+ xDimProp, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(DP_PROP_ORIENTATION)),
+ sheet::DataPilotFieldOrientation_HIDDEN );
+ if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
+ {
+ long nFormat = ScUnoHelpFunctions::GetLongProperty(
+ xDimProp,
+ rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(DP_PROP_NUMBERFORMAT)) );
+
+ return nFormat; // use format from first found data dimension
+ }
+ }
+ }
+
+ return 0; // none found
+}
+
+void lcl_SortFields( ScDPOutLevelData* pFields, long nFieldCount )
+{
+ for (long i=0; i+1<nFieldCount; i++)
+ {
+ for (long j=0; j+i+1<nFieldCount; j++)
+ if ( pFields[j+1] < pFields[j] )
+ pFields[j].Swap( pFields[j+1] );
+ }
+}
+
+sal_Bool lcl_MemberEmpty( const uno::Sequence<sheet::MemberResult>& rSeq )
+{
+ // used to skip levels that have no members
+
+ long nLen = rSeq.getLength();
+ const sheet::MemberResult* pArray = rSeq.getConstArray();
+ for (long i=0; i<nLen; i++)
+ if (pArray[i].Flags & sheet::MemberResultFlags::HASMEMBER)
+ return false;
+
+ return sal_True; // no member data -> empty
+}
+
+uno::Sequence<sheet::MemberResult> lcl_GetSelectedPageAsResult( const uno::Reference<beans::XPropertySet>& xDimProp )
+{
+ uno::Sequence<sheet::MemberResult> aRet;
+ if ( xDimProp.is() )
+ {
+ try
+ {
+ //! merge with ScDPDimension::setPropertyValue?
+
+ uno::Any aValue = xDimProp->getPropertyValue( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(DP_PROP_FILTER)) );
+
+ uno::Sequence<sheet::TableFilterField> aSeq;
+ if (aValue >>= aSeq)
+ {
+ if ( aSeq.getLength() == 1 )
+ {
+ const sheet::TableFilterField& rField = aSeq[0];
+ if ( rField.Field == 0 && rField.Operator == sheet::FilterOperator_EQUAL && !rField.IsNumeric )
+ {
+ rtl::OUString aSelectedPage( rField.StringValue );
+ //! different name/caption string?
+ sheet::MemberResult aResult( aSelectedPage, aSelectedPage, 0 );
+ aRet = uno::Sequence<sheet::MemberResult>( &aResult, 1 );
+ }
+ }
+ // else return empty sequence
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ // recent addition - allow source to not handle it (no error)
+ }
+ }
+ return aRet;
+}
+
+ScDPOutput::ScDPOutput( ScDocument* pD, const uno::Reference<sheet::XDimensionsSupplier>& xSrc,
+ const ScAddress& rPos, sal_Bool bFilter ) :
+ pDoc( pD ),
+ xSource( xSrc ),
+ aStartPos( rPos ),
+ bDoFilter( bFilter ),
+ bResultsError( false ),
+ mbHasDataLayout(false),
+ pColNumFmt( NULL ),
+ pRowNumFmt( NULL ),
+ nColFmtCount( 0 ),
+ nRowFmtCount( 0 ),
+ nSingleNumFmt( 0 ),
+ bSizesValid( false ),
+ bSizeOverflow( false ),
+ mbHeaderLayout( false )
+{
+ nTabStartCol = nMemberStartCol = nDataStartCol = nTabEndCol = 0;
+ nTabStartRow = nMemberStartRow = nDataStartRow = nTabEndRow = 0;
+
+ pColFields = new ScDPOutLevelData[SC_DPOUT_MAXLEVELS];
+ pRowFields = new ScDPOutLevelData[SC_DPOUT_MAXLEVELS];
+ pPageFields = new ScDPOutLevelData[SC_DPOUT_MAXLEVELS];
+ nColFieldCount = 0;
+ nRowFieldCount = 0;
+ nPageFieldCount = 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() );
+ long nDimCount = xDims->getCount();
+ for (long nDim=0; nDim<nDimCount; nDim++)
+ {
+ uno::Reference<uno::XInterface> xDim =
+ ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) );
+ 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 =
+ (sheet::DataPilotFieldOrientation) ScUnoHelpFunctions::GetEnumProperty(
+ xDimProp, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(DP_PROP_ORIENTATION)),
+ sheet::DataPilotFieldOrientation_HIDDEN );
+ long nDimPos = ScUnoHelpFunctions::GetLongProperty( xDimProp,
+ rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(DP_PROP_POSITION)) );
+ sal_Bool bIsDataLayout = ScUnoHelpFunctions::GetBoolProperty(
+ xDimProp,
+ rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(DP_PROP_ISDATALAYOUT)) );
+ bool bHasHiddenMember = ScUnoHelpFunctions::GetBoolProperty(
+ xDimProp, OUString(RTL_CONSTASCII_USTRINGPARAM(SC_UNO_HAS_HIDDEN_MEMBER)));
+
+ if ( eDimOrient != sheet::DataPilotFieldOrientation_HIDDEN )
+ {
+ uno::Reference<container::XIndexAccess> xHiers =
+ new ScNameToIndexAccess( xDimSupp->getHierarchies() );
+ long nHierarchy = ScUnoHelpFunctions::GetLongProperty(
+ xDimProp,
+ rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(DP_PROP_USEDHIERARCHY)) );
+ if ( nHierarchy >= xHiers->getCount() )
+ nHierarchy = 0;
+
+ uno::Reference<uno::XInterface> xHier =
+ ScUnoHelpFunctions::AnyToInterface(
+ xHiers->getByIndex(nHierarchy) );
+ uno::Reference<sheet::XLevelsSupplier> xHierSupp( xHier, uno::UNO_QUERY );
+ if ( xHierSupp.is() )
+ {
+ uno::Reference<container::XIndexAccess> xLevels =
+ new ScNameToIndexAccess( xHierSupp->getLevels() );
+ long nLevCount = xLevels->getCount();
+ for (long nLev=0; nLev<nLevCount; nLev++)
+ {
+ uno::Reference<uno::XInterface> xLevel =
+ ScUnoHelpFunctions::AnyToInterface(
+ xLevels->getByIndex(nLev) );
+ uno::Reference<container::XNamed> xLevNam( xLevel, uno::UNO_QUERY );
+ uno::Reference<sheet::XDataPilotMemberResults> xLevRes(
+ xLevel, uno::UNO_QUERY );
+ if ( xLevNam.is() && xLevRes.is() )
+ {
+ String 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,
+ OUString(RTL_CONSTASCII_USTRINGPARAM(SC_UNO_LAYOUTNAME)), aName );
+
+ bool bRowFieldHasMember = false;
+ switch ( eDimOrient )
+ {
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ pColFields[nColFieldCount].nDim = nDim;
+ pColFields[nColFieldCount].nHier = nHierarchy;
+ pColFields[nColFieldCount].nLevel = nLev;
+ pColFields[nColFieldCount].nDimPos = nDimPos;
+ pColFields[nColFieldCount].aResult = xLevRes->getResults();
+ pColFields[nColFieldCount].maName = aName;
+ pColFields[nColFieldCount].aCaption= aCaption;
+ pColFields[nColFieldCount].mbHasHiddenMember = bHasHiddenMember;
+ if (!lcl_MemberEmpty(pColFields[nColFieldCount].aResult))
+ ++nColFieldCount;
+ break;
+ case sheet::DataPilotFieldOrientation_ROW:
+ pRowFields[nRowFieldCount].nDim = nDim;
+ pRowFields[nRowFieldCount].nHier = nHierarchy;
+ pRowFields[nRowFieldCount].nLevel = nLev;
+ pRowFields[nRowFieldCount].nDimPos = nDimPos;
+ pRowFields[nRowFieldCount].aResult = xLevRes->getResults();
+ pRowFields[nRowFieldCount].maName = aName;
+ pRowFields[nRowFieldCount].aCaption= aCaption;
+ pRowFields[nRowFieldCount].mbHasHiddenMember = bHasHiddenMember;
+ if (!lcl_MemberEmpty(pRowFields[nRowFieldCount].aResult))
+ {
+ ++nRowFieldCount;
+ bRowFieldHasMember = true;
+ }
+ break;
+ case sheet::DataPilotFieldOrientation_PAGE:
+ pPageFields[nPageFieldCount].nDim = nDim;
+ pPageFields[nPageFieldCount].nHier = nHierarchy;
+ pPageFields[nPageFieldCount].nLevel = nLev;
+ pPageFields[nPageFieldCount].nDimPos = nDimPos;
+ pPageFields[nPageFieldCount].aResult = lcl_GetSelectedPageAsResult(xDimProp);
+ pPageFields[nPageFieldCount].maName = aName;
+ pPageFields[nPageFieldCount].aCaption= aCaption;
+ pPageFields[nPageFieldCount].mbHasHiddenMember = bHasHiddenMember;
+ // no check on results for page fields
+ ++nPageFieldCount;
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ // get number formats from data dimensions
+ if ( bIsDataLayout )
+ {
+ if (bRowFieldHasMember)
+ mbHasDataLayout = true;
+
+ DBG_ASSERT( 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 );
+ }
+ }
+ }
+ lcl_SortFields( pColFields, nColFieldCount );
+ lcl_SortFields( pRowFields, nRowFieldCount );
+ lcl_SortFields( pPageFields, nPageFieldCount );
+
+ // get data results:
+
+ try
+ {
+ aData = xResult->getResults();
+ }
+ catch (uno::RuntimeException&)
+ {
+ bResultsError = sal_True;
+ }
+ }
+
+ // get "DataDescription" property (may be missing in external sources)
+
+ uno::Reference<beans::XPropertySet> xSrcProp( xSource, uno::UNO_QUERY );
+ if ( xSrcProp.is() )
+ {
+ try
+ {
+ uno::Any aAny = xSrcProp->getPropertyValue(
+ rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(SC_UNO_DATADESC)) );
+ rtl::OUString aUStr;
+ aAny >>= aUStr;
+ aDataDescription = String( aUStr );
+ }
+ catch(uno::Exception&)
+ {
+ }
+ }
+}
+
+ScDPOutput::~ScDPOutput()
+{
+ delete[] pColFields;
+ delete[] pRowFields;
+ delete[] pPageFields;
+
+ delete[] pColNumFmt;
+ delete[] pRowNumFmt;
+}
+
+void ScDPOutput::SetPosition( const ScAddress& rPos )
+{
+ aStartPos = rPos;
+ bSizesValid = bSizeOverflow = false;
+}
+
+void ScDPOutput::DataCell( SCCOL nCol, SCROW nRow, SCTAB nTab, const sheet::DataResult& rData )
+{
+ long nFlags = rData.Flags;
+ if ( nFlags & sheet::DataResultFlags::ERROR )
+ {
+ pDoc->SetError( nCol, nRow, nTab, errNoValue );
+ }
+ else if ( nFlags & sheet::DataResultFlags::HASDATA )
+ {
+ pDoc->SetValue( nCol, nRow, nTab, rData.Value );
+
+ // use number formats from source
+
+ DBG_ASSERT( bSizesValid, "DataCell: !bSizesValid" );
+ sal_uInt32 nFormat = 0;
+ if ( pColNumFmt )
+ {
+ if ( nCol >= nDataStartCol )
+ {
+ long nIndex = nCol - nDataStartCol;
+ if ( nIndex < nColFmtCount )
+ nFormat = pColNumFmt[nIndex];
+ }
+ }
+ else if ( pRowNumFmt )
+ {
+ if ( nRow >= nDataStartRow )
+ {
+ long nIndex = nRow - nDataStartRow;
+ if ( nIndex < nRowFmtCount )
+ nFormat = pRowNumFmt[nIndex];
+ }
+ }
+ else if ( nSingleNumFmt != 0 )
+ nFormat = nSingleNumFmt; // single format is used everywhere
+ if ( nFormat != 0 )
+ 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, sal_Bool bColHeader, long nLevel )
+{
+ long nFlags = rData.Flags;
+
+ if ( nFlags & sheet::MemberResultFlags::HASMEMBER )
+ {
+ pDoc->SetString( nCol, nRow, nTab, rData.Caption);
+ }
+
+ if ( nFlags & sheet::MemberResultFlags::SUBTOTAL )
+ {
+ OutputImpl outputimp( pDoc, nTab,
+ nTabStartCol, nTabStartRow, nMemberStartCol, nMemberStartRow,
+ nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow );
+ //! limit frames to horizontal or vertical?
+ if (bColHeader)
+ {
+ outputimp.OutputBlockFrame( nCol,nMemberStartRow+(SCROW)nLevel, nCol,nDataStartRow-1 );
+
+ lcl_SetStyleById( pDoc,nTab, nCol,nMemberStartRow+(SCROW)nLevel, nCol,nDataStartRow-1,
+ STR_PIVOT_STYLE_TITLE );
+ lcl_SetStyleById( pDoc,nTab, nCol,nDataStartRow, nCol,nTabEndRow,
+ STR_PIVOT_STYLE_RESULT );
+ }
+ else
+ {
+ outputimp.OutputBlockFrame( nMemberStartCol+(SCCOL)nLevel,nRow, nDataStartCol-1,nRow );
+ lcl_SetStyleById( pDoc,nTab, nMemberStartCol+(SCCOL)nLevel,nRow, nDataStartCol-1,nRow,
+ STR_PIVOT_STYLE_TITLE );
+ lcl_SetStyleById( pDoc,nTab, nDataStartCol,nRow, nTabEndCol,nRow,
+ STR_PIVOT_STYLE_RESULT );
+ }
+ }
+}
+
+void ScDPOutput::FieldCell( SCCOL nCol, SCROW nRow, SCTAB nTab, const String& rCaption,
+ bool bInTable, bool bPopup, bool bHasHiddenMember )
+{
+ pDoc->SetString( nCol, nRow, nTab, rCaption );
+ if (bInTable)
+ lcl_SetFrame( pDoc,nTab, nCol,nRow, nCol,nRow, 20 );
+
+ // Button
+ sal_uInt16 nMergeFlag = SC_MF_BUTTON;
+ if (bPopup)
+ nMergeFlag |= SC_MF_BUTTON_POPUP;
+ if (bHasHiddenMember)
+ nMergeFlag |= SC_MF_HIDDEN_MEMBER;
+ pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, nMergeFlag);
+
+ lcl_SetStyleById( pDoc,nTab, nCol,nRow, nCol,nRow, STR_PIVOT_STYLE_FIELDNAME );
+}
+
+void lcl_DoFilterButton( ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ pDoc->SetString( nCol, nRow, nTab, ScGlobal::GetRscString(STR_CELL_FILTER) );
+ pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, SC_MF_BUTTON);
+}
+
+void ScDPOutput::CalcSizes()
+{
+ if (!bSizesValid)
+ {
+ // get column size of data from first row
+ //! 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() && nColFieldCount == 0)
+ // Insert an extra header row only when there is no column field.
+ nHeaderSize = 2;
+
+ // calculate output positions and sizes
+
+ long nPageSize = 0; //! use page fields!
+ if ( bDoFilter || nPageFieldCount )
+ {
+ nPageSize += nPageFieldCount + 1; // plus one empty row
+ if ( bDoFilter )
+ ++nPageSize; // filter button above the page fields
+ }
+
+ if ( aStartPos.Col() + nRowFieldCount + nColCount - 1 > MAXCOL ||
+ aStartPos.Row() + nPageSize + nHeaderSize + nColFieldCount + nRowCount > MAXROW )
+ {
+ bSizeOverflow = sal_True;
+ }
+
+ nTabStartCol = aStartPos.Col();
+ nTabStartRow = aStartPos.Row() + (SCROW)nPageSize; // below page fields
+ nMemberStartCol = nTabStartCol;
+ nMemberStartRow = nTabStartRow + (SCROW) nHeaderSize;
+ nDataStartCol = nMemberStartCol + (SCCOL)nRowFieldCount;
+ nDataStartRow = nMemberStartRow + (SCROW)nColFieldCount;
+ if ( nColCount > 0 )
+ nTabEndCol = nDataStartCol + (SCCOL)nColCount - 1;
+ else
+ nTabEndCol = nDataStartCol; // single column will remain empty
+ // if page fields are involved, include the page selection cells
+ if ( nPageFieldCount > 0 && nTabEndCol < nTabStartCol + 1 )
+ nTabEndCol = nTabStartCol + 1;
+ if ( nRowCount > 0 )
+ nTabEndRow = nDataStartRow + (SCROW)nRowCount - 1;
+ else
+ nTabEndRow = nDataStartRow; // single row will remain empty
+ bSizesValid = sal_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()
+{
+ long nField;
+ 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
+ //! include IDF_OBJECTS ???
+ pDoc->DeleteAreaTab( aStartPos.Col(), aStartPos.Row(), nTabEndCol, nTabEndRow, nTab, IDF_ALL );
+
+ if ( bDoFilter )
+ lcl_DoFilterButton( pDoc, aStartPos.Col(), aStartPos.Row(), nTab );
+
+ // output data results:
+
+ for (long nRow=0; nRow<nRowCount; nRow++)
+ {
+ SCROW nRowPos = nDataStartRow + (SCROW)nRow; //! check for overflow
+ const sheet::DataResult* pColAry = pRowAry[nRow].getConstArray();
+ long nThisColCount = pRowAry[nRow].getLength();
+ DBG_ASSERT( nThisColCount == nColCount, "count mismatch" ); //! ???
+ for (long nCol=0; nCol<nThisColCount; nCol++)
+ {
+ SCCOL nColPos = nDataStartCol + (SCCOL)nCol; //! check for overflow
+ DataCell( nColPos, nRowPos, nTab, pColAry[nCol] );
+ }
+ }
+ // output page fields:
+
+ for (nField=0; nField<nPageFieldCount; 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].aCaption, false, false, pPageFields[nField].mbHasHiddenMember );
+ SCCOL nFldCol = nHdrCol + 1;
+
+ String aPageValue;
+ if ( pPageFields[nField].aResult.getLength() == 1 )
+ aPageValue = pPageFields[nField].aResult[0].Caption;
+ else
+ aPageValue = String( ScResId( SCSTR_ALL ) ); //! separate string?
+
+ pDoc->SetString( nFldCol, nHdrRow, nTab, aPageValue );
+
+ lcl_SetFrame( pDoc,nTab, nFldCol,nHdrRow, nFldCol,nHdrRow, 20 );
+ pDoc->ApplyAttr( nFldCol, nHdrRow, nTab, ScMergeFlagAttr(SC_MF_AUTO) );
+ //! which style?
+ }
+
+ // data description
+ // (may get overwritten by first row field)
+
+ String aDesc = aDataDescription;
+ if ( !aDesc.Len() )
+ {
+ //! use default string ("result") ?
+ }
+ pDoc->SetString( nTabStartCol, nTabStartRow, nTab, aDesc );
+
+ // set STR_PIVOT_STYLE_INNER for whole data area (subtotals are overwritten)
+
+ if ( nDataStartRow > nTabStartRow )
+ lcl_SetStyleById( pDoc, nTab, nTabStartCol, nTabStartRow, nTabEndCol, nDataStartRow-1,
+ STR_PIVOT_STYLE_TOP );
+ lcl_SetStyleById( pDoc, nTab, nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow,
+ STR_PIVOT_STYLE_INNER );
+
+ // output column headers:
+ OutputImpl outputimp( pDoc, nTab,
+ nTabStartCol, nTabStartRow, nMemberStartCol, nMemberStartRow,
+ nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow );
+ for (nField=0; nField<nColFieldCount; nField++)
+ {
+ SCCOL nHdrCol = nDataStartCol + (SCCOL)nField; //! check for overflow
+ FieldCell( nHdrCol, nTabStartRow, nTab, pColFields[nField].aCaption, true, true, pColFields[nField].mbHasHiddenMember );
+
+ SCROW nRowPos = nMemberStartRow + (SCROW)nField; //! check for overflow
+ const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nField].aResult;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+ long nThisColCount = rSequence.getLength();
+ DBG_ASSERT( nThisColCount == nColCount, "count mismatch" ); //! ???
+ for (long nCol=0; nCol<nThisColCount; nCol++)
+ {
+ SCCOL nColPos = nDataStartCol + (SCCOL)nCol; //! check for overflow
+ HeaderCell( nColPos, nRowPos, nTab, pArray[nCol], true, nField );
+ if ( ( pArray[nCol].Flags & sheet::MemberResultFlags::HASMEMBER ) &&
+ !( pArray[nCol].Flags & sheet::MemberResultFlags::SUBTOTAL ) )
+ {
+ long nEnd = nCol;
+ while ( nEnd+1 < nThisColCount && ( pArray[nEnd+1].Flags & sheet::MemberResultFlags::CONTINUE ) )
+ ++nEnd;
+ SCCOL nEndColPos = nDataStartCol + (SCCOL)nEnd; //! check for overflow
+ if ( nField+1 < nColFieldCount )
+ {
+ if ( nField == nColFieldCount - 2 )
+ {
+ outputimp.AddCol( nColPos );
+ if ( nColPos + 1 == nEndColPos )
+ outputimp.OutputBlockFrame( nColPos,nRowPos, nEndColPos,nRowPos+1, sal_True );
+ }
+ else
+ outputimp.OutputBlockFrame( nColPos,nRowPos, nEndColPos,nRowPos );
+
+ lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nEndColPos,nDataStartRow-1, STR_PIVOT_STYLE_CATEGORY );
+ }
+ else
+ lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nColPos,nDataStartRow-1, STR_PIVOT_STYLE_CATEGORY );
+ }
+ else if ( pArray[nCol].Flags & sheet::MemberResultFlags::SUBTOTAL )
+ outputimp.AddCol( nColPos );
+ }
+ if ( nField== 0 && nColFieldCount == 1 )
+ outputimp.OutputBlockFrame( nDataStartCol,nTabStartRow, nTabEndCol,nRowPos-1 );
+ }
+
+ // output row headers:
+ std::vector<sal_Bool> vbSetBorder;
+ vbSetBorder.resize( nTabEndRow - nDataStartRow + 1, false );
+ for (nField=0; nField<nRowFieldCount; nField++)
+ {
+ bool bDataLayout = mbHasDataLayout && (nField == nRowFieldCount-1);
+
+ SCCOL nHdrCol = nTabStartCol + (SCCOL)nField; //! check for overflow
+ SCROW nHdrRow = nDataStartRow - 1;
+ FieldCell( nHdrCol, nHdrRow, nTab, pRowFields[nField].aCaption, true, !bDataLayout,
+ pRowFields[nField].mbHasHiddenMember );
+
+ SCCOL nColPos = nMemberStartCol + (SCCOL)nField; //! check for overflow
+ const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].aResult;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+ long nThisRowCount = rSequence.getLength();
+ DBG_ASSERT( nThisRowCount == nRowCount, "count mismatch" ); //! ???
+ for (long nRow=0; nRow<nThisRowCount; nRow++)
+ {
+ SCROW nRowPos = nDataStartRow + (SCROW)nRow; //! 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 < nRowFieldCount )
+ {
+ long nEnd = nRow;
+ while ( nEnd+1 < nThisRowCount && ( pArray[nEnd+1].Flags & sheet::MemberResultFlags::CONTINUE ) )
+ ++nEnd;
+ SCROW nEndRowPos = nDataStartRow + (SCROW)nEnd; //! check for overflow
+ outputimp.AddRow( nRowPos );
+ if ( vbSetBorder[ nRow ] == false )
+ {
+ outputimp.OutputBlockFrame( nColPos, nRowPos, nTabEndCol, nEndRowPos );
+ vbSetBorder[ nRow ] = sal_True;
+ }
+ outputimp.OutputBlockFrame( nColPos, nRowPos, nColPos, nEndRowPos );
+
+ if ( nField == nRowFieldCount - 2 )
+ outputimp.OutputBlockFrame( nColPos+1, nRowPos, nColPos+1, nEndRowPos );
+
+ lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nDataStartCol-1,nEndRowPos, STR_PIVOT_STYLE_CATEGORY );
+ }
+ else
+ lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nDataStartCol-1,nRowPos, STR_PIVOT_STYLE_CATEGORY );
+ }
+ else if ( pArray[nRow].Flags & sheet::MemberResultFlags::SUBTOTAL )
+ outputimp.AddRow( nRowPos );
+ }
+ }
+
+ 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:
+ DBG_ASSERT(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;
+}
+
+long ScDPOutput::GetHeaderRows()
+{
+ return nPageFieldCount + ( bDoFilter ? 1 : 0 );
+}
+
+void ScDPOutput::GetMemberResultNames( ScStrCollection& rNames, 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.
+
+ uno::Sequence<sheet::MemberResult> aMemberResults;
+ bool bFound = false;
+ long nField;
+
+ // look in column fields
+
+ for (nField=0; nField<nColFieldCount && !bFound; nField++)
+ if ( pColFields[nField].nDim == nDimension )
+ {
+ aMemberResults = pColFields[nField].aResult;
+ bFound = true;
+ }
+
+ // look in row fields
+
+ for (nField=0; nField<nRowFieldCount && !bFound; nField++)
+ if ( pRowFields[nField].nDim == nDimension )
+ {
+ aMemberResults = pRowFields[nField].aResult;
+ bFound = true;
+ }
+
+ // collect the member names
+
+ if ( bFound )
+ {
+ const sheet::MemberResult* pArray = aMemberResults.getConstArray();
+ long nResultCount = aMemberResults.getLength();
+
+ for (long nItem=0; nItem<nResultCount; nItem++)
+ {
+ if ( pArray[nItem].Flags & sheet::MemberResultFlags::HASMEMBER )
+ {
+ StrData* pNew = new StrData( pArray[nItem].Name );
+ if ( !rNames.Insert( pNew ) )
+ delete pNew;
+ }
+ }
+ }
+}
+
+void ScDPOutput::SetHeaderLayout(bool bUseGrid)
+{
+ mbHeaderLayout = bUseGrid;
+ bSizesValid = false;
+}
+
+bool ScDPOutput::GetHeaderLayout() const
+{
+ return mbHeaderLayout;
+}
+
+void lcl_GetTableVars( sal_Int32& rGrandTotalCols, sal_Int32& rGrandTotalRows, sal_Int32& rDataLayoutIndex,
+ std::vector<String>& rDataNames, std::vector<String>& 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 );
+ sal_Bool bColGrand = ScUnoHelpFunctions::GetBoolProperty( xSrcProp,
+ rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(DP_PROP_COLUMNGRAND)) );
+ if ( bColGrand )
+ rGrandTotalCols = 1; // default if data layout not in columns
+
+ sal_Bool bRowGrand = ScUnoHelpFunctions::GetBoolProperty( xSrcProp,
+ rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(DP_PROP_ROWGRAND)) );
+ if ( bRowGrand )
+ rGrandTotalRows = 1; // default if data layout not in rows
+
+ if ( xSource.is() )
+ {
+ // find index and orientation of "data layout" dimension, count data dimensions
+
+ sal_Int32 nDataCount = 0;
+
+ uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xSource->getDimensions() );
+ long nDimCount = xDims->getCount();
+ for (long nDim=0; nDim<nDimCount; nDim++)
+ {
+ uno::Reference<uno::XInterface> xDim =
+ ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) );
+ uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
+ if ( xDimProp.is() )
+ {
+ sheet::DataPilotFieldOrientation eDimOrient =
+ (sheet::DataPilotFieldOrientation) ScUnoHelpFunctions::GetEnumProperty(
+ xDimProp, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(DP_PROP_ORIENTATION)),
+ sheet::DataPilotFieldOrientation_HIDDEN );
+ if ( ScUnoHelpFunctions::GetBoolProperty( xDimProp,
+ rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(DP_PROP_ISDATALAYOUT)) ) )
+ {
+ rDataLayoutIndex = nDim;
+ rDataOrient = eDimOrient;
+ }
+ if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
+ {
+ String aSourceName;
+ String aGivenName;
+ ScDPOutput::GetDataDimensionNames( aSourceName, aGivenName, xDim );
+ 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);
+ sal_Int32 nSize = aFilters.size();
+
+ DataPilotTableResultData aResData;
+ aResData.FieldFilters.realloc(nSize);
+ for (sal_Int32 i = 0; i < nSize; ++i)
+ aResData.FieldFilters[i] = aFilters[i];
+
+ aResData.DataFieldIndex = 0;
+ Reference<beans::XPropertySet> xPropSet(xSource, UNO_QUERY);
+ if (xPropSet.is())
+ {
+ sal_Int32 nDataFieldCount = ScUnoHelpFunctions::GetLongProperty( xPropSet,
+ rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(SC_UNO_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 = makeAny(aResData);
+ return;
+ }
+ case DataPilotTablePositionType::COLUMN_HEADER:
+ {
+ long nField = nRow - nTabStartRow - 1; // 1st line is used for the buttons
+ if (nField < 0)
+ break;
+
+ const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nField].aResult;
+ if (rSequence.getLength() == 0)
+ break;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+
+ 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 = OUString(pArray[nItem].Name);
+ aHeaderData.Flags = pArray[nItem].Flags;
+ aHeaderData.Dimension = static_cast<sal_Int32>(pColFields[nField].nDim);
+ aHeaderData.Hierarchy = static_cast<sal_Int32>(pColFields[nField].nHier);
+ aHeaderData.Level = static_cast<sal_Int32>(pColFields[nField].nLevel);
+
+ rPosData.PositionData = makeAny(aHeaderData);
+ return;
+ }
+ case DataPilotTablePositionType::ROW_HEADER:
+ {
+ long nField = nCol - nTabStartCol;
+ if (nField < 0)
+ break;
+
+ const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].aResult;
+ if (rSequence.getLength() == 0)
+ break;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+
+ 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 = OUString(pArray[nItem].Name);
+ aHeaderData.Flags = pArray[nItem].Flags;
+ aHeaderData.Dimension = static_cast<sal_Int32>(pRowFields[nField].nDim);
+ aHeaderData.Hierarchy = static_cast<sal_Int32>(pRowFields[nField].nHier);
+ aHeaderData.Level = static_cast<sal_Int32>(pRowFields[nField].nLevel);
+
+ rPosData.PositionData = makeAny(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,
+ rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(SC_UNO_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<String> aDataNames;
+ std::vector<String> 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 (SCCOL nColField = 0; nColField < nColFieldCount && bFilterByCol; ++nColField)
+ {
+ if (pColFields[nColField].nDim == 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].aResult;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+
+ DBG_ASSERT(nDataStartCol + rSequence.getLength() - 1 == nTabEndCol, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
+
+ long nItem = nCol - nDataStartCol;
+ // get origin of "continue" fields
+ while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
+ --nItem;
+
+ filter.MatchValue = pArray[nItem].Name;
+ rFilters.push_back(filter);
+ }
+
+ // row fields
+ for (SCROW nRowField = 0; nRowField < nRowFieldCount && bFilterByRow; ++nRowField)
+ {
+ if (pRowFields[nRowField].nDim == 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].aResult;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+
+ DBG_ASSERT(nDataStartRow + rSequence.getLength() - 1 == nTabEndRow, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
+
+ long nItem = nRow - nDataStartRow;
+ // get origin of "continue" fields
+ while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
+ --nItem;
+
+ filter.MatchValue = pArray[nItem].Name;
+ rFilters.push_back(filter);
+ }
+
+ return true;
+}
+
+//
+// helper functions for ScDPOutput::GetPivotData
+//
+
+bool lcl_IsNamedDataField( const ScDPGetPivotDataField& rTarget, const String& rSourceName, const String& rGivenName )
+{
+ // match one of the names, ignoring case
+ return ScGlobal::GetpTransliteration()->isEqual( rTarget.maFieldName, rSourceName ) ||
+ ScGlobal::GetpTransliteration()->isEqual( rTarget.maFieldName, rGivenName );
+}
+
+bool lcl_IsNamedCategoryField( const ScDPGetPivotDataField& rFilter, const ScDPOutLevelData& rField )
+{
+ return ScGlobal::GetpTransliteration()->isEqual( rFilter.maFieldName, rField.maName );
+}
+
+bool lcl_IsCondition( const sheet::MemberResult& rResultEntry, const ScDPGetPivotDataField& rFilter )
+{
+ //! handle numeric conditions?
+ return ScGlobal::GetpTransliteration()->isEqual( rResultEntry.Name, rFilter.maValStr );
+}
+
+bool lcl_CheckPageField( const ScDPOutLevelData& rField,
+ const std::vector< ScDPGetPivotDataField >& rFilters,
+ std::vector< sal_Bool >& rFilterUsed )
+{
+ for (SCSIZE nFilterPos = 0; nFilterPos < rFilters.size(); ++nFilterPos)
+ {
+ if ( lcl_IsNamedCategoryField( rFilters[nFilterPos], rField ) )
+ {
+ rFilterUsed[nFilterPos] = sal_True;
+
+ // page field result is empty or the selection as single entry (see lcl_GetSelectedPageAsResult)
+ if ( rField.aResult.getLength() == 1 &&
+ lcl_IsCondition( rField.aResult[0], rFilters[nFilterPos] ) )
+ {
+ return true; // condition matches page selection
+ }
+ else
+ {
+ return false; // no page selection or different entry
+ }
+ }
+ }
+
+ return true; // valid if the page field doesn't have a filter
+}
+
+uno::Sequence<sheet::GeneralFunction> lcl_GetSubTotals(
+ const uno::Reference<sheet::XDimensionsSupplier>& xSource, const ScDPOutLevelData& rField )
+{
+ uno::Sequence<sheet::GeneralFunction> aSubTotals;
+
+ uno::Reference<sheet::XHierarchiesSupplier> xHierSupp;
+ uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
+ uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
+ sal_Int32 nIntCount = xIntDims->getCount();
+ if ( rField.nDim < nIntCount )
+ {
+ uno::Reference<uno::XInterface> xIntDim = ScUnoHelpFunctions::AnyToInterface(
+ xIntDims->getByIndex( rField.nDim ) );
+ xHierSupp = uno::Reference<sheet::XHierarchiesSupplier>( xIntDim, uno::UNO_QUERY );
+ }
+ DBG_ASSERT( xHierSupp.is(), "dimension not found" );
+
+ sal_Int32 nHierCount = 0;
+ uno::Reference<container::XIndexAccess> xHiers;
+ if ( xHierSupp.is() )
+ {
+ uno::Reference<container::XNameAccess> xHiersName = xHierSupp->getHierarchies();
+ xHiers = new ScNameToIndexAccess( xHiersName );
+ nHierCount = xHiers->getCount();
+ }
+ uno::Reference<uno::XInterface> xHier;
+ if ( rField.nHier < nHierCount )
+ xHier = ScUnoHelpFunctions::AnyToInterface( xHiers->getByIndex( rField.nHier ) );
+ DBG_ASSERT( xHier.is(), "hierarchy not found" );
+
+ sal_Int32 nLevCount = 0;
+ uno::Reference<container::XIndexAccess> xLevels;
+ uno::Reference<sheet::XLevelsSupplier> xLevSupp( xHier, uno::UNO_QUERY );
+ if ( xLevSupp.is() )
+ {
+ uno::Reference<container::XNameAccess> xLevsName = xLevSupp->getLevels();
+ xLevels = new ScNameToIndexAccess( xLevsName );
+ nLevCount = xLevels->getCount();
+ }
+ uno::Reference<uno::XInterface> xLevel;
+ if ( rField.nLevel < nLevCount )
+ xLevel = ScUnoHelpFunctions::AnyToInterface( xLevels->getByIndex( rField.nLevel ) );
+ DBG_ASSERT( xLevel.is(), "level not found" );
+
+ uno::Reference<beans::XPropertySet> xLevelProp( xLevel, uno::UNO_QUERY );
+ if ( xLevelProp.is() )
+ {
+ try
+ {
+ uno::Any aValue = xLevelProp->getPropertyValue( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(DP_PROP_SUBTOTALS)) );
+ aValue >>= aSubTotals;
+ }
+ catch(uno::Exception&)
+ {
+ }
+ }
+
+ return aSubTotals;
+}
+
+void lcl_FilterInclude( std::vector< sal_Bool >& rResult, std::vector< sal_Int32 >& rSubtotal,
+ const ScDPOutLevelData& rField,
+ const std::vector< ScDPGetPivotDataField >& rFilters,
+ std::vector< sal_Bool >& rFilterUsed,
+ bool& rBeforeDataLayout,
+ sal_Int32 nGrandTotals, sal_Int32 nDataLayoutIndex,
+ const std::vector<String>& rDataNames, const std::vector<String>& rGivenNames,
+ const ScDPGetPivotDataField& rTarget, const uno::Reference<sheet::XDimensionsSupplier>& xSource )
+{
+ // returns true if a filter was given for the field
+
+ DBG_ASSERT( rFilters.size() == rFilterUsed.size(), "wrong size" );
+
+ const bool bIsDataLayout = ( rField.nDim == nDataLayoutIndex );
+ if (bIsDataLayout)
+ rBeforeDataLayout = false;
+
+ bool bHasFilter = false;
+ ScDPGetPivotDataField aFilter;
+ if ( !bIsDataLayout ) // selection of data field is handled separately
+ {
+ for (SCSIZE nFilterPos = 0; nFilterPos < rFilters.size() && !bHasFilter; ++nFilterPos)
+ {
+ if ( lcl_IsNamedCategoryField( rFilters[nFilterPos], rField ) )
+ {
+ aFilter = rFilters[nFilterPos];
+ rFilterUsed[nFilterPos] = sal_True;
+ bHasFilter = true;
+ }
+ }
+ }
+
+ bool bHasFunc = bHasFilter && aFilter.meFunction != sheet::GeneralFunction_NONE;
+
+ uno::Sequence<sheet::GeneralFunction> aSubTotals;
+ if ( !bIsDataLayout )
+ aSubTotals = lcl_GetSubTotals( xSource, rField );
+ bool bManualSub = ( aSubTotals.getLength() > 0 && aSubTotals[0] != sheet::GeneralFunction_AUTO );
+
+ const uno::Sequence<sheet::MemberResult>& rSequence = rField.aResult;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+ sal_Int32 nSize = rSequence.getLength();
+
+ DBG_ASSERT( (sal_Int32)rResult.size() == nSize, "Number of fields do not match result count" );
+
+ sal_Int32 nContCount = 0;
+ sal_Int32 nSubTotalCount = 0;
+ sheet::MemberResult aPrevious;
+ for( sal_Int32 j=0; j < nSize; j++ )
+ {
+ sheet::MemberResult aResultEntry = pArray[j];
+ if ( aResultEntry.Flags & sheet::MemberResultFlags::CONTINUE )
+ {
+ aResultEntry = aPrevious;
+ ++nContCount;
+ }
+ else if ( ( aResultEntry.Flags & sheet::MemberResultFlags::SUBTOTAL ) == 0 )
+ {
+ // count the CONTINUE entries before a SUBTOTAL
+ nContCount = 0;
+ }
+
+ if ( j >= nSize - nGrandTotals )
+ {
+ // mark as subtotal for the preceding data
+ if ( ( aResultEntry.Flags & sheet::MemberResultFlags::SUBTOTAL ) != 0 )
+ {
+ rSubtotal[j] = nSize - nGrandTotals;
+
+ if ( rResult[j] && nGrandTotals > 1 )
+ {
+ // grand total is always automatic
+ sal_Int32 nDataPos = j - ( nSize - nGrandTotals );
+ DBG_ASSERT( nDataPos < (sal_Int32)rDataNames.size(), "wrong data count" );
+ String aSourceName( rDataNames[nDataPos] ); // vector contains source names
+ String aGivenName( rGivenNames[nDataPos] );
+
+ rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName );
+ }
+ }
+
+ // treat "grand total" columns/rows as empty description, as if they were marked
+ // in a previous field
+
+ DBG_ASSERT( ( aResultEntry.Flags &
+ ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL ) ) == 0 ||
+ ( aResultEntry.Flags &
+ ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL ) ) ==
+ ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL ),
+ "non-subtotal member found in grand total result" );
+ aResultEntry.Flags = 0;
+ }
+
+ // mark subtotals (not grand total) for preceding data (assume CONTINUE is set)
+ if ( ( aResultEntry.Flags & sheet::MemberResultFlags::SUBTOTAL ) != 0 )
+ {
+ rSubtotal[j] = nContCount + 1 + nSubTotalCount;
+
+ if ( rResult[j] )
+ {
+ if ( bManualSub )
+ {
+ if ( rBeforeDataLayout )
+ {
+ // manual subtotals and several data fields
+
+ sal_Int32 nDataCount = rDataNames.size();
+ sal_Int32 nFuncPos = nSubTotalCount / nDataCount; // outer order: subtotal functions
+ sal_Int32 nDataPos = nSubTotalCount % nDataCount; // inner order: data fields
+
+ String aSourceName( rDataNames[nDataPos] ); // vector contains source names
+ String aGivenName( rGivenNames[nDataPos] );
+
+ DBG_ASSERT( nFuncPos < aSubTotals.getLength(), "wrong subtotal count" );
+ rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName ) &&
+ aSubTotals[nFuncPos] == aFilter.meFunction;
+ }
+ else
+ {
+ // manual subtotals for a single data field
+
+ DBG_ASSERT( nSubTotalCount < aSubTotals.getLength(), "wrong subtotal count" );
+ rResult[j] = ( aSubTotals[nSubTotalCount] == aFilter.meFunction );
+ }
+ }
+ else // automatic subtotals
+ {
+ if ( rBeforeDataLayout )
+ {
+ DBG_ASSERT( nSubTotalCount < (sal_Int32)rDataNames.size(), "wrong data count" );
+ String aSourceName( rDataNames[nSubTotalCount] ); // vector contains source names
+ String aGivenName( rGivenNames[nSubTotalCount] );
+
+ rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName );
+ }
+
+ // if a function was specified, automatic subtotals never match
+ if ( bHasFunc )
+ rResult[j] = false;
+ }
+ }
+
+ ++nSubTotalCount;
+ }
+ else
+ nSubTotalCount = 0;
+
+ if( rResult[j] )
+ {
+ if ( bIsDataLayout )
+ {
+ if ( ( aResultEntry.Flags & sheet::MemberResultFlags::HASMEMBER ) != 0 )
+ {
+ // Asterisks are added in ScDPSaveData::WriteToSource to create unique names.
+ //! preserve original name there?
+ String aSourceName( aResultEntry.Name );
+ aSourceName.EraseTrailingChars( '*' );
+
+ String aGivenName( aResultEntry.Caption ); //! Should use a stored name when available
+ aGivenName.EraseLeadingChars( '\'' );
+
+ rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName );
+ }
+ }
+ else if ( bHasFilter )
+ {
+ // name must match (simple value or subtotal)
+ rResult[j] = ( ( aResultEntry.Flags & sheet::MemberResultFlags::HASMEMBER ) != 0 ) &&
+ lcl_IsCondition( aResultEntry, aFilter );
+
+ // if a function was specified, simple (non-subtotal) values never match
+ if ( bHasFunc && nSubTotalCount == 0 )
+ rResult[j] = false;
+ }
+ // if no condition is given, keep the columns/rows included
+ }
+ aPrevious = aResultEntry;
+ }
+}
+
+void lcl_StripSubTotals( std::vector< sal_Bool >& rResult, const std::vector< sal_Int32 >& rSubtotal )
+{
+ sal_Int32 nSize = rResult.size();
+ DBG_ASSERT( (sal_Int32)rSubtotal.size() == nSize, "sizes don't match" );
+
+ for (sal_Int32 nPos=0; nPos<nSize; nPos++)
+ if ( rResult[nPos] && rSubtotal[nPos] )
+ {
+ // if a subtotal is included, clear the result flag for the columns/rows that the subtotal includes
+ sal_Int32 nStart = nPos - rSubtotal[nPos];
+ DBG_ASSERT( nStart >= 0, "invalid subtotal count" );
+
+ for (sal_Int32 nPrev = nStart; nPrev < nPos; nPrev++)
+ rResult[nPrev] = false;
+ }
+}
+
+String lcl_GetDataFieldName( const String& rSourceName, sheet::GeneralFunction eFunc )
+{
+ sal_uInt16 nStrId = 0;
+ switch ( eFunc )
+ {
+ case sheet::GeneralFunction_SUM: nStrId = STR_FUN_TEXT_SUM; break;
+ case sheet::GeneralFunction_COUNT:
+ case sheet::GeneralFunction_COUNTNUMS: nStrId = STR_FUN_TEXT_COUNT; break;
+ case sheet::GeneralFunction_AVERAGE: nStrId = STR_FUN_TEXT_AVG; break;
+ case sheet::GeneralFunction_MAX: nStrId = STR_FUN_TEXT_MAX; break;
+ case sheet::GeneralFunction_MIN: nStrId = STR_FUN_TEXT_MIN; break;
+ case sheet::GeneralFunction_PRODUCT: nStrId = STR_FUN_TEXT_PRODUCT; break;
+ case sheet::GeneralFunction_STDEV:
+ case sheet::GeneralFunction_STDEVP: nStrId = STR_FUN_TEXT_STDDEV; break;
+ case sheet::GeneralFunction_VAR:
+ case sheet::GeneralFunction_VARP: nStrId = STR_FUN_TEXT_VAR; break;
+ case sheet::GeneralFunction_NONE:
+ case sheet::GeneralFunction_AUTO:
+ default:
+ {
+ DBG_ERRORFILE("wrong function");
+ }
+ }
+ if ( !nStrId )
+ return String();
+
+ String aRet( ScGlobal::GetRscString( nStrId ) );
+ aRet.AppendAscii(RTL_CONSTASCII_STRINGPARAM( " - " ));
+ aRet.Append( rSourceName );
+ return aRet;
+}
+
+void ScDPOutput::GetDataDimensionNames( String& rSourceName, String& 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() )
+ {
+ // Asterisks are added in ScDPSaveData::WriteToSource to create unique names.
+ //! preserve original name there?
+ rSourceName = xDimName->getName();
+ rSourceName.EraseTrailingChars( '*' );
+
+ // Generate "given name" the same way as in dptabres.
+ //! Should use a stored name when available
+
+ sheet::GeneralFunction eFunc = (sheet::GeneralFunction)ScUnoHelpFunctions::GetEnumProperty(
+ xDimProp, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(DP_PROP_FUNCTION)),
+ sheet::GeneralFunction_NONE );
+ rGivenName = lcl_GetDataFieldName( rSourceName, eFunc );
+ }
+}
+
+// Returns sal_True on success and stores the result in rTarget
+// Returns sal_False if rFilters or rTarget describes something that is not visible
+sal_Bool ScDPOutput::GetPivotData( ScDPGetPivotDataField& rTarget,
+ const std::vector< ScDPGetPivotDataField >& rFilters )
+{
+ CalcSizes();
+
+ // need to know about grand total columns/rows:
+ sal_Int32 nGrandTotalCols;
+ sal_Int32 nGrandTotalRows;
+ sal_Int32 nDataLayoutIndex;
+ std::vector<String> aDataNames;
+ std::vector<String> aGivenNames;
+ sheet::DataPilotFieldOrientation eDataOrient;
+ lcl_GetTableVars( nGrandTotalCols, nGrandTotalRows, nDataLayoutIndex, aDataNames, aGivenNames, eDataOrient, xSource );
+
+ if ( aDataNames.empty() )
+ return false; // incomplete table without data fields -> no result
+
+ if ( eDataOrient == sheet::DataPilotFieldOrientation_HIDDEN )
+ {
+ // no data layout field -> single data field -> must match the selected field in rTarget
+
+ DBG_ASSERT( aDataNames.size() == 1, "several data fields but no data layout field" );
+ if ( !lcl_IsNamedDataField( rTarget, aDataNames[0], aGivenNames[0] ) )
+ return false;
+ }
+
+ std::vector< sal_Bool > aIncludeCol( nColCount, sal_True );
+ std::vector< sal_Int32 > aSubtotalCol( nColCount, 0 );
+ std::vector< sal_Bool > aIncludeRow( nRowCount, sal_True );
+ std::vector< sal_Int32 > aSubtotalRow( nRowCount, 0 );
+
+ std::vector< sal_Bool > aFilterUsed( rFilters.size(), false );
+
+ long nField;
+ long nCol;
+ long nRow;
+ bool bBeforeDataLayout;
+
+ // look in column fields
+
+ bBeforeDataLayout = ( eDataOrient == sheet::DataPilotFieldOrientation_COLUMN );
+ for (nField=0; nField<nColFieldCount; nField++)
+ lcl_FilterInclude( aIncludeCol, aSubtotalCol, pColFields[nField], rFilters, aFilterUsed, bBeforeDataLayout,
+ nGrandTotalCols, nDataLayoutIndex, aDataNames, aGivenNames, rTarget, xSource );
+
+ // look in row fields
+
+ bBeforeDataLayout = ( eDataOrient == sheet::DataPilotFieldOrientation_ROW );
+ for (nField=0; nField<nRowFieldCount; nField++)
+ lcl_FilterInclude( aIncludeRow, aSubtotalRow, pRowFields[nField], rFilters, aFilterUsed, bBeforeDataLayout,
+ nGrandTotalRows, nDataLayoutIndex, aDataNames, aGivenNames, rTarget, xSource );
+
+ // page fields
+
+ for (nField=0; nField<nPageFieldCount; nField++)
+ if ( !lcl_CheckPageField( pPageFields[nField], rFilters, aFilterUsed ) )
+ return false;
+
+ // all filter fields must be used
+ for (SCSIZE nFilter=0; nFilter<aFilterUsed.size(); nFilter++)
+ if (!aFilterUsed[nFilter])
+ return false;
+
+ lcl_StripSubTotals( aIncludeCol, aSubtotalCol );
+ lcl_StripSubTotals( aIncludeRow, aSubtotalRow );
+
+ long nColPos = 0;
+ long nColIncluded = 0;
+ for (nCol=0; nCol<nColCount; nCol++)
+ if (aIncludeCol[nCol])
+ {
+ nColPos = nCol;
+ ++nColIncluded;
+ }
+
+ long nRowPos = 0;
+ long nRowIncluded = 0;
+ for (nRow=0; nRow<nRowCount; nRow++)
+ if (aIncludeRow[nRow])
+ {
+ nRowPos = nRow;
+ ++nRowIncluded;
+ }
+
+ if ( nColIncluded != 1 || nRowIncluded != 1 )
+ return false;
+
+ const uno::Sequence<sheet::DataResult>& rDataRow = aData[nRowPos];
+ if ( nColPos >= rDataRow.getLength() )
+ return false;
+
+ const sheet::DataResult& rResult = rDataRow[nColPos];
+ if ( rResult.Flags & sheet::DataResultFlags::ERROR )
+ return false; //! different error?
+
+ rTarget.mbValIsStr = false;
+ rTarget.mnValNum = rResult.Value;
+
+ return sal_True;
+}
+
+sal_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() );
+}
+
+long ScDPOutput::GetHeaderDim( const ScAddress& rPos, sal_uInt16& 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 && nCol < nDataStartCol + nColFieldCount )
+ {
+ rOrient = sheet::DataPilotFieldOrientation_COLUMN;
+ long nField = nCol - nDataStartCol;
+ return pColFields[nField].nDim;
+ }
+
+ // test for row header
+
+ if ( nRow+1 == nDataStartRow && nCol >= nTabStartCol && nCol < nTabStartCol + nRowFieldCount )
+ {
+ rOrient = sheet::DataPilotFieldOrientation_ROW;
+ long nField = nCol - nTabStartCol;
+ return pRowFields[nField].nDim;
+ }
+
+ // test for page field
+
+ SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 );
+ if ( nCol == aStartPos.Col() && nRow >= nPageStartRow && nRow < nPageStartRow + nPageFieldCount )
+ {
+ rOrient = sheet::DataPilotFieldOrientation_PAGE;
+ long nField = nRow - nPageStartRow;
+ return pPageFields[nField].nDim;
+ }
+
+ //! single data field (?)
+
+ rOrient = sheet::DataPilotFieldOrientation_HIDDEN;
+ return -1; // invalid
+}
+
+sal_Bool ScDPOutput::GetHeaderDrag( const ScAddress& rPos, sal_Bool bMouseLeft, sal_Bool bMouseTop,
+ long nDragDim,
+ Rectangle& rPosRect, sal_uInt16& rOrient, 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 && nRow < nMemberStartRow + nColFieldCount )
+ {
+ long nField = nRow - nMemberStartRow;
+ if (nField < 0)
+ {
+ nField = 0;
+ bMouseTop = sal_True;
+ }
+ //! find start of dimension
+
+ rPosRect = Rectangle( nDataStartCol, nMemberStartRow + nField,
+ nTabEndCol, nMemberStartRow + nField -1 );
+
+ sal_Bool bFound = false; // is this within the same orientation?
+ sal_Bool bBeforeDrag = false;
+ sal_Bool bAfterDrag = false;
+ for (long nPos=0; nPos<nColFieldCount && !bFound; nPos++)
+ {
+ if (pColFields[nPos].nDim == nDragDim)
+ {
+ bFound = sal_True;
+ if ( nField < nPos )
+ bBeforeDrag = sal_True;
+ else if ( nField > nPos )
+ bAfterDrag = sal_True;
+ }
+ }
+
+ if ( bFound )
+ {
+ if (!bBeforeDrag)
+ {
+ ++rPosRect.Bottom();
+ if (bAfterDrag)
+ ++rPosRect.Top();
+ }
+ }
+ else
+ {
+ if ( !bMouseTop )
+ {
+ ++rPosRect.Top();
+ ++rPosRect.Bottom();
+ ++nField;
+ }
+ }
+
+ rOrient = sheet::DataPilotFieldOrientation_COLUMN;
+ rDimPos = nField; //!...
+ return sal_True;
+ }
+
+ // test for row header
+
+ // special case if no row fields
+ sal_Bool bSpecial = ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow &&
+ nRowFieldCount == 0 && nCol == nTabStartCol && bMouseLeft );
+
+ if ( bSpecial || ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow &&
+ nCol + 1 >= nTabStartCol && nCol < nTabStartCol + nRowFieldCount ) )
+ {
+ long nField = nCol - nTabStartCol;
+ //! find start of dimension
+
+ rPosRect = Rectangle( nTabStartCol + nField, nDataStartRow - 1,
+ nTabStartCol + nField - 1, nTabEndRow );
+
+ sal_Bool bFound = false; // is this within the same orientation?
+ sal_Bool bBeforeDrag = false;
+ sal_Bool bAfterDrag = false;
+ for (long nPos=0; nPos<nRowFieldCount && !bFound; nPos++)
+ {
+ if (pRowFields[nPos].nDim == nDragDim)
+ {
+ bFound = sal_True;
+ if ( nField < nPos )
+ bBeforeDrag = sal_True;
+ else if ( nField > nPos )
+ bAfterDrag = sal_True;
+ }
+ }
+
+ if ( bFound )
+ {
+ if (!bBeforeDrag)
+ {
+ ++rPosRect.Right();
+ if (bAfterDrag)
+ ++rPosRect.Left();
+ }
+ }
+ else
+ {
+ if ( !bMouseLeft )
+ {
+ ++rPosRect.Left();
+ ++rPosRect.Right();
+ ++nField;
+ }
+ }
+
+ rOrient = sheet::DataPilotFieldOrientation_ROW;
+ rDimPos = nField; //!...
+ return sal_True;
+ }
+
+ // test for page fields
+
+ SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 );
+ if ( nCol >= aStartPos.Col() && nCol <= nTabEndCol &&
+ nRow + 1 >= nPageStartRow && nRow < nPageStartRow + nPageFieldCount )
+ {
+ long nField = nRow - nPageStartRow;
+ if (nField < 0)
+ {
+ nField = 0;
+ bMouseTop = sal_True;
+ }
+ //! find start of dimension
+
+ rPosRect = Rectangle( aStartPos.Col(), nPageStartRow + nField,
+ nTabEndCol, nPageStartRow + nField - 1 );
+
+ sal_Bool bFound = false; // is this within the same orientation?
+ sal_Bool bBeforeDrag = false;
+ sal_Bool bAfterDrag = false;
+ for (long nPos=0; nPos<nPageFieldCount && !bFound; nPos++)
+ {
+ if (pPageFields[nPos].nDim == nDragDim)
+ {
+ bFound = sal_True;
+ if ( nField < nPos )
+ bBeforeDrag = sal_True;
+ else if ( nField > nPos )
+ bAfterDrag = sal_True;
+ }
+ }
+
+ if ( bFound )
+ {
+ if (!bBeforeDrag)
+ {
+ ++rPosRect.Bottom();
+ if (bAfterDrag)
+ ++rPosRect.Top();
+ }
+ }
+ else
+ {
+ if ( !bMouseTop )
+ {
+ ++rPosRect.Top();
+ ++rPosRect.Bottom();
+ ++nField;
+ }
+ }
+
+ rOrient = sheet::DataPilotFieldOrientation_PAGE;
+ rDimPos = nField; //!...
+ return sal_True;
+ }
+
+ return false;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */