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