1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <scitems.hxx>
21 #include <editeng/borderline.hxx>
22 #include <editeng/boxitem.hxx>
23 #include <editeng/wghtitem.hxx>
24 #include <editeng/justifyitem.hxx>
25 #include <o3tl/safeint.hxx>
26 #include <osl/diagnose.h>
27 #include <svl/itemset.hxx>
28
29 #include <dpoutput.hxx>
30 #include <document.hxx>
31 #include <attrib.hxx>
32 #include <formula/errorcodes.hxx>
33 #include <miscuno.hxx>
34 #include <globstr.hrc>
35 #include <stlpool.hxx>
36 #include <stlsheet.hxx>
37 #include <scresid.hxx>
38 #include <unonames.hxx>
39 #include <strings.hrc>
40 #include <stringutil.hxx>
41 #include <dputil.hxx>
42
43 #include <com/sun/star/beans/XPropertySet.hpp>
44 #include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
45 #include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
46 #include <com/sun/star/sheet/DataPilotTablePositionData.hpp>
47 #include <com/sun/star/sheet/DataPilotTableResultData.hpp>
48 #include <com/sun/star/sheet/MemberResultFlags.hpp>
49 #include <com/sun/star/sheet/DataResultFlags.hpp>
50 #include <com/sun/star/sheet/DataPilotTablePositionType.hpp>
51 #include <com/sun/star/sheet/GeneralFunction2.hpp>
52 #include <com/sun/star/sheet/MemberResult.hpp>
53 #include <com/sun/star/sheet/XDataPilotMemberResults.hpp>
54 #include <com/sun/star/sheet/XDataPilotResults.hpp>
55 #include <com/sun/star/sheet/XDimensionsSupplier.hpp>
56 #include <com/sun/star/sheet/XHierarchiesSupplier.hpp>
57 #include <com/sun/star/sheet/XLevelsSupplier.hpp>
58 #include <com/sun/star/sheet/XMembersAccess.hpp>
59 #include <com/sun/star/sheet/XMembersSupplier.hpp>
60
61 #include <string_view>
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
73 #define SC_DP_FRAME_INNER_BOLD 20
74 #define SC_DP_FRAME_OUTER_BOLD 40
75
76 #define SC_DP_FRAME_COLOR Color(0,0,0) //( 0x20, 0x40, 0x68 )
77
78 struct ScDPOutLevelData
79 {
80 tools::Long mnDim;
81 tools::Long mnHier;
82 tools::Long mnLevel;
83 tools::Long mnDimPos;
84 sal_uInt32 mnSrcNumFmt; /// Prevailing number format used in the source data.
85 uno::Sequence<sheet::MemberResult> maResult;
86 OUString maName; /// Name is the internal field name.
87 OUString maCaption; /// Caption is the name visible in the output table.
88 bool mbHasHiddenMember:1;
89 bool mbDataLayout:1;
90 bool mbPageDim:1;
91
ScDPOutLevelDataScDPOutLevelData92 ScDPOutLevelData(tools::Long nDim, tools::Long nHier, tools::Long nLevel, tools::Long nDimPos, sal_uInt32 nSrcNumFmt, const uno::Sequence<sheet::MemberResult> &aResult,
93 const OUString &aName, const OUString &aCaption, bool bHasHiddenMember, bool bDataLayout, bool bPageDim) :
94 mnDim(nDim), mnHier(nHier), mnLevel(nLevel), mnDimPos(nDimPos), mnSrcNumFmt(nSrcNumFmt), maResult(aResult),
95 maName(aName), maCaption(aCaption), mbHasHiddenMember(bHasHiddenMember), mbDataLayout(bDataLayout),
96 mbPageDim(bPageDim)
97 {
98 }
99
100 // bug (73840) in uno::Sequence - copy and then assign doesn't work!
101 };
102
103
104
105 namespace {
106 struct ScDPOutLevelDataComparator
107 {
operator ()__anon2ffb30190111::ScDPOutLevelDataComparator108 bool operator()(const ScDPOutLevelData & rA, const ScDPOutLevelData & rB)
109 {
110 return rA.mnDimPos<rB.mnDimPos || ( rA.mnDimPos==rB.mnDimPos && rA.mnHier<rB.mnHier ) ||
111 ( rA.mnDimPos==rB.mnDimPos && rA.mnHier==rB.mnHier && rA.mnLevel<rB.mnLevel );
112 }
113 };
114
115
116 class ScDPOutputImpl
117 {
118 ScDocument* mpDoc;
119 sal_uInt16 mnTab;
120 ::std::vector< bool > mbNeedLineCols;
121 ::std::vector< SCCOL > mnCols;
122
123 ::std::vector< bool > mbNeedLineRows;
124 ::std::vector< SCROW > mnRows;
125
126 SCCOL mnTabStartCol;
127 SCROW mnTabStartRow;
128
129 SCCOL mnDataStartCol;
130 SCROW mnDataStartRow;
131 SCCOL mnTabEndCol;
132 SCROW mnTabEndRow;
133
134 public:
135 ScDPOutputImpl( ScDocument* pDoc, sal_uInt16 nTab,
136 SCCOL nTabStartCol,
137 SCROW nTabStartRow,
138 SCCOL nDataStartCol,
139 SCROW nDataStartRow,
140 SCCOL nTabEndCol,
141 SCROW nTabEndRow );
142 bool AddRow( SCROW nRow );
143 bool AddCol( SCCOL nCol );
144
145 void OutputDataArea();
146 void OutputBlockFrame ( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, bool bHori = false );
147
148 };
149
OutputDataArea()150 void ScDPOutputImpl::OutputDataArea()
151 {
152 AddRow( mnDataStartRow );
153 AddCol( mnDataStartCol );
154
155 mnCols.push_back( mnTabEndCol+1); //set last row bottom
156 mnRows.push_back( mnTabEndRow+1); //set last col bottom
157
158 bool bAllRows = ( ( mnTabEndRow - mnDataStartRow + 2 ) == static_cast<SCROW>(mnRows.size()) );
159
160 std::sort( mnCols.begin(), mnCols.end());
161 std::sort( mnRows.begin(), mnRows.end());
162
163 for( SCCOL nCol = 0; nCol < static_cast<SCCOL>(mnCols.size())-1; nCol ++ )
164 {
165 if ( !bAllRows )
166 {
167 if ( nCol < static_cast<SCCOL>(mnCols.size())-2)
168 {
169 for ( SCROW i = nCol%2; i < static_cast<SCROW>(mnRows.size())-2; i +=2 )
170 OutputBlockFrame( mnCols[nCol], mnRows[i], mnCols[nCol+1]-1, mnRows[i+1]-1 );
171 if ( mnRows.size()>=2 )
172 OutputBlockFrame( mnCols[nCol], mnRows[mnRows.size()-2], mnCols[nCol+1]-1, mnRows[mnRows.size()-1]-1 );
173 }
174 else
175 {
176 for ( SCROW i = 0 ; i < static_cast<SCROW>(mnRows.size())-1; i++ )
177 OutputBlockFrame( mnCols[nCol], mnRows[i], mnCols[nCol+1]-1, mnRows[i+1]-1 );
178 }
179 }
180 else
181 OutputBlockFrame( mnCols[nCol], mnRows.front(), mnCols[nCol+1]-1, mnRows.back()-1, bAllRows );
182 }
183 //out put rows area outer framer
184 if ( mnTabStartCol != mnDataStartCol )
185 {
186 if ( mnTabStartRow != mnDataStartRow )
187 OutputBlockFrame( mnTabStartCol, mnTabStartRow, mnDataStartCol-1, mnDataStartRow-1 );
188 OutputBlockFrame( mnTabStartCol, mnDataStartRow, mnDataStartCol-1, mnTabEndRow );
189 }
190 //out put cols area outer framer
191 OutputBlockFrame( mnDataStartCol, mnTabStartRow, mnTabEndCol, mnDataStartRow-1 );
192 }
193
ScDPOutputImpl(ScDocument * pDoc,sal_uInt16 nTab,SCCOL nTabStartCol,SCROW nTabStartRow,SCCOL nDataStartCol,SCROW nDataStartRow,SCCOL nTabEndCol,SCROW nTabEndRow)194 ScDPOutputImpl::ScDPOutputImpl( ScDocument* pDoc, sal_uInt16 nTab,
195 SCCOL nTabStartCol,
196 SCROW nTabStartRow,
197 SCCOL nDataStartCol,
198 SCROW nDataStartRow,
199 SCCOL nTabEndCol,
200 SCROW nTabEndRow ):
201 mpDoc( pDoc ),
202 mnTab( nTab ),
203 mnTabStartCol( nTabStartCol ),
204 mnTabStartRow( nTabStartRow ),
205 mnDataStartCol ( nDataStartCol ),
206 mnDataStartRow ( nDataStartRow ),
207 mnTabEndCol( nTabEndCol ),
208 mnTabEndRow( nTabEndRow )
209 {
210 mbNeedLineCols.resize( nTabEndCol-nDataStartCol+1, false );
211 mbNeedLineRows.resize( nTabEndRow-nDataStartRow+1, false );
212
213 }
214
AddRow(SCROW nRow)215 bool ScDPOutputImpl::AddRow( SCROW nRow )
216 {
217 if ( !mbNeedLineRows[ nRow - mnDataStartRow ] )
218 {
219 mbNeedLineRows[ nRow - mnDataStartRow ] = true;
220 mnRows.push_back( nRow );
221 return true;
222 }
223 else
224 return false;
225 }
226
AddCol(SCCOL nCol)227 bool ScDPOutputImpl::AddCol( SCCOL nCol )
228 {
229
230 if ( !mbNeedLineCols[ nCol - mnDataStartCol ] )
231 {
232 mbNeedLineCols[ nCol - mnDataStartCol ] = true;
233 mnCols.push_back( nCol );
234 return true;
235 }
236 else
237 return false;
238 }
239
OutputBlockFrame(SCCOL nStartCol,SCROW nStartRow,SCCOL nEndCol,SCROW nEndRow,bool bHori)240 void ScDPOutputImpl::OutputBlockFrame ( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, bool bHori )
241 {
242 Color color = SC_DP_FRAME_COLOR;
243 ::editeng::SvxBorderLine aLine( &color, SC_DP_FRAME_INNER_BOLD );
244 ::editeng::SvxBorderLine aOutLine( &color, SC_DP_FRAME_OUTER_BOLD );
245
246 SvxBoxItem aBox( ATTR_BORDER );
247
248 if ( nStartCol == mnTabStartCol )
249 aBox.SetLine(&aOutLine, SvxBoxItemLine::LEFT);
250 else
251 aBox.SetLine(&aLine, SvxBoxItemLine::LEFT);
252
253 if ( nStartRow == mnTabStartRow )
254 aBox.SetLine(&aOutLine, SvxBoxItemLine::TOP);
255 else
256 aBox.SetLine(&aLine, SvxBoxItemLine::TOP);
257
258 if ( nEndCol == mnTabEndCol ) //bottom row
259 aBox.SetLine(&aOutLine, SvxBoxItemLine::RIGHT);
260 else
261 aBox.SetLine(&aLine, SvxBoxItemLine::RIGHT);
262
263 if ( nEndRow == mnTabEndRow ) //bottom
264 aBox.SetLine(&aOutLine, SvxBoxItemLine::BOTTOM);
265 else
266 aBox.SetLine(&aLine, SvxBoxItemLine::BOTTOM);
267
268 SvxBoxInfoItem aBoxInfo( ATTR_BORDER_INNER );
269 aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::VERT,false );
270 if ( bHori )
271 {
272 aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI);
273 aBoxInfo.SetLine( &aLine, SvxBoxInfoItemLine::HORI );
274 }
275 else
276 aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI,false );
277
278 aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::DISTANCE,false);
279
280 mpDoc->ApplyFrameAreaTab(ScRange(nStartCol, nStartRow, mnTab, nEndCol, nEndRow , mnTab), aBox, aBoxInfo);
281
282 }
283
lcl_SetStyleById(ScDocument * pDoc,SCTAB nTab,SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2,const char * pStrId)284 void lcl_SetStyleById(ScDocument* pDoc, SCTAB nTab,
285 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
286 const char* pStrId)
287 {
288 if ( nCol1 > nCol2 || nRow1 > nRow2 )
289 {
290 OSL_FAIL("SetStyleById: invalid range");
291 return;
292 }
293
294 OUString aStyleName = ScResId(pStrId);
295 ScStyleSheetPool* pStlPool = pDoc->GetStyleSheetPool();
296 ScStyleSheet* pStyle = static_cast<ScStyleSheet*>( pStlPool->Find( aStyleName, SfxStyleFamily::Para ) );
297 if (!pStyle)
298 {
299 // create new style (was in ScPivot::SetStyle)
300
301 pStyle = static_cast<ScStyleSheet*>( &pStlPool->Make( aStyleName, SfxStyleFamily::Para,
302 SfxStyleSearchBits::UserDefined ) );
303 pStyle->SetParent( ScResId(STR_STYLENAME_STANDARD) );
304 SfxItemSet& rSet = pStyle->GetItemSet();
305 if (strcmp(pStrId, STR_PIVOT_STYLENAME_RESULT) == 0 || strcmp(pStrId, STR_PIVOT_STYLENAME_TITLE) == 0){
306 rSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
307 rSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_CJK_FONT_WEIGHT ) );
308 rSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_CTL_FONT_WEIGHT ) );
309 }
310 if (strcmp(pStrId, STR_PIVOT_STYLENAME_CATEGORY) == 0 || strcmp(pStrId, STR_PIVOT_STYLENAME_TITLE) == 0)
311 rSet.Put( SvxHorJustifyItem( SvxCellHorJustify::Left, ATTR_HOR_JUSTIFY ) );
312 }
313
314 pDoc->ApplyStyleAreaTab( nCol1, nRow1, nCol2, nRow2, nTab, *pStyle );
315 }
316
lcl_SetFrame(ScDocument * pDoc,SCTAB nTab,SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2,sal_uInt16 nWidth)317 void lcl_SetFrame( ScDocument* pDoc, SCTAB nTab,
318 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
319 sal_uInt16 nWidth )
320 {
321 ::editeng::SvxBorderLine aLine(nullptr, nWidth, SvxBorderLineStyle::SOLID);
322 SvxBoxItem aBox( ATTR_BORDER );
323 aBox.SetLine(&aLine, SvxBoxItemLine::LEFT);
324 aBox.SetLine(&aLine, SvxBoxItemLine::TOP);
325 aBox.SetLine(&aLine, SvxBoxItemLine::RIGHT);
326 aBox.SetLine(&aLine, SvxBoxItemLine::BOTTOM);
327 SvxBoxInfoItem aBoxInfo( ATTR_BORDER_INNER );
328 aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI,false);
329 aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::VERT,false);
330 aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::DISTANCE,false);
331
332 pDoc->ApplyFrameAreaTab(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab), aBox, aBoxInfo);
333 }
334
lcl_FillNumberFormats(std::unique_ptr<sal_uInt32[]> & rFormats,sal_Int32 & rCount,const uno::Reference<sheet::XDataPilotMemberResults> & xLevRes,const uno::Reference<container::XIndexAccess> & xDims)335 void lcl_FillNumberFormats( std::unique_ptr<sal_uInt32[]>& rFormats, sal_Int32& rCount,
336 const uno::Reference<sheet::XDataPilotMemberResults>& xLevRes,
337 const uno::Reference<container::XIndexAccess>& xDims )
338 {
339 if ( rFormats )
340 return; // already set
341
342 // xLevRes is from the data layout dimension
343 //TODO: use result sequence from ScDPOutLevelData!
344
345 uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults();
346
347 tools::Long nSize = aResult.getLength();
348 if (!nSize)
349 return;
350
351 // get names/formats for all data dimensions
352 //TODO: merge this with the loop to collect ScDPOutLevelData?
353
354 std::vector <OUString> aDataNames;
355 std::vector <sal_uInt32> aDataFormats;
356 sal_Int32 nDimCount = xDims->getCount();
357 sal_Int32 nDim = 0;
358 for ( ; nDim < nDimCount ; nDim++)
359 {
360 uno::Reference<uno::XInterface> xDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
361 uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
362 uno::Reference<container::XNamed> xDimName( xDim, uno::UNO_QUERY );
363 if ( xDimProp.is() && xDimName.is() )
364 {
365 sheet::DataPilotFieldOrientation eDimOrient =
366 ScUnoHelpFunctions::GetEnumProperty(
367 xDimProp, SC_UNO_DP_ORIENTATION,
368 sheet::DataPilotFieldOrientation_HIDDEN );
369 if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
370 {
371 aDataNames.push_back(xDimName->getName());
372 tools::Long nFormat = ScUnoHelpFunctions::GetLongProperty(
373 xDimProp,
374 SC_UNONAME_NUMFMT );
375 aDataFormats.push_back(nFormat);
376 }
377 }
378 }
379
380 if (aDataFormats.empty())
381 return;
382
383 const sheet::MemberResult* pArray = aResult.getConstArray();
384
385 OUString aName;
386 sal_uInt32* pNumFmt = new sal_uInt32[nSize];
387 if (aDataFormats.size() == 1)
388 {
389 // only one data dimension -> use its numberformat everywhere
390 tools::Long nFormat = aDataFormats[0];
391 for (tools::Long nPos=0; nPos<nSize; nPos++)
392 pNumFmt[nPos] = nFormat;
393 }
394 else
395 {
396 for (tools::Long nPos=0; nPos<nSize; nPos++)
397 {
398 // if CONTINUE bit is set, keep previous name
399 //TODO: keep number format instead!
400 if ( !(pArray[nPos].Flags & sheet::MemberResultFlags::CONTINUE) )
401 aName = pArray[nPos].Name;
402
403 sal_uInt32 nFormat = 0;
404 for (size_t i=0; i<aDataFormats.size(); i++)
405 if (aName == aDataNames[i]) //TODO: search more efficiently?
406 {
407 nFormat = aDataFormats[i];
408 break;
409 }
410 pNumFmt[nPos] = nFormat;
411 }
412 }
413
414 rFormats.reset( pNumFmt );
415 rCount = nSize;
416 }
417
lcl_GetFirstNumberFormat(const uno::Reference<container::XIndexAccess> & xDims)418 sal_uInt32 lcl_GetFirstNumberFormat( const uno::Reference<container::XIndexAccess>& xDims )
419 {
420 tools::Long nDimCount = xDims->getCount();
421 for (tools::Long nDim=0; nDim<nDimCount; nDim++)
422 {
423 uno::Reference<beans::XPropertySet> xDimProp(xDims->getByIndex(nDim), uno::UNO_QUERY);
424 if ( xDimProp.is() )
425 {
426 sheet::DataPilotFieldOrientation eDimOrient =
427 ScUnoHelpFunctions::GetEnumProperty(
428 xDimProp, SC_UNO_DP_ORIENTATION,
429 sheet::DataPilotFieldOrientation_HIDDEN );
430 if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
431 {
432 tools::Long nFormat = ScUnoHelpFunctions::GetLongProperty(
433 xDimProp,
434 SC_UNONAME_NUMFMT );
435
436 return nFormat; // use format from first found data dimension
437 }
438 }
439 }
440
441 return 0; // none found
442 }
443
lcl_MemberEmpty(const uno::Sequence<sheet::MemberResult> & rSeq)444 bool lcl_MemberEmpty( const uno::Sequence<sheet::MemberResult>& rSeq )
445 {
446 // used to skip levels that have no members
447
448 return std::none_of(rSeq.begin(), rSeq.end(),
449 [](const sheet::MemberResult& rMem) {
450 return rMem.Flags & sheet::MemberResultFlags::HASMEMBER; });
451 }
452
453 /**
454 * Get visible page dimension members as results, except that, if all
455 * members are visible, then this function returns empty result.
456 */
getVisiblePageMembersAsResults(const uno::Reference<uno::XInterface> & xLevel)457 uno::Sequence<sheet::MemberResult> getVisiblePageMembersAsResults( const uno::Reference<uno::XInterface>& xLevel )
458 {
459 if (!xLevel.is())
460 return uno::Sequence<sheet::MemberResult>();
461
462 uno::Reference<sheet::XMembersSupplier> xMSupplier(xLevel, UNO_QUERY);
463 if (!xMSupplier.is())
464 return uno::Sequence<sheet::MemberResult>();
465
466 uno::Reference<sheet::XMembersAccess> xNA = xMSupplier->getMembers();
467 if (!xNA.is())
468 return uno::Sequence<sheet::MemberResult>();
469
470 std::vector<sheet::MemberResult> aRes;
471 const uno::Sequence<OUString> aNames = xNA->getElementNames();
472 for (const OUString& rName : aNames)
473 {
474 xNA->getByName(rName);
475
476 uno::Reference<beans::XPropertySet> xMemPS(xNA->getByName(rName), UNO_QUERY);
477 if (!xMemPS.is())
478 continue;
479
480 OUString aCaption = ScUnoHelpFunctions::GetStringProperty(xMemPS, SC_UNO_DP_LAYOUTNAME, OUString());
481 if (aCaption.isEmpty())
482 aCaption = rName;
483
484 bool bVisible = ScUnoHelpFunctions::GetBoolProperty(xMemPS, SC_UNO_DP_ISVISIBLE);
485
486 if (bVisible)
487 {
488 /* TODO: any numeric value to obtain? */
489 double fValue;
490 rtl::math::setNan(&fValue);
491 aRes.emplace_back(rName, aCaption, 0, fValue);
492 }
493 }
494
495 if (aNames.getLength() == static_cast<sal_Int32>(aRes.size()))
496 // All members are visible. Return empty result.
497 return uno::Sequence<sheet::MemberResult>();
498
499 return ScUnoHelpFunctions::VectorToSequence(aRes);
500 }
501
502 }
503
ScDPOutput(ScDocument * pD,const uno::Reference<sheet::XDimensionsSupplier> & xSrc,const ScAddress & rPos,bool bFilter)504 ScDPOutput::ScDPOutput( ScDocument* pD, const uno::Reference<sheet::XDimensionsSupplier>& xSrc,
505 const ScAddress& rPos, bool bFilter ) :
506 pDoc( pD ),
507 xSource( xSrc ),
508 aStartPos( rPos ),
509 nColFmtCount( 0 ),
510 nRowFmtCount( 0 ),
511 nSingleNumFmt( 0 ),
512 nColCount(0),
513 nRowCount(0),
514 nHeaderSize(0),
515 bDoFilter(bFilter),
516 bResultsError(false),
517 bSizesValid(false),
518 bSizeOverflow(false),
519 mbHeaderLayout(false)
520 {
521 nTabStartCol = nMemberStartCol = nDataStartCol = nTabEndCol = 0;
522 nTabStartRow = nMemberStartRow = nDataStartRow = nTabEndRow = 0;
523
524 uno::Reference<sheet::XDataPilotResults> xResult( xSource, uno::UNO_QUERY );
525 if ( xSource.is() && xResult.is() )
526 {
527 // get dimension results:
528
529 uno::Reference<container::XIndexAccess> xDims =
530 new ScNameToIndexAccess( xSource->getDimensions() );
531 tools::Long nDimCount = xDims->getCount();
532 for (tools::Long nDim=0; nDim<nDimCount; nDim++)
533 {
534 uno::Reference<uno::XInterface> xDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
535 uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
536 uno::Reference<sheet::XHierarchiesSupplier> xDimSupp( xDim, uno::UNO_QUERY );
537 if ( xDimProp.is() && xDimSupp.is() )
538 {
539 sheet::DataPilotFieldOrientation eDimOrient =
540 ScUnoHelpFunctions::GetEnumProperty(
541 xDimProp, SC_UNO_DP_ORIENTATION,
542 sheet::DataPilotFieldOrientation_HIDDEN );
543 tools::Long nDimPos = ScUnoHelpFunctions::GetLongProperty( xDimProp,
544 SC_UNO_DP_POSITION );
545 bool bIsDataLayout = ScUnoHelpFunctions::GetBoolProperty(
546 xDimProp, SC_UNO_DP_ISDATALAYOUT);
547 bool bHasHiddenMember = ScUnoHelpFunctions::GetBoolProperty(
548 xDimProp, SC_UNO_DP_HAS_HIDDEN_MEMBER);
549 sal_Int32 nNumFmt = ScUnoHelpFunctions::GetLongProperty(
550 xDimProp, SC_UNO_DP_NUMBERFO);
551
552 if ( eDimOrient != sheet::DataPilotFieldOrientation_HIDDEN )
553 {
554 uno::Reference<container::XIndexAccess> xHiers =
555 new ScNameToIndexAccess( xDimSupp->getHierarchies() );
556 tools::Long nHierarchy = ScUnoHelpFunctions::GetLongProperty(
557 xDimProp,
558 SC_UNO_DP_USEDHIERARCHY );
559 if ( nHierarchy >= xHiers->getCount() )
560 nHierarchy = 0;
561
562 uno::Reference<sheet::XLevelsSupplier> xHierSupp(xHiers->getByIndex(nHierarchy),
563 uno::UNO_QUERY);
564 if ( xHierSupp.is() )
565 {
566 uno::Reference<container::XIndexAccess> xLevels =
567 new ScNameToIndexAccess( xHierSupp->getLevels() );
568 tools::Long nLevCount = xLevels->getCount();
569 for (tools::Long nLev=0; nLev<nLevCount; nLev++)
570 {
571 uno::Reference<uno::XInterface> xLevel(xLevels->getByIndex(nLev),
572 uno::UNO_QUERY);
573 uno::Reference<container::XNamed> xLevNam( xLevel, uno::UNO_QUERY );
574 uno::Reference<sheet::XDataPilotMemberResults> xLevRes(
575 xLevel, uno::UNO_QUERY );
576 if ( xLevNam.is() && xLevRes.is() )
577 {
578 OUString aName = xLevNam->getName();
579 Reference<XPropertySet> xPropSet(xLevel, UNO_QUERY);
580 // Caption equals the field name by default.
581 // #i108948# use ScUnoHelpFunctions::GetStringProperty, because
582 // LayoutName is new and may not be present in external implementation
583 OUString aCaption = ScUnoHelpFunctions::GetStringProperty( xPropSet,
584 SC_UNO_DP_LAYOUTNAME, aName );
585
586 switch ( eDimOrient )
587 {
588 case sheet::DataPilotFieldOrientation_COLUMN:
589 {
590 uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults();
591 if (!lcl_MemberEmpty(aResult))
592 {
593 ScDPOutLevelData tmp(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName,
594 aCaption, bHasHiddenMember, bIsDataLayout, false);
595 pColFields.push_back(tmp);
596 }
597 }
598 break;
599 case sheet::DataPilotFieldOrientation_ROW:
600 {
601 uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults();
602 if (!lcl_MemberEmpty(aResult))
603 {
604 ScDPOutLevelData tmp(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName,
605 aCaption, bHasHiddenMember, bIsDataLayout, false);
606 pRowFields.push_back(tmp);
607 }
608 }
609 break;
610 case sheet::DataPilotFieldOrientation_PAGE:
611 {
612 uno::Sequence<sheet::MemberResult> aResult = getVisiblePageMembersAsResults(xLevel);
613 // no check on results for page fields
614 ScDPOutLevelData tmp(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName,
615 aCaption, bHasHiddenMember, false, true);
616 pPageFields.push_back(tmp);
617 }
618 break;
619 default:
620 {
621 // added to avoid warnings
622 }
623 }
624
625 // get number formats from data dimensions
626 if ( bIsDataLayout )
627 {
628 OSL_ENSURE( nLevCount == 1, "data layout: multiple levels?" );
629 if ( eDimOrient == sheet::DataPilotFieldOrientation_COLUMN )
630 lcl_FillNumberFormats( pColNumFmt, nColFmtCount, xLevRes, xDims );
631 else if ( eDimOrient == sheet::DataPilotFieldOrientation_ROW )
632 lcl_FillNumberFormats( pRowNumFmt, nRowFmtCount, xLevRes, xDims );
633 }
634 }
635 }
636 }
637 }
638 else if ( bIsDataLayout )
639 {
640 // data layout dimension is hidden (allowed if there is only one data dimension)
641 // -> use the number format from the first data dimension for all results
642
643 nSingleNumFmt = lcl_GetFirstNumberFormat( xDims );
644 }
645 }
646 }
647 std::sort(pColFields.begin(), pColFields.end(), ScDPOutLevelDataComparator());
648 std::sort(pRowFields.begin(), pRowFields.end(), ScDPOutLevelDataComparator());
649 std::sort(pPageFields.begin(), pPageFields.end(), ScDPOutLevelDataComparator());
650
651 // get data results:
652
653 try
654 {
655 aData = xResult->getResults();
656 }
657 catch (const uno::RuntimeException&)
658 {
659 bResultsError = true;
660 }
661 }
662
663 // get "DataDescription" property (may be missing in external sources)
664
665 uno::Reference<beans::XPropertySet> xSrcProp( xSource, uno::UNO_QUERY );
666 if ( !xSrcProp.is() )
667 return;
668
669 try
670 {
671 uno::Any aAny = xSrcProp->getPropertyValue( SC_UNO_DP_DATADESC );
672 OUString aUStr;
673 aAny >>= aUStr;
674 aDataDescription = aUStr;
675 }
676 catch(const uno::Exception&)
677 {
678 }
679 }
680
~ScDPOutput()681 ScDPOutput::~ScDPOutput()
682 {
683 }
684
SetPosition(const ScAddress & rPos)685 void ScDPOutput::SetPosition( const ScAddress& rPos )
686 {
687 aStartPos = rPos;
688 bSizesValid = bSizeOverflow = false;
689 }
690
DataCell(SCCOL nCol,SCROW nRow,SCTAB nTab,const sheet::DataResult & rData)691 void ScDPOutput::DataCell( SCCOL nCol, SCROW nRow, SCTAB nTab, const sheet::DataResult& rData )
692 {
693 tools::Long nFlags = rData.Flags;
694 if ( nFlags & sheet::DataResultFlags::ERROR )
695 {
696 pDoc->SetError( nCol, nRow, nTab, FormulaError::NoValue );
697 }
698 else if ( nFlags & sheet::DataResultFlags::HASDATA )
699 {
700 pDoc->SetValue( nCol, nRow, nTab, rData.Value );
701
702 // use number formats from source
703
704 OSL_ENSURE( bSizesValid, "DataCell: !bSizesValid" );
705 sal_uInt32 nFormat = 0;
706 bool bApplyFormat = false;
707 if ( pColNumFmt )
708 {
709 if ( nCol >= nDataStartCol )
710 {
711 tools::Long nIndex = nCol - nDataStartCol;
712 if ( nIndex < nColFmtCount )
713 {
714 nFormat = pColNumFmt[nIndex];
715 bApplyFormat = true;
716 }
717 }
718 }
719 else if ( pRowNumFmt )
720 {
721 if ( nRow >= nDataStartRow )
722 {
723 tools::Long nIndex = nRow - nDataStartRow;
724 if ( nIndex < nRowFmtCount )
725 {
726 nFormat = pRowNumFmt[nIndex];
727 bApplyFormat = true;
728 }
729 }
730 }
731 else if ( nSingleNumFmt != 0 )
732 {
733 nFormat = nSingleNumFmt; // single format is used everywhere
734 bApplyFormat = true;
735 }
736
737 if (bApplyFormat)
738 pDoc->ApplyAttr(nCol, nRow, nTab, SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
739 }
740 // SubTotal formatting is controlled by headers
741 }
742
HeaderCell(SCCOL nCol,SCROW nRow,SCTAB nTab,const sheet::MemberResult & rData,bool bColHeader,tools::Long nLevel)743 void ScDPOutput::HeaderCell( SCCOL nCol, SCROW nRow, SCTAB nTab,
744 const sheet::MemberResult& rData, bool bColHeader, tools::Long nLevel )
745 {
746 tools::Long nFlags = rData.Flags;
747
748 if ( nFlags & sheet::MemberResultFlags::HASMEMBER )
749 {
750 bool bNumeric = (nFlags & sheet::MemberResultFlags::NUMERIC) != 0;
751 if (bNumeric && std::isfinite( rData.Value))
752 {
753 pDoc->SetValue( nCol, nRow, nTab, rData.Value);
754 }
755 else
756 {
757 ScSetStringParam aParam;
758 if (bNumeric)
759 aParam.setNumericInput();
760 else
761 aParam.setTextInput();
762
763 pDoc->SetString(nCol, nRow, nTab, rData.Caption, &aParam);
764 }
765 }
766
767 if ( !(nFlags & sheet::MemberResultFlags::SUBTOTAL) )
768 return;
769
770 ScDPOutputImpl outputimp( pDoc, nTab,
771 nTabStartCol, nTabStartRow,
772 nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow );
773 //TODO: limit frames to horizontal or vertical?
774 if (bColHeader)
775 {
776 outputimp.OutputBlockFrame( nCol,nMemberStartRow+static_cast<SCROW>(nLevel), nCol,nDataStartRow-1 );
777
778 lcl_SetStyleById( pDoc,nTab, nCol,nMemberStartRow+static_cast<SCROW>(nLevel), nCol,nDataStartRow-1,
779 STR_PIVOT_STYLENAME_TITLE );
780 lcl_SetStyleById( pDoc,nTab, nCol,nDataStartRow, nCol,nTabEndRow,
781 STR_PIVOT_STYLENAME_RESULT );
782 }
783 else
784 {
785 outputimp.OutputBlockFrame( nMemberStartCol+static_cast<SCCOL>(nLevel),nRow, nDataStartCol-1,nRow );
786 lcl_SetStyleById( pDoc,nTab, nMemberStartCol+static_cast<SCCOL>(nLevel),nRow, nDataStartCol-1,nRow,
787 STR_PIVOT_STYLENAME_TITLE );
788 lcl_SetStyleById( pDoc,nTab, nDataStartCol,nRow, nTabEndCol,nRow,
789 STR_PIVOT_STYLENAME_RESULT );
790 }
791 }
792
FieldCell(SCCOL nCol,SCROW nRow,SCTAB nTab,const ScDPOutLevelData & rData,bool bInTable)793 void ScDPOutput::FieldCell(
794 SCCOL nCol, SCROW nRow, SCTAB nTab, const ScDPOutLevelData& rData, bool bInTable)
795 {
796 // Avoid unwanted automatic format detection.
797 ScSetStringParam aParam;
798 aParam.mbDetectNumberFormat = false;
799 aParam.meSetTextNumFormat = ScSetStringParam::Always;
800 aParam.mbHandleApostrophe = false;
801 pDoc->SetString(nCol, nRow, nTab, rData.maCaption, &aParam);
802
803 if (bInTable)
804 lcl_SetFrame( pDoc,nTab, nCol,nRow, nCol,nRow, 20 );
805
806 // For field button drawing
807 ScMF nMergeFlag = ScMF::NONE;
808 if (rData.mbHasHiddenMember)
809 nMergeFlag |= ScMF::HiddenMember;
810
811 if (rData.mbPageDim)
812 {
813 nMergeFlag |= ScMF::ButtonPopup;
814 pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, ScMF::Button);
815 pDoc->ApplyFlagsTab(nCol+1, nRow, nCol+1, nRow, nTab, nMergeFlag);
816 }
817 else
818 {
819 nMergeFlag |= ScMF::Button;
820 if (!rData.mbDataLayout)
821 nMergeFlag |= ScMF::ButtonPopup;
822 pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, nMergeFlag);
823 }
824
825 lcl_SetStyleById( pDoc,nTab, nCol,nRow, nCol,nRow, STR_PIVOT_STYLENAME_FIELDNAME );
826 }
827
lcl_DoFilterButton(ScDocument * pDoc,SCCOL nCol,SCROW nRow,SCTAB nTab)828 static void lcl_DoFilterButton( ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
829 {
830 pDoc->SetString( nCol, nRow, nTab, ScResId(STR_CELL_FILTER) );
831 pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, ScMF::Button);
832 }
833
CalcSizes()834 void ScDPOutput::CalcSizes()
835 {
836 if (bSizesValid)
837 return;
838
839 // get column size of data from first row
840 //TODO: allow different sizes (and clear following areas) ???
841
842 nRowCount = aData.getLength();
843 const uno::Sequence<sheet::DataResult>* pRowAry = aData.getConstArray();
844 nColCount = nRowCount ? ( pRowAry[0].getLength() ) : 0;
845
846 nHeaderSize = 1;
847 if (GetHeaderLayout() && pColFields.empty())
848 // Insert an extra header row only when there is no column field.
849 nHeaderSize = 2;
850
851 // calculate output positions and sizes
852
853 tools::Long nPageSize = 0; // use page fields!
854 if ( bDoFilter || !pPageFields.empty() )
855 {
856 nPageSize += pPageFields.size() + 1; // plus one empty row
857 if ( bDoFilter )
858 ++nPageSize; // filter button above the page fields
859 }
860
861 if ( aStartPos.Col() + static_cast<tools::Long>(pRowFields.size()) + nColCount - 1 > MAXCOL ||
862 aStartPos.Row() + nPageSize + nHeaderSize + pColFields.size() + nRowCount > MAXROW )
863 {
864 bSizeOverflow = true;
865 }
866
867 nTabStartCol = aStartPos.Col();
868 nTabStartRow = aStartPos.Row() + static_cast<SCROW>(nPageSize); // below page fields
869 nMemberStartCol = nTabStartCol;
870 nMemberStartRow = nTabStartRow + static_cast<SCROW>(nHeaderSize);
871 nDataStartCol = nMemberStartCol + static_cast<SCCOL>(pRowFields.size());
872 nDataStartRow = nMemberStartRow + static_cast<SCROW>(pColFields.size());
873 if ( nColCount > 0 )
874 nTabEndCol = nDataStartCol + static_cast<SCCOL>(nColCount) - 1;
875 else
876 nTabEndCol = nDataStartCol; // single column will remain empty
877 // if page fields are involved, include the page selection cells
878 if ( !pPageFields.empty() && nTabEndCol < nTabStartCol + 1 )
879 nTabEndCol = nTabStartCol + 1;
880 if ( nRowCount > 0 )
881 nTabEndRow = nDataStartRow + static_cast<SCROW>(nRowCount) - 1;
882 else
883 nTabEndRow = nDataStartRow; // single row will remain empty
884 bSizesValid = true;
885 }
886
GetPositionType(const ScAddress & rPos)887 sal_Int32 ScDPOutput::GetPositionType(const ScAddress& rPos)
888 {
889 using namespace ::com::sun::star::sheet;
890
891 SCCOL nCol = rPos.Col();
892 SCROW nRow = rPos.Row();
893 SCTAB nTab = rPos.Tab();
894 if ( nTab != aStartPos.Tab() )
895 return DataPilotTablePositionType::NOT_IN_TABLE;
896
897 CalcSizes();
898
899 // Make sure the cursor is within the table.
900 if (nCol < nTabStartCol || nRow < nTabStartRow || nCol > nTabEndCol || nRow > nTabEndRow)
901 return DataPilotTablePositionType::NOT_IN_TABLE;
902
903 // test for result data area.
904 if (nCol >= nDataStartCol && nCol <= nTabEndCol && nRow >= nDataStartRow && nRow <= nTabEndRow)
905 return DataPilotTablePositionType::RESULT;
906
907 bool bInColHeader = (nRow >= nTabStartRow && nRow < nDataStartRow);
908 bool bInRowHeader = (nCol >= nTabStartCol && nCol < nDataStartCol);
909
910 if (bInColHeader && bInRowHeader)
911 // probably in that ugly little box at the upper-left corner of the table.
912 return DataPilotTablePositionType::OTHER;
913
914 if (bInColHeader)
915 {
916 if (nRow == nTabStartRow)
917 // first row in the column header area is always used for column
918 // field buttons.
919 return DataPilotTablePositionType::OTHER;
920
921 return DataPilotTablePositionType::COLUMN_HEADER;
922 }
923
924 if (bInRowHeader)
925 return DataPilotTablePositionType::ROW_HEADER;
926
927 return DataPilotTablePositionType::OTHER;
928 }
929
Output()930 void ScDPOutput::Output()
931 {
932 SCTAB nTab = aStartPos.Tab();
933 const uno::Sequence<sheet::DataResult>* pRowAry = aData.getConstArray();
934
935 // calculate output positions and sizes
936
937 CalcSizes();
938 if ( bSizeOverflow || bResultsError ) // does output area exceed sheet limits?
939 return; // nothing
940
941 // clear whole (new) output area
942 // when modifying table, clear old area !
943 //TODO: include InsertDeleteFlags::OBJECTS ???
944 pDoc->DeleteAreaTab( aStartPos.Col(), aStartPos.Row(), nTabEndCol, nTabEndRow, nTab, InsertDeleteFlags::ALL );
945
946 if ( bDoFilter )
947 lcl_DoFilterButton( pDoc, aStartPos.Col(), aStartPos.Row(), nTab );
948
949 // output page fields:
950
951 for (size_t nField=0; nField<pPageFields.size(); ++nField)
952 {
953 SCCOL nHdrCol = aStartPos.Col();
954 SCROW nHdrRow = aStartPos.Row() + nField + ( bDoFilter ? 1 : 0 );
955 // draw without frame for consistency with filter button:
956 FieldCell(nHdrCol, nHdrRow, nTab, pPageFields[nField], false);
957 SCCOL nFldCol = nHdrCol + 1;
958
959 OUString aPageValue = ScResId(SCSTR_ALL);
960 const uno::Sequence<sheet::MemberResult>& rRes = pPageFields[nField].maResult;
961 sal_Int32 n = rRes.getLength();
962 if (n == 1)
963 {
964 if (rRes[0].Caption.isEmpty())
965 aPageValue = ScResId(STR_EMPTYDATA);
966 else
967 aPageValue = rRes[0].Caption;
968 }
969 else if (n > 1)
970 aPageValue = ScResId(SCSTR_MULTIPLE);
971
972 ScSetStringParam aParam;
973 aParam.setTextInput();
974 pDoc->SetString(nFldCol, nHdrRow, nTab, aPageValue, &aParam);
975
976 lcl_SetFrame( pDoc,nTab, nFldCol,nHdrRow, nFldCol,nHdrRow, 20 );
977 }
978
979 // data description
980 // (may get overwritten by first row field)
981
982 if (aDataDescription.isEmpty())
983 {
984 //TODO: use default string ("result") ?
985 }
986 pDoc->SetString(nTabStartCol, nTabStartRow, nTab, aDataDescription);
987
988 // set STR_PIVOT_STYLENAME_INNER for whole data area (subtotals are overwritten)
989
990 if ( nDataStartRow > nTabStartRow )
991 lcl_SetStyleById( pDoc, nTab, nTabStartCol, nTabStartRow, nTabEndCol, nDataStartRow-1,
992 STR_PIVOT_STYLENAME_TOP );
993 lcl_SetStyleById( pDoc, nTab, nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow,
994 STR_PIVOT_STYLENAME_INNER );
995
996 // output column headers:
997 ScDPOutputImpl outputimp( pDoc, nTab,
998 nTabStartCol, nTabStartRow,
999 nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow );
1000 for (size_t nField=0; nField<pColFields.size(); nField++)
1001 {
1002 SCCOL nHdrCol = nDataStartCol + static_cast<SCCOL>(nField); //TODO: check for overflow
1003 FieldCell(nHdrCol, nTabStartRow, nTab, pColFields[nField], true);
1004
1005 SCROW nRowPos = nMemberStartRow + static_cast<SCROW>(nField); //TODO: check for overflow
1006 const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nField].maResult;
1007 const sheet::MemberResult* pArray = rSequence.getConstArray();
1008 tools::Long nThisColCount = rSequence.getLength();
1009 OSL_ENSURE( nThisColCount == nColCount, "count mismatch" ); //TODO: ???
1010 for (tools::Long nCol=0; nCol<nThisColCount; nCol++)
1011 {
1012 SCCOL nColPos = nDataStartCol + static_cast<SCCOL>(nCol); //TODO: check for overflow
1013 HeaderCell( nColPos, nRowPos, nTab, pArray[nCol], true, nField );
1014 if ( ( pArray[nCol].Flags & sheet::MemberResultFlags::HASMEMBER ) &&
1015 !( pArray[nCol].Flags & sheet::MemberResultFlags::SUBTOTAL ) )
1016 {
1017 tools::Long nEnd = nCol;
1018 while ( nEnd+1 < nThisColCount && ( pArray[nEnd+1].Flags & sheet::MemberResultFlags::CONTINUE ) )
1019 ++nEnd;
1020 SCCOL nEndColPos = nDataStartCol + static_cast<SCCOL>(nEnd); //TODO: check for overflow
1021 if ( nField+1 < pColFields.size())
1022 {
1023 if ( nField == pColFields.size() - 2 )
1024 {
1025 outputimp.AddCol( nColPos );
1026 if ( nColPos + 1 == nEndColPos )
1027 outputimp.OutputBlockFrame( nColPos,nRowPos, nEndColPos,nRowPos+1, true );
1028 }
1029 else
1030 outputimp.OutputBlockFrame( nColPos,nRowPos, nEndColPos,nRowPos );
1031
1032 lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nEndColPos,nDataStartRow-1, STR_PIVOT_STYLENAME_CATEGORY );
1033 }
1034 else
1035 lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nColPos,nDataStartRow-1, STR_PIVOT_STYLENAME_CATEGORY );
1036 }
1037 else if ( pArray[nCol].Flags & sheet::MemberResultFlags::SUBTOTAL )
1038 outputimp.AddCol( nColPos );
1039
1040 // Apply the same number format as in data source.
1041 pDoc->ApplyAttr(nColPos, nRowPos, nTab, SfxUInt32Item(ATTR_VALUE_FORMAT, pColFields[nField].mnSrcNumFmt));
1042 }
1043 if ( nField== 0 && pColFields.size() == 1 )
1044 outputimp.OutputBlockFrame( nDataStartCol,nTabStartRow, nTabEndCol,nRowPos-1 );
1045 }
1046
1047 // output row headers:
1048 std::vector<bool> vbSetBorder;
1049 vbSetBorder.resize( nTabEndRow - nDataStartRow + 1, false );
1050 for (size_t nField=0; nField<pRowFields.size(); nField++)
1051 {
1052 SCCOL nHdrCol = nTabStartCol + static_cast<SCCOL>(nField); //TODO: check for overflow
1053 SCROW nHdrRow = nDataStartRow - 1;
1054 FieldCell(nHdrCol, nHdrRow, nTab, pRowFields[nField], true);
1055
1056 SCCOL nColPos = nMemberStartCol + static_cast<SCCOL>(nField); //TODO: check for overflow
1057 const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].maResult;
1058 const sheet::MemberResult* pArray = rSequence.getConstArray();
1059 sal_Int32 nThisRowCount = rSequence.getLength();
1060 OSL_ENSURE( nThisRowCount == nRowCount, "count mismatch" ); //TODO: ???
1061 for (sal_Int32 nRow=0; nRow<nThisRowCount; nRow++)
1062 {
1063 SCROW nRowPos = nDataStartRow + static_cast<SCROW>(nRow); //TODO: check for overflow
1064 HeaderCell( nColPos, nRowPos, nTab, pArray[nRow], false, nField );
1065 if ( ( pArray[nRow].Flags & sheet::MemberResultFlags::HASMEMBER ) &&
1066 !( pArray[nRow].Flags & sheet::MemberResultFlags::SUBTOTAL ) )
1067 {
1068 if ( nField+1 < pRowFields.size() )
1069 {
1070 tools::Long nEnd = nRow;
1071 while ( nEnd+1 < nThisRowCount && ( pArray[nEnd+1].Flags & sheet::MemberResultFlags::CONTINUE ) )
1072 ++nEnd;
1073 SCROW nEndRowPos = nDataStartRow + static_cast<SCROW>(nEnd); //TODO: check for overflow
1074 outputimp.AddRow( nRowPos );
1075 if ( !vbSetBorder[ nRow ] )
1076 {
1077 outputimp.OutputBlockFrame( nColPos, nRowPos, nTabEndCol, nEndRowPos );
1078 vbSetBorder[ nRow ] = true;
1079 }
1080 outputimp.OutputBlockFrame( nColPos, nRowPos, nColPos, nEndRowPos );
1081
1082 if ( nField == pRowFields.size() - 2 )
1083 outputimp.OutputBlockFrame( nColPos+1, nRowPos, nColPos+1, nEndRowPos );
1084
1085 lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nDataStartCol-1,nEndRowPos, STR_PIVOT_STYLENAME_CATEGORY );
1086 }
1087 else
1088 lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nDataStartCol-1,nRowPos, STR_PIVOT_STYLENAME_CATEGORY );
1089 }
1090 else if ( pArray[nRow].Flags & sheet::MemberResultFlags::SUBTOTAL )
1091 outputimp.AddRow( nRowPos );
1092
1093 // Apply the same number format as in data source.
1094 pDoc->ApplyAttr(nColPos, nRowPos, nTab, SfxUInt32Item(ATTR_VALUE_FORMAT, pRowFields[nField].mnSrcNumFmt));
1095 }
1096 }
1097
1098 if (nColCount == 1 && nRowCount > 0 && pColFields.empty())
1099 {
1100 // the table contains exactly one data field and no column fields.
1101 // Display data description at top right corner.
1102 ScSetStringParam aParam;
1103 aParam.setTextInput();
1104 pDoc->SetString(nDataStartCol, nDataStartRow-1, nTab, aDataDescription, &aParam);
1105 }
1106
1107 // output data results:
1108
1109 for (sal_Int32 nRow=0; nRow<nRowCount; nRow++)
1110 {
1111 SCROW nRowPos = nDataStartRow + static_cast<SCROW>(nRow); //TODO: check for overflow
1112 const sheet::DataResult* pColAry = pRowAry[nRow].getConstArray();
1113 sal_Int32 nThisColCount = pRowAry[nRow].getLength();
1114 OSL_ENSURE( nThisColCount == nColCount, "count mismatch" ); //TODO: ???
1115 for (sal_Int32 nCol=0; nCol<nThisColCount; nCol++)
1116 {
1117 SCCOL nColPos = nDataStartCol + static_cast<SCCOL>(nCol); //TODO: check for overflow
1118 DataCell( nColPos, nRowPos, nTab, pColAry[nCol] );
1119 }
1120 }
1121
1122 outputimp.OutputDataArea();
1123 }
1124
GetOutputRange(sal_Int32 nRegionType)1125 ScRange ScDPOutput::GetOutputRange( sal_Int32 nRegionType )
1126 {
1127 using namespace ::com::sun::star::sheet;
1128
1129 CalcSizes();
1130
1131 SCTAB nTab = aStartPos.Tab();
1132 switch (nRegionType)
1133 {
1134 case DataPilotOutputRangeType::RESULT:
1135 return ScRange(nDataStartCol, nDataStartRow, nTab, nTabEndCol, nTabEndRow, nTab);
1136 case DataPilotOutputRangeType::TABLE:
1137 return ScRange(aStartPos.Col(), nTabStartRow, nTab, nTabEndCol, nTabEndRow, nTab);
1138 default:
1139 OSL_ENSURE(nRegionType == DataPilotOutputRangeType::WHOLE, "ScDPOutput::GetOutputRange: unknown region type");
1140 break;
1141 }
1142 return ScRange(aStartPos.Col(), aStartPos.Row(), nTab, nTabEndCol, nTabEndRow, nTab);
1143 }
1144
HasError()1145 bool ScDPOutput::HasError()
1146 {
1147 CalcSizes();
1148
1149 return bSizeOverflow || bResultsError;
1150 }
1151
GetHeaderRows() const1152 sal_Int32 ScDPOutput::GetHeaderRows() const
1153 {
1154 return pPageFields.size() + ( bDoFilter ? 1 : 0 );
1155 }
1156
1157 namespace
1158 {
insertNames(ScDPUniqueStringSet & rNames,const uno::Sequence<sheet::MemberResult> & rMemberResults)1159 void insertNames(ScDPUniqueStringSet& rNames, const uno::Sequence<sheet::MemberResult>& rMemberResults)
1160 {
1161 for (const sheet::MemberResult& rMemberResult : rMemberResults)
1162 {
1163 if (rMemberResult.Flags & sheet::MemberResultFlags::HASMEMBER)
1164 rNames.insert(rMemberResult.Name);
1165 }
1166 }
1167 }
1168
GetMemberResultNames(ScDPUniqueStringSet & rNames,tools::Long nDimension)1169 void ScDPOutput::GetMemberResultNames(ScDPUniqueStringSet& rNames, tools::Long nDimension)
1170 {
1171 // Return the list of all member names in a dimension's MemberResults.
1172 // Only the dimension has to be compared because this is only used with table data,
1173 // where each dimension occurs only once.
1174
1175 auto lFindDimension = [nDimension](const ScDPOutLevelData& rField) { return rField.mnDim == nDimension; };
1176
1177 // look in column fields
1178 auto colit = std::find_if(pColFields.begin(), pColFields.end(), lFindDimension);
1179 if (colit != pColFields.end())
1180 {
1181 // collect the member names
1182 insertNames(rNames, colit->maResult);
1183 return;
1184 }
1185
1186 // look in row fields
1187 auto rowit = std::find_if(pRowFields.begin(), pRowFields.end(), lFindDimension);
1188 if (rowit != pRowFields.end())
1189 {
1190 // collect the member names
1191 insertNames(rNames, rowit->maResult);
1192 }
1193 }
1194
SetHeaderLayout(bool bUseGrid)1195 void ScDPOutput::SetHeaderLayout(bool bUseGrid)
1196 {
1197 mbHeaderLayout = bUseGrid;
1198 bSizesValid = false;
1199 }
1200
1201 namespace {
1202
lcl_GetTableVars(sal_Int32 & rGrandTotalCols,sal_Int32 & rGrandTotalRows,sal_Int32 & rDataLayoutIndex,std::vector<OUString> & rDataNames,std::vector<OUString> & rGivenNames,sheet::DataPilotFieldOrientation & rDataOrient,const uno::Reference<sheet::XDimensionsSupplier> & xSource)1203 void lcl_GetTableVars( sal_Int32& rGrandTotalCols, sal_Int32& rGrandTotalRows, sal_Int32& rDataLayoutIndex,
1204 std::vector<OUString>& rDataNames, std::vector<OUString>& rGivenNames,
1205 sheet::DataPilotFieldOrientation& rDataOrient,
1206 const uno::Reference<sheet::XDimensionsSupplier>& xSource )
1207 {
1208 rDataLayoutIndex = -1; // invalid
1209 rGrandTotalCols = 0;
1210 rGrandTotalRows = 0;
1211 rDataOrient = sheet::DataPilotFieldOrientation_HIDDEN;
1212
1213 uno::Reference<beans::XPropertySet> xSrcProp( xSource, uno::UNO_QUERY );
1214 bool bColGrand = ScUnoHelpFunctions::GetBoolProperty(
1215 xSrcProp, SC_UNO_DP_COLGRAND);
1216 if ( bColGrand )
1217 rGrandTotalCols = 1; // default if data layout not in columns
1218
1219 bool bRowGrand = ScUnoHelpFunctions::GetBoolProperty(
1220 xSrcProp, SC_UNO_DP_ROWGRAND);
1221 if ( bRowGrand )
1222 rGrandTotalRows = 1; // default if data layout not in rows
1223
1224 if ( !xSource.is() )
1225 return;
1226
1227 // find index and orientation of "data layout" dimension, count data dimensions
1228
1229 sal_Int32 nDataCount = 0;
1230
1231 uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xSource->getDimensions() );
1232 tools::Long nDimCount = xDims->getCount();
1233 for (tools::Long nDim=0; nDim<nDimCount; nDim++)
1234 {
1235 uno::Reference<uno::XInterface> xDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
1236 uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
1237 if ( xDimProp.is() )
1238 {
1239 sheet::DataPilotFieldOrientation eDimOrient =
1240 ScUnoHelpFunctions::GetEnumProperty(
1241 xDimProp, SC_UNO_DP_ORIENTATION,
1242 sheet::DataPilotFieldOrientation_HIDDEN );
1243 if ( ScUnoHelpFunctions::GetBoolProperty( xDimProp,
1244 SC_UNO_DP_ISDATALAYOUT ) )
1245 {
1246 rDataLayoutIndex = nDim;
1247 rDataOrient = eDimOrient;
1248 }
1249 if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
1250 {
1251 OUString aSourceName;
1252 OUString aGivenName;
1253 ScDPOutput::GetDataDimensionNames( aSourceName, aGivenName, xDim );
1254 try
1255 {
1256 uno::Any aValue = xDimProp->getPropertyValue( SC_UNO_DP_LAYOUTNAME );
1257
1258 if( aValue.hasValue() )
1259 {
1260 OUString strLayoutName;
1261
1262 if( ( aValue >>= strLayoutName ) && !strLayoutName.isEmpty() )
1263 aGivenName = strLayoutName;
1264 }
1265 }
1266 catch(const uno::Exception&)
1267 {
1268 }
1269 rDataNames.push_back( aSourceName );
1270 rGivenNames.push_back( aGivenName );
1271
1272 ++nDataCount;
1273 }
1274 }
1275 }
1276
1277 if ( ( rDataOrient == sheet::DataPilotFieldOrientation_COLUMN ) && bColGrand )
1278 rGrandTotalCols = nDataCount;
1279 else if ( ( rDataOrient == sheet::DataPilotFieldOrientation_ROW ) && bRowGrand )
1280 rGrandTotalRows = nDataCount;
1281 }
1282
1283 }
1284
GetPositionData(const ScAddress & rPos,DataPilotTablePositionData & rPosData)1285 void ScDPOutput::GetPositionData(const ScAddress& rPos, DataPilotTablePositionData& rPosData)
1286 {
1287 using namespace ::com::sun::star::sheet;
1288
1289 SCCOL nCol = rPos.Col();
1290 SCROW nRow = rPos.Row();
1291 SCTAB nTab = rPos.Tab();
1292 if ( nTab != aStartPos.Tab() )
1293 return; // wrong sheet
1294
1295 // calculate output positions and sizes
1296
1297 CalcSizes();
1298
1299 rPosData.PositionType = GetPositionType(rPos);
1300 switch (rPosData.PositionType)
1301 {
1302 case DataPilotTablePositionType::RESULT:
1303 {
1304 vector<DataPilotFieldFilter> aFilters;
1305 GetDataResultPositionData(aFilters, rPos);
1306 sal_Int32 nSize = aFilters.size();
1307
1308 DataPilotTableResultData aResData;
1309 aResData.FieldFilters.realloc(nSize);
1310 for (sal_Int32 i = 0; i < nSize; ++i)
1311 aResData.FieldFilters[i] = aFilters[i];
1312
1313 aResData.DataFieldIndex = 0;
1314 Reference<beans::XPropertySet> xPropSet(xSource, UNO_QUERY);
1315 if (xPropSet.is())
1316 {
1317 sal_Int32 nDataFieldCount = ScUnoHelpFunctions::GetLongProperty( xPropSet,
1318 SC_UNO_DP_DATAFIELDCOUNT );
1319 if (nDataFieldCount > 0)
1320 aResData.DataFieldIndex = (nRow - nDataStartRow) % nDataFieldCount;
1321 }
1322
1323 // Copy appropriate DataResult object from the cached sheet::DataResult table.
1324 if (aData.getLength() > nRow - nDataStartRow &&
1325 aData[nRow-nDataStartRow].getLength() > nCol-nDataStartCol)
1326 aResData.Result = aData[nRow-nDataStartRow][nCol-nDataStartCol];
1327
1328 rPosData.PositionData <<= aResData;
1329 return;
1330 }
1331 case DataPilotTablePositionType::COLUMN_HEADER:
1332 {
1333 tools::Long nField = nRow - nTabStartRow - 1; // 1st line is used for the buttons
1334 if (nField < 0)
1335 break;
1336
1337 const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nField].maResult;
1338 if (!rSequence.hasElements())
1339 break;
1340 const sheet::MemberResult* pArray = rSequence.getConstArray();
1341
1342 tools::Long nItem = nCol - nDataStartCol;
1343 // get origin of "continue" fields
1344 while (nItem > 0 && ( pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1345 --nItem;
1346
1347 if (nItem < 0)
1348 break;
1349
1350 DataPilotTableHeaderData aHeaderData;
1351 aHeaderData.MemberName = pArray[nItem].Name;
1352 aHeaderData.Flags = pArray[nItem].Flags;
1353 aHeaderData.Dimension = static_cast<sal_Int32>(pColFields[nField].mnDim);
1354 aHeaderData.Hierarchy = static_cast<sal_Int32>(pColFields[nField].mnHier);
1355 aHeaderData.Level = static_cast<sal_Int32>(pColFields[nField].mnLevel);
1356
1357 rPosData.PositionData <<= aHeaderData;
1358 return;
1359 }
1360 case DataPilotTablePositionType::ROW_HEADER:
1361 {
1362 tools::Long nField = nCol - nTabStartCol;
1363 if (nField < 0)
1364 break;
1365
1366 const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].maResult;
1367 if (!rSequence.hasElements())
1368 break;
1369 const sheet::MemberResult* pArray = rSequence.getConstArray();
1370
1371 tools::Long nItem = nRow - nDataStartRow;
1372 // get origin of "continue" fields
1373 while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1374 --nItem;
1375
1376 if (nItem < 0)
1377 break;
1378
1379 DataPilotTableHeaderData aHeaderData;
1380 aHeaderData.MemberName = pArray[nItem].Name;
1381 aHeaderData.Flags = pArray[nItem].Flags;
1382 aHeaderData.Dimension = static_cast<sal_Int32>(pRowFields[nField].mnDim);
1383 aHeaderData.Hierarchy = static_cast<sal_Int32>(pRowFields[nField].mnHier);
1384 aHeaderData.Level = static_cast<sal_Int32>(pRowFields[nField].mnLevel);
1385
1386 rPosData.PositionData <<= aHeaderData;
1387 return;
1388 }
1389 }
1390 }
1391
GetDataResultPositionData(vector<sheet::DataPilotFieldFilter> & rFilters,const ScAddress & rPos)1392 bool ScDPOutput::GetDataResultPositionData(vector<sheet::DataPilotFieldFilter>& rFilters, const ScAddress& rPos)
1393 {
1394 // Check to make sure there is at least one data field.
1395 Reference<beans::XPropertySet> xPropSet(xSource, UNO_QUERY);
1396 if (!xPropSet.is())
1397 return false;
1398
1399 sal_Int32 nDataFieldCount = ScUnoHelpFunctions::GetLongProperty( xPropSet,
1400 SC_UNO_DP_DATAFIELDCOUNT );
1401 if (nDataFieldCount == 0)
1402 // No data field is present in this datapilot table.
1403 return false;
1404
1405 // #i111421# use lcl_GetTableVars for correct size of totals and data layout position
1406 sal_Int32 nGrandTotalCols;
1407 sal_Int32 nGrandTotalRows;
1408 sal_Int32 nDataLayoutIndex;
1409 std::vector<OUString> aDataNames;
1410 std::vector<OUString> aGivenNames;
1411 sheet::DataPilotFieldOrientation eDataOrient;
1412 lcl_GetTableVars( nGrandTotalCols, nGrandTotalRows, nDataLayoutIndex, aDataNames, aGivenNames, eDataOrient, xSource );
1413
1414 SCCOL nCol = rPos.Col();
1415 SCROW nRow = rPos.Row();
1416 SCTAB nTab = rPos.Tab();
1417 if ( nTab != aStartPos.Tab() )
1418 return false; // wrong sheet
1419
1420 CalcSizes();
1421
1422 // test for data area.
1423 if (nCol < nDataStartCol || nCol > nTabEndCol || nRow < nDataStartRow || nRow > nTabEndRow)
1424 {
1425 // Cell is outside the data field area.
1426 return false;
1427 }
1428
1429 bool bFilterByCol = (nCol <= static_cast<SCCOL>(nTabEndCol - nGrandTotalCols));
1430 bool bFilterByRow = (nRow <= static_cast<SCROW>(nTabEndRow - nGrandTotalRows));
1431
1432 // column fields
1433 for (size_t nColField = 0; nColField < pColFields.size() && bFilterByCol; ++nColField)
1434 {
1435 if (pColFields[nColField].mnDim == nDataLayoutIndex)
1436 // There is no sense including the data layout field for filtering.
1437 continue;
1438
1439 sheet::DataPilotFieldFilter filter;
1440 filter.FieldName = pColFields[nColField].maName;
1441
1442 const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nColField].maResult;
1443 const sheet::MemberResult* pArray = rSequence.getConstArray();
1444
1445 OSL_ENSURE(nDataStartCol + rSequence.getLength() - 1 == nTabEndCol, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
1446
1447 tools::Long nItem = nCol - nDataStartCol;
1448 // get origin of "continue" fields
1449 while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1450 --nItem;
1451
1452 filter.MatchValueName = pArray[nItem].Name;
1453 rFilters.push_back(filter);
1454 }
1455
1456 // row fields
1457 for (size_t nRowField = 0; nRowField < pRowFields.size() && bFilterByRow; ++nRowField)
1458 {
1459 if (pRowFields[nRowField].mnDim == nDataLayoutIndex)
1460 // There is no sense including the data layout field for filtering.
1461 continue;
1462
1463 sheet::DataPilotFieldFilter filter;
1464 filter.FieldName = pRowFields[nRowField].maName;
1465
1466 const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nRowField].maResult;
1467 const sheet::MemberResult* pArray = rSequence.getConstArray();
1468
1469 OSL_ENSURE(nDataStartRow + rSequence.getLength() - 1 == nTabEndRow, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
1470
1471 tools::Long nItem = nRow - nDataStartRow;
1472 // get origin of "continue" fields
1473 while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1474 --nItem;
1475
1476 filter.MatchValueName = pArray[nItem].Name;
1477 rFilters.push_back(filter);
1478 }
1479
1480 return true;
1481 }
1482
1483 namespace {
1484
lcl_GetDataFieldName(std::u16string_view rSourceName,sal_Int16 eFunc)1485 OUString lcl_GetDataFieldName( std::u16string_view rSourceName, sal_Int16 eFunc )
1486 {
1487 const char* pStrId = nullptr;
1488 switch ( eFunc )
1489 {
1490 case sheet::GeneralFunction2::SUM: pStrId = STR_FUN_TEXT_SUM; break;
1491 case sheet::GeneralFunction2::COUNT:
1492 case sheet::GeneralFunction2::COUNTNUMS: pStrId = STR_FUN_TEXT_COUNT; break;
1493 case sheet::GeneralFunction2::AVERAGE: pStrId = STR_FUN_TEXT_AVG; break;
1494 case sheet::GeneralFunction2::MEDIAN: pStrId = STR_FUN_TEXT_MEDIAN; break;
1495 case sheet::GeneralFunction2::MAX: pStrId = STR_FUN_TEXT_MAX; break;
1496 case sheet::GeneralFunction2::MIN: pStrId = STR_FUN_TEXT_MIN; break;
1497 case sheet::GeneralFunction2::PRODUCT: pStrId = STR_FUN_TEXT_PRODUCT; break;
1498 case sheet::GeneralFunction2::STDEV:
1499 case sheet::GeneralFunction2::STDEVP: pStrId = STR_FUN_TEXT_STDDEV; break;
1500 case sheet::GeneralFunction2::VAR:
1501 case sheet::GeneralFunction2::VARP: pStrId = STR_FUN_TEXT_VAR; break;
1502 case sheet::GeneralFunction2::NONE:
1503 case sheet::GeneralFunction2::AUTO: break;
1504 default:
1505 {
1506 assert(false);
1507 }
1508 }
1509 if (!pStrId)
1510 return OUString();
1511
1512 return ScResId(pStrId) + " - " + rSourceName;
1513 }
1514
1515 }
1516
GetDataDimensionNames(OUString & rSourceName,OUString & rGivenName,const uno::Reference<uno::XInterface> & xDim)1517 void ScDPOutput::GetDataDimensionNames(
1518 OUString& rSourceName, OUString& rGivenName, const uno::Reference<uno::XInterface>& xDim )
1519 {
1520 uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
1521 uno::Reference<container::XNamed> xDimName( xDim, uno::UNO_QUERY );
1522 if ( !(xDimProp.is() && xDimName.is()) )
1523 return;
1524
1525 // Asterisks are added in ScDPSaveData::WriteToSource to create unique names.
1526 //TODO: preserve original name there?
1527 rSourceName = ScDPUtil::getSourceDimensionName(xDimName->getName());
1528
1529 // Generate "given name" the same way as in dptabres.
1530 //TODO: Should use a stored name when available
1531
1532 sal_Int16 eFunc = ScUnoHelpFunctions::GetShortProperty(
1533 xDimProp, SC_UNO_DP_FUNCTION2,
1534 sheet::GeneralFunction2::NONE );
1535 rGivenName = lcl_GetDataFieldName( rSourceName, eFunc );
1536 }
1537
IsFilterButton(const ScAddress & rPos)1538 bool ScDPOutput::IsFilterButton( const ScAddress& rPos )
1539 {
1540 SCCOL nCol = rPos.Col();
1541 SCROW nRow = rPos.Row();
1542 SCTAB nTab = rPos.Tab();
1543 if ( nTab != aStartPos.Tab() || !bDoFilter )
1544 return false; // wrong sheet or no button at all
1545
1546 // filter button is at top left
1547 return ( nCol == aStartPos.Col() && nRow == aStartPos.Row() );
1548 }
1549
GetHeaderDim(const ScAddress & rPos,sheet::DataPilotFieldOrientation & rOrient)1550 tools::Long ScDPOutput::GetHeaderDim( const ScAddress& rPos, sheet::DataPilotFieldOrientation& rOrient )
1551 {
1552 SCCOL nCol = rPos.Col();
1553 SCROW nRow = rPos.Row();
1554 SCTAB nTab = rPos.Tab();
1555 if ( nTab != aStartPos.Tab() )
1556 return -1; // wrong sheet
1557
1558 // calculate output positions and sizes
1559
1560 CalcSizes();
1561
1562 // test for column header
1563
1564 if ( nRow == nTabStartRow && nCol >= nDataStartCol && o3tl::make_unsigned(nCol) < nDataStartCol + pColFields.size())
1565 {
1566 rOrient = sheet::DataPilotFieldOrientation_COLUMN;
1567 tools::Long nField = nCol - nDataStartCol;
1568 return pColFields[nField].mnDim;
1569 }
1570
1571 // test for row header
1572
1573 if ( nRow+1 == nDataStartRow && nCol >= nTabStartCol && o3tl::make_unsigned(nCol) < nTabStartCol + pRowFields.size() )
1574 {
1575 rOrient = sheet::DataPilotFieldOrientation_ROW;
1576 tools::Long nField = nCol - nTabStartCol;
1577 return pRowFields[nField].mnDim;
1578 }
1579
1580 // test for page field
1581
1582 SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 );
1583 if ( nCol == aStartPos.Col() && nRow >= nPageStartRow && o3tl::make_unsigned(nRow) < nPageStartRow + pPageFields.size() )
1584 {
1585 rOrient = sheet::DataPilotFieldOrientation_PAGE;
1586 tools::Long nField = nRow - nPageStartRow;
1587 return pPageFields[nField].mnDim;
1588 }
1589
1590 //TODO: single data field (?)
1591
1592 rOrient = sheet::DataPilotFieldOrientation_HIDDEN;
1593 return -1; // invalid
1594 }
1595
GetHeaderDrag(const ScAddress & rPos,bool bMouseLeft,bool bMouseTop,tools::Long nDragDim,tools::Rectangle & rPosRect,sheet::DataPilotFieldOrientation & rOrient,tools::Long & rDimPos)1596 bool ScDPOutput::GetHeaderDrag( const ScAddress& rPos, bool bMouseLeft, bool bMouseTop,
1597 tools::Long nDragDim,
1598 tools::Rectangle& rPosRect, sheet::DataPilotFieldOrientation& rOrient, tools::Long& rDimPos )
1599 {
1600 // Rectangle instead of ScRange for rPosRect to allow for negative values
1601
1602 SCCOL nCol = rPos.Col();
1603 SCROW nRow = rPos.Row();
1604 SCTAB nTab = rPos.Tab();
1605 if ( nTab != aStartPos.Tab() )
1606 return false; // wrong sheet
1607
1608 // calculate output positions and sizes
1609
1610 CalcSizes();
1611
1612 // test for column header
1613
1614 if ( nCol >= nDataStartCol && nCol <= nTabEndCol &&
1615 nRow + 1 >= nMemberStartRow && o3tl::make_unsigned(nRow) < nMemberStartRow + pColFields.size())
1616 {
1617 tools::Long nField = nRow - nMemberStartRow;
1618 if (nField < 0)
1619 {
1620 nField = 0;
1621 bMouseTop = true;
1622 }
1623 //TODO: find start of dimension
1624
1625 rPosRect = tools::Rectangle( nDataStartCol, nMemberStartRow + nField,
1626 nTabEndCol, nMemberStartRow + nField -1 );
1627
1628 bool bFound = false; // is this within the same orientation?
1629 bool bBeforeDrag = false;
1630 bool bAfterDrag = false;
1631 for (tools::Long nPos=0; o3tl::make_unsigned(nPos)<pColFields.size() && !bFound; nPos++)
1632 {
1633 if (pColFields[nPos].mnDim == nDragDim)
1634 {
1635 bFound = true;
1636 if ( nField < nPos )
1637 bBeforeDrag = true;
1638 else if ( nField > nPos )
1639 bAfterDrag = true;
1640 }
1641 }
1642
1643 if ( bFound )
1644 {
1645 if (!bBeforeDrag)
1646 {
1647 rPosRect.AdjustBottom( 1 );
1648 if (bAfterDrag)
1649 rPosRect.AdjustTop( 1 );
1650 }
1651 }
1652 else
1653 {
1654 if ( !bMouseTop )
1655 {
1656 rPosRect.AdjustTop( 1 );
1657 rPosRect.AdjustBottom( 1 );
1658 ++nField;
1659 }
1660 }
1661
1662 rOrient = sheet::DataPilotFieldOrientation_COLUMN;
1663 rDimPos = nField; //!...
1664 return true;
1665 }
1666
1667 // test for row header
1668
1669 // special case if no row fields
1670 bool bSpecial = ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow &&
1671 pRowFields.empty() && nCol == nTabStartCol && bMouseLeft );
1672
1673 if ( bSpecial || ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow &&
1674 nCol + 1 >= nTabStartCol && o3tl::make_unsigned(nCol) < nTabStartCol + pRowFields.size() ) )
1675 {
1676 tools::Long nField = nCol - nTabStartCol;
1677 //TODO: find start of dimension
1678
1679 rPosRect = tools::Rectangle( nTabStartCol + nField, nDataStartRow - 1,
1680 nTabStartCol + nField - 1, nTabEndRow );
1681
1682 bool bFound = false; // is this within the same orientation?
1683 bool bBeforeDrag = false;
1684 bool bAfterDrag = false;
1685 for (tools::Long nPos=0; o3tl::make_unsigned(nPos)<pRowFields.size() && !bFound; nPos++)
1686 {
1687 if (pRowFields[nPos].mnDim == nDragDim)
1688 {
1689 bFound = true;
1690 if ( nField < nPos )
1691 bBeforeDrag = true;
1692 else if ( nField > nPos )
1693 bAfterDrag = true;
1694 }
1695 }
1696
1697 if ( bFound )
1698 {
1699 if (!bBeforeDrag)
1700 {
1701 rPosRect.AdjustRight( 1 );
1702 if (bAfterDrag)
1703 rPosRect.AdjustLeft( 1 );
1704 }
1705 }
1706 else
1707 {
1708 if ( !bMouseLeft )
1709 {
1710 rPosRect.AdjustLeft( 1 );
1711 rPosRect.AdjustRight( 1 );
1712 ++nField;
1713 }
1714 }
1715
1716 rOrient = sheet::DataPilotFieldOrientation_ROW;
1717 rDimPos = nField; //!...
1718 return true;
1719 }
1720
1721 // test for page fields
1722
1723 SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 );
1724 if ( nCol >= aStartPos.Col() && nCol <= nTabEndCol &&
1725 nRow + 1 >= nPageStartRow && o3tl::make_unsigned(nRow) < nPageStartRow + pPageFields.size() )
1726 {
1727 tools::Long nField = nRow - nPageStartRow;
1728 if (nField < 0)
1729 {
1730 nField = 0;
1731 bMouseTop = true;
1732 }
1733 //TODO: find start of dimension
1734
1735 rPosRect = tools::Rectangle( aStartPos.Col(), nPageStartRow + nField,
1736 nTabEndCol, nPageStartRow + nField - 1 );
1737
1738 bool bFound = false; // is this within the same orientation?
1739 bool bBeforeDrag = false;
1740 bool bAfterDrag = false;
1741 for (tools::Long nPos=0; o3tl::make_unsigned(nPos)<pPageFields.size() && !bFound; nPos++)
1742 {
1743 if (pPageFields[nPos].mnDim == nDragDim)
1744 {
1745 bFound = true;
1746 if ( nField < nPos )
1747 bBeforeDrag = true;
1748 else if ( nField > nPos )
1749 bAfterDrag = true;
1750 }
1751 }
1752
1753 if ( bFound )
1754 {
1755 if (!bBeforeDrag)
1756 {
1757 rPosRect.AdjustBottom( 1 );
1758 if (bAfterDrag)
1759 rPosRect.AdjustTop( 1 );
1760 }
1761 }
1762 else
1763 {
1764 if ( !bMouseTop )
1765 {
1766 rPosRect.AdjustTop( 1 );
1767 rPosRect.AdjustBottom( 1 );
1768 ++nField;
1769 }
1770 }
1771
1772 rOrient = sheet::DataPilotFieldOrientation_PAGE;
1773 rDimPos = nField; //!...
1774 return true;
1775 }
1776
1777 return false;
1778 }
1779
1780 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1781