update dev300-m57
[ooovba.git] / sc / source / core / data / dpoutput.cxx
bloba4751316275e7be2ad05e9c3e5d89cb035d15fd0
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 bHeaderLayout( 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->ApplyAttr( nCol, nRow, nTab, ScMergeFlagAttr(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->ApplyAttr( nCol, nRow, nTab, ScMergeFlagAttr(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;
1049 void ScDPOutput::GetPositionData(const ScAddress& rPos, DataPilotTablePositionData& rPosData)
1051 using namespace ::com::sun::star::sheet;
1053 SCCOL nCol = rPos.Col();
1054 SCROW nRow = rPos.Row();
1055 SCTAB nTab = rPos.Tab();
1056 if ( nTab != aStartPos.Tab() )
1057 return; // wrong sheet
1059 // calculate output positions and sizes
1061 CalcSizes();
1063 rPosData.PositionType = GetPositionType(rPos);
1064 switch (rPosData.PositionType)
1066 case DataPilotTablePositionType::RESULT:
1068 vector<DataPilotFieldFilter> aFilters;
1069 GetDataResultPositionData(aFilters, rPos);
1070 sal_Int32 nSize = aFilters.size();
1072 DataPilotTableResultData aResData;
1073 aResData.FieldFilters.realloc(nSize);
1074 for (sal_Int32 i = 0; i < nSize; ++i)
1075 aResData.FieldFilters[i] = aFilters[i];
1077 aResData.DataFieldIndex = 0;
1078 Reference<beans::XPropertySet> xPropSet(xSource, UNO_QUERY);
1079 if (xPropSet.is())
1081 sal_Int32 nDataFieldCount = 0;
1082 Any any = xPropSet->getPropertyValue(rtl::OUString::createFromAscii("DataFieldCount"));
1083 if ((any >>= nDataFieldCount) && nDataFieldCount > 0)
1084 aResData.DataFieldIndex = (nRow - nDataStartRow) % nDataFieldCount;
1087 // Copy appropriate DataResult object from the cached sheet::DataResult table.
1088 if (aData.getLength() > nRow - nDataStartRow &&
1089 aData[nRow-nDataStartRow].getLength() > nCol-nDataStartCol)
1090 aResData.Result = aData[nRow-nDataStartRow][nCol-nDataStartCol];
1092 rPosData.PositionData = makeAny(aResData);
1093 return;
1095 case DataPilotTablePositionType::COLUMN_HEADER:
1097 long nField = nRow - nTabStartRow - 1; // 1st line is used for the buttons
1098 if (nField < 0)
1099 break;
1101 const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nField].aResult;
1102 if (rSequence.getLength() == 0)
1103 break;
1104 const sheet::MemberResult* pArray = rSequence.getConstArray();
1106 long nItem = nCol - nDataStartCol;
1107 // get origin of "continue" fields
1108 while (nItem > 0 && ( pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1109 --nItem;
1111 if (nItem < 0)
1112 break;
1114 DataPilotTableHeaderData aHeaderData;
1115 aHeaderData.MemberName = OUString(pArray[nItem].Name);
1116 aHeaderData.Flags = pArray[nItem].Flags;
1117 aHeaderData.Dimension = static_cast<sal_Int32>(pColFields[nField].nDim);
1118 aHeaderData.Hierarchy = static_cast<sal_Int32>(pColFields[nField].nHier);
1119 aHeaderData.Level = static_cast<sal_Int32>(pColFields[nField].nLevel);
1121 rPosData.PositionData = makeAny(aHeaderData);
1122 return;
1124 case DataPilotTablePositionType::ROW_HEADER:
1126 long nField = nCol - nTabStartCol;
1127 if (nField < 0)
1128 break;
1130 const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].aResult;
1131 if (rSequence.getLength() == 0)
1132 break;
1133 const sheet::MemberResult* pArray = rSequence.getConstArray();
1135 long nItem = nRow - nDataStartRow;
1136 // get origin of "continue" fields
1137 while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1138 --nItem;
1140 if (nItem < 0)
1141 break;
1143 DataPilotTableHeaderData aHeaderData;
1144 aHeaderData.MemberName = OUString(pArray[nItem].Name);
1145 aHeaderData.Flags = pArray[nItem].Flags;
1146 aHeaderData.Dimension = static_cast<sal_Int32>(pRowFields[nField].nDim);
1147 aHeaderData.Hierarchy = static_cast<sal_Int32>(pRowFields[nField].nHier);
1148 aHeaderData.Level = static_cast<sal_Int32>(pRowFields[nField].nLevel);
1150 rPosData.PositionData = makeAny(aHeaderData);
1151 return;
1156 bool ScDPOutput::GetDataResultPositionData(vector<sheet::DataPilotFieldFilter>& rFilters, const ScAddress& rPos)
1158 // Check to make sure there is at least one data field.
1159 Reference<beans::XPropertySet> xPropSet(xSource, UNO_QUERY);
1160 if (!xPropSet.is())
1161 return false;
1163 sal_Int32 nDataFieldCount = 0;
1164 Any any = xPropSet->getPropertyValue(rtl::OUString::createFromAscii("DataFieldCount"));
1165 if (!(any >>= nDataFieldCount) || nDataFieldCount == 0)
1166 // No data field is present in this datapilot table.
1167 return false;
1169 bool bColGrand = bool();
1170 any = xPropSet->getPropertyValue(rtl::OUString::createFromAscii(SC_UNO_COLGRAND));
1171 if (!(any >>= bColGrand))
1172 return false;
1174 bool bRowGrand = bool();
1175 any = xPropSet->getPropertyValue(rtl::OUString::createFromAscii(SC_UNO_ROWGRAND));
1176 if (!(any >>= bRowGrand))
1177 return false;
1179 SCCOL nCol = rPos.Col();
1180 SCROW nRow = rPos.Row();
1181 SCTAB nTab = rPos.Tab();
1182 if ( nTab != aStartPos.Tab() )
1183 return false; // wrong sheet
1185 CalcSizes();
1187 // test for data area.
1188 if (nCol < nDataStartCol || nCol > nTabEndCol || nRow < nDataStartRow || nRow > nTabEndRow)
1190 // Cell is outside the data field area.
1191 return false;
1194 bool bFilterByCol = !(bColGrand && (nCol == nTabEndCol));
1195 bool bFilterByRow = !(bRowGrand && (nRow == nTabEndRow));
1197 // column fields
1198 for (SCCOL nColField = 0; nColField < nColFieldCount && bFilterByCol; ++nColField)
1200 sheet::DataPilotFieldFilter filter;
1201 filter.FieldName = pColFields[nColField].maName;
1203 const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nColField].aResult;
1204 const sheet::MemberResult* pArray = rSequence.getConstArray();
1206 DBG_ASSERT(nDataStartCol + rSequence.getLength() - 1 == nTabEndCol, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
1208 long nItem = nCol - nDataStartCol;
1209 // get origin of "continue" fields
1210 while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1211 --nItem;
1213 filter.MatchValue = pArray[nItem].Name;
1214 rFilters.push_back(filter);
1217 // row fields
1218 bool bDataLayoutExists = (nDataFieldCount > 1);
1219 for (SCROW nRowField = 0; nRowField < nRowFieldCount && bFilterByRow; ++nRowField)
1221 if (bDataLayoutExists && nRowField == nRowFieldCount - 1)
1222 // There is no sense including the data layout field for filtering.
1223 continue;
1225 sheet::DataPilotFieldFilter filter;
1226 filter.FieldName = pRowFields[nRowField].maName;
1228 const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nRowField].aResult;
1229 const sheet::MemberResult* pArray = rSequence.getConstArray();
1231 DBG_ASSERT(nDataStartRow + rSequence.getLength() - 1 == nTabEndRow, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
1233 long nItem = nRow - nDataStartRow;
1234 // get origin of "continue" fields
1235 while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1236 --nItem;
1238 filter.MatchValue = pArray[nItem].Name;
1239 rFilters.push_back(filter);
1242 return true;
1245 void ScDPOutput::SetHeaderLayout (bool bLayout)
1247 bHeaderLayout = bLayout;
1248 bSizesValid = FALSE;
1252 // helper functions for ScDPOutput::GetPivotData
1255 bool lcl_IsNamedDataField( const ScDPGetPivotDataField& rTarget, const String& rSourceName, const String& rGivenName )
1257 // match one of the names, ignoring case
1258 return ScGlobal::pTransliteration->isEqual( rTarget.maFieldName, rSourceName ) ||
1259 ScGlobal::pTransliteration->isEqual( rTarget.maFieldName, rGivenName );
1262 bool lcl_IsNamedCategoryField( const ScDPGetPivotDataField& rFilter, const ScDPOutLevelData& rField )
1264 return ScGlobal::pTransliteration->isEqual( rFilter.maFieldName, rField.maName );
1267 bool lcl_IsCondition( const sheet::MemberResult& rResultEntry, const ScDPGetPivotDataField& rFilter )
1269 //! handle numeric conditions?
1270 return ScGlobal::pTransliteration->isEqual( rResultEntry.Name, rFilter.maValStr );
1273 bool lcl_CheckPageField( const ScDPOutLevelData& rField,
1274 const std::vector< ScDPGetPivotDataField >& rFilters,
1275 std::vector< BOOL >& rFilterUsed )
1277 for (SCSIZE nFilterPos = 0; nFilterPos < rFilters.size(); ++nFilterPos)
1279 if ( lcl_IsNamedCategoryField( rFilters[nFilterPos], rField ) )
1281 rFilterUsed[nFilterPos] = TRUE;
1283 // page field result is empty or the selection as single entry (see lcl_GetSelectedPageAsResult)
1284 if ( rField.aResult.getLength() == 1 &&
1285 lcl_IsCondition( rField.aResult[0], rFilters[nFilterPos] ) )
1287 return true; // condition matches page selection
1289 else
1291 return false; // no page selection or different entry
1296 return true; // valid if the page field doesn't have a filter
1299 uno::Sequence<sheet::GeneralFunction> lcl_GetSubTotals(
1300 const uno::Reference<sheet::XDimensionsSupplier>& xSource, const ScDPOutLevelData& rField )
1302 uno::Sequence<sheet::GeneralFunction> aSubTotals;
1304 uno::Reference<sheet::XHierarchiesSupplier> xHierSupp;
1305 uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
1306 uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
1307 sal_Int32 nIntCount = xIntDims->getCount();
1308 if ( rField.nDim < nIntCount )
1310 uno::Reference<uno::XInterface> xIntDim = ScUnoHelpFunctions::AnyToInterface(
1311 xIntDims->getByIndex( rField.nDim ) );
1312 xHierSupp = uno::Reference<sheet::XHierarchiesSupplier>( xIntDim, uno::UNO_QUERY );
1314 DBG_ASSERT( xHierSupp.is(), "dimension not found" );
1316 sal_Int32 nHierCount = 0;
1317 uno::Reference<container::XIndexAccess> xHiers;
1318 if ( xHierSupp.is() )
1320 uno::Reference<container::XNameAccess> xHiersName = xHierSupp->getHierarchies();
1321 xHiers = new ScNameToIndexAccess( xHiersName );
1322 nHierCount = xHiers->getCount();
1324 uno::Reference<uno::XInterface> xHier;
1325 if ( rField.nHier < nHierCount )
1326 xHier = ScUnoHelpFunctions::AnyToInterface( xHiers->getByIndex( rField.nHier ) );
1327 DBG_ASSERT( xHier.is(), "hierarchy not found" );
1329 sal_Int32 nLevCount = 0;
1330 uno::Reference<container::XIndexAccess> xLevels;
1331 uno::Reference<sheet::XLevelsSupplier> xLevSupp( xHier, uno::UNO_QUERY );
1332 if ( xLevSupp.is() )
1334 uno::Reference<container::XNameAccess> xLevsName = xLevSupp->getLevels();
1335 xLevels = new ScNameToIndexAccess( xLevsName );
1336 nLevCount = xLevels->getCount();
1338 uno::Reference<uno::XInterface> xLevel;
1339 if ( rField.nLevel < nLevCount )
1340 xLevel = ScUnoHelpFunctions::AnyToInterface( xLevels->getByIndex( rField.nLevel ) );
1341 DBG_ASSERT( xLevel.is(), "level not found" );
1343 uno::Reference<beans::XPropertySet> xLevelProp( xLevel, uno::UNO_QUERY );
1344 if ( xLevelProp.is() )
1348 uno::Any aValue = xLevelProp->getPropertyValue( rtl::OUString::createFromAscii(DP_PROP_SUBTOTALS) );
1349 aValue >>= aSubTotals;
1351 catch(uno::Exception&)
1356 return aSubTotals;
1359 void lcl_FilterInclude( std::vector< BOOL >& rResult, std::vector< sal_Int32 >& rSubtotal,
1360 const ScDPOutLevelData& rField,
1361 const std::vector< ScDPGetPivotDataField >& rFilters,
1362 std::vector< BOOL >& rFilterUsed,
1363 bool& rBeforeDataLayout,
1364 sal_Int32 nGrandTotals, sal_Int32 nDataLayoutIndex,
1365 const std::vector<String>& rDataNames, const std::vector<String>& rGivenNames,
1366 const ScDPGetPivotDataField& rTarget, const uno::Reference<sheet::XDimensionsSupplier>& xSource )
1368 // returns true if a filter was given for the field
1370 DBG_ASSERT( rFilters.size() == rFilterUsed.size(), "wrong size" );
1372 const bool bIsDataLayout = ( rField.nDim == nDataLayoutIndex );
1373 if (bIsDataLayout)
1374 rBeforeDataLayout = false;
1376 bool bHasFilter = false;
1377 ScDPGetPivotDataField aFilter;
1378 if ( !bIsDataLayout ) // selection of data field is handled separately
1380 for (SCSIZE nFilterPos = 0; nFilterPos < rFilters.size() && !bHasFilter; ++nFilterPos)
1382 if ( lcl_IsNamedCategoryField( rFilters[nFilterPos], rField ) )
1384 aFilter = rFilters[nFilterPos];
1385 rFilterUsed[nFilterPos] = TRUE;
1386 bHasFilter = true;
1391 bool bHasFunc = bHasFilter && aFilter.meFunction != sheet::GeneralFunction_NONE;
1393 uno::Sequence<sheet::GeneralFunction> aSubTotals;
1394 if ( !bIsDataLayout )
1395 aSubTotals = lcl_GetSubTotals( xSource, rField );
1396 bool bManualSub = ( aSubTotals.getLength() > 0 && aSubTotals[0] != sheet::GeneralFunction_AUTO );
1398 const uno::Sequence<sheet::MemberResult>& rSequence = rField.aResult;
1399 const sheet::MemberResult* pArray = rSequence.getConstArray();
1400 sal_Int32 nSize = rSequence.getLength();
1402 DBG_ASSERT( (sal_Int32)rResult.size() == nSize, "Number of fields do not match result count" );
1404 sal_Int32 nContCount = 0;
1405 sal_Int32 nSubTotalCount = 0;
1406 sheet::MemberResult aPrevious;
1407 for( sal_Int32 j=0; j < nSize; j++ )
1409 sheet::MemberResult aResultEntry = pArray[j];
1410 if ( aResultEntry.Flags & sheet::MemberResultFlags::CONTINUE )
1412 aResultEntry = aPrevious;
1413 ++nContCount;
1415 else if ( ( aResultEntry.Flags & sheet::MemberResultFlags::SUBTOTAL ) == 0 )
1417 // count the CONTINUE entries before a SUBTOTAL
1418 nContCount = 0;
1421 if ( j >= nSize - nGrandTotals )
1423 // mark as subtotal for the preceding data
1424 if ( ( aResultEntry.Flags & sheet::MemberResultFlags::SUBTOTAL ) != 0 )
1426 rSubtotal[j] = nSize - nGrandTotals;
1428 if ( rResult[j] && nGrandTotals > 1 )
1430 // grand total is always automatic
1431 sal_Int32 nDataPos = j - ( nSize - nGrandTotals );
1432 DBG_ASSERT( nDataPos < (sal_Int32)rDataNames.size(), "wrong data count" );
1433 String aSourceName( rDataNames[nDataPos] ); // vector contains source names
1434 String aGivenName( rGivenNames[nDataPos] );
1436 rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName );
1440 // treat "grand total" columns/rows as empty description, as if they were marked
1441 // in a previous field
1443 DBG_ASSERT( ( aResultEntry.Flags &
1444 ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL ) ) == 0 ||
1445 ( aResultEntry.Flags &
1446 ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL ) ) ==
1447 ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL ),
1448 "non-subtotal member found in grand total result" );
1449 aResultEntry.Flags = 0;
1452 // mark subtotals (not grand total) for preceding data (assume CONTINUE is set)
1453 if ( ( aResultEntry.Flags & sheet::MemberResultFlags::SUBTOTAL ) != 0 )
1455 rSubtotal[j] = nContCount + 1 + nSubTotalCount;
1457 if ( rResult[j] )
1459 if ( bManualSub )
1461 if ( rBeforeDataLayout )
1463 // manual subtotals and several data fields
1465 sal_Int32 nDataCount = rDataNames.size();
1466 sal_Int32 nFuncPos = nSubTotalCount / nDataCount; // outer order: subtotal functions
1467 sal_Int32 nDataPos = nSubTotalCount % nDataCount; // inner order: data fields
1469 String aSourceName( rDataNames[nDataPos] ); // vector contains source names
1470 String aGivenName( rGivenNames[nDataPos] );
1472 DBG_ASSERT( nFuncPos < aSubTotals.getLength(), "wrong subtotal count" );
1473 rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName ) &&
1474 aSubTotals[nFuncPos] == aFilter.meFunction;
1476 else
1478 // manual subtotals for a single data field
1480 DBG_ASSERT( nSubTotalCount < aSubTotals.getLength(), "wrong subtotal count" );
1481 rResult[j] = ( aSubTotals[nSubTotalCount] == aFilter.meFunction );
1484 else // automatic subtotals
1486 if ( rBeforeDataLayout )
1488 DBG_ASSERT( nSubTotalCount < (sal_Int32)rDataNames.size(), "wrong data count" );
1489 String aSourceName( rDataNames[nSubTotalCount] ); // vector contains source names
1490 String aGivenName( rGivenNames[nSubTotalCount] );
1492 rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName );
1495 // if a function was specified, automatic subtotals never match
1496 if ( bHasFunc )
1497 rResult[j] = FALSE;
1501 ++nSubTotalCount;
1503 else
1504 nSubTotalCount = 0;
1506 if( rResult[j] )
1508 if ( bIsDataLayout )
1510 if ( ( aResultEntry.Flags & sheet::MemberResultFlags::HASMEMBER ) != 0 )
1512 // Asterisks are added in ScDPSaveData::WriteToSource to create unique names.
1513 //! preserve original name there?
1514 String aSourceName( aResultEntry.Name );
1515 aSourceName.EraseTrailingChars( '*' );
1517 String aGivenName( aResultEntry.Caption ); //! Should use a stored name when available
1518 aGivenName.EraseLeadingChars( '\'' );
1520 rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName );
1523 else if ( bHasFilter )
1525 // name must match (simple value or subtotal)
1526 rResult[j] = ( ( aResultEntry.Flags & sheet::MemberResultFlags::HASMEMBER ) != 0 ) &&
1527 lcl_IsCondition( aResultEntry, aFilter );
1529 // if a function was specified, simple (non-subtotal) values never match
1530 if ( bHasFunc && nSubTotalCount == 0 )
1531 rResult[j] = FALSE;
1533 // if no condition is given, keep the columns/rows included
1535 aPrevious = aResultEntry;
1539 void lcl_StripSubTotals( std::vector< BOOL >& rResult, const std::vector< sal_Int32 >& rSubtotal )
1541 sal_Int32 nSize = rResult.size();
1542 DBG_ASSERT( (sal_Int32)rSubtotal.size() == nSize, "sizes don't match" );
1544 for (sal_Int32 nPos=0; nPos<nSize; nPos++)
1545 if ( rResult[nPos] && rSubtotal[nPos] )
1547 // if a subtotal is included, clear the result flag for the columns/rows that the subtotal includes
1548 sal_Int32 nStart = nPos - rSubtotal[nPos];
1549 DBG_ASSERT( nStart >= 0, "invalid subtotal count" );
1551 for (sal_Int32 nPrev = nStart; nPrev < nPos; nPrev++)
1552 rResult[nPrev] = FALSE;
1556 String lcl_GetDataFieldName( const String& rSourceName, sheet::GeneralFunction eFunc )
1558 USHORT nStrId = 0;
1559 switch ( eFunc )
1561 case sheet::GeneralFunction_SUM: nStrId = STR_FUN_TEXT_SUM; break;
1562 case sheet::GeneralFunction_COUNT:
1563 case sheet::GeneralFunction_COUNTNUMS: nStrId = STR_FUN_TEXT_COUNT; break;
1564 case sheet::GeneralFunction_AVERAGE: nStrId = STR_FUN_TEXT_AVG; break;
1565 case sheet::GeneralFunction_MAX: nStrId = STR_FUN_TEXT_MAX; break;
1566 case sheet::GeneralFunction_MIN: nStrId = STR_FUN_TEXT_MIN; break;
1567 case sheet::GeneralFunction_PRODUCT: nStrId = STR_FUN_TEXT_PRODUCT; break;
1568 case sheet::GeneralFunction_STDEV:
1569 case sheet::GeneralFunction_STDEVP: nStrId = STR_FUN_TEXT_STDDEV; break;
1570 case sheet::GeneralFunction_VAR:
1571 case sheet::GeneralFunction_VARP: nStrId = STR_FUN_TEXT_VAR; break;
1572 case sheet::GeneralFunction_NONE:
1573 case sheet::GeneralFunction_AUTO:
1574 default:
1576 DBG_ERRORFILE("wrong function");
1579 if ( !nStrId )
1580 return String();
1582 String aRet( ScGlobal::GetRscString( nStrId ) );
1583 aRet.AppendAscii(RTL_CONSTASCII_STRINGPARAM( " - " ));
1584 aRet.Append( rSourceName );
1585 return aRet;
1588 // static
1589 void ScDPOutput::GetDataDimensionNames( String& rSourceName, String& rGivenName,
1590 const uno::Reference<uno::XInterface>& xDim )
1592 uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
1593 uno::Reference<container::XNamed> xDimName( xDim, uno::UNO_QUERY );
1594 if ( xDimProp.is() && xDimName.is() )
1596 // Asterisks are added in ScDPSaveData::WriteToSource to create unique names.
1597 //! preserve original name there?
1598 rSourceName = xDimName->getName();
1599 rSourceName.EraseTrailingChars( '*' );
1601 // Generate "given name" the same way as in dptabres.
1602 //! Should use a stored name when available
1604 sheet::GeneralFunction eFunc = (sheet::GeneralFunction)ScUnoHelpFunctions::GetEnumProperty(
1605 xDimProp, rtl::OUString::createFromAscii(DP_PROP_FUNCTION),
1606 sheet::GeneralFunction_NONE );
1607 rGivenName = lcl_GetDataFieldName( rSourceName, eFunc );
1611 void lcl_GetTableVars( sal_Int32& rGrandTotalCols, sal_Int32& rGrandTotalRows, sal_Int32& rDataLayoutIndex,
1612 std::vector<String>& rDataNames, std::vector<String>& rGivenNames,
1613 sheet::DataPilotFieldOrientation& rDataOrient,
1614 const uno::Reference<sheet::XDimensionsSupplier>& xSource )
1616 rDataLayoutIndex = -1; // invalid
1617 rGrandTotalCols = 0;
1618 rGrandTotalRows = 0;
1619 rDataOrient = sheet::DataPilotFieldOrientation_HIDDEN;
1621 uno::Reference<beans::XPropertySet> xSrcProp( xSource, uno::UNO_QUERY );
1622 BOOL bColGrand = ScUnoHelpFunctions::GetBoolProperty( xSrcProp,
1623 rtl::OUString::createFromAscii(DP_PROP_COLUMNGRAND) );
1624 if ( bColGrand )
1625 rGrandTotalCols = 1; // default if data layout not in columns
1627 BOOL bRowGrand = ScUnoHelpFunctions::GetBoolProperty( xSrcProp,
1628 rtl::OUString::createFromAscii(DP_PROP_ROWGRAND) );
1629 if ( bRowGrand )
1630 rGrandTotalRows = 1; // default if data layout not in rows
1632 if ( xSource.is() )
1634 // find index and orientation of "data layout" dimension, count data dimensions
1636 sal_Int32 nDataCount = 0;
1638 uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xSource->getDimensions() );
1639 long nDimCount = xDims->getCount();
1640 for (long nDim=0; nDim<nDimCount; nDim++)
1642 uno::Reference<uno::XInterface> xDim =
1643 ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) );
1644 uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
1645 if ( xDimProp.is() )
1647 sheet::DataPilotFieldOrientation eDimOrient =
1648 (sheet::DataPilotFieldOrientation) ScUnoHelpFunctions::GetEnumProperty(
1649 xDimProp, rtl::OUString::createFromAscii(DP_PROP_ORIENTATION),
1650 sheet::DataPilotFieldOrientation_HIDDEN );
1651 if ( ScUnoHelpFunctions::GetBoolProperty( xDimProp,
1652 rtl::OUString::createFromAscii(DP_PROP_ISDATALAYOUT) ) )
1654 rDataLayoutIndex = nDim;
1655 rDataOrient = eDimOrient;
1657 if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
1659 String aSourceName;
1660 String aGivenName;
1661 ScDPOutput::GetDataDimensionNames( aSourceName, aGivenName, xDim );
1662 rDataNames.push_back( aSourceName );
1663 rGivenNames.push_back( aGivenName );
1665 ++nDataCount;
1670 if ( ( rDataOrient == sheet::DataPilotFieldOrientation_COLUMN ) && bColGrand )
1671 rGrandTotalCols = nDataCount;
1672 else if ( ( rDataOrient == sheet::DataPilotFieldOrientation_ROW ) && bRowGrand )
1673 rGrandTotalRows = nDataCount;
1677 // Returns TRUE on success and stores the result in rTarget
1678 // Returns FALSE if rFilters or rTarget describes something that is not visible
1679 BOOL ScDPOutput::GetPivotData( ScDPGetPivotDataField& rTarget,
1680 const std::vector< ScDPGetPivotDataField >& rFilters )
1682 CalcSizes();
1684 // need to know about grand total columns/rows:
1685 sal_Int32 nGrandTotalCols;
1686 sal_Int32 nGrandTotalRows;
1687 sal_Int32 nDataLayoutIndex;
1688 std::vector<String> aDataNames;
1689 std::vector<String> aGivenNames;
1690 sheet::DataPilotFieldOrientation eDataOrient;
1691 lcl_GetTableVars( nGrandTotalCols, nGrandTotalRows, nDataLayoutIndex, aDataNames, aGivenNames, eDataOrient, xSource );
1693 if ( aDataNames.empty() )
1694 return FALSE; // incomplete table without data fields -> no result
1696 if ( eDataOrient == sheet::DataPilotFieldOrientation_HIDDEN )
1698 // no data layout field -> single data field -> must match the selected field in rTarget
1700 DBG_ASSERT( aDataNames.size() == 1, "several data fields but no data layout field" );
1701 if ( !lcl_IsNamedDataField( rTarget, aDataNames[0], aGivenNames[0] ) )
1702 return FALSE;
1705 std::vector< BOOL > aIncludeCol( nColCount, TRUE );
1706 std::vector< sal_Int32 > aSubtotalCol( nColCount, 0 );
1707 std::vector< BOOL > aIncludeRow( nRowCount, TRUE );
1708 std::vector< sal_Int32 > aSubtotalRow( nRowCount, 0 );
1710 std::vector< BOOL > aFilterUsed( rFilters.size(), FALSE );
1712 long nField;
1713 long nCol;
1714 long nRow;
1715 bool bBeforeDataLayout;
1717 // look in column fields
1719 bBeforeDataLayout = ( eDataOrient == sheet::DataPilotFieldOrientation_COLUMN );
1720 for (nField=0; nField<nColFieldCount; nField++)
1721 lcl_FilterInclude( aIncludeCol, aSubtotalCol, pColFields[nField], rFilters, aFilterUsed, bBeforeDataLayout,
1722 nGrandTotalCols, nDataLayoutIndex, aDataNames, aGivenNames, rTarget, xSource );
1724 // look in row fields
1726 bBeforeDataLayout = ( eDataOrient == sheet::DataPilotFieldOrientation_ROW );
1727 for (nField=0; nField<nRowFieldCount; nField++)
1728 lcl_FilterInclude( aIncludeRow, aSubtotalRow, pRowFields[nField], rFilters, aFilterUsed, bBeforeDataLayout,
1729 nGrandTotalRows, nDataLayoutIndex, aDataNames, aGivenNames, rTarget, xSource );
1731 // page fields
1733 for (nField=0; nField<nPageFieldCount; nField++)
1734 if ( !lcl_CheckPageField( pPageFields[nField], rFilters, aFilterUsed ) )
1735 return FALSE;
1737 // all filter fields must be used
1738 for (SCSIZE nFilter=0; nFilter<aFilterUsed.size(); nFilter++)
1739 if (!aFilterUsed[nFilter])
1740 return FALSE;
1742 lcl_StripSubTotals( aIncludeCol, aSubtotalCol );
1743 lcl_StripSubTotals( aIncludeRow, aSubtotalRow );
1745 long nColPos = 0;
1746 long nColIncluded = 0;
1747 for (nCol=0; nCol<nColCount; nCol++)
1748 if (aIncludeCol[nCol])
1750 nColPos = nCol;
1751 ++nColIncluded;
1754 long nRowPos = 0;
1755 long nRowIncluded = 0;
1756 for (nRow=0; nRow<nRowCount; nRow++)
1757 if (aIncludeRow[nRow])
1759 nRowPos = nRow;
1760 ++nRowIncluded;
1763 if ( nColIncluded != 1 || nRowIncluded != 1 )
1764 return FALSE;
1766 const uno::Sequence<sheet::DataResult>& rDataRow = aData[nRowPos];
1767 if ( nColPos >= rDataRow.getLength() )
1768 return FALSE;
1770 const sheet::DataResult& rResult = rDataRow[nColPos];
1771 if ( rResult.Flags & sheet::DataResultFlags::ERROR )
1772 return FALSE; //! different error?
1774 rTarget.mbValIsStr = FALSE;
1775 rTarget.mnValNum = rResult.Value;
1777 return TRUE;
1780 BOOL ScDPOutput::IsFilterButton( const ScAddress& rPos )
1782 SCCOL nCol = rPos.Col();
1783 SCROW nRow = rPos.Row();
1784 SCTAB nTab = rPos.Tab();
1785 if ( nTab != aStartPos.Tab() || !bDoFilter )
1786 return FALSE; // wrong sheet or no button at all
1788 // filter button is at top left
1789 return ( nCol == aStartPos.Col() && nRow == aStartPos.Row() );
1792 long ScDPOutput::GetHeaderDim( const ScAddress& rPos, USHORT& rOrient )
1794 SCCOL nCol = rPos.Col();
1795 SCROW nRow = rPos.Row();
1796 SCTAB nTab = rPos.Tab();
1797 if ( nTab != aStartPos.Tab() )
1798 return -1; // wrong sheet
1800 // calculate output positions and sizes
1802 CalcSizes();
1804 // test for column header
1806 if ( nRow == nTabStartRow && nCol >= nDataStartCol && nCol < nDataStartCol + nColFieldCount )
1808 rOrient = sheet::DataPilotFieldOrientation_COLUMN;
1809 long nField = nCol - nDataStartCol;
1810 return pColFields[nField].nDim;
1813 // test for row header
1815 if ( nRow+1 == nDataStartRow && nCol >= nTabStartCol && nCol < nTabStartCol + nRowFieldCount )
1817 rOrient = sheet::DataPilotFieldOrientation_ROW;
1818 long nField = nCol - nTabStartCol;
1819 return pRowFields[nField].nDim;
1822 // test for page field
1824 SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 );
1825 if ( nCol == aStartPos.Col() && nRow >= nPageStartRow && nRow < nPageStartRow + nPageFieldCount )
1827 rOrient = sheet::DataPilotFieldOrientation_PAGE;
1828 long nField = nRow - nPageStartRow;
1829 return pPageFields[nField].nDim;
1832 //! single data field (?)
1834 rOrient = sheet::DataPilotFieldOrientation_HIDDEN;
1835 return -1; // invalid
1838 BOOL ScDPOutput::GetHeaderDrag( const ScAddress& rPos, BOOL bMouseLeft, BOOL bMouseTop,
1839 long nDragDim,
1840 Rectangle& rPosRect, USHORT& rOrient, long& rDimPos )
1842 // Rectangle instead of ScRange for rPosRect to allow for negative values
1844 SCCOL nCol = rPos.Col();
1845 SCROW nRow = rPos.Row();
1846 SCTAB nTab = rPos.Tab();
1847 if ( nTab != aStartPos.Tab() )
1848 return FALSE; // wrong sheet
1850 // calculate output positions and sizes
1852 CalcSizes();
1854 // test for column header
1856 if ( nCol >= nDataStartCol && nCol <= nTabEndCol &&
1857 nRow + 1 >= nMemberStartRow && nRow < nMemberStartRow + nColFieldCount )
1859 long nField = nRow - nMemberStartRow;
1860 if (nField < 0)
1862 nField = 0;
1863 bMouseTop = TRUE;
1865 //! find start of dimension
1867 rPosRect = Rectangle( nDataStartCol, nMemberStartRow + nField,
1868 nTabEndCol, nMemberStartRow + nField -1 );
1870 BOOL bFound = FALSE; // is this within the same orientation?
1871 BOOL bBeforeDrag = FALSE;
1872 BOOL bAfterDrag = FALSE;
1873 for (long nPos=0; nPos<nColFieldCount && !bFound; nPos++)
1875 if (pColFields[nPos].nDim == nDragDim)
1877 bFound = TRUE;
1878 if ( nField < nPos )
1879 bBeforeDrag = TRUE;
1880 else if ( nField > nPos )
1881 bAfterDrag = TRUE;
1885 if ( bFound )
1887 if (!bBeforeDrag)
1889 ++rPosRect.Bottom();
1890 if (bAfterDrag)
1891 ++rPosRect.Top();
1894 else
1896 if ( !bMouseTop )
1898 ++rPosRect.Top();
1899 ++rPosRect.Bottom();
1900 ++nField;
1904 rOrient = sheet::DataPilotFieldOrientation_COLUMN;
1905 rDimPos = nField; //!...
1906 return TRUE;
1909 // test for row header
1911 // special case if no row fields
1912 BOOL bSpecial = ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow &&
1913 nRowFieldCount == 0 && nCol == nTabStartCol && bMouseLeft );
1915 if ( bSpecial || ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow &&
1916 nCol + 1 >= nTabStartCol && nCol < nTabStartCol + nRowFieldCount ) )
1918 long nField = nCol - nTabStartCol;
1919 //! find start of dimension
1921 rPosRect = Rectangle( nTabStartCol + nField, nDataStartRow - 1,
1922 nTabStartCol + nField - 1, nTabEndRow );
1924 BOOL bFound = FALSE; // is this within the same orientation?
1925 BOOL bBeforeDrag = FALSE;
1926 BOOL bAfterDrag = FALSE;
1927 for (long nPos=0; nPos<nRowFieldCount && !bFound; nPos++)
1929 if (pRowFields[nPos].nDim == nDragDim)
1931 bFound = TRUE;
1932 if ( nField < nPos )
1933 bBeforeDrag = TRUE;
1934 else if ( nField > nPos )
1935 bAfterDrag = TRUE;
1939 if ( bFound )
1941 if (!bBeforeDrag)
1943 ++rPosRect.Right();
1944 if (bAfterDrag)
1945 ++rPosRect.Left();
1948 else
1950 if ( !bMouseLeft )
1952 ++rPosRect.Left();
1953 ++rPosRect.Right();
1954 ++nField;
1958 rOrient = sheet::DataPilotFieldOrientation_ROW;
1959 rDimPos = nField; //!...
1960 return TRUE;
1963 // test for page fields
1965 SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 );
1966 if ( nCol >= aStartPos.Col() && nCol <= nTabEndCol &&
1967 nRow + 1 >= nPageStartRow && nRow < nPageStartRow + nPageFieldCount )
1969 long nField = nRow - nPageStartRow;
1970 if (nField < 0)
1972 nField = 0;
1973 bMouseTop = TRUE;
1975 //! find start of dimension
1977 rPosRect = Rectangle( aStartPos.Col(), nPageStartRow + nField,
1978 nTabEndCol, nPageStartRow + nField - 1 );
1980 BOOL bFound = FALSE; // is this within the same orientation?
1981 BOOL bBeforeDrag = FALSE;
1982 BOOL bAfterDrag = FALSE;
1983 for (long nPos=0; nPos<nPageFieldCount && !bFound; nPos++)
1985 if (pPageFields[nPos].nDim == nDragDim)
1987 bFound = TRUE;
1988 if ( nField < nPos )
1989 bBeforeDrag = TRUE;
1990 else if ( nField > nPos )
1991 bAfterDrag = TRUE;
1995 if ( bFound )
1997 if (!bBeforeDrag)
1999 ++rPosRect.Bottom();
2000 if (bAfterDrag)
2001 ++rPosRect.Top();
2004 else
2006 if ( !bMouseTop )
2008 ++rPosRect.Top();
2009 ++rPosRect.Bottom();
2010 ++nField;
2014 rOrient = sheet::DataPilotFieldOrientation_PAGE;
2015 rDimPos = nField; //!...
2016 return TRUE;
2019 return FALSE;