xref: /trunk/main/sc/source/core/data/dpoutput.cxx (revision b3f79822)
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 
108 	ScDPOutLevelData()
109     {
110         nDim = nHier = nLevel = nDimPos = -1;
111         mbHasHiddenMember = false;
112     }
113 
114 	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 
118 	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 
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 
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 
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 
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 
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 
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 
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 
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 
554 ScDPOutput::~ScDPOutput()
555 {
556 	delete[] pColFields;
557 	delete[] pRowFields;
558 	delete[] pPageFields;
559 
560 	delete[] pColNumFmt;
561 	delete[] pRowNumFmt;
562 }
563 
564 void ScDPOutput::SetPosition( const ScAddress& rPos )
565 {
566 	aStartPos = rPos;
567 	bSizesValid = bSizeOverflow = sal_False;
568 }
569 
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 
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 
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 
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 
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 
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 
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 
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 
1016 sal_Bool ScDPOutput::HasError()
1017 {
1018 	CalcSizes();
1019 
1020 	return bSizeOverflow || bResultsError;
1021 }
1022 
1023 long ScDPOutput::GetHeaderRows()
1024 {
1025 	return nPageFieldCount + ( bDoFilter ? 1 : 0 );
1026 }
1027 
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 
1075 void ScDPOutput::SetHeaderLayout(bool bUseGrid)
1076 {
1077     mbHeaderLayout = bUseGrid;
1078     bSizesValid = false;
1079 }
1080 
1081 bool ScDPOutput::GetHeaderLayout() const
1082 {
1083     return mbHeaderLayout;
1084 }
1085 
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                     rDataNames.push_back( aSourceName );
1138                     rGivenNames.push_back( aGivenName );
1139 
1140                     ++nDataCount;
1141                 }
1142             }
1143         }
1144 
1145         if ( ( rDataOrient == sheet::DataPilotFieldOrientation_COLUMN ) && bColGrand )
1146             rGrandTotalCols = nDataCount;
1147         else if ( ( rDataOrient == sheet::DataPilotFieldOrientation_ROW ) && bRowGrand )
1148             rGrandTotalRows = nDataCount;
1149     }
1150 }
1151 
1152 void ScDPOutput::GetPositionData(const ScAddress& rPos, DataPilotTablePositionData& rPosData)
1153 {
1154     using namespace ::com::sun::star::sheet;
1155 
1156 	SCCOL nCol = rPos.Col();
1157 	SCROW nRow = rPos.Row();
1158 	SCTAB nTab = rPos.Tab();
1159 	if ( nTab != aStartPos.Tab() )
1160 		return;										// wrong sheet
1161 
1162 	//	calculate output positions and sizes
1163 
1164 	CalcSizes();
1165 
1166     rPosData.PositionType = GetPositionType(rPos);
1167     switch (rPosData.PositionType)
1168     {
1169         case DataPilotTablePositionType::RESULT:
1170         {
1171             vector<DataPilotFieldFilter> aFilters;
1172             GetDataResultPositionData(aFilters, rPos);
1173             sal_Int32 nSize = aFilters.size();
1174 
1175             DataPilotTableResultData aResData;
1176             aResData.FieldFilters.realloc(nSize);
1177             for (sal_Int32 i = 0; i < nSize; ++i)
1178                 aResData.FieldFilters[i] = aFilters[i];
1179 
1180             aResData.DataFieldIndex = 0;
1181             Reference<beans::XPropertySet> xPropSet(xSource, UNO_QUERY);
1182             if (xPropSet.is())
1183             {
1184                 sal_Int32 nDataFieldCount = ScUnoHelpFunctions::GetLongProperty( xPropSet,
1185                                             rtl::OUString::createFromAscii(SC_UNO_DATAFIELDCOUNT) );
1186                 if (nDataFieldCount > 0)
1187                     aResData.DataFieldIndex = (nRow - nDataStartRow) % nDataFieldCount;
1188             }
1189 
1190             // Copy appropriate DataResult object from the cached sheet::DataResult table.
1191             if (aData.getLength() > nRow - nDataStartRow &&
1192                 aData[nRow-nDataStartRow].getLength() > nCol-nDataStartCol)
1193                 aResData.Result = aData[nRow-nDataStartRow][nCol-nDataStartCol];
1194 
1195             rPosData.PositionData = makeAny(aResData);
1196             return;
1197         }
1198         case DataPilotTablePositionType::COLUMN_HEADER:
1199         {
1200             long nField = nRow - nTabStartRow - 1; // 1st line is used for the buttons
1201             if (nField < 0)
1202                 break;
1203 
1204             const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nField].aResult;
1205             if (rSequence.getLength() == 0)
1206                 break;
1207             const sheet::MemberResult* pArray = rSequence.getConstArray();
1208 
1209             long nItem = nCol - nDataStartCol;
1210             //  get origin of "continue" fields
1211             while (nItem > 0 && ( pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1212                 --nItem;
1213 
1214             if (nItem < 0)
1215                 break;
1216 
1217             DataPilotTableHeaderData aHeaderData;
1218             aHeaderData.MemberName = OUString(pArray[nItem].Name);
1219             aHeaderData.Flags = pArray[nItem].Flags;
1220             aHeaderData.Dimension = static_cast<sal_Int32>(pColFields[nField].nDim);
1221             aHeaderData.Hierarchy = static_cast<sal_Int32>(pColFields[nField].nHier);
1222             aHeaderData.Level     = static_cast<sal_Int32>(pColFields[nField].nLevel);
1223 
1224             rPosData.PositionData = makeAny(aHeaderData);
1225             return;
1226         }
1227         case DataPilotTablePositionType::ROW_HEADER:
1228         {
1229             long nField = nCol - nTabStartCol;
1230             if (nField < 0)
1231                 break;
1232 
1233             const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].aResult;
1234             if (rSequence.getLength() == 0)
1235                 break;
1236             const sheet::MemberResult* pArray = rSequence.getConstArray();
1237 
1238             long nItem = nRow - nDataStartRow;
1239             //	get origin of "continue" fields
1240             while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1241                 --nItem;
1242 
1243             if (nItem < 0)
1244                 break;
1245 
1246             DataPilotTableHeaderData aHeaderData;
1247             aHeaderData.MemberName = OUString(pArray[nItem].Name);
1248             aHeaderData.Flags = pArray[nItem].Flags;
1249             aHeaderData.Dimension = static_cast<sal_Int32>(pRowFields[nField].nDim);
1250             aHeaderData.Hierarchy = static_cast<sal_Int32>(pRowFields[nField].nHier);
1251             aHeaderData.Level     = static_cast<sal_Int32>(pRowFields[nField].nLevel);
1252 
1253             rPosData.PositionData = makeAny(aHeaderData);
1254             return;
1255         }
1256     }
1257 }
1258 
1259 bool ScDPOutput::GetDataResultPositionData(vector<sheet::DataPilotFieldFilter>& rFilters, const ScAddress& rPos)
1260 {
1261     // Check to make sure there is at least one data field.
1262     Reference<beans::XPropertySet> xPropSet(xSource, UNO_QUERY);
1263     if (!xPropSet.is())
1264         return false;
1265 
1266     sal_Int32 nDataFieldCount = ScUnoHelpFunctions::GetLongProperty( xPropSet,
1267                                 rtl::OUString::createFromAscii(SC_UNO_DATAFIELDCOUNT) );
1268     if (nDataFieldCount == 0)
1269         // No data field is present in this datapilot table.
1270         return false;
1271 
1272     // #i111421# use lcl_GetTableVars for correct size of totals and data layout position
1273     sal_Int32 nGrandTotalCols;
1274     sal_Int32 nGrandTotalRows;
1275     sal_Int32 nDataLayoutIndex;
1276     std::vector<String> aDataNames;
1277     std::vector<String> aGivenNames;
1278     sheet::DataPilotFieldOrientation eDataOrient;
1279     lcl_GetTableVars( nGrandTotalCols, nGrandTotalRows, nDataLayoutIndex, aDataNames, aGivenNames, eDataOrient, xSource );
1280 
1281     SCCOL nCol = rPos.Col();
1282     SCROW nRow = rPos.Row();
1283     SCTAB nTab = rPos.Tab();
1284     if ( nTab != aStartPos.Tab() )
1285         return false;                                     // wrong sheet
1286 
1287     CalcSizes();
1288 
1289     // test for data area.
1290     if (nCol < nDataStartCol || nCol > nTabEndCol || nRow < nDataStartRow || nRow > nTabEndRow)
1291     {
1292         // Cell is outside the data field area.
1293         return false;
1294     }
1295 
1296     bool bFilterByCol = (nCol <= static_cast<SCCOL>(nTabEndCol - nGrandTotalCols));
1297     bool bFilterByRow = (nRow <= static_cast<SCROW>(nTabEndRow - nGrandTotalRows));
1298 
1299     // column fields
1300     for (SCCOL nColField = 0; nColField < nColFieldCount && bFilterByCol; ++nColField)
1301     {
1302         if (pColFields[nColField].nDim == nDataLayoutIndex)
1303             // There is no sense including the data layout field for filtering.
1304             continue;
1305 
1306         sheet::DataPilotFieldFilter filter;
1307         filter.FieldName = pColFields[nColField].maName;
1308 
1309         const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nColField].aResult;
1310         const sheet::MemberResult* pArray = rSequence.getConstArray();
1311 
1312         DBG_ASSERT(nDataStartCol + rSequence.getLength() - 1 == nTabEndCol, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
1313 
1314         long nItem = nCol - nDataStartCol;
1315                 //	get origin of "continue" fields
1316         while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1317             --nItem;
1318 
1319         filter.MatchValue = pArray[nItem].Name;
1320         rFilters.push_back(filter);
1321     }
1322 
1323     // row fields
1324     for (SCROW nRowField = 0; nRowField < nRowFieldCount && bFilterByRow; ++nRowField)
1325     {
1326         if (pRowFields[nRowField].nDim == nDataLayoutIndex)
1327             // There is no sense including the data layout field for filtering.
1328             continue;
1329 
1330         sheet::DataPilotFieldFilter filter;
1331         filter.FieldName = pRowFields[nRowField].maName;
1332 
1333         const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nRowField].aResult;
1334         const sheet::MemberResult* pArray = rSequence.getConstArray();
1335 
1336         DBG_ASSERT(nDataStartRow + rSequence.getLength() - 1 == nTabEndRow, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
1337 
1338         long nItem = nRow - nDataStartRow;
1339 			//	get origin of "continue" fields
1340         while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1341             --nItem;
1342 
1343         filter.MatchValue = pArray[nItem].Name;
1344         rFilters.push_back(filter);
1345     }
1346 
1347     return true;
1348 }
1349 
1350 //
1351 //  helper functions for ScDPOutput::GetPivotData
1352 //
1353 
1354 bool lcl_IsNamedDataField( const ScDPGetPivotDataField& rTarget, const String& rSourceName, const String& rGivenName )
1355 {
1356     // match one of the names, ignoring case
1357     return ScGlobal::GetpTransliteration()->isEqual( rTarget.maFieldName, rSourceName ) ||
1358            ScGlobal::GetpTransliteration()->isEqual( rTarget.maFieldName, rGivenName );
1359 }
1360 
1361 bool lcl_IsNamedCategoryField( const ScDPGetPivotDataField& rFilter, const ScDPOutLevelData& rField )
1362 {
1363     return ScGlobal::GetpTransliteration()->isEqual( rFilter.maFieldName, rField.maName );
1364 }
1365 
1366 bool lcl_IsCondition( const sheet::MemberResult& rResultEntry, const ScDPGetPivotDataField& rFilter )
1367 {
1368     //! handle numeric conditions?
1369     return ScGlobal::GetpTransliteration()->isEqual( rResultEntry.Name, rFilter.maValStr );
1370 }
1371 
1372 bool lcl_CheckPageField( const ScDPOutLevelData& rField,
1373                         const std::vector< ScDPGetPivotDataField >& rFilters,
1374                         std::vector< sal_Bool >& rFilterUsed )
1375 {
1376     for (SCSIZE nFilterPos = 0; nFilterPos < rFilters.size(); ++nFilterPos)
1377     {
1378         if ( lcl_IsNamedCategoryField( rFilters[nFilterPos], rField ) )
1379         {
1380             rFilterUsed[nFilterPos] = sal_True;
1381 
1382             // page field result is empty or the selection as single entry (see lcl_GetSelectedPageAsResult)
1383             if ( rField.aResult.getLength() == 1 &&
1384                  lcl_IsCondition( rField.aResult[0], rFilters[nFilterPos] ) )
1385             {
1386                 return true;        // condition matches page selection
1387             }
1388             else
1389             {
1390                 return false;       // no page selection or different entry
1391             }
1392         }
1393     }
1394 
1395     return true;    // valid if the page field doesn't have a filter
1396 }
1397 
1398 uno::Sequence<sheet::GeneralFunction> lcl_GetSubTotals(
1399         const uno::Reference<sheet::XDimensionsSupplier>& xSource, const ScDPOutLevelData& rField )
1400 {
1401     uno::Sequence<sheet::GeneralFunction> aSubTotals;
1402 
1403     uno::Reference<sheet::XHierarchiesSupplier> xHierSupp;
1404     uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
1405     uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
1406     sal_Int32 nIntCount = xIntDims->getCount();
1407     if ( rField.nDim < nIntCount )
1408     {
1409         uno::Reference<uno::XInterface> xIntDim = ScUnoHelpFunctions::AnyToInterface(
1410                                     xIntDims->getByIndex( rField.nDim ) );
1411         xHierSupp = uno::Reference<sheet::XHierarchiesSupplier>( xIntDim, uno::UNO_QUERY );
1412     }
1413     DBG_ASSERT( xHierSupp.is(), "dimension not found" );
1414 
1415     sal_Int32 nHierCount = 0;
1416     uno::Reference<container::XIndexAccess> xHiers;
1417     if ( xHierSupp.is() )
1418     {
1419         uno::Reference<container::XNameAccess> xHiersName = xHierSupp->getHierarchies();
1420         xHiers = new ScNameToIndexAccess( xHiersName );
1421         nHierCount = xHiers->getCount();
1422     }
1423     uno::Reference<uno::XInterface> xHier;
1424     if ( rField.nHier < nHierCount )
1425         xHier = ScUnoHelpFunctions::AnyToInterface( xHiers->getByIndex( rField.nHier ) );
1426     DBG_ASSERT( xHier.is(), "hierarchy not found" );
1427 
1428     sal_Int32 nLevCount = 0;
1429     uno::Reference<container::XIndexAccess> xLevels;
1430     uno::Reference<sheet::XLevelsSupplier> xLevSupp( xHier, uno::UNO_QUERY );
1431     if ( xLevSupp.is() )
1432     {
1433         uno::Reference<container::XNameAccess> xLevsName = xLevSupp->getLevels();
1434         xLevels = new ScNameToIndexAccess( xLevsName );
1435         nLevCount = xLevels->getCount();
1436     }
1437     uno::Reference<uno::XInterface> xLevel;
1438     if ( rField.nLevel < nLevCount )
1439         xLevel = ScUnoHelpFunctions::AnyToInterface( xLevels->getByIndex( rField.nLevel ) );
1440     DBG_ASSERT( xLevel.is(), "level not found" );
1441 
1442     uno::Reference<beans::XPropertySet> xLevelProp( xLevel, uno::UNO_QUERY );
1443     if ( xLevelProp.is() )
1444     {
1445         try
1446         {
1447             uno::Any aValue = xLevelProp->getPropertyValue( rtl::OUString::createFromAscii(DP_PROP_SUBTOTALS) );
1448             aValue >>= aSubTotals;
1449         }
1450         catch(uno::Exception&)
1451         {
1452         }
1453     }
1454 
1455     return aSubTotals;
1456 }
1457 
1458 void lcl_FilterInclude( std::vector< sal_Bool >& rResult, std::vector< sal_Int32 >& rSubtotal,
1459                         const ScDPOutLevelData& rField,
1460                         const std::vector< ScDPGetPivotDataField >& rFilters,
1461                         std::vector< sal_Bool >& rFilterUsed,
1462                         bool& rBeforeDataLayout,
1463                         sal_Int32 nGrandTotals, sal_Int32 nDataLayoutIndex,
1464                         const std::vector<String>& rDataNames, const std::vector<String>& rGivenNames,
1465                         const ScDPGetPivotDataField& rTarget, const uno::Reference<sheet::XDimensionsSupplier>& xSource )
1466 {
1467     // returns true if a filter was given for the field
1468 
1469     DBG_ASSERT( rFilters.size() == rFilterUsed.size(), "wrong size" );
1470 
1471     const bool bIsDataLayout = ( rField.nDim == nDataLayoutIndex );
1472     if (bIsDataLayout)
1473         rBeforeDataLayout = false;
1474 
1475     bool bHasFilter = false;
1476     ScDPGetPivotDataField aFilter;
1477     if ( !bIsDataLayout )          // selection of data field is handled separately
1478     {
1479         for (SCSIZE nFilterPos = 0; nFilterPos < rFilters.size() && !bHasFilter; ++nFilterPos)
1480         {
1481             if ( lcl_IsNamedCategoryField( rFilters[nFilterPos], rField ) )
1482             {
1483                 aFilter = rFilters[nFilterPos];
1484                 rFilterUsed[nFilterPos] = sal_True;
1485                 bHasFilter = true;
1486             }
1487         }
1488     }
1489 
1490     bool bHasFunc = bHasFilter && aFilter.meFunction != sheet::GeneralFunction_NONE;
1491 
1492     uno::Sequence<sheet::GeneralFunction> aSubTotals;
1493     if ( !bIsDataLayout )
1494         aSubTotals = lcl_GetSubTotals( xSource, rField );
1495     bool bManualSub = ( aSubTotals.getLength() > 0 && aSubTotals[0] != sheet::GeneralFunction_AUTO );
1496 
1497     const uno::Sequence<sheet::MemberResult>& rSequence = rField.aResult;
1498     const sheet::MemberResult* pArray = rSequence.getConstArray();
1499     sal_Int32 nSize = rSequence.getLength();
1500 
1501     DBG_ASSERT( (sal_Int32)rResult.size() == nSize, "Number of fields do not match result count" );
1502 
1503     sal_Int32 nContCount = 0;
1504     sal_Int32 nSubTotalCount = 0;
1505     sheet::MemberResult aPrevious;
1506     for( sal_Int32 j=0; j < nSize; j++ )
1507     {
1508         sheet::MemberResult aResultEntry = pArray[j];
1509         if ( aResultEntry.Flags & sheet::MemberResultFlags::CONTINUE )
1510         {
1511             aResultEntry = aPrevious;
1512             ++nContCount;
1513         }
1514         else if ( ( aResultEntry.Flags & sheet::MemberResultFlags::SUBTOTAL ) == 0 )
1515         {
1516             // count the CONTINUE entries before a SUBTOTAL
1517             nContCount = 0;
1518         }
1519 
1520         if ( j >= nSize - nGrandTotals )
1521         {
1522             // mark as subtotal for the preceding data
1523             if ( ( aResultEntry.Flags & sheet::MemberResultFlags::SUBTOTAL ) != 0 )
1524             {
1525                 rSubtotal[j] = nSize - nGrandTotals;
1526 
1527                 if ( rResult[j] && nGrandTotals > 1 )
1528                 {
1529                     // grand total is always automatic
1530                     sal_Int32 nDataPos = j - ( nSize - nGrandTotals );
1531                     DBG_ASSERT( nDataPos < (sal_Int32)rDataNames.size(), "wrong data count" );
1532                     String aSourceName( rDataNames[nDataPos] );     // vector contains source names
1533                     String aGivenName( rGivenNames[nDataPos] );
1534 
1535                     rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName );
1536                 }
1537             }
1538 
1539             // treat "grand total" columns/rows as empty description, as if they were marked
1540             // in a previous field
1541 
1542             DBG_ASSERT( ( aResultEntry.Flags &
1543                             ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL ) ) == 0 ||
1544                         ( aResultEntry.Flags &
1545                             ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL ) ) ==
1546                                 ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL ),
1547                         "non-subtotal member found in grand total result" );
1548             aResultEntry.Flags = 0;
1549         }
1550 
1551         // mark subtotals (not grand total) for preceding data (assume CONTINUE is set)
1552         if ( ( aResultEntry.Flags & sheet::MemberResultFlags::SUBTOTAL ) != 0 )
1553         {
1554             rSubtotal[j] = nContCount + 1 + nSubTotalCount;
1555 
1556             if ( rResult[j] )
1557             {
1558                 if ( bManualSub )
1559                 {
1560                     if ( rBeforeDataLayout )
1561                     {
1562                         // manual subtotals and several data fields
1563 
1564                         sal_Int32 nDataCount = rDataNames.size();
1565                         sal_Int32 nFuncPos = nSubTotalCount / nDataCount;       // outer order: subtotal functions
1566                         sal_Int32 nDataPos = nSubTotalCount % nDataCount;       // inner order: data fields
1567 
1568                         String aSourceName( rDataNames[nDataPos] );             // vector contains source names
1569                         String aGivenName( rGivenNames[nDataPos] );
1570 
1571                         DBG_ASSERT( nFuncPos < aSubTotals.getLength(), "wrong subtotal count" );
1572                         rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName ) &&
1573                                      aSubTotals[nFuncPos] == aFilter.meFunction;
1574                     }
1575                     else
1576                     {
1577                         // manual subtotals for a single data field
1578 
1579                         DBG_ASSERT( nSubTotalCount < aSubTotals.getLength(), "wrong subtotal count" );
1580                         rResult[j] = ( aSubTotals[nSubTotalCount] == aFilter.meFunction );
1581                     }
1582                 }
1583                 else    // automatic subtotals
1584                 {
1585                     if ( rBeforeDataLayout )
1586                     {
1587                         DBG_ASSERT( nSubTotalCount < (sal_Int32)rDataNames.size(), "wrong data count" );
1588                         String aSourceName( rDataNames[nSubTotalCount] );       // vector contains source names
1589                         String aGivenName( rGivenNames[nSubTotalCount] );
1590 
1591                         rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName );
1592                     }
1593 
1594                     // if a function was specified, automatic subtotals never match
1595                     if ( bHasFunc )
1596                         rResult[j] = sal_False;
1597                 }
1598             }
1599 
1600             ++nSubTotalCount;
1601         }
1602         else
1603             nSubTotalCount = 0;
1604 
1605         if( rResult[j] )
1606         {
1607             if ( bIsDataLayout )
1608             {
1609                 if ( ( aResultEntry.Flags & sheet::MemberResultFlags::HASMEMBER ) != 0 )
1610                 {
1611                     // Asterisks are added in ScDPSaveData::WriteToSource to create unique names.
1612                     //! preserve original name there?
1613                     String aSourceName( aResultEntry.Name );
1614                     aSourceName.EraseTrailingChars( '*' );
1615 
1616                     String aGivenName( aResultEntry.Caption );  //! Should use a stored name when available
1617                     aGivenName.EraseLeadingChars( '\'' );
1618 
1619                     rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName );
1620                 }
1621             }
1622             else if ( bHasFilter )
1623             {
1624                 // name must match (simple value or subtotal)
1625                 rResult[j] = ( ( aResultEntry.Flags & sheet::MemberResultFlags::HASMEMBER ) != 0 ) &&
1626                              lcl_IsCondition( aResultEntry, aFilter );
1627 
1628                 // if a function was specified, simple (non-subtotal) values never match
1629                 if ( bHasFunc && nSubTotalCount == 0 )
1630                     rResult[j] = sal_False;
1631             }
1632             // if no condition is given, keep the columns/rows included
1633         }
1634         aPrevious = aResultEntry;
1635     }
1636 }
1637 
1638 void lcl_StripSubTotals( std::vector< sal_Bool >& rResult, const std::vector< sal_Int32 >& rSubtotal )
1639 {
1640     sal_Int32 nSize = rResult.size();
1641     DBG_ASSERT( (sal_Int32)rSubtotal.size() == nSize, "sizes don't match" );
1642 
1643     for (sal_Int32 nPos=0; nPos<nSize; nPos++)
1644         if ( rResult[nPos] && rSubtotal[nPos] )
1645         {
1646             // if a subtotal is included, clear the result flag for the columns/rows that the subtotal includes
1647             sal_Int32 nStart = nPos - rSubtotal[nPos];
1648             DBG_ASSERT( nStart >= 0, "invalid subtotal count" );
1649 
1650             for (sal_Int32 nPrev = nStart; nPrev < nPos; nPrev++)
1651                 rResult[nPrev] = sal_False;
1652         }
1653 }
1654 
1655 String lcl_GetDataFieldName( const String& rSourceName, sheet::GeneralFunction eFunc )
1656 {
1657     sal_uInt16 nStrId = 0;
1658     switch ( eFunc )
1659     {
1660         case sheet::GeneralFunction_SUM:        nStrId = STR_FUN_TEXT_SUM;      break;
1661         case sheet::GeneralFunction_COUNT:
1662         case sheet::GeneralFunction_COUNTNUMS:  nStrId = STR_FUN_TEXT_COUNT;    break;
1663         case sheet::GeneralFunction_AVERAGE:    nStrId = STR_FUN_TEXT_AVG;      break;
1664         case sheet::GeneralFunction_MAX:        nStrId = STR_FUN_TEXT_MAX;      break;
1665         case sheet::GeneralFunction_MIN:        nStrId = STR_FUN_TEXT_MIN;      break;
1666         case sheet::GeneralFunction_PRODUCT:    nStrId = STR_FUN_TEXT_PRODUCT;  break;
1667         case sheet::GeneralFunction_STDEV:
1668         case sheet::GeneralFunction_STDEVP:     nStrId = STR_FUN_TEXT_STDDEV;   break;
1669         case sheet::GeneralFunction_VAR:
1670         case sheet::GeneralFunction_VARP:       nStrId = STR_FUN_TEXT_VAR;      break;
1671         case sheet::GeneralFunction_NONE:
1672         case sheet::GeneralFunction_AUTO:
1673         default:
1674         {
1675             DBG_ERRORFILE("wrong function");
1676         }
1677     }
1678     if ( !nStrId )
1679         return String();
1680 
1681     String aRet( ScGlobal::GetRscString( nStrId ) );
1682     aRet.AppendAscii(RTL_CONSTASCII_STRINGPARAM( " - " ));
1683     aRet.Append( rSourceName );
1684     return aRet;
1685 }
1686 
1687 // static
1688 void ScDPOutput::GetDataDimensionNames( String& rSourceName, String& rGivenName,
1689                                         const uno::Reference<uno::XInterface>& xDim )
1690 {
1691     uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
1692     uno::Reference<container::XNamed> xDimName( xDim, uno::UNO_QUERY );
1693     if ( xDimProp.is() && xDimName.is() )
1694     {
1695         // Asterisks are added in ScDPSaveData::WriteToSource to create unique names.
1696         //! preserve original name there?
1697         rSourceName = xDimName->getName();
1698         rSourceName.EraseTrailingChars( '*' );
1699 
1700         // Generate "given name" the same way as in dptabres.
1701         //! Should use a stored name when available
1702 
1703         sheet::GeneralFunction eFunc = (sheet::GeneralFunction)ScUnoHelpFunctions::GetEnumProperty(
1704                                 xDimProp, rtl::OUString::createFromAscii(DP_PROP_FUNCTION),
1705                                 sheet::GeneralFunction_NONE );
1706         rGivenName = lcl_GetDataFieldName( rSourceName, eFunc );
1707     }
1708 }
1709 
1710 // Returns sal_True on success and stores the result in rTarget
1711 // Returns sal_False if rFilters or rTarget describes something that is not visible
1712 sal_Bool ScDPOutput::GetPivotData( ScDPGetPivotDataField& rTarget,
1713                                const std::vector< ScDPGetPivotDataField >& rFilters )
1714 {
1715     CalcSizes();
1716 
1717     // need to know about grand total columns/rows:
1718     sal_Int32 nGrandTotalCols;
1719     sal_Int32 nGrandTotalRows;
1720     sal_Int32 nDataLayoutIndex;
1721     std::vector<String> aDataNames;
1722     std::vector<String> aGivenNames;
1723     sheet::DataPilotFieldOrientation eDataOrient;
1724     lcl_GetTableVars( nGrandTotalCols, nGrandTotalRows, nDataLayoutIndex, aDataNames, aGivenNames, eDataOrient, xSource );
1725 
1726     if ( aDataNames.empty() )
1727         return sal_False;               // incomplete table without data fields -> no result
1728 
1729     if ( eDataOrient == sheet::DataPilotFieldOrientation_HIDDEN )
1730     {
1731         // no data layout field -> single data field -> must match the selected field in rTarget
1732 
1733         DBG_ASSERT( aDataNames.size() == 1, "several data fields but no data layout field" );
1734         if ( !lcl_IsNamedDataField( rTarget, aDataNames[0], aGivenNames[0] ) )
1735             return sal_False;
1736     }
1737 
1738     std::vector< sal_Bool > aIncludeCol( nColCount, sal_True );
1739     std::vector< sal_Int32 > aSubtotalCol( nColCount, 0 );
1740     std::vector< sal_Bool > aIncludeRow( nRowCount, sal_True );
1741     std::vector< sal_Int32 > aSubtotalRow( nRowCount, 0 );
1742 
1743     std::vector< sal_Bool > aFilterUsed( rFilters.size(), sal_False );
1744 
1745     long nField;
1746     long nCol;
1747     long nRow;
1748     bool bBeforeDataLayout;
1749 
1750     // look in column fields
1751 
1752     bBeforeDataLayout = ( eDataOrient == sheet::DataPilotFieldOrientation_COLUMN );
1753     for (nField=0; nField<nColFieldCount; nField++)
1754         lcl_FilterInclude( aIncludeCol, aSubtotalCol, pColFields[nField], rFilters, aFilterUsed, bBeforeDataLayout,
1755                            nGrandTotalCols, nDataLayoutIndex, aDataNames, aGivenNames, rTarget, xSource );
1756 
1757     // look in row fields
1758 
1759     bBeforeDataLayout = ( eDataOrient == sheet::DataPilotFieldOrientation_ROW );
1760     for (nField=0; nField<nRowFieldCount; nField++)
1761         lcl_FilterInclude( aIncludeRow, aSubtotalRow, pRowFields[nField], rFilters, aFilterUsed, bBeforeDataLayout,
1762                            nGrandTotalRows, nDataLayoutIndex, aDataNames, aGivenNames, rTarget, xSource );
1763 
1764     // page fields
1765 
1766     for (nField=0; nField<nPageFieldCount; nField++)
1767         if ( !lcl_CheckPageField( pPageFields[nField], rFilters, aFilterUsed ) )
1768             return sal_False;
1769 
1770     // all filter fields must be used
1771     for (SCSIZE nFilter=0; nFilter<aFilterUsed.size(); nFilter++)
1772         if (!aFilterUsed[nFilter])
1773             return sal_False;
1774 
1775     lcl_StripSubTotals( aIncludeCol, aSubtotalCol );
1776     lcl_StripSubTotals( aIncludeRow, aSubtotalRow );
1777 
1778     long nColPos = 0;
1779     long nColIncluded = 0;
1780     for (nCol=0; nCol<nColCount; nCol++)
1781         if (aIncludeCol[nCol])
1782         {
1783             nColPos = nCol;
1784             ++nColIncluded;
1785         }
1786 
1787     long nRowPos = 0;
1788     long nRowIncluded = 0;
1789     for (nRow=0; nRow<nRowCount; nRow++)
1790         if (aIncludeRow[nRow])
1791         {
1792             nRowPos = nRow;
1793             ++nRowIncluded;
1794         }
1795 
1796     if ( nColIncluded != 1 || nRowIncluded != 1 )
1797         return sal_False;
1798 
1799     const uno::Sequence<sheet::DataResult>& rDataRow = aData[nRowPos];
1800     if ( nColPos >= rDataRow.getLength() )
1801         return sal_False;
1802 
1803     const sheet::DataResult& rResult = rDataRow[nColPos];
1804     if ( rResult.Flags & sheet::DataResultFlags::ERROR )
1805         return sal_False;                                       //! different error?
1806 
1807     rTarget.mbValIsStr = sal_False;
1808     rTarget.mnValNum = rResult.Value;
1809 
1810     return sal_True;
1811 }
1812 
1813 sal_Bool ScDPOutput::IsFilterButton( const ScAddress& rPos )
1814 {
1815 	SCCOL nCol = rPos.Col();
1816 	SCROW nRow = rPos.Row();
1817 	SCTAB nTab = rPos.Tab();
1818 	if ( nTab != aStartPos.Tab() || !bDoFilter )
1819 		return sal_False;								// wrong sheet or no button at all
1820 
1821 	//	filter button is at top left
1822 	return ( nCol == aStartPos.Col() && nRow == aStartPos.Row() );
1823 }
1824 
1825 long ScDPOutput::GetHeaderDim( const ScAddress& rPos, sal_uInt16& rOrient )
1826 {
1827 	SCCOL nCol = rPos.Col();
1828 	SCROW nRow = rPos.Row();
1829 	SCTAB nTab = rPos.Tab();
1830 	if ( nTab != aStartPos.Tab() )
1831 		return -1;										// wrong sheet
1832 
1833 	//	calculate output positions and sizes
1834 
1835 	CalcSizes();
1836 
1837 	//	test for column header
1838 
1839 	if ( nRow == nTabStartRow && nCol >= nDataStartCol && nCol < nDataStartCol + nColFieldCount )
1840 	{
1841 		rOrient = sheet::DataPilotFieldOrientation_COLUMN;
1842 		long nField = nCol - nDataStartCol;
1843 		return pColFields[nField].nDim;
1844 	}
1845 
1846 	//	test for row header
1847 
1848 	if ( nRow+1 == nDataStartRow && nCol >= nTabStartCol && nCol < nTabStartCol + nRowFieldCount )
1849 	{
1850 		rOrient = sheet::DataPilotFieldOrientation_ROW;
1851 		long nField = nCol - nTabStartCol;
1852 		return pRowFields[nField].nDim;
1853 	}
1854 
1855 	//	test for page field
1856 
1857 	SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 );
1858 	if ( nCol == aStartPos.Col() && nRow >= nPageStartRow && nRow < nPageStartRow + nPageFieldCount )
1859 	{
1860 		rOrient = sheet::DataPilotFieldOrientation_PAGE;
1861 		long nField = nRow - nPageStartRow;
1862 		return pPageFields[nField].nDim;
1863 	}
1864 
1865 	//!	single data field (?)
1866 
1867 	rOrient = sheet::DataPilotFieldOrientation_HIDDEN;
1868 	return -1;		// invalid
1869 }
1870 
1871 sal_Bool ScDPOutput::GetHeaderDrag( const ScAddress& rPos, sal_Bool bMouseLeft, sal_Bool bMouseTop,
1872 								long nDragDim,
1873 								Rectangle& rPosRect, sal_uInt16& rOrient, long& rDimPos )
1874 {
1875 	//	Rectangle instead of ScRange for rPosRect to allow for negative values
1876 
1877 	SCCOL nCol = rPos.Col();
1878 	SCROW nRow = rPos.Row();
1879 	SCTAB nTab = rPos.Tab();
1880 	if ( nTab != aStartPos.Tab() )
1881 		return sal_False;										// wrong sheet
1882 
1883 	//	calculate output positions and sizes
1884 
1885 	CalcSizes();
1886 
1887 	//	test for column header
1888 
1889 	if ( nCol >= nDataStartCol && nCol <= nTabEndCol &&
1890 			nRow + 1 >= nMemberStartRow && nRow < nMemberStartRow + nColFieldCount )
1891 	{
1892 		long nField = nRow - nMemberStartRow;
1893 		if (nField < 0)
1894 		{
1895 			nField = 0;
1896 			bMouseTop = sal_True;
1897 		}
1898 		//!	find start of dimension
1899 
1900 		rPosRect = Rectangle( nDataStartCol, nMemberStartRow + nField,
1901 							  nTabEndCol, nMemberStartRow + nField -1 );
1902 
1903 		sal_Bool bFound = sal_False;			// is this within the same orientation?
1904 		sal_Bool bBeforeDrag = sal_False;
1905 		sal_Bool bAfterDrag = sal_False;
1906 		for (long nPos=0; nPos<nColFieldCount && !bFound; nPos++)
1907 		{
1908 			if (pColFields[nPos].nDim == nDragDim)
1909 			{
1910 				bFound = sal_True;
1911 				if ( nField < nPos )
1912 					bBeforeDrag = sal_True;
1913 				else if ( nField > nPos )
1914 					bAfterDrag = sal_True;
1915 			}
1916 		}
1917 
1918 		if ( bFound )
1919 		{
1920 			if (!bBeforeDrag)
1921 			{
1922 				++rPosRect.Bottom();
1923 				if (bAfterDrag)
1924 					++rPosRect.Top();
1925 			}
1926 		}
1927 		else
1928 		{
1929 			if ( !bMouseTop )
1930 			{
1931 				++rPosRect.Top();
1932 				++rPosRect.Bottom();
1933 				++nField;
1934 			}
1935 		}
1936 
1937 		rOrient = sheet::DataPilotFieldOrientation_COLUMN;
1938 		rDimPos = nField;						//!...
1939 		return sal_True;
1940 	}
1941 
1942 	//	test for row header
1943 
1944 	//	special case if no row fields
1945 	sal_Bool bSpecial = ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow &&
1946 						nRowFieldCount == 0 && nCol == nTabStartCol && bMouseLeft );
1947 
1948 	if ( bSpecial || ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow &&
1949 						nCol + 1 >= nTabStartCol && nCol < nTabStartCol + nRowFieldCount ) )
1950 	{
1951 		long nField = nCol - nTabStartCol;
1952 		//!	find start of dimension
1953 
1954 		rPosRect = Rectangle( nTabStartCol + nField, nDataStartRow - 1,
1955 							  nTabStartCol + nField - 1, nTabEndRow );
1956 
1957 		sal_Bool bFound = sal_False;			// is this within the same orientation?
1958 		sal_Bool bBeforeDrag = sal_False;
1959 		sal_Bool bAfterDrag = sal_False;
1960 		for (long nPos=0; nPos<nRowFieldCount && !bFound; nPos++)
1961 		{
1962 			if (pRowFields[nPos].nDim == nDragDim)
1963 			{
1964 				bFound = sal_True;
1965 				if ( nField < nPos )
1966 					bBeforeDrag = sal_True;
1967 				else if ( nField > nPos )
1968 					bAfterDrag = sal_True;
1969 			}
1970 		}
1971 
1972 		if ( bFound )
1973 		{
1974 			if (!bBeforeDrag)
1975 			{
1976 				++rPosRect.Right();
1977 				if (bAfterDrag)
1978 					++rPosRect.Left();
1979 			}
1980 		}
1981 		else
1982 		{
1983 			if ( !bMouseLeft )
1984 			{
1985 				++rPosRect.Left();
1986 				++rPosRect.Right();
1987 				++nField;
1988 			}
1989 		}
1990 
1991 		rOrient = sheet::DataPilotFieldOrientation_ROW;
1992 		rDimPos = nField;						//!...
1993 		return sal_True;
1994 	}
1995 
1996 	//	test for page fields
1997 
1998 	SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 );
1999 	if ( nCol >= aStartPos.Col() && nCol <= nTabEndCol &&
2000 			nRow + 1 >= nPageStartRow && nRow < nPageStartRow + nPageFieldCount )
2001 	{
2002 		long nField = nRow - nPageStartRow;
2003 		if (nField < 0)
2004 		{
2005 			nField = 0;
2006 			bMouseTop = sal_True;
2007 		}
2008 		//!	find start of dimension
2009 
2010 		rPosRect = Rectangle( aStartPos.Col(), nPageStartRow + nField,
2011 							  nTabEndCol, nPageStartRow + nField - 1 );
2012 
2013 		sal_Bool bFound = sal_False;			// is this within the same orientation?
2014 		sal_Bool bBeforeDrag = sal_False;
2015 		sal_Bool bAfterDrag = sal_False;
2016 		for (long nPos=0; nPos<nPageFieldCount && !bFound; nPos++)
2017 		{
2018 			if (pPageFields[nPos].nDim == nDragDim)
2019 			{
2020 				bFound = sal_True;
2021 				if ( nField < nPos )
2022 					bBeforeDrag = sal_True;
2023 				else if ( nField > nPos )
2024 					bAfterDrag = sal_True;
2025 			}
2026 		}
2027 
2028 		if ( bFound )
2029 		{
2030 			if (!bBeforeDrag)
2031 			{
2032 				++rPosRect.Bottom();
2033 				if (bAfterDrag)
2034 					++rPosRect.Top();
2035 			}
2036 		}
2037 		else
2038 		{
2039 			if ( !bMouseTop )
2040 			{
2041 				++rPosRect.Top();
2042 				++rPosRect.Bottom();
2043 				++nField;
2044 			}
2045 		}
2046 
2047 		rOrient = sheet::DataPilotFieldOrientation_PAGE;
2048 		rDimPos = nField;						//!...
2049 		return sal_True;
2050 	}
2051 
2052 	return sal_False;
2053 }
2054 
2055 
2056 
2057