update dev300-m58
[ooovba.git] / sc / source / core / data / dpoutput.cxx
blob6894e560450f7be6c9555e76ad148c42115951d2
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: dpoutput.cxx,v $
10 * $Revision: 1.17.30.2 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
36 // INCLUDE ---------------------------------------------------------------
38 #include "scitems.hxx"
39 #include <svx/algitem.hxx>
40 #include <svx/boxitem.hxx>
41 #include <svx/brshitem.hxx>
42 #include <svx/wghtitem.hxx>
43 #include <unotools/transliterationwrapper.hxx>
45 #include "dpoutput.hxx"
46 #include "dptabsrc.hxx"
47 #include "dpcachetable.hxx"
48 #include "document.hxx"
49 #include "patattr.hxx"
50 #include "docpool.hxx"
51 #include "markdata.hxx"
52 #include "attrib.hxx"
53 #include "formula/errorcodes.hxx" // errNoValue
54 #include "miscuno.hxx"
55 #include "globstr.hrc"
56 #include "stlpool.hxx"
57 #include "stlsheet.hxx"
58 #include "collect.hxx"
59 #include "scresid.hxx"
60 #include "unonames.hxx"
61 #include "sc.hrc"
63 #include <com/sun/star/container/XNamed.hpp>
64 #include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
65 #include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
66 #include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
67 #include <com/sun/star/sheet/DataPilotTablePositionData.hpp>
68 #include <com/sun/star/sheet/DataPilotTablePositionType.hpp>
69 #include <com/sun/star/sheet/DataPilotTableResultData.hpp>
70 #include <com/sun/star/sheet/DataResultFlags.hpp>
71 #include <com/sun/star/sheet/GeneralFunction.hpp>
72 #include <com/sun/star/sheet/MemberResultFlags.hpp>
73 #include <com/sun/star/sheet/TableFilterField.hpp>
74 #include <com/sun/star/sheet/XDataPilotMemberResults.hpp>
75 #include <com/sun/star/sheet/XDataPilotResults.hpp>
76 #include <com/sun/star/sheet/XHierarchiesSupplier.hpp>
77 #include <com/sun/star/sheet/XLevelsSupplier.hpp>
78 #include <com/sun/star/beans/XPropertySet.hpp>
80 #include <vector>
82 using namespace com::sun::star;
83 using ::std::vector;
84 using ::com::sun::star::beans::XPropertySet;
85 using ::com::sun::star::uno::Sequence;
86 using ::com::sun::star::uno::UNO_QUERY;
87 using ::com::sun::star::uno::Reference;
88 using ::com::sun::star::sheet::DataPilotTablePositionData;
89 using ::com::sun::star::sheet::DataPilotTableResultData;
90 using ::com::sun::star::uno::makeAny;
91 using ::com::sun::star::uno::Any;
92 using ::rtl::OUString;
94 // -----------------------------------------------------------------------
96 //! move to a header file
97 //! use names from unonames.hxx?
98 #define DP_PROP_FUNCTION "Function"
99 #define DP_PROP_ORIENTATION "Orientation"
100 #define DP_PROP_POSITION "Position"
101 #define DP_PROP_USEDHIERARCHY "UsedHierarchy"
102 #define DP_PROP_ISDATALAYOUT "IsDataLayoutDimension"
103 #define DP_PROP_NUMBERFORMAT "NumberFormat"
104 #define DP_PROP_FILTER "Filter"
105 #define DP_PROP_COLUMNGRAND "ColumnGrand"
106 #define DP_PROP_ROWGRAND "RowGrand"
107 #define DP_PROP_SUBTOTALS "SubTotals"
109 // -----------------------------------------------------------------------
111 //! dynamic!!!
112 #define SC_DPOUT_MAXLEVELS 256
115 struct ScDPOutLevelData
117 long nDim;
118 long nHier;
119 long nLevel;
120 long nDimPos;
121 uno::Sequence<sheet::MemberResult> aResult;
122 String maName; /// Name is the internal field name.
123 String aCaption; /// Caption is the name visible in the output table.
124 bool mbHasHiddenMember;
126 ScDPOutLevelData()
128 nDim = nHier = nLevel = nDimPos = -1;
129 mbHasHiddenMember = false;
132 BOOL operator<(const ScDPOutLevelData& r) const
133 { return nDimPos<r.nDimPos || ( nDimPos==r.nDimPos && nHier<r.nHier ) ||
134 ( nDimPos==r.nDimPos && nHier==r.nHier && nLevel<r.nLevel ); }
136 void Swap(ScDPOutLevelData& r)
137 //! { ScDPOutLevelData aTemp = r; r = *this; *this = aTemp; }
138 { ScDPOutLevelData aTemp; aTemp = r; r = *this; *this = aTemp; }
140 //! bug (73840) in uno::Sequence - copy and then assign doesn't work!
143 // -----------------------------------------------------------------------
145 void lcl_SetStyleById( ScDocument* pDoc, SCTAB nTab,
146 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
147 USHORT nStrId )
149 if ( nCol1 > nCol2 || nRow1 > nRow2 )
151 DBG_ERROR("SetStyleById: invalid range");
152 return;
155 String aStyleName = ScGlobal::GetRscString( nStrId );
156 ScStyleSheetPool* pStlPool = pDoc->GetStyleSheetPool();
157 ScStyleSheet* pStyle = (ScStyleSheet*) pStlPool->Find( aStyleName, SFX_STYLE_FAMILY_PARA );
158 if (!pStyle)
160 // create new style (was in ScPivot::SetStyle)
162 pStyle = (ScStyleSheet*) &pStlPool->Make( aStyleName, SFX_STYLE_FAMILY_PARA,
163 SFXSTYLEBIT_USERDEF );
164 pStyle->SetParent( ScGlobal::GetRscString(STR_STYLENAME_STANDARD) );
165 SfxItemSet& rSet = pStyle->GetItemSet();
166 if ( nStrId==STR_PIVOT_STYLE_RESULT || nStrId==STR_PIVOT_STYLE_TITLE )
167 rSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
168 if ( nStrId==STR_PIVOT_STYLE_CATEGORY || nStrId==STR_PIVOT_STYLE_TITLE )
169 rSet.Put( SvxHorJustifyItem( SVX_HOR_JUSTIFY_LEFT, ATTR_HOR_JUSTIFY ) );
172 pDoc->ApplyStyleAreaTab( nCol1, nRow1, nCol2, nRow2, nTab, *pStyle );
175 void lcl_SetFrame( ScDocument* pDoc, SCTAB nTab,
176 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
177 USHORT nWidth )
179 SvxBorderLine aLine;
180 aLine.SetOutWidth(nWidth);
181 SvxBoxItem aBox( ATTR_BORDER );
182 aBox.SetLine(&aLine, BOX_LINE_LEFT);
183 aBox.SetLine(&aLine, BOX_LINE_TOP);
184 aBox.SetLine(&aLine, BOX_LINE_RIGHT);
185 aBox.SetLine(&aLine, BOX_LINE_BOTTOM);
186 SvxBoxInfoItem aBoxInfo( ATTR_BORDER_INNER );
187 aBoxInfo.SetValid(VALID_HORI,FALSE);
188 aBoxInfo.SetValid(VALID_VERT,FALSE);
189 aBoxInfo.SetValid(VALID_DISTANCE,FALSE);
191 pDoc->ApplyFrameAreaTab( ScRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab ), &aBox, &aBoxInfo );
194 // -----------------------------------------------------------------------
196 void lcl_FillNumberFormats( UINT32*& rFormats, long& rCount,
197 const uno::Reference<sheet::XDataPilotMemberResults>& xLevRes,
198 const uno::Reference<container::XIndexAccess>& xDims )
200 if ( rFormats )
201 return; // already set
203 // xLevRes is from the data layout dimension
204 //! use result sequence from ScDPOutLevelData!
206 uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults();
208 long nSize = aResult.getLength();
209 if (nSize)
211 // get names/formats for all data dimensions
212 //! merge this with the loop to collect ScDPOutLevelData?
214 String aDataNames[SC_DPOUT_MAXLEVELS];
215 UINT32 nDataFormats[SC_DPOUT_MAXLEVELS];
216 long nDataCount = 0;
217 BOOL bAnySet = FALSE;
219 long nDimCount = xDims->getCount();
220 for (long nDim=0; nDim<nDimCount; nDim++)
222 uno::Reference<uno::XInterface> xDim =
223 ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) );
224 uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
225 uno::Reference<container::XNamed> xDimName( xDim, uno::UNO_QUERY );
226 if ( xDimProp.is() && xDimName.is() )
228 sheet::DataPilotFieldOrientation eDimOrient =
229 (sheet::DataPilotFieldOrientation) ScUnoHelpFunctions::GetEnumProperty(
230 xDimProp, rtl::OUString::createFromAscii(DP_PROP_ORIENTATION),
231 sheet::DataPilotFieldOrientation_HIDDEN );
232 if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
234 aDataNames[nDataCount] = String( xDimName->getName() );
235 long nFormat = ScUnoHelpFunctions::GetLongProperty(
236 xDimProp,
237 rtl::OUString::createFromAscii(DP_PROP_NUMBERFORMAT) );
238 nDataFormats[nDataCount] = nFormat;
239 if ( nFormat != 0 )
240 bAnySet = TRUE;
241 ++nDataCount;
246 if ( bAnySet ) // forget everything if all formats are 0 (or no data dimensions)
248 const sheet::MemberResult* pArray = aResult.getConstArray();
250 String aName;
251 UINT32* pNumFmt = new UINT32[nSize];
252 if (nDataCount == 1)
254 // only one data dimension -> use its numberformat everywhere
255 long nFormat = nDataFormats[0];
256 for (long nPos=0; nPos<nSize; nPos++)
257 pNumFmt[nPos] = nFormat;
259 else
261 for (long nPos=0; nPos<nSize; nPos++)
263 // if CONTINUE bit is set, keep previous name
264 //! keep number format instead!
265 if ( !(pArray[nPos].Flags & sheet::MemberResultFlags::CONTINUE) )
266 aName = String( pArray[nPos].Name );
268 UINT32 nFormat = 0;
269 for (long i=0; i<nDataCount; i++)
270 if (aName == aDataNames[i]) //! search more efficiently?
272 nFormat = nDataFormats[i];
273 break;
275 pNumFmt[nPos] = nFormat;
279 rFormats = pNumFmt;
280 rCount = nSize;
285 UINT32 lcl_GetFirstNumberFormat( const uno::Reference<container::XIndexAccess>& xDims )
287 long nDimCount = xDims->getCount();
288 for (long nDim=0; nDim<nDimCount; nDim++)
290 uno::Reference<uno::XInterface> xDim =
291 ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) );
292 uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
293 if ( xDimProp.is() )
295 sheet::DataPilotFieldOrientation eDimOrient =
296 (sheet::DataPilotFieldOrientation) ScUnoHelpFunctions::GetEnumProperty(
297 xDimProp, rtl::OUString::createFromAscii(DP_PROP_ORIENTATION),
298 sheet::DataPilotFieldOrientation_HIDDEN );
299 if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
301 long nFormat = ScUnoHelpFunctions::GetLongProperty(
302 xDimProp,
303 rtl::OUString::createFromAscii(DP_PROP_NUMBERFORMAT) );
305 return nFormat; // use format from first found data dimension
310 return 0; // none found
313 void lcl_SortFields( ScDPOutLevelData* pFields, long nFieldCount )
315 for (long i=0; i+1<nFieldCount; i++)
317 for (long j=0; j+i+1<nFieldCount; j++)
318 if ( pFields[j+1] < pFields[j] )
319 pFields[j].Swap( pFields[j+1] );
323 BOOL lcl_MemberEmpty( const uno::Sequence<sheet::MemberResult>& rSeq )
325 // used to skip levels that have no members
327 long nLen = rSeq.getLength();
328 const sheet::MemberResult* pArray = rSeq.getConstArray();
329 for (long i=0; i<nLen; i++)
330 if (pArray[i].Flags & sheet::MemberResultFlags::HASMEMBER)
331 return FALSE;
333 return TRUE; // no member data -> empty
336 uno::Sequence<sheet::MemberResult> lcl_GetSelectedPageAsResult( const uno::Reference<beans::XPropertySet>& xDimProp )
338 uno::Sequence<sheet::MemberResult> aRet;
339 if ( xDimProp.is() )
343 //! merge with ScDPDimension::setPropertyValue?
345 uno::Any aValue = xDimProp->getPropertyValue( rtl::OUString::createFromAscii(DP_PROP_FILTER) );
347 uno::Sequence<sheet::TableFilterField> aSeq;
348 if (aValue >>= aSeq)
350 if ( aSeq.getLength() == 1 )
352 const sheet::TableFilterField& rField = aSeq[0];
353 if ( rField.Field == 0 && rField.Operator == sheet::FilterOperator_EQUAL && !rField.IsNumeric )
355 rtl::OUString aSelectedPage( rField.StringValue );
356 //! different name/caption string?
357 sheet::MemberResult aResult( aSelectedPage, aSelectedPage, 0 );
358 aRet = uno::Sequence<sheet::MemberResult>( &aResult, 1 );
361 // else return empty sequence
364 catch ( uno::Exception& )
366 // recent addition - allow source to not handle it (no error)
369 return aRet;
372 ScDPOutput::ScDPOutput( ScDocument* pD, const uno::Reference<sheet::XDimensionsSupplier>& xSrc,
373 const ScAddress& rPos, BOOL bFilter ) :
374 pDoc( pD ),
375 xSource( xSrc ),
376 aStartPos( rPos ),
377 bDoFilter( bFilter ),
378 bResultsError( FALSE ),
379 mbHasDataLayout(false),
380 pColNumFmt( NULL ),
381 pRowNumFmt( NULL ),
382 nColFmtCount( 0 ),
383 nRowFmtCount( 0 ),
384 nSingleNumFmt( 0 ),
385 bSizesValid( FALSE ),
386 bSizeOverflow( FALSE ),
387 mbHeaderLayout( false )
389 nTabStartCol = nMemberStartCol = nDataStartCol = nTabEndCol = 0;
390 nTabStartRow = nMemberStartRow = nDataStartRow = nTabEndRow = 0;
392 pColFields = new ScDPOutLevelData[SC_DPOUT_MAXLEVELS];
393 pRowFields = new ScDPOutLevelData[SC_DPOUT_MAXLEVELS];
394 pPageFields = new ScDPOutLevelData[SC_DPOUT_MAXLEVELS];
395 nColFieldCount = 0;
396 nRowFieldCount = 0;
397 nPageFieldCount = 0;
399 uno::Reference<sheet::XDataPilotResults> xResult( xSource, uno::UNO_QUERY );
400 if ( xSource.is() && xResult.is() )
402 // get dimension results:
404 uno::Reference<container::XIndexAccess> xDims =
405 new ScNameToIndexAccess( xSource->getDimensions() );
406 long nDimCount = xDims->getCount();
407 for (long nDim=0; nDim<nDimCount; nDim++)
409 uno::Reference<uno::XInterface> xDim =
410 ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) );
411 uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
412 uno::Reference<sheet::XHierarchiesSupplier> xDimSupp( xDim, uno::UNO_QUERY );
413 if ( xDimProp.is() && xDimSupp.is() )
415 sheet::DataPilotFieldOrientation eDimOrient =
416 (sheet::DataPilotFieldOrientation) ScUnoHelpFunctions::GetEnumProperty(
417 xDimProp, rtl::OUString::createFromAscii(DP_PROP_ORIENTATION),
418 sheet::DataPilotFieldOrientation_HIDDEN );
419 long nDimPos = ScUnoHelpFunctions::GetLongProperty( xDimProp,
420 rtl::OUString::createFromAscii(DP_PROP_POSITION) );
421 BOOL bIsDataLayout = ScUnoHelpFunctions::GetBoolProperty(
422 xDimProp,
423 rtl::OUString::createFromAscii(DP_PROP_ISDATALAYOUT) );
424 bool bHasHiddenMember = ScUnoHelpFunctions::GetBoolProperty(
425 xDimProp, OUString::createFromAscii(SC_UNO_HAS_HIDDEN_MEMBER));
427 if ( eDimOrient != sheet::DataPilotFieldOrientation_HIDDEN )
429 uno::Reference<container::XIndexAccess> xHiers =
430 new ScNameToIndexAccess( xDimSupp->getHierarchies() );
431 long nHierarchy = ScUnoHelpFunctions::GetLongProperty(
432 xDimProp,
433 rtl::OUString::createFromAscii(DP_PROP_USEDHIERARCHY) );
434 if ( nHierarchy >= xHiers->getCount() )
435 nHierarchy = 0;
437 uno::Reference<uno::XInterface> xHier =
438 ScUnoHelpFunctions::AnyToInterface(
439 xHiers->getByIndex(nHierarchy) );
440 uno::Reference<sheet::XLevelsSupplier> xHierSupp( xHier, uno::UNO_QUERY );
441 if ( xHierSupp.is() )
443 uno::Reference<container::XIndexAccess> xLevels =
444 new ScNameToIndexAccess( xHierSupp->getLevels() );
445 long nLevCount = xLevels->getCount();
446 for (long nLev=0; nLev<nLevCount; nLev++)
448 uno::Reference<uno::XInterface> xLevel =
449 ScUnoHelpFunctions::AnyToInterface(
450 xLevels->getByIndex(nLev) );
451 uno::Reference<container::XNamed> xLevNam( xLevel, uno::UNO_QUERY );
452 uno::Reference<sheet::XDataPilotMemberResults> xLevRes(
453 xLevel, uno::UNO_QUERY );
454 if ( xLevNam.is() && xLevRes.is() )
456 String aName = xLevNam->getName();
457 OUString aCaption = aName; // Caption equals the field name by default.
458 Reference<XPropertySet> xPropSet(xLevel, UNO_QUERY);
459 if (xPropSet.is())
461 Any any = xPropSet->getPropertyValue(
462 OUString::createFromAscii(SC_UNO_LAYOUTNAME));
463 any >>= aCaption;
466 bool bRowFieldHasMember = false;
467 switch ( eDimOrient )
469 case sheet::DataPilotFieldOrientation_COLUMN:
470 pColFields[nColFieldCount].nDim = nDim;
471 pColFields[nColFieldCount].nHier = nHierarchy;
472 pColFields[nColFieldCount].nLevel = nLev;
473 pColFields[nColFieldCount].nDimPos = nDimPos;
474 pColFields[nColFieldCount].aResult = xLevRes->getResults();
475 pColFields[nColFieldCount].maName = aName;
476 pColFields[nColFieldCount].aCaption= aCaption;
477 pColFields[nColFieldCount].mbHasHiddenMember = bHasHiddenMember;
478 if (!lcl_MemberEmpty(pColFields[nColFieldCount].aResult))
479 ++nColFieldCount;
480 break;
481 case sheet::DataPilotFieldOrientation_ROW:
482 pRowFields[nRowFieldCount].nDim = nDim;
483 pRowFields[nRowFieldCount].nHier = nHierarchy;
484 pRowFields[nRowFieldCount].nLevel = nLev;
485 pRowFields[nRowFieldCount].nDimPos = nDimPos;
486 pRowFields[nRowFieldCount].aResult = xLevRes->getResults();
487 pRowFields[nRowFieldCount].maName = aName;
488 pRowFields[nRowFieldCount].aCaption= aCaption;
489 pRowFields[nRowFieldCount].mbHasHiddenMember = bHasHiddenMember;
490 if (!lcl_MemberEmpty(pRowFields[nRowFieldCount].aResult))
492 ++nRowFieldCount;
493 bRowFieldHasMember = true;
495 break;
496 case sheet::DataPilotFieldOrientation_PAGE:
497 pPageFields[nPageFieldCount].nDim = nDim;
498 pPageFields[nPageFieldCount].nHier = nHierarchy;
499 pPageFields[nPageFieldCount].nLevel = nLev;
500 pPageFields[nPageFieldCount].nDimPos = nDimPos;
501 pPageFields[nPageFieldCount].aResult = lcl_GetSelectedPageAsResult(xDimProp);
502 pPageFields[nPageFieldCount].maName = aName;
503 pPageFields[nPageFieldCount].aCaption= aCaption;
504 pPageFields[nPageFieldCount].mbHasHiddenMember = bHasHiddenMember;
505 // no check on results for page fields
506 ++nPageFieldCount;
507 break;
508 default:
510 // added to avoid warnings
514 // get number formats from data dimensions
515 if ( bIsDataLayout )
517 if (bRowFieldHasMember)
518 mbHasDataLayout = true;
520 DBG_ASSERT( nLevCount == 1, "data layout: multiple levels?" );
521 if ( eDimOrient == sheet::DataPilotFieldOrientation_COLUMN )
522 lcl_FillNumberFormats( pColNumFmt, nColFmtCount, xLevRes, xDims );
523 else if ( eDimOrient == sheet::DataPilotFieldOrientation_ROW )
524 lcl_FillNumberFormats( pRowNumFmt, nRowFmtCount, xLevRes, xDims );
530 else if ( bIsDataLayout )
532 // data layout dimension is hidden (allowed if there is only one data dimension)
533 // -> use the number format from the first data dimension for all results
535 nSingleNumFmt = lcl_GetFirstNumberFormat( xDims );
539 lcl_SortFields( pColFields, nColFieldCount );
540 lcl_SortFields( pRowFields, nRowFieldCount );
541 lcl_SortFields( pPageFields, nPageFieldCount );
543 // get data results:
547 aData = xResult->getResults();
549 catch (uno::RuntimeException&)
551 bResultsError = TRUE;
555 // get "DataDescription" property (may be missing in external sources)
557 uno::Reference<beans::XPropertySet> xSrcProp( xSource, uno::UNO_QUERY );
558 if ( xSrcProp.is() )
562 uno::Any aAny = xSrcProp->getPropertyValue(
563 rtl::OUString::createFromAscii(SC_UNO_DATADESC) );
564 rtl::OUString aUStr;
565 aAny >>= aUStr;
566 aDataDescription = String( aUStr );
568 catch(uno::Exception&)
574 ScDPOutput::~ScDPOutput()
576 delete[] pColFields;
577 delete[] pRowFields;
578 delete[] pPageFields;
580 delete[] pColNumFmt;
581 delete[] pRowNumFmt;
584 void ScDPOutput::SetPosition( const ScAddress& rPos )
586 aStartPos = rPos;
587 bSizesValid = bSizeOverflow = FALSE;
590 void ScDPOutput::DataCell( SCCOL nCol, SCROW nRow, SCTAB nTab, const sheet::DataResult& rData )
592 long nFlags = rData.Flags;
593 if ( nFlags & sheet::DataResultFlags::ERROR )
595 pDoc->SetError( nCol, nRow, nTab, errNoValue );
597 else if ( nFlags & sheet::DataResultFlags::HASDATA )
599 pDoc->SetValue( nCol, nRow, nTab, rData.Value );
601 // use number formats from source
603 DBG_ASSERT( bSizesValid, "DataCell: !bSizesValid" );
604 UINT32 nFormat = 0;
605 if ( pColNumFmt )
607 if ( nCol >= nDataStartCol )
609 long nIndex = nCol - nDataStartCol;
610 if ( nIndex < nColFmtCount )
611 nFormat = pColNumFmt[nIndex];
614 else if ( pRowNumFmt )
616 if ( nRow >= nDataStartRow )
618 long nIndex = nRow - nDataStartRow;
619 if ( nIndex < nRowFmtCount )
620 nFormat = pRowNumFmt[nIndex];
623 else if ( nSingleNumFmt != 0 )
624 nFormat = nSingleNumFmt; // single format is used everywhere
625 if ( nFormat != 0 )
626 pDoc->ApplyAttr( nCol, nRow, nTab, SfxUInt32Item( ATTR_VALUE_FORMAT, nFormat ) );
628 else
630 //pDoc->SetString( nCol, nRow, nTab, EMPTY_STRING );
633 // SubTotal formatting is controlled by headers
636 void ScDPOutput::HeaderCell( SCCOL nCol, SCROW nRow, SCTAB nTab,
637 const sheet::MemberResult& rData, BOOL bColHeader, long nLevel )
639 long nFlags = rData.Flags;
641 rtl::OUStringBuffer aCaptionBuf;
642 if (!(nFlags & sheet::MemberResultFlags::NUMERIC))
643 // This caption is not a number. Make sure it won't get parsed as one.
644 aCaptionBuf.append(sal_Unicode('\''));
645 aCaptionBuf.append(rData.Caption);
647 if ( nFlags & sheet::MemberResultFlags::HASMEMBER )
649 pDoc->SetString( nCol, nRow, nTab, aCaptionBuf.makeStringAndClear() );
651 else
653 //pDoc->SetString( nCol, nRow, nTab, EMPTY_STRING );
656 if ( nFlags & sheet::MemberResultFlags::SUBTOTAL )
658 // SvxWeightItem aItem( WEIGHT_BOLD ); // weight is in the style
660 //! limit frames to horizontal or vertical?
661 if (bColHeader)
663 lcl_SetFrame( pDoc,nTab, nCol,nMemberStartRow+(SCROW)nLevel, nCol,nTabEndRow, 20 );
664 lcl_SetStyleById( pDoc,nTab, nCol,nMemberStartRow+(SCROW)nLevel, nCol,nDataStartRow-1,
665 STR_PIVOT_STYLE_TITLE );
666 lcl_SetStyleById( pDoc,nTab, nCol,nDataStartRow, nCol,nTabEndRow,
667 STR_PIVOT_STYLE_RESULT );
669 else
671 lcl_SetFrame( pDoc,nTab, nMemberStartCol+(SCCOL)nLevel,nRow, nTabEndCol,nRow, 20 );
672 lcl_SetStyleById( pDoc,nTab, nMemberStartCol+(SCCOL)nLevel,nRow, nDataStartCol-1,nRow,
673 STR_PIVOT_STYLE_TITLE );
674 lcl_SetStyleById( pDoc,nTab, nDataStartCol,nRow, nTabEndCol,nRow,
675 STR_PIVOT_STYLE_RESULT );
680 void ScDPOutput::FieldCell( SCCOL nCol, SCROW nRow, SCTAB nTab, const String& rCaption,
681 bool bInTable, bool bPopup, bool bHasHiddenMember )
683 pDoc->SetString( nCol, nRow, nTab, rCaption );
684 if (bInTable)
685 lcl_SetFrame( pDoc,nTab, nCol,nRow, nCol,nRow, 20 );
687 // Button
688 sal_uInt16 nMergeFlag = SC_MF_BUTTON;
689 if (bPopup)
690 nMergeFlag |= SC_MF_BUTTON_POPUP;
691 if (bHasHiddenMember)
692 nMergeFlag |= SC_MF_HIDDEN_MEMBER;
693 pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, nMergeFlag);
695 lcl_SetStyleById( pDoc,nTab, nCol,nRow, nCol,nRow, STR_PIVOT_STYLE_FIELDNAME );
698 void lcl_DoFilterButton( ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
700 pDoc->SetString( nCol, nRow, nTab, ScGlobal::GetRscString(STR_CELL_FILTER) );
701 pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, SC_MF_BUTTON);
704 void ScDPOutput::CalcSizes()
706 if (!bSizesValid)
708 // get column size of data from first row
709 //! allow different sizes (and clear following areas) ???
711 nRowCount = aData.getLength();
712 const uno::Sequence<sheet::DataResult>* pRowAry = aData.getConstArray();
713 nColCount = nRowCount ? ( pRowAry[0].getLength() ) : 0;
715 nHeaderSize = 1;
716 if (GetHeaderLayout() && nColFieldCount == 0)
717 // Insert an extra header row only when there is no column field.
718 nHeaderSize = 2;
720 // calculate output positions and sizes
722 long nPageSize = 0; //! use page fields!
723 if ( bDoFilter || nPageFieldCount )
725 nPageSize += nPageFieldCount + 1; // plus one empty row
726 if ( bDoFilter )
727 ++nPageSize; // filter button above the page fields
730 if ( aStartPos.Col() + nRowFieldCount + nColCount - 1 > MAXCOL ||
731 aStartPos.Row() + nPageSize + nHeaderSize + nColFieldCount + nRowCount > MAXROW )
733 bSizeOverflow = TRUE;
736 nTabStartCol = aStartPos.Col();
737 nTabStartRow = aStartPos.Row() + (SCROW)nPageSize; // below page fields
738 nMemberStartCol = nTabStartCol;
739 nMemberStartRow = nTabStartRow + (SCROW) nHeaderSize;
740 nDataStartCol = nMemberStartCol + (SCCOL)nRowFieldCount;
741 nDataStartRow = nMemberStartRow + (SCROW)nColFieldCount;
742 if ( nColCount > 0 )
743 nTabEndCol = nDataStartCol + (SCCOL)nColCount - 1;
744 else
745 nTabEndCol = nDataStartCol; // single column will remain empty
746 // if page fields are involved, include the page selection cells
747 if ( nPageFieldCount > 0 && nTabEndCol < nTabStartCol + 1 )
748 nTabEndCol = nTabStartCol + 1;
749 if ( nRowCount > 0 )
750 nTabEndRow = nDataStartRow + (SCROW)nRowCount - 1;
751 else
752 nTabEndRow = nDataStartRow; // single row will remain empty
753 bSizesValid = TRUE;
757 sal_Int32 ScDPOutput::GetPositionType(const ScAddress& rPos)
759 using namespace ::com::sun::star::sheet;
761 SCCOL nCol = rPos.Col();
762 SCROW nRow = rPos.Row();
763 SCTAB nTab = rPos.Tab();
764 if ( nTab != aStartPos.Tab() )
765 return DataPilotTablePositionType::NOT_IN_TABLE;
767 CalcSizes();
769 // Make sure the cursor is within the table.
770 if (nCol < nTabStartCol || nRow < nTabStartRow || nCol > nTabEndCol || nRow > nTabEndRow)
771 return DataPilotTablePositionType::NOT_IN_TABLE;
773 // test for result data area.
774 if (nCol >= nDataStartCol && nCol <= nTabEndCol && nRow >= nDataStartRow && nRow <= nTabEndRow)
775 return DataPilotTablePositionType::RESULT;
777 bool bInColHeader = (nRow >= nTabStartRow && nRow < nDataStartRow);
778 bool bInRowHeader = (nCol >= nTabStartCol && nCol < nDataStartCol);
780 if (bInColHeader && bInRowHeader)
781 // probably in that ugly little box at the upper-left corner of the table.
782 return DataPilotTablePositionType::OTHER;
784 if (bInColHeader)
786 if (nRow == nTabStartRow)
787 // first row in the column header area is always used for column
788 // field buttons.
789 return DataPilotTablePositionType::OTHER;
791 return DataPilotTablePositionType::COLUMN_HEADER;
794 if (bInRowHeader)
795 return DataPilotTablePositionType::ROW_HEADER;
797 return DataPilotTablePositionType::OTHER;
800 void ScDPOutput::Output()
802 long nField;
803 SCTAB nTab = aStartPos.Tab();
804 const uno::Sequence<sheet::DataResult>* pRowAry = aData.getConstArray();
806 // calculate output positions and sizes
808 CalcSizes();
809 if ( bSizeOverflow || bResultsError ) // does output area exceed sheet limits?
810 return; // nothing
812 // clear whole (new) output area
813 //! when modifying table, clear old area
814 //! include IDF_OBJECTS ???
815 pDoc->DeleteAreaTab( aStartPos.Col(), aStartPos.Row(), nTabEndCol, nTabEndRow, nTab, IDF_ALL );
817 if ( bDoFilter )
818 lcl_DoFilterButton( pDoc, aStartPos.Col(), aStartPos.Row(), nTab );
820 // output page fields:
822 for (nField=0; nField<nPageFieldCount; nField++)
824 SCCOL nHdrCol = aStartPos.Col();
825 SCROW nHdrRow = aStartPos.Row() + nField + ( bDoFilter ? 1 : 0 );
826 // draw without frame for consistency with filter button:
827 FieldCell( nHdrCol, nHdrRow, nTab, pPageFields[nField].aCaption, false, false, pPageFields[nField].mbHasHiddenMember );
828 SCCOL nFldCol = nHdrCol + 1;
830 String aPageValue;
831 if ( pPageFields[nField].aResult.getLength() == 1 )
832 aPageValue = pPageFields[nField].aResult[0].Caption;
833 else
834 aPageValue = String( ScResId( SCSTR_ALL ) ); //! separate string?
836 pDoc->SetString( nFldCol, nHdrRow, nTab, aPageValue );
838 lcl_SetFrame( pDoc,nTab, nFldCol,nHdrRow, nFldCol,nHdrRow, 20 );
839 pDoc->ApplyAttr( nFldCol, nHdrRow, nTab, ScMergeFlagAttr(SC_MF_AUTO) );
840 //! which style?
843 // data description
844 // (may get overwritten by first row field)
846 String aDesc = aDataDescription;
847 if ( !aDesc.Len() )
849 //! use default string ("result") ?
851 pDoc->SetString( nTabStartCol, nTabStartRow, nTab, aDesc );
853 // set STR_PIVOT_STYLE_INNER for whole data area (subtotals are overwritten)
855 if ( nDataStartRow > nTabStartRow )
856 lcl_SetStyleById( pDoc, nTab, nTabStartCol, nTabStartRow, nTabEndCol, nDataStartRow-1,
857 STR_PIVOT_STYLE_TOP );
858 lcl_SetStyleById( pDoc, nTab, nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow,
859 STR_PIVOT_STYLE_INNER );
861 // output column headers:
863 for (nField=0; nField<nColFieldCount; nField++)
865 SCCOL nHdrCol = nDataStartCol + (SCCOL)nField; //! check for overflow
866 FieldCell( nHdrCol, nTabStartRow, nTab, pColFields[nField].aCaption, true, true, pColFields[nField].mbHasHiddenMember );
868 SCROW nRowPos = nMemberStartRow + (SCROW)nField; //! check for overflow
869 const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nField].aResult;
870 const sheet::MemberResult* pArray = rSequence.getConstArray();
871 long nThisColCount = rSequence.getLength();
872 DBG_ASSERT( nThisColCount == nColCount, "count mismatch" ); //! ???
873 for (long nCol=0; nCol<nThisColCount; nCol++)
875 SCCOL nColPos = nDataStartCol + (SCCOL)nCol; //! check for overflow
876 HeaderCell( nColPos, nRowPos, nTab, pArray[nCol], TRUE, nField );
877 if ( ( pArray[nCol].Flags & sheet::MemberResultFlags::HASMEMBER ) &&
878 !( pArray[nCol].Flags & sheet::MemberResultFlags::SUBTOTAL ) )
880 if ( nField+1 < nColFieldCount )
882 long nEnd = nCol;
883 while ( nEnd+1 < nThisColCount && ( pArray[nEnd+1].Flags & sheet::MemberResultFlags::CONTINUE ) )
884 ++nEnd;
885 SCCOL nEndColPos = nDataStartCol + (SCCOL)nEnd; //! check for overflow
886 lcl_SetFrame( pDoc,nTab, nColPos,nRowPos, nEndColPos,nRowPos, 20 );
887 lcl_SetFrame( pDoc,nTab, nColPos,nRowPos, nEndColPos,nTabEndRow, 20 );
889 lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nEndColPos,nDataStartRow-1, STR_PIVOT_STYLE_CATEGORY );
891 else
892 lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nColPos,nDataStartRow-1, STR_PIVOT_STYLE_CATEGORY );
897 // output row headers:
899 for (nField=0; nField<nRowFieldCount; nField++)
901 bool bDataLayout = mbHasDataLayout && (nField == nRowFieldCount-1);
903 SCCOL nHdrCol = nTabStartCol + (SCCOL)nField; //! check for overflow
904 SCROW nHdrRow = nDataStartRow - 1;
905 FieldCell( nHdrCol, nHdrRow, nTab, pRowFields[nField].aCaption, true, !bDataLayout,
906 pRowFields[nField].mbHasHiddenMember );
908 SCCOL nColPos = nMemberStartCol + (SCCOL)nField; //! check for overflow
909 const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].aResult;
910 const sheet::MemberResult* pArray = rSequence.getConstArray();
911 long nThisRowCount = rSequence.getLength();
912 DBG_ASSERT( nThisRowCount == nRowCount, "count mismatch" ); //! ???
913 for (long nRow=0; nRow<nThisRowCount; nRow++)
915 SCROW nRowPos = nDataStartRow + (SCROW)nRow; //! check for overflow
916 HeaderCell( nColPos, nRowPos, nTab, pArray[nRow], FALSE, nField );
917 if ( ( pArray[nRow].Flags & sheet::MemberResultFlags::HASMEMBER ) &&
918 !( pArray[nRow].Flags & sheet::MemberResultFlags::SUBTOTAL ) )
920 if ( nField+1 < nRowFieldCount )
922 long nEnd = nRow;
923 while ( nEnd+1 < nThisRowCount && ( pArray[nEnd+1].Flags & sheet::MemberResultFlags::CONTINUE ) )
924 ++nEnd;
925 SCROW nEndRowPos = nDataStartRow + (SCROW)nEnd; //! check for overflow
926 lcl_SetFrame( pDoc,nTab, nColPos,nRowPos, nColPos,nEndRowPos, 20 );
927 lcl_SetFrame( pDoc,nTab, nColPos,nRowPos, nTabEndCol,nEndRowPos, 20 );
929 lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nDataStartCol-1,nEndRowPos, STR_PIVOT_STYLE_CATEGORY );
931 else
932 lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nDataStartCol-1,nRowPos, STR_PIVOT_STYLE_CATEGORY );
937 // output data results:
939 for (long nRow=0; nRow<nRowCount; nRow++)
941 SCROW nRowPos = nDataStartRow + (SCROW)nRow; //! check for overflow
942 const sheet::DataResult* pColAry = pRowAry[nRow].getConstArray();
943 long nThisColCount = pRowAry[nRow].getLength();
944 DBG_ASSERT( nThisColCount == nColCount, "count mismatch" ); //! ???
945 for (long nCol=0; nCol<nThisColCount; nCol++)
947 SCCOL nColPos = nDataStartCol + (SCCOL)nCol; //! check for overflow
948 DataCell( nColPos, nRowPos, nTab, pColAry[nCol] );
952 // frame around the whole table
954 lcl_SetFrame( pDoc,nTab, nDataStartCol,nDataStartRow, nTabEndCol,nTabEndRow, 20 );
955 if ( nDataStartCol > nMemberStartCol )
956 lcl_SetFrame( pDoc,nTab, nMemberStartCol,nDataStartRow, nDataStartCol-1,nTabEndRow, 20 );
957 if ( nDataStartRow > nMemberStartRow )
958 lcl_SetFrame( pDoc,nTab, nDataStartCol,nMemberStartRow, nTabEndCol,nDataStartRow-1, 20 );
960 lcl_SetFrame( pDoc,nTab, nTabStartCol,nTabStartRow, nTabEndCol,nTabEndRow, 40 );
963 ScRange ScDPOutput::GetOutputRange( sal_Int32 nRegionType )
965 using namespace ::com::sun::star::sheet;
967 CalcSizes();
969 // fprintf(stdout, "ScDPOutput::GetOutputRange: aStartPos = (%ld, %d)\n", aStartPos.Row(), aStartPos.Col());fflush(stdout);
970 // fprintf(stdout, "ScDPOutput::GetOutputRange: nTabStart (Row = %ld, Col = %ld)\n", nTabStartRow, nTabStartCol);fflush(stdout);
971 // fprintf(stdout, "ScDPOutput::GetOutputRange: nMemberStart (Row = %ld, Col = %ld)\n", nMemberStartRow, nMemberStartCol);fflush(stdout);
972 // fprintf(stdout, "ScDPOutput::GetOutputRange: nDataStart (Row = %ld, Col = %ld)\n", nDataStartRow, nDataStartCol);fflush(stdout);
973 // fprintf(stdout, "ScDPOutput::GetOutputRange: nTabEnd (Row = %ld, Col = %ld)\n", nTabEndRow, nTabStartCol);fflush(stdout);
975 SCTAB nTab = aStartPos.Tab();
976 switch (nRegionType)
978 case DataPilotOutputRangeType::RESULT:
979 return ScRange(nDataStartCol, nDataStartRow, nTab, nTabEndCol, nTabEndRow, nTab);
980 case DataPilotOutputRangeType::TABLE:
981 return ScRange(aStartPos.Col(), nTabStartRow, nTab, nTabEndCol, nTabEndRow, nTab);
982 default:
983 DBG_ASSERT(nRegionType == DataPilotOutputRangeType::WHOLE, "ScDPOutput::GetOutputRange: unknown region type");
984 break;
986 return ScRange(aStartPos.Col(), aStartPos.Row(), nTab, nTabEndCol, nTabEndRow, nTab);
989 BOOL ScDPOutput::HasError()
991 CalcSizes();
993 return bSizeOverflow || bResultsError;
996 long ScDPOutput::GetHeaderRows()
998 return nPageFieldCount + ( bDoFilter ? 1 : 0 );
1001 void ScDPOutput::GetMemberResultNames( ScStrCollection& rNames, long nDimension )
1003 // Return the list of all member names in a dimension's MemberResults.
1004 // Only the dimension has to be compared because this is only used with table data,
1005 // where each dimension occurs only once.
1007 uno::Sequence<sheet::MemberResult> aMemberResults;
1008 bool bFound = false;
1009 long nField;
1011 // look in column fields
1013 for (nField=0; nField<nColFieldCount && !bFound; nField++)
1014 if ( pColFields[nField].nDim == nDimension )
1016 aMemberResults = pColFields[nField].aResult;
1017 bFound = true;
1020 // look in row fields
1022 for (nField=0; nField<nRowFieldCount && !bFound; nField++)
1023 if ( pRowFields[nField].nDim == nDimension )
1025 aMemberResults = pRowFields[nField].aResult;
1026 bFound = true;
1029 // collect the member names
1031 if ( bFound )
1033 const sheet::MemberResult* pArray = aMemberResults.getConstArray();
1034 long nResultCount = aMemberResults.getLength();
1036 for (long nItem=0; nItem<nResultCount; nItem++)
1038 if ( pArray[nItem].Flags & sheet::MemberResultFlags::HASMEMBER )
1040 StrData* pNew = new StrData( pArray[nItem].Name );
1041 if ( !rNames.Insert( pNew ) )
1042 delete pNew;
1048 void ScDPOutput::SetHeaderLayout(bool bUseGrid)
1050 mbHeaderLayout = bUseGrid;
1051 bSizesValid = false;
1054 bool ScDPOutput::GetHeaderLayout() const
1056 return mbHeaderLayout;
1059 void ScDPOutput::GetPositionData(const ScAddress& rPos, DataPilotTablePositionData& rPosData)
1061 using namespace ::com::sun::star::sheet;
1063 SCCOL nCol = rPos.Col();
1064 SCROW nRow = rPos.Row();
1065 SCTAB nTab = rPos.Tab();
1066 if ( nTab != aStartPos.Tab() )
1067 return; // wrong sheet
1069 // calculate output positions and sizes
1071 CalcSizes();
1073 rPosData.PositionType = GetPositionType(rPos);
1074 switch (rPosData.PositionType)
1076 case DataPilotTablePositionType::RESULT:
1078 vector<DataPilotFieldFilter> aFilters;
1079 GetDataResultPositionData(aFilters, rPos);
1080 sal_Int32 nSize = aFilters.size();
1082 DataPilotTableResultData aResData;
1083 aResData.FieldFilters.realloc(nSize);
1084 for (sal_Int32 i = 0; i < nSize; ++i)
1085 aResData.FieldFilters[i] = aFilters[i];
1087 aResData.DataFieldIndex = 0;
1088 Reference<beans::XPropertySet> xPropSet(xSource, UNO_QUERY);
1089 if (xPropSet.is())
1091 sal_Int32 nDataFieldCount = 0;
1092 Any any = xPropSet->getPropertyValue(rtl::OUString::createFromAscii("DataFieldCount"));
1093 if ((any >>= nDataFieldCount) && nDataFieldCount > 0)
1094 aResData.DataFieldIndex = (nRow - nDataStartRow) % nDataFieldCount;
1097 // Copy appropriate DataResult object from the cached sheet::DataResult table.
1098 if (aData.getLength() > nRow - nDataStartRow &&
1099 aData[nRow-nDataStartRow].getLength() > nCol-nDataStartCol)
1100 aResData.Result = aData[nRow-nDataStartRow][nCol-nDataStartCol];
1102 rPosData.PositionData = makeAny(aResData);
1103 return;
1105 case DataPilotTablePositionType::COLUMN_HEADER:
1107 long nField = nRow - nTabStartRow - 1; // 1st line is used for the buttons
1108 if (nField < 0)
1109 break;
1111 const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nField].aResult;
1112 if (rSequence.getLength() == 0)
1113 break;
1114 const sheet::MemberResult* pArray = rSequence.getConstArray();
1116 long nItem = nCol - nDataStartCol;
1117 // get origin of "continue" fields
1118 while (nItem > 0 && ( pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1119 --nItem;
1121 if (nItem < 0)
1122 break;
1124 DataPilotTableHeaderData aHeaderData;
1125 aHeaderData.MemberName = OUString(pArray[nItem].Name);
1126 aHeaderData.Flags = pArray[nItem].Flags;
1127 aHeaderData.Dimension = static_cast<sal_Int32>(pColFields[nField].nDim);
1128 aHeaderData.Hierarchy = static_cast<sal_Int32>(pColFields[nField].nHier);
1129 aHeaderData.Level = static_cast<sal_Int32>(pColFields[nField].nLevel);
1131 rPosData.PositionData = makeAny(aHeaderData);
1132 return;
1134 case DataPilotTablePositionType::ROW_HEADER:
1136 long nField = nCol - nTabStartCol;
1137 if (nField < 0)
1138 break;
1140 const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].aResult;
1141 if (rSequence.getLength() == 0)
1142 break;
1143 const sheet::MemberResult* pArray = rSequence.getConstArray();
1145 long nItem = nRow - nDataStartRow;
1146 // get origin of "continue" fields
1147 while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1148 --nItem;
1150 if (nItem < 0)
1151 break;
1153 DataPilotTableHeaderData aHeaderData;
1154 aHeaderData.MemberName = OUString(pArray[nItem].Name);
1155 aHeaderData.Flags = pArray[nItem].Flags;
1156 aHeaderData.Dimension = static_cast<sal_Int32>(pRowFields[nField].nDim);
1157 aHeaderData.Hierarchy = static_cast<sal_Int32>(pRowFields[nField].nHier);
1158 aHeaderData.Level = static_cast<sal_Int32>(pRowFields[nField].nLevel);
1160 rPosData.PositionData = makeAny(aHeaderData);
1161 return;
1166 bool ScDPOutput::GetDataResultPositionData(vector<sheet::DataPilotFieldFilter>& rFilters, const ScAddress& rPos)
1168 // Check to make sure there is at least one data field.
1169 Reference<beans::XPropertySet> xPropSet(xSource, UNO_QUERY);
1170 if (!xPropSet.is())
1171 return false;
1173 sal_Int32 nDataFieldCount = 0;
1174 Any any = xPropSet->getPropertyValue(rtl::OUString::createFromAscii("DataFieldCount"));
1175 if (!(any >>= nDataFieldCount) || nDataFieldCount == 0)
1176 // No data field is present in this datapilot table.
1177 return false;
1179 bool bColGrand = bool();
1180 any = xPropSet->getPropertyValue(rtl::OUString::createFromAscii(SC_UNO_COLGRAND));
1181 if (!(any >>= bColGrand))
1182 return false;
1184 bool bRowGrand = bool();
1185 any = xPropSet->getPropertyValue(rtl::OUString::createFromAscii(SC_UNO_ROWGRAND));
1186 if (!(any >>= bRowGrand))
1187 return false;
1189 SCCOL nCol = rPos.Col();
1190 SCROW nRow = rPos.Row();
1191 SCTAB nTab = rPos.Tab();
1192 if ( nTab != aStartPos.Tab() )
1193 return false; // wrong sheet
1195 CalcSizes();
1197 // test for data area.
1198 if (nCol < nDataStartCol || nCol > nTabEndCol || nRow < nDataStartRow || nRow > nTabEndRow)
1200 // Cell is outside the data field area.
1201 return false;
1204 bool bFilterByCol = !(bColGrand && (nCol == nTabEndCol));
1205 bool bFilterByRow = !(bRowGrand && (nRow == nTabEndRow));
1207 // column fields
1208 for (SCCOL nColField = 0; nColField < nColFieldCount && bFilterByCol; ++nColField)
1210 sheet::DataPilotFieldFilter filter;
1211 filter.FieldName = pColFields[nColField].maName;
1213 const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nColField].aResult;
1214 const sheet::MemberResult* pArray = rSequence.getConstArray();
1216 DBG_ASSERT(nDataStartCol + rSequence.getLength() - 1 == nTabEndCol, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
1218 long nItem = nCol - nDataStartCol;
1219 // get origin of "continue" fields
1220 while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1221 --nItem;
1223 filter.MatchValue = pArray[nItem].Name;
1224 rFilters.push_back(filter);
1227 // row fields
1228 bool bDataLayoutExists = (nDataFieldCount > 1);
1229 for (SCROW nRowField = 0; nRowField < nRowFieldCount && bFilterByRow; ++nRowField)
1231 if (bDataLayoutExists && nRowField == nRowFieldCount - 1)
1232 // There is no sense including the data layout field for filtering.
1233 continue;
1235 sheet::DataPilotFieldFilter filter;
1236 filter.FieldName = pRowFields[nRowField].maName;
1238 const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nRowField].aResult;
1239 const sheet::MemberResult* pArray = rSequence.getConstArray();
1241 DBG_ASSERT(nDataStartRow + rSequence.getLength() - 1 == nTabEndRow, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
1243 long nItem = nRow - nDataStartRow;
1244 // get origin of "continue" fields
1245 while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1246 --nItem;
1248 filter.MatchValue = pArray[nItem].Name;
1249 rFilters.push_back(filter);
1252 return true;
1256 // helper functions for ScDPOutput::GetPivotData
1259 bool lcl_IsNamedDataField( const ScDPGetPivotDataField& rTarget, const String& rSourceName, const String& rGivenName )
1261 // match one of the names, ignoring case
1262 return ScGlobal::GetpTransliteration()->isEqual( rTarget.maFieldName, rSourceName ) ||
1263 ScGlobal::GetpTransliteration()->isEqual( rTarget.maFieldName, rGivenName );
1266 bool lcl_IsNamedCategoryField( const ScDPGetPivotDataField& rFilter, const ScDPOutLevelData& rField )
1268 //! name from source instead of caption?
1269 return ScGlobal::GetpTransliteration()->isEqual( rFilter.maFieldName, rField.maName );
1272 bool lcl_IsCondition( const sheet::MemberResult& rResultEntry, const ScDPGetPivotDataField& rFilter )
1274 //! handle numeric conditions?
1275 return ScGlobal::GetpTransliteration()->isEqual( rResultEntry.Name, rFilter.maValStr );
1278 bool lcl_CheckPageField( const ScDPOutLevelData& rField,
1279 const std::vector< ScDPGetPivotDataField >& rFilters,
1280 std::vector< BOOL >& rFilterUsed )
1282 for (SCSIZE nFilterPos = 0; nFilterPos < rFilters.size(); ++nFilterPos)
1284 if ( lcl_IsNamedCategoryField( rFilters[nFilterPos], rField ) )
1286 rFilterUsed[nFilterPos] = TRUE;
1288 // page field result is empty or the selection as single entry (see lcl_GetSelectedPageAsResult)
1289 if ( rField.aResult.getLength() == 1 &&
1290 lcl_IsCondition( rField.aResult[0], rFilters[nFilterPos] ) )
1292 return true; // condition matches page selection
1294 else
1296 return false; // no page selection or different entry
1301 return true; // valid if the page field doesn't have a filter
1304 uno::Sequence<sheet::GeneralFunction> lcl_GetSubTotals(
1305 const uno::Reference<sheet::XDimensionsSupplier>& xSource, const ScDPOutLevelData& rField )
1307 uno::Sequence<sheet::GeneralFunction> aSubTotals;
1309 uno::Reference<sheet::XHierarchiesSupplier> xHierSupp;
1310 uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
1311 uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
1312 sal_Int32 nIntCount = xIntDims->getCount();
1313 if ( rField.nDim < nIntCount )
1315 uno::Reference<uno::XInterface> xIntDim = ScUnoHelpFunctions::AnyToInterface(
1316 xIntDims->getByIndex( rField.nDim ) );
1317 xHierSupp = uno::Reference<sheet::XHierarchiesSupplier>( xIntDim, uno::UNO_QUERY );
1319 DBG_ASSERT( xHierSupp.is(), "dimension not found" );
1321 sal_Int32 nHierCount = 0;
1322 uno::Reference<container::XIndexAccess> xHiers;
1323 if ( xHierSupp.is() )
1325 uno::Reference<container::XNameAccess> xHiersName = xHierSupp->getHierarchies();
1326 xHiers = new ScNameToIndexAccess( xHiersName );
1327 nHierCount = xHiers->getCount();
1329 uno::Reference<uno::XInterface> xHier;
1330 if ( rField.nHier < nHierCount )
1331 xHier = ScUnoHelpFunctions::AnyToInterface( xHiers->getByIndex( rField.nHier ) );
1332 DBG_ASSERT( xHier.is(), "hierarchy not found" );
1334 sal_Int32 nLevCount = 0;
1335 uno::Reference<container::XIndexAccess> xLevels;
1336 uno::Reference<sheet::XLevelsSupplier> xLevSupp( xHier, uno::UNO_QUERY );
1337 if ( xLevSupp.is() )
1339 uno::Reference<container::XNameAccess> xLevsName = xLevSupp->getLevels();
1340 xLevels = new ScNameToIndexAccess( xLevsName );
1341 nLevCount = xLevels->getCount();
1343 uno::Reference<uno::XInterface> xLevel;
1344 if ( rField.nLevel < nLevCount )
1345 xLevel = ScUnoHelpFunctions::AnyToInterface( xLevels->getByIndex( rField.nLevel ) );
1346 DBG_ASSERT( xLevel.is(), "level not found" );
1348 uno::Reference<beans::XPropertySet> xLevelProp( xLevel, uno::UNO_QUERY );
1349 if ( xLevelProp.is() )
1353 uno::Any aValue = xLevelProp->getPropertyValue( rtl::OUString::createFromAscii(DP_PROP_SUBTOTALS) );
1354 aValue >>= aSubTotals;
1356 catch(uno::Exception&)
1361 return aSubTotals;
1364 void lcl_FilterInclude( std::vector< BOOL >& rResult, std::vector< sal_Int32 >& rSubtotal,
1365 const ScDPOutLevelData& rField,
1366 const std::vector< ScDPGetPivotDataField >& rFilters,
1367 std::vector< BOOL >& rFilterUsed,
1368 bool& rBeforeDataLayout,
1369 sal_Int32 nGrandTotals, sal_Int32 nDataLayoutIndex,
1370 const std::vector<String>& rDataNames, const std::vector<String>& rGivenNames,
1371 const ScDPGetPivotDataField& rTarget, const uno::Reference<sheet::XDimensionsSupplier>& xSource )
1373 // returns true if a filter was given for the field
1375 DBG_ASSERT( rFilters.size() == rFilterUsed.size(), "wrong size" );
1377 const bool bIsDataLayout = ( rField.nDim == nDataLayoutIndex );
1378 if (bIsDataLayout)
1379 rBeforeDataLayout = false;
1381 bool bHasFilter = false;
1382 ScDPGetPivotDataField aFilter;
1383 if ( !bIsDataLayout ) // selection of data field is handled separately
1385 for (SCSIZE nFilterPos = 0; nFilterPos < rFilters.size() && !bHasFilter; ++nFilterPos)
1387 if ( lcl_IsNamedCategoryField( rFilters[nFilterPos], rField ) )
1389 aFilter = rFilters[nFilterPos];
1390 rFilterUsed[nFilterPos] = TRUE;
1391 bHasFilter = true;
1396 bool bHasFunc = bHasFilter && aFilter.meFunction != sheet::GeneralFunction_NONE;
1398 uno::Sequence<sheet::GeneralFunction> aSubTotals;
1399 if ( !bIsDataLayout )
1400 aSubTotals = lcl_GetSubTotals( xSource, rField );
1401 bool bManualSub = ( aSubTotals.getLength() > 0 && aSubTotals[0] != sheet::GeneralFunction_AUTO );
1403 const uno::Sequence<sheet::MemberResult>& rSequence = rField.aResult;
1404 const sheet::MemberResult* pArray = rSequence.getConstArray();
1405 sal_Int32 nSize = rSequence.getLength();
1407 DBG_ASSERT( (sal_Int32)rResult.size() == nSize, "Number of fields do not match result count" );
1409 sal_Int32 nContCount = 0;
1410 sal_Int32 nSubTotalCount = 0;
1411 sheet::MemberResult aPrevious;
1412 for( sal_Int32 j=0; j < nSize; j++ )
1414 sheet::MemberResult aResultEntry = pArray[j];
1415 if ( aResultEntry.Flags & sheet::MemberResultFlags::CONTINUE )
1417 aResultEntry = aPrevious;
1418 ++nContCount;
1420 else if ( ( aResultEntry.Flags & sheet::MemberResultFlags::SUBTOTAL ) == 0 )
1422 // count the CONTINUE entries before a SUBTOTAL
1423 nContCount = 0;
1426 if ( j >= nSize - nGrandTotals )
1428 // mark as subtotal for the preceding data
1429 if ( ( aResultEntry.Flags & sheet::MemberResultFlags::SUBTOTAL ) != 0 )
1431 rSubtotal[j] = nSize - nGrandTotals;
1433 if ( rResult[j] && nGrandTotals > 1 )
1435 // grand total is always automatic
1436 sal_Int32 nDataPos = j - ( nSize - nGrandTotals );
1437 DBG_ASSERT( nDataPos < (sal_Int32)rDataNames.size(), "wrong data count" );
1438 String aSourceName( rDataNames[nDataPos] ); // vector contains source names
1439 String aGivenName( rGivenNames[nDataPos] );
1441 rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName );
1445 // treat "grand total" columns/rows as empty description, as if they were marked
1446 // in a previous field
1448 DBG_ASSERT( ( aResultEntry.Flags &
1449 ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL ) ) == 0 ||
1450 ( aResultEntry.Flags &
1451 ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL ) ) ==
1452 ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL ),
1453 "non-subtotal member found in grand total result" );
1454 aResultEntry.Flags = 0;
1457 // mark subtotals (not grand total) for preceding data (assume CONTINUE is set)
1458 if ( ( aResultEntry.Flags & sheet::MemberResultFlags::SUBTOTAL ) != 0 )
1460 rSubtotal[j] = nContCount + 1 + nSubTotalCount;
1462 if ( rResult[j] )
1464 if ( bManualSub )
1466 if ( rBeforeDataLayout )
1468 // manual subtotals and several data fields
1470 sal_Int32 nDataCount = rDataNames.size();
1471 sal_Int32 nFuncPos = nSubTotalCount / nDataCount; // outer order: subtotal functions
1472 sal_Int32 nDataPos = nSubTotalCount % nDataCount; // inner order: data fields
1474 String aSourceName( rDataNames[nDataPos] ); // vector contains source names
1475 String aGivenName( rGivenNames[nDataPos] );
1477 DBG_ASSERT( nFuncPos < aSubTotals.getLength(), "wrong subtotal count" );
1478 rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName ) &&
1479 aSubTotals[nFuncPos] == aFilter.meFunction;
1481 else
1483 // manual subtotals for a single data field
1485 DBG_ASSERT( nSubTotalCount < aSubTotals.getLength(), "wrong subtotal count" );
1486 rResult[j] = ( aSubTotals[nSubTotalCount] == aFilter.meFunction );
1489 else // automatic subtotals
1491 if ( rBeforeDataLayout )
1493 DBG_ASSERT( nSubTotalCount < (sal_Int32)rDataNames.size(), "wrong data count" );
1494 String aSourceName( rDataNames[nSubTotalCount] ); // vector contains source names
1495 String aGivenName( rGivenNames[nSubTotalCount] );
1497 rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName );
1500 // if a function was specified, automatic subtotals never match
1501 if ( bHasFunc )
1502 rResult[j] = FALSE;
1506 ++nSubTotalCount;
1508 else
1509 nSubTotalCount = 0;
1511 if( rResult[j] )
1513 if ( bIsDataLayout )
1515 if ( ( aResultEntry.Flags & sheet::MemberResultFlags::HASMEMBER ) != 0 )
1517 // Asterisks are added in ScDPSaveData::WriteToSource to create unique names.
1518 //! preserve original name there?
1519 String aSourceName( aResultEntry.Name );
1520 aSourceName.EraseTrailingChars( '*' );
1522 String aGivenName( aResultEntry.Caption ); //! Should use a stored name when available
1523 aGivenName.EraseLeadingChars( '\'' );
1525 rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName );
1528 else if ( bHasFilter )
1530 // name must match (simple value or subtotal)
1531 rResult[j] = ( ( aResultEntry.Flags & sheet::MemberResultFlags::HASMEMBER ) != 0 ) &&
1532 lcl_IsCondition( aResultEntry, aFilter );
1534 // if a function was specified, simple (non-subtotal) values never match
1535 if ( bHasFunc && nSubTotalCount == 0 )
1536 rResult[j] = FALSE;
1538 // if no condition is given, keep the columns/rows included
1540 aPrevious = aResultEntry;
1544 void lcl_StripSubTotals( std::vector< BOOL >& rResult, const std::vector< sal_Int32 >& rSubtotal )
1546 sal_Int32 nSize = rResult.size();
1547 DBG_ASSERT( (sal_Int32)rSubtotal.size() == nSize, "sizes don't match" );
1549 for (sal_Int32 nPos=0; nPos<nSize; nPos++)
1550 if ( rResult[nPos] && rSubtotal[nPos] )
1552 // if a subtotal is included, clear the result flag for the columns/rows that the subtotal includes
1553 sal_Int32 nStart = nPos - rSubtotal[nPos];
1554 DBG_ASSERT( nStart >= 0, "invalid subtotal count" );
1556 for (sal_Int32 nPrev = nStart; nPrev < nPos; nPrev++)
1557 rResult[nPrev] = FALSE;
1561 String lcl_GetDataFieldName( const String& rSourceName, sheet::GeneralFunction eFunc )
1563 USHORT nStrId = 0;
1564 switch ( eFunc )
1566 case sheet::GeneralFunction_SUM: nStrId = STR_FUN_TEXT_SUM; break;
1567 case sheet::GeneralFunction_COUNT:
1568 case sheet::GeneralFunction_COUNTNUMS: nStrId = STR_FUN_TEXT_COUNT; break;
1569 case sheet::GeneralFunction_AVERAGE: nStrId = STR_FUN_TEXT_AVG; break;
1570 case sheet::GeneralFunction_MAX: nStrId = STR_FUN_TEXT_MAX; break;
1571 case sheet::GeneralFunction_MIN: nStrId = STR_FUN_TEXT_MIN; break;
1572 case sheet::GeneralFunction_PRODUCT: nStrId = STR_FUN_TEXT_PRODUCT; break;
1573 case sheet::GeneralFunction_STDEV:
1574 case sheet::GeneralFunction_STDEVP: nStrId = STR_FUN_TEXT_STDDEV; break;
1575 case sheet::GeneralFunction_VAR:
1576 case sheet::GeneralFunction_VARP: nStrId = STR_FUN_TEXT_VAR; break;
1577 case sheet::GeneralFunction_NONE:
1578 case sheet::GeneralFunction_AUTO:
1579 default:
1581 DBG_ERRORFILE("wrong function");
1584 if ( !nStrId )
1585 return String();
1587 String aRet( ScGlobal::GetRscString( nStrId ) );
1588 aRet.AppendAscii(RTL_CONSTASCII_STRINGPARAM( " - " ));
1589 aRet.Append( rSourceName );
1590 return aRet;
1593 // static
1594 void ScDPOutput::GetDataDimensionNames( String& rSourceName, String& rGivenName,
1595 const uno::Reference<uno::XInterface>& xDim )
1597 uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
1598 uno::Reference<container::XNamed> xDimName( xDim, uno::UNO_QUERY );
1599 if ( xDimProp.is() && xDimName.is() )
1601 // Asterisks are added in ScDPSaveData::WriteToSource to create unique names.
1602 //! preserve original name there?
1603 rSourceName = xDimName->getName();
1604 rSourceName.EraseTrailingChars( '*' );
1606 // Generate "given name" the same way as in dptabres.
1607 //! Should use a stored name when available
1609 sheet::GeneralFunction eFunc = (sheet::GeneralFunction)ScUnoHelpFunctions::GetEnumProperty(
1610 xDimProp, rtl::OUString::createFromAscii(DP_PROP_FUNCTION),
1611 sheet::GeneralFunction_NONE );
1612 rGivenName = lcl_GetDataFieldName( rSourceName, eFunc );
1616 void lcl_GetTableVars( sal_Int32& rGrandTotalCols, sal_Int32& rGrandTotalRows, sal_Int32& rDataLayoutIndex,
1617 std::vector<String>& rDataNames, std::vector<String>& rGivenNames,
1618 sheet::DataPilotFieldOrientation& rDataOrient,
1619 const uno::Reference<sheet::XDimensionsSupplier>& xSource )
1621 rDataLayoutIndex = -1; // invalid
1622 rGrandTotalCols = 0;
1623 rGrandTotalRows = 0;
1624 rDataOrient = sheet::DataPilotFieldOrientation_HIDDEN;
1626 uno::Reference<beans::XPropertySet> xSrcProp( xSource, uno::UNO_QUERY );
1627 BOOL bColGrand = ScUnoHelpFunctions::GetBoolProperty( xSrcProp,
1628 rtl::OUString::createFromAscii(DP_PROP_COLUMNGRAND) );
1629 if ( bColGrand )
1630 rGrandTotalCols = 1; // default if data layout not in columns
1632 BOOL bRowGrand = ScUnoHelpFunctions::GetBoolProperty( xSrcProp,
1633 rtl::OUString::createFromAscii(DP_PROP_ROWGRAND) );
1634 if ( bRowGrand )
1635 rGrandTotalRows = 1; // default if data layout not in rows
1637 if ( xSource.is() )
1639 // find index and orientation of "data layout" dimension, count data dimensions
1641 sal_Int32 nDataCount = 0;
1643 uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xSource->getDimensions() );
1644 long nDimCount = xDims->getCount();
1645 for (long nDim=0; nDim<nDimCount; nDim++)
1647 uno::Reference<uno::XInterface> xDim =
1648 ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) );
1649 uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
1650 if ( xDimProp.is() )
1652 sheet::DataPilotFieldOrientation eDimOrient =
1653 (sheet::DataPilotFieldOrientation) ScUnoHelpFunctions::GetEnumProperty(
1654 xDimProp, rtl::OUString::createFromAscii(DP_PROP_ORIENTATION),
1655 sheet::DataPilotFieldOrientation_HIDDEN );
1656 if ( ScUnoHelpFunctions::GetBoolProperty( xDimProp,
1657 rtl::OUString::createFromAscii(DP_PROP_ISDATALAYOUT) ) )
1659 rDataLayoutIndex = nDim;
1660 rDataOrient = eDimOrient;
1662 if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
1664 String aSourceName;
1665 String aGivenName;
1666 ScDPOutput::GetDataDimensionNames( aSourceName, aGivenName, xDim );
1667 rDataNames.push_back( aSourceName );
1668 rGivenNames.push_back( aGivenName );
1670 ++nDataCount;
1675 if ( ( rDataOrient == sheet::DataPilotFieldOrientation_COLUMN ) && bColGrand )
1676 rGrandTotalCols = nDataCount;
1677 else if ( ( rDataOrient == sheet::DataPilotFieldOrientation_ROW ) && bRowGrand )
1678 rGrandTotalRows = nDataCount;
1682 // Returns TRUE on success and stores the result in rTarget
1683 // Returns FALSE if rFilters or rTarget describes something that is not visible
1684 BOOL ScDPOutput::GetPivotData( ScDPGetPivotDataField& rTarget,
1685 const std::vector< ScDPGetPivotDataField >& rFilters )
1687 CalcSizes();
1689 // need to know about grand total columns/rows:
1690 sal_Int32 nGrandTotalCols;
1691 sal_Int32 nGrandTotalRows;
1692 sal_Int32 nDataLayoutIndex;
1693 std::vector<String> aDataNames;
1694 std::vector<String> aGivenNames;
1695 sheet::DataPilotFieldOrientation eDataOrient;
1696 lcl_GetTableVars( nGrandTotalCols, nGrandTotalRows, nDataLayoutIndex, aDataNames, aGivenNames, eDataOrient, xSource );
1698 if ( aDataNames.empty() )
1699 return FALSE; // incomplete table without data fields -> no result
1701 if ( eDataOrient == sheet::DataPilotFieldOrientation_HIDDEN )
1703 // no data layout field -> single data field -> must match the selected field in rTarget
1705 DBG_ASSERT( aDataNames.size() == 1, "several data fields but no data layout field" );
1706 if ( !lcl_IsNamedDataField( rTarget, aDataNames[0], aGivenNames[0] ) )
1707 return FALSE;
1710 std::vector< BOOL > aIncludeCol( nColCount, TRUE );
1711 std::vector< sal_Int32 > aSubtotalCol( nColCount, 0 );
1712 std::vector< BOOL > aIncludeRow( nRowCount, TRUE );
1713 std::vector< sal_Int32 > aSubtotalRow( nRowCount, 0 );
1715 std::vector< BOOL > aFilterUsed( rFilters.size(), FALSE );
1717 long nField;
1718 long nCol;
1719 long nRow;
1720 bool bBeforeDataLayout;
1722 // look in column fields
1724 bBeforeDataLayout = ( eDataOrient == sheet::DataPilotFieldOrientation_COLUMN );
1725 for (nField=0; nField<nColFieldCount; nField++)
1726 lcl_FilterInclude( aIncludeCol, aSubtotalCol, pColFields[nField], rFilters, aFilterUsed, bBeforeDataLayout,
1727 nGrandTotalCols, nDataLayoutIndex, aDataNames, aGivenNames, rTarget, xSource );
1729 // look in row fields
1731 bBeforeDataLayout = ( eDataOrient == sheet::DataPilotFieldOrientation_ROW );
1732 for (nField=0; nField<nRowFieldCount; nField++)
1733 lcl_FilterInclude( aIncludeRow, aSubtotalRow, pRowFields[nField], rFilters, aFilterUsed, bBeforeDataLayout,
1734 nGrandTotalRows, nDataLayoutIndex, aDataNames, aGivenNames, rTarget, xSource );
1736 // page fields
1738 for (nField=0; nField<nPageFieldCount; nField++)
1739 if ( !lcl_CheckPageField( pPageFields[nField], rFilters, aFilterUsed ) )
1740 return FALSE;
1742 // all filter fields must be used
1743 for (SCSIZE nFilter=0; nFilter<aFilterUsed.size(); nFilter++)
1744 if (!aFilterUsed[nFilter])
1745 return FALSE;
1747 lcl_StripSubTotals( aIncludeCol, aSubtotalCol );
1748 lcl_StripSubTotals( aIncludeRow, aSubtotalRow );
1750 long nColPos = 0;
1751 long nColIncluded = 0;
1752 for (nCol=0; nCol<nColCount; nCol++)
1753 if (aIncludeCol[nCol])
1755 nColPos = nCol;
1756 ++nColIncluded;
1759 long nRowPos = 0;
1760 long nRowIncluded = 0;
1761 for (nRow=0; nRow<nRowCount; nRow++)
1762 if (aIncludeRow[nRow])
1764 nRowPos = nRow;
1765 ++nRowIncluded;
1768 if ( nColIncluded != 1 || nRowIncluded != 1 )
1769 return FALSE;
1771 const uno::Sequence<sheet::DataResult>& rDataRow = aData[nRowPos];
1772 if ( nColPos >= rDataRow.getLength() )
1773 return FALSE;
1775 const sheet::DataResult& rResult = rDataRow[nColPos];
1776 if ( rResult.Flags & sheet::DataResultFlags::ERROR )
1777 return FALSE; //! different error?
1779 rTarget.mbValIsStr = FALSE;
1780 rTarget.mnValNum = rResult.Value;
1782 return TRUE;
1785 BOOL ScDPOutput::IsFilterButton( const ScAddress& rPos )
1787 SCCOL nCol = rPos.Col();
1788 SCROW nRow = rPos.Row();
1789 SCTAB nTab = rPos.Tab();
1790 if ( nTab != aStartPos.Tab() || !bDoFilter )
1791 return FALSE; // wrong sheet or no button at all
1793 // filter button is at top left
1794 return ( nCol == aStartPos.Col() && nRow == aStartPos.Row() );
1797 long ScDPOutput::GetHeaderDim( const ScAddress& rPos, USHORT& rOrient )
1799 SCCOL nCol = rPos.Col();
1800 SCROW nRow = rPos.Row();
1801 SCTAB nTab = rPos.Tab();
1802 if ( nTab != aStartPos.Tab() )
1803 return -1; // wrong sheet
1805 // calculate output positions and sizes
1807 CalcSizes();
1809 // test for column header
1811 if ( nRow == nTabStartRow && nCol >= nDataStartCol && nCol < nDataStartCol + nColFieldCount )
1813 rOrient = sheet::DataPilotFieldOrientation_COLUMN;
1814 long nField = nCol - nDataStartCol;
1815 return pColFields[nField].nDim;
1818 // test for row header
1820 if ( nRow+1 == nDataStartRow && nCol >= nTabStartCol && nCol < nTabStartCol + nRowFieldCount )
1822 rOrient = sheet::DataPilotFieldOrientation_ROW;
1823 long nField = nCol - nTabStartCol;
1824 return pRowFields[nField].nDim;
1827 // test for page field
1829 SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 );
1830 if ( nCol == aStartPos.Col() && nRow >= nPageStartRow && nRow < nPageStartRow + nPageFieldCount )
1832 rOrient = sheet::DataPilotFieldOrientation_PAGE;
1833 long nField = nRow - nPageStartRow;
1834 return pPageFields[nField].nDim;
1837 //! single data field (?)
1839 rOrient = sheet::DataPilotFieldOrientation_HIDDEN;
1840 return -1; // invalid
1843 BOOL ScDPOutput::GetHeaderDrag( const ScAddress& rPos, BOOL bMouseLeft, BOOL bMouseTop,
1844 long nDragDim,
1845 Rectangle& rPosRect, USHORT& rOrient, long& rDimPos )
1847 // Rectangle instead of ScRange for rPosRect to allow for negative values
1849 SCCOL nCol = rPos.Col();
1850 SCROW nRow = rPos.Row();
1851 SCTAB nTab = rPos.Tab();
1852 if ( nTab != aStartPos.Tab() )
1853 return FALSE; // wrong sheet
1855 // calculate output positions and sizes
1857 CalcSizes();
1859 // test for column header
1861 if ( nCol >= nDataStartCol && nCol <= nTabEndCol &&
1862 nRow + 1 >= nMemberStartRow && nRow < nMemberStartRow + nColFieldCount )
1864 long nField = nRow - nMemberStartRow;
1865 if (nField < 0)
1867 nField = 0;
1868 bMouseTop = TRUE;
1870 //! find start of dimension
1872 rPosRect = Rectangle( nDataStartCol, nMemberStartRow + nField,
1873 nTabEndCol, nMemberStartRow + nField -1 );
1875 BOOL bFound = FALSE; // is this within the same orientation?
1876 BOOL bBeforeDrag = FALSE;
1877 BOOL bAfterDrag = FALSE;
1878 for (long nPos=0; nPos<nColFieldCount && !bFound; nPos++)
1880 if (pColFields[nPos].nDim == nDragDim)
1882 bFound = TRUE;
1883 if ( nField < nPos )
1884 bBeforeDrag = TRUE;
1885 else if ( nField > nPos )
1886 bAfterDrag = TRUE;
1890 if ( bFound )
1892 if (!bBeforeDrag)
1894 ++rPosRect.Bottom();
1895 if (bAfterDrag)
1896 ++rPosRect.Top();
1899 else
1901 if ( !bMouseTop )
1903 ++rPosRect.Top();
1904 ++rPosRect.Bottom();
1905 ++nField;
1909 rOrient = sheet::DataPilotFieldOrientation_COLUMN;
1910 rDimPos = nField; //!...
1911 return TRUE;
1914 // test for row header
1916 // special case if no row fields
1917 BOOL bSpecial = ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow &&
1918 nRowFieldCount == 0 && nCol == nTabStartCol && bMouseLeft );
1920 if ( bSpecial || ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow &&
1921 nCol + 1 >= nTabStartCol && nCol < nTabStartCol + nRowFieldCount ) )
1923 long nField = nCol - nTabStartCol;
1924 //! find start of dimension
1926 rPosRect = Rectangle( nTabStartCol + nField, nDataStartRow - 1,
1927 nTabStartCol + nField - 1, nTabEndRow );
1929 BOOL bFound = FALSE; // is this within the same orientation?
1930 BOOL bBeforeDrag = FALSE;
1931 BOOL bAfterDrag = FALSE;
1932 for (long nPos=0; nPos<nRowFieldCount && !bFound; nPos++)
1934 if (pRowFields[nPos].nDim == nDragDim)
1936 bFound = TRUE;
1937 if ( nField < nPos )
1938 bBeforeDrag = TRUE;
1939 else if ( nField > nPos )
1940 bAfterDrag = TRUE;
1944 if ( bFound )
1946 if (!bBeforeDrag)
1948 ++rPosRect.Right();
1949 if (bAfterDrag)
1950 ++rPosRect.Left();
1953 else
1955 if ( !bMouseLeft )
1957 ++rPosRect.Left();
1958 ++rPosRect.Right();
1959 ++nField;
1963 rOrient = sheet::DataPilotFieldOrientation_ROW;
1964 rDimPos = nField; //!...
1965 return TRUE;
1968 // test for page fields
1970 SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 );
1971 if ( nCol >= aStartPos.Col() && nCol <= nTabEndCol &&
1972 nRow + 1 >= nPageStartRow && nRow < nPageStartRow + nPageFieldCount )
1974 long nField = nRow - nPageStartRow;
1975 if (nField < 0)
1977 nField = 0;
1978 bMouseTop = TRUE;
1980 //! find start of dimension
1982 rPosRect = Rectangle( aStartPos.Col(), nPageStartRow + nField,
1983 nTabEndCol, nPageStartRow + nField - 1 );
1985 BOOL bFound = FALSE; // is this within the same orientation?
1986 BOOL bBeforeDrag = FALSE;
1987 BOOL bAfterDrag = FALSE;
1988 for (long nPos=0; nPos<nPageFieldCount && !bFound; nPos++)
1990 if (pPageFields[nPos].nDim == nDragDim)
1992 bFound = TRUE;
1993 if ( nField < nPos )
1994 bBeforeDrag = TRUE;
1995 else if ( nField > nPos )
1996 bAfterDrag = TRUE;
2000 if ( bFound )
2002 if (!bBeforeDrag)
2004 ++rPosRect.Bottom();
2005 if (bAfterDrag)
2006 ++rPosRect.Top();
2009 else
2011 if ( !bMouseTop )
2013 ++rPosRect.Top();
2014 ++rPosRect.Bottom();
2015 ++nField;
2019 rOrient = sheet::DataPilotFieldOrientation_PAGE;
2020 rDimPos = nField; //!...
2021 return TRUE;
2024 return FALSE;