Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  dpoutput.cxx   Sprache: C

 
/* -*- 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 <dpobject.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 <pivot/DPOutLevelData.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 <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
#include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>

#include <limits>
#include <string_view>
#include <utility>
#include <vector>
#include <iostream>

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 )

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 );
    }
};
// end anonymous namespace

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);

}

namespace
{

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>&&nbsp;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 comphelper::containerToSequence(aRes);
}

// end anonymous namespace

ScDPOutput::ScDPOutput(ScDocument* pDocument, uno::Reference<sheet::XDimensionsSupplier> xSource,
                       const ScAddress& rPosition, bool bFilter, bool bExpandCollapse, ScDPObject& rObject, bool bHideHeader)
    : mpDocument(pDocument)
    , maFormatOutput(rObject)
    , mxSource(std::move(xSource))
    , maStartPos(rPosition)
    , mnColFormatCount(0)
    , mnRowFormatCount(0)
    , mnSingleNumberFormat(0)
    , mnRowDims(0)
    , mnColCount(0)
    , mnRowCount(0)
    , mnHeaderSize(0)
    , mbDoFilter(bFilter)
    , mbResultsError(false)
    , mbSizesValid(false)
    , mbSizeOverflow(false)
    , mbHeaderLayout(false)
    , mbHasCompactRowField(false)
    , mbExpandCollapse(bExpandCollapse)
    , mbHideHeader(bHideHeader)
{
    mnTabStartCol = mnMemberStartCol = mnDataStartCol = mnTabEndCol = 0;
    mnTabStartRow = mnMemberStartRow = mnDataStartRow = mnTabEndRow = 0;

    uno::Reference<sheet::XDataPilotResults> xResult(mxSource, uno::UNO_QUERY);
    if (mxSource.is() && xResult.is())
    {
        //  get dimension results:

        uno::Reference<container::XIndexAccess> xDims =
                new ScNameToIndexAccess(mxSource->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))
                                        {
                                            mpColFields.emplace_back(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName,
                                                                     aCaption, bHasHiddenMember, bIsDataLayout, false);
                                        }
                                    }
                                    break;
                                    case sheet::DataPilotFieldOrientation_ROW:
                                    {
                                        uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults();
                                        ++mnRowDims;
                                        // We want only to remove the DATA column if it is empty
                                        // and not any other empty columns (to still show the
                                        // header columns)
                                        bool bSkip = lcl_MemberEmpty(aResult) && bIsDataLayout;
                                        if (!bSkip)
                                        {
                                            bool bFieldCompact = false;
                                            try
                                            {
                                                sheet::DataPilotFieldLayoutInfo aLayoutInfo;
                                                xPropSet->getPropertyValue( SC_UNO_DP_LAYOUT ) >>= aLayoutInfo;
                                                bFieldCompact = (aLayoutInfo.LayoutMode == sheet::DataPilotFieldLayoutMode::COMPACT_LAYOUT);
                                            }
                                            catch (uno::Exception&)
                                            {
                                            }
                                            mpRowFields.emplace_back(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName,
                                                                     aCaption, bHasHiddenMember, bIsDataLayout, false);
                                            maRowCompactFlags.push_back(bFieldCompact);
                                            mbHasCompactRowField |= bFieldCompact;
                                        }

                                    }
                                    break;
                                    case sheet::DataPilotFieldOrientation_PAGE:
                                    {
                                        uno::Sequence<sheet::MemberResult> aResult = getVisiblePageMembersAsResults(xLevel);
                                        // no check on results for page fields
                                        mpPageFields.emplace_back(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName,
                                                                  aCaption, bHasHiddenMember, falsetrue);
                                    }
                                    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(mpColNumberFormat, mnColFormatCount, xLevRes, xDims);
                                    else if ( eDimOrient == sheet::DataPilotFieldOrientation_ROW )
                                        lcl_FillNumberFormats(mpRowNumberFormat, mnRowFormatCount, 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

                    mnSingleNumberFormat = lcl_GetFirstNumberFormat( xDims );
                }
            }
        }
        std::sort(mpColFields.begin(), mpColFields.end(), ScDPOutLevelDataComparator());
        std::sort(mpRowFields.begin(), mpRowFields.end(), ScDPOutLevelDataComparator());
        std::sort(mpPageFields.begin(), mpPageFields.end(), ScDPOutLevelDataComparator());

        //  get data results:

        try
        {
            maData = xResult->getResults();
        }
        catch (const uno::RuntimeException&)
        {
            mbResultsError = true;
        }
    }

    // get "DataDescription" property (may be missing in external sources)

    uno::Reference<beans::XPropertySet> xSrcProp(mxSource, uno::UNO_QUERY);
    if ( !xSrcProp.is() )
        return;

    try
    {
        uno::Any aAny = xSrcProp->getPropertyValue( SC_UNO_DP_DATADESC );
        OUString aUStr;
        aAny >>= aUStr;
        maDataDescription = aUStr;
    }
    catch(const uno::Exception&)
    {
    }
}

ScDPOutput::~ScDPOutput()
{
}

void ScDPOutput::SetPosition(const ScAddress& rPosition)
{
    maStartPos = rPosition;
    mbSizesValid = mbSizeOverflow = false;
}

void ScDPOutput::DataCell( SCCOL nCol, SCROW nRow, SCTAB nTab, const sheet::DataResult&&nbsp;rData )
{
    tools::Long nFlags = rData.Flags;
    if ( nFlags & sheet::DataResultFlags::ERROR )
    {
        mpDocument->SetError( nCol, nRow, nTab, FormulaError::NoValue );
    }
    else if ( nFlags & sheet::DataResultFlags::HASDATA )
    {
        mpDocument->SetValue( nCol, nRow, nTab, rData.Value );

        //  use number formats from source

        OSL_ENSURE(mbSizesValid, "DataCell: !bSizesValid");
        sal_uInt32 nFormat = 0;
        bool bApplyFormat = false;
        if (mpColNumberFormat)
        {
            if (nCol >= mnDataStartCol)
            {
                tools::Long nIndex = nCol - mnDataStartCol;
                if (nIndex < mnColFormatCount)
                {
                    nFormat = mpColNumberFormat[nIndex];
                    bApplyFormat = true;
                }
            }
        }
        else if (mpRowNumberFormat)
        {
            if (nRow >= mnDataStartRow)
            {
                tools::Long nIndex = nRow - mnDataStartRow;
                if (nIndex < mnRowFormatCount)
                {
                    nFormat = mpRowNumberFormat[nIndex];
                    bApplyFormat = true;
                }
            }
        }
        else if (mnSingleNumberFormat != 0)
        {
            nFormat = mnSingleNumberFormat;        // single format is used everywhere
            bApplyFormat = true;
        }

        if (bApplyFormat)
            mpDocument->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))
        {
            mpDocument->SetValue( nCol, nRow, nTab, rData.Value);
        }
        else
        {
            ScSetStringParam aParam;
            if (bNumeric)
                aParam.setNumericInput();
            else
                aParam.setTextInput();

            mpDocument->SetString(nCol, nRow, nTab, rData.Caption, &aParam);
        }
    }

    if ( !(nFlags & sheet::MemberResultFlags::SUBTOTAL) )
        return;

    ScDPOutputImpl outputimp(mpDocument, nTab,
        mnTabStartCol, mnTabStartRow,
        mnDataStartCol, mnDataStartRow, mnTabEndCol, mnTabEndRow);
    //TODO: limit frames to horizontal or vertical?
    if (bColHeader)
    {
        outputimp.OutputBlockFrame(nCol, mnMemberStartRow+static_cast<SCROW>(nLevel), nCol, mnDataStartRow - 1);

        lcl_SetStyleById(mpDocument, nTab, nCol, mnMemberStartRow + static_cast<SCROW>(nLevel), nCol, mnDataStartRow - 1, STR_PIVOT_STYLENAME_TITLE);
        lcl_SetStyleById(mpDocument, nTab, nCol, mnDataStartRow, nCol, mnTabEndRow, STR_PIVOT_STYLENAME_RESULT );
    }
    else
    {
        outputimp.OutputBlockFrame(mnMemberStartCol + static_cast<SCCOL>(nLevel), nRow, mnDataStartCol - 1, nRow);
        lcl_SetStyleById(mpDocument, nTab, mnMemberStartCol + static_cast<SCCOL>(nLevel), nRow, mnDataStartCol - 1, nRow, STR_PIVOT_STYLENAME_TITLE);
        lcl_SetStyleById(mpDocument, nTab, mnDataStartCol, nRow, mnTabEndCol, nRow, STR_PIVOT_STYLENAME_RESULT);
    }
}

void ScDPOutput::MultiFieldCell(SCCOL nCol, SCROW nRow, SCTAB nTab, bool bRowField)
{
    mpDocument->SetString(nCol, nRow, nTab, ScResId(bRowField ? STR_PIVOT_ROW_LABELS : STR_PIVOT_COL_LABELS));

    ScMF nMergeFlag = ScMF::Button;
    for (auto& rData : mpRowFields)
    {
        if (rData.mbHasHiddenMember)
        {
            nMergeFlag |= ScMF::HiddenMember;
            break;
        }
    }

    nMergeFlag |= ScMF::ButtonPopup2;

    mpDocument->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, nMergeFlag);
    lcl_SetStyleById(mpDocument, nTab, nCol, nRow, nCol, nRow, STR_PIVOT_STYLENAME_FIELDNAME);
}

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;
    mpDocument->SetString(nCol, nRow, nTab, rData.maCaption, &aParam);

    if (bInTable)
        lcl_SetFrame(mpDocument, 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;
        mpDocument->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, ScMF::Button);
        mpDocument->ApplyFlagsTab(nCol+1, nRow, nCol+1, nRow, nTab, nMergeFlag);
    }
    else
    {
        nMergeFlag |= ScMF::Button;
        if (!rData.mbDataLayout)
            nMergeFlag |= ScMF::ButtonPopup;
        mpDocument->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, nMergeFlag);
    }

    lcl_SetStyleById(mpDocument, 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);
}

SCCOL ScDPOutput::GetColumnsForRowFields() const
{
    if (!mbHasCompactRowField)
        return static_cast<SCCOL>(mpRowFields.size());

    SCCOL nNum = 0;
    for (const auto bCompact: maRowCompactFlags)
        if (!bCompact)
            ++nNum;

    if (maRowCompactFlags.back())
        ++nNum;

    return nNum;
}

void ScDPOutput::CalcSizes()
{
    if (mbSizesValid)
        return;

    //  get column size of data from first row
    //TODO: allow different sizes (and clear following areas) ???

    mnRowCount = maData.getLength();
    const uno::Sequence<sheet::DataResult>* pRowAry = maData.getConstArray();
    mnColCount = mnRowCount ? ( pRowAry[0].getLength() ) : 0;

    mnHeaderSize = 1;
    if (mbHideHeader)
        mnHeaderSize = 0;
    else if (GetHeaderLayout() && mpColFields.empty())
        // Insert an extra header row only when there is no column field.
        mnHeaderSize = 2;

    //  calculate output positions and sizes

    tools::Long nPageSize = 0;     // use page fields!
    if (mbDoFilter || !mpPageFields.empty())
    {
        nPageSize += mpPageFields.size() + 1;   // plus one empty row
        if (mbDoFilter)
            ++nPageSize;        //  filter button above the page fields
    }

    if (maStartPos.Col() + static_cast<tools::Long>(mpRowFields.size()) + mnColCount - 1 > mpDocument->MaxCol() ||
        maStartPos.Row() + nPageSize + mnHeaderSize + static_cast<tools::Long>(mpColFields.size()) + mnRowCount > mpDocument->MaxRow())
    {
        mbSizeOverflow = true;
    }

    mnTabStartCol = maStartPos.Col();
    mnTabStartRow = maStartPos.Row() + static_cast<SCROW>(nPageSize);          // below page fields
    mnMemberStartCol = mnTabStartCol;
    mnMemberStartRow = mnTabStartRow + static_cast<SCROW>(mnHeaderSize);
    mnDataStartCol = mnMemberStartCol + GetColumnsForRowFields();
    mnDataStartRow = mnMemberStartRow + static_cast<SCROW>(mpColFields.size());
    if (mnColCount > 0)
        mnTabEndCol = mnDataStartCol + static_cast<SCCOL>(mnColCount) - 1;
    else
        mnTabEndCol = mnDataStartCol;     // single column will remain empty
    // if page fields are involved, include the page selection cells
    if (!mpPageFields.empty() && mnTabEndCol < mnTabStartCol + 1)
        mnTabEndCol = mnTabStartCol + 1;
    if (mnRowCount > 0)
        mnTabEndRow = mnDataStartRow + static_cast<SCROW>(mnRowCount) - 1;
    else
        mnTabEndRow = mnDataStartRow;     // single row will remain empty
    mbSizesValid = 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 != maStartPos.Tab() )
        return DataPilotTablePositionType::NOT_IN_TABLE;

    CalcSizes();

    // Make sure the cursor is within the table.
    if (nCol < mnTabStartCol || nRow < mnTabStartRow || nCol > mnTabEndCol || nRow > mnTabEndRow)
        return DataPilotTablePositionType::NOT_IN_TABLE;

    // test for result data area.
    if (nCol >= mnDataStartCol && nCol <= mnTabEndCol && nRow >= mnDataStartRow && nRow <= mnTabEndRow)
        return DataPilotTablePositionType::RESULT;

    bool bInColHeader = (nRow >= mnTabStartRow && nRow < mnDataStartRow);
    bool bInRowHeader = (nCol >= mnTabStartCol && nCol < mnDataStartCol);

    if (bInColHeader && bInRowHeader)
        // probably in that ugly little box at the upper-left corner of the table.
        return DataPilotTablePositionType::OTHER;

    if (bInColHeader)
    {
        if (nRow == mnTabStartRow)
            // 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::outputPageFields(SCTAB nTab)
{
    for (size_t nField = 0; nField < mpPageFields.size(); ++nField)
    {
        SCCOL nHeaderCol = maStartPos.Col();
        SCROW nHeaderRow = maStartPos.Row() + nField + (mbDoFilter ? 1 : 0);
        // draw without frame for consistency with filter button:
        FieldCell(nHeaderCol, nHeaderRow, nTab, mpPageFields[nField], false);
        SCCOL nFieldCol = nHeaderCol + 1;

        OUString aPageValue = ScResId(SCSTR_ALL);
        const uno::Sequence<sheet::MemberResult>& rRes = mpPageFields[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();
        mpDocument->SetString(nFieldCol, nHeaderRow, nTab, aPageValue, &aParam);

        lcl_SetFrame(mpDocument, nTab, nFieldCol, nHeaderRow, nFieldCol, nHeaderRow, 20);
    }
}

void ScDPOutput::outputColumnHeaders(SCTAB nTab, ScDPOutputImpl& rOutputImpl)
{
    size_t nNumColFields = mpColFields.size();

    for (size_t nField = 0; nField < nNumColFields; nField++)
    {
        SCCOL nHeaderCol = mnDataStartCol + SCCOL(nField); //TODO: check for overflow

        if (mnMemberStartRow > mnTabStartRow)
        {
            if (!mbHasCompactRowField || nNumColFields == 1)
                FieldCell(nHeaderCol, mnTabStartRow, nTab, mpColFields[nField], true);
            else if (!nField)
                MultiFieldCell(nHeaderCol, mnTabStartRow, nTab, false /* bRowField */);
        }

        SCROW nRowPos = mnMemberStartRow + SCROW(nField); //TODO: check for overflow
        const uno::Sequence<sheet::MemberResult> rMemberSequence = mpColFields[nField].maResult;
        const sheet::MemberResult* pMemberArray = rMemberSequence.getConstArray();
        tools::Long nThisColCount = rMemberSequence.getLength();
        OSL_ENSURE(nThisColCount == mnColCount, "count mismatch"); //TODO: ???

        for (tools::Long nColumn = 0; nColumn < nThisColCount; nColumn++)
        {
            sheet::MemberResult const& rMember = rMemberSequence[nColumn];

            SCCOL nColPos = mnDataStartCol + SCCOL(nColumn); //TODO: check for overflow

            HeaderCell(nColPos, nRowPos, nTab, rMember, true, nField);

            if ((rMember.Flags & sheet::MemberResultFlags::HASMEMBER) &&
               !(rMember.Flags & sheet::MemberResultFlags::SUBTOTAL))
            {
                // Check the number of columns this spreads
                tools::Long nEnd = nColumn;
                while (nEnd + 1 < nThisColCount && (pMemberArray[nEnd + 1].Flags & sheet::MemberResultFlags::CONTINUE))
                    ++nEnd;

                SCCOL nEndColPos = mnDataStartCol + SCCOL(nEnd); //TODO: check for overflow
                if (nField + 1 < mpColFields.size())
                {
                    if (nField + 2 == mpColFields.size())
                    {
                        rOutputImpl.AddCol( nColPos );
                        if (nColPos + 1 == nEndColPos)
                            rOutputImpl.OutputBlockFrame(nColPos, nRowPos, nEndColPos, nRowPos + 1, true);
                    }
                    else
                        rOutputImpl.OutputBlockFrame(nColPos, nRowPos, nEndColPos, nRowPos);

                    lcl_SetStyleById(mpDocument, nTab, nColPos, nRowPos, nEndColPos, mnDataStartRow - 1, STR_PIVOT_STYLENAME_CATEGORY);
                }
                else
                {
                    lcl_SetStyleById(mpDocument, nTab, nColPos, nRowPos, nColPos, mnDataStartRow - 1, STR_PIVOT_STYLENAME_CATEGORY);
                }
            }
            else if (rMember.Flags & sheet::MemberResultFlags::SUBTOTAL)
            {
                rOutputImpl.AddCol(nColPos);
            }

            // Resolve formats
            maFormatOutput.insertFieldMember(nField, mpColFields[nField], nColumn, rMember, nColPos, nRowPos, sc::FormatResultDirection::COLUMN);

            // Apply the same number format as in data source.
            mpDocument->ApplyAttr(nColPos, nRowPos, nTab, SfxUInt32Item(ATTR_VALUE_FORMAT, mpColFields[nField].mnSrcNumFmt));
        }
        if (nField == 0 && mpColFields.size() == 1 && mnMemberStartRow > mnTabStartRow)
            rOutputImpl.OutputBlockFrame(mnDataStartCol, mnTabStartRow, mnTabEndCol, nRowPos - 1);
    }
}

void ScDPOutput::outputRowHeader(SCTAB nTab, ScDPOutputImpl& rOutputImpl)
{
    std::vector<bool> vbSetBorder;
    vbSetBorder.resize(mnTabEndRow - mnDataStartRow + 1, false);
    size_t nFieldColOffset = 0;
    size_t nFieldIndentLevel = 0; // To calculate indent level for fields packed in a column.
    size_t nNumRowFields = mpRowFields.size();
    for (size_t nField = 0; nField < nNumRowFields; nField++)
    {
        const bool bCompactField = maRowCompactFlags[nField];
        SCCOL nHdrCol = mnTabStartCol + SCCOL(nField); //TODO: check for overflow
        SCROW nHdrRow = mnDataStartRow - 1;
        if (!mbHasCompactRowField || nNumRowFields == 1)
            FieldCell(nHdrCol, nHdrRow, nTab, mpRowFields[nField], true);
        else if (!nField)
            MultiFieldCell(nHdrCol, nHdrRow, nTab, true /* bRowField */);

        SCCOL nColPos = mnMemberStartCol + SCCOL(nFieldColOffset); //TODO: check for overflow
        const uno::Sequence<sheet::MemberResult> rMemberSequence = mpRowFields[nField].maResult;
        const sheet::MemberResult* pMemberArray = rMemberSequence.getConstArray();
        sal_Int32 nThisRowCount = rMemberSequence.getLength();
        OSL_ENSURE(nThisRowCount == mnRowCount, "count mismatch");     //TODO: ???
        for (sal_Int32 nRow = 0; nRow < nThisRowCount; nRow++)
        {
            sheet::MemberResult const& rMember = rMemberSequence[nRow];
            const sheet::MemberResult& rData = rMember;
            const bool bHasMember = rData.Flags & sheet::MemberResultFlags::HASMEMBER;
            const bool bSubtotal = rData.Flags & sheet::MemberResultFlags::SUBTOTAL;
            SCROW nRowPos = mnDataStartRow + SCROW(nRow); //TODO: check for overflow
            HeaderCell( nColPos, nRowPos, nTab, rData, false, nFieldColOffset );
            if (bHasMember && !bSubtotal)
            {
                if (nField + 1 < mpRowFields.size())
                {
                    tools::Long nEnd = nRow;
                    while (nEnd + 1 < nThisRowCount && (pMemberArray[nEnd + 1].Flags & sheet::MemberResultFlags::CONTINUE))
                    {
                        ++nEnd;
                    }
                    SCROW nEndRowPos = mnDataStartRow + SCROW(nEnd); //TODO: check for overflow
                    rOutputImpl.AddRow(nRowPos);
                    if (!vbSetBorder[nRow] )
                    {
                        rOutputImpl.OutputBlockFrame(nColPos, nRowPos, mnTabEndCol, nEndRowPos);
                        vbSetBorder[nRow] = true;
                    }
                    rOutputImpl.OutputBlockFrame(nColPos, nRowPos, nColPos, nEndRowPos);

                    if (nField == mpRowFields.size() - 2)
                        rOutputImpl.OutputBlockFrame(nColPos + 1, nRowPos, nColPos + 1, nEndRowPos);

                    lcl_SetStyleById(mpDocument, nTab, nColPos, nRowPos, mnDataStartCol - 1, nEndRowPos, STR_PIVOT_STYLENAME_CATEGORY);
                }
                else
                {
                    lcl_SetStyleById(mpDocument, nTab, nColPos, nRowPos, mnDataStartCol - 1, nRowPos, STR_PIVOT_STYLENAME_CATEGORY);
                }

                // Set flags for collapse/expand buttons and indent field header text
                {
                    bool bLast = mnRowDims == (nField + 1);
                    size_t nMinIndentLevel = mbExpandCollapse ? 1 : 0;
                    tools::Long nIndent = o3tl::convert(13 * (bLast ? nFieldIndentLevel : nMinIndentLevel + nFieldIndentLevel), o3tl::Length::px, o3tl::Length::twip);
                    bool bHasContinue = !bLast && nRow + 1 < nThisRowCount && (pMemberArray[nRow + 1].Flags & sheet::MemberResultFlags::CONTINUE);
                    if (nIndent)
                        mpDocument->ApplyAttr(nColPos, nRowPos, nTab, ScIndentItem(nIndent));
                    if (mbExpandCollapse && !bLast)
                    {
                        mpDocument->ApplyFlagsTab(nColPos, nRowPos, nColPos, nRowPos, nTab,
                            bHasContinue ? ScMF::DpCollapse : ScMF::DpExpand);
                    }
                }
            }
            else if (bSubtotal)
            {
                rOutputImpl.AddRow(nRowPos);
            }

            // Resolve formats
            maFormatOutput.insertFieldMember(nField, mpRowFields[nField], nRow, rMember, nColPos, nRowPos, sc::FormatResultDirection::ROW);

            // Apply the same number format as in data source.
            mpDocument->ApplyAttr(nColPos, nRowPos, nTab, SfxUInt32Item(ATTR_VALUE_FORMAT, mpRowFields[nField].mnSrcNumFmt));
        }

        if (!bCompactField)
        {
            // Next field should be placed in next column only if current field has a non-compact layout.
            ++nFieldColOffset;
            nFieldIndentLevel = 0; // Reset indent level.
        }
        else
        {
            ++nFieldIndentLevel;
        }
    }
}

void ScDPOutput::outputDataResults(SCTAB nTab)
{
    const uno::Sequence<sheet::DataResult>* pRowAry = maData.getConstArray();

    for (sal_Int32 nRow = 0; nRow < mnRowCount; nRow++)
    {
        SCROW nRowPos = mnDataStartRow + SCROW(nRow); //TODO: check for overflow
        const sheet::DataResult* pColAry = pRowAry[nRow].getConstArray();
        sal_Int32 nThisColCount = pRowAry[nRow].getLength();
        OSL_ENSURE(nThisColCount == mnColCount, "count mismatch"); //TODO: ???
        for (sal_Int32 nCol = 0; nCol < nThisColCount; nCol++)
        {
            SCCOL nColPos = mnDataStartCol + SCCOL(nCol); //TODO: check for overflow
            DataCell(nColPos, nRowPos, nTab, pColAry[nCol]);
        }
    }

    maFormatOutput.apply(*mpDocument);
}

void ScDPOutput::Output()
{
    SCTAB nTab = maStartPos.Tab();

    //  calculate output positions and sizes
    CalcSizes();

    if (mbSizeOverflow || mbResultsError)   // does output area exceed sheet limits?
        return;                             // nothing

    // Prepare format output
    bool bColumnFieldIsDataOnly = mnColCount == 1 && mnRowCount > 0 && mpColFields.empty();
    maFormatOutput.prepare(nTab, mpColFields, mpRowFields, bColumnFieldIsDataOnly);

    //  clear whole (new) output area
    // when modifying table, clear old area !
    //TODO: include InsertDeleteFlags::OBJECTS ???
    mpDocument->DeleteAreaTab(maStartPos.Col(), maStartPos.Row(), mnTabEndCol, mnTabEndRow, nTab, InsertDeleteFlags::ALL );

    if (mbDoFilter)
        lcl_DoFilterButton(mpDocument, maStartPos.Col(), maStartPos.Row(), nTab);

    outputPageFields(nTab);

    //  data description
    //  (may get overwritten by first row field)

    if (maDataDescription.isEmpty())
    {
        //TODO: use default string ("result") ?
    }
    mpDocument->SetString(mnTabStartCol, mnTabStartRow, nTab, maDataDescription);

    //  set STR_PIVOT_STYLENAME_INNER for whole data area (subtotals are overwritten)

    if (mnDataStartRow > mnTabStartRow)
        lcl_SetStyleById(mpDocument, nTab, mnTabStartCol, mnTabStartRow, mnTabEndCol, mnDataStartRow - 1, STR_PIVOT_STYLENAME_TOP);
    lcl_SetStyleById(mpDocument, nTab, mnDataStartCol, mnDataStartRow, mnTabEndCol, mnTabEndRow, STR_PIVOT_STYLENAME_INNER);

    ScDPOutputImpl aOutputImpl(mpDocument, nTab, mnTabStartCol, mnTabStartRow,
                               mnDataStartCol, mnDataStartRow, mnTabEndCol, mnTabEndRow);

    outputColumnHeaders(nTab, aOutputImpl);

    outputRowHeader(nTab, aOutputImpl);

    if (bColumnFieldIsDataOnly)
    {
        // the table contains exactly one data field and no column fields.
        // Display data description at top right corner.
        ScSetStringParam aParam;
        aParam.setTextInput();
        SCCOL nCol = mnDataStartCol;
        SCCOL nRow = mnDataStartRow - 1;
        mpDocument->SetString(nCol, nRow, nTab, maDataDescription, &aParam);
        maFormatOutput.insertEmptyDataColumn(nCol, nRow);
    }

    outputDataResults(nTab);

    aOutputImpl.OutputDataArea();
}

ScRange ScDPOutput::GetOutputRange( sal_Int32 nRegionType )
{
    using namespace ::com::sun::star::sheet;

    CalcSizes();

    SCTAB nTab = maStartPos.Tab();
    switch (nRegionType)
    {
        case DataPilotOutputRangeType::RESULT:
            return ScRange(mnDataStartCol, mnDataStartRow, nTab, mnTabEndCol, mnTabEndRow, nTab);
        case DataPilotOutputRangeType::TABLE:
            return ScRange(maStartPos.Col(), mnTabStartRow, nTab, mnTabEndCol, mnTabEndRow, nTab);
        default:
            OSL_ENSURE(nRegionType == DataPilotOutputRangeType::WHOLE, "ScDPOutput::GetOutputRange: unknown region type");
        break;
    }
    return ScRange(maStartPos.Col(), maStartPos.Row(), nTab, mnTabEndCol, mnTabEndRow, nTab);
}

bool ScDPOutput::HasError()
{
    CalcSizes();

    return mbSizeOverflow || mbResultsError;
}

sal_Int32 ScDPOutput::GetHeaderRows() const
{
    return mpPageFields.size() + (mbDoFilter ? 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(mpColFields.begin(), mpColFields.end(), lFindDimension);
    if (colit != mpColFields.end())
    {
        // collect the member names
        insertNames(rNames, colit->maResult);
        return;
    }

    // look in row fields
    auto rowit = std::find_if(mpRowFields.begin(), mpRowFields.end(), lFindDimension);
    if (rowit != mpRowFields.end())
    {
        // collect the member names
        insertNames(rNames, rowit->maResult);
    }
}

void ScDPOutput::SetHeaderLayout(bool bUseGrid)
{
    mbHeaderLayout = bUseGrid;
    mbSizesValid = 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::GetRowFieldRange(SCCOL nCol, sal_Int32& nRowFieldStart, sal_Int32& nRowFieldEnd) const
{
    if (!mbHasCompactRowField)
    {
        nRowFieldStart = nCol;
        nRowFieldEnd = nCol + 1;
        return;
    }

    if (nCol >= static_cast<SCCOL>(maRowCompactFlags.size()))
    {
        nRowFieldStart = nRowFieldEnd = 0;
        return;
    }

    nRowFieldStart = -1;
    nRowFieldEnd = -1;
    SCCOL nCurCol = 0;
    sal_Int32 nField = 0;

    for (const auto bCompact: maRowCompactFlags)
    {
        if (nCurCol == nCol && nRowFieldStart == -1)
            nRowFieldStart = nField;

        if (!bCompact)
            ++nCurCol;

        ++nField;

        if (nCurCol == (nCol + 1) && nRowFieldStart != -1 && nRowFieldEnd == -1)
        {
            nRowFieldEnd = nField;
            break;
        }
    }

    if (nRowFieldStart != -1 && nRowFieldEnd == -1 && nCurCol == nCol)
        nRowFieldEnd = static_cast<sal_Int32>(maRowCompactFlags.size());

    if (nRowFieldStart == -1 || nRowFieldEnd == -1)
    {
        SAL_WARN("sc.core""ScDPOutput::GetRowFieldRange : unable to find field range for nCol = " << nCol);
        nRowFieldStart = nRowFieldEnd = 0;
    }
}

sal_Int32 ScDPOutput::GetRowFieldCompact(SCCOL nColQuery, SCROW nRowQuery) const
{
    if (!mbHasCompactRowField)
        return nColQuery - mnTabStartCol;

    SCCOL nCol = nColQuery - mnTabStartCol;
    sal_Int32 nStartField = 0;
    sal_Int32 nEndField = 0;
    GetRowFieldRange(nCol, nStartField, nEndField);

    for (sal_Int32 nField = nEndField - 1; nField >= nStartField; --nField)
    {
        const uno::Sequence<sheet::MemberResult> rSequence = mpRowFields[nField].maResult;
        const sheet::MemberResult* pArray = rSequence.getConstArray();
        sal_Int32 nThisRowCount = rSequence.getLength();
        SCROW nRow = nRowQuery - mnDataStartRow;
        if (nRow >= 0 && nRow < nThisRowCount)
        {
            const sheet::MemberResult& rData = pArray[nRow];
            if ((rData.Flags & sheet::MemberResultFlags::HASMEMBER)
                && !(rData.Flags & sheet::MemberResultFlags::SUBTOTAL))
            {
                return nField;
            }
        }
    }

    return -1;
}

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 != maStartPos.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(mxSource, UNO_QUERY);
            if (xPropSet.is())
            {
                sal_Int32 nDataFieldCount = ScUnoHelpFunctions::GetLongProperty( xPropSet,
                                            SC_UNO_DP_DATAFIELDCOUNT );
                if (nDataFieldCount > 0)
                    aResData.DataFieldIndex = (nRow - mnDataStartRow) % nDataFieldCount;
            }

            // Copy appropriate DataResult object from the cached sheet::DataResult table.
            if (maData.getLength() > nRow - mnDataStartRow &&
                maData[nRow - mnDataStartRow].getLength() > nCol - mnDataStartCol)
                aResData.Result = maData[nRow - mnDataStartRow][nCol - mnDataStartCol];

            rPosData.PositionData <<= aResData;
            return;
        }
        case DataPilotTablePositionType::COLUMN_HEADER:
        {
            tools::Long nField = nRow - mnTabStartRow - 1; // 1st line is used for the buttons
            if (nField < 0)
                break;

            if (mpColFields.size() < o3tl::make_unsigned(nField) + 1 )
                break;
            const uno::Sequence<sheet::MemberResult> rSequence = mpColFields[nField].maResult;
            if (!rSequence.hasElements())
                break;
            const sheet::MemberResult* pArray = rSequence.getConstArray();

            tools::Long nItem = nCol - mnDataStartCol;
            //  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>(mpColFields[nField].mnDim);
            aHeaderData.Hierarchy = static_cast<sal_Int32>(mpColFields[nField].mnHier);
            aHeaderData.Level     = static_cast<sal_Int32>(mpColFields[nField].mnLevel);

            rPosData.PositionData <<= aHeaderData;
            return;
        }
        case DataPilotTablePositionType::ROW_HEADER:
        {
            tools::Long nField = GetRowFieldCompact(nCol, nRow);
            if (nField < 0)
                break;

            if (mpRowFields.size() < o3tl::make_unsigned(nField) + 1 )
                break;
            const uno::Sequence<sheet::MemberResult> rSequence = mpRowFields[nField].maResult;
            if (!rSequence.hasElements())
                break;
            const sheet::MemberResult* pArray = rSequence.getConstArray();

            tools::Long nItem = nRow - mnDataStartRow;
            //  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>(mpRowFields[nField].mnDim);
            aHeaderData.Hierarchy = static_cast<sal_Int32>(mpRowFields[nField].mnHier);
            aHeaderData.Level     = static_cast<sal_Int32>(mpRowFields[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(mxSource, 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, mxSource);

    SCCOL nCol = rPos.Col();
    SCROW nRow = rPos.Row();
    SCTAB nTab = rPos.Tab();
    if (nTab != maStartPos.Tab())
        return false// wrong sheet

    CalcSizes();

    // test for data area.
    if (nCol < mnDataStartCol || nCol > mnTabEndCol || nRow < mnDataStartRow || nRow > mnTabEndRow)
    {
        // Cell is outside the data field area.
        return false;
    }

    bool bFilterByCol = (nCol <= static_cast<SCCOL>(mnTabEndCol - nGrandTotalCols));
    bool bFilterByRow = (nRow <= static_cast<SCROW>(mnTabEndRow - nGrandTotalRows));

    // column fields
    for (size_t nColField = 0; nColField < mpColFields.size() && bFilterByCol; ++nColField)
    {
        if (mpColFields[nColField].mnDim == nDataLayoutIndex)
            // There is no sense including the data layout field for filtering.
            continue;

        sheet::DataPilotFieldFilter filter;
        filter.FieldName = mpColFields[nColField].maName;

        const uno::Sequence<sheet::MemberResult> rSequence = mpColFields[nColField].maResult;
        const sheet::MemberResult* pArray = rSequence.getConstArray();

        OSL_ENSURE(mnDataStartCol + rSequence.getLength() - 1 == mnTabEndCol, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");

        tools::Long nItem = nCol - mnDataStartCol;
                //  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 < mpRowFields.size() && bFilterByRow; ++nRowField)
    {
        if (mpRowFields[nRowField].mnDim == nDataLayoutIndex)
            // There is no sense including the data layout field for filtering.
            continue;

        sheet::DataPilotFieldFilter filter;
        filter.FieldName = mpRowFields[nRowField].maName;

        const uno::Sequence<sheet::MemberResult> rSequence = mpRowFields[nRowField].maResult;
        const sheet::MemberResult* pArray = rSequence.getConstArray();

        OSL_ENSURE(mnDataStartRow + rSequence.getLength() - 1 == mnTabEndRow, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");

        tools::Long nItem = nRow - mnDataStartRow;
            //  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;
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=94 G=93

¤ Dauer der Verarbeitung: 0.29 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge