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