xref: /trunk/main/sc/source/core/data/dpoutput.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sc.hxx"
30 
31 
32 
33 // INCLUDE ---------------------------------------------------------------
34 
35 #include "scitems.hxx"
36 #include <svx/algitem.hxx>
37 #include <editeng/boxitem.hxx>
38 #include <editeng/brshitem.hxx>
39 #include <editeng/wghtitem.hxx>
40 #include <unotools/transliterationwrapper.hxx>
41 
42 #include "dpoutput.hxx"
43 #include "dptabsrc.hxx"
44 #include "dpcachetable.hxx"
45 #include "document.hxx"
46 #include "patattr.hxx"
47 #include "docpool.hxx"
48 #include "markdata.hxx"
49 #include "attrib.hxx"
50 #include "formula/errorcodes.hxx"       // errNoValue
51 #include "miscuno.hxx"
52 #include "globstr.hrc"
53 #include "stlpool.hxx"
54 #include "stlsheet.hxx"
55 #include "collect.hxx"
56 #include "scresid.hxx"
57 #include "unonames.hxx"
58 #include "sc.hrc"
59 // Wang Xu Ming -- 2009-8-17
60 // DataPilot Migration - Cache&&Performance
61 #include "scdpoutputimpl.hxx"
62 #include "dpglobal.hxx"
63 // End Comments
64 #include <com/sun/star/beans/XPropertySet.hpp>
65 
66 #include <vector>
67 
68 using namespace com::sun::star;
69 using ::std::vector;
70 using ::com::sun::star::beans::XPropertySet;
71 using ::com::sun::star::uno::Sequence;
72 using ::com::sun::star::uno::UNO_QUERY;
73 using ::com::sun::star::uno::Reference;
74 using ::com::sun::star::sheet::DataPilotTablePositionData;
75 using ::com::sun::star::sheet::DataPilotTableResultData;
76 using ::com::sun::star::uno::makeAny;
77 using ::com::sun::star::uno::Any;
78 using ::rtl::OUString;
79 
80 // -----------------------------------------------------------------------
81 
82 //! move to a header file
83 //! use names from unonames.hxx?
84 #define DP_PROP_FUNCTION            "Function"
85 #define DP_PROP_ORIENTATION         "Orientation"
86 #define DP_PROP_POSITION            "Position"
87 #define DP_PROP_USEDHIERARCHY       "UsedHierarchy"
88 #define DP_PROP_ISDATALAYOUT        "IsDataLayoutDimension"
89 #define DP_PROP_NUMBERFORMAT        "NumberFormat"
90 #define DP_PROP_FILTER              "Filter"
91 #define DP_PROP_COLUMNGRAND         "ColumnGrand"
92 #define DP_PROP_ROWGRAND            "RowGrand"
93 #define DP_PROP_SUBTOTALS           "SubTotals"
94 
95 // -----------------------------------------------------------------------
96 
97 //! dynamic!!!
98 #define SC_DPOUT_MAXLEVELS  256
99 
100 
101 struct ScDPOutLevelData
102 {
103     long                                nDim;
104     long                                nHier;
105     long                                nLevel;
106     long                                nDimPos;
107     uno::Sequence<sheet::MemberResult>  aResult;
108     String                              maName;   /// Name is the internal field name.
109     String                              aCaption; /// Caption is the name visible in the output table.
110     bool                                mbHasHiddenMember;
111 
112     ScDPOutLevelData()
113     {
114         nDim = nHier = nLevel = nDimPos = -1;
115         mbHasHiddenMember = false;
116     }
117 
118     sal_Bool operator<(const ScDPOutLevelData& r) const
119         { return nDimPos<r.nDimPos || ( nDimPos==r.nDimPos && nHier<r.nHier ) ||
120             ( nDimPos==r.nDimPos && nHier==r.nHier && nLevel<r.nLevel ); }
121 
122     void Swap(ScDPOutLevelData& r)
123 //!     { ScDPOutLevelData aTemp = r; r = *this; *this = aTemp; }
124         { ScDPOutLevelData aTemp; aTemp = r; r = *this; *this = aTemp; }
125 
126     //! bug (73840) in uno::Sequence - copy and then assign doesn't work!
127 };
128 
129 // -----------------------------------------------------------------------
130 
131 void lcl_SetStyleById( ScDocument* pDoc, SCTAB nTab,
132                     SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
133                     sal_uInt16 nStrId )
134 {
135     if ( nCol1 > nCol2 || nRow1 > nRow2 )
136     {
137         DBG_ERROR("SetStyleById: invalid range");
138         return;
139     }
140 
141     String aStyleName = ScGlobal::GetRscString( nStrId );
142     ScStyleSheetPool* pStlPool = pDoc->GetStyleSheetPool();
143     ScStyleSheet* pStyle = (ScStyleSheet*) pStlPool->Find( aStyleName, SFX_STYLE_FAMILY_PARA );
144     if (!pStyle)
145     {
146         //  create new style (was in ScPivot::SetStyle)
147 
148         pStyle = (ScStyleSheet*) &pStlPool->Make( aStyleName, SFX_STYLE_FAMILY_PARA,
149                                                     SFXSTYLEBIT_USERDEF );
150         pStyle->SetParent( ScGlobal::GetRscString(STR_STYLENAME_STANDARD) );
151         SfxItemSet& rSet = pStyle->GetItemSet();
152         if ( nStrId==STR_PIVOT_STYLE_RESULT || nStrId==STR_PIVOT_STYLE_TITLE )
153             rSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
154         if ( nStrId==STR_PIVOT_STYLE_CATEGORY || nStrId==STR_PIVOT_STYLE_TITLE )
155             rSet.Put( SvxHorJustifyItem( SVX_HOR_JUSTIFY_LEFT, ATTR_HOR_JUSTIFY ) );
156     }
157 
158     pDoc->ApplyStyleAreaTab( nCol1, nRow1, nCol2, nRow2, nTab, *pStyle );
159 }
160 
161 void lcl_SetFrame( ScDocument* pDoc, SCTAB nTab,
162                     SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
163                     sal_uInt16 nWidth )
164 {
165     SvxBorderLine aLine;
166     aLine.SetOutWidth(nWidth);
167     SvxBoxItem aBox( ATTR_BORDER );
168     aBox.SetLine(&aLine, BOX_LINE_LEFT);
169     aBox.SetLine(&aLine, BOX_LINE_TOP);
170     aBox.SetLine(&aLine, BOX_LINE_RIGHT);
171     aBox.SetLine(&aLine, BOX_LINE_BOTTOM);
172     SvxBoxInfoItem aBoxInfo( ATTR_BORDER_INNER );
173     aBoxInfo.SetValid(VALID_HORI,sal_False);
174     aBoxInfo.SetValid(VALID_VERT,sal_False);
175     aBoxInfo.SetValid(VALID_DISTANCE,sal_False);
176 
177     pDoc->ApplyFrameAreaTab( ScRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab ), &aBox, &aBoxInfo );
178 }
179 
180 // -----------------------------------------------------------------------
181 
182 void lcl_FillNumberFormats( sal_uInt32*& rFormats, long& rCount,
183                             const uno::Reference<sheet::XDataPilotMemberResults>& xLevRes,
184                             const uno::Reference<container::XIndexAccess>& xDims )
185 {
186     if ( rFormats )
187         return;                         // already set
188 
189     //  xLevRes is from the data layout dimension
190     //! use result sequence from ScDPOutLevelData!
191 
192     uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults();
193 
194     long nSize = aResult.getLength();
195     if (nSize)
196     {
197         //  get names/formats for all data dimensions
198         //! merge this with the loop to collect ScDPOutLevelData?
199 
200         String aDataNames[SC_DPOUT_MAXLEVELS];
201         sal_uInt32 nDataFormats[SC_DPOUT_MAXLEVELS];
202         long nDataCount = 0;
203         sal_Bool bAnySet = sal_False;
204 
205         long nDimCount = xDims->getCount();
206         for (long nDim=0; nDim<nDimCount; nDim++)
207         {
208             uno::Reference<uno::XInterface> xDim =
209                     ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) );
210             uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
211             uno::Reference<container::XNamed> xDimName( xDim, uno::UNO_QUERY );
212             if ( xDimProp.is() && xDimName.is() )
213             {
214                 sheet::DataPilotFieldOrientation eDimOrient =
215                     (sheet::DataPilotFieldOrientation) ScUnoHelpFunctions::GetEnumProperty(
216                         xDimProp, rtl::OUString::createFromAscii(DP_PROP_ORIENTATION),
217                         sheet::DataPilotFieldOrientation_HIDDEN );
218                 if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
219                 {
220                     aDataNames[nDataCount] = String( xDimName->getName() );
221                     long nFormat = ScUnoHelpFunctions::GetLongProperty(
222                                             xDimProp,
223                                             rtl::OUString::createFromAscii(DP_PROP_NUMBERFORMAT) );
224                     nDataFormats[nDataCount] = nFormat;
225                     if ( nFormat != 0 )
226                         bAnySet = sal_True;
227                     ++nDataCount;
228                 }
229             }
230         }
231 
232         if ( bAnySet )      // forget everything if all formats are 0 (or no data dimensions)
233         {
234             const sheet::MemberResult* pArray = aResult.getConstArray();
235 
236             String aName;
237             sal_uInt32* pNumFmt = new sal_uInt32[nSize];
238             if (nDataCount == 1)
239             {
240                 //  only one data dimension -> use its numberformat everywhere
241                 long nFormat = nDataFormats[0];
242                 for (long nPos=0; nPos<nSize; nPos++)
243                     pNumFmt[nPos] = nFormat;
244             }
245             else
246             {
247                 for (long nPos=0; nPos<nSize; nPos++)
248                 {
249                     //  if CONTINUE bit is set, keep previous name
250                     //! keep number format instead!
251                     if ( !(pArray[nPos].Flags & sheet::MemberResultFlags::CONTINUE) )
252                         aName = String( pArray[nPos].Name );
253 
254                     sal_uInt32 nFormat = 0;
255                     for (long i=0; i<nDataCount; i++)
256                         if (aName == aDataNames[i])         //! search more efficiently?
257                         {
258                             nFormat = nDataFormats[i];
259                             break;
260                         }
261                     pNumFmt[nPos] = nFormat;
262                 }
263             }
264 
265             rFormats = pNumFmt;
266             rCount = nSize;
267         }
268     }
269 }
270 
271 sal_uInt32 lcl_GetFirstNumberFormat( const uno::Reference<container::XIndexAccess>& xDims )
272 {
273     long nDimCount = xDims->getCount();
274     for (long nDim=0; nDim<nDimCount; nDim++)
275     {
276         uno::Reference<uno::XInterface> xDim =
277                 ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) );
278         uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
279         if ( xDimProp.is() )
280         {
281             sheet::DataPilotFieldOrientation eDimOrient =
282                 (sheet::DataPilotFieldOrientation) ScUnoHelpFunctions::GetEnumProperty(
283                     xDimProp, rtl::OUString::createFromAscii(DP_PROP_ORIENTATION),
284                     sheet::DataPilotFieldOrientation_HIDDEN );
285             if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
286             {
287                 long nFormat = ScUnoHelpFunctions::GetLongProperty(
288                                         xDimProp,
289                                         rtl::OUString::createFromAscii(DP_PROP_NUMBERFORMAT) );
290 
291                 return nFormat;     // use format from first found data dimension
292             }
293         }
294     }
295 
296     return 0;       // none found
297 }
298 
299 void lcl_SortFields( ScDPOutLevelData* pFields, long nFieldCount )
300 {
301     for (long i=0; i+1<nFieldCount; i++)
302     {
303         for (long j=0; j+i+1<nFieldCount; j++)
304             if ( pFields[j+1] < pFields[j] )
305                 pFields[j].Swap( pFields[j+1] );
306     }
307 }
308 
309 sal_Bool lcl_MemberEmpty( const uno::Sequence<sheet::MemberResult>& rSeq )
310 {
311     //  used to skip levels that have no members
312 
313     long nLen = rSeq.getLength();
314     const sheet::MemberResult* pArray = rSeq.getConstArray();
315     for (long i=0; i<nLen; i++)
316         if (pArray[i].Flags & sheet::MemberResultFlags::HASMEMBER)
317             return sal_False;
318 
319     return sal_True;    // no member data -> empty
320 }
321 
322 uno::Sequence<sheet::MemberResult> lcl_GetSelectedPageAsResult( const uno::Reference<beans::XPropertySet>& xDimProp )
323 {
324     uno::Sequence<sheet::MemberResult> aRet;
325     if ( xDimProp.is() )
326     {
327         try
328         {
329             //! merge with ScDPDimension::setPropertyValue?
330 
331             uno::Any aValue = xDimProp->getPropertyValue( rtl::OUString::createFromAscii(DP_PROP_FILTER) );
332 
333             uno::Sequence<sheet::TableFilterField> aSeq;
334             if (aValue >>= aSeq)
335             {
336                 if ( aSeq.getLength() == 1 )
337                 {
338                     const sheet::TableFilterField& rField = aSeq[0];
339                     if ( rField.Field == 0 && rField.Operator == sheet::FilterOperator_EQUAL && !rField.IsNumeric )
340                     {
341                         rtl::OUString aSelectedPage( rField.StringValue );
342                         //! different name/caption string?
343                         sheet::MemberResult aResult( aSelectedPage, aSelectedPage, 0 );
344                         aRet = uno::Sequence<sheet::MemberResult>( &aResult, 1 );
345                     }
346                 }
347                 // else return empty sequence
348             }
349         }
350         catch ( uno::Exception& )
351         {
352             // recent addition - allow source to not handle it (no error)
353         }
354     }
355     return aRet;
356 }
357 
358 ScDPOutput::ScDPOutput( ScDocument* pD, const uno::Reference<sheet::XDimensionsSupplier>& xSrc,
359                                 const ScAddress& rPos, sal_Bool bFilter ) :
360     pDoc( pD ),
361     xSource( xSrc ),
362     aStartPos( rPos ),
363     bDoFilter( bFilter ),
364     bResultsError( sal_False ),
365     mbHasDataLayout(false),
366     pColNumFmt( NULL ),
367     pRowNumFmt( NULL ),
368     nColFmtCount( 0 ),
369     nRowFmtCount( 0 ),
370     nSingleNumFmt( 0 ),
371     bSizesValid( sal_False ),
372     bSizeOverflow( sal_False ),
373     mbHeaderLayout( false )
374 {
375     nTabStartCol = nMemberStartCol = nDataStartCol = nTabEndCol = 0;
376     nTabStartRow = nMemberStartRow = nDataStartRow = nTabEndRow = 0;
377 
378     pColFields  = new ScDPOutLevelData[SC_DPOUT_MAXLEVELS];
379     pRowFields  = new ScDPOutLevelData[SC_DPOUT_MAXLEVELS];
380     pPageFields = new ScDPOutLevelData[SC_DPOUT_MAXLEVELS];
381     nColFieldCount = 0;
382     nRowFieldCount = 0;
383     nPageFieldCount = 0;
384 
385     uno::Reference<sheet::XDataPilotResults> xResult( xSource, uno::UNO_QUERY );
386     if ( xSource.is() && xResult.is() )
387     {
388         //  get dimension results:
389 
390         uno::Reference<container::XIndexAccess> xDims =
391                 new ScNameToIndexAccess( xSource->getDimensions() );
392         long nDimCount = xDims->getCount();
393         for (long nDim=0; nDim<nDimCount; nDim++)
394         {
395             uno::Reference<uno::XInterface> xDim =
396                     ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) );
397             uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
398             uno::Reference<sheet::XHierarchiesSupplier> xDimSupp( xDim, uno::UNO_QUERY );
399             if ( xDimProp.is() && xDimSupp.is() )
400             {
401                 sheet::DataPilotFieldOrientation eDimOrient =
402                     (sheet::DataPilotFieldOrientation) ScUnoHelpFunctions::GetEnumProperty(
403                         xDimProp, rtl::OUString::createFromAscii(DP_PROP_ORIENTATION),
404                         sheet::DataPilotFieldOrientation_HIDDEN );
405                 long nDimPos = ScUnoHelpFunctions::GetLongProperty( xDimProp,
406                         rtl::OUString::createFromAscii(DP_PROP_POSITION) );
407                 sal_Bool bIsDataLayout = ScUnoHelpFunctions::GetBoolProperty(
408                                                 xDimProp,
409                                                 rtl::OUString::createFromAscii(DP_PROP_ISDATALAYOUT) );
410                 bool bHasHiddenMember = ScUnoHelpFunctions::GetBoolProperty(
411                     xDimProp, OUString::createFromAscii(SC_UNO_HAS_HIDDEN_MEMBER));
412 
413                 if ( eDimOrient != sheet::DataPilotFieldOrientation_HIDDEN )
414                 {
415                     uno::Reference<container::XIndexAccess> xHiers =
416                             new ScNameToIndexAccess( xDimSupp->getHierarchies() );
417                     long nHierarchy = ScUnoHelpFunctions::GetLongProperty(
418                                             xDimProp,
419                                             rtl::OUString::createFromAscii(DP_PROP_USEDHIERARCHY) );
420                     if ( nHierarchy >= xHiers->getCount() )
421                         nHierarchy = 0;
422 
423                     uno::Reference<uno::XInterface> xHier =
424                             ScUnoHelpFunctions::AnyToInterface(
425                                                 xHiers->getByIndex(nHierarchy) );
426                     uno::Reference<sheet::XLevelsSupplier> xHierSupp( xHier, uno::UNO_QUERY );
427                     if ( xHierSupp.is() )
428                     {
429                         uno::Reference<container::XIndexAccess> xLevels =
430                                 new ScNameToIndexAccess( xHierSupp->getLevels() );
431                         long nLevCount = xLevels->getCount();
432                         for (long nLev=0; nLev<nLevCount; nLev++)
433                         {
434                             uno::Reference<uno::XInterface> xLevel =
435                                         ScUnoHelpFunctions::AnyToInterface(
436                                                             xLevels->getByIndex(nLev) );
437                             uno::Reference<container::XNamed> xLevNam( xLevel, uno::UNO_QUERY );
438                             uno::Reference<sheet::XDataPilotMemberResults> xLevRes(
439                                     xLevel, uno::UNO_QUERY );
440                             if ( xLevNam.is() && xLevRes.is() )
441                             {
442                                 String aName = xLevNam->getName();
443                                 Reference<XPropertySet> xPropSet(xLevel, UNO_QUERY);
444                                 // Caption equals the field name by default.
445                                 // #i108948# use ScUnoHelpFunctions::GetStringProperty, because
446                                 // LayoutName is new and may not be present in external implementation
447                                 OUString aCaption = ScUnoHelpFunctions::GetStringProperty( xPropSet,
448                                     OUString::createFromAscii(SC_UNO_LAYOUTNAME), aName );
449 
450                                 bool bRowFieldHasMember = false;
451                                 switch ( eDimOrient )
452                                 {
453                                     case sheet::DataPilotFieldOrientation_COLUMN:
454                                         pColFields[nColFieldCount].nDim    = nDim;
455                                         pColFields[nColFieldCount].nHier   = nHierarchy;
456                                         pColFields[nColFieldCount].nLevel  = nLev;
457                                         pColFields[nColFieldCount].nDimPos = nDimPos;
458                                         pColFields[nColFieldCount].aResult = xLevRes->getResults();
459                                         pColFields[nColFieldCount].maName  = aName;
460                                         pColFields[nColFieldCount].aCaption= aCaption;
461                                         pColFields[nColFieldCount].mbHasHiddenMember = bHasHiddenMember;
462                                         if (!lcl_MemberEmpty(pColFields[nColFieldCount].aResult))
463                                             ++nColFieldCount;
464                                         break;
465                                     case sheet::DataPilotFieldOrientation_ROW:
466                                         pRowFields[nRowFieldCount].nDim    = nDim;
467                                         pRowFields[nRowFieldCount].nHier   = nHierarchy;
468                                         pRowFields[nRowFieldCount].nLevel  = nLev;
469                                         pRowFields[nRowFieldCount].nDimPos = nDimPos;
470                                         pRowFields[nRowFieldCount].aResult = xLevRes->getResults();
471                                         pRowFields[nRowFieldCount].maName  = aName;
472                                         pRowFields[nRowFieldCount].aCaption= aCaption;
473                                         pRowFields[nRowFieldCount].mbHasHiddenMember = bHasHiddenMember;
474                                         if (!lcl_MemberEmpty(pRowFields[nRowFieldCount].aResult))
475                                         {
476                                             ++nRowFieldCount;
477                                             bRowFieldHasMember = true;
478                                         }
479                                         break;
480                                     case sheet::DataPilotFieldOrientation_PAGE:
481                                         pPageFields[nPageFieldCount].nDim    = nDim;
482                                         pPageFields[nPageFieldCount].nHier   = nHierarchy;
483                                         pPageFields[nPageFieldCount].nLevel  = nLev;
484                                         pPageFields[nPageFieldCount].nDimPos = nDimPos;
485                                         pPageFields[nPageFieldCount].aResult = lcl_GetSelectedPageAsResult(xDimProp);
486                                         pPageFields[nPageFieldCount].maName  = aName;
487                                         pPageFields[nPageFieldCount].aCaption= aCaption;
488                                         pPageFields[nPageFieldCount].mbHasHiddenMember = bHasHiddenMember;
489                                         // no check on results for page fields
490                                         ++nPageFieldCount;
491                                         break;
492                                     default:
493                                     {
494                                         // added to avoid warnings
495                                     }
496                                 }
497 
498                                 // get number formats from data dimensions
499                                 if ( bIsDataLayout )
500                                 {
501                                     if (bRowFieldHasMember)
502                                         mbHasDataLayout = true;
503 
504                                     DBG_ASSERT( nLevCount == 1, "data layout: multiple levels?" );
505                                     if ( eDimOrient == sheet::DataPilotFieldOrientation_COLUMN )
506                                         lcl_FillNumberFormats( pColNumFmt, nColFmtCount, xLevRes, xDims );
507                                     else if ( eDimOrient == sheet::DataPilotFieldOrientation_ROW )
508                                         lcl_FillNumberFormats( pRowNumFmt, nRowFmtCount, xLevRes, xDims );
509                                 }
510                             }
511                         }
512                     }
513                 }
514                 else if ( bIsDataLayout )
515                 {
516                     // data layout dimension is hidden (allowed if there is only one data dimension)
517                     // -> use the number format from the first data dimension for all results
518 
519                     nSingleNumFmt = lcl_GetFirstNumberFormat( xDims );
520                 }
521             }
522         }
523         lcl_SortFields( pColFields, nColFieldCount );
524         lcl_SortFields( pRowFields, nRowFieldCount );
525         lcl_SortFields( pPageFields, nPageFieldCount );
526 
527         //  get data results:
528 
529         try
530         {
531             aData = xResult->getResults();
532         }
533         catch (uno::RuntimeException&)
534         {
535             bResultsError = sal_True;
536         }
537     }
538 
539     // get "DataDescription" property (may be missing in external sources)
540 
541     uno::Reference<beans::XPropertySet> xSrcProp( xSource, uno::UNO_QUERY );
542     if ( xSrcProp.is() )
543     {
544         try
545         {
546             uno::Any aAny = xSrcProp->getPropertyValue(
547                     rtl::OUString::createFromAscii(SC_UNO_DATADESC) );
548             rtl::OUString aUStr;
549             aAny >>= aUStr;
550             aDataDescription = String( aUStr );
551         }
552         catch(uno::Exception&)
553         {
554         }
555     }
556 }
557 
558 ScDPOutput::~ScDPOutput()
559 {
560     delete[] pColFields;
561     delete[] pRowFields;
562     delete[] pPageFields;
563 
564     delete[] pColNumFmt;
565     delete[] pRowNumFmt;
566 }
567 
568 void ScDPOutput::SetPosition( const ScAddress& rPos )
569 {
570     aStartPos = rPos;
571     bSizesValid = bSizeOverflow = sal_False;
572 }
573 
574 void ScDPOutput::DataCell( SCCOL nCol, SCROW nRow, SCTAB nTab, const sheet::DataResult& rData )
575 {
576     long nFlags = rData.Flags;
577     if ( nFlags & sheet::DataResultFlags::ERROR )
578     {
579         pDoc->SetError( nCol, nRow, nTab, errNoValue );
580     }
581     else if ( nFlags & sheet::DataResultFlags::HASDATA )
582     {
583         pDoc->SetValue( nCol, nRow, nTab, rData.Value );
584 
585         //  use number formats from source
586 
587         DBG_ASSERT( bSizesValid, "DataCell: !bSizesValid" );
588         sal_uInt32 nFormat = 0;
589         if ( pColNumFmt )
590         {
591             if ( nCol >= nDataStartCol )
592             {
593                 long nIndex = nCol - nDataStartCol;
594                 if ( nIndex < nColFmtCount )
595                     nFormat = pColNumFmt[nIndex];
596             }
597         }
598         else if ( pRowNumFmt )
599         {
600             if ( nRow >= nDataStartRow )
601             {
602                 long nIndex = nRow - nDataStartRow;
603                 if ( nIndex < nRowFmtCount )
604                     nFormat = pRowNumFmt[nIndex];
605             }
606         }
607         else if ( nSingleNumFmt != 0 )
608             nFormat = nSingleNumFmt;        // single format is used everywhere
609         if ( nFormat != 0 )
610             pDoc->ApplyAttr( nCol, nRow, nTab, SfxUInt32Item( ATTR_VALUE_FORMAT, nFormat ) );
611     }
612     else
613     {
614         //pDoc->SetString( nCol, nRow, nTab, EMPTY_STRING );
615     }
616 
617     //  SubTotal formatting is controlled by headers
618 }
619 
620 void ScDPOutput::HeaderCell( SCCOL nCol, SCROW nRow, SCTAB nTab,
621                                 const sheet::MemberResult& rData, sal_Bool bColHeader, long nLevel )
622 {
623     long nFlags = rData.Flags;
624 
625     rtl::OUStringBuffer aCaptionBuf;
626     if (!(nFlags & sheet::MemberResultFlags::NUMERIC))
627         // This caption is not a number.  Make sure it won't get parsed as one.
628         aCaptionBuf.append(sal_Unicode('\''));
629     aCaptionBuf.append(rData.Caption);
630 
631     if ( nFlags & sheet::MemberResultFlags::HASMEMBER )
632     {
633         pDoc->SetString( nCol, nRow, nTab, aCaptionBuf.makeStringAndClear() );
634     }
635     else
636     {
637         //pDoc->SetString( nCol, nRow, nTab, EMPTY_STRING );
638     }
639 
640     if ( nFlags & sheet::MemberResultFlags::SUBTOTAL )
641     {
642 //      SvxWeightItem aItem( WEIGHT_BOLD );     // weight is in the style
643         // Wang Xu Ming -- 2009-8-17
644         // DataPilot Migration - Cache&&Performance
645         OutputImpl outputimp( pDoc, nTab,
646             nTabStartCol, nTabStartRow, nMemberStartCol, nMemberStartRow,
647             nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow );
648         // End Comments
649         //! limit frames to horizontal or vertical?
650         if (bColHeader)
651         {
652             // Wang Xu Ming -- 2009-8-17
653             // DataPilot Migration - Cache&&Performance
654             //lcl_SetFrame( pDoc,nTab, nCol,nMemberStartRow+(SCROW)nLevel, nCol,nTabEndRow, SC_DP_FRAME_INNER_BOLD );
655             outputimp.OutputBlockFrame( nCol,nMemberStartRow+(SCROW)nLevel, nCol,nDataStartRow-1 );
656             // End Comments
657 
658             lcl_SetStyleById( pDoc,nTab, nCol,nMemberStartRow+(SCROW)nLevel, nCol,nDataStartRow-1,
659                                     STR_PIVOT_STYLE_TITLE );
660             lcl_SetStyleById( pDoc,nTab, nCol,nDataStartRow, nCol,nTabEndRow,
661                                     STR_PIVOT_STYLE_RESULT );
662         }
663         else
664         {
665             // Wang Xu Ming -- 2009-8-17
666             // DataPilot Migration - Cache&&Performance
667             //lcl_SetFrame( pDoc,nTab, nMemberStartCol+(sal_uInt16)nLevel,nRow, nTabEndCol,nRow, SC_DP_FRAME_INNER_BOLD );
668             outputimp.OutputBlockFrame( nMemberStartCol+(SCCOL)nLevel,nRow, nDataStartCol-1,nRow );
669             // End Comments
670             lcl_SetStyleById( pDoc,nTab, nMemberStartCol+(SCCOL)nLevel,nRow, nDataStartCol-1,nRow,
671                                     STR_PIVOT_STYLE_TITLE );
672             lcl_SetStyleById( pDoc,nTab, nDataStartCol,nRow, nTabEndCol,nRow,
673                                     STR_PIVOT_STYLE_RESULT );
674         }
675     }
676 }
677 
678 void ScDPOutput::FieldCell( SCCOL nCol, SCROW nRow, SCTAB nTab, const String& rCaption,
679                             bool bInTable, bool bPopup, bool bHasHiddenMember )
680 {
681     pDoc->SetString( nCol, nRow, nTab, rCaption );
682     if (bInTable)
683         lcl_SetFrame( pDoc,nTab, nCol,nRow, nCol,nRow, 20 );
684 
685     //  Button
686     sal_uInt16 nMergeFlag = SC_MF_BUTTON;
687     if (bPopup)
688         nMergeFlag |= SC_MF_BUTTON_POPUP;
689     if (bHasHiddenMember)
690         nMergeFlag |= SC_MF_HIDDEN_MEMBER;
691     pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, nMergeFlag);
692 
693     lcl_SetStyleById( pDoc,nTab, nCol,nRow, nCol,nRow, STR_PIVOT_STYLE_FIELDNAME );
694 }
695 
696 void lcl_DoFilterButton( ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
697 {
698     pDoc->SetString( nCol, nRow, nTab, ScGlobal::GetRscString(STR_CELL_FILTER) );
699     pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, SC_MF_BUTTON);
700 }
701 
702 void ScDPOutput::CalcSizes()
703 {
704     if (!bSizesValid)
705     {
706         //  get column size of data from first row
707         //! allow different sizes (and clear following areas) ???
708 
709         nRowCount = aData.getLength();
710         const uno::Sequence<sheet::DataResult>* pRowAry = aData.getConstArray();
711         nColCount = nRowCount ? ( pRowAry[0].getLength() ) : 0;
712 
713         nHeaderSize = 1;
714         if (GetHeaderLayout() && nColFieldCount == 0)
715             // Insert an extra header row only when there is no column field.
716             nHeaderSize = 2;
717 
718         //  calculate output positions and sizes
719 
720         long nPageSize = 0;     //! use page fields!
721         if ( bDoFilter || nPageFieldCount )
722         {
723             nPageSize += nPageFieldCount + 1;   // plus one empty row
724             if ( bDoFilter )
725                 ++nPageSize;        //  filter button above the page fields
726         }
727 
728         if ( aStartPos.Col() + nRowFieldCount + nColCount - 1 > MAXCOL ||
729              aStartPos.Row() + nPageSize + nHeaderSize + nColFieldCount + nRowCount > MAXROW )
730         {
731             bSizeOverflow = sal_True;
732         }
733 
734         nTabStartCol = aStartPos.Col();
735         nTabStartRow = aStartPos.Row() + (SCROW)nPageSize;          // below page fields
736         nMemberStartCol = nTabStartCol;
737         nMemberStartRow = nTabStartRow + (SCROW) nHeaderSize;
738         nDataStartCol = nMemberStartCol + (SCCOL)nRowFieldCount;
739         nDataStartRow = nMemberStartRow + (SCROW)nColFieldCount;
740         if ( nColCount > 0 )
741             nTabEndCol = nDataStartCol + (SCCOL)nColCount - 1;
742         else
743             nTabEndCol = nDataStartCol;     // single column will remain empty
744         // if page fields are involved, include the page selection cells
745         if ( nPageFieldCount > 0 && nTabEndCol < nTabStartCol + 1 )
746             nTabEndCol = nTabStartCol + 1;
747         if ( nRowCount > 0 )
748             nTabEndRow = nDataStartRow + (SCROW)nRowCount - 1;
749         else
750             nTabEndRow = nDataStartRow;     // single row will remain empty
751         bSizesValid = sal_True;
752     }
753 }
754 
755 sal_Int32 ScDPOutput::GetPositionType(const ScAddress& rPos)
756 {
757     using namespace ::com::sun::star::sheet;
758 
759     SCCOL nCol = rPos.Col();
760     SCROW nRow = rPos.Row();
761     SCTAB nTab = rPos.Tab();
762     if ( nTab != aStartPos.Tab() )
763         return DataPilotTablePositionType::NOT_IN_TABLE;
764 
765     CalcSizes();
766 
767     // Make sure the cursor is within the table.
768     if (nCol < nTabStartCol || nRow < nTabStartRow || nCol > nTabEndCol || nRow > nTabEndRow)
769         return DataPilotTablePositionType::NOT_IN_TABLE;
770 
771     // test for result data area.
772     if (nCol >= nDataStartCol && nCol <= nTabEndCol && nRow >= nDataStartRow && nRow <= nTabEndRow)
773         return DataPilotTablePositionType::RESULT;
774 
775     bool bInColHeader = (nRow >= nTabStartRow && nRow < nDataStartRow);
776     bool bInRowHeader = (nCol >= nTabStartCol && nCol < nDataStartCol);
777 
778     if (bInColHeader && bInRowHeader)
779         // probably in that ugly little box at the upper-left corner of the table.
780         return DataPilotTablePositionType::OTHER;
781 
782     if (bInColHeader)
783     {
784         if (nRow == nTabStartRow)
785             // first row in the column header area is always used for column
786             // field buttons.
787             return DataPilotTablePositionType::OTHER;
788 
789         return DataPilotTablePositionType::COLUMN_HEADER;
790     }
791 
792     if (bInRowHeader)
793         return DataPilotTablePositionType::ROW_HEADER;
794 
795     return DataPilotTablePositionType::OTHER;
796 }
797 
798 void ScDPOutput::Output()
799 {
800     long nField;
801     SCTAB nTab = aStartPos.Tab();
802     const uno::Sequence<sheet::DataResult>* pRowAry = aData.getConstArray();
803 
804     //  calculate output positions and sizes
805 
806     CalcSizes();
807     if ( bSizeOverflow || bResultsError )   // does output area exceed sheet limits?
808         return;                             // nothing
809 
810     //  clear whole (new) output area
811     //! when modifying table, clear old area
812     //! include IDF_OBJECTS ???
813     pDoc->DeleteAreaTab( aStartPos.Col(), aStartPos.Row(), nTabEndCol, nTabEndRow, nTab, IDF_ALL );
814 
815     if ( bDoFilter )
816         lcl_DoFilterButton( pDoc, aStartPos.Col(), aStartPos.Row(), nTab );
817 
818     //  output data results:
819 
820     for (long nRow=0; nRow<nRowCount; nRow++)
821     {
822         SCROW nRowPos = nDataStartRow + (SCROW)nRow;                    //! check for overflow
823         const sheet::DataResult* pColAry = pRowAry[nRow].getConstArray();
824         long nThisColCount = pRowAry[nRow].getLength();
825         DBG_ASSERT( nThisColCount == nColCount, "count mismatch" );     //! ???
826         for (long nCol=0; nCol<nThisColCount; nCol++)
827         {
828             SCCOL nColPos = nDataStartCol + (SCCOL)nCol;                //! check for overflow
829             DataCell( nColPos, nRowPos, nTab, pColAry[nCol] );
830         }
831     }
832     //  output page fields:
833 
834     for (nField=0; nField<nPageFieldCount; nField++)
835     {
836         SCCOL nHdrCol = aStartPos.Col();
837         SCROW nHdrRow = aStartPos.Row() + nField + ( bDoFilter ? 1 : 0 );
838         // draw without frame for consistency with filter button:
839         FieldCell( nHdrCol, nHdrRow, nTab, pPageFields[nField].aCaption, false, false, pPageFields[nField].mbHasHiddenMember );
840         SCCOL nFldCol = nHdrCol + 1;
841 
842         String aPageValue;
843         if ( pPageFields[nField].aResult.getLength() == 1 )
844             aPageValue = pPageFields[nField].aResult[0].Caption;
845         else
846             aPageValue = String( ScResId( SCSTR_ALL ) );        //! separate string?
847 
848         pDoc->SetString( nFldCol, nHdrRow, nTab, aPageValue );
849 
850         lcl_SetFrame( pDoc,nTab, nFldCol,nHdrRow, nFldCol,nHdrRow, 20 );
851         pDoc->ApplyAttr( nFldCol, nHdrRow, nTab, ScMergeFlagAttr(SC_MF_AUTO) );
852         //! which style?
853     }
854 
855     //  data description
856     //  (may get overwritten by first row field)
857 
858     String aDesc = aDataDescription;
859     if ( !aDesc.Len() )
860     {
861         //! use default string ("result") ?
862     }
863     pDoc->SetString( nTabStartCol, nTabStartRow, nTab, aDesc );
864 
865     //  set STR_PIVOT_STYLE_INNER for whole data area (subtotals are overwritten)
866 
867     if ( nDataStartRow > nTabStartRow )
868         lcl_SetStyleById( pDoc, nTab, nTabStartCol, nTabStartRow, nTabEndCol, nDataStartRow-1,
869                             STR_PIVOT_STYLE_TOP );
870     lcl_SetStyleById( pDoc, nTab, nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow,
871                         STR_PIVOT_STYLE_INNER );
872 
873     //  output column headers:
874     // Wang Xu Ming -- 2009-8-17
875     // DataPilot Migration - Cache&&Performance
876     OutputImpl outputimp( pDoc, nTab,
877         nTabStartCol, nTabStartRow, nMemberStartCol, nMemberStartRow,
878         nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow );
879     // End Comments
880     for (nField=0; nField<nColFieldCount; nField++)
881     {
882         SCCOL nHdrCol = nDataStartCol + (SCCOL)nField;              //! check for overflow
883         FieldCell( nHdrCol, nTabStartRow, nTab, pColFields[nField].aCaption, true, true, pColFields[nField].mbHasHiddenMember );
884 
885         SCROW nRowPos = nMemberStartRow + (SCROW)nField;                //! check for overflow
886         const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nField].aResult;
887         const sheet::MemberResult* pArray = rSequence.getConstArray();
888         long nThisColCount = rSequence.getLength();
889         DBG_ASSERT( nThisColCount == nColCount, "count mismatch" );     //! ???
890         for (long nCol=0; nCol<nThisColCount; nCol++)
891         {
892             SCCOL nColPos = nDataStartCol + (SCCOL)nCol;                //! check for overflow
893             HeaderCell( nColPos, nRowPos, nTab, pArray[nCol], sal_True, nField );
894             // Wang Xu Ming -- 2009-8-17
895             // DataPilot Migration - Cache&&Performance
896             if ( ( pArray[nCol].Flags & sheet::MemberResultFlags::HASMEMBER ) &&
897                 !( pArray[nCol].Flags & sheet::MemberResultFlags::SUBTOTAL ) )
898             {
899                 long nEnd = nCol;
900                 while ( nEnd+1 < nThisColCount && ( pArray[nEnd+1].Flags & sheet::MemberResultFlags::CONTINUE ) )
901                     ++nEnd;
902                 SCCOL nEndColPos = nDataStartCol + (SCCOL)nEnd;     //! check for overflow
903                 if ( nField+1 < nColFieldCount )
904                 {
905                     //                  lcl_SetFrame( pDoc,nTab, nColPos,nRowPos, nEndColPos,nRowPos, SC_DP_FRAME_INNER_BOLD );
906                     //                  lcl_SetFrame( pDoc,nTab, nColPos,nRowPos, nEndColPos,nTabEndRow, SC_DP_FRAME_INNER_BOLD );
907                     if ( nField == nColFieldCount - 2 )
908                     {
909                         outputimp.AddCol( nColPos );
910                         if ( nColPos + 1 == nEndColPos  )
911                             outputimp.OutputBlockFrame( nColPos,nRowPos, nEndColPos,nRowPos+1, sal_True );
912                     }
913                     else
914                         outputimp.OutputBlockFrame( nColPos,nRowPos, nEndColPos,nRowPos );
915 
916                     lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nEndColPos,nDataStartRow-1, STR_PIVOT_STYLE_CATEGORY );
917                 }
918                 else
919                     lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nColPos,nDataStartRow-1, STR_PIVOT_STYLE_CATEGORY );
920             }
921             else if (  pArray[nCol].Flags & sheet::MemberResultFlags::SUBTOTAL )
922                 outputimp.AddCol( nColPos );
923         }
924         if ( nField== 0 && nColFieldCount == 1 )
925             outputimp.OutputBlockFrame( nDataStartCol,nTabStartRow, nTabEndCol,nRowPos-1 );
926             // End Comments
927     }
928 
929     //  output row headers:
930     std::vector<sal_Bool> vbSetBorder;
931     vbSetBorder.resize( nTabEndRow - nDataStartRow + 1, sal_False );
932     for (nField=0; nField<nRowFieldCount; nField++)
933     {
934         bool bDataLayout = mbHasDataLayout && (nField == nRowFieldCount-1);
935 
936         SCCOL nHdrCol = nTabStartCol + (SCCOL)nField;                   //! check for overflow
937         SCROW nHdrRow = nDataStartRow - 1;
938         FieldCell( nHdrCol, nHdrRow, nTab, pRowFields[nField].aCaption, true, !bDataLayout,
939                    pRowFields[nField].mbHasHiddenMember );
940 
941         SCCOL nColPos = nMemberStartCol + (SCCOL)nField;                //! check for overflow
942         const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].aResult;
943         const sheet::MemberResult* pArray = rSequence.getConstArray();
944         long nThisRowCount = rSequence.getLength();
945         DBG_ASSERT( nThisRowCount == nRowCount, "count mismatch" );     //! ???
946         for (long nRow=0; nRow<nThisRowCount; nRow++)
947         {
948             SCROW nRowPos = nDataStartRow + (SCROW)nRow;                //! check for overflow
949             HeaderCell( nColPos, nRowPos, nTab, pArray[nRow], sal_False, nField );
950             if ( ( pArray[nRow].Flags & sheet::MemberResultFlags::HASMEMBER ) &&
951                 !( pArray[nRow].Flags & sheet::MemberResultFlags::SUBTOTAL ) )
952             {
953                 if ( nField+1 < nRowFieldCount )
954                 {
955                     long nEnd = nRow;
956                     while ( nEnd+1 < nThisRowCount && ( pArray[nEnd+1].Flags & sheet::MemberResultFlags::CONTINUE ) )
957                         ++nEnd;
958                     SCROW nEndRowPos = nDataStartRow + (SCROW)nEnd;     //! check for overflow
959                     // Wang Xu Ming -- 2009-8-17
960                     // DataPilot Migration - Cache&&Performance
961                     //  lcl_SetFrame( pDoc,nTab, nColPos,nRowPos, nColPos,nEndRowPos, SC_DP_FRAME_INNER_BOLD );
962                     //lcl_SetFrame( pDoc,nTab, nColPos,nRowPos, nTabEndCol,nEndRowPos, SC_DP_FRAME_INNER_BOLD );
963                     outputimp.AddRow( nRowPos );
964                     if ( vbSetBorder[ nRow ] == sal_False )
965                     {
966                         outputimp.OutputBlockFrame( nColPos, nRowPos, nTabEndCol, nEndRowPos );
967                         vbSetBorder[ nRow ]  = sal_True;
968                     }
969                     outputimp.OutputBlockFrame( nColPos, nRowPos, nColPos, nEndRowPos );
970 
971                     if ( nField == nRowFieldCount - 2 )
972                         outputimp.OutputBlockFrame( nColPos+1, nRowPos, nColPos+1, nEndRowPos );
973                     // End Comments
974 
975                     lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nDataStartCol-1,nEndRowPos, STR_PIVOT_STYLE_CATEGORY );
976                 }
977                 else
978                     lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nDataStartCol-1,nRowPos, STR_PIVOT_STYLE_CATEGORY );
979             }
980             // Wang Xu Ming -- 2009-8-17
981             // DataPilot Migration - Cache&&Performance
982             else if (  pArray[nRow].Flags & sheet::MemberResultFlags::SUBTOTAL )
983                 outputimp.AddRow( nRowPos );
984             // End Comments
985         }
986     }
987 
988 // Wang Xu Ming -- 2009-8-17
989 // DataPilot Migration - Cache&&Performance
990     outputimp.OutputDataArea();
991 // End Comments
992 }
993 
994 ScRange ScDPOutput::GetOutputRange( sal_Int32 nRegionType )
995 {
996     using namespace ::com::sun::star::sheet;
997 
998     CalcSizes();
999 
1000 //  fprintf(stdout, "ScDPOutput::GetOutputRange: aStartPos = (%ld, %d)\n", aStartPos.Row(), aStartPos.Col());fflush(stdout);
1001 //  fprintf(stdout, "ScDPOutput::GetOutputRange: nTabStart (Row = %ld, Col = %ld)\n", nTabStartRow, nTabStartCol);fflush(stdout);
1002 //  fprintf(stdout, "ScDPOutput::GetOutputRange: nMemberStart (Row = %ld, Col = %ld)\n", nMemberStartRow, nMemberStartCol);fflush(stdout);
1003 //  fprintf(stdout, "ScDPOutput::GetOutputRange: nDataStart (Row = %ld, Col = %ld)\n", nDataStartRow, nDataStartCol);fflush(stdout);
1004 //  fprintf(stdout, "ScDPOutput::GetOutputRange: nTabEnd (Row = %ld, Col = %ld)\n", nTabEndRow, nTabStartCol);fflush(stdout);
1005 
1006     SCTAB nTab = aStartPos.Tab();
1007     switch (nRegionType)
1008     {
1009         case DataPilotOutputRangeType::RESULT:
1010             return ScRange(nDataStartCol, nDataStartRow, nTab, nTabEndCol, nTabEndRow, nTab);
1011         case DataPilotOutputRangeType::TABLE:
1012             return ScRange(aStartPos.Col(), nTabStartRow, nTab, nTabEndCol, nTabEndRow, nTab);
1013         default:
1014             DBG_ASSERT(nRegionType == DataPilotOutputRangeType::WHOLE, "ScDPOutput::GetOutputRange: unknown region type");
1015         break;
1016     }
1017     return ScRange(aStartPos.Col(), aStartPos.Row(), nTab, nTabEndCol, nTabEndRow, nTab);
1018 }
1019 
1020 sal_Bool ScDPOutput::HasError()
1021 {
1022     CalcSizes();
1023 
1024     return bSizeOverflow || bResultsError;
1025 }
1026 
1027 long ScDPOutput::GetHeaderRows()
1028 {
1029     return nPageFieldCount + ( bDoFilter ? 1 : 0 );
1030 }
1031 
1032 void ScDPOutput::GetMemberResultNames( ScStrCollection& rNames, long nDimension )
1033 {
1034     //  Return the list of all member names in a dimension's MemberResults.
1035     //  Only the dimension has to be compared because this is only used with table data,
1036     //  where each dimension occurs only once.
1037 
1038     uno::Sequence<sheet::MemberResult> aMemberResults;
1039     bool bFound = false;
1040     long nField;
1041 
1042     // look in column fields
1043 
1044     for (nField=0; nField<nColFieldCount && !bFound; nField++)
1045         if ( pColFields[nField].nDim == nDimension )
1046         {
1047             aMemberResults = pColFields[nField].aResult;
1048             bFound = true;
1049         }
1050 
1051     // look in row fields
1052 
1053     for (nField=0; nField<nRowFieldCount && !bFound; nField++)
1054         if ( pRowFields[nField].nDim == nDimension )
1055         {
1056             aMemberResults = pRowFields[nField].aResult;
1057             bFound = true;
1058         }
1059 
1060     // collect the member names
1061 
1062     if ( bFound )
1063     {
1064         const sheet::MemberResult* pArray = aMemberResults.getConstArray();
1065         long nResultCount = aMemberResults.getLength();
1066 
1067         for (long nItem=0; nItem<nResultCount; nItem++)
1068         {
1069             if ( pArray[nItem].Flags & sheet::MemberResultFlags::HASMEMBER )
1070             {
1071                 StrData* pNew = new StrData( pArray[nItem].Name );
1072                 if ( !rNames.Insert( pNew ) )
1073                     delete pNew;
1074             }
1075         }
1076     }
1077 }
1078 
1079 void ScDPOutput::SetHeaderLayout(bool bUseGrid)
1080 {
1081     mbHeaderLayout = bUseGrid;
1082     bSizesValid = false;
1083 }
1084 
1085 bool ScDPOutput::GetHeaderLayout() const
1086 {
1087     return mbHeaderLayout;
1088 }
1089 
1090 void lcl_GetTableVars( sal_Int32& rGrandTotalCols, sal_Int32& rGrandTotalRows, sal_Int32& rDataLayoutIndex,
1091                              std::vector<String>& rDataNames, std::vector<String>& rGivenNames,
1092                              sheet::DataPilotFieldOrientation& rDataOrient,
1093                              const uno::Reference<sheet::XDimensionsSupplier>& xSource )
1094 {
1095     rDataLayoutIndex = -1;  // invalid
1096     rGrandTotalCols = 0;
1097     rGrandTotalRows = 0;
1098     rDataOrient = sheet::DataPilotFieldOrientation_HIDDEN;
1099 
1100     uno::Reference<beans::XPropertySet> xSrcProp( xSource, uno::UNO_QUERY );
1101     sal_Bool bColGrand = ScUnoHelpFunctions::GetBoolProperty( xSrcProp,
1102                                          rtl::OUString::createFromAscii(DP_PROP_COLUMNGRAND) );
1103     if ( bColGrand )
1104         rGrandTotalCols = 1;    // default if data layout not in columns
1105 
1106     sal_Bool bRowGrand = ScUnoHelpFunctions::GetBoolProperty( xSrcProp,
1107                                          rtl::OUString::createFromAscii(DP_PROP_ROWGRAND) );
1108     if ( bRowGrand )
1109         rGrandTotalRows = 1;    // default if data layout not in rows
1110 
1111     if ( xSource.is() )
1112     {
1113         // find index and orientation of "data layout" dimension, count data dimensions
1114 
1115         sal_Int32 nDataCount = 0;
1116 
1117         uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xSource->getDimensions() );
1118         long nDimCount = xDims->getCount();
1119         for (long nDim=0; nDim<nDimCount; nDim++)
1120         {
1121             uno::Reference<uno::XInterface> xDim =
1122                     ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) );
1123             uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
1124             if ( xDimProp.is() )
1125             {
1126                 sheet::DataPilotFieldOrientation eDimOrient =
1127                     (sheet::DataPilotFieldOrientation) ScUnoHelpFunctions::GetEnumProperty(
1128                         xDimProp, rtl::OUString::createFromAscii(DP_PROP_ORIENTATION),
1129                         sheet::DataPilotFieldOrientation_HIDDEN );
1130                 if ( ScUnoHelpFunctions::GetBoolProperty( xDimProp,
1131                                          rtl::OUString::createFromAscii(DP_PROP_ISDATALAYOUT) ) )
1132                 {
1133                     rDataLayoutIndex = nDim;
1134                     rDataOrient = eDimOrient;
1135                 }
1136                 if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
1137                 {
1138                     String aSourceName;
1139                     String aGivenName;
1140                     ScDPOutput::GetDataDimensionNames( aSourceName, aGivenName, xDim );
1141                     rDataNames.push_back( aSourceName );
1142                     rGivenNames.push_back( aGivenName );
1143 
1144                     ++nDataCount;
1145                 }
1146             }
1147         }
1148 
1149         if ( ( rDataOrient == sheet::DataPilotFieldOrientation_COLUMN ) && bColGrand )
1150             rGrandTotalCols = nDataCount;
1151         else if ( ( rDataOrient == sheet::DataPilotFieldOrientation_ROW ) && bRowGrand )
1152             rGrandTotalRows = nDataCount;
1153     }
1154 }
1155 
1156 void ScDPOutput::GetPositionData(const ScAddress& rPos, DataPilotTablePositionData& rPosData)
1157 {
1158     using namespace ::com::sun::star::sheet;
1159 
1160     SCCOL nCol = rPos.Col();
1161     SCROW nRow = rPos.Row();
1162     SCTAB nTab = rPos.Tab();
1163     if ( nTab != aStartPos.Tab() )
1164         return;                                     // wrong sheet
1165 
1166     //  calculate output positions and sizes
1167 
1168     CalcSizes();
1169 
1170     rPosData.PositionType = GetPositionType(rPos);
1171     switch (rPosData.PositionType)
1172     {
1173         case DataPilotTablePositionType::RESULT:
1174         {
1175             vector<DataPilotFieldFilter> aFilters;
1176             GetDataResultPositionData(aFilters, rPos);
1177             sal_Int32 nSize = aFilters.size();
1178 
1179             DataPilotTableResultData aResData;
1180             aResData.FieldFilters.realloc(nSize);
1181             for (sal_Int32 i = 0; i < nSize; ++i)
1182                 aResData.FieldFilters[i] = aFilters[i];
1183 
1184             aResData.DataFieldIndex = 0;
1185             Reference<beans::XPropertySet> xPropSet(xSource, UNO_QUERY);
1186             if (xPropSet.is())
1187             {
1188                 sal_Int32 nDataFieldCount = ScUnoHelpFunctions::GetLongProperty( xPropSet,
1189                                             rtl::OUString::createFromAscii(SC_UNO_DATAFIELDCOUNT) );
1190                 if (nDataFieldCount > 0)
1191                     aResData.DataFieldIndex = (nRow - nDataStartRow) % nDataFieldCount;
1192             }
1193 
1194             // Copy appropriate DataResult object from the cached sheet::DataResult table.
1195             if (aData.getLength() > nRow - nDataStartRow &&
1196                 aData[nRow-nDataStartRow].getLength() > nCol-nDataStartCol)
1197                 aResData.Result = aData[nRow-nDataStartRow][nCol-nDataStartCol];
1198 
1199             rPosData.PositionData = makeAny(aResData);
1200             return;
1201         }
1202         case DataPilotTablePositionType::COLUMN_HEADER:
1203         {
1204             long nField = nRow - nTabStartRow - 1; // 1st line is used for the buttons
1205             if (nField < 0)
1206                 break;
1207 
1208             const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nField].aResult;
1209             if (rSequence.getLength() == 0)
1210                 break;
1211             const sheet::MemberResult* pArray = rSequence.getConstArray();
1212 
1213             long nItem = nCol - nDataStartCol;
1214             //  get origin of "continue" fields
1215             while (nItem > 0 && ( pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1216                 --nItem;
1217 
1218             if (nItem < 0)
1219                 break;
1220 
1221             DataPilotTableHeaderData aHeaderData;
1222             aHeaderData.MemberName = OUString(pArray[nItem].Name);
1223             aHeaderData.Flags = pArray[nItem].Flags;
1224             aHeaderData.Dimension = static_cast<sal_Int32>(pColFields[nField].nDim);
1225             aHeaderData.Hierarchy = static_cast<sal_Int32>(pColFields[nField].nHier);
1226             aHeaderData.Level     = static_cast<sal_Int32>(pColFields[nField].nLevel);
1227 
1228             rPosData.PositionData = makeAny(aHeaderData);
1229             return;
1230         }
1231         case DataPilotTablePositionType::ROW_HEADER:
1232         {
1233             long nField = nCol - nTabStartCol;
1234             if (nField < 0)
1235                 break;
1236 
1237             const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].aResult;
1238             if (rSequence.getLength() == 0)
1239                 break;
1240             const sheet::MemberResult* pArray = rSequence.getConstArray();
1241 
1242             long nItem = nRow - nDataStartRow;
1243             //  get origin of "continue" fields
1244             while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1245                 --nItem;
1246 
1247             if (nItem < 0)
1248                 break;
1249 
1250             DataPilotTableHeaderData aHeaderData;
1251             aHeaderData.MemberName = OUString(pArray[nItem].Name);
1252             aHeaderData.Flags = pArray[nItem].Flags;
1253             aHeaderData.Dimension = static_cast<sal_Int32>(pRowFields[nField].nDim);
1254             aHeaderData.Hierarchy = static_cast<sal_Int32>(pRowFields[nField].nHier);
1255             aHeaderData.Level     = static_cast<sal_Int32>(pRowFields[nField].nLevel);
1256 
1257             rPosData.PositionData = makeAny(aHeaderData);
1258             return;
1259         }
1260     }
1261 }
1262 
1263 bool ScDPOutput::GetDataResultPositionData(vector<sheet::DataPilotFieldFilter>& rFilters, const ScAddress& rPos)
1264 {
1265     // Check to make sure there is at least one data field.
1266     Reference<beans::XPropertySet> xPropSet(xSource, UNO_QUERY);
1267     if (!xPropSet.is())
1268         return false;
1269 
1270     sal_Int32 nDataFieldCount = ScUnoHelpFunctions::GetLongProperty( xPropSet,
1271                                 rtl::OUString::createFromAscii(SC_UNO_DATAFIELDCOUNT) );
1272     if (nDataFieldCount == 0)
1273         // No data field is present in this datapilot table.
1274         return false;
1275 
1276     // #i111421# use lcl_GetTableVars for correct size of totals and data layout position
1277     sal_Int32 nGrandTotalCols;
1278     sal_Int32 nGrandTotalRows;
1279     sal_Int32 nDataLayoutIndex;
1280     std::vector<String> aDataNames;
1281     std::vector<String> aGivenNames;
1282     sheet::DataPilotFieldOrientation eDataOrient;
1283     lcl_GetTableVars( nGrandTotalCols, nGrandTotalRows, nDataLayoutIndex, aDataNames, aGivenNames, eDataOrient, xSource );
1284 
1285     SCCOL nCol = rPos.Col();
1286     SCROW nRow = rPos.Row();
1287     SCTAB nTab = rPos.Tab();
1288     if ( nTab != aStartPos.Tab() )
1289         return false;                                     // wrong sheet
1290 
1291     CalcSizes();
1292 
1293     // test for data area.
1294     if (nCol < nDataStartCol || nCol > nTabEndCol || nRow < nDataStartRow || nRow > nTabEndRow)
1295     {
1296         // Cell is outside the data field area.
1297         return false;
1298     }
1299 
1300     bool bFilterByCol = (nCol <= static_cast<SCCOL>(nTabEndCol - nGrandTotalCols));
1301     bool bFilterByRow = (nRow <= static_cast<SCROW>(nTabEndRow - nGrandTotalRows));
1302 
1303     // column fields
1304     for (SCCOL nColField = 0; nColField < nColFieldCount && bFilterByCol; ++nColField)
1305     {
1306         if (pColFields[nColField].nDim == nDataLayoutIndex)
1307             // There is no sense including the data layout field for filtering.
1308             continue;
1309 
1310         sheet::DataPilotFieldFilter filter;
1311         filter.FieldName = pColFields[nColField].maName;
1312 
1313         const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nColField].aResult;
1314         const sheet::MemberResult* pArray = rSequence.getConstArray();
1315 
1316         DBG_ASSERT(nDataStartCol + rSequence.getLength() - 1 == nTabEndCol, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
1317 
1318         long nItem = nCol - nDataStartCol;
1319                 //  get origin of "continue" fields
1320         while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1321             --nItem;
1322 
1323         filter.MatchValue = pArray[nItem].Name;
1324         rFilters.push_back(filter);
1325     }
1326 
1327     // row fields
1328     for (SCROW nRowField = 0; nRowField < nRowFieldCount && bFilterByRow; ++nRowField)
1329     {
1330         if (pRowFields[nRowField].nDim == nDataLayoutIndex)
1331             // There is no sense including the data layout field for filtering.
1332             continue;
1333 
1334         sheet::DataPilotFieldFilter filter;
1335         filter.FieldName = pRowFields[nRowField].maName;
1336 
1337         const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nRowField].aResult;
1338         const sheet::MemberResult* pArray = rSequence.getConstArray();
1339 
1340         DBG_ASSERT(nDataStartRow + rSequence.getLength() - 1 == nTabEndRow, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
1341 
1342         long nItem = nRow - nDataStartRow;
1343             //  get origin of "continue" fields
1344         while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1345             --nItem;
1346 
1347         filter.MatchValue = pArray[nItem].Name;
1348         rFilters.push_back(filter);
1349     }
1350 
1351     return true;
1352 }
1353 
1354 //
1355 //  helper functions for ScDPOutput::GetPivotData
1356 //
1357 
1358 bool lcl_IsNamedDataField( const ScDPGetPivotDataField& rTarget, const String& rSourceName, const String& rGivenName )
1359 {
1360     // match one of the names, ignoring case
1361     return ScGlobal::GetpTransliteration()->isEqual( rTarget.maFieldName, rSourceName ) ||
1362            ScGlobal::GetpTransliteration()->isEqual( rTarget.maFieldName, rGivenName );
1363 }
1364 
1365 bool lcl_IsNamedCategoryField( const ScDPGetPivotDataField& rFilter, const ScDPOutLevelData& rField )
1366 {
1367     return ScGlobal::GetpTransliteration()->isEqual( rFilter.maFieldName, rField.maName );
1368 }
1369 
1370 bool lcl_IsCondition( const sheet::MemberResult& rResultEntry, const ScDPGetPivotDataField& rFilter )
1371 {
1372     //! handle numeric conditions?
1373     return ScGlobal::GetpTransliteration()->isEqual( rResultEntry.Name, rFilter.maValStr );
1374 }
1375 
1376 bool lcl_CheckPageField( const ScDPOutLevelData& rField,
1377                         const std::vector< ScDPGetPivotDataField >& rFilters,
1378                         std::vector< sal_Bool >& rFilterUsed )
1379 {
1380     for (SCSIZE nFilterPos = 0; nFilterPos < rFilters.size(); ++nFilterPos)
1381     {
1382         if ( lcl_IsNamedCategoryField( rFilters[nFilterPos], rField ) )
1383         {
1384             rFilterUsed[nFilterPos] = sal_True;
1385 
1386             // page field result is empty or the selection as single entry (see lcl_GetSelectedPageAsResult)
1387             if ( rField.aResult.getLength() == 1 &&
1388                  lcl_IsCondition( rField.aResult[0], rFilters[nFilterPos] ) )
1389             {
1390                 return true;        // condition matches page selection
1391             }
1392             else
1393             {
1394                 return false;       // no page selection or different entry
1395             }
1396         }
1397     }
1398 
1399     return true;    // valid if the page field doesn't have a filter
1400 }
1401 
1402 uno::Sequence<sheet::GeneralFunction> lcl_GetSubTotals(
1403         const uno::Reference<sheet::XDimensionsSupplier>& xSource, const ScDPOutLevelData& rField )
1404 {
1405     uno::Sequence<sheet::GeneralFunction> aSubTotals;
1406 
1407     uno::Reference<sheet::XHierarchiesSupplier> xHierSupp;
1408     uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
1409     uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
1410     sal_Int32 nIntCount = xIntDims->getCount();
1411     if ( rField.nDim < nIntCount )
1412     {
1413         uno::Reference<uno::XInterface> xIntDim = ScUnoHelpFunctions::AnyToInterface(
1414                                     xIntDims->getByIndex( rField.nDim ) );
1415         xHierSupp = uno::Reference<sheet::XHierarchiesSupplier>( xIntDim, uno::UNO_QUERY );
1416     }
1417     DBG_ASSERT( xHierSupp.is(), "dimension not found" );
1418 
1419     sal_Int32 nHierCount = 0;
1420     uno::Reference<container::XIndexAccess> xHiers;
1421     if ( xHierSupp.is() )
1422     {
1423         uno::Reference<container::XNameAccess> xHiersName = xHierSupp->getHierarchies();
1424         xHiers = new ScNameToIndexAccess( xHiersName );
1425         nHierCount = xHiers->getCount();
1426     }
1427     uno::Reference<uno::XInterface> xHier;
1428     if ( rField.nHier < nHierCount )
1429         xHier = ScUnoHelpFunctions::AnyToInterface( xHiers->getByIndex( rField.nHier ) );
1430     DBG_ASSERT( xHier.is(), "hierarchy not found" );
1431 
1432     sal_Int32 nLevCount = 0;
1433     uno::Reference<container::XIndexAccess> xLevels;
1434     uno::Reference<sheet::XLevelsSupplier> xLevSupp( xHier, uno::UNO_QUERY );
1435     if ( xLevSupp.is() )
1436     {
1437         uno::Reference<container::XNameAccess> xLevsName = xLevSupp->getLevels();
1438         xLevels = new ScNameToIndexAccess( xLevsName );
1439         nLevCount = xLevels->getCount();
1440     }
1441     uno::Reference<uno::XInterface> xLevel;
1442     if ( rField.nLevel < nLevCount )
1443         xLevel = ScUnoHelpFunctions::AnyToInterface( xLevels->getByIndex( rField.nLevel ) );
1444     DBG_ASSERT( xLevel.is(), "level not found" );
1445 
1446     uno::Reference<beans::XPropertySet> xLevelProp( xLevel, uno::UNO_QUERY );
1447     if ( xLevelProp.is() )
1448     {
1449         try
1450         {
1451             uno::Any aValue = xLevelProp->getPropertyValue( rtl::OUString::createFromAscii(DP_PROP_SUBTOTALS) );
1452             aValue >>= aSubTotals;
1453         }
1454         catch(uno::Exception&)
1455         {
1456         }
1457     }
1458 
1459     return aSubTotals;
1460 }
1461 
1462 void lcl_FilterInclude( std::vector< sal_Bool >& rResult, std::vector< sal_Int32 >& rSubtotal,
1463                         const ScDPOutLevelData& rField,
1464                         const std::vector< ScDPGetPivotDataField >& rFilters,
1465                         std::vector< sal_Bool >& rFilterUsed,
1466                         bool& rBeforeDataLayout,
1467                         sal_Int32 nGrandTotals, sal_Int32 nDataLayoutIndex,
1468                         const std::vector<String>& rDataNames, const std::vector<String>& rGivenNames,
1469                         const ScDPGetPivotDataField& rTarget, const uno::Reference<sheet::XDimensionsSupplier>& xSource )
1470 {
1471     // returns true if a filter was given for the field
1472 
1473     DBG_ASSERT( rFilters.size() == rFilterUsed.size(), "wrong size" );
1474 
1475     const bool bIsDataLayout = ( rField.nDim == nDataLayoutIndex );
1476     if (bIsDataLayout)
1477         rBeforeDataLayout = false;
1478 
1479     bool bHasFilter = false;
1480     ScDPGetPivotDataField aFilter;
1481     if ( !bIsDataLayout )          // selection of data field is handled separately
1482     {
1483         for (SCSIZE nFilterPos = 0; nFilterPos < rFilters.size() && !bHasFilter; ++nFilterPos)
1484         {
1485             if ( lcl_IsNamedCategoryField( rFilters[nFilterPos], rField ) )
1486             {
1487                 aFilter = rFilters[nFilterPos];
1488                 rFilterUsed[nFilterPos] = sal_True;
1489                 bHasFilter = true;
1490             }
1491         }
1492     }
1493 
1494     bool bHasFunc = bHasFilter && aFilter.meFunction != sheet::GeneralFunction_NONE;
1495 
1496     uno::Sequence<sheet::GeneralFunction> aSubTotals;
1497     if ( !bIsDataLayout )
1498         aSubTotals = lcl_GetSubTotals( xSource, rField );
1499     bool bManualSub = ( aSubTotals.getLength() > 0 && aSubTotals[0] != sheet::GeneralFunction_AUTO );
1500 
1501     const uno::Sequence<sheet::MemberResult>& rSequence = rField.aResult;
1502     const sheet::MemberResult* pArray = rSequence.getConstArray();
1503     sal_Int32 nSize = rSequence.getLength();
1504 
1505     DBG_ASSERT( (sal_Int32)rResult.size() == nSize, "Number of fields do not match result count" );
1506 
1507     sal_Int32 nContCount = 0;
1508     sal_Int32 nSubTotalCount = 0;
1509     sheet::MemberResult aPrevious;
1510     for( sal_Int32 j=0; j < nSize; j++ )
1511     {
1512         sheet::MemberResult aResultEntry = pArray[j];
1513         if ( aResultEntry.Flags & sheet::MemberResultFlags::CONTINUE )
1514         {
1515             aResultEntry = aPrevious;
1516             ++nContCount;
1517         }
1518         else if ( ( aResultEntry.Flags & sheet::MemberResultFlags::SUBTOTAL ) == 0 )
1519         {
1520             // count the CONTINUE entries before a SUBTOTAL
1521             nContCount = 0;
1522         }
1523 
1524         if ( j >= nSize - nGrandTotals )
1525         {
1526             // mark as subtotal for the preceding data
1527             if ( ( aResultEntry.Flags & sheet::MemberResultFlags::SUBTOTAL ) != 0 )
1528             {
1529                 rSubtotal[j] = nSize - nGrandTotals;
1530 
1531                 if ( rResult[j] && nGrandTotals > 1 )
1532                 {
1533                     // grand total is always automatic
1534                     sal_Int32 nDataPos = j - ( nSize - nGrandTotals );
1535                     DBG_ASSERT( nDataPos < (sal_Int32)rDataNames.size(), "wrong data count" );
1536                     String aSourceName( rDataNames[nDataPos] );     // vector contains source names
1537                     String aGivenName( rGivenNames[nDataPos] );
1538 
1539                     rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName );
1540                 }
1541             }
1542 
1543             // treat "grand total" columns/rows as empty description, as if they were marked
1544             // in a previous field
1545 
1546             DBG_ASSERT( ( aResultEntry.Flags &
1547                             ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL ) ) == 0 ||
1548                         ( aResultEntry.Flags &
1549                             ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL ) ) ==
1550                                 ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL ),
1551                         "non-subtotal member found in grand total result" );
1552             aResultEntry.Flags = 0;
1553         }
1554 
1555         // mark subtotals (not grand total) for preceding data (assume CONTINUE is set)
1556         if ( ( aResultEntry.Flags & sheet::MemberResultFlags::SUBTOTAL ) != 0 )
1557         {
1558             rSubtotal[j] = nContCount + 1 + nSubTotalCount;
1559 
1560             if ( rResult[j] )
1561             {
1562                 if ( bManualSub )
1563                 {
1564                     if ( rBeforeDataLayout )
1565                     {
1566                         // manual subtotals and several data fields
1567 
1568                         sal_Int32 nDataCount = rDataNames.size();
1569                         sal_Int32 nFuncPos = nSubTotalCount / nDataCount;       // outer order: subtotal functions
1570                         sal_Int32 nDataPos = nSubTotalCount % nDataCount;       // inner order: data fields
1571 
1572                         String aSourceName( rDataNames[nDataPos] );             // vector contains source names
1573                         String aGivenName( rGivenNames[nDataPos] );
1574 
1575                         DBG_ASSERT( nFuncPos < aSubTotals.getLength(), "wrong subtotal count" );
1576                         rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName ) &&
1577                                      aSubTotals[nFuncPos] == aFilter.meFunction;
1578                     }
1579                     else
1580                     {
1581                         // manual subtotals for a single data field
1582 
1583                         DBG_ASSERT( nSubTotalCount < aSubTotals.getLength(), "wrong subtotal count" );
1584                         rResult[j] = ( aSubTotals[nSubTotalCount] == aFilter.meFunction );
1585                     }
1586                 }
1587                 else    // automatic subtotals
1588                 {
1589                     if ( rBeforeDataLayout )
1590                     {
1591                         DBG_ASSERT( nSubTotalCount < (sal_Int32)rDataNames.size(), "wrong data count" );
1592                         String aSourceName( rDataNames[nSubTotalCount] );       // vector contains source names
1593                         String aGivenName( rGivenNames[nSubTotalCount] );
1594 
1595                         rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName );
1596                     }
1597 
1598                     // if a function was specified, automatic subtotals never match
1599                     if ( bHasFunc )
1600                         rResult[j] = sal_False;
1601                 }
1602             }
1603 
1604             ++nSubTotalCount;
1605         }
1606         else
1607             nSubTotalCount = 0;
1608 
1609         if( rResult[j] )
1610         {
1611             if ( bIsDataLayout )
1612             {
1613                 if ( ( aResultEntry.Flags & sheet::MemberResultFlags::HASMEMBER ) != 0 )
1614                 {
1615                     // Asterisks are added in ScDPSaveData::WriteToSource to create unique names.
1616                     //! preserve original name there?
1617                     String aSourceName( aResultEntry.Name );
1618                     aSourceName.EraseTrailingChars( '*' );
1619 
1620                     String aGivenName( aResultEntry.Caption );  //! Should use a stored name when available
1621                     aGivenName.EraseLeadingChars( '\'' );
1622 
1623                     rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName );
1624                 }
1625             }
1626             else if ( bHasFilter )
1627             {
1628                 // name must match (simple value or subtotal)
1629                 rResult[j] = ( ( aResultEntry.Flags & sheet::MemberResultFlags::HASMEMBER ) != 0 ) &&
1630                              lcl_IsCondition( aResultEntry, aFilter );
1631 
1632                 // if a function was specified, simple (non-subtotal) values never match
1633                 if ( bHasFunc && nSubTotalCount == 0 )
1634                     rResult[j] = sal_False;
1635             }
1636             // if no condition is given, keep the columns/rows included
1637         }
1638         aPrevious = aResultEntry;
1639     }
1640 }
1641 
1642 void lcl_StripSubTotals( std::vector< sal_Bool >& rResult, const std::vector< sal_Int32 >& rSubtotal )
1643 {
1644     sal_Int32 nSize = rResult.size();
1645     DBG_ASSERT( (sal_Int32)rSubtotal.size() == nSize, "sizes don't match" );
1646 
1647     for (sal_Int32 nPos=0; nPos<nSize; nPos++)
1648         if ( rResult[nPos] && rSubtotal[nPos] )
1649         {
1650             // if a subtotal is included, clear the result flag for the columns/rows that the subtotal includes
1651             sal_Int32 nStart = nPos - rSubtotal[nPos];
1652             DBG_ASSERT( nStart >= 0, "invalid subtotal count" );
1653 
1654             for (sal_Int32 nPrev = nStart; nPrev < nPos; nPrev++)
1655                 rResult[nPrev] = sal_False;
1656         }
1657 }
1658 
1659 String lcl_GetDataFieldName( const String& rSourceName, sheet::GeneralFunction eFunc )
1660 {
1661     sal_uInt16 nStrId = 0;
1662     switch ( eFunc )
1663     {
1664         case sheet::GeneralFunction_SUM:        nStrId = STR_FUN_TEXT_SUM;      break;
1665         case sheet::GeneralFunction_COUNT:
1666         case sheet::GeneralFunction_COUNTNUMS:  nStrId = STR_FUN_TEXT_COUNT;    break;
1667         case sheet::GeneralFunction_AVERAGE:    nStrId = STR_FUN_TEXT_AVG;      break;
1668         case sheet::GeneralFunction_MAX:        nStrId = STR_FUN_TEXT_MAX;      break;
1669         case sheet::GeneralFunction_MIN:        nStrId = STR_FUN_TEXT_MIN;      break;
1670         case sheet::GeneralFunction_PRODUCT:    nStrId = STR_FUN_TEXT_PRODUCT;  break;
1671         case sheet::GeneralFunction_STDEV:
1672         case sheet::GeneralFunction_STDEVP:     nStrId = STR_FUN_TEXT_STDDEV;   break;
1673         case sheet::GeneralFunction_VAR:
1674         case sheet::GeneralFunction_VARP:       nStrId = STR_FUN_TEXT_VAR;      break;
1675         case sheet::GeneralFunction_NONE:
1676         case sheet::GeneralFunction_AUTO:
1677         default:
1678         {
1679             DBG_ERRORFILE("wrong function");
1680         }
1681     }
1682     if ( !nStrId )
1683         return String();
1684 
1685     String aRet( ScGlobal::GetRscString( nStrId ) );
1686     aRet.AppendAscii(RTL_CONSTASCII_STRINGPARAM( " - " ));
1687     aRet.Append( rSourceName );
1688     return aRet;
1689 }
1690 
1691 // static
1692 void ScDPOutput::GetDataDimensionNames( String& rSourceName, String& rGivenName,
1693                                         const uno::Reference<uno::XInterface>& xDim )
1694 {
1695     uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
1696     uno::Reference<container::XNamed> xDimName( xDim, uno::UNO_QUERY );
1697     if ( xDimProp.is() && xDimName.is() )
1698     {
1699         // Asterisks are added in ScDPSaveData::WriteToSource to create unique names.
1700         //! preserve original name there?
1701         rSourceName = xDimName->getName();
1702         rSourceName.EraseTrailingChars( '*' );
1703 
1704         // Generate "given name" the same way as in dptabres.
1705         //! Should use a stored name when available
1706 
1707         sheet::GeneralFunction eFunc = (sheet::GeneralFunction)ScUnoHelpFunctions::GetEnumProperty(
1708                                 xDimProp, rtl::OUString::createFromAscii(DP_PROP_FUNCTION),
1709                                 sheet::GeneralFunction_NONE );
1710         rGivenName = lcl_GetDataFieldName( rSourceName, eFunc );
1711     }
1712 }
1713 
1714 // Returns sal_True on success and stores the result in rTarget
1715 // Returns sal_False if rFilters or rTarget describes something that is not visible
1716 sal_Bool ScDPOutput::GetPivotData( ScDPGetPivotDataField& rTarget,
1717                                const std::vector< ScDPGetPivotDataField >& rFilters )
1718 {
1719     CalcSizes();
1720 
1721     // need to know about grand total columns/rows:
1722     sal_Int32 nGrandTotalCols;
1723     sal_Int32 nGrandTotalRows;
1724     sal_Int32 nDataLayoutIndex;
1725     std::vector<String> aDataNames;
1726     std::vector<String> aGivenNames;
1727     sheet::DataPilotFieldOrientation eDataOrient;
1728     lcl_GetTableVars( nGrandTotalCols, nGrandTotalRows, nDataLayoutIndex, aDataNames, aGivenNames, eDataOrient, xSource );
1729 
1730     if ( aDataNames.empty() )
1731         return sal_False;               // incomplete table without data fields -> no result
1732 
1733     if ( eDataOrient == sheet::DataPilotFieldOrientation_HIDDEN )
1734     {
1735         // no data layout field -> single data field -> must match the selected field in rTarget
1736 
1737         DBG_ASSERT( aDataNames.size() == 1, "several data fields but no data layout field" );
1738         if ( !lcl_IsNamedDataField( rTarget, aDataNames[0], aGivenNames[0] ) )
1739             return sal_False;
1740     }
1741 
1742     std::vector< sal_Bool > aIncludeCol( nColCount, sal_True );
1743     std::vector< sal_Int32 > aSubtotalCol( nColCount, 0 );
1744     std::vector< sal_Bool > aIncludeRow( nRowCount, sal_True );
1745     std::vector< sal_Int32 > aSubtotalRow( nRowCount, 0 );
1746 
1747     std::vector< sal_Bool > aFilterUsed( rFilters.size(), sal_False );
1748 
1749     long nField;
1750     long nCol;
1751     long nRow;
1752     bool bBeforeDataLayout;
1753 
1754     // look in column fields
1755 
1756     bBeforeDataLayout = ( eDataOrient == sheet::DataPilotFieldOrientation_COLUMN );
1757     for (nField=0; nField<nColFieldCount; nField++)
1758         lcl_FilterInclude( aIncludeCol, aSubtotalCol, pColFields[nField], rFilters, aFilterUsed, bBeforeDataLayout,
1759                            nGrandTotalCols, nDataLayoutIndex, aDataNames, aGivenNames, rTarget, xSource );
1760 
1761     // look in row fields
1762 
1763     bBeforeDataLayout = ( eDataOrient == sheet::DataPilotFieldOrientation_ROW );
1764     for (nField=0; nField<nRowFieldCount; nField++)
1765         lcl_FilterInclude( aIncludeRow, aSubtotalRow, pRowFields[nField], rFilters, aFilterUsed, bBeforeDataLayout,
1766                            nGrandTotalRows, nDataLayoutIndex, aDataNames, aGivenNames, rTarget, xSource );
1767 
1768     // page fields
1769 
1770     for (nField=0; nField<nPageFieldCount; nField++)
1771         if ( !lcl_CheckPageField( pPageFields[nField], rFilters, aFilterUsed ) )
1772             return sal_False;
1773 
1774     // all filter fields must be used
1775     for (SCSIZE nFilter=0; nFilter<aFilterUsed.size(); nFilter++)
1776         if (!aFilterUsed[nFilter])
1777             return sal_False;
1778 
1779     lcl_StripSubTotals( aIncludeCol, aSubtotalCol );
1780     lcl_StripSubTotals( aIncludeRow, aSubtotalRow );
1781 
1782     long nColPos = 0;
1783     long nColIncluded = 0;
1784     for (nCol=0; nCol<nColCount; nCol++)
1785         if (aIncludeCol[nCol])
1786         {
1787             nColPos = nCol;
1788             ++nColIncluded;
1789         }
1790 
1791     long nRowPos = 0;
1792     long nRowIncluded = 0;
1793     for (nRow=0; nRow<nRowCount; nRow++)
1794         if (aIncludeRow[nRow])
1795         {
1796             nRowPos = nRow;
1797             ++nRowIncluded;
1798         }
1799 
1800     if ( nColIncluded != 1 || nRowIncluded != 1 )
1801         return sal_False;
1802 
1803     const uno::Sequence<sheet::DataResult>& rDataRow = aData[nRowPos];
1804     if ( nColPos >= rDataRow.getLength() )
1805         return sal_False;
1806 
1807     const sheet::DataResult& rResult = rDataRow[nColPos];
1808     if ( rResult.Flags & sheet::DataResultFlags::ERROR )
1809         return sal_False;                                       //! different error?
1810 
1811     rTarget.mbValIsStr = sal_False;
1812     rTarget.mnValNum = rResult.Value;
1813 
1814     return sal_True;
1815 }
1816 
1817 sal_Bool ScDPOutput::IsFilterButton( const ScAddress& rPos )
1818 {
1819     SCCOL nCol = rPos.Col();
1820     SCROW nRow = rPos.Row();
1821     SCTAB nTab = rPos.Tab();
1822     if ( nTab != aStartPos.Tab() || !bDoFilter )
1823         return sal_False;                               // wrong sheet or no button at all
1824 
1825     //  filter button is at top left
1826     return ( nCol == aStartPos.Col() && nRow == aStartPos.Row() );
1827 }
1828 
1829 long ScDPOutput::GetHeaderDim( const ScAddress& rPos, sal_uInt16& rOrient )
1830 {
1831     SCCOL nCol = rPos.Col();
1832     SCROW nRow = rPos.Row();
1833     SCTAB nTab = rPos.Tab();
1834     if ( nTab != aStartPos.Tab() )
1835         return -1;                                      // wrong sheet
1836 
1837     //  calculate output positions and sizes
1838 
1839     CalcSizes();
1840 
1841     //  test for column header
1842 
1843     if ( nRow == nTabStartRow && nCol >= nDataStartCol && nCol < nDataStartCol + nColFieldCount )
1844     {
1845         rOrient = sheet::DataPilotFieldOrientation_COLUMN;
1846         long nField = nCol - nDataStartCol;
1847         return pColFields[nField].nDim;
1848     }
1849 
1850     //  test for row header
1851 
1852     if ( nRow+1 == nDataStartRow && nCol >= nTabStartCol && nCol < nTabStartCol + nRowFieldCount )
1853     {
1854         rOrient = sheet::DataPilotFieldOrientation_ROW;
1855         long nField = nCol - nTabStartCol;
1856         return pRowFields[nField].nDim;
1857     }
1858 
1859     //  test for page field
1860 
1861     SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 );
1862     if ( nCol == aStartPos.Col() && nRow >= nPageStartRow && nRow < nPageStartRow + nPageFieldCount )
1863     {
1864         rOrient = sheet::DataPilotFieldOrientation_PAGE;
1865         long nField = nRow - nPageStartRow;
1866         return pPageFields[nField].nDim;
1867     }
1868 
1869     //! single data field (?)
1870 
1871     rOrient = sheet::DataPilotFieldOrientation_HIDDEN;
1872     return -1;      // invalid
1873 }
1874 
1875 sal_Bool ScDPOutput::GetHeaderDrag( const ScAddress& rPos, sal_Bool bMouseLeft, sal_Bool bMouseTop,
1876                                 long nDragDim,
1877                                 Rectangle& rPosRect, sal_uInt16& rOrient, long& rDimPos )
1878 {
1879     //  Rectangle instead of ScRange for rPosRect to allow for negative values
1880 
1881     SCCOL nCol = rPos.Col();
1882     SCROW nRow = rPos.Row();
1883     SCTAB nTab = rPos.Tab();
1884     if ( nTab != aStartPos.Tab() )
1885         return sal_False;                                       // wrong sheet
1886 
1887     //  calculate output positions and sizes
1888 
1889     CalcSizes();
1890 
1891     //  test for column header
1892 
1893     if ( nCol >= nDataStartCol && nCol <= nTabEndCol &&
1894             nRow + 1 >= nMemberStartRow && nRow < nMemberStartRow + nColFieldCount )
1895     {
1896         long nField = nRow - nMemberStartRow;
1897         if (nField < 0)
1898         {
1899             nField = 0;
1900             bMouseTop = sal_True;
1901         }
1902         //! find start of dimension
1903 
1904         rPosRect = Rectangle( nDataStartCol, nMemberStartRow + nField,
1905                               nTabEndCol, nMemberStartRow + nField -1 );
1906 
1907         sal_Bool bFound = sal_False;            // is this within the same orientation?
1908         sal_Bool bBeforeDrag = sal_False;
1909         sal_Bool bAfterDrag = sal_False;
1910         for (long nPos=0; nPos<nColFieldCount && !bFound; nPos++)
1911         {
1912             if (pColFields[nPos].nDim == nDragDim)
1913             {
1914                 bFound = sal_True;
1915                 if ( nField < nPos )
1916                     bBeforeDrag = sal_True;
1917                 else if ( nField > nPos )
1918                     bAfterDrag = sal_True;
1919             }
1920         }
1921 
1922         if ( bFound )
1923         {
1924             if (!bBeforeDrag)
1925             {
1926                 ++rPosRect.Bottom();
1927                 if (bAfterDrag)
1928                     ++rPosRect.Top();
1929             }
1930         }
1931         else
1932         {
1933             if ( !bMouseTop )
1934             {
1935                 ++rPosRect.Top();
1936                 ++rPosRect.Bottom();
1937                 ++nField;
1938             }
1939         }
1940 
1941         rOrient = sheet::DataPilotFieldOrientation_COLUMN;
1942         rDimPos = nField;                       //!...
1943         return sal_True;
1944     }
1945 
1946     //  test for row header
1947 
1948     //  special case if no row fields
1949     sal_Bool bSpecial = ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow &&
1950                         nRowFieldCount == 0 && nCol == nTabStartCol && bMouseLeft );
1951 
1952     if ( bSpecial || ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow &&
1953                         nCol + 1 >= nTabStartCol && nCol < nTabStartCol + nRowFieldCount ) )
1954     {
1955         long nField = nCol - nTabStartCol;
1956         //! find start of dimension
1957 
1958         rPosRect = Rectangle( nTabStartCol + nField, nDataStartRow - 1,
1959                               nTabStartCol + nField - 1, nTabEndRow );
1960 
1961         sal_Bool bFound = sal_False;            // is this within the same orientation?
1962         sal_Bool bBeforeDrag = sal_False;
1963         sal_Bool bAfterDrag = sal_False;
1964         for (long nPos=0; nPos<nRowFieldCount && !bFound; nPos++)
1965         {
1966             if (pRowFields[nPos].nDim == nDragDim)
1967             {
1968                 bFound = sal_True;
1969                 if ( nField < nPos )
1970                     bBeforeDrag = sal_True;
1971                 else if ( nField > nPos )
1972                     bAfterDrag = sal_True;
1973             }
1974         }
1975 
1976         if ( bFound )
1977         {
1978             if (!bBeforeDrag)
1979             {
1980                 ++rPosRect.Right();
1981                 if (bAfterDrag)
1982                     ++rPosRect.Left();
1983             }
1984         }
1985         else
1986         {
1987             if ( !bMouseLeft )
1988             {
1989                 ++rPosRect.Left();
1990                 ++rPosRect.Right();
1991                 ++nField;
1992             }
1993         }
1994 
1995         rOrient = sheet::DataPilotFieldOrientation_ROW;
1996         rDimPos = nField;                       //!...
1997         return sal_True;
1998     }
1999 
2000     //  test for page fields
2001 
2002     SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 );
2003     if ( nCol >= aStartPos.Col() && nCol <= nTabEndCol &&
2004             nRow + 1 >= nPageStartRow && nRow < nPageStartRow + nPageFieldCount )
2005     {
2006         long nField = nRow - nPageStartRow;
2007         if (nField < 0)
2008         {
2009             nField = 0;
2010             bMouseTop = sal_True;
2011         }
2012         //! find start of dimension
2013 
2014         rPosRect = Rectangle( aStartPos.Col(), nPageStartRow + nField,
2015                               nTabEndCol, nPageStartRow + nField - 1 );
2016 
2017         sal_Bool bFound = sal_False;            // is this within the same orientation?
2018         sal_Bool bBeforeDrag = sal_False;
2019         sal_Bool bAfterDrag = sal_False;
2020         for (long nPos=0; nPos<nPageFieldCount && !bFound; nPos++)
2021         {
2022             if (pPageFields[nPos].nDim == nDragDim)
2023             {
2024                 bFound = sal_True;
2025                 if ( nField < nPos )
2026                     bBeforeDrag = sal_True;
2027                 else if ( nField > nPos )
2028                     bAfterDrag = sal_True;
2029             }
2030         }
2031 
2032         if ( bFound )
2033         {
2034             if (!bBeforeDrag)
2035             {
2036                 ++rPosRect.Bottom();
2037                 if (bAfterDrag)
2038                     ++rPosRect.Top();
2039             }
2040         }
2041         else
2042         {
2043             if ( !bMouseTop )
2044             {
2045                 ++rPosRect.Top();
2046                 ++rPosRect.Bottom();
2047                 ++nField;
2048             }
2049         }
2050 
2051         rOrient = sheet::DataPilotFieldOrientation_PAGE;
2052         rDimPos = nField;                       //!...
2053         return sal_True;
2054     }
2055 
2056     return sal_False;
2057 }
2058 
2059 
2060 
2061