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