/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace com::sun::star; using ::std::vector; using ::com::sun::star::beans::XPropertySet; using ::com::sun::star::uno::Sequence; using ::com::sun::star::uno::UNO_QUERY; using ::com::sun::star::uno::Reference; using ::com::sun::star::sheet::DataPilotTablePositionData; using ::com::sun::star::sheet::DataPilotTableResultData; #define SC_DP_FRAME_INNER_BOLD 20 #define SC_DP_FRAME_OUTER_BOLD 40 #define SC_DP_FRAME_COLOR Color(0,0,0) //( 0x20, 0x40, 0x68 ) struct ScDPOutLevelData { tools::Long mnDim; tools::Long mnHier; tools::Long mnLevel; tools::Long mnDimPos; sal_uInt32 mnSrcNumFmt; /// Prevailing number format used in the source data. uno::Sequence maResult; OUString maName; /// Name is the internal field name. OUString maCaption; /// Caption is the name visible in the output table. bool mbHasHiddenMember:1; bool mbDataLayout:1; bool mbPageDim:1; ScDPOutLevelData(tools::Long nDim, tools::Long nHier, tools::Long nLevel, tools::Long nDimPos, sal_uInt32 nSrcNumFmt, const uno::Sequence &aResult, const OUString &aName, const OUString &aCaption, bool bHasHiddenMember, bool bDataLayout, bool bPageDim) : mnDim(nDim), mnHier(nHier), mnLevel(nLevel), mnDimPos(nDimPos), mnSrcNumFmt(nSrcNumFmt), maResult(aResult), maName(aName), maCaption(aCaption), mbHasHiddenMember(bHasHiddenMember), mbDataLayout(bDataLayout), mbPageDim(bPageDim) { } // bug (73840) in uno::Sequence - copy and then assign doesn't work! }; namespace { struct ScDPOutLevelDataComparator { bool operator()(const ScDPOutLevelData & rA, const ScDPOutLevelData & rB) { return rA.mnDimPos 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(mnRows.size()) ); std::sort( mnCols.begin(), mnCols.end()); std::sort( mnRows.begin(), mnRows.end()); for( SCCOL nCol = 0; nCol < static_cast(mnCols.size())-1; nCol ++ ) { if ( !bAllRows ) { if ( nCol < static_cast(mnCols.size())-2) { for ( SCROW i = nCol%2; i < static_cast(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(mnRows.size())-1; i++ ) OutputBlockFrame( mnCols[nCol], mnRows[i], mnCols[nCol+1]-1, mnRows[i+1]-1 ); } } else OutputBlockFrame( mnCols[nCol], mnRows.front(), mnCols[nCol+1]-1, mnRows.back()-1, bAllRows ); } //out put rows area outer framer if ( mnTabStartCol != mnDataStartCol ) { if ( mnTabStartRow != mnDataStartRow ) OutputBlockFrame( mnTabStartCol, mnTabStartRow, mnDataStartCol-1, mnDataStartRow-1 ); OutputBlockFrame( mnTabStartCol, mnDataStartRow, mnDataStartCol-1, mnTabEndRow ); } //out put cols area outer framer OutputBlockFrame( mnDataStartCol, mnTabStartRow, mnTabEndCol, mnDataStartRow-1 ); } ScDPOutputImpl::ScDPOutputImpl( ScDocument* pDoc, sal_uInt16 nTab, SCCOL nTabStartCol, SCROW nTabStartRow, SCCOL nDataStartCol, SCROW nDataStartRow, SCCOL nTabEndCol, SCROW nTabEndRow ): mpDoc( pDoc ), mnTab( nTab ), mnTabStartCol( nTabStartCol ), mnTabStartRow( nTabStartRow ), mnDataStartCol ( nDataStartCol ), mnDataStartRow ( nDataStartRow ), mnTabEndCol( nTabEndCol ), mnTabEndRow( nTabEndRow ) { mbNeedLineCols.resize( nTabEndCol-nDataStartCol+1, false ); mbNeedLineRows.resize( nTabEndRow-nDataStartRow+1, false ); } bool ScDPOutputImpl::AddRow( SCROW nRow ) { if ( !mbNeedLineRows[ nRow - mnDataStartRow ] ) { mbNeedLineRows[ nRow - mnDataStartRow ] = true; mnRows.push_back( nRow ); return true; } else return false; } bool ScDPOutputImpl::AddCol( SCCOL nCol ) { if ( !mbNeedLineCols[ nCol - mnDataStartCol ] ) { mbNeedLineCols[ nCol - mnDataStartCol ] = true; mnCols.push_back( nCol ); return true; } else return false; } void ScDPOutputImpl::OutputBlockFrame ( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, bool bHori ) { Color color = SC_DP_FRAME_COLOR; ::editeng::SvxBorderLine aLine( &color, SC_DP_FRAME_INNER_BOLD ); ::editeng::SvxBorderLine aOutLine( &color, SC_DP_FRAME_OUTER_BOLD ); SvxBoxItem aBox( ATTR_BORDER ); if ( nStartCol == mnTabStartCol ) aBox.SetLine(&aOutLine, SvxBoxItemLine::LEFT); else aBox.SetLine(&aLine, SvxBoxItemLine::LEFT); if ( nStartRow == mnTabStartRow ) aBox.SetLine(&aOutLine, SvxBoxItemLine::TOP); else aBox.SetLine(&aLine, SvxBoxItemLine::TOP); if ( nEndCol == mnTabEndCol ) //bottom row aBox.SetLine(&aOutLine, SvxBoxItemLine::RIGHT); else aBox.SetLine(&aLine, SvxBoxItemLine::RIGHT); if ( nEndRow == mnTabEndRow ) //bottom aBox.SetLine(&aOutLine, SvxBoxItemLine::BOTTOM); else aBox.SetLine(&aLine, SvxBoxItemLine::BOTTOM); SvxBoxInfoItem aBoxInfo( ATTR_BORDER_INNER ); aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::VERT,false ); if ( bHori ) { aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI); aBoxInfo.SetLine( &aLine, SvxBoxInfoItemLine::HORI ); } else aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI,false ); aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::DISTANCE,false); mpDoc->ApplyFrameAreaTab(ScRange(nStartCol, nStartRow, mnTab, nEndCol, nEndRow , mnTab), aBox, aBoxInfo); } void lcl_SetStyleById(ScDocument* pDoc, SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, TranslateId pStrId) { if ( nCol1 > nCol2 || nRow1 > nRow2 ) { OSL_FAIL("SetStyleById: invalid range"); return; } OUString aStyleName = ScResId(pStrId); ScStyleSheetPool* pStlPool = pDoc->GetStyleSheetPool(); ScStyleSheet* pStyle = static_cast( pStlPool->Find( aStyleName, SfxStyleFamily::Para ) ); if (!pStyle) { // create new style (was in ScPivot::SetStyle) pStyle = static_cast( &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& rFormats, sal_Int32& rCount, const uno::Reference& xLevRes, const uno::Reference& xDims ) { if ( rFormats ) return; // already set // xLevRes is from the data layout dimension //TODO: use result sequence from ScDPOutLevelData! uno::Sequence 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 aDataNames; std::vector aDataFormats; sal_Int32 nDimCount = xDims->getCount(); sal_Int32 nDim = 0; for ( ; nDim < nDimCount ; nDim++) { uno::Reference xDim(xDims->getByIndex(nDim), uno::UNO_QUERY); uno::Reference xDimProp( xDim, uno::UNO_QUERY ); uno::Reference 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& xDims ) { tools::Long nDimCount = xDims->getCount(); for (tools::Long nDim=0; nDim 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& 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 getVisiblePageMembersAsResults( const uno::Reference& xLevel ) { if (!xLevel.is()) return uno::Sequence(); uno::Reference xMSupplier(xLevel, UNO_QUERY); if (!xMSupplier.is()) return uno::Sequence(); uno::Reference xNA = xMSupplier->getMembers(); if (!xNA.is()) return uno::Sequence(); std::vector aRes; const uno::Sequence aNames = xNA->getElementNames(); for (const OUString& rName : aNames) { xNA->getByName(rName); uno::Reference 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::quiet_NaN()); } } if (o3tl::make_unsigned(aNames.getLength()) == aRes.size()) // All members are visible. Return empty result. return uno::Sequence(); return ScUnoHelpFunctions::VectorToSequence(aRes); } } ScDPOutput::ScDPOutput( ScDocument* pD, const uno::Reference& xSrc, const ScAddress& rPos, bool bFilter ) : pDoc( pD ), xSource( xSrc ), aStartPos( rPos ), nColFmtCount( 0 ), nRowFmtCount( 0 ), nSingleNumFmt( 0 ), nColCount(0), nRowCount(0), nHeaderSize(0), bDoFilter(bFilter), bResultsError(false), bSizesValid(false), bSizeOverflow(false), mbHeaderLayout(false) { nTabStartCol = nMemberStartCol = nDataStartCol = nTabEndCol = 0; nTabStartRow = nMemberStartRow = nDataStartRow = nTabEndRow = 0; uno::Reference xResult( xSource, uno::UNO_QUERY ); if ( xSource.is() && xResult.is() ) { // get dimension results: uno::Reference xDims = new ScNameToIndexAccess( xSource->getDimensions() ); tools::Long nDimCount = xDims->getCount(); for (tools::Long nDim=0; nDim xDim(xDims->getByIndex(nDim), uno::UNO_QUERY); uno::Reference xDimProp( xDim, uno::UNO_QUERY ); uno::Reference 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 xHiers = new ScNameToIndexAccess( xDimSupp->getHierarchies() ); tools::Long nHierarchy = ScUnoHelpFunctions::GetLongProperty( xDimProp, SC_UNO_DP_USEDHIERARCHY ); if ( nHierarchy >= xHiers->getCount() ) nHierarchy = 0; uno::Reference xHierSupp(xHiers->getByIndex(nHierarchy), uno::UNO_QUERY); if ( xHierSupp.is() ) { uno::Reference xLevels = new ScNameToIndexAccess( xHierSupp->getLevels() ); tools::Long nLevCount = xLevels->getCount(); for (tools::Long nLev=0; nLev xLevel(xLevels->getByIndex(nLev), uno::UNO_QUERY); uno::Reference xLevNam( xLevel, uno::UNO_QUERY ); uno::Reference xLevRes( xLevel, uno::UNO_QUERY ); if ( xLevNam.is() && xLevRes.is() ) { OUString aName = xLevNam->getName(); Reference 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 aResult = xLevRes->getResults(); if (!lcl_MemberEmpty(aResult)) { ScDPOutLevelData tmp(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName, aCaption, bHasHiddenMember, bIsDataLayout, false); pColFields.push_back(tmp); } } break; case sheet::DataPilotFieldOrientation_ROW: { uno::Sequence aResult = xLevRes->getResults(); if (!lcl_MemberEmpty(aResult)) { ScDPOutLevelData tmp(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName, aCaption, bHasHiddenMember, bIsDataLayout, false); pRowFields.push_back(tmp); } } break; case sheet::DataPilotFieldOrientation_PAGE: { uno::Sequence aResult = getVisiblePageMembersAsResults(xLevel); // no check on results for page fields ScDPOutLevelData tmp(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName, aCaption, bHasHiddenMember, false, true); pPageFields.push_back(tmp); } break; default: { // added to avoid warnings } } // get number formats from data dimensions if ( bIsDataLayout ) { OSL_ENSURE( nLevCount == 1, "data layout: multiple levels?" ); if ( eDimOrient == sheet::DataPilotFieldOrientation_COLUMN ) lcl_FillNumberFormats( pColNumFmt, nColFmtCount, xLevRes, xDims ); else if ( eDimOrient == sheet::DataPilotFieldOrientation_ROW ) lcl_FillNumberFormats( pRowNumFmt, nRowFmtCount, xLevRes, xDims ); } } } } } else if ( bIsDataLayout ) { // data layout dimension is hidden (allowed if there is only one data dimension) // -> use the number format from the first data dimension for all results nSingleNumFmt = lcl_GetFirstNumberFormat( xDims ); } } } std::sort(pColFields.begin(), pColFields.end(), ScDPOutLevelDataComparator()); std::sort(pRowFields.begin(), pRowFields.end(), ScDPOutLevelDataComparator()); std::sort(pPageFields.begin(), pPageFields.end(), ScDPOutLevelDataComparator()); // get data results: try { aData = xResult->getResults(); } catch (const uno::RuntimeException&) { bResultsError = true; } } // get "DataDescription" property (may be missing in external sources) uno::Reference xSrcProp( xSource, uno::UNO_QUERY ); if ( !xSrcProp.is() ) return; try { uno::Any aAny = xSrcProp->getPropertyValue( SC_UNO_DP_DATADESC ); OUString aUStr; aAny >>= aUStr; aDataDescription = aUStr; } catch(const uno::Exception&) { } } ScDPOutput::~ScDPOutput() { } void ScDPOutput::SetPosition( const ScAddress& rPos ) { aStartPos = rPos; bSizesValid = bSizeOverflow = false; } void ScDPOutput::DataCell( SCCOL nCol, SCROW nRow, SCTAB nTab, const sheet::DataResult& rData ) { tools::Long nFlags = rData.Flags; if ( nFlags & sheet::DataResultFlags::ERROR ) { pDoc->SetError( nCol, nRow, nTab, FormulaError::NoValue ); } else if ( nFlags & sheet::DataResultFlags::HASDATA ) { pDoc->SetValue( nCol, nRow, nTab, rData.Value ); // use number formats from source OSL_ENSURE( bSizesValid, "DataCell: !bSizesValid" ); sal_uInt32 nFormat = 0; bool bApplyFormat = false; if ( pColNumFmt ) { if ( nCol >= nDataStartCol ) { tools::Long nIndex = nCol - nDataStartCol; if ( nIndex < nColFmtCount ) { nFormat = pColNumFmt[nIndex]; bApplyFormat = true; } } } else if ( pRowNumFmt ) { if ( nRow >= nDataStartRow ) { tools::Long nIndex = nRow - nDataStartRow; if ( nIndex < nRowFmtCount ) { nFormat = pRowNumFmt[nIndex]; bApplyFormat = true; } } } else if ( nSingleNumFmt != 0 ) { nFormat = nSingleNumFmt; // single format is used everywhere bApplyFormat = true; } if (bApplyFormat) pDoc->ApplyAttr(nCol, nRow, nTab, SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat)); } // SubTotal formatting is controlled by headers } void ScDPOutput::HeaderCell( SCCOL nCol, SCROW nRow, SCTAB nTab, const sheet::MemberResult& rData, bool bColHeader, tools::Long nLevel ) { tools::Long nFlags = rData.Flags; if ( nFlags & sheet::MemberResultFlags::HASMEMBER ) { bool bNumeric = (nFlags & sheet::MemberResultFlags::NUMERIC) != 0; if (bNumeric && std::isfinite( rData.Value)) { pDoc->SetValue( nCol, nRow, nTab, rData.Value); } else { ScSetStringParam aParam; if (bNumeric) aParam.setNumericInput(); else aParam.setTextInput(); pDoc->SetString(nCol, nRow, nTab, rData.Caption, &aParam); } } if ( !(nFlags & sheet::MemberResultFlags::SUBTOTAL) ) return; ScDPOutputImpl outputimp( pDoc, nTab, nTabStartCol, nTabStartRow, nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow ); //TODO: limit frames to horizontal or vertical? if (bColHeader) { outputimp.OutputBlockFrame( nCol,nMemberStartRow+static_cast(nLevel), nCol,nDataStartRow-1 ); lcl_SetStyleById( pDoc,nTab, nCol,nMemberStartRow+static_cast(nLevel), nCol,nDataStartRow-1, STR_PIVOT_STYLENAME_TITLE ); lcl_SetStyleById( pDoc,nTab, nCol,nDataStartRow, nCol,nTabEndRow, STR_PIVOT_STYLENAME_RESULT ); } else { outputimp.OutputBlockFrame( nMemberStartCol+static_cast(nLevel),nRow, nDataStartCol-1,nRow ); lcl_SetStyleById( pDoc,nTab, nMemberStartCol+static_cast(nLevel),nRow, nDataStartCol-1,nRow, STR_PIVOT_STYLENAME_TITLE ); lcl_SetStyleById( pDoc,nTab, nDataStartCol,nRow, nTabEndCol,nRow, STR_PIVOT_STYLENAME_RESULT ); } } void ScDPOutput::FieldCell( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScDPOutLevelData& rData, bool bInTable) { // Avoid unwanted automatic format detection. ScSetStringParam aParam; aParam.mbDetectNumberFormat = false; aParam.meSetTextNumFormat = ScSetStringParam::Always; aParam.mbHandleApostrophe = false; pDoc->SetString(nCol, nRow, nTab, rData.maCaption, &aParam); if (bInTable) lcl_SetFrame( pDoc,nTab, nCol,nRow, nCol,nRow, 20 ); // For field button drawing ScMF nMergeFlag = ScMF::NONE; if (rData.mbHasHiddenMember) nMergeFlag |= ScMF::HiddenMember; if (rData.mbPageDim) { nMergeFlag |= ScMF::ButtonPopup; pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, ScMF::Button); pDoc->ApplyFlagsTab(nCol+1, nRow, nCol+1, nRow, nTab, nMergeFlag); } else { nMergeFlag |= ScMF::Button; if (!rData.mbDataLayout) nMergeFlag |= ScMF::ButtonPopup; pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, nMergeFlag); } lcl_SetStyleById( pDoc,nTab, nCol,nRow, nCol,nRow, STR_PIVOT_STYLENAME_FIELDNAME ); } static void lcl_DoFilterButton( ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab ) { pDoc->SetString( nCol, nRow, nTab, ScResId(STR_CELL_FILTER) ); pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, ScMF::Button); } void ScDPOutput::CalcSizes() { if (bSizesValid) return; // get column size of data from first row //TODO: allow different sizes (and clear following areas) ??? nRowCount = aData.getLength(); const uno::Sequence* pRowAry = aData.getConstArray(); nColCount = nRowCount ? ( pRowAry[0].getLength() ) : 0; nHeaderSize = 1; if (GetHeaderLayout() && pColFields.empty()) // Insert an extra header row only when there is no column field. nHeaderSize = 2; // calculate output positions and sizes tools::Long nPageSize = 0; // use page fields! if ( bDoFilter || !pPageFields.empty() ) { nPageSize += pPageFields.size() + 1; // plus one empty row if ( bDoFilter ) ++nPageSize; // filter button above the page fields } if ( aStartPos.Col() + static_cast(pRowFields.size()) + nColCount - 1 > pDoc->MaxCol() || aStartPos.Row() + nPageSize + nHeaderSize + static_cast(pColFields.size()) + nRowCount > pDoc->MaxRow()) { bSizeOverflow = true; } nTabStartCol = aStartPos.Col(); nTabStartRow = aStartPos.Row() + static_cast(nPageSize); // below page fields nMemberStartCol = nTabStartCol; nMemberStartRow = nTabStartRow + static_cast(nHeaderSize); nDataStartCol = nMemberStartCol + static_cast(pRowFields.size()); nDataStartRow = nMemberStartRow + static_cast(pColFields.size()); if ( nColCount > 0 ) nTabEndCol = nDataStartCol + static_cast(nColCount) - 1; else nTabEndCol = nDataStartCol; // single column will remain empty // if page fields are involved, include the page selection cells if ( !pPageFields.empty() && nTabEndCol < nTabStartCol + 1 ) nTabEndCol = nTabStartCol + 1; if ( nRowCount > 0 ) nTabEndRow = nDataStartRow + static_cast(nRowCount) - 1; else nTabEndRow = nDataStartRow; // single row will remain empty bSizesValid = true; } sal_Int32 ScDPOutput::GetPositionType(const ScAddress& rPos) { using namespace ::com::sun::star::sheet; SCCOL nCol = rPos.Col(); SCROW nRow = rPos.Row(); SCTAB nTab = rPos.Tab(); if ( nTab != aStartPos.Tab() ) return DataPilotTablePositionType::NOT_IN_TABLE; CalcSizes(); // Make sure the cursor is within the table. if (nCol < nTabStartCol || nRow < nTabStartRow || nCol > nTabEndCol || nRow > nTabEndRow) return DataPilotTablePositionType::NOT_IN_TABLE; // test for result data area. if (nCol >= nDataStartCol && nCol <= nTabEndCol && nRow >= nDataStartRow && nRow <= nTabEndRow) return DataPilotTablePositionType::RESULT; bool bInColHeader = (nRow >= nTabStartRow && nRow < nDataStartRow); bool bInRowHeader = (nCol >= nTabStartCol && nCol < nDataStartCol); if (bInColHeader && bInRowHeader) // probably in that ugly little box at the upper-left corner of the table. return DataPilotTablePositionType::OTHER; if (bInColHeader) { if (nRow == nTabStartRow) // first row in the column header area is always used for column // field buttons. return DataPilotTablePositionType::OTHER; return DataPilotTablePositionType::COLUMN_HEADER; } if (bInRowHeader) return DataPilotTablePositionType::ROW_HEADER; return DataPilotTablePositionType::OTHER; } void ScDPOutput::Output() { SCTAB nTab = aStartPos.Tab(); const uno::Sequence* pRowAry = aData.getConstArray(); // calculate output positions and sizes CalcSizes(); if ( bSizeOverflow || bResultsError ) // does output area exceed sheet limits? return; // nothing // clear whole (new) output area // when modifying table, clear old area ! //TODO: include InsertDeleteFlags::OBJECTS ??? pDoc->DeleteAreaTab( aStartPos.Col(), aStartPos.Row(), nTabEndCol, nTabEndRow, nTab, InsertDeleteFlags::ALL ); if ( bDoFilter ) lcl_DoFilterButton( pDoc, aStartPos.Col(), aStartPos.Row(), nTab ); // output page fields: for (size_t nField=0; nField& rRes = pPageFields[nField].maResult; sal_Int32 n = rRes.getLength(); if (n == 1) { if (rRes[0].Caption.isEmpty()) aPageValue = ScResId(STR_EMPTYDATA); else aPageValue = rRes[0].Caption; } else if (n > 1) aPageValue = ScResId(SCSTR_MULTIPLE); ScSetStringParam aParam; aParam.setTextInput(); pDoc->SetString(nFldCol, nHdrRow, nTab, aPageValue, &aParam); lcl_SetFrame( pDoc,nTab, nFldCol,nHdrRow, nFldCol,nHdrRow, 20 ); } // data description // (may get overwritten by first row field) if (aDataDescription.isEmpty()) { //TODO: use default string ("result") ? } pDoc->SetString(nTabStartCol, nTabStartRow, nTab, aDataDescription); // set STR_PIVOT_STYLENAME_INNER for whole data area (subtotals are overwritten) if ( nDataStartRow > nTabStartRow ) lcl_SetStyleById( pDoc, nTab, nTabStartCol, nTabStartRow, nTabEndCol, nDataStartRow-1, STR_PIVOT_STYLENAME_TOP ); lcl_SetStyleById( pDoc, nTab, nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow, STR_PIVOT_STYLENAME_INNER ); // output column headers: ScDPOutputImpl outputimp( pDoc, nTab, nTabStartCol, nTabStartRow, nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow ); for (size_t nField=0; nField(nField); //TODO: check for overflow FieldCell(nHdrCol, nTabStartRow, nTab, pColFields[nField], true); SCROW nRowPos = nMemberStartRow + static_cast(nField); //TODO: check for overflow const uno::Sequence rSequence = pColFields[nField].maResult; const sheet::MemberResult* pArray = rSequence.getConstArray(); tools::Long nThisColCount = rSequence.getLength(); OSL_ENSURE( nThisColCount == nColCount, "count mismatch" ); //TODO: ??? for (tools::Long nCol=0; nCol(nCol); //TODO: check for overflow HeaderCell( nColPos, nRowPos, nTab, pArray[nCol], true, nField ); if ( ( pArray[nCol].Flags & sheet::MemberResultFlags::HASMEMBER ) && !( pArray[nCol].Flags & sheet::MemberResultFlags::SUBTOTAL ) ) { tools::Long nEnd = nCol; while ( nEnd+1 < nThisColCount && ( pArray[nEnd+1].Flags & sheet::MemberResultFlags::CONTINUE ) ) ++nEnd; SCCOL nEndColPos = nDataStartCol + static_cast(nEnd); //TODO: check for overflow if ( nField+1 < pColFields.size()) { if ( nField == pColFields.size() - 2 ) { outputimp.AddCol( nColPos ); if ( nColPos + 1 == nEndColPos ) outputimp.OutputBlockFrame( nColPos,nRowPos, nEndColPos,nRowPos+1, true ); } else outputimp.OutputBlockFrame( nColPos,nRowPos, nEndColPos,nRowPos ); lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nEndColPos,nDataStartRow-1, STR_PIVOT_STYLENAME_CATEGORY ); } else lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nColPos,nDataStartRow-1, STR_PIVOT_STYLENAME_CATEGORY ); } else if ( pArray[nCol].Flags & sheet::MemberResultFlags::SUBTOTAL ) outputimp.AddCol( nColPos ); // Apply the same number format as in data source. pDoc->ApplyAttr(nColPos, nRowPos, nTab, SfxUInt32Item(ATTR_VALUE_FORMAT, pColFields[nField].mnSrcNumFmt)); } if ( nField== 0 && pColFields.size() == 1 ) outputimp.OutputBlockFrame( nDataStartCol,nTabStartRow, nTabEndCol,nRowPos-1 ); } // output row headers: std::vector vbSetBorder; vbSetBorder.resize( nTabEndRow - nDataStartRow + 1, false ); for (size_t nField=0; nField(nField); //TODO: check for overflow SCROW nHdrRow = nDataStartRow - 1; FieldCell(nHdrCol, nHdrRow, nTab, pRowFields[nField], true); SCCOL nColPos = nMemberStartCol + static_cast(nField); //TODO: check for overflow const uno::Sequence rSequence = pRowFields[nField].maResult; const sheet::MemberResult* pArray = rSequence.getConstArray(); sal_Int32 nThisRowCount = rSequence.getLength(); OSL_ENSURE( nThisRowCount == nRowCount, "count mismatch" ); //TODO: ??? for (sal_Int32 nRow=0; nRow(nRow); //TODO: check for overflow HeaderCell( nColPos, nRowPos, nTab, pArray[nRow], false, nField ); if ( ( pArray[nRow].Flags & sheet::MemberResultFlags::HASMEMBER ) && !( pArray[nRow].Flags & sheet::MemberResultFlags::SUBTOTAL ) ) { if ( nField+1 < pRowFields.size() ) { tools::Long nEnd = nRow; while ( nEnd+1 < nThisRowCount && ( pArray[nEnd+1].Flags & sheet::MemberResultFlags::CONTINUE ) ) ++nEnd; SCROW nEndRowPos = nDataStartRow + static_cast(nEnd); //TODO: check for overflow outputimp.AddRow( nRowPos ); if ( !vbSetBorder[ nRow ] ) { outputimp.OutputBlockFrame( nColPos, nRowPos, nTabEndCol, nEndRowPos ); vbSetBorder[ nRow ] = true; } outputimp.OutputBlockFrame( nColPos, nRowPos, nColPos, nEndRowPos ); if ( nField == pRowFields.size() - 2 ) outputimp.OutputBlockFrame( nColPos+1, nRowPos, nColPos+1, nEndRowPos ); lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nDataStartCol-1,nEndRowPos, STR_PIVOT_STYLENAME_CATEGORY ); } else lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nDataStartCol-1,nRowPos, STR_PIVOT_STYLENAME_CATEGORY ); } else if ( pArray[nRow].Flags & sheet::MemberResultFlags::SUBTOTAL ) outputimp.AddRow( nRowPos ); // Apply the same number format as in data source. pDoc->ApplyAttr(nColPos, nRowPos, nTab, SfxUInt32Item(ATTR_VALUE_FORMAT, pRowFields[nField].mnSrcNumFmt)); } } if (nColCount == 1 && nRowCount > 0 && pColFields.empty()) { // the table contains exactly one data field and no column fields. // Display data description at top right corner. ScSetStringParam aParam; aParam.setTextInput(); pDoc->SetString(nDataStartCol, nDataStartRow-1, nTab, aDataDescription, &aParam); } // output data results: for (sal_Int32 nRow=0; nRow(nRow); //TODO: check for overflow const sheet::DataResult* pColAry = pRowAry[nRow].getConstArray(); sal_Int32 nThisColCount = pRowAry[nRow].getLength(); OSL_ENSURE( nThisColCount == nColCount, "count mismatch" ); //TODO: ??? for (sal_Int32 nCol=0; nCol(nCol); //TODO: check for overflow DataCell( nColPos, nRowPos, nTab, pColAry[nCol] ); } } outputimp.OutputDataArea(); } ScRange ScDPOutput::GetOutputRange( sal_Int32 nRegionType ) { using namespace ::com::sun::star::sheet; CalcSizes(); SCTAB nTab = aStartPos.Tab(); switch (nRegionType) { case DataPilotOutputRangeType::RESULT: return ScRange(nDataStartCol, nDataStartRow, nTab, nTabEndCol, nTabEndRow, nTab); case DataPilotOutputRangeType::TABLE: return ScRange(aStartPos.Col(), nTabStartRow, nTab, nTabEndCol, nTabEndRow, nTab); default: OSL_ENSURE(nRegionType == DataPilotOutputRangeType::WHOLE, "ScDPOutput::GetOutputRange: unknown region type"); break; } return ScRange(aStartPos.Col(), aStartPos.Row(), nTab, nTabEndCol, nTabEndRow, nTab); } bool ScDPOutput::HasError() { CalcSizes(); return bSizeOverflow || bResultsError; } sal_Int32 ScDPOutput::GetHeaderRows() const { return pPageFields.size() + ( bDoFilter ? 1 : 0 ); } namespace { void insertNames(ScDPUniqueStringSet& rNames, const uno::Sequence& rMemberResults) { for (const sheet::MemberResult& rMemberResult : rMemberResults) { if (rMemberResult.Flags & sheet::MemberResultFlags::HASMEMBER) rNames.insert(rMemberResult.Name); } } } void ScDPOutput::GetMemberResultNames(ScDPUniqueStringSet& rNames, tools::Long nDimension) { // Return the list of all member names in a dimension's MemberResults. // Only the dimension has to be compared because this is only used with table data, // where each dimension occurs only once. auto lFindDimension = [nDimension](const ScDPOutLevelData& rField) { return rField.mnDim == nDimension; }; // look in column fields auto colit = std::find_if(pColFields.begin(), pColFields.end(), lFindDimension); if (colit != pColFields.end()) { // collect the member names insertNames(rNames, colit->maResult); return; } // look in row fields auto rowit = std::find_if(pRowFields.begin(), pRowFields.end(), lFindDimension); if (rowit != pRowFields.end()) { // collect the member names insertNames(rNames, rowit->maResult); } } void ScDPOutput::SetHeaderLayout(bool bUseGrid) { mbHeaderLayout = bUseGrid; bSizesValid = false; } namespace { void lcl_GetTableVars( sal_Int32& rGrandTotalCols, sal_Int32& rGrandTotalRows, sal_Int32& rDataLayoutIndex, std::vector& rDataNames, std::vector& rGivenNames, sheet::DataPilotFieldOrientation& rDataOrient, const uno::Reference& xSource ) { rDataLayoutIndex = -1; // invalid rGrandTotalCols = 0; rGrandTotalRows = 0; rDataOrient = sheet::DataPilotFieldOrientation_HIDDEN; uno::Reference 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 xDims = new ScNameToIndexAccess( xSource->getDimensions() ); tools::Long nDimCount = xDims->getCount(); for (tools::Long nDim=0; nDim xDim(xDims->getByIndex(nDim), uno::UNO_QUERY); uno::Reference xDimProp( xDim, uno::UNO_QUERY ); if ( xDimProp.is() ) { sheet::DataPilotFieldOrientation eDimOrient = ScUnoHelpFunctions::GetEnumProperty( xDimProp, SC_UNO_DP_ORIENTATION, sheet::DataPilotFieldOrientation_HIDDEN ); if ( ScUnoHelpFunctions::GetBoolProperty( xDimProp, SC_UNO_DP_ISDATALAYOUT ) ) { rDataLayoutIndex = nDim; rDataOrient = eDimOrient; } if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA ) { OUString aSourceName; OUString aGivenName; ScDPOutput::GetDataDimensionNames( aSourceName, aGivenName, xDim ); try { uno::Any aValue = xDimProp->getPropertyValue( SC_UNO_DP_LAYOUTNAME ); if( aValue.hasValue() ) { OUString strLayoutName; if( ( aValue >>= strLayoutName ) && !strLayoutName.isEmpty() ) aGivenName = strLayoutName; } } catch(const uno::Exception&) { } rDataNames.push_back( aSourceName ); rGivenNames.push_back( aGivenName ); ++nDataCount; } } } if ( ( rDataOrient == sheet::DataPilotFieldOrientation_COLUMN ) && bColGrand ) rGrandTotalCols = nDataCount; else if ( ( rDataOrient == sheet::DataPilotFieldOrientation_ROW ) && bRowGrand ) rGrandTotalRows = nDataCount; } } void ScDPOutput::GetPositionData(const ScAddress& rPos, DataPilotTablePositionData& rPosData) { using namespace ::com::sun::star::sheet; SCCOL nCol = rPos.Col(); SCROW nRow = rPos.Row(); SCTAB nTab = rPos.Tab(); if ( nTab != aStartPos.Tab() ) return; // wrong sheet // calculate output positions and sizes CalcSizes(); rPosData.PositionType = GetPositionType(rPos); switch (rPosData.PositionType) { case DataPilotTablePositionType::RESULT: { vector aFilters; GetDataResultPositionData(aFilters, rPos); DataPilotTableResultData aResData; aResData.FieldFilters = comphelper::containerToSequence(aFilters); aResData.DataFieldIndex = 0; Reference xPropSet(xSource, UNO_QUERY); if (xPropSet.is()) { sal_Int32 nDataFieldCount = ScUnoHelpFunctions::GetLongProperty( xPropSet, SC_UNO_DP_DATAFIELDCOUNT ); if (nDataFieldCount > 0) aResData.DataFieldIndex = (nRow - nDataStartRow) % nDataFieldCount; } // Copy appropriate DataResult object from the cached sheet::DataResult table. if (aData.getLength() > nRow - nDataStartRow && aData[nRow-nDataStartRow].getLength() > nCol-nDataStartCol) aResData.Result = aData[nRow-nDataStartRow][nCol-nDataStartCol]; rPosData.PositionData <<= aResData; return; } case DataPilotTablePositionType::COLUMN_HEADER: { tools::Long nField = nRow - nTabStartRow - 1; // 1st line is used for the buttons if (nField < 0) break; if (pColFields.size() < o3tl::make_unsigned(nField) + 1 ) break; const uno::Sequence rSequence = pColFields[nField].maResult; if (!rSequence.hasElements()) break; const sheet::MemberResult* pArray = rSequence.getConstArray(); tools::Long nItem = nCol - nDataStartCol; // get origin of "continue" fields while (nItem > 0 && ( pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) ) --nItem; if (nItem < 0) break; DataPilotTableHeaderData aHeaderData; aHeaderData.MemberName = pArray[nItem].Name; aHeaderData.Flags = pArray[nItem].Flags; aHeaderData.Dimension = static_cast(pColFields[nField].mnDim); aHeaderData.Hierarchy = static_cast(pColFields[nField].mnHier); aHeaderData.Level = static_cast(pColFields[nField].mnLevel); rPosData.PositionData <<= aHeaderData; return; } case DataPilotTablePositionType::ROW_HEADER: { tools::Long nField = nCol - nTabStartCol; if (nField < 0) break; if (pRowFields.size() < o3tl::make_unsigned(nField) + 1 ) break; const uno::Sequence rSequence = pRowFields[nField].maResult; if (!rSequence.hasElements()) break; const sheet::MemberResult* pArray = rSequence.getConstArray(); tools::Long nItem = nRow - nDataStartRow; // get origin of "continue" fields while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) ) --nItem; if (nItem < 0) break; DataPilotTableHeaderData aHeaderData; aHeaderData.MemberName = pArray[nItem].Name; aHeaderData.Flags = pArray[nItem].Flags; aHeaderData.Dimension = static_cast(pRowFields[nField].mnDim); aHeaderData.Hierarchy = static_cast(pRowFields[nField].mnHier); aHeaderData.Level = static_cast(pRowFields[nField].mnLevel); rPosData.PositionData <<= aHeaderData; return; } } } bool ScDPOutput::GetDataResultPositionData(vector& rFilters, const ScAddress& rPos) { // Check to make sure there is at least one data field. Reference xPropSet(xSource, UNO_QUERY); if (!xPropSet.is()) return false; sal_Int32 nDataFieldCount = ScUnoHelpFunctions::GetLongProperty( xPropSet, SC_UNO_DP_DATAFIELDCOUNT ); if (nDataFieldCount == 0) // No data field is present in this datapilot table. return false; // #i111421# use lcl_GetTableVars for correct size of totals and data layout position sal_Int32 nGrandTotalCols; sal_Int32 nGrandTotalRows; sal_Int32 nDataLayoutIndex; std::vector aDataNames; std::vector 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(nTabEndCol - nGrandTotalCols)); bool bFilterByRow = (nRow <= static_cast(nTabEndRow - nGrandTotalRows)); // column fields for (size_t nColField = 0; nColField < pColFields.size() && bFilterByCol; ++nColField) { if (pColFields[nColField].mnDim == nDataLayoutIndex) // There is no sense including the data layout field for filtering. continue; sheet::DataPilotFieldFilter filter; filter.FieldName = pColFields[nColField].maName; const uno::Sequence rSequence = pColFields[nColField].maResult; const sheet::MemberResult* pArray = rSequence.getConstArray(); OSL_ENSURE(nDataStartCol + rSequence.getLength() - 1 == nTabEndCol, "ScDPOutput::GetDataFieldCellData: error in geometric assumption"); tools::Long nItem = nCol - nDataStartCol; // get origin of "continue" fields while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) ) --nItem; filter.MatchValueName = pArray[nItem].Name; rFilters.push_back(filter); } // row fields for (size_t nRowField = 0; nRowField < pRowFields.size() && bFilterByRow; ++nRowField) { if (pRowFields[nRowField].mnDim == nDataLayoutIndex) // There is no sense including the data layout field for filtering. continue; sheet::DataPilotFieldFilter filter; filter.FieldName = pRowFields[nRowField].maName; const uno::Sequence rSequence = pRowFields[nRowField].maResult; const sheet::MemberResult* pArray = rSequence.getConstArray(); OSL_ENSURE(nDataStartRow + rSequence.getLength() - 1 == nTabEndRow, "ScDPOutput::GetDataFieldCellData: error in geometric assumption"); tools::Long nItem = nRow - nDataStartRow; // get origin of "continue" fields while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) ) --nItem; filter.MatchValueName = pArray[nItem].Name; rFilters.push_back(filter); } return true; } namespace { OUString lcl_GetDataFieldName( std::u16string_view rSourceName, sal_Int16 eFunc ) { TranslateId pStrId; switch ( eFunc ) { case sheet::GeneralFunction2::SUM: pStrId = STR_FUN_TEXT_SUM; break; case sheet::GeneralFunction2::COUNT: case sheet::GeneralFunction2::COUNTNUMS: pStrId = STR_FUN_TEXT_COUNT; break; case sheet::GeneralFunction2::AVERAGE: pStrId = STR_FUN_TEXT_AVG; break; case sheet::GeneralFunction2::MEDIAN: pStrId = STR_FUN_TEXT_MEDIAN; break; case sheet::GeneralFunction2::MAX: pStrId = STR_FUN_TEXT_MAX; break; case sheet::GeneralFunction2::MIN: pStrId = STR_FUN_TEXT_MIN; break; case sheet::GeneralFunction2::PRODUCT: pStrId = STR_FUN_TEXT_PRODUCT; break; case sheet::GeneralFunction2::STDEV: case sheet::GeneralFunction2::STDEVP: pStrId = STR_FUN_TEXT_STDDEV; break; case sheet::GeneralFunction2::VAR: case sheet::GeneralFunction2::VARP: pStrId = STR_FUN_TEXT_VAR; break; case sheet::GeneralFunction2::NONE: case sheet::GeneralFunction2::AUTO: break; default: { assert(false); } } if (!pStrId) return OUString(); return ScResId(pStrId) + " - " + rSourceName; } } void ScDPOutput::GetDataDimensionNames( OUString& rSourceName, OUString& rGivenName, const uno::Reference& xDim ) { uno::Reference xDimProp( xDim, uno::UNO_QUERY ); uno::Reference xDimName( xDim, uno::UNO_QUERY ); if ( !(xDimProp.is() && xDimName.is()) ) return; // Asterisks are added in ScDPSaveData::WriteToSource to create unique names. //TODO: preserve original name there? rSourceName = ScDPUtil::getSourceDimensionName(xDimName->getName()); // Generate "given name" the same way as in dptabres. //TODO: Should use a stored name when available sal_Int16 eFunc = ScUnoHelpFunctions::GetShortProperty( xDimProp, SC_UNO_DP_FUNCTION2, sheet::GeneralFunction2::NONE ); rGivenName = lcl_GetDataFieldName( rSourceName, eFunc ); } bool ScDPOutput::IsFilterButton( const ScAddress& rPos ) { SCCOL nCol = rPos.Col(); SCROW nRow = rPos.Row(); SCTAB nTab = rPos.Tab(); if ( nTab != aStartPos.Tab() || !bDoFilter ) return false; // wrong sheet or no button at all // filter button is at top left return ( nCol == aStartPos.Col() && nRow == aStartPos.Row() ); } tools::Long ScDPOutput::GetHeaderDim( const ScAddress& rPos, sheet::DataPilotFieldOrientation& rOrient ) { SCCOL nCol = rPos.Col(); SCROW nRow = rPos.Row(); SCTAB nTab = rPos.Tab(); if ( nTab != aStartPos.Tab() ) return -1; // wrong sheet // calculate output positions and sizes CalcSizes(); // test for column header if ( nRow == nTabStartRow && nCol >= nDataStartCol && o3tl::make_unsigned(nCol) < nDataStartCol + pColFields.size()) { rOrient = sheet::DataPilotFieldOrientation_COLUMN; tools::Long nField = nCol - nDataStartCol; return pColFields[nField].mnDim; } // test for row header if ( nRow+1 == nDataStartRow && nCol >= nTabStartCol && o3tl::make_unsigned(nCol) < nTabStartCol + pRowFields.size() ) { rOrient = sheet::DataPilotFieldOrientation_ROW; tools::Long nField = nCol - nTabStartCol; return pRowFields[nField].mnDim; } // test for page field SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 ); if ( nCol == aStartPos.Col() && nRow >= nPageStartRow && o3tl::make_unsigned(nRow) < nPageStartRow + pPageFields.size() ) { rOrient = sheet::DataPilotFieldOrientation_PAGE; tools::Long nField = nRow - nPageStartRow; return pPageFields[nField].mnDim; } //TODO: single data field (?) rOrient = sheet::DataPilotFieldOrientation_HIDDEN; return -1; // invalid } bool ScDPOutput::GetHeaderDrag( const ScAddress& rPos, bool bMouseLeft, bool bMouseTop, tools::Long nDragDim, tools::Rectangle& rPosRect, sheet::DataPilotFieldOrientation& rOrient, tools::Long& rDimPos ) { // Rectangle instead of ScRange for rPosRect to allow for negative values SCCOL nCol = rPos.Col(); SCROW nRow = rPos.Row(); SCTAB nTab = rPos.Tab(); if ( nTab != aStartPos.Tab() ) return false; // wrong sheet // calculate output positions and sizes CalcSizes(); // test for column header if ( nCol >= nDataStartCol && nCol <= nTabEndCol && nRow + 1 >= nMemberStartRow && o3tl::make_unsigned(nRow) < nMemberStartRow + pColFields.size()) { tools::Long nField = nRow - nMemberStartRow; if (nField < 0) { nField = 0; bMouseTop = true; } //TODO: find start of dimension rPosRect = tools::Rectangle( nDataStartCol, nMemberStartRow + nField, nTabEndCol, nMemberStartRow + nField -1 ); bool bFound = false; // is this within the same orientation? bool bBeforeDrag = false; bool bAfterDrag = false; for (tools::Long nPos=0; o3tl::make_unsigned(nPos) nPos ) bAfterDrag = true; } } if ( bFound ) { if (!bBeforeDrag) { rPosRect.AdjustBottom( 1 ); if (bAfterDrag) rPosRect.AdjustTop( 1 ); } } else { if ( !bMouseTop ) { rPosRect.AdjustTop( 1 ); rPosRect.AdjustBottom( 1 ); ++nField; } } rOrient = sheet::DataPilotFieldOrientation_COLUMN; rDimPos = nField; //!... return true; } // test for row header // special case if no row fields bool bSpecial = ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow && pRowFields.empty() && nCol == nTabStartCol && bMouseLeft ); if ( bSpecial || ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow && nCol + 1 >= nTabStartCol && o3tl::make_unsigned(nCol) < nTabStartCol + pRowFields.size() ) ) { tools::Long nField = nCol - nTabStartCol; //TODO: find start of dimension rPosRect = tools::Rectangle( nTabStartCol + nField, nDataStartRow - 1, nTabStartCol + nField - 1, nTabEndRow ); bool bFound = false; // is this within the same orientation? bool bBeforeDrag = false; bool bAfterDrag = false; for (tools::Long nPos=0; o3tl::make_unsigned(nPos) nPos ) bAfterDrag = true; } } if ( bFound ) { if (!bBeforeDrag) { rPosRect.AdjustRight( 1 ); if (bAfterDrag) rPosRect.AdjustLeft( 1 ); } } else { if ( !bMouseLeft ) { rPosRect.AdjustLeft( 1 ); rPosRect.AdjustRight( 1 ); ++nField; } } rOrient = sheet::DataPilotFieldOrientation_ROW; rDimPos = nField; //!... return true; } // test for page fields SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 ); if ( nCol >= aStartPos.Col() && nCol <= nTabEndCol && nRow + 1 >= nPageStartRow && o3tl::make_unsigned(nRow) < nPageStartRow + pPageFields.size() ) { tools::Long nField = nRow - nPageStartRow; if (nField < 0) { nField = 0; bMouseTop = true; } //TODO: find start of dimension rPosRect = tools::Rectangle( aStartPos.Col(), nPageStartRow + nField, nTabEndCol, nPageStartRow + nField - 1 ); bool bFound = false; // is this within the same orientation? bool bBeforeDrag = false; bool bAfterDrag = false; for (tools::Long nPos=0; o3tl::make_unsigned(nPos) nPos ) bAfterDrag = true; } } if ( bFound ) { if (!bBeforeDrag) { rPosRect.AdjustBottom( 1 ); if (bAfterDrag) rPosRect.AdjustTop( 1 ); } } else { if ( !bMouseTop ) { rPosRect.AdjustTop( 1 ); rPosRect.AdjustBottom( 1 ); ++nField; } } rOrient = sheet::DataPilotFieldOrientation_PAGE; rDimPos = nField; //!... return true; } return false; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */