Header columns can disappear with filtered data in pivot tables
[LibreOffice.git] / sc / source / core / data / dpoutput.cxx
blob11a69b427146a54f3f82032cb37e6e690f262039
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <scitems.hxx>
22 #include <comphelper/sequence.hxx>
23 #include <editeng/borderline.hxx>
24 #include <editeng/boxitem.hxx>
25 #include <editeng/wghtitem.hxx>
26 #include <editeng/justifyitem.hxx>
27 #include <o3tl/safeint.hxx>
28 #include <osl/diagnose.h>
29 #include <svl/itemset.hxx>
31 #include <dpoutput.hxx>
32 #include <document.hxx>
33 #include <attrib.hxx>
34 #include <formula/errorcodes.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>
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>
63 #include <limits>
64 #include <string_view>
65 #include <vector>
67 using namespace com::sun::star;
68 using ::std::vector;
69 using ::com::sun::star::beans::XPropertySet;
70 using ::com::sun::star::uno::Sequence;
71 using ::com::sun::star::uno::UNO_QUERY;
72 using ::com::sun::star::uno::Reference;
73 using ::com::sun::star::sheet::DataPilotTablePositionData;
74 using ::com::sun::star::sheet::DataPilotTableResultData;
76 #define SC_DP_FRAME_INNER_BOLD 20
77 #define SC_DP_FRAME_OUTER_BOLD 40
79 #define SC_DP_FRAME_COLOR Color(0,0,0) //( 0x20, 0x40, 0x68 )
81 struct ScDPOutLevelData
83 tools::Long mnDim;
84 tools::Long mnHier;
85 tools::Long mnLevel;
86 tools::Long mnDimPos;
87 sal_uInt32 mnSrcNumFmt; /// Prevailing number format used in the source data.
88 uno::Sequence<sheet::MemberResult> maResult;
89 OUString maName; /// Name is the internal field name.
90 OUString maCaption; /// Caption is the name visible in the output table.
91 bool mbHasHiddenMember:1;
92 bool mbDataLayout:1;
93 bool mbPageDim:1;
95 ScDPOutLevelData(tools::Long nDim, tools::Long nHier, tools::Long nLevel, tools::Long nDimPos, sal_uInt32 nSrcNumFmt, const uno::Sequence<sheet::MemberResult> &aResult,
96 const OUString &aName, const OUString &aCaption, bool bHasHiddenMember, bool bDataLayout, bool bPageDim) :
97 mnDim(nDim), mnHier(nHier), mnLevel(nLevel), mnDimPos(nDimPos), mnSrcNumFmt(nSrcNumFmt), maResult(aResult),
98 maName(aName), maCaption(aCaption), mbHasHiddenMember(bHasHiddenMember), mbDataLayout(bDataLayout),
99 mbPageDim(bPageDim)
103 // bug (73840) in uno::Sequence - copy and then assign doesn't work!
108 namespace {
109 struct ScDPOutLevelDataComparator
111 bool operator()(const ScDPOutLevelData & rA, const ScDPOutLevelData & rB)
113 return rA.mnDimPos<rB.mnDimPos || ( rA.mnDimPos==rB.mnDimPos && rA.mnHier<rB.mnHier ) ||
114 ( rA.mnDimPos==rB.mnDimPos && rA.mnHier==rB.mnHier && rA.mnLevel<rB.mnLevel );
119 class ScDPOutputImpl
121 ScDocument* mpDoc;
122 sal_uInt16 mnTab;
123 ::std::vector< bool > mbNeedLineCols;
124 ::std::vector< SCCOL > mnCols;
126 ::std::vector< bool > mbNeedLineRows;
127 ::std::vector< SCROW > mnRows;
129 SCCOL mnTabStartCol;
130 SCROW mnTabStartRow;
132 SCCOL mnDataStartCol;
133 SCROW mnDataStartRow;
134 SCCOL mnTabEndCol;
135 SCROW mnTabEndRow;
137 public:
138 ScDPOutputImpl( ScDocument* pDoc, sal_uInt16 nTab,
139 SCCOL nTabStartCol,
140 SCROW nTabStartRow,
141 SCCOL nDataStartCol,
142 SCROW nDataStartRow,
143 SCCOL nTabEndCol,
144 SCROW nTabEndRow );
145 bool AddRow( SCROW nRow );
146 bool AddCol( SCCOL nCol );
148 void OutputDataArea();
149 void OutputBlockFrame ( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, bool bHori = false );
153 void ScDPOutputImpl::OutputDataArea()
155 AddRow( mnDataStartRow );
156 AddCol( mnDataStartCol );
158 mnCols.push_back( mnTabEndCol+1); //set last row bottom
159 mnRows.push_back( mnTabEndRow+1); //set last col bottom
161 bool bAllRows = ( ( mnTabEndRow - mnDataStartRow + 2 ) == static_cast<SCROW>(mnRows.size()) );
163 std::sort( mnCols.begin(), mnCols.end());
164 std::sort( mnRows.begin(), mnRows.end());
166 for( SCCOL nCol = 0; nCol < static_cast<SCCOL>(mnCols.size())-1; nCol ++ )
168 if ( !bAllRows )
170 if ( nCol < static_cast<SCCOL>(mnCols.size())-2)
172 for ( SCROW i = nCol%2; i < static_cast<SCROW>(mnRows.size())-2; i +=2 )
173 OutputBlockFrame( mnCols[nCol], mnRows[i], mnCols[nCol+1]-1, mnRows[i+1]-1 );
174 if ( mnRows.size()>=2 )
175 OutputBlockFrame( mnCols[nCol], mnRows[mnRows.size()-2], mnCols[nCol+1]-1, mnRows[mnRows.size()-1]-1 );
177 else
179 for ( SCROW i = 0 ; i < static_cast<SCROW>(mnRows.size())-1; i++ )
180 OutputBlockFrame( mnCols[nCol], mnRows[i], mnCols[nCol+1]-1, mnRows[i+1]-1 );
183 else
184 OutputBlockFrame( mnCols[nCol], mnRows.front(), mnCols[nCol+1]-1, mnRows.back()-1, bAllRows );
186 //out put rows area outer framer
187 if ( mnTabStartCol != mnDataStartCol )
189 if ( mnTabStartRow != mnDataStartRow )
190 OutputBlockFrame( mnTabStartCol, mnTabStartRow, mnDataStartCol-1, mnDataStartRow-1 );
191 OutputBlockFrame( mnTabStartCol, mnDataStartRow, mnDataStartCol-1, mnTabEndRow );
193 //out put cols area outer framer
194 OutputBlockFrame( mnDataStartCol, mnTabStartRow, mnTabEndCol, mnDataStartRow-1 );
197 ScDPOutputImpl::ScDPOutputImpl( ScDocument* pDoc, sal_uInt16 nTab,
198 SCCOL nTabStartCol,
199 SCROW nTabStartRow,
200 SCCOL nDataStartCol,
201 SCROW nDataStartRow,
202 SCCOL nTabEndCol,
203 SCROW nTabEndRow ):
204 mpDoc( pDoc ),
205 mnTab( nTab ),
206 mnTabStartCol( nTabStartCol ),
207 mnTabStartRow( nTabStartRow ),
208 mnDataStartCol ( nDataStartCol ),
209 mnDataStartRow ( nDataStartRow ),
210 mnTabEndCol( nTabEndCol ),
211 mnTabEndRow( nTabEndRow )
213 mbNeedLineCols.resize( nTabEndCol-nDataStartCol+1, false );
214 mbNeedLineRows.resize( nTabEndRow-nDataStartRow+1, false );
218 bool ScDPOutputImpl::AddRow( SCROW nRow )
220 if ( !mbNeedLineRows[ nRow - mnDataStartRow ] )
222 mbNeedLineRows[ nRow - mnDataStartRow ] = true;
223 mnRows.push_back( nRow );
224 return true;
226 else
227 return false;
230 bool ScDPOutputImpl::AddCol( SCCOL nCol )
233 if ( !mbNeedLineCols[ nCol - mnDataStartCol ] )
235 mbNeedLineCols[ nCol - mnDataStartCol ] = true;
236 mnCols.push_back( nCol );
237 return true;
239 else
240 return false;
243 void ScDPOutputImpl::OutputBlockFrame ( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, bool bHori )
245 Color color = SC_DP_FRAME_COLOR;
246 ::editeng::SvxBorderLine aLine( &color, SC_DP_FRAME_INNER_BOLD );
247 ::editeng::SvxBorderLine aOutLine( &color, SC_DP_FRAME_OUTER_BOLD );
249 SvxBoxItem aBox( ATTR_BORDER );
251 if ( nStartCol == mnTabStartCol )
252 aBox.SetLine(&aOutLine, SvxBoxItemLine::LEFT);
253 else
254 aBox.SetLine(&aLine, SvxBoxItemLine::LEFT);
256 if ( nStartRow == mnTabStartRow )
257 aBox.SetLine(&aOutLine, SvxBoxItemLine::TOP);
258 else
259 aBox.SetLine(&aLine, SvxBoxItemLine::TOP);
261 if ( nEndCol == mnTabEndCol ) //bottom row
262 aBox.SetLine(&aOutLine, SvxBoxItemLine::RIGHT);
263 else
264 aBox.SetLine(&aLine, SvxBoxItemLine::RIGHT);
266 if ( nEndRow == mnTabEndRow ) //bottom
267 aBox.SetLine(&aOutLine, SvxBoxItemLine::BOTTOM);
268 else
269 aBox.SetLine(&aLine, SvxBoxItemLine::BOTTOM);
271 SvxBoxInfoItem aBoxInfo( ATTR_BORDER_INNER );
272 aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::VERT,false );
273 if ( bHori )
275 aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI);
276 aBoxInfo.SetLine( &aLine, SvxBoxInfoItemLine::HORI );
278 else
279 aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI,false );
281 aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::DISTANCE,false);
283 mpDoc->ApplyFrameAreaTab(ScRange(nStartCol, nStartRow, mnTab, nEndCol, nEndRow , mnTab), aBox, aBoxInfo);
287 void lcl_SetStyleById(ScDocument* pDoc, SCTAB nTab,
288 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
289 TranslateId pStrId)
291 if ( nCol1 > nCol2 || nRow1 > nRow2 )
293 OSL_FAIL("SetStyleById: invalid range");
294 return;
297 OUString aStyleName = ScResId(pStrId);
298 ScStyleSheetPool* pStlPool = pDoc->GetStyleSheetPool();
299 ScStyleSheet* pStyle = static_cast<ScStyleSheet*>( pStlPool->Find( aStyleName, SfxStyleFamily::Para ) );
300 if (!pStyle)
302 // create new style (was in ScPivot::SetStyle)
304 pStyle = static_cast<ScStyleSheet*>( &pStlPool->Make( aStyleName, SfxStyleFamily::Para,
305 SfxStyleSearchBits::UserDefined ) );
306 pStyle->SetParent( ScResId(STR_STYLENAME_STANDARD) );
307 SfxItemSet& rSet = pStyle->GetItemSet();
308 if (pStrId == STR_PIVOT_STYLENAME_RESULT || pStrId == STR_PIVOT_STYLENAME_TITLE){
309 rSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
310 rSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_CJK_FONT_WEIGHT ) );
311 rSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_CTL_FONT_WEIGHT ) );
313 if (pStrId == STR_PIVOT_STYLENAME_CATEGORY || pStrId == STR_PIVOT_STYLENAME_TITLE)
314 rSet.Put( SvxHorJustifyItem( SvxCellHorJustify::Left, ATTR_HOR_JUSTIFY ) );
317 pDoc->ApplyStyleAreaTab( nCol1, nRow1, nCol2, nRow2, nTab, *pStyle );
320 void lcl_SetFrame( ScDocument* pDoc, SCTAB nTab,
321 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
322 sal_uInt16 nWidth )
324 ::editeng::SvxBorderLine aLine(nullptr, nWidth, SvxBorderLineStyle::SOLID);
325 SvxBoxItem aBox( ATTR_BORDER );
326 aBox.SetLine(&aLine, SvxBoxItemLine::LEFT);
327 aBox.SetLine(&aLine, SvxBoxItemLine::TOP);
328 aBox.SetLine(&aLine, SvxBoxItemLine::RIGHT);
329 aBox.SetLine(&aLine, SvxBoxItemLine::BOTTOM);
330 SvxBoxInfoItem aBoxInfo( ATTR_BORDER_INNER );
331 aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI,false);
332 aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::VERT,false);
333 aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::DISTANCE,false);
335 pDoc->ApplyFrameAreaTab(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab), aBox, aBoxInfo);
338 void lcl_FillNumberFormats( std::unique_ptr<sal_uInt32[]>& rFormats, sal_Int32& rCount,
339 const uno::Reference<sheet::XDataPilotMemberResults>& xLevRes,
340 const uno::Reference<container::XIndexAccess>& xDims )
342 if ( rFormats )
343 return; // already set
345 // xLevRes is from the data layout dimension
346 //TODO: use result sequence from ScDPOutLevelData!
348 uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults();
350 tools::Long nSize = aResult.getLength();
351 if (!nSize)
352 return;
354 // get names/formats for all data dimensions
355 //TODO: merge this with the loop to collect ScDPOutLevelData?
357 std::vector <OUString> aDataNames;
358 std::vector <sal_uInt32> aDataFormats;
359 sal_Int32 nDimCount = xDims->getCount();
360 sal_Int32 nDim = 0;
361 for ( ; nDim < nDimCount ; nDim++)
363 uno::Reference<uno::XInterface> xDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
364 uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
365 uno::Reference<container::XNamed> xDimName( xDim, uno::UNO_QUERY );
366 if ( xDimProp.is() && xDimName.is() )
368 sheet::DataPilotFieldOrientation eDimOrient =
369 ScUnoHelpFunctions::GetEnumProperty(
370 xDimProp, SC_UNO_DP_ORIENTATION,
371 sheet::DataPilotFieldOrientation_HIDDEN );
372 if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
374 aDataNames.push_back(xDimName->getName());
375 tools::Long nFormat = ScUnoHelpFunctions::GetLongProperty(
376 xDimProp,
377 SC_UNONAME_NUMFMT );
378 aDataFormats.push_back(nFormat);
383 if (aDataFormats.empty())
384 return;
386 const sheet::MemberResult* pArray = aResult.getConstArray();
388 OUString aName;
389 sal_uInt32* pNumFmt = new sal_uInt32[nSize];
390 if (aDataFormats.size() == 1)
392 // only one data dimension -> use its numberformat everywhere
393 tools::Long nFormat = aDataFormats[0];
394 for (tools::Long nPos=0; nPos<nSize; nPos++)
395 pNumFmt[nPos] = nFormat;
397 else
399 for (tools::Long nPos=0; nPos<nSize; nPos++)
401 // if CONTINUE bit is set, keep previous name
402 //TODO: keep number format instead!
403 if ( !(pArray[nPos].Flags & sheet::MemberResultFlags::CONTINUE) )
404 aName = pArray[nPos].Name;
406 sal_uInt32 nFormat = 0;
407 for (size_t i=0; i<aDataFormats.size(); i++)
408 if (aName == aDataNames[i]) //TODO: search more efficiently?
410 nFormat = aDataFormats[i];
411 break;
413 pNumFmt[nPos] = nFormat;
417 rFormats.reset( pNumFmt );
418 rCount = nSize;
421 sal_uInt32 lcl_GetFirstNumberFormat( const uno::Reference<container::XIndexAccess>& xDims )
423 tools::Long nDimCount = xDims->getCount();
424 for (tools::Long nDim=0; nDim<nDimCount; nDim++)
426 uno::Reference<beans::XPropertySet> xDimProp(xDims->getByIndex(nDim), uno::UNO_QUERY);
427 if ( xDimProp.is() )
429 sheet::DataPilotFieldOrientation eDimOrient =
430 ScUnoHelpFunctions::GetEnumProperty(
431 xDimProp, SC_UNO_DP_ORIENTATION,
432 sheet::DataPilotFieldOrientation_HIDDEN );
433 if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
435 tools::Long nFormat = ScUnoHelpFunctions::GetLongProperty(
436 xDimProp,
437 SC_UNONAME_NUMFMT );
439 return nFormat; // use format from first found data dimension
444 return 0; // none found
447 bool lcl_MemberEmpty( const uno::Sequence<sheet::MemberResult>& rSeq )
449 // used to skip levels that have no members
451 return std::none_of(rSeq.begin(), rSeq.end(),
452 [](const sheet::MemberResult& rMem) {
453 return rMem.Flags & sheet::MemberResultFlags::HASMEMBER; });
457 * Get visible page dimension members as results, except that, if all
458 * members are visible, then this function returns empty result.
460 uno::Sequence<sheet::MemberResult> getVisiblePageMembersAsResults( const uno::Reference<uno::XInterface>& xLevel )
462 if (!xLevel.is())
463 return uno::Sequence<sheet::MemberResult>();
465 uno::Reference<sheet::XMembersSupplier> xMSupplier(xLevel, UNO_QUERY);
466 if (!xMSupplier.is())
467 return uno::Sequence<sheet::MemberResult>();
469 uno::Reference<sheet::XMembersAccess> xNA = xMSupplier->getMembers();
470 if (!xNA.is())
471 return uno::Sequence<sheet::MemberResult>();
473 std::vector<sheet::MemberResult> aRes;
474 const uno::Sequence<OUString> aNames = xNA->getElementNames();
475 for (const OUString& rName : aNames)
477 xNA->getByName(rName);
479 uno::Reference<beans::XPropertySet> xMemPS(xNA->getByName(rName), UNO_QUERY);
480 if (!xMemPS.is())
481 continue;
483 OUString aCaption = ScUnoHelpFunctions::GetStringProperty(xMemPS, SC_UNO_DP_LAYOUTNAME, OUString());
484 if (aCaption.isEmpty())
485 aCaption = rName;
487 bool bVisible = ScUnoHelpFunctions::GetBoolProperty(xMemPS, SC_UNO_DP_ISVISIBLE);
489 if (bVisible)
491 /* TODO: any numeric value to obtain? */
492 aRes.emplace_back(rName, aCaption, 0, std::numeric_limits<double>::quiet_NaN());
496 if (aNames.getLength() == static_cast<sal_Int32>(aRes.size()))
497 // All members are visible. Return empty result.
498 return uno::Sequence<sheet::MemberResult>();
500 return ScUnoHelpFunctions::VectorToSequence(aRes);
505 ScDPOutput::ScDPOutput( ScDocument* pD, const uno::Reference<sheet::XDimensionsSupplier>& xSrc,
506 const ScAddress& rPos, bool bFilter ) :
507 pDoc( pD ),
508 xSource( xSrc ),
509 aStartPos( rPos ),
510 nColFmtCount( 0 ),
511 nRowFmtCount( 0 ),
512 nSingleNumFmt( 0 ),
513 nColCount(0),
514 nRowCount(0),
515 nHeaderSize(0),
516 bDoFilter(bFilter),
517 bResultsError(false),
518 bSizesValid(false),
519 bSizeOverflow(false),
520 mbHeaderLayout(false)
522 nTabStartCol = nMemberStartCol = nDataStartCol = nTabEndCol = 0;
523 nTabStartRow = nMemberStartRow = nDataStartRow = nTabEndRow = 0;
525 uno::Reference<sheet::XDataPilotResults> xResult( xSource, uno::UNO_QUERY );
526 if ( xSource.is() && xResult.is() )
528 // get dimension results:
530 uno::Reference<container::XIndexAccess> xDims =
531 new ScNameToIndexAccess( xSource->getDimensions() );
532 tools::Long nDimCount = xDims->getCount();
533 for (tools::Long nDim=0; nDim<nDimCount; nDim++)
535 uno::Reference<uno::XInterface> xDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
536 uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
537 uno::Reference<sheet::XHierarchiesSupplier> xDimSupp( xDim, uno::UNO_QUERY );
538 if ( xDimProp.is() && xDimSupp.is() )
540 sheet::DataPilotFieldOrientation eDimOrient =
541 ScUnoHelpFunctions::GetEnumProperty(
542 xDimProp, SC_UNO_DP_ORIENTATION,
543 sheet::DataPilotFieldOrientation_HIDDEN );
544 tools::Long nDimPos = ScUnoHelpFunctions::GetLongProperty( xDimProp,
545 SC_UNO_DP_POSITION );
546 bool bIsDataLayout = ScUnoHelpFunctions::GetBoolProperty(
547 xDimProp, SC_UNO_DP_ISDATALAYOUT);
548 bool bHasHiddenMember = ScUnoHelpFunctions::GetBoolProperty(
549 xDimProp, SC_UNO_DP_HAS_HIDDEN_MEMBER);
550 sal_Int32 nNumFmt = ScUnoHelpFunctions::GetLongProperty(
551 xDimProp, SC_UNO_DP_NUMBERFO);
553 if ( eDimOrient != sheet::DataPilotFieldOrientation_HIDDEN )
555 uno::Reference<container::XIndexAccess> xHiers =
556 new ScNameToIndexAccess( xDimSupp->getHierarchies() );
557 tools::Long nHierarchy = ScUnoHelpFunctions::GetLongProperty(
558 xDimProp,
559 SC_UNO_DP_USEDHIERARCHY );
560 if ( nHierarchy >= xHiers->getCount() )
561 nHierarchy = 0;
563 uno::Reference<sheet::XLevelsSupplier> xHierSupp(xHiers->getByIndex(nHierarchy),
564 uno::UNO_QUERY);
565 if ( xHierSupp.is() )
567 uno::Reference<container::XIndexAccess> xLevels =
568 new ScNameToIndexAccess( xHierSupp->getLevels() );
569 tools::Long nLevCount = xLevels->getCount();
570 for (tools::Long nLev=0; nLev<nLevCount; nLev++)
572 uno::Reference<uno::XInterface> xLevel(xLevels->getByIndex(nLev),
573 uno::UNO_QUERY);
574 uno::Reference<container::XNamed> xLevNam( xLevel, uno::UNO_QUERY );
575 uno::Reference<sheet::XDataPilotMemberResults> xLevRes(
576 xLevel, uno::UNO_QUERY );
577 if ( xLevNam.is() && xLevRes.is() )
579 OUString aName = xLevNam->getName();
580 Reference<XPropertySet> xPropSet(xLevel, UNO_QUERY);
581 // Caption equals the field name by default.
582 // #i108948# use ScUnoHelpFunctions::GetStringProperty, because
583 // LayoutName is new and may not be present in external implementation
584 OUString aCaption = ScUnoHelpFunctions::GetStringProperty( xPropSet,
585 SC_UNO_DP_LAYOUTNAME, aName );
587 switch ( eDimOrient )
589 case sheet::DataPilotFieldOrientation_COLUMN:
591 uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults();
592 if (!lcl_MemberEmpty(aResult))
594 ScDPOutLevelData tmp(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName,
595 aCaption, bHasHiddenMember, bIsDataLayout, false);
596 pColFields.push_back(tmp);
599 break;
600 case sheet::DataPilotFieldOrientation_ROW:
602 uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults();
603 // We want only to remove the DATA column if it is empty
604 // and not any other empty columns (to still show the
605 // header columns)
606 bool bSkip = lcl_MemberEmpty(aResult) && bIsDataLayout;
607 if (!bSkip)
609 ScDPOutLevelData tmp(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName,
610 aCaption, bHasHiddenMember, bIsDataLayout, false);
611 pRowFields.push_back(tmp);
614 break;
615 case sheet::DataPilotFieldOrientation_PAGE:
617 uno::Sequence<sheet::MemberResult> aResult = getVisiblePageMembersAsResults(xLevel);
618 // no check on results for page fields
619 ScDPOutLevelData tmp(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName,
620 aCaption, bHasHiddenMember, false, true);
621 pPageFields.push_back(tmp);
623 break;
624 default:
626 // added to avoid warnings
630 // get number formats from data dimensions
631 if ( bIsDataLayout )
633 OSL_ENSURE( nLevCount == 1, "data layout: multiple levels?" );
634 if ( eDimOrient == sheet::DataPilotFieldOrientation_COLUMN )
635 lcl_FillNumberFormats( pColNumFmt, nColFmtCount, xLevRes, xDims );
636 else if ( eDimOrient == sheet::DataPilotFieldOrientation_ROW )
637 lcl_FillNumberFormats( pRowNumFmt, nRowFmtCount, xLevRes, xDims );
643 else if ( bIsDataLayout )
645 // data layout dimension is hidden (allowed if there is only one data dimension)
646 // -> use the number format from the first data dimension for all results
648 nSingleNumFmt = lcl_GetFirstNumberFormat( xDims );
652 std::sort(pColFields.begin(), pColFields.end(), ScDPOutLevelDataComparator());
653 std::sort(pRowFields.begin(), pRowFields.end(), ScDPOutLevelDataComparator());
654 std::sort(pPageFields.begin(), pPageFields.end(), ScDPOutLevelDataComparator());
656 // get data results:
660 aData = xResult->getResults();
662 catch (const uno::RuntimeException&)
664 bResultsError = true;
668 // get "DataDescription" property (may be missing in external sources)
670 uno::Reference<beans::XPropertySet> xSrcProp( xSource, uno::UNO_QUERY );
671 if ( !xSrcProp.is() )
672 return;
676 uno::Any aAny = xSrcProp->getPropertyValue( SC_UNO_DP_DATADESC );
677 OUString aUStr;
678 aAny >>= aUStr;
679 aDataDescription = aUStr;
681 catch(const uno::Exception&)
686 ScDPOutput::~ScDPOutput()
690 void ScDPOutput::SetPosition( const ScAddress& rPos )
692 aStartPos = rPos;
693 bSizesValid = bSizeOverflow = false;
696 void ScDPOutput::DataCell( SCCOL nCol, SCROW nRow, SCTAB nTab, const sheet::DataResult& rData )
698 tools::Long nFlags = rData.Flags;
699 if ( nFlags & sheet::DataResultFlags::ERROR )
701 pDoc->SetError( nCol, nRow, nTab, FormulaError::NoValue );
703 else if ( nFlags & sheet::DataResultFlags::HASDATA )
705 pDoc->SetValue( nCol, nRow, nTab, rData.Value );
707 // use number formats from source
709 OSL_ENSURE( bSizesValid, "DataCell: !bSizesValid" );
710 sal_uInt32 nFormat = 0;
711 bool bApplyFormat = false;
712 if ( pColNumFmt )
714 if ( nCol >= nDataStartCol )
716 tools::Long nIndex = nCol - nDataStartCol;
717 if ( nIndex < nColFmtCount )
719 nFormat = pColNumFmt[nIndex];
720 bApplyFormat = true;
724 else if ( pRowNumFmt )
726 if ( nRow >= nDataStartRow )
728 tools::Long nIndex = nRow - nDataStartRow;
729 if ( nIndex < nRowFmtCount )
731 nFormat = pRowNumFmt[nIndex];
732 bApplyFormat = true;
736 else if ( nSingleNumFmt != 0 )
738 nFormat = nSingleNumFmt; // single format is used everywhere
739 bApplyFormat = true;
742 if (bApplyFormat)
743 pDoc->ApplyAttr(nCol, nRow, nTab, SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
745 // SubTotal formatting is controlled by headers
748 void ScDPOutput::HeaderCell( SCCOL nCol, SCROW nRow, SCTAB nTab,
749 const sheet::MemberResult& rData, bool bColHeader, tools::Long nLevel )
751 tools::Long nFlags = rData.Flags;
753 if ( nFlags & sheet::MemberResultFlags::HASMEMBER )
755 bool bNumeric = (nFlags & sheet::MemberResultFlags::NUMERIC) != 0;
756 if (bNumeric && std::isfinite( rData.Value))
758 pDoc->SetValue( nCol, nRow, nTab, rData.Value);
760 else
762 ScSetStringParam aParam;
763 if (bNumeric)
764 aParam.setNumericInput();
765 else
766 aParam.setTextInput();
768 pDoc->SetString(nCol, nRow, nTab, rData.Caption, &aParam);
772 if ( !(nFlags & sheet::MemberResultFlags::SUBTOTAL) )
773 return;
775 ScDPOutputImpl outputimp( pDoc, nTab,
776 nTabStartCol, nTabStartRow,
777 nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow );
778 //TODO: limit frames to horizontal or vertical?
779 if (bColHeader)
781 outputimp.OutputBlockFrame( nCol,nMemberStartRow+static_cast<SCROW>(nLevel), nCol,nDataStartRow-1 );
783 lcl_SetStyleById( pDoc,nTab, nCol,nMemberStartRow+static_cast<SCROW>(nLevel), nCol,nDataStartRow-1,
784 STR_PIVOT_STYLENAME_TITLE );
785 lcl_SetStyleById( pDoc,nTab, nCol,nDataStartRow, nCol,nTabEndRow,
786 STR_PIVOT_STYLENAME_RESULT );
788 else
790 outputimp.OutputBlockFrame( nMemberStartCol+static_cast<SCCOL>(nLevel),nRow, nDataStartCol-1,nRow );
791 lcl_SetStyleById( pDoc,nTab, nMemberStartCol+static_cast<SCCOL>(nLevel),nRow, nDataStartCol-1,nRow,
792 STR_PIVOT_STYLENAME_TITLE );
793 lcl_SetStyleById( pDoc,nTab, nDataStartCol,nRow, nTabEndCol,nRow,
794 STR_PIVOT_STYLENAME_RESULT );
798 void ScDPOutput::FieldCell(
799 SCCOL nCol, SCROW nRow, SCTAB nTab, const ScDPOutLevelData& rData, bool bInTable)
801 // Avoid unwanted automatic format detection.
802 ScSetStringParam aParam;
803 aParam.mbDetectNumberFormat = false;
804 aParam.meSetTextNumFormat = ScSetStringParam::Always;
805 aParam.mbHandleApostrophe = false;
806 pDoc->SetString(nCol, nRow, nTab, rData.maCaption, &aParam);
808 if (bInTable)
809 lcl_SetFrame( pDoc,nTab, nCol,nRow, nCol,nRow, 20 );
811 // For field button drawing
812 ScMF nMergeFlag = ScMF::NONE;
813 if (rData.mbHasHiddenMember)
814 nMergeFlag |= ScMF::HiddenMember;
816 if (rData.mbPageDim)
818 nMergeFlag |= ScMF::ButtonPopup;
819 pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, ScMF::Button);
820 pDoc->ApplyFlagsTab(nCol+1, nRow, nCol+1, nRow, nTab, nMergeFlag);
822 else
824 nMergeFlag |= ScMF::Button;
825 if (!rData.mbDataLayout)
826 nMergeFlag |= ScMF::ButtonPopup;
827 pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, nMergeFlag);
830 lcl_SetStyleById( pDoc,nTab, nCol,nRow, nCol,nRow, STR_PIVOT_STYLENAME_FIELDNAME );
833 static void lcl_DoFilterButton( ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
835 pDoc->SetString( nCol, nRow, nTab, ScResId(STR_CELL_FILTER) );
836 pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, ScMF::Button);
839 void ScDPOutput::CalcSizes()
841 if (bSizesValid)
842 return;
844 // get column size of data from first row
845 //TODO: allow different sizes (and clear following areas) ???
847 nRowCount = aData.getLength();
848 const uno::Sequence<sheet::DataResult>* pRowAry = aData.getConstArray();
849 nColCount = nRowCount ? ( pRowAry[0].getLength() ) : 0;
851 nHeaderSize = 1;
852 if (GetHeaderLayout() && pColFields.empty())
853 // Insert an extra header row only when there is no column field.
854 nHeaderSize = 2;
856 // calculate output positions and sizes
858 tools::Long nPageSize = 0; // use page fields!
859 if ( bDoFilter || !pPageFields.empty() )
861 nPageSize += pPageFields.size() + 1; // plus one empty row
862 if ( bDoFilter )
863 ++nPageSize; // filter button above the page fields
866 if ( aStartPos.Col() + static_cast<tools::Long>(pRowFields.size()) + nColCount - 1 > MAXCOL ||
867 aStartPos.Row() + nPageSize + nHeaderSize + pColFields.size() + nRowCount > MAXROW )
869 bSizeOverflow = true;
872 nTabStartCol = aStartPos.Col();
873 nTabStartRow = aStartPos.Row() + static_cast<SCROW>(nPageSize); // below page fields
874 nMemberStartCol = nTabStartCol;
875 nMemberStartRow = nTabStartRow + static_cast<SCROW>(nHeaderSize);
876 nDataStartCol = nMemberStartCol + static_cast<SCCOL>(pRowFields.size());
877 nDataStartRow = nMemberStartRow + static_cast<SCROW>(pColFields.size());
878 if ( nColCount > 0 )
879 nTabEndCol = nDataStartCol + static_cast<SCCOL>(nColCount) - 1;
880 else
881 nTabEndCol = nDataStartCol; // single column will remain empty
882 // if page fields are involved, include the page selection cells
883 if ( !pPageFields.empty() && nTabEndCol < nTabStartCol + 1 )
884 nTabEndCol = nTabStartCol + 1;
885 if ( nRowCount > 0 )
886 nTabEndRow = nDataStartRow + static_cast<SCROW>(nRowCount) - 1;
887 else
888 nTabEndRow = nDataStartRow; // single row will remain empty
889 bSizesValid = true;
892 sal_Int32 ScDPOutput::GetPositionType(const ScAddress& rPos)
894 using namespace ::com::sun::star::sheet;
896 SCCOL nCol = rPos.Col();
897 SCROW nRow = rPos.Row();
898 SCTAB nTab = rPos.Tab();
899 if ( nTab != aStartPos.Tab() )
900 return DataPilotTablePositionType::NOT_IN_TABLE;
902 CalcSizes();
904 // Make sure the cursor is within the table.
905 if (nCol < nTabStartCol || nRow < nTabStartRow || nCol > nTabEndCol || nRow > nTabEndRow)
906 return DataPilotTablePositionType::NOT_IN_TABLE;
908 // test for result data area.
909 if (nCol >= nDataStartCol && nCol <= nTabEndCol && nRow >= nDataStartRow && nRow <= nTabEndRow)
910 return DataPilotTablePositionType::RESULT;
912 bool bInColHeader = (nRow >= nTabStartRow && nRow < nDataStartRow);
913 bool bInRowHeader = (nCol >= nTabStartCol && nCol < nDataStartCol);
915 if (bInColHeader && bInRowHeader)
916 // probably in that ugly little box at the upper-left corner of the table.
917 return DataPilotTablePositionType::OTHER;
919 if (bInColHeader)
921 if (nRow == nTabStartRow)
922 // first row in the column header area is always used for column
923 // field buttons.
924 return DataPilotTablePositionType::OTHER;
926 return DataPilotTablePositionType::COLUMN_HEADER;
929 if (bInRowHeader)
930 return DataPilotTablePositionType::ROW_HEADER;
932 return DataPilotTablePositionType::OTHER;
935 void ScDPOutput::Output()
937 SCTAB nTab = aStartPos.Tab();
938 const uno::Sequence<sheet::DataResult>* pRowAry = aData.getConstArray();
940 // calculate output positions and sizes
942 CalcSizes();
943 if ( bSizeOverflow || bResultsError ) // does output area exceed sheet limits?
944 return; // nothing
946 // clear whole (new) output area
947 // when modifying table, clear old area !
948 //TODO: include InsertDeleteFlags::OBJECTS ???
949 pDoc->DeleteAreaTab( aStartPos.Col(), aStartPos.Row(), nTabEndCol, nTabEndRow, nTab, InsertDeleteFlags::ALL );
951 if ( bDoFilter )
952 lcl_DoFilterButton( pDoc, aStartPos.Col(), aStartPos.Row(), nTab );
954 // output page fields:
956 for (size_t nField=0; nField<pPageFields.size(); ++nField)
958 SCCOL nHdrCol = aStartPos.Col();
959 SCROW nHdrRow = aStartPos.Row() + nField + ( bDoFilter ? 1 : 0 );
960 // draw without frame for consistency with filter button:
961 FieldCell(nHdrCol, nHdrRow, nTab, pPageFields[nField], false);
962 SCCOL nFldCol = nHdrCol + 1;
964 OUString aPageValue = ScResId(SCSTR_ALL);
965 const uno::Sequence<sheet::MemberResult>& rRes = pPageFields[nField].maResult;
966 sal_Int32 n = rRes.getLength();
967 if (n == 1)
969 if (rRes[0].Caption.isEmpty())
970 aPageValue = ScResId(STR_EMPTYDATA);
971 else
972 aPageValue = rRes[0].Caption;
974 else if (n > 1)
975 aPageValue = ScResId(SCSTR_MULTIPLE);
977 ScSetStringParam aParam;
978 aParam.setTextInput();
979 pDoc->SetString(nFldCol, nHdrRow, nTab, aPageValue, &aParam);
981 lcl_SetFrame( pDoc,nTab, nFldCol,nHdrRow, nFldCol,nHdrRow, 20 );
984 // data description
985 // (may get overwritten by first row field)
987 if (aDataDescription.isEmpty())
989 //TODO: use default string ("result") ?
991 pDoc->SetString(nTabStartCol, nTabStartRow, nTab, aDataDescription);
993 // set STR_PIVOT_STYLENAME_INNER for whole data area (subtotals are overwritten)
995 if ( nDataStartRow > nTabStartRow )
996 lcl_SetStyleById( pDoc, nTab, nTabStartCol, nTabStartRow, nTabEndCol, nDataStartRow-1,
997 STR_PIVOT_STYLENAME_TOP );
998 lcl_SetStyleById( pDoc, nTab, nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow,
999 STR_PIVOT_STYLENAME_INNER );
1001 // output column headers:
1002 ScDPOutputImpl outputimp( pDoc, nTab,
1003 nTabStartCol, nTabStartRow,
1004 nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow );
1005 for (size_t nField=0; nField<pColFields.size(); nField++)
1007 SCCOL nHdrCol = nDataStartCol + static_cast<SCCOL>(nField); //TODO: check for overflow
1008 FieldCell(nHdrCol, nTabStartRow, nTab, pColFields[nField], true);
1010 SCROW nRowPos = nMemberStartRow + static_cast<SCROW>(nField); //TODO: check for overflow
1011 const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nField].maResult;
1012 const sheet::MemberResult* pArray = rSequence.getConstArray();
1013 tools::Long nThisColCount = rSequence.getLength();
1014 OSL_ENSURE( nThisColCount == nColCount, "count mismatch" ); //TODO: ???
1015 for (tools::Long nCol=0; nCol<nThisColCount; nCol++)
1017 SCCOL nColPos = nDataStartCol + static_cast<SCCOL>(nCol); //TODO: check for overflow
1018 HeaderCell( nColPos, nRowPos, nTab, pArray[nCol], true, nField );
1019 if ( ( pArray[nCol].Flags & sheet::MemberResultFlags::HASMEMBER ) &&
1020 !( pArray[nCol].Flags & sheet::MemberResultFlags::SUBTOTAL ) )
1022 tools::Long nEnd = nCol;
1023 while ( nEnd+1 < nThisColCount && ( pArray[nEnd+1].Flags & sheet::MemberResultFlags::CONTINUE ) )
1024 ++nEnd;
1025 SCCOL nEndColPos = nDataStartCol + static_cast<SCCOL>(nEnd); //TODO: check for overflow
1026 if ( nField+1 < pColFields.size())
1028 if ( nField == pColFields.size() - 2 )
1030 outputimp.AddCol( nColPos );
1031 if ( nColPos + 1 == nEndColPos )
1032 outputimp.OutputBlockFrame( nColPos,nRowPos, nEndColPos,nRowPos+1, true );
1034 else
1035 outputimp.OutputBlockFrame( nColPos,nRowPos, nEndColPos,nRowPos );
1037 lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nEndColPos,nDataStartRow-1, STR_PIVOT_STYLENAME_CATEGORY );
1039 else
1040 lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nColPos,nDataStartRow-1, STR_PIVOT_STYLENAME_CATEGORY );
1042 else if ( pArray[nCol].Flags & sheet::MemberResultFlags::SUBTOTAL )
1043 outputimp.AddCol( nColPos );
1045 // Apply the same number format as in data source.
1046 pDoc->ApplyAttr(nColPos, nRowPos, nTab, SfxUInt32Item(ATTR_VALUE_FORMAT, pColFields[nField].mnSrcNumFmt));
1048 if ( nField== 0 && pColFields.size() == 1 )
1049 outputimp.OutputBlockFrame( nDataStartCol,nTabStartRow, nTabEndCol,nRowPos-1 );
1052 // output row headers:
1053 std::vector<bool> vbSetBorder;
1054 vbSetBorder.resize( nTabEndRow - nDataStartRow + 1, false );
1055 for (size_t nField=0; nField<pRowFields.size(); nField++)
1057 SCCOL nHdrCol = nTabStartCol + static_cast<SCCOL>(nField); //TODO: check for overflow
1058 SCROW nHdrRow = nDataStartRow - 1;
1059 FieldCell(nHdrCol, nHdrRow, nTab, pRowFields[nField], true);
1061 SCCOL nColPos = nMemberStartCol + static_cast<SCCOL>(nField); //TODO: check for overflow
1062 const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].maResult;
1063 const sheet::MemberResult* pArray = rSequence.getConstArray();
1064 sal_Int32 nThisRowCount = rSequence.getLength();
1065 OSL_ENSURE( nThisRowCount == nRowCount, "count mismatch" ); //TODO: ???
1066 for (sal_Int32 nRow=0; nRow<nThisRowCount; nRow++)
1068 SCROW nRowPos = nDataStartRow + static_cast<SCROW>(nRow); //TODO: check for overflow
1069 HeaderCell( nColPos, nRowPos, nTab, pArray[nRow], false, nField );
1070 if ( ( pArray[nRow].Flags & sheet::MemberResultFlags::HASMEMBER ) &&
1071 !( pArray[nRow].Flags & sheet::MemberResultFlags::SUBTOTAL ) )
1073 if ( nField+1 < pRowFields.size() )
1075 tools::Long nEnd = nRow;
1076 while ( nEnd+1 < nThisRowCount && ( pArray[nEnd+1].Flags & sheet::MemberResultFlags::CONTINUE ) )
1077 ++nEnd;
1078 SCROW nEndRowPos = nDataStartRow + static_cast<SCROW>(nEnd); //TODO: check for overflow
1079 outputimp.AddRow( nRowPos );
1080 if ( !vbSetBorder[ nRow ] )
1082 outputimp.OutputBlockFrame( nColPos, nRowPos, nTabEndCol, nEndRowPos );
1083 vbSetBorder[ nRow ] = true;
1085 outputimp.OutputBlockFrame( nColPos, nRowPos, nColPos, nEndRowPos );
1087 if ( nField == pRowFields.size() - 2 )
1088 outputimp.OutputBlockFrame( nColPos+1, nRowPos, nColPos+1, nEndRowPos );
1090 lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nDataStartCol-1,nEndRowPos, STR_PIVOT_STYLENAME_CATEGORY );
1092 else
1093 lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nDataStartCol-1,nRowPos, STR_PIVOT_STYLENAME_CATEGORY );
1095 else if ( pArray[nRow].Flags & sheet::MemberResultFlags::SUBTOTAL )
1096 outputimp.AddRow( nRowPos );
1098 // Apply the same number format as in data source.
1099 pDoc->ApplyAttr(nColPos, nRowPos, nTab, SfxUInt32Item(ATTR_VALUE_FORMAT, pRowFields[nField].mnSrcNumFmt));
1103 if (nColCount == 1 && nRowCount > 0 && pColFields.empty())
1105 // the table contains exactly one data field and no column fields.
1106 // Display data description at top right corner.
1107 ScSetStringParam aParam;
1108 aParam.setTextInput();
1109 pDoc->SetString(nDataStartCol, nDataStartRow-1, nTab, aDataDescription, &aParam);
1112 // output data results:
1114 for (sal_Int32 nRow=0; nRow<nRowCount; nRow++)
1116 SCROW nRowPos = nDataStartRow + static_cast<SCROW>(nRow); //TODO: check for overflow
1117 const sheet::DataResult* pColAry = pRowAry[nRow].getConstArray();
1118 sal_Int32 nThisColCount = pRowAry[nRow].getLength();
1119 OSL_ENSURE( nThisColCount == nColCount, "count mismatch" ); //TODO: ???
1120 for (sal_Int32 nCol=0; nCol<nThisColCount; nCol++)
1122 SCCOL nColPos = nDataStartCol + static_cast<SCCOL>(nCol); //TODO: check for overflow
1123 DataCell( nColPos, nRowPos, nTab, pColAry[nCol] );
1127 outputimp.OutputDataArea();
1130 ScRange ScDPOutput::GetOutputRange( sal_Int32 nRegionType )
1132 using namespace ::com::sun::star::sheet;
1134 CalcSizes();
1136 SCTAB nTab = aStartPos.Tab();
1137 switch (nRegionType)
1139 case DataPilotOutputRangeType::RESULT:
1140 return ScRange(nDataStartCol, nDataStartRow, nTab, nTabEndCol, nTabEndRow, nTab);
1141 case DataPilotOutputRangeType::TABLE:
1142 return ScRange(aStartPos.Col(), nTabStartRow, nTab, nTabEndCol, nTabEndRow, nTab);
1143 default:
1144 OSL_ENSURE(nRegionType == DataPilotOutputRangeType::WHOLE, "ScDPOutput::GetOutputRange: unknown region type");
1145 break;
1147 return ScRange(aStartPos.Col(), aStartPos.Row(), nTab, nTabEndCol, nTabEndRow, nTab);
1150 bool ScDPOutput::HasError()
1152 CalcSizes();
1154 return bSizeOverflow || bResultsError;
1157 sal_Int32 ScDPOutput::GetHeaderRows() const
1159 return pPageFields.size() + ( bDoFilter ? 1 : 0 );
1162 namespace
1164 void insertNames(ScDPUniqueStringSet& rNames, const uno::Sequence<sheet::MemberResult>& rMemberResults)
1166 for (const sheet::MemberResult& rMemberResult : rMemberResults)
1168 if (rMemberResult.Flags & sheet::MemberResultFlags::HASMEMBER)
1169 rNames.insert(rMemberResult.Name);
1174 void ScDPOutput::GetMemberResultNames(ScDPUniqueStringSet& rNames, tools::Long nDimension)
1176 // Return the list of all member names in a dimension's MemberResults.
1177 // Only the dimension has to be compared because this is only used with table data,
1178 // where each dimension occurs only once.
1180 auto lFindDimension = [nDimension](const ScDPOutLevelData& rField) { return rField.mnDim == nDimension; };
1182 // look in column fields
1183 auto colit = std::find_if(pColFields.begin(), pColFields.end(), lFindDimension);
1184 if (colit != pColFields.end())
1186 // collect the member names
1187 insertNames(rNames, colit->maResult);
1188 return;
1191 // look in row fields
1192 auto rowit = std::find_if(pRowFields.begin(), pRowFields.end(), lFindDimension);
1193 if (rowit != pRowFields.end())
1195 // collect the member names
1196 insertNames(rNames, rowit->maResult);
1200 void ScDPOutput::SetHeaderLayout(bool bUseGrid)
1202 mbHeaderLayout = bUseGrid;
1203 bSizesValid = false;
1206 namespace {
1208 void lcl_GetTableVars( sal_Int32& rGrandTotalCols, sal_Int32& rGrandTotalRows, sal_Int32& rDataLayoutIndex,
1209 std::vector<OUString>& rDataNames, std::vector<OUString>& rGivenNames,
1210 sheet::DataPilotFieldOrientation& rDataOrient,
1211 const uno::Reference<sheet::XDimensionsSupplier>& xSource )
1213 rDataLayoutIndex = -1; // invalid
1214 rGrandTotalCols = 0;
1215 rGrandTotalRows = 0;
1216 rDataOrient = sheet::DataPilotFieldOrientation_HIDDEN;
1218 uno::Reference<beans::XPropertySet> xSrcProp( xSource, uno::UNO_QUERY );
1219 bool bColGrand = ScUnoHelpFunctions::GetBoolProperty(
1220 xSrcProp, SC_UNO_DP_COLGRAND);
1221 if ( bColGrand )
1222 rGrandTotalCols = 1; // default if data layout not in columns
1224 bool bRowGrand = ScUnoHelpFunctions::GetBoolProperty(
1225 xSrcProp, SC_UNO_DP_ROWGRAND);
1226 if ( bRowGrand )
1227 rGrandTotalRows = 1; // default if data layout not in rows
1229 if ( !xSource.is() )
1230 return;
1232 // find index and orientation of "data layout" dimension, count data dimensions
1234 sal_Int32 nDataCount = 0;
1236 uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xSource->getDimensions() );
1237 tools::Long nDimCount = xDims->getCount();
1238 for (tools::Long nDim=0; nDim<nDimCount; nDim++)
1240 uno::Reference<uno::XInterface> xDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
1241 uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
1242 if ( xDimProp.is() )
1244 sheet::DataPilotFieldOrientation eDimOrient =
1245 ScUnoHelpFunctions::GetEnumProperty(
1246 xDimProp, SC_UNO_DP_ORIENTATION,
1247 sheet::DataPilotFieldOrientation_HIDDEN );
1248 if ( ScUnoHelpFunctions::GetBoolProperty( xDimProp,
1249 SC_UNO_DP_ISDATALAYOUT ) )
1251 rDataLayoutIndex = nDim;
1252 rDataOrient = eDimOrient;
1254 if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
1256 OUString aSourceName;
1257 OUString aGivenName;
1258 ScDPOutput::GetDataDimensionNames( aSourceName, aGivenName, xDim );
1261 uno::Any aValue = xDimProp->getPropertyValue( SC_UNO_DP_LAYOUTNAME );
1263 if( aValue.hasValue() )
1265 OUString strLayoutName;
1267 if( ( aValue >>= strLayoutName ) && !strLayoutName.isEmpty() )
1268 aGivenName = strLayoutName;
1271 catch(const uno::Exception&)
1274 rDataNames.push_back( aSourceName );
1275 rGivenNames.push_back( aGivenName );
1277 ++nDataCount;
1282 if ( ( rDataOrient == sheet::DataPilotFieldOrientation_COLUMN ) && bColGrand )
1283 rGrandTotalCols = nDataCount;
1284 else if ( ( rDataOrient == sheet::DataPilotFieldOrientation_ROW ) && bRowGrand )
1285 rGrandTotalRows = nDataCount;
1290 void ScDPOutput::GetPositionData(const ScAddress& rPos, DataPilotTablePositionData& rPosData)
1292 using namespace ::com::sun::star::sheet;
1294 SCCOL nCol = rPos.Col();
1295 SCROW nRow = rPos.Row();
1296 SCTAB nTab = rPos.Tab();
1297 if ( nTab != aStartPos.Tab() )
1298 return; // wrong sheet
1300 // calculate output positions and sizes
1302 CalcSizes();
1304 rPosData.PositionType = GetPositionType(rPos);
1305 switch (rPosData.PositionType)
1307 case DataPilotTablePositionType::RESULT:
1309 vector<DataPilotFieldFilter> aFilters;
1310 GetDataResultPositionData(aFilters, rPos);
1312 DataPilotTableResultData aResData;
1313 aResData.FieldFilters = comphelper::containerToSequence(aFilters);
1314 aResData.DataFieldIndex = 0;
1315 Reference<beans::XPropertySet> xPropSet(xSource, UNO_QUERY);
1316 if (xPropSet.is())
1318 sal_Int32 nDataFieldCount = ScUnoHelpFunctions::GetLongProperty( xPropSet,
1319 SC_UNO_DP_DATAFIELDCOUNT );
1320 if (nDataFieldCount > 0)
1321 aResData.DataFieldIndex = (nRow - nDataStartRow) % nDataFieldCount;
1324 // Copy appropriate DataResult object from the cached sheet::DataResult table.
1325 if (aData.getLength() > nRow - nDataStartRow &&
1326 aData[nRow-nDataStartRow].getLength() > nCol-nDataStartCol)
1327 aResData.Result = aData[nRow-nDataStartRow][nCol-nDataStartCol];
1329 rPosData.PositionData <<= aResData;
1330 return;
1332 case DataPilotTablePositionType::COLUMN_HEADER:
1334 tools::Long nField = nRow - nTabStartRow - 1; // 1st line is used for the buttons
1335 if (nField < 0)
1336 break;
1338 if (pColFields.size() < o3tl::make_unsigned(nField) + 1 )
1339 break;
1340 const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nField].maResult;
1341 if (!rSequence.hasElements())
1342 break;
1343 const sheet::MemberResult* pArray = rSequence.getConstArray();
1345 tools::Long nItem = nCol - nDataStartCol;
1346 // get origin of "continue" fields
1347 while (nItem > 0 && ( pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1348 --nItem;
1350 if (nItem < 0)
1351 break;
1353 DataPilotTableHeaderData aHeaderData;
1354 aHeaderData.MemberName = pArray[nItem].Name;
1355 aHeaderData.Flags = pArray[nItem].Flags;
1356 aHeaderData.Dimension = static_cast<sal_Int32>(pColFields[nField].mnDim);
1357 aHeaderData.Hierarchy = static_cast<sal_Int32>(pColFields[nField].mnHier);
1358 aHeaderData.Level = static_cast<sal_Int32>(pColFields[nField].mnLevel);
1360 rPosData.PositionData <<= aHeaderData;
1361 return;
1363 case DataPilotTablePositionType::ROW_HEADER:
1365 tools::Long nField = nCol - nTabStartCol;
1366 if (nField < 0)
1367 break;
1369 if (pRowFields.size() < o3tl::make_unsigned(nField) + 1 )
1370 break;
1371 const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].maResult;
1372 if (!rSequence.hasElements())
1373 break;
1374 const sheet::MemberResult* pArray = rSequence.getConstArray();
1376 tools::Long nItem = nRow - nDataStartRow;
1377 // get origin of "continue" fields
1378 while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1379 --nItem;
1381 if (nItem < 0)
1382 break;
1384 DataPilotTableHeaderData aHeaderData;
1385 aHeaderData.MemberName = pArray[nItem].Name;
1386 aHeaderData.Flags = pArray[nItem].Flags;
1387 aHeaderData.Dimension = static_cast<sal_Int32>(pRowFields[nField].mnDim);
1388 aHeaderData.Hierarchy = static_cast<sal_Int32>(pRowFields[nField].mnHier);
1389 aHeaderData.Level = static_cast<sal_Int32>(pRowFields[nField].mnLevel);
1391 rPosData.PositionData <<= aHeaderData;
1392 return;
1397 bool ScDPOutput::GetDataResultPositionData(vector<sheet::DataPilotFieldFilter>& rFilters, const ScAddress& rPos)
1399 // Check to make sure there is at least one data field.
1400 Reference<beans::XPropertySet> xPropSet(xSource, UNO_QUERY);
1401 if (!xPropSet.is())
1402 return false;
1404 sal_Int32 nDataFieldCount = ScUnoHelpFunctions::GetLongProperty( xPropSet,
1405 SC_UNO_DP_DATAFIELDCOUNT );
1406 if (nDataFieldCount == 0)
1407 // No data field is present in this datapilot table.
1408 return false;
1410 // #i111421# use lcl_GetTableVars for correct size of totals and data layout position
1411 sal_Int32 nGrandTotalCols;
1412 sal_Int32 nGrandTotalRows;
1413 sal_Int32 nDataLayoutIndex;
1414 std::vector<OUString> aDataNames;
1415 std::vector<OUString> aGivenNames;
1416 sheet::DataPilotFieldOrientation eDataOrient;
1417 lcl_GetTableVars( nGrandTotalCols, nGrandTotalRows, nDataLayoutIndex, aDataNames, aGivenNames, eDataOrient, xSource );
1419 SCCOL nCol = rPos.Col();
1420 SCROW nRow = rPos.Row();
1421 SCTAB nTab = rPos.Tab();
1422 if ( nTab != aStartPos.Tab() )
1423 return false; // wrong sheet
1425 CalcSizes();
1427 // test for data area.
1428 if (nCol < nDataStartCol || nCol > nTabEndCol || nRow < nDataStartRow || nRow > nTabEndRow)
1430 // Cell is outside the data field area.
1431 return false;
1434 bool bFilterByCol = (nCol <= static_cast<SCCOL>(nTabEndCol - nGrandTotalCols));
1435 bool bFilterByRow = (nRow <= static_cast<SCROW>(nTabEndRow - nGrandTotalRows));
1437 // column fields
1438 for (size_t nColField = 0; nColField < pColFields.size() && bFilterByCol; ++nColField)
1440 if (pColFields[nColField].mnDim == nDataLayoutIndex)
1441 // There is no sense including the data layout field for filtering.
1442 continue;
1444 sheet::DataPilotFieldFilter filter;
1445 filter.FieldName = pColFields[nColField].maName;
1447 const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nColField].maResult;
1448 const sheet::MemberResult* pArray = rSequence.getConstArray();
1450 OSL_ENSURE(nDataStartCol + rSequence.getLength() - 1 == nTabEndCol, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
1452 tools::Long nItem = nCol - nDataStartCol;
1453 // get origin of "continue" fields
1454 while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1455 --nItem;
1457 filter.MatchValueName = pArray[nItem].Name;
1458 rFilters.push_back(filter);
1461 // row fields
1462 for (size_t nRowField = 0; nRowField < pRowFields.size() && bFilterByRow; ++nRowField)
1464 if (pRowFields[nRowField].mnDim == nDataLayoutIndex)
1465 // There is no sense including the data layout field for filtering.
1466 continue;
1468 sheet::DataPilotFieldFilter filter;
1469 filter.FieldName = pRowFields[nRowField].maName;
1471 const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nRowField].maResult;
1472 const sheet::MemberResult* pArray = rSequence.getConstArray();
1474 OSL_ENSURE(nDataStartRow + rSequence.getLength() - 1 == nTabEndRow, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
1476 tools::Long nItem = nRow - nDataStartRow;
1477 // get origin of "continue" fields
1478 while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
1479 --nItem;
1481 filter.MatchValueName = pArray[nItem].Name;
1482 rFilters.push_back(filter);
1485 return true;
1488 namespace {
1490 OUString lcl_GetDataFieldName( std::u16string_view rSourceName, sal_Int16 eFunc )
1492 TranslateId pStrId;
1493 switch ( eFunc )
1495 case sheet::GeneralFunction2::SUM: pStrId = STR_FUN_TEXT_SUM; break;
1496 case sheet::GeneralFunction2::COUNT:
1497 case sheet::GeneralFunction2::COUNTNUMS: pStrId = STR_FUN_TEXT_COUNT; break;
1498 case sheet::GeneralFunction2::AVERAGE: pStrId = STR_FUN_TEXT_AVG; break;
1499 case sheet::GeneralFunction2::MEDIAN: pStrId = STR_FUN_TEXT_MEDIAN; break;
1500 case sheet::GeneralFunction2::MAX: pStrId = STR_FUN_TEXT_MAX; break;
1501 case sheet::GeneralFunction2::MIN: pStrId = STR_FUN_TEXT_MIN; break;
1502 case sheet::GeneralFunction2::PRODUCT: pStrId = STR_FUN_TEXT_PRODUCT; break;
1503 case sheet::GeneralFunction2::STDEV:
1504 case sheet::GeneralFunction2::STDEVP: pStrId = STR_FUN_TEXT_STDDEV; break;
1505 case sheet::GeneralFunction2::VAR:
1506 case sheet::GeneralFunction2::VARP: pStrId = STR_FUN_TEXT_VAR; break;
1507 case sheet::GeneralFunction2::NONE:
1508 case sheet::GeneralFunction2::AUTO: break;
1509 default:
1511 assert(false);
1514 if (!pStrId)
1515 return OUString();
1517 return ScResId(pStrId) + " - " + rSourceName;
1522 void ScDPOutput::GetDataDimensionNames(
1523 OUString& rSourceName, OUString& rGivenName, const uno::Reference<uno::XInterface>& xDim )
1525 uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
1526 uno::Reference<container::XNamed> xDimName( xDim, uno::UNO_QUERY );
1527 if ( !(xDimProp.is() && xDimName.is()) )
1528 return;
1530 // Asterisks are added in ScDPSaveData::WriteToSource to create unique names.
1531 //TODO: preserve original name there?
1532 rSourceName = ScDPUtil::getSourceDimensionName(xDimName->getName());
1534 // Generate "given name" the same way as in dptabres.
1535 //TODO: Should use a stored name when available
1537 sal_Int16 eFunc = ScUnoHelpFunctions::GetShortProperty(
1538 xDimProp, SC_UNO_DP_FUNCTION2,
1539 sheet::GeneralFunction2::NONE );
1540 rGivenName = lcl_GetDataFieldName( rSourceName, eFunc );
1543 bool ScDPOutput::IsFilterButton( const ScAddress& rPos )
1545 SCCOL nCol = rPos.Col();
1546 SCROW nRow = rPos.Row();
1547 SCTAB nTab = rPos.Tab();
1548 if ( nTab != aStartPos.Tab() || !bDoFilter )
1549 return false; // wrong sheet or no button at all
1551 // filter button is at top left
1552 return ( nCol == aStartPos.Col() && nRow == aStartPos.Row() );
1555 tools::Long ScDPOutput::GetHeaderDim( const ScAddress& rPos, sheet::DataPilotFieldOrientation& rOrient )
1557 SCCOL nCol = rPos.Col();
1558 SCROW nRow = rPos.Row();
1559 SCTAB nTab = rPos.Tab();
1560 if ( nTab != aStartPos.Tab() )
1561 return -1; // wrong sheet
1563 // calculate output positions and sizes
1565 CalcSizes();
1567 // test for column header
1569 if ( nRow == nTabStartRow && nCol >= nDataStartCol && o3tl::make_unsigned(nCol) < nDataStartCol + pColFields.size())
1571 rOrient = sheet::DataPilotFieldOrientation_COLUMN;
1572 tools::Long nField = nCol - nDataStartCol;
1573 return pColFields[nField].mnDim;
1576 // test for row header
1578 if ( nRow+1 == nDataStartRow && nCol >= nTabStartCol && o3tl::make_unsigned(nCol) < nTabStartCol + pRowFields.size() )
1580 rOrient = sheet::DataPilotFieldOrientation_ROW;
1581 tools::Long nField = nCol - nTabStartCol;
1582 return pRowFields[nField].mnDim;
1585 // test for page field
1587 SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 );
1588 if ( nCol == aStartPos.Col() && nRow >= nPageStartRow && o3tl::make_unsigned(nRow) < nPageStartRow + pPageFields.size() )
1590 rOrient = sheet::DataPilotFieldOrientation_PAGE;
1591 tools::Long nField = nRow - nPageStartRow;
1592 return pPageFields[nField].mnDim;
1595 //TODO: single data field (?)
1597 rOrient = sheet::DataPilotFieldOrientation_HIDDEN;
1598 return -1; // invalid
1601 bool ScDPOutput::GetHeaderDrag( const ScAddress& rPos, bool bMouseLeft, bool bMouseTop,
1602 tools::Long nDragDim,
1603 tools::Rectangle& rPosRect, sheet::DataPilotFieldOrientation& rOrient, tools::Long& rDimPos )
1605 // Rectangle instead of ScRange for rPosRect to allow for negative values
1607 SCCOL nCol = rPos.Col();
1608 SCROW nRow = rPos.Row();
1609 SCTAB nTab = rPos.Tab();
1610 if ( nTab != aStartPos.Tab() )
1611 return false; // wrong sheet
1613 // calculate output positions and sizes
1615 CalcSizes();
1617 // test for column header
1619 if ( nCol >= nDataStartCol && nCol <= nTabEndCol &&
1620 nRow + 1 >= nMemberStartRow && o3tl::make_unsigned(nRow) < nMemberStartRow + pColFields.size())
1622 tools::Long nField = nRow - nMemberStartRow;
1623 if (nField < 0)
1625 nField = 0;
1626 bMouseTop = true;
1628 //TODO: find start of dimension
1630 rPosRect = tools::Rectangle( nDataStartCol, nMemberStartRow + nField,
1631 nTabEndCol, nMemberStartRow + nField -1 );
1633 bool bFound = false; // is this within the same orientation?
1634 bool bBeforeDrag = false;
1635 bool bAfterDrag = false;
1636 for (tools::Long nPos=0; o3tl::make_unsigned(nPos)<pColFields.size() && !bFound; nPos++)
1638 if (pColFields[nPos].mnDim == nDragDim)
1640 bFound = true;
1641 if ( nField < nPos )
1642 bBeforeDrag = true;
1643 else if ( nField > nPos )
1644 bAfterDrag = true;
1648 if ( bFound )
1650 if (!bBeforeDrag)
1652 rPosRect.AdjustBottom( 1 );
1653 if (bAfterDrag)
1654 rPosRect.AdjustTop( 1 );
1657 else
1659 if ( !bMouseTop )
1661 rPosRect.AdjustTop( 1 );
1662 rPosRect.AdjustBottom( 1 );
1663 ++nField;
1667 rOrient = sheet::DataPilotFieldOrientation_COLUMN;
1668 rDimPos = nField; //!...
1669 return true;
1672 // test for row header
1674 // special case if no row fields
1675 bool bSpecial = ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow &&
1676 pRowFields.empty() && nCol == nTabStartCol && bMouseLeft );
1678 if ( bSpecial || ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow &&
1679 nCol + 1 >= nTabStartCol && o3tl::make_unsigned(nCol) < nTabStartCol + pRowFields.size() ) )
1681 tools::Long nField = nCol - nTabStartCol;
1682 //TODO: find start of dimension
1684 rPosRect = tools::Rectangle( nTabStartCol + nField, nDataStartRow - 1,
1685 nTabStartCol + nField - 1, nTabEndRow );
1687 bool bFound = false; // is this within the same orientation?
1688 bool bBeforeDrag = false;
1689 bool bAfterDrag = false;
1690 for (tools::Long nPos=0; o3tl::make_unsigned(nPos)<pRowFields.size() && !bFound; nPos++)
1692 if (pRowFields[nPos].mnDim == nDragDim)
1694 bFound = true;
1695 if ( nField < nPos )
1696 bBeforeDrag = true;
1697 else if ( nField > nPos )
1698 bAfterDrag = true;
1702 if ( bFound )
1704 if (!bBeforeDrag)
1706 rPosRect.AdjustRight( 1 );
1707 if (bAfterDrag)
1708 rPosRect.AdjustLeft( 1 );
1711 else
1713 if ( !bMouseLeft )
1715 rPosRect.AdjustLeft( 1 );
1716 rPosRect.AdjustRight( 1 );
1717 ++nField;
1721 rOrient = sheet::DataPilotFieldOrientation_ROW;
1722 rDimPos = nField; //!...
1723 return true;
1726 // test for page fields
1728 SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 );
1729 if ( nCol >= aStartPos.Col() && nCol <= nTabEndCol &&
1730 nRow + 1 >= nPageStartRow && o3tl::make_unsigned(nRow) < nPageStartRow + pPageFields.size() )
1732 tools::Long nField = nRow - nPageStartRow;
1733 if (nField < 0)
1735 nField = 0;
1736 bMouseTop = true;
1738 //TODO: find start of dimension
1740 rPosRect = tools::Rectangle( aStartPos.Col(), nPageStartRow + nField,
1741 nTabEndCol, nPageStartRow + nField - 1 );
1743 bool bFound = false; // is this within the same orientation?
1744 bool bBeforeDrag = false;
1745 bool bAfterDrag = false;
1746 for (tools::Long nPos=0; o3tl::make_unsigned(nPos)<pPageFields.size() && !bFound; nPos++)
1748 if (pPageFields[nPos].mnDim == nDragDim)
1750 bFound = true;
1751 if ( nField < nPos )
1752 bBeforeDrag = true;
1753 else if ( nField > nPos )
1754 bAfterDrag = true;
1758 if ( bFound )
1760 if (!bBeforeDrag)
1762 rPosRect.AdjustBottom( 1 );
1763 if (bAfterDrag)
1764 rPosRect.AdjustTop( 1 );
1767 else
1769 if ( !bMouseTop )
1771 rPosRect.AdjustTop( 1 );
1772 rPosRect.AdjustBottom( 1 );
1773 ++nField;
1777 rOrient = sheet::DataPilotFieldOrientation_PAGE;
1778 rDimPos = nField; //!...
1779 return true;
1782 return false;
1785 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */