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