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