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