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