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 <tools/debug.hxx> 36 #include <rtl/math.hxx> 37 38 #include "dptabdat.hxx" 39 #include "dptabres.hxx" 40 #include "dptabsrc.hxx" 41 #include "global.hxx" 42 #include "subtotal.hxx" 43 #include "globstr.hrc" 44 #include "datauno.hxx" // ScDataUnoConversion 45 46 #include "document.hxx" // for DumpState only! 47 48 #include <math.h> 49 #include <float.h> //! Test !!! 50 #include <algorithm> 51 #include <hash_map> 52 53 #include <com/sun/star/sheet/DataResultFlags.hpp> 54 #include <com/sun/star/sheet/MemberResultFlags.hpp> 55 #include <com/sun/star/sheet/DataPilotFieldOrientation.hpp> 56 #include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp> 57 #include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp> 58 #include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp> 59 #include <com/sun/star/sheet/DataPilotFieldSortMode.hpp> 60 #include <com/sun/star/sheet/DataPilotFieldFilter.hpp> 61 62 using namespace com::sun::star; 63 using ::std::vector; 64 using ::std::pair; 65 using ::std::hash_map; 66 using ::com::sun::star::uno::Sequence; 67 using ::rtl::OUString; 68 69 // ----------------------------------------------------------------------- 70 71 SV_IMPL_PTRARR( ScDPDataMembers, ScDPDataMemberPtr ); 72 73 // ----------------------------------------------------------------------- 74 75 static sal_uInt16 nFuncStrIds[12] = // passend zum enum ScSubTotalFunc 76 { 77 0, // SUBTOTAL_FUNC_NONE 78 STR_FUN_TEXT_AVG, // SUBTOTAL_FUNC_AVE 79 STR_FUN_TEXT_COUNT, // SUBTOTAL_FUNC_CNT 80 STR_FUN_TEXT_COUNT, // SUBTOTAL_FUNC_CNT2 81 STR_FUN_TEXT_MAX, // SUBTOTAL_FUNC_MAX 82 STR_FUN_TEXT_MIN, // SUBTOTAL_FUNC_MIN 83 STR_FUN_TEXT_PRODUCT, // SUBTOTAL_FUNC_PROD 84 STR_FUN_TEXT_STDDEV, // SUBTOTAL_FUNC_STD 85 STR_FUN_TEXT_STDDEV, // SUBTOTAL_FUNC_STDP 86 STR_FUN_TEXT_SUM, // SUBTOTAL_FUNC_SUM 87 STR_FUN_TEXT_VAR, // SUBTOTAL_FUNC_VAR 88 STR_FUN_TEXT_VAR // SUBTOTAL_FUNC_VARP 89 }; 90 namespace { 91 template < typename T > 92 void lcl_ResizePointVector( T & vec, size_t nSize ) 93 { 94 95 for ( size_t i = 0 ; i < vec.size(); i++ ) 96 { 97 if ( vec[i] ) 98 delete vec[i]; 99 } 100 vec.resize( nSize, NULL ); 101 } 102 sal_Bool lcl_SearchMember( const std::vector <ScDPResultMember *>& list, SCROW nOrder, SCROW& rIndex) 103 { 104 rIndex = list.size(); 105 sal_Bool bFound = sal_False; 106 SCROW nLo = 0; 107 SCROW nHi = list.size() - 1; 108 SCROW nIndex; 109 while (nLo <= nHi) 110 { 111 nIndex = (nLo + nHi) / 2; 112 if ( list[nIndex]->GetOrder() < nOrder ) 113 nLo = nIndex + 1; 114 else 115 { 116 nHi = nIndex - 1; 117 if ( list[nIndex]->GetOrder() == nOrder ) 118 { 119 bFound = sal_True; 120 nLo = nIndex; 121 } 122 } 123 } 124 rIndex = nLo; 125 return bFound; 126 } 127 } 128 // ----------------------------------------------------------------------- 129 130 // 131 // function objects for sorting of the column and row members: 132 // 133 134 class ScDPRowMembersOrder 135 { 136 ScDPResultDimension& rDimension; 137 long nMeasure; 138 sal_Bool bAscending; 139 140 public: 141 ScDPRowMembersOrder( ScDPResultDimension& rDim, long nM, sal_Bool bAsc ) : 142 rDimension(rDim), 143 nMeasure(nM), 144 bAscending(bAsc) 145 {} 146 ~ScDPRowMembersOrder() {} 147 148 sal_Bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const; 149 }; 150 151 class ScDPColMembersOrder 152 { 153 ScDPDataDimension& rDimension; 154 long nMeasure; 155 sal_Bool bAscending; 156 157 public: 158 ScDPColMembersOrder( ScDPDataDimension& rDim, long nM, sal_Bool bAsc ) : 159 rDimension(rDim), 160 nMeasure(nM), 161 bAscending(bAsc) 162 {} 163 ~ScDPColMembersOrder() {} 164 165 sal_Bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const; 166 }; 167 168 static sal_Bool lcl_IsLess( const ScDPDataMember* pDataMember1, const ScDPDataMember* pDataMember2, long nMeasure, sal_Bool bAscending ) 169 { 170 // members can be NULL if used for rows 171 172 ScDPSubTotalState aEmptyState; 173 const ScDPAggData* pAgg1 = pDataMember1 ? pDataMember1->GetConstAggData( nMeasure, aEmptyState ) : NULL; 174 const ScDPAggData* pAgg2 = pDataMember2 ? pDataMember2->GetConstAggData( nMeasure, aEmptyState ) : NULL; 175 176 sal_Bool bError1 = pAgg1 && pAgg1->HasError(); 177 sal_Bool bError2 = pAgg2 && pAgg2->HasError(); 178 if ( bError1 ) 179 { 180 if ( bError2 ) 181 return sal_False; // equal 182 else 183 return sal_False; // errors are always sorted at the end 184 } 185 else if ( bError2 ) 186 return sal_True; // errors are always sorted at the end 187 else 188 { 189 double fVal1 = ( pAgg1 && pAgg1->HasData() ) ? pAgg1->GetResult() : 0.0; // no data is sorted as 0 190 double fVal2 = ( pAgg2 && pAgg2->HasData() ) ? pAgg2->GetResult() : 0.0; 191 192 // compare values 193 // don't have to check approxEqual, as this is the only sort criterion 194 195 return bAscending ? ( fVal1 < fVal2 ) : ( fVal1 > fVal2 ); 196 } 197 } 198 199 static sal_Bool lcl_IsEqual( const ScDPDataMember* pDataMember1, const ScDPDataMember* pDataMember2, long nMeasure ) 200 { 201 // members can be NULL if used for rows 202 203 ScDPSubTotalState aEmptyState; 204 const ScDPAggData* pAgg1 = pDataMember1 ? pDataMember1->GetConstAggData( nMeasure, aEmptyState ) : NULL; 205 const ScDPAggData* pAgg2 = pDataMember2 ? pDataMember2->GetConstAggData( nMeasure, aEmptyState ) : NULL; 206 207 sal_Bool bError1 = pAgg1 && pAgg1->HasError(); 208 sal_Bool bError2 = pAgg2 && pAgg2->HasError(); 209 if ( bError1 ) 210 { 211 if ( bError2 ) 212 return sal_True; // equal 213 else 214 return sal_False; 215 } 216 else if ( bError2 ) 217 return sal_False; 218 else 219 { 220 double fVal1 = ( pAgg1 && pAgg1->HasData() ) ? pAgg1->GetResult() : 0.0; // no data is sorted as 0 221 double fVal2 = ( pAgg2 && pAgg2->HasData() ) ? pAgg2->GetResult() : 0.0; 222 223 // compare values 224 // this is used to find equal data at the end of the AutoShow range, so approxEqual must be used 225 226 return rtl::math::approxEqual( fVal1, fVal2 ); 227 } 228 } 229 230 sal_Bool ScDPRowMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const 231 { 232 const ScDPResultMember* pMember1 = rDimension.GetMember(nIndex1); 233 const ScDPResultMember* pMember2 = rDimension.GetMember(nIndex2); 234 // Wang Xu Ming -- 3/17/2009 235 236 // make the hide item to the largest order. 237 if ( !pMember1->IsVisible() || !pMember2->IsVisible() ) 238 return pMember1->IsVisible(); 239 const ScDPDataMember* pDataMember1 = pMember1->GetDataRoot() ; 240 const ScDPDataMember* pDataMember2 = pMember2->GetDataRoot(); 241 // End Comments 242 // GetDataRoot can be NULL if there was no data. 243 // IsVisible == sal_False can happen after AutoShow. 244 return lcl_IsLess( pDataMember1, pDataMember2, nMeasure, bAscending ); 245 } 246 247 sal_Bool ScDPColMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const 248 { 249 ScDPDataMember* pDataMember1 = rDimension.GetMember(nIndex1); 250 ScDPDataMember* pDataMember2 = rDimension.GetMember(nIndex2); 251 // Wang Xu Ming -- 2009-6-17 252 sal_Bool bHide1 = pDataMember1 && !pDataMember1->IsVisible(); 253 sal_Bool bHide2 = pDataMember2 && !pDataMember2->IsVisible(); 254 if ( bHide1 || bHide2 ) 255 return !bHide1; 256 // End Comments 257 return lcl_IsLess( pDataMember1, pDataMember2, nMeasure, bAscending ); 258 } 259 260 // ----------------------------------------------------------------------- 261 262 ScDPInitState::ScDPInitState() : 263 nCount( 0 ) 264 { 265 pIndex = new long[SC_DAPI_MAXFIELDS]; 266 pData = new SCROW[SC_DAPI_MAXFIELDS]; 267 } 268 269 ScDPInitState::~ScDPInitState() 270 { 271 delete[] pIndex; 272 delete[] pData; 273 } 274 275 void ScDPInitState::AddMember( long nSourceIndex, SCROW nMember ) 276 { 277 DBG_ASSERT( nCount < SC_DAPI_MAXFIELDS, "too many InitState members" ); 278 if ( nCount < SC_DAPI_MAXFIELDS ) 279 { 280 pIndex[nCount] = nSourceIndex; 281 pData[nCount] = nMember; 282 ++nCount; 283 } 284 } 285 286 void ScDPInitState::RemoveMember() 287 { 288 DBG_ASSERT( nCount > 0, "RemoveColIndex without index" ); 289 if ( nCount > 0 ) 290 --nCount; 291 } 292 293 SCROW ScDPInitState::GetNameIdForIndex( long nIndexValue ) const 294 { 295 for (long i=0; i<nCount; i++) 296 if ( pIndex[i] == nIndexValue ) 297 return pData[i]; 298 299 return -1; // not found 300 } 301 302 // ----------------------------------------------------------------------- 303 304 void lcl_DumpRow( const String& rType, const String& rName, const ScDPAggData* pAggData, 305 ScDocument* pDoc, ScAddress& rPos ) 306 { 307 SCCOL nCol = rPos.Col(); 308 SCROW nRow = rPos.Row(); 309 SCTAB nTab = rPos.Tab(); 310 pDoc->SetString( nCol++, nRow, nTab, rType ); 311 pDoc->SetString( nCol++, nRow, nTab, rName ); 312 while ( pAggData ) 313 { 314 pDoc->SetValue( nCol++, nRow, nTab, pAggData->GetResult() ); 315 pAggData = pAggData->GetExistingChild(); 316 } 317 rPos.SetRow( nRow + 1 ); 318 } 319 320 void lcl_Indent( ScDocument* pDoc, SCROW nStartRow, const ScAddress& rPos ) 321 { 322 SCCOL nCol = rPos.Col(); 323 SCTAB nTab = rPos.Tab(); 324 325 String aString; 326 for (SCROW nRow = nStartRow; nRow < rPos.Row(); nRow++) 327 { 328 pDoc->GetString( nCol, nRow, nTab, aString ); 329 if ( aString.Len() ) 330 { 331 aString.InsertAscii( " ", 0 ); 332 pDoc->SetString( nCol, nRow, nTab, aString ); 333 } 334 } 335 } 336 337 // ----------------------------------------------------------------------- 338 339 ScDPRunningTotalState::ScDPRunningTotalState( ScDPResultMember* pColRoot, ScDPResultMember* pRowRoot ) : 340 pColResRoot( pColRoot ), 341 pRowResRoot( pRowRoot ), 342 nColIndexPos( 0 ), 343 nRowIndexPos( 0 ) 344 { 345 pColVisible = new long[SC_DAPI_MAXFIELDS+1]; 346 pColIndexes = new long[SC_DAPI_MAXFIELDS+1]; 347 pRowVisible = new long[SC_DAPI_MAXFIELDS+1]; 348 pRowIndexes = new long[SC_DAPI_MAXFIELDS+1]; 349 pColIndexes[0] = -1; 350 pRowIndexes[0] = -1; 351 } 352 353 ScDPRunningTotalState::~ScDPRunningTotalState() 354 { 355 delete[] pColVisible; 356 delete[] pColIndexes; 357 delete[] pRowVisible; 358 delete[] pRowIndexes; 359 } 360 361 void ScDPRunningTotalState::AddColIndex( long nVisible, long nSorted ) 362 { 363 DBG_ASSERT( nColIndexPos < SC_DAPI_MAXFIELDS, "too many column indexes" ); 364 if ( nColIndexPos < SC_DAPI_MAXFIELDS ) 365 { 366 pColVisible[nColIndexPos] = nVisible; 367 pColIndexes[nColIndexPos] = nSorted; 368 pColVisible[nColIndexPos+1] = -1; 369 pColIndexes[nColIndexPos+1] = -1; 370 ++nColIndexPos; 371 } 372 } 373 374 void ScDPRunningTotalState::AddRowIndex( long nVisible, long nSorted ) 375 { 376 DBG_ASSERT( nRowIndexPos < SC_DAPI_MAXFIELDS, "too many row indexes" ); 377 if ( nRowIndexPos < SC_DAPI_MAXFIELDS ) 378 { 379 pRowVisible[nRowIndexPos] = nVisible; 380 pRowIndexes[nRowIndexPos] = nSorted; 381 pRowVisible[nRowIndexPos+1] = -1; 382 pRowIndexes[nRowIndexPos+1] = -1; 383 ++nRowIndexPos; 384 } 385 } 386 387 void ScDPRunningTotalState::RemoveColIndex() 388 { 389 DBG_ASSERT( nColIndexPos > 0, "RemoveColIndex without index" ); 390 if ( nColIndexPos > 0 ) 391 { 392 --nColIndexPos; 393 pColVisible[nColIndexPos] = -1; 394 pColIndexes[nColIndexPos] = -1; 395 } 396 } 397 398 void ScDPRunningTotalState::RemoveRowIndex() 399 { 400 DBG_ASSERT( nRowIndexPos > 0, "RemoveRowIndex without index" ); 401 if ( nRowIndexPos > 0 ) 402 { 403 --nRowIndexPos; 404 pRowVisible[nRowIndexPos] = -1; 405 pRowIndexes[nRowIndexPos] = -1; 406 } 407 } 408 409 // ----------------------------------------------------------------------- 410 411 ScDPRelativePos::ScDPRelativePos( long nBase, long nDir ) : 412 nBasePos( nBase ), 413 nDirection( nDir ) 414 { 415 } 416 417 // ----------------------------------------------------------------------- 418 419 void ScDPAggData::Update( const ScDPValueData& rNext, ScSubTotalFunc eFunc, const ScDPSubTotalState& rSubState ) 420 { 421 if (nCount<0) // error? 422 return; // nothing more... 423 424 if ( rNext.nType == SC_VALTYPE_EMPTY ) 425 return; 426 427 if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE && rSubState.eRowForce != SUBTOTAL_FUNC_NONE && 428 rSubState.eColForce != rSubState.eRowForce ) 429 return; 430 if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eColForce; 431 if ( rSubState.eRowForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eRowForce; 432 433 if ( eFunc == SUBTOTAL_FUNC_NONE ) 434 return; 435 436 if ( eFunc != SUBTOTAL_FUNC_CNT2 ) // CNT2 counts everything, incl. strings and errors 437 { 438 if ( rNext.nType == SC_VALTYPE_ERROR ) 439 { 440 nCount = -1; // -1 for error (not for CNT2) 441 return; 442 } 443 if ( rNext.nType == SC_VALTYPE_STRING ) 444 return; // ignore 445 } 446 447 ++nCount; // for all functions 448 449 switch (eFunc) 450 { 451 case SUBTOTAL_FUNC_SUM: 452 case SUBTOTAL_FUNC_AVE: 453 if ( !SubTotal::SafePlus( fVal, rNext.fValue ) ) 454 nCount = -1; // -1 for error 455 break; 456 case SUBTOTAL_FUNC_PROD: 457 if ( nCount == 1 ) // copy first value (fVal is initialized to 0) 458 fVal = rNext.fValue; 459 else if ( !SubTotal::SafeMult( fVal, rNext.fValue ) ) 460 nCount = -1; // -1 for error 461 break; 462 case SUBTOTAL_FUNC_CNT: 463 case SUBTOTAL_FUNC_CNT2: 464 // nothing more than incrementing nCount 465 break; 466 case SUBTOTAL_FUNC_MAX: 467 if ( nCount == 1 || rNext.fValue > fVal ) 468 fVal = rNext.fValue; 469 break; 470 case SUBTOTAL_FUNC_MIN: 471 if ( nCount == 1 || rNext.fValue < fVal ) 472 fVal = rNext.fValue; 473 break; 474 case SUBTOTAL_FUNC_STD: 475 case SUBTOTAL_FUNC_STDP: 476 case SUBTOTAL_FUNC_VAR: 477 case SUBTOTAL_FUNC_VARP: 478 { 479 // fAux is used to sum up squares 480 if ( !SubTotal::SafePlus( fVal, rNext.fValue ) ) 481 nCount = -1; // -1 for error 482 double fAdd = rNext.fValue; 483 if ( !SubTotal::SafeMult( fAdd, rNext.fValue ) || 484 !SubTotal::SafePlus( fAux, fAdd ) ) 485 nCount = -1; // -1 for error 486 } 487 break; 488 default: 489 DBG_ERROR("invalid function"); 490 } 491 } 492 493 void ScDPAggData::Calculate( ScSubTotalFunc eFunc, const ScDPSubTotalState& rSubState ) 494 { 495 // calculate the original result 496 // (without reference value, used as the basis for reference value calculation) 497 498 // called several times at the cross-section of several subtotals - don't calculate twice then 499 if ( IsCalculated() ) 500 return; 501 502 if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eColForce; 503 if ( rSubState.eRowForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eRowForce; 504 505 if ( eFunc == SUBTOTAL_FUNC_NONE ) // this happens when there is no data dimension 506 { 507 nCount = SC_DPAGG_RESULT_EMPTY; // make sure there's a valid state for HasData etc. 508 return; 509 } 510 511 // check the error conditions for the selected function 512 513 sal_Bool bError = sal_False; 514 switch (eFunc) 515 { 516 case SUBTOTAL_FUNC_SUM: 517 case SUBTOTAL_FUNC_PROD: 518 case SUBTOTAL_FUNC_CNT: 519 case SUBTOTAL_FUNC_CNT2: 520 bError = ( nCount < 0 ); // only real errors 521 break; 522 523 case SUBTOTAL_FUNC_AVE: 524 case SUBTOTAL_FUNC_MAX: 525 case SUBTOTAL_FUNC_MIN: 526 case SUBTOTAL_FUNC_STDP: 527 case SUBTOTAL_FUNC_VARP: 528 bError = ( nCount <= 0 ); // no data is an error 529 break; 530 531 case SUBTOTAL_FUNC_STD: 532 case SUBTOTAL_FUNC_VAR: 533 bError = ( nCount < 2 ); // need at least 2 values 534 break; 535 536 default: 537 DBG_ERROR("invalid function"); 538 } 539 540 // calculate the selected function 541 542 double fResult = 0.0; 543 if ( !bError ) 544 { 545 switch (eFunc) 546 { 547 case SUBTOTAL_FUNC_MAX: 548 case SUBTOTAL_FUNC_MIN: 549 case SUBTOTAL_FUNC_SUM: 550 case SUBTOTAL_FUNC_PROD: 551 // different error conditions are handled above 552 fResult = fVal; 553 break; 554 555 case SUBTOTAL_FUNC_CNT: 556 case SUBTOTAL_FUNC_CNT2: 557 fResult = nCount; 558 break; 559 560 case SUBTOTAL_FUNC_AVE: 561 if ( nCount > 0 ) 562 fResult = fVal / (double) nCount; 563 break; 564 565 //! use safe mul for fVal * fVal 566 567 case SUBTOTAL_FUNC_STD: 568 if ( nCount >= 2 ) 569 fResult = sqrt((fAux - fVal*fVal/(double)(nCount)) / (double)(nCount-1)); 570 break; 571 case SUBTOTAL_FUNC_VAR: 572 if ( nCount >= 2 ) 573 fResult = (fAux - fVal*fVal/(double)(nCount)) / (double)(nCount-1); 574 break; 575 case SUBTOTAL_FUNC_STDP: 576 if ( nCount > 0 ) 577 fResult = sqrt((fAux - fVal*fVal/(double)(nCount)) / (double)nCount); 578 break; 579 case SUBTOTAL_FUNC_VARP: 580 if ( nCount > 0 ) 581 fResult = (fAux - fVal*fVal/(double)(nCount)) / (double)nCount; 582 break; 583 default: 584 DBG_ERROR("invalid function"); 585 } 586 } 587 588 sal_Bool bEmpty = ( nCount == 0 ); // no data 589 590 // store the result 591 // Empty is checked first, so empty results are shown empty even for "average" etc. 592 // If these results should be treated as errors in reference value calculations, 593 // a separate state value (EMPTY_ERROR) is needed. 594 // Now, for compatibility, empty "average" results are counted as 0. 595 596 if ( bEmpty ) 597 nCount = SC_DPAGG_RESULT_EMPTY; 598 else if ( bError ) 599 nCount = SC_DPAGG_RESULT_ERROR; 600 else 601 nCount = SC_DPAGG_RESULT_VALID; 602 603 if ( bEmpty || bError ) 604 fResult = 0.0; // default, in case the state is later modified 605 606 // fprintf(stdout, "ScDPAggData::Calculate: result = %g\n", fResult);fflush(stdout); 607 fVal = fResult; // used directly from now on 608 fAux = 0.0; // used for running total or original result of reference value 609 } 610 611 sal_Bool ScDPAggData::IsCalculated() const 612 { 613 return ( nCount <= SC_DPAGG_RESULT_EMPTY ); 614 } 615 616 double ScDPAggData::GetResult() const 617 { 618 DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); 619 620 return fVal; // use calculated value 621 } 622 623 sal_Bool ScDPAggData::HasError() const 624 { 625 DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); 626 627 return ( nCount == SC_DPAGG_RESULT_ERROR ); 628 } 629 630 sal_Bool ScDPAggData::HasData() const 631 { 632 DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); 633 634 return ( nCount != SC_DPAGG_RESULT_EMPTY ); // values or error 635 } 636 637 void ScDPAggData::SetResult( double fNew ) 638 { 639 DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); 640 641 fVal = fNew; // don't reset error flag 642 } 643 644 void ScDPAggData::SetError() 645 { 646 DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); 647 648 nCount = SC_DPAGG_RESULT_ERROR; 649 } 650 651 void ScDPAggData::SetEmpty( sal_Bool bSet ) 652 { 653 DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); 654 655 if ( bSet ) 656 nCount = SC_DPAGG_RESULT_EMPTY; 657 else 658 nCount = SC_DPAGG_RESULT_VALID; 659 } 660 661 double ScDPAggData::GetAuxiliary() const 662 { 663 // after Calculate, fAux is used as auxiliary value for running totals and reference values 664 DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); 665 666 return fAux; 667 } 668 669 void ScDPAggData::SetAuxiliary( double fNew ) 670 { 671 // after Calculate, fAux is used as auxiliary value for running totals and reference values 672 DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); 673 674 fAux = fNew; 675 } 676 677 ScDPAggData* ScDPAggData::GetChild() 678 { 679 if (!pChild) 680 pChild = new ScDPAggData; 681 return pChild; 682 } 683 684 void ScDPAggData::Reset() 685 { 686 fVal = 0.0; 687 fAux = 0.0; 688 nCount = SC_DPAGG_EMPTY; 689 delete pChild; 690 pChild = NULL; 691 } 692 693 // ----------------------------------------------------------------------- 694 695 ScDPRowTotals::ScDPRowTotals() : 696 bIsInColRoot( sal_False ) 697 { 698 } 699 700 ScDPRowTotals::~ScDPRowTotals() 701 { 702 } 703 704 ScDPAggData* lcl_GetChildTotal( ScDPAggData* pFirst, long nMeasure ) 705 { 706 DBG_ASSERT( nMeasure >= 0, "GetColTotal: no measure" ); 707 708 ScDPAggData* pAgg = pFirst; 709 long nSkip = nMeasure; 710 711 // subtotal settings are ignored - colum/row totals exist once per measure 712 713 for ( long nPos=0; nPos<nSkip; nPos++ ) 714 pAgg = pAgg->GetChild(); // column total is constructed empty - children need to be created 715 716 if ( !pAgg->IsCalculated() ) 717 { 718 // for first use, simulate an empty calculation 719 ScDPSubTotalState aEmptyState; 720 pAgg->Calculate( SUBTOTAL_FUNC_SUM, aEmptyState ); 721 } 722 723 return pAgg; 724 } 725 726 ScDPAggData* ScDPRowTotals::GetRowTotal( long nMeasure ) 727 { 728 return lcl_GetChildTotal( &aRowTotal, nMeasure ); 729 } 730 731 ScDPAggData* ScDPRowTotals::GetGrandTotal( long nMeasure ) 732 { 733 return lcl_GetChildTotal( &aGrandTotal, nMeasure ); 734 } 735 736 // ----------------------------------------------------------------------- 737 738 static ScSubTotalFunc lcl_GetForceFunc( const ScDPLevel* pLevel, long nFuncNo ) 739 { 740 ScSubTotalFunc eRet = SUBTOTAL_FUNC_NONE; 741 if ( pLevel ) 742 { 743 //! direct access via ScDPLevel 744 745 uno::Sequence<sheet::GeneralFunction> aSeq = pLevel->getSubTotals(); 746 long nSequence = aSeq.getLength(); 747 if ( nSequence && aSeq[0] != sheet::GeneralFunction_AUTO ) 748 { 749 // For manual subtotals, "automatic" is added as first function. 750 // ScDPResultMember::GetSubTotalCount adds to the count, here NONE has to be 751 // returned as the first function then. 752 753 --nFuncNo; // keep NONE for first (check below), move the other entries 754 } 755 756 if ( nFuncNo >= 0 && nFuncNo < nSequence ) 757 { 758 sheet::GeneralFunction eUser = aSeq.getConstArray()[nFuncNo]; 759 if (eUser != sheet::GeneralFunction_AUTO) 760 eRet = ScDataUnoConversion::GeneralToSubTotal( eUser ); 761 } 762 } 763 return eRet; 764 } 765 766 // ----------------------------------------------------------------------- 767 768 ScDPResultData::ScDPResultData( ScDPSource* pSrc ) : //! Ref 769 pSource( pSrc ), 770 nMeasCount( 0 ), 771 pMeasFuncs( NULL ), 772 pMeasRefs( NULL ), 773 pMeasRefOrient( NULL ), 774 pMeasNames( NULL ), 775 bLateInit( sal_False ), 776 bDataAtCol( sal_False ), 777 bDataAtRow( sal_False ) 778 { 779 780 lcl_ResizePointVector( mpDimMembers , SC_DAPI_MAXFIELDS ); 781 } 782 783 ScDPResultData::~ScDPResultData() 784 { 785 delete[] pMeasFuncs; 786 delete[] pMeasRefs; 787 delete[] pMeasRefOrient; 788 delete[] pMeasNames; 789 790 lcl_ResizePointVector( mpDimMembers , 0 ); 791 } 792 793 void ScDPResultData::SetMeasureData( long nCount, const ScSubTotalFunc* pFunctions, 794 const sheet::DataPilotFieldReference* pRefs, const sal_uInt16* pRefOrient, 795 const String* pNames ) 796 { 797 delete[] pMeasFuncs; 798 delete[] pMeasRefs; 799 delete[] pMeasRefOrient; 800 delete[] pMeasNames; 801 if ( nCount ) 802 { 803 nMeasCount = nCount; 804 pMeasFuncs = new ScSubTotalFunc[nCount]; 805 pMeasRefs = new sheet::DataPilotFieldReference[nCount]; 806 pMeasRefOrient = new sal_uInt16[nCount]; 807 pMeasNames = new String[nCount]; 808 for (long i=0; i<nCount; i++) 809 { 810 pMeasFuncs[i] = pFunctions[i]; 811 pMeasRefs[i] = pRefs[i]; 812 pMeasRefOrient[i] = pRefOrient[i]; 813 pMeasNames[i] = pNames[i]; 814 } 815 } 816 else 817 { 818 // use one dummy measure 819 nMeasCount = 1; 820 pMeasFuncs = new ScSubTotalFunc[1]; 821 pMeasFuncs[0] = SUBTOTAL_FUNC_NONE; 822 pMeasRefs = new sheet::DataPilotFieldReference[1]; // default ctor is ok 823 pMeasRefOrient = new sal_uInt16[1]; 824 pMeasRefOrient[0] = sheet::DataPilotFieldOrientation_HIDDEN; 825 pMeasNames = new String[1]; 826 pMeasNames[0] = ScGlobal::GetRscString( STR_EMPTYDATA ); 827 } 828 } 829 830 void ScDPResultData::SetDataLayoutOrientation( sal_uInt16 nOrient ) 831 { 832 bDataAtCol = ( nOrient == sheet::DataPilotFieldOrientation_COLUMN ); 833 bDataAtRow = ( nOrient == sheet::DataPilotFieldOrientation_ROW ); 834 } 835 836 void ScDPResultData::SetLateInit( sal_Bool bSet ) 837 { 838 bLateInit = bSet; 839 } 840 841 long ScDPResultData::GetColStartMeasure() const 842 { 843 if ( nMeasCount == 1 ) return 0; 844 return bDataAtCol ? SC_DPMEASURE_ALL : SC_DPMEASURE_ANY; 845 } 846 847 long ScDPResultData::GetRowStartMeasure() const 848 { 849 if ( nMeasCount == 1 ) return 0; 850 return bDataAtRow ? SC_DPMEASURE_ALL : SC_DPMEASURE_ANY; 851 } 852 853 ScSubTotalFunc ScDPResultData::GetMeasureFunction(long nMeasure) const 854 { 855 DBG_ASSERT( pMeasFuncs && nMeasure < nMeasCount, "bumm" ); 856 return pMeasFuncs[nMeasure]; 857 } 858 859 const sheet::DataPilotFieldReference& ScDPResultData::GetMeasureRefVal(long nMeasure) const 860 { 861 DBG_ASSERT( pMeasRefs && nMeasure < nMeasCount, "bumm" ); 862 return pMeasRefs[nMeasure]; 863 } 864 865 sal_uInt16 ScDPResultData::GetMeasureRefOrient(long nMeasure) const 866 { 867 DBG_ASSERT( pMeasRefOrient && nMeasure < nMeasCount, "bumm" ); 868 return pMeasRefOrient[nMeasure]; 869 } 870 871 String ScDPResultData::GetMeasureString(long nMeasure, sal_Bool bForce, ScSubTotalFunc eForceFunc, bool& rbTotalResult) const 872 { 873 // with bForce==sal_True, return function instead of "result" for single measure 874 // with eForceFunc != SUBTOTAL_FUNC_NONE, always use eForceFunc 875 rbTotalResult = false; 876 if ( nMeasure < 0 || ( nMeasCount == 1 && !bForce && eForceFunc == SUBTOTAL_FUNC_NONE ) ) 877 { 878 // for user-specified subtotal function with all measures, 879 // display only function name 880 if ( eForceFunc != SUBTOTAL_FUNC_NONE ) 881 return ScGlobal::GetRscString(nFuncStrIds[eForceFunc]); 882 883 rbTotalResult = true; 884 return ScGlobal::GetRscString(STR_TABLE_ERGEBNIS); 885 } 886 else 887 { 888 DBG_ASSERT( pMeasNames && nMeasure < nMeasCount, "bumm" ); 889 ScDPDimension* pDataDim = pSource->GetDataDimension(nMeasure); 890 if (pDataDim) 891 { 892 const OUString* pLayoutName = pDataDim->GetLayoutName(); 893 if (pLayoutName) 894 return *pLayoutName; 895 } 896 String aRet; 897 ScSubTotalFunc eFunc = ( eForceFunc == SUBTOTAL_FUNC_NONE ) ? 898 GetMeasureFunction(nMeasure) : eForceFunc; 899 sal_uInt16 nId = nFuncStrIds[eFunc]; 900 if (nId) 901 { 902 aRet += ScGlobal::GetRscString(nId); // function name 903 aRet.AppendAscii(RTL_CONSTASCII_STRINGPARAM( " - " )); 904 } 905 aRet += pMeasNames[nMeasure]; // field name 906 907 return aRet; 908 } 909 } 910 911 String ScDPResultData::GetMeasureDimensionName(long nMeasure) const 912 { 913 if ( nMeasure < 0 ) 914 { 915 DBG_ERROR("GetMeasureDimensionName: negative"); 916 return String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("***")); 917 } 918 919 return pSource->GetDataDimName( nMeasure ); 920 } 921 922 sal_Bool ScDPResultData::IsBaseForGroup( long nDim ) const 923 { 924 return pSource->GetData()->IsBaseForGroup( nDim ); 925 } 926 927 long ScDPResultData::GetGroupBase( long nGroupDim ) const 928 { 929 return pSource->GetData()->GetGroupBase( nGroupDim ); 930 } 931 932 sal_Bool ScDPResultData::IsNumOrDateGroup( long nDim ) const 933 { 934 return pSource->GetData()->IsNumOrDateGroup( nDim ); 935 } 936 937 sal_Bool ScDPResultData::IsInGroup( const ScDPItemData& rGroupData, long nGroupIndex, 938 long nBaseDataId, long nBaseIndex ) const 939 { 940 const ScDPItemData* pData = pSource->GetItemDataById( nGroupIndex , nBaseDataId); 941 if ( pData ) 942 return pSource->GetData()->IsInGroup( rGroupData, nGroupIndex, *pData , nBaseIndex ); 943 else 944 return sal_False; 945 } 946 sal_Bool ScDPResultData::IsInGroup( SCROW nGroupDataId, long nGroupIndex, 947 const ScDPItemData& rBaseData, long nBaseIndex ) const 948 { 949 const ScDPItemData* pGroupData = pSource->GetItemDataById( nGroupIndex , nGroupDataId); 950 if ( pGroupData ) 951 return pSource->GetData()->IsInGroup( *pGroupData, nGroupIndex, rBaseData , nBaseIndex ); 952 else 953 return sal_False; 954 } 955 956 sal_Bool ScDPResultData::HasCommonElement(/* const ScDPItemData& rFirstData*/SCROW nFirstDataId, long nFirstIndex, 957 const ScDPItemData& rSecondData, long nSecondIndex ) const 958 { 959 const ScDPItemData* pFirstData = pSource->GetItemDataById( nFirstIndex , nFirstDataId); 960 if ( pFirstData ) 961 return pSource->GetData()->HasCommonElement( *pFirstData, nFirstIndex, rSecondData, nSecondIndex ); 962 else 963 return sal_False; 964 } 965 966 const ScDPSource* ScDPResultData::GetSource() const 967 { 968 return pSource; 969 } 970 971 ResultMembers* ScDPResultData::GetDimResultMembers( long nDim , ScDPDimension* pDim, ScDPLevel* pLevel) const 972 { 973 if ( mpDimMembers[ nDim ] == NULL ) 974 { 975 976 //long nDimSource = pDim->GetDimension(); 977 978 ResultMembers* pResultMembers = new ResultMembers(); 979 // global order is used to initialize aMembers, so it doesn't have to be looked at later 980 const ScMemberSortOrder& rGlobalOrder = pLevel->GetGlobalOrder(); 981 982 ScDPMembers* pMembers = pLevel->GetMembersObject(); 983 long nMembCount = pMembers->getCount(); 984 for ( long i=0; i<nMembCount; i++ ) 985 { 986 long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i]; 987 ScDPMember* pMember = pMembers->getByIndex(nSorted); 988 if ( NULL == pResultMembers->FindMember( pMember->GetItemDataId() ) ) 989 { 990 ScDPParentDimData* pNew = new ScDPParentDimData( i, pDim, pLevel, pMember ); 991 pResultMembers->InsertMember( pNew ); 992 } 993 } 994 995 mpDimMembers[ nDim ] = pResultMembers; 996 } 997 return mpDimMembers[ nDim ]; 998 999 } 1000 1001 // ----------------------------------------------------------------------- 1002 1003 1004 ScDPResultMember::ScDPResultMember( const ScDPResultData* pData, const ScDPParentDimData& rParentDimData , 1005 sal_Bool bForceSub ) : 1006 pResultData( pData ), 1007 aParentDimData( rParentDimData ), 1008 pChildDimension( NULL ), 1009 pDataRoot( NULL ), 1010 bHasElements( sal_False ), 1011 bForceSubTotal( bForceSub ), 1012 bHasHiddenDetails( sal_False ), 1013 bInitialized( sal_False ), 1014 bAutoHidden( sal_False ), 1015 nMemberStep( 1 ) 1016 { 1017 // pParentLevel/pMemberDesc is 0 for root members 1018 } 1019 1020 ScDPResultMember::ScDPResultMember( const ScDPResultData* pData, 1021 sal_Bool bForceSub ) : 1022 pResultData( pData ), 1023 pChildDimension( NULL ), 1024 pDataRoot( NULL ), 1025 bHasElements( sal_False ), 1026 bForceSubTotal( bForceSub ), 1027 bHasHiddenDetails( sal_False ), 1028 bInitialized( sal_False ), 1029 bAutoHidden( sal_False ), 1030 nMemberStep( 1 ) 1031 { 1032 } 1033 ScDPResultMember::~ScDPResultMember() 1034 { 1035 delete pChildDimension; 1036 delete pDataRoot; 1037 } 1038 1039 String ScDPResultMember::GetName() const 1040 { 1041 const ScDPMember* pMemberDesc = GetDPMember(); 1042 if (pMemberDesc) 1043 return pMemberDesc->GetNameStr(); 1044 else 1045 return ScGlobal::GetRscString(STR_PIVOT_TOTAL); // root member 1046 } 1047 1048 void ScDPResultMember::FillItemData( ScDPItemData& rData ) const 1049 { 1050 const ScDPMember* pMemberDesc = GetDPMember(); 1051 if (pMemberDesc) 1052 pMemberDesc->FillItemData( rData ); 1053 else 1054 rData.SetString( ScGlobal::GetRscString(STR_PIVOT_TOTAL) ); // root member 1055 } 1056 1057 sal_Bool ScDPResultMember::IsNamedItem( SCROW nIndex ) const 1058 { 1059 //! store ScDPMember pointer instead of ScDPMember ??? 1060 const ScDPMember* pMemberDesc = GetDPMember(); 1061 if (pMemberDesc) 1062 return ((ScDPMember*)pMemberDesc)->IsNamedItem( nIndex ); 1063 return sal_False; 1064 } 1065 1066 bool ScDPResultMember::IsValidEntry( const vector< SCROW >& aMembers ) const 1067 { 1068 if ( !IsValid() ) 1069 return false; 1070 1071 const ScDPResultDimension* pChildDim = GetChildDimension(); 1072 if (pChildDim) 1073 { 1074 if (aMembers.size() < 2) 1075 return false; 1076 1077 vector<SCROW>::const_iterator itr = aMembers.begin(); 1078 vector<SCROW> aChildMembers(++itr, aMembers.end()); 1079 return pChildDim->IsValidEntry(aChildMembers); 1080 } 1081 else 1082 return true; 1083 } 1084 1085 void ScDPResultMember::InitFrom( const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev, 1086 size_t nPos, ScDPInitState& rInitState , 1087 sal_Bool bInitChild /*= sal_True */) 1088 { 1089 // with LateInit, initialize only those members that have data 1090 if ( pResultData->IsLateInit() ) 1091 return; 1092 1093 bInitialized = sal_True; 1094 1095 if (nPos >= ppDim.size()) 1096 return; 1097 1098 // skip child dimension if details are not shown 1099 if ( GetDPMember() && !GetDPMember()->getShowDetails() ) 1100 { 1101 // Wang Xu Ming -- 2009-6-16 1102 // Show DataLayout dimention 1103 nMemberStep = 1; 1104 while ( nPos < ppDim.size() ) 1105 { 1106 if ( ppDim[nPos] ->getIsDataLayoutDimension() ) 1107 { 1108 if ( !pChildDimension ) 1109 pChildDimension = new ScDPResultDimension( pResultData ); 1110 pChildDimension->InitFrom( ppDim, ppLev, nPos, rInitState , sal_False ); 1111 return; 1112 } 1113 else 1114 { //find next dim 1115 nPos ++; 1116 nMemberStep ++; 1117 } 1118 } 1119 // End Comments 1120 bHasHiddenDetails = sal_True; // only if there is a next dimension 1121 return; 1122 } 1123 1124 if ( bInitChild ) 1125 { 1126 pChildDimension = new ScDPResultDimension( pResultData ); 1127 pChildDimension->InitFrom( ppDim, ppLev, nPos, rInitState, sal_True ); 1128 } 1129 } 1130 1131 void ScDPResultMember::LateInitFrom( LateInitParams& rParams/*const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev*/, 1132 const vector< SCROW >& pItemData, size_t nPos, 1133 ScDPInitState& rInitState ) 1134 { 1135 // without LateInit, everything has already been initialized 1136 if ( !pResultData->IsLateInit() ) 1137 return; 1138 1139 bInitialized = sal_True; 1140 1141 if ( rParams.IsEnd( nPos ) /*nPos >= ppDim.size()*/) 1142 // No next dimension. Bail out. 1143 return; 1144 1145 // skip child dimension if details are not shown 1146 if ( GetDPMember() && !GetDPMember()->getShowDetails() ) 1147 { 1148 // Wang Xu Ming -- 2009-6-16 1149 // DataPilot Migration 1150 // Show DataLayout dimention 1151 nMemberStep = 1; 1152 while ( !rParams.IsEnd( nPos ) ) 1153 { 1154 if ( rParams.GetDim( nPos ) ->getIsDataLayoutDimension() ) 1155 { 1156 if ( !pChildDimension ) 1157 pChildDimension = new ScDPResultDimension( pResultData ); 1158 1159 // #i111462# reset InitChild flag only for this child dimension's LateInitFrom call, 1160 // not for following members of parent dimensions 1161 sal_Bool bWasInitChild = rParams.GetInitChild(); 1162 rParams.SetInitChild( sal_False ); 1163 pChildDimension->LateInitFrom( rParams, pItemData, nPos, rInitState ); 1164 rParams.SetInitChild( bWasInitChild ); 1165 return; 1166 } 1167 else 1168 { //find next dim 1169 nPos ++; 1170 nMemberStep ++; 1171 } 1172 } 1173 // End Comments 1174 bHasHiddenDetails = sal_True; // only if there is a next dimension 1175 return; 1176 } 1177 1178 // LateInitFrom is called several times... 1179 if ( rParams.GetInitChild() ) 1180 { 1181 if ( !pChildDimension ) 1182 pChildDimension = new ScDPResultDimension( pResultData ); 1183 pChildDimension->LateInitFrom( rParams, pItemData, nPos, rInitState ); 1184 } 1185 } 1186 1187 sal_Bool ScDPResultMember::IsSubTotalInTitle(long nMeasure) const 1188 { 1189 sal_Bool bRet = sal_False; 1190 if ( pChildDimension && /*pParentLevel*/GetParentLevel() && 1191 /*pParentLevel*/GetParentLevel()->IsOutlineLayout() && /*pParentLevel*/GetParentLevel()->IsSubtotalsAtTop() ) 1192 { 1193 long nUserSubStart; 1194 long nSubTotals = GetSubTotalCount( &nUserSubStart ); 1195 nSubTotals -= nUserSubStart; // visible count 1196 if ( nSubTotals ) 1197 { 1198 if ( nMeasure == SC_DPMEASURE_ALL ) 1199 nSubTotals *= pResultData->GetMeasureCount(); // number of subtotals that will be inserted 1200 1201 // only a single subtotal row will be shown in the outline title row 1202 if ( nSubTotals == 1 ) 1203 bRet = sal_True; 1204 } 1205 } 1206 return bRet; 1207 } 1208 1209 long ScDPResultMember::GetSize(long nMeasure) const 1210 { 1211 if ( !IsVisible() ) 1212 return 0; 1213 const ScDPLevel* pParentLevel = GetParentLevel(); 1214 long nExtraSpace = 0; 1215 if ( pParentLevel && pParentLevel->IsAddEmpty() ) 1216 ++nExtraSpace; 1217 1218 if ( pChildDimension ) 1219 { 1220 // outline layout takes up an extra row for the title only if subtotals aren't shown in that row 1221 if ( pParentLevel && pParentLevel->IsOutlineLayout() && !IsSubTotalInTitle( nMeasure ) ) 1222 ++nExtraSpace; 1223 1224 long nSize = pChildDimension->GetSize(nMeasure); 1225 long nUserSubStart; 1226 long nUserSubCount = GetSubTotalCount( &nUserSubStart ); 1227 nUserSubCount -= nUserSubStart; // for output size, use visible count 1228 if ( nUserSubCount ) 1229 { 1230 if ( nMeasure == SC_DPMEASURE_ALL ) 1231 nSize += pResultData->GetMeasureCount() * nUserSubCount; 1232 else 1233 nSize += nUserSubCount; 1234 } 1235 return nSize + nExtraSpace; 1236 } 1237 else 1238 { 1239 if ( nMeasure == SC_DPMEASURE_ALL ) 1240 return pResultData->GetMeasureCount() + nExtraSpace; 1241 else 1242 return 1 + nExtraSpace; 1243 } 1244 } 1245 1246 sal_Bool ScDPResultMember::IsVisible() const 1247 { 1248 // not initialized -> shouldn't be there at all 1249 // (allocated only to preserve ordering) 1250 const ScDPLevel* pParentLevel = GetParentLevel(); 1251 return ( bHasElements || ( pParentLevel && pParentLevel->getShowEmpty() ) ) && IsValid() && bInitialized; 1252 } 1253 1254 sal_Bool ScDPResultMember::IsValid() const 1255 { 1256 // non-Valid members are left out of calculation 1257 1258 // was member set no invisible at the DataPilotSource? 1259 const ScDPMember* pMemberDesc =GetDPMember(); 1260 if ( pMemberDesc && !pMemberDesc->getIsVisible() ) 1261 return sal_False; 1262 1263 if ( bAutoHidden ) 1264 return sal_False; 1265 1266 return sal_True; 1267 } 1268 1269 sal_Bool ScDPResultMember::HasHiddenDetails() const 1270 { 1271 // bHasHiddenDetails is set only if the "show details" flag is off, 1272 // and there was a child dimension to skip 1273 1274 return bHasHiddenDetails; 1275 } 1276 1277 long ScDPResultMember::GetSubTotalCount( long* pUserSubStart ) const 1278 { 1279 if ( pUserSubStart ) 1280 *pUserSubStart = 0; // default 1281 1282 const ScDPLevel* pParentLevel = GetParentLevel(); 1283 1284 if ( bForceSubTotal ) // set if needed for root members 1285 return 1; // grand total is always "automatic" 1286 else if ( pParentLevel ) 1287 { 1288 //! direct access via ScDPLevel 1289 1290 uno::Sequence<sheet::GeneralFunction> aSeq = pParentLevel->getSubTotals(); 1291 long nSequence = aSeq.getLength(); 1292 if ( nSequence && aSeq[0] != sheet::GeneralFunction_AUTO ) 1293 { 1294 // For manual subtotals, always add "automatic" as first function 1295 // (used for calculation, but not for display, needed for sorting, see lcl_GetForceFunc) 1296 1297 ++nSequence; 1298 if ( pUserSubStart ) 1299 *pUserSubStart = 1; // visible subtotals start at 1 1300 } 1301 return nSequence; 1302 } 1303 else 1304 return 0; 1305 } 1306 1307 void ScDPResultMember::ProcessData( const vector< SCROW >& aChildMembers, const ScDPResultDimension* pDataDim, 1308 const vector< SCROW >& aDataMembers, const vector<ScDPValueData>& aValues ) 1309 { 1310 SetHasElements(); 1311 1312 if (pChildDimension) 1313 pChildDimension->ProcessData( aChildMembers, pDataDim, aDataMembers, aValues ); 1314 1315 if ( !pDataRoot ) 1316 { 1317 pDataRoot = new ScDPDataMember( pResultData, NULL ); 1318 if ( pDataDim ) 1319 pDataRoot->InitFrom( pDataDim ); // recursive 1320 } 1321 1322 ScDPSubTotalState aSubState; // initial state 1323 1324 long nUserSubCount = GetSubTotalCount(); 1325 1326 // Calculate at least automatic if no subtotals are selected, 1327 // show only own values if there's no child dimension (innermost). 1328 if ( !nUserSubCount || !pChildDimension ) 1329 nUserSubCount = 1; 1330 1331 const ScDPLevel* pParentLevel = GetParentLevel(); 1332 1333 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic" 1334 { 1335 // #i68338# if nUserSubCount is 1 (automatic only), don't set nRowSubTotalFunc 1336 if ( pChildDimension && nUserSubCount > 1 ) 1337 { 1338 aSubState.nRowSubTotalFunc = nUserPos; 1339 aSubState.eRowForce = lcl_GetForceFunc( pParentLevel, nUserPos ); 1340 } 1341 1342 pDataRoot->ProcessData( aDataMembers, aValues, aSubState ); 1343 } 1344 } 1345 1346 /** 1347 * Parse subtotal string and replace all occurrences of '?' with the caption 1348 * string. Do ensure that escaped characters are not translated. 1349 */ 1350 static String lcl_parseSubtotalName(const String& rSubStr, const String& rCaption) 1351 { 1352 String aNewStr; 1353 xub_StrLen n = rSubStr.Len(); 1354 bool bEscaped = false; 1355 for (xub_StrLen i = 0; i < n; ++i) 1356 { 1357 sal_Unicode c = rSubStr.GetChar(i); 1358 if (!bEscaped && c == sal_Unicode('\\')) 1359 { 1360 bEscaped = true; 1361 continue; 1362 } 1363 1364 if (!bEscaped && c == sal_Unicode('?')) 1365 aNewStr.Append(rCaption); 1366 else 1367 aNewStr.Append(c); 1368 bEscaped = false; 1369 } 1370 return aNewStr; 1371 } 1372 1373 void ScDPResultMember::FillMemberResults( uno::Sequence<sheet::MemberResult>* pSequences, 1374 long& rPos, long nMeasure, sal_Bool bRoot, 1375 const String* pMemberName, 1376 const String* pMemberCaption ) 1377 { 1378 // IsVisible() test is in ScDPResultDimension::FillMemberResults 1379 // (not on data layout dimension) 1380 1381 long nSize = GetSize(nMeasure); 1382 sheet::MemberResult* pArray = pSequences->getArray(); 1383 DBG_ASSERT( rPos+nSize <= pSequences->getLength(), "bumm" ); 1384 1385 sal_Bool bIsNumeric = sal_False; 1386 String aName; 1387 if ( pMemberName ) // if pMemberName != NULL, use instead of real member name 1388 aName = *pMemberName; 1389 else 1390 { 1391 ScDPItemData aItemData; 1392 FillItemData( aItemData ); 1393 aName = aItemData.GetString(); 1394 bIsNumeric = aItemData.IsValue(); 1395 } 1396 const ScDPDimension* pParentDim = GetParentDim(); 1397 if ( bIsNumeric && pParentDim && pResultData->IsNumOrDateGroup( pParentDim->GetDimension() ) ) 1398 { 1399 // Numeric group dimensions use numeric entries for proper sorting, 1400 // but the group titles must be output as text. 1401 bIsNumeric = sal_False; 1402 } 1403 1404 String aCaption = aName; 1405 const ScDPMember* pMemberDesc = GetDPMember(); 1406 if (pMemberDesc) 1407 { 1408 const OUString* pLayoutName = pMemberDesc->GetLayoutName(); 1409 if (pLayoutName) 1410 { 1411 aCaption = *pLayoutName; 1412 bIsNumeric = false; // layout name is always non-numeric. 1413 } 1414 } 1415 1416 if ( pMemberCaption ) // use pMemberCaption if != NULL 1417 aCaption = *pMemberCaption; 1418 if (!aCaption.Len()) 1419 aCaption = ScGlobal::GetRscString(STR_EMPTYDATA); 1420 1421 if (bIsNumeric) 1422 pArray[rPos].Flags |= sheet::MemberResultFlags::NUMERIC; 1423 else 1424 pArray[rPos].Flags &= ~sheet::MemberResultFlags::NUMERIC; 1425 1426 if ( nSize && !bRoot ) // root is overwritten by first dimension 1427 { 1428 pArray[rPos].Name = rtl::OUString(aName); 1429 pArray[rPos].Caption = rtl::OUString(aCaption); 1430 pArray[rPos].Flags |= sheet::MemberResultFlags::HASMEMBER; 1431 1432 // set "continue" flag (removed for subtotals later) 1433 for (long i=1; i<nSize; i++) 1434 pArray[rPos+i].Flags |= sheet::MemberResultFlags::CONTINUE; 1435 } 1436 1437 const ScDPLevel* pParentLevel = GetParentLevel(); 1438 long nExtraSpace = 0; 1439 if ( pParentLevel && pParentLevel->IsAddEmpty() ) 1440 ++nExtraSpace; 1441 1442 sal_Bool bTitleLine = sal_False; 1443 if ( pParentLevel && pParentLevel->IsOutlineLayout() ) 1444 bTitleLine = sal_True; 1445 1446 // if the subtotals are shown at the top (title row) in outline layout, 1447 // no extra row for the subtotals is needed 1448 sal_Bool bSubTotalInTitle = IsSubTotalInTitle( nMeasure ); 1449 1450 sal_Bool bHasChild = ( pChildDimension != NULL ); 1451 if (bHasChild) 1452 { 1453 if ( bTitleLine ) // in tabular layout the title is on a separate row 1454 ++rPos; // -> fill child dimension one row below 1455 1456 if (bRoot) // same sequence for root member 1457 pChildDimension->FillMemberResults( pSequences, rPos, nMeasure ); 1458 else 1459 //pChildDimension->FillMemberResults( pSequences + 1, rPos, nMeasure ); 1460 pChildDimension->FillMemberResults( pSequences + nMemberStep/*1*/, rPos, nMeasure ); 1461 1462 if ( bTitleLine ) // title row is included in GetSize, so the following 1463 --rPos; // positions are calculated with the normal values 1464 } 1465 1466 rPos += nSize; 1467 1468 long nUserSubStart; 1469 long nUserSubCount = GetSubTotalCount(&nUserSubStart); 1470 if ( nUserSubCount && pChildDimension && !bSubTotalInTitle ) 1471 { 1472 long nMemberMeasure = nMeasure; 1473 long nSubSize = pResultData->GetCountForMeasure(nMeasure); 1474 1475 rPos -= nSubSize * (nUserSubCount - nUserSubStart); // GetSize includes space for SubTotal 1476 rPos -= nExtraSpace; // GetSize includes the empty line 1477 1478 for (long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++) 1479 { 1480 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ ) 1481 { 1482 if ( nMeasure == SC_DPMEASURE_ALL ) 1483 nMemberMeasure = nSubCount; 1484 1485 ScSubTotalFunc eForce = SUBTOTAL_FUNC_NONE; 1486 if (bHasChild) 1487 eForce = lcl_GetForceFunc( pParentLevel, nUserPos ); 1488 1489 bool bTotalResult = false; 1490 String aSubStr = aCaption; 1491 aSubStr += ' '; 1492 aSubStr += pResultData->GetMeasureString(nMemberMeasure, sal_False, eForce, bTotalResult); 1493 1494 if (bTotalResult) 1495 { 1496 if (pMemberDesc) 1497 { 1498 // single data field layout. 1499 const OUString* pSubtotalName = pParentDim->GetSubtotalName(); 1500 if (pSubtotalName) 1501 aSubStr = lcl_parseSubtotalName(*pSubtotalName, aCaption); 1502 pArray[rPos].Flags &= ~sheet::MemberResultFlags::GRANDTOTAL; 1503 } 1504 else 1505 { 1506 // root member - subtotal (grand total?) for multi-data field layout. 1507 const rtl::OUString* pGrandTotalName = pResultData->GetSource()->GetGrandTotalName(); 1508 if (pGrandTotalName) 1509 aSubStr = *pGrandTotalName; 1510 pArray[rPos].Flags |= sheet::MemberResultFlags::GRANDTOTAL; 1511 } 1512 } 1513 1514 pArray[rPos].Name = rtl::OUString(aName); 1515 pArray[rPos].Caption = rtl::OUString(aSubStr); 1516 pArray[rPos].Flags = ( pArray[rPos].Flags | 1517 ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL) ) & 1518 ~sheet::MemberResultFlags::CONTINUE; 1519 1520 if ( nMeasure == SC_DPMEASURE_ALL ) 1521 { 1522 // data layout dimension is (direct/indirect) child of this. 1523 // data layout dimension must have name for all entries. 1524 1525 uno::Sequence<sheet::MemberResult>* pLayoutSeq = pSequences; 1526 if (!bRoot) 1527 ++pLayoutSeq; 1528 ScDPResultDimension* pLayoutDim = pChildDimension; 1529 while ( pLayoutDim && !pLayoutDim->IsDataLayout() ) 1530 { 1531 pLayoutDim = pLayoutDim->GetFirstChildDimension(); 1532 ++pLayoutSeq; 1533 } 1534 if ( pLayoutDim ) 1535 { 1536 sheet::MemberResult* pLayoutArray = pLayoutSeq->getArray(); 1537 String aDataName = pResultData->GetMeasureDimensionName(nMemberMeasure); 1538 pLayoutArray[rPos].Name = rtl::OUString(aDataName); 1539 } 1540 } 1541 1542 rPos += 1; 1543 } 1544 } 1545 1546 rPos += nExtraSpace; // add again (subtracted above) 1547 } 1548 } 1549 1550 void ScDPResultMember::FillDataResults( const ScDPResultMember* pRefMember, 1551 uno::Sequence< uno::Sequence<sheet::DataResult> >& rSequence, 1552 long& rRow, long nMeasure ) const 1553 { 1554 // IsVisible() test is in ScDPResultDimension::FillDataResults 1555 // (not on data layout dimension) 1556 const ScDPLevel* pParentLevel = GetParentLevel(); 1557 long nStartRow = rRow; 1558 1559 long nExtraSpace = 0; 1560 if ( pParentLevel && pParentLevel->IsAddEmpty() ) 1561 ++nExtraSpace; 1562 1563 sal_Bool bTitleLine = sal_False; 1564 if ( pParentLevel && pParentLevel->IsOutlineLayout() ) 1565 bTitleLine = sal_True; 1566 1567 sal_Bool bSubTotalInTitle = IsSubTotalInTitle( nMeasure ); 1568 1569 sal_Bool bHasChild = ( pChildDimension != NULL ); 1570 if (bHasChild) 1571 { 1572 if ( bTitleLine ) // in tabular layout the title is on a separate row 1573 ++rRow; // -> fill child dimension one row below 1574 1575 pChildDimension->FillDataResults( pRefMember, rSequence, rRow, nMeasure ); // doesn't modify rRow 1576 rRow += GetSize( nMeasure ); 1577 1578 if ( bTitleLine ) // title row is included in GetSize, so the following 1579 --rRow; // positions are calculated with the normal values 1580 } 1581 1582 long nUserSubStart; 1583 long nUserSubCount = GetSubTotalCount(&nUserSubStart); 1584 if ( nUserSubCount || !bHasChild ) 1585 { 1586 // Calculate at least automatic if no subtotals are selected, 1587 // show only own values if there's no child dimension (innermost). 1588 if ( !nUserSubCount || !bHasChild ) 1589 { 1590 nUserSubCount = 1; 1591 nUserSubStart = 0; 1592 } 1593 1594 long nMemberMeasure = nMeasure; 1595 long nSubSize = pResultData->GetCountForMeasure(nMeasure); 1596 if (bHasChild) 1597 { 1598 rRow -= nSubSize * ( nUserSubCount - nUserSubStart ); // GetSize includes space for SubTotal 1599 rRow -= nExtraSpace; // GetSize includes the empty line 1600 } 1601 1602 long nMoveSubTotal = 0; 1603 if ( bSubTotalInTitle ) 1604 { 1605 nMoveSubTotal = rRow - nStartRow; // force to first (title) row 1606 rRow = nStartRow; 1607 } 1608 1609 if ( pDataRoot ) 1610 { 1611 ScDPSubTotalState aSubState; // initial state 1612 1613 for (long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++) 1614 { 1615 if ( bHasChild && nUserSubCount > 1 ) 1616 { 1617 aSubState.nRowSubTotalFunc = nUserPos; 1618 aSubState.eRowForce = lcl_GetForceFunc( /*pParentLevel*/GetParentLevel() , nUserPos ); 1619 } 1620 1621 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ ) 1622 { 1623 if ( nMeasure == SC_DPMEASURE_ALL ) 1624 nMemberMeasure = nSubCount; 1625 else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL ) 1626 nMemberMeasure = SC_DPMEASURE_ALL; 1627 1628 DBG_ASSERT( rRow < rSequence.getLength(), "bumm" ); 1629 uno::Sequence<sheet::DataResult>& rSubSeq = rSequence.getArray()[rRow]; 1630 long nSeqCol = 0; 1631 pDataRoot->FillDataRow( pRefMember, rSubSeq, nSeqCol, nMemberMeasure, bHasChild, aSubState ); 1632 1633 rRow += 1; 1634 } 1635 } 1636 } 1637 else 1638 rRow += nSubSize * ( nUserSubCount - nUserSubStart ); // empty rows occur when ShowEmpty is true 1639 1640 // add extra space again if subtracted from GetSize above, 1641 // add to own size if no children 1642 rRow += nExtraSpace; 1643 1644 rRow += nMoveSubTotal; 1645 } 1646 } 1647 1648 void ScDPResultMember::UpdateDataResults( const ScDPResultMember* pRefMember, long nMeasure ) const 1649 { 1650 // IsVisible() test is in ScDPResultDimension::FillDataResults 1651 // (not on data layout dimension) 1652 1653 sal_Bool bHasChild = ( pChildDimension != NULL ); 1654 1655 long nUserSubCount = GetSubTotalCount(); 1656 // process subtotals even if not shown 1657 // if ( nUserSubCount || !bHasChild ) 1658 { 1659 // Calculate at least automatic if no subtotals are selected, 1660 // show only own values if there's no child dimension (innermost). 1661 if ( !nUserSubCount || !bHasChild ) 1662 nUserSubCount = 1; 1663 1664 long nMemberMeasure = nMeasure; 1665 long nSubSize = pResultData->GetCountForMeasure(nMeasure); 1666 1667 if ( pDataRoot ) 1668 { 1669 ScDPSubTotalState aSubState; // initial state 1670 1671 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic" 1672 { 1673 if ( bHasChild && nUserSubCount > 1 ) 1674 { 1675 aSubState.nRowSubTotalFunc = nUserPos; 1676 aSubState.eRowForce = lcl_GetForceFunc( /*pParentLevel*/GetParentLevel() , nUserPos ); 1677 } 1678 1679 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ ) 1680 { 1681 if ( nMeasure == SC_DPMEASURE_ALL ) 1682 nMemberMeasure = nSubCount; 1683 else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL ) 1684 nMemberMeasure = SC_DPMEASURE_ALL; 1685 1686 pDataRoot->UpdateDataRow( pRefMember, nMemberMeasure, bHasChild, aSubState ); 1687 } 1688 } 1689 } 1690 } 1691 1692 if (bHasChild) // child dimension must be processed last, so the column total is known 1693 { 1694 pChildDimension->UpdateDataResults( pRefMember, nMeasure ); 1695 } 1696 } 1697 1698 void ScDPResultMember::SortMembers( ScDPResultMember* pRefMember ) 1699 { 1700 sal_Bool bHasChild = ( pChildDimension != NULL ); 1701 if (bHasChild) 1702 pChildDimension->SortMembers( pRefMember ); // sorting is done at the dimension 1703 1704 if ( IsRoot() && pDataRoot ) 1705 { 1706 // use the row root member to sort columns 1707 // sub total count is always 1 1708 1709 pDataRoot->SortMembers( pRefMember ); 1710 } 1711 } 1712 1713 void ScDPResultMember::DoAutoShow( ScDPResultMember* pRefMember ) 1714 { 1715 sal_Bool bHasChild = ( pChildDimension != NULL ); 1716 if (bHasChild) 1717 pChildDimension->DoAutoShow( pRefMember ); // sorting is done at the dimension 1718 1719 if ( IsRoot()&& pDataRoot ) 1720 { 1721 // use the row root member to sort columns 1722 // sub total count is always 1 1723 1724 pDataRoot->DoAutoShow( pRefMember ); 1725 } 1726 } 1727 1728 void ScDPResultMember::ResetResults( sal_Bool /*bRoot*/ ) 1729 { 1730 if (pDataRoot) 1731 pDataRoot->ResetResults(); 1732 1733 if (pChildDimension) 1734 pChildDimension->ResetResults(); 1735 1736 // if (!bRoot) 1737 // bHasElements = sal_False; 1738 } 1739 1740 void ScDPResultMember::UpdateRunningTotals( const ScDPResultMember* pRefMember, long nMeasure, 1741 ScDPRunningTotalState& rRunning, ScDPRowTotals& rTotals ) const 1742 { 1743 // IsVisible() test is in ScDPResultDimension::FillDataResults 1744 // (not on data layout dimension) 1745 1746 rTotals.SetInColRoot( IsRoot() ); 1747 1748 sal_Bool bHasChild = ( pChildDimension != NULL ); 1749 1750 long nUserSubCount = GetSubTotalCount(); 1751 //if ( nUserSubCount || !bHasChild ) 1752 { 1753 // Calculate at least automatic if no subtotals are selected, 1754 // show only own values if there's no child dimension (innermost). 1755 if ( !nUserSubCount || !bHasChild ) 1756 nUserSubCount = 1; 1757 1758 long nMemberMeasure = nMeasure; 1759 long nSubSize = pResultData->GetCountForMeasure(nMeasure); 1760 1761 if ( pDataRoot ) 1762 { 1763 ScDPSubTotalState aSubState; // initial state 1764 1765 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic" 1766 { 1767 if ( bHasChild && nUserSubCount > 1 ) 1768 { 1769 aSubState.nRowSubTotalFunc = nUserPos; 1770 aSubState.eRowForce = lcl_GetForceFunc( /*pParentLevel*/GetParentLevel(), nUserPos ); 1771 } 1772 1773 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ ) 1774 { 1775 if ( nMeasure == SC_DPMEASURE_ALL ) 1776 nMemberMeasure = nSubCount; 1777 else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL ) 1778 nMemberMeasure = SC_DPMEASURE_ALL; 1779 1780 pDataRoot->UpdateRunningTotals( pRefMember, nMemberMeasure, 1781 bHasChild, aSubState, rRunning, rTotals, *this ); 1782 } 1783 } 1784 } 1785 } 1786 1787 if (bHasChild) // child dimension must be processed last, so the column total is known 1788 { 1789 pChildDimension->UpdateRunningTotals( pRefMember, nMeasure, rRunning, rTotals ); 1790 } 1791 } 1792 1793 void ScDPResultMember::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const 1794 { 1795 lcl_DumpRow( String::CreateFromAscii("ScDPResultMember"), GetName(), NULL, pDoc, rPos ); 1796 SCROW nStartRow = rPos.Row(); 1797 1798 if (pDataRoot) 1799 pDataRoot->DumpState( pRefMember, pDoc, rPos ); 1800 1801 if (pChildDimension) 1802 pChildDimension->DumpState( pRefMember, pDoc, rPos ); 1803 1804 lcl_Indent( pDoc, nStartRow, rPos ); 1805 } 1806 1807 ScDPAggData* ScDPResultMember::GetColTotal( long nMeasure ) const 1808 { 1809 return lcl_GetChildTotal( const_cast<ScDPAggData*>(&aColTotal), nMeasure ); 1810 } 1811 1812 void ScDPResultMember::FillVisibilityData(ScDPResultVisibilityData& rData) const 1813 { 1814 if (pChildDimension) 1815 pChildDimension->FillVisibilityData(rData); 1816 } 1817 1818 // ----------------------------------------------------------------------- 1819 1820 ScDPDataMember::ScDPDataMember( const ScDPResultData* pData, const ScDPResultMember* pRes ) : 1821 pResultData( pData ), 1822 pResultMember( pRes ), 1823 pChildDimension( NULL ) 1824 { 1825 // pResultMember is 0 for root members 1826 } 1827 1828 ScDPDataMember::~ScDPDataMember() 1829 { 1830 delete pChildDimension; 1831 } 1832 1833 String ScDPDataMember::GetName() const 1834 { 1835 if (pResultMember) 1836 return pResultMember->GetName(); 1837 else 1838 return EMPTY_STRING; 1839 } 1840 1841 sal_Bool ScDPDataMember::IsVisible() const 1842 { 1843 if (pResultMember) 1844 return pResultMember->IsVisible(); 1845 else 1846 return sal_False; 1847 } 1848 1849 sal_Bool ScDPDataMember::IsNamedItem( /*const ScDPItemData& r*/SCROW r ) const 1850 { 1851 if (pResultMember) 1852 return pResultMember->IsNamedItem(r); 1853 else 1854 return sal_False; 1855 } 1856 1857 sal_Bool ScDPDataMember::HasHiddenDetails() const 1858 { 1859 if (pResultMember) 1860 return pResultMember->HasHiddenDetails(); 1861 else 1862 return sal_False; 1863 } 1864 1865 void ScDPDataMember::InitFrom( const ScDPResultDimension* pDim ) 1866 { 1867 if ( !pChildDimension ) 1868 pChildDimension = new ScDPDataDimension(pResultData); 1869 pChildDimension->InitFrom(pDim); 1870 } 1871 1872 const long SC_SUBTOTALPOS_AUTO = -1; // default 1873 const long SC_SUBTOTALPOS_SKIP = -2; // don't use 1874 1875 long lcl_GetSubTotalPos( const ScDPSubTotalState& rSubState ) 1876 { 1877 if ( rSubState.nColSubTotalFunc >= 0 && rSubState.nRowSubTotalFunc >= 0 && 1878 rSubState.nColSubTotalFunc != rSubState.nRowSubTotalFunc ) 1879 { 1880 // #i68338# don't return the same index for different combinations (leading to repeated updates), 1881 // return a "don't use" value instead 1882 1883 return SC_SUBTOTALPOS_SKIP; 1884 } 1885 1886 long nRet = SC_SUBTOTALPOS_AUTO; 1887 if ( rSubState.nColSubTotalFunc >= 0 ) nRet = rSubState.nColSubTotalFunc; 1888 if ( rSubState.nRowSubTotalFunc >= 0 ) nRet = rSubState.nRowSubTotalFunc; 1889 return nRet; 1890 } 1891 1892 void ScDPDataMember::UpdateValues( const vector<ScDPValueData>& aValues, const ScDPSubTotalState& rSubState ) 1893 { 1894 //! find out how many and which subtotals are used 1895 1896 ScDPAggData* pAgg = &aAggregate; 1897 1898 long nSubPos = lcl_GetSubTotalPos(rSubState); 1899 if (nSubPos == SC_SUBTOTALPOS_SKIP) 1900 return; 1901 if (nSubPos > 0) 1902 { 1903 long nSkip = nSubPos * pResultData->GetMeasureCount(); 1904 for (long i=0; i<nSkip; i++) 1905 pAgg = pAgg->GetChild(); // created if not there 1906 } 1907 1908 size_t nCount = aValues.size(); 1909 for (size_t nPos = 0; nPos < nCount; ++nPos) 1910 { 1911 pAgg->Update(aValues[nPos], pResultData->GetMeasureFunction(nPos), rSubState); 1912 pAgg = pAgg->GetChild(); 1913 } 1914 } 1915 1916 void ScDPDataMember::ProcessData( const vector< SCROW >& aChildMembers, const vector<ScDPValueData>& aValues, 1917 const ScDPSubTotalState& rSubState ) 1918 { 1919 if ( pResultData->IsLateInit() && !pChildDimension && pResultMember && pResultMember->GetChildDimension() ) 1920 { 1921 // if this DataMember doesn't have a child dimension because the ResultMember's 1922 // child dimension wasn't there yet during this DataMembers's creation, 1923 // create the child dimension now 1924 InitFrom( pResultMember->GetChildDimension() ); 1925 } 1926 1927 ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column 1928 1929 long nUserSubCount = pResultMember ? pResultMember->GetSubTotalCount() : 0; 1930 1931 // Calculate at least automatic if no subtotals are selected, 1932 // show only own values if there's no child dimension (innermost). 1933 if ( !nUserSubCount || !pChildDimension ) 1934 nUserSubCount = 1; 1935 1936 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic" 1937 { 1938 if ( pChildDimension && nUserSubCount > 1 ) 1939 { 1940 const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL; 1941 aLocalSubState.nColSubTotalFunc = nUserPos; 1942 aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos ); 1943 } 1944 1945 UpdateValues( aValues, aLocalSubState ); 1946 } 1947 1948 if (pChildDimension) 1949 pChildDimension->ProcessData( aChildMembers, aValues, rSubState ); // with unmodified subtotal state 1950 } 1951 1952 sal_Bool ScDPDataMember::HasData( long nMeasure, const ScDPSubTotalState& rSubState ) const 1953 { 1954 if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE && rSubState.eRowForce != SUBTOTAL_FUNC_NONE && 1955 rSubState.eColForce != rSubState.eRowForce ) 1956 return sal_False; 1957 1958 // #74542# HasData can be different between measures! 1959 1960 const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState ); 1961 if (!pAgg) 1962 return sal_False; //! error? 1963 1964 return pAgg->HasData(); 1965 } 1966 1967 sal_Bool ScDPDataMember::HasError( long nMeasure, const ScDPSubTotalState& rSubState ) const 1968 { 1969 const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState ); 1970 if (!pAgg) 1971 return sal_True; 1972 1973 return pAgg->HasError(); 1974 } 1975 1976 double ScDPDataMember::GetAggregate( long nMeasure, const ScDPSubTotalState& rSubState ) const 1977 { 1978 const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState ); 1979 if (!pAgg) 1980 return DBL_MAX; //! error? 1981 1982 return pAgg->GetResult(); 1983 } 1984 1985 ScDPAggData* ScDPDataMember::GetAggData( long nMeasure, const ScDPSubTotalState& rSubState ) 1986 { 1987 DBG_ASSERT( nMeasure >= 0, "GetAggData: no measure" ); 1988 1989 ScDPAggData* pAgg = &aAggregate; 1990 long nSkip = nMeasure; 1991 long nSubPos = lcl_GetSubTotalPos(rSubState); 1992 if (nSubPos == SC_SUBTOTALPOS_SKIP) 1993 return NULL; 1994 if (nSubPos > 0) 1995 nSkip += nSubPos * pResultData->GetMeasureCount(); 1996 1997 for ( long nPos=0; nPos<nSkip; nPos++ ) 1998 pAgg = pAgg->GetChild(); //! need to create children here? 1999 2000 return pAgg; 2001 } 2002 2003 const ScDPAggData* ScDPDataMember::GetConstAggData( long nMeasure, const ScDPSubTotalState& rSubState ) const 2004 { 2005 DBG_ASSERT( nMeasure >= 0, "GetConstAggData: no measure" ); 2006 2007 const ScDPAggData* pAgg = &aAggregate; 2008 long nSkip = nMeasure; 2009 long nSubPos = lcl_GetSubTotalPos(rSubState); 2010 if (nSubPos == SC_SUBTOTALPOS_SKIP) 2011 return NULL; 2012 if (nSubPos > 0) 2013 nSkip += nSubPos * pResultData->GetMeasureCount(); 2014 2015 for ( long nPos=0; nPos<nSkip; nPos++ ) 2016 { 2017 pAgg = pAgg->GetExistingChild(); 2018 if (!pAgg) 2019 return NULL; 2020 } 2021 2022 return pAgg; 2023 } 2024 2025 void ScDPDataMember::FillDataRow( const ScDPResultMember* pRefMember, 2026 uno::Sequence<sheet::DataResult>& rSequence, 2027 long& rCol, long nMeasure, sal_Bool bIsSubTotalRow, 2028 const ScDPSubTotalState& rSubState ) const 2029 { 2030 DBG_ASSERT( pRefMember == pResultMember || !pResultMember, "bla" ); 2031 2032 if ( pRefMember->IsVisible() ) //! here or in ScDPDataDimension::FillDataRow ??? 2033 { 2034 long nStartCol = rCol; 2035 2036 const ScDPDataDimension* pDataChild = GetChildDimension(); 2037 const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension(); 2038 2039 const ScDPLevel* pRefParentLevel = const_cast<ScDPResultMember*>(pRefMember)->GetParentLevel(); 2040 2041 long nExtraSpace = 0; 2042 if ( pRefParentLevel && pRefParentLevel->IsAddEmpty() ) 2043 ++nExtraSpace; 2044 2045 sal_Bool bTitleLine = sal_False; 2046 if ( pRefParentLevel && pRefParentLevel->IsOutlineLayout() ) 2047 bTitleLine = sal_True; 2048 2049 sal_Bool bSubTotalInTitle = pRefMember->IsSubTotalInTitle( nMeasure ); 2050 2051 // leave space for children even if the DataMember hasn't been initialized 2052 // (pDataChild is null then, this happens when no values for it are in this row) 2053 sal_Bool bHasChild = ( pRefChild != NULL ); 2054 2055 if ( bHasChild ) 2056 { 2057 if ( bTitleLine ) // in tabular layout the title is on a separate column 2058 ++rCol; // -> fill child dimension one column below 2059 2060 if ( pDataChild ) 2061 pDataChild->FillDataRow( pRefChild, rSequence, rCol, nMeasure, bIsSubTotalRow, rSubState ); 2062 rCol += (sal_uInt16)pRefMember->GetSize( nMeasure ); 2063 2064 if ( bTitleLine ) // title column is included in GetSize, so the following 2065 --rCol; // positions are calculated with the normal values 2066 } 2067 2068 long nUserSubStart; 2069 long nUserSubCount = pRefMember->GetSubTotalCount(&nUserSubStart); 2070 if ( nUserSubCount || !bHasChild ) 2071 { 2072 // Calculate at least automatic if no subtotals are selected, 2073 // show only own values if there's no child dimension (innermost). 2074 if ( !nUserSubCount || !bHasChild ) 2075 { 2076 nUserSubCount = 1; 2077 nUserSubStart = 0; 2078 } 2079 2080 ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column 2081 2082 long nMemberMeasure = nMeasure; 2083 long nSubSize = pResultData->GetCountForMeasure(nMeasure); 2084 if (bHasChild) 2085 { 2086 rCol -= nSubSize * ( nUserSubCount - nUserSubStart ); // GetSize includes space for SubTotal 2087 rCol -= nExtraSpace; // GetSize includes the empty line 2088 } 2089 2090 long nMoveSubTotal = 0; 2091 if ( bSubTotalInTitle ) 2092 { 2093 nMoveSubTotal = rCol - nStartCol; // force to first (title) column 2094 rCol = nStartCol; 2095 } 2096 2097 for (long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++) 2098 { 2099 if ( pChildDimension && nUserSubCount > 1 ) 2100 { 2101 const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL; 2102 aLocalSubState.nColSubTotalFunc = nUserPos; 2103 aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos ); 2104 } 2105 2106 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ ) 2107 { 2108 if ( nMeasure == SC_DPMEASURE_ALL ) 2109 nMemberMeasure = nSubCount; 2110 2111 DBG_ASSERT( rCol < rSequence.getLength(), "bumm" ); 2112 sheet::DataResult& rRes = rSequence.getArray()[rCol]; 2113 2114 if ( HasData( nMemberMeasure, aLocalSubState ) ) 2115 { 2116 if ( HasError( nMemberMeasure, aLocalSubState ) ) 2117 { 2118 rRes.Value = 0; 2119 rRes.Flags |= sheet::DataResultFlags::ERROR; 2120 } 2121 else 2122 { 2123 rRes.Value = GetAggregate( nMemberMeasure, aLocalSubState ); 2124 rRes.Flags |= sheet::DataResultFlags::HASDATA; 2125 } 2126 } 2127 2128 if ( bHasChild || bIsSubTotalRow ) 2129 rRes.Flags |= sheet::DataResultFlags::SUBTOTAL; 2130 2131 rCol += 1; 2132 } 2133 } 2134 2135 // add extra space again if subtracted from GetSize above, 2136 // add to own size if no children 2137 rCol += nExtraSpace; 2138 2139 rCol += nMoveSubTotal; 2140 } 2141 } 2142 } 2143 2144 void ScDPDataMember::UpdateDataRow( const ScDPResultMember* pRefMember, 2145 long nMeasure, sal_Bool bIsSubTotalRow, 2146 const ScDPSubTotalState& rSubState ) 2147 { 2148 DBG_ASSERT( pRefMember == pResultMember || !pResultMember, "bla" ); 2149 2150 // Calculate must be called even if not visible (for use as reference value) 2151 const ScDPDataDimension* pDataChild = GetChildDimension(); 2152 const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension(); 2153 2154 // leave space for children even if the DataMember hasn't been initialized 2155 // (pDataChild is null then, this happens when no values for it are in this row) 2156 sal_Bool bHasChild = ( pRefChild != NULL ); 2157 2158 // process subtotals even if not shown 2159 long nUserSubCount = pRefMember->GetSubTotalCount(); 2160 2161 // Calculate at least automatic if no subtotals are selected, 2162 // show only own values if there's no child dimension (innermost). 2163 if ( !nUserSubCount || !bHasChild ) 2164 nUserSubCount = 1; 2165 2166 ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column 2167 2168 long nMemberMeasure = nMeasure; 2169 long nSubSize = pResultData->GetCountForMeasure(nMeasure); 2170 2171 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic" 2172 { 2173 if ( pChildDimension && nUserSubCount > 1 ) 2174 { 2175 const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL; 2176 aLocalSubState.nColSubTotalFunc = nUserPos; 2177 aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos ); 2178 } 2179 2180 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ ) 2181 { 2182 if ( nMeasure == SC_DPMEASURE_ALL ) 2183 nMemberMeasure = nSubCount; 2184 2185 // update data... 2186 ScDPAggData* pAggData = GetAggData( nMemberMeasure, aLocalSubState ); 2187 if (pAggData) 2188 { 2189 //! aLocalSubState? 2190 ScSubTotalFunc eFunc = pResultData->GetMeasureFunction( nMemberMeasure ); 2191 sheet::DataPilotFieldReference aReferenceValue = pResultData->GetMeasureRefVal( nMemberMeasure ); 2192 sal_Int32 eRefType = aReferenceValue.ReferenceType; 2193 2194 // calculate the result first - for all members, regardless of reference value 2195 pAggData->Calculate( eFunc, aLocalSubState ); 2196 2197 if ( eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE || 2198 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE || 2199 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE ) 2200 { 2201 // copy the result into auxiliary value, so differences can be 2202 // calculated in any order 2203 pAggData->SetAuxiliary( pAggData->GetResult() ); 2204 } 2205 // column/row percentage/index is now in UpdateRunningTotals, so it doesn't disturb sorting 2206 } 2207 } 2208 } 2209 2210 if ( bHasChild ) // child dimension must be processed last, so the row total is known 2211 { 2212 if ( pDataChild ) 2213 pDataChild->UpdateDataRow( pRefChild, nMeasure, bIsSubTotalRow, rSubState ); 2214 } 2215 } 2216 2217 void ScDPDataMember::SortMembers( ScDPResultMember* pRefMember ) 2218 { 2219 DBG_ASSERT( pRefMember == pResultMember || !pResultMember, "bla" ); 2220 2221 if ( pRefMember->IsVisible() ) //! here or in ScDPDataDimension ??? 2222 { 2223 ScDPDataDimension* pDataChild = GetChildDimension(); 2224 ScDPResultDimension* pRefChild = pRefMember->GetChildDimension(); 2225 if ( pRefChild && pDataChild ) 2226 pDataChild->SortMembers( pRefChild ); // sorting is done at the dimension 2227 } 2228 } 2229 2230 void ScDPDataMember::DoAutoShow( ScDPResultMember* pRefMember ) 2231 { 2232 DBG_ASSERT( pRefMember == pResultMember || !pResultMember, "bla" ); 2233 2234 if ( pRefMember->IsVisible() ) //! here or in ScDPDataDimension ??? 2235 { 2236 ScDPDataDimension* pDataChild = GetChildDimension(); 2237 ScDPResultDimension* pRefChild = pRefMember->GetChildDimension(); 2238 if ( pRefChild && pDataChild ) 2239 pDataChild->DoAutoShow( pRefChild ); // sorting is done at the dimension 2240 } 2241 } 2242 2243 void ScDPDataMember::ResetResults() 2244 { 2245 aAggregate.Reset(); 2246 2247 ScDPDataDimension* pDataChild = GetChildDimension(); 2248 if ( pDataChild ) 2249 pDataChild->ResetResults(); 2250 } 2251 2252 void ScDPDataMember::UpdateRunningTotals( const ScDPResultMember* pRefMember, 2253 long nMeasure, sal_Bool bIsSubTotalRow, 2254 const ScDPSubTotalState& rSubState, ScDPRunningTotalState& rRunning, 2255 ScDPRowTotals& rTotals, const ScDPResultMember& rRowParent ) 2256 { 2257 DBG_ASSERT( pRefMember == pResultMember || !pResultMember, "bla" ); 2258 2259 if ( pRefMember->IsVisible() ) //! here or in ScDPDataDimension::UpdateRunningTotals ??? 2260 { 2261 const ScDPDataDimension* pDataChild = GetChildDimension(); 2262 const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension(); 2263 2264 sal_Bool bIsRoot = ( pResultMember == NULL || pResultMember->GetParentLevel() == NULL ); 2265 2266 // leave space for children even if the DataMember hasn't been initialized 2267 // (pDataChild is null then, this happens when no values for it are in this row) 2268 sal_Bool bHasChild = ( pRefChild != NULL ); 2269 2270 long nUserSubCount = pRefMember->GetSubTotalCount(); 2271 //if ( nUserSubCount || !bHasChild ) 2272 { 2273 // Calculate at least automatic if no subtotals are selected, 2274 // show only own values if there's no child dimension (innermost). 2275 if ( !nUserSubCount || !bHasChild ) 2276 nUserSubCount = 1; 2277 2278 ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column 2279 2280 long nMemberMeasure = nMeasure; 2281 long nSubSize = pResultData->GetCountForMeasure(nMeasure); 2282 2283 for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic" 2284 { 2285 if ( pChildDimension && nUserSubCount > 1 ) 2286 { 2287 const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL; 2288 aLocalSubState.nColSubTotalFunc = nUserPos; 2289 aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos ); 2290 } 2291 2292 for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ ) 2293 { 2294 if ( nMeasure == SC_DPMEASURE_ALL ) 2295 nMemberMeasure = nSubCount; 2296 2297 // update data... 2298 ScDPAggData* pAggData = GetAggData( nMemberMeasure, aLocalSubState ); 2299 if (pAggData) 2300 { 2301 //! aLocalSubState? 2302 sheet::DataPilotFieldReference aReferenceValue = pResultData->GetMeasureRefVal( nMemberMeasure ); 2303 sal_Int32 eRefType = aReferenceValue.ReferenceType; 2304 2305 if ( eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL || 2306 eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE || 2307 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE || 2308 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE ) 2309 { 2310 sal_Bool bRunningTotal = ( eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL ); 2311 sal_Bool bRelative = 2312 ( aReferenceValue.ReferenceItemType != sheet::DataPilotFieldReferenceItemType::NAMED && !bRunningTotal ); 2313 long nRelativeDir = bRelative ? 2314 ( ( aReferenceValue.ReferenceItemType == sheet::DataPilotFieldReferenceItemType::PREVIOUS ) ? -1 : 1 ) : 0; 2315 2316 const long* pColVisible = rRunning.GetColVisible(); 2317 const long* pColIndexes = rRunning.GetColIndexes(); 2318 const long* pRowVisible = rRunning.GetRowVisible(); 2319 const long* pRowIndexes = rRunning.GetRowIndexes(); 2320 2321 String aRefFieldName = aReferenceValue.ReferenceField; 2322 2323 //! aLocalSubState? 2324 sal_uInt16 nRefOrient = pResultData->GetMeasureRefOrient( nMemberMeasure ); 2325 sal_Bool bRefDimInCol = ( nRefOrient == sheet::DataPilotFieldOrientation_COLUMN ); 2326 sal_Bool bRefDimInRow = ( nRefOrient == sheet::DataPilotFieldOrientation_ROW ); 2327 2328 const ScDPResultDimension* pSelectDim = NULL; 2329 long nRowPos = 0; 2330 long nColPos = 0; 2331 2332 // 2333 // find the reference field in column or row dimensions 2334 // 2335 2336 if ( bRefDimInRow ) // look in row dimensions 2337 { 2338 pSelectDim = rRunning.GetRowResRoot()->GetChildDimension(); 2339 while ( pSelectDim && pSelectDim->GetName() != aRefFieldName ) 2340 { 2341 long nIndex = pRowIndexes[nRowPos]; 2342 if ( nIndex >= 0 && nIndex < pSelectDim->GetMemberCount() ) 2343 pSelectDim = pSelectDim->GetMember(nIndex)->GetChildDimension(); 2344 else 2345 pSelectDim = NULL; 2346 ++nRowPos; 2347 } 2348 // child dimension of innermost member? 2349 if ( pSelectDim && pRowIndexes[nRowPos] < 0 ) 2350 pSelectDim = NULL; 2351 } 2352 2353 if ( bRefDimInCol ) // look in column dimensions 2354 { 2355 pSelectDim = rRunning.GetColResRoot()->GetChildDimension(); 2356 while ( pSelectDim && pSelectDim->GetName() != aRefFieldName ) 2357 { 2358 long nIndex = pColIndexes[nColPos]; 2359 if ( nIndex >= 0 && nIndex < pSelectDim->GetMemberCount() ) 2360 pSelectDim = pSelectDim->GetMember(nIndex)->GetChildDimension(); 2361 else 2362 pSelectDim = NULL; 2363 ++nColPos; 2364 } 2365 // child dimension of innermost member? 2366 if ( pSelectDim && pColIndexes[nColPos] < 0 ) 2367 pSelectDim = NULL; 2368 } 2369 2370 sal_Bool bNoDetailsInRef = sal_False; 2371 if ( pSelectDim && bRunningTotal ) 2372 { 2373 // Running totals: 2374 // If details are hidden for this member in the reference dimension, 2375 // don't show or sum up the value. Otherwise, for following members, 2376 // the running totals of details and subtotals wouldn't match. 2377 2378 long nMyIndex = bRefDimInCol ? pColIndexes[nColPos] : pRowIndexes[nRowPos]; 2379 if ( nMyIndex >= 0 && nMyIndex < pSelectDim->GetMemberCount() ) 2380 { 2381 const ScDPResultMember* pMyRefMember = pSelectDim->GetMember(nMyIndex); 2382 if ( pMyRefMember && pMyRefMember->HasHiddenDetails() ) 2383 { 2384 pSelectDim = NULL; // don't calculate 2385 bNoDetailsInRef = sal_True; // show error, not empty 2386 } 2387 } 2388 } 2389 2390 if ( bRelative ) 2391 { 2392 // Difference/Percentage from previous/next: 2393 // If details are hidden for this member in the innermost column/row 2394 // dimension (the orientation of the reference dimension), show an 2395 // error value. 2396 // - If the no-details dimension is the reference dimension, its 2397 // members will be skipped when finding the previous/next member, 2398 // so there must be no results for its members. 2399 // - If the no-details dimension is outside of the reference dimension, 2400 // no calculation in the reference dimension is possible. 2401 // - Otherwise, the error isn't strictly necessary, but shown for 2402 // consistency. 2403 2404 sal_Bool bInnerNoDetails = bRefDimInCol ? HasHiddenDetails() : 2405 ( bRefDimInRow ? rRowParent.HasHiddenDetails() : sal_True ); 2406 if ( bInnerNoDetails ) 2407 { 2408 pSelectDim = NULL; 2409 bNoDetailsInRef = sal_True; // show error, not empty 2410 } 2411 } 2412 2413 if ( !bRefDimInCol && !bRefDimInRow ) // invalid dimension specified 2414 bNoDetailsInRef = sal_True; // pSelectDim is then already NULL 2415 2416 // 2417 // get the member for the reference item and do the calculation 2418 // 2419 2420 if ( bRunningTotal ) 2421 { 2422 // running total in (dimension) -> find first existing member 2423 2424 if ( pSelectDim ) 2425 { 2426 ScDPDataMember* pSelectMember; 2427 if ( bRefDimInCol ) 2428 pSelectMember = ScDPResultDimension::GetColReferenceMember( NULL, NULL, 2429 nColPos, rRunning ); 2430 else 2431 { 2432 long nSkip = nRowPos + 1; // including the reference dimension 2433 pSelectMember = pSelectDim->GetRowReferenceMember( NULL, NULL, 2434 pRowIndexes+nSkip, pColIndexes ); 2435 } 2436 2437 if ( pSelectMember ) 2438 { 2439 // The running total is kept as the auxiliary value in 2440 // the first available member for the reference dimension. 2441 // Members are visited in final order, so each one's result 2442 // can be used and then modified. 2443 2444 ScDPAggData* pSelectData = pSelectMember-> 2445 GetAggData( nMemberMeasure, aLocalSubState ); 2446 if ( pSelectData ) 2447 { 2448 double fTotal = pSelectData->GetAuxiliary(); 2449 fTotal += pAggData->GetResult(); 2450 pSelectData->SetAuxiliary( fTotal ); 2451 pAggData->SetResult( fTotal ); 2452 pAggData->SetEmpty(sal_False); // always display 2453 } 2454 } 2455 else 2456 pAggData->SetError(); 2457 } 2458 else if (bNoDetailsInRef) 2459 pAggData->SetError(); 2460 else 2461 pAggData->SetEmpty(sal_True); // empty (dim set to 0 above) 2462 } 2463 else 2464 { 2465 // difference/percentage -> find specified member 2466 2467 if ( pSelectDim ) 2468 { 2469 String aRefItemName = aReferenceValue.ReferenceItemName; 2470 ScDPRelativePos aRefItemPos( 0, nRelativeDir ); // nBasePos is modified later 2471 2472 const String* pRefName = NULL; 2473 const ScDPRelativePos* pRefPos = NULL; 2474 if ( bRelative ) 2475 pRefPos = &aRefItemPos; 2476 else 2477 pRefName = &aRefItemName; 2478 2479 ScDPDataMember* pSelectMember; 2480 if ( bRefDimInCol ) 2481 { 2482 aRefItemPos.nBasePos = pColVisible[nColPos]; // without sort order applied 2483 pSelectMember = ScDPResultDimension::GetColReferenceMember( pRefPos, pRefName, 2484 nColPos, rRunning ); 2485 } 2486 else 2487 { 2488 aRefItemPos.nBasePos = pRowVisible[nRowPos]; // without sort order applied 2489 long nSkip = nRowPos + 1; // including the reference dimension 2490 pSelectMember = pSelectDim->GetRowReferenceMember( pRefPos, pRefName, 2491 pRowIndexes+nSkip, pColIndexes ); 2492 } 2493 2494 // difference or perc.difference is empty for the reference item itself 2495 if ( pSelectMember == this && 2496 eRefType != sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ) 2497 { 2498 pAggData->SetEmpty(sal_True); 2499 } 2500 else if ( pSelectMember ) 2501 { 2502 const ScDPAggData* pOtherAggData = pSelectMember-> 2503 GetConstAggData( nMemberMeasure, aLocalSubState ); 2504 DBG_ASSERT( pOtherAggData, "no agg data" ); 2505 if ( pOtherAggData ) 2506 { 2507 // Reference member may be visited before or after this one, 2508 // so the auxiliary value is used for the original result. 2509 2510 double fOtherResult = pOtherAggData->GetAuxiliary(); 2511 double fThisResult = pAggData->GetResult(); 2512 sal_Bool bError = sal_False; 2513 switch ( eRefType ) 2514 { 2515 case sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE: 2516 fThisResult = fThisResult - fOtherResult; 2517 break; 2518 case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE: 2519 if ( fOtherResult == 0.0 ) 2520 bError = sal_True; 2521 else 2522 fThisResult = fThisResult / fOtherResult; 2523 break; 2524 case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE: 2525 if ( fOtherResult == 0.0 ) 2526 bError = sal_True; 2527 else 2528 fThisResult = ( fThisResult - fOtherResult ) / fOtherResult; 2529 break; 2530 default: 2531 DBG_ERROR("invalid calculation type"); 2532 } 2533 if ( bError ) 2534 { 2535 pAggData->SetError(); 2536 } 2537 else 2538 { 2539 pAggData->SetResult(fThisResult); 2540 pAggData->SetEmpty(sal_False); // always display 2541 } 2542 //! errors in data? 2543 } 2544 } 2545 else if (bRelative && !bNoDetailsInRef) 2546 pAggData->SetEmpty(sal_True); // empty 2547 else 2548 pAggData->SetError(); // error 2549 } 2550 else if (bNoDetailsInRef) 2551 pAggData->SetError(); // error 2552 else 2553 pAggData->SetEmpty(sal_True); // empty 2554 } 2555 } 2556 else if ( eRefType == sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE || 2557 eRefType == sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE || 2558 eRefType == sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE || 2559 eRefType == sheet::DataPilotFieldReferenceType::INDEX ) 2560 { 2561 // 2562 // set total values when they are encountered (always before their use) 2563 // 2564 2565 ScDPAggData* pColTotalData = pRefMember->GetColTotal( nMemberMeasure ); 2566 ScDPAggData* pRowTotalData = rTotals.GetRowTotal( nMemberMeasure ); 2567 ScDPAggData* pGrandTotalData = rTotals.GetGrandTotal( nMemberMeasure ); 2568 2569 double fTotalValue = pAggData->HasError() ? 0 : pAggData->GetResult(); 2570 2571 if ( bIsRoot && rTotals.IsInColRoot() && pGrandTotalData ) 2572 pGrandTotalData->SetAuxiliary( fTotalValue ); 2573 2574 if ( bIsRoot && pRowTotalData ) 2575 pRowTotalData->SetAuxiliary( fTotalValue ); 2576 2577 if ( rTotals.IsInColRoot() && pColTotalData ) 2578 pColTotalData->SetAuxiliary( fTotalValue ); 2579 2580 // 2581 // find relation to total values 2582 // 2583 2584 switch ( eRefType ) 2585 { 2586 case sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE: 2587 case sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE: 2588 case sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE: 2589 { 2590 double nTotal; 2591 if ( eRefType == sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE ) 2592 nTotal = pRowTotalData->GetAuxiliary(); 2593 else if ( eRefType == sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE ) 2594 nTotal = pColTotalData->GetAuxiliary(); 2595 else 2596 nTotal = pGrandTotalData->GetAuxiliary(); 2597 2598 if ( nTotal == 0.0 ) 2599 pAggData->SetError(); 2600 else 2601 pAggData->SetResult( pAggData->GetResult() / nTotal ); 2602 } 2603 break; 2604 case sheet::DataPilotFieldReferenceType::INDEX: 2605 { 2606 double nColTotal = pColTotalData->GetAuxiliary(); 2607 double nRowTotal = pRowTotalData->GetAuxiliary(); 2608 double nGrandTotal = pGrandTotalData->GetAuxiliary(); 2609 if ( nRowTotal == 0.0 || nColTotal == 0.0 ) 2610 pAggData->SetError(); 2611 else 2612 pAggData->SetResult( 2613 ( pAggData->GetResult() * nGrandTotal ) / 2614 ( nRowTotal * nColTotal ) ); 2615 } 2616 break; 2617 } 2618 } 2619 } 2620 } 2621 } 2622 } 2623 2624 if ( bHasChild ) // child dimension must be processed last, so the row total is known 2625 { 2626 if ( pDataChild ) 2627 pDataChild->UpdateRunningTotals( pRefChild, nMeasure, 2628 bIsSubTotalRow, rSubState, rRunning, rTotals, rRowParent ); 2629 } 2630 } 2631 } 2632 2633 void ScDPDataMember::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const 2634 { 2635 lcl_DumpRow( String::CreateFromAscii("ScDPDataMember"), GetName(), &aAggregate, pDoc, rPos ); 2636 SCROW nStartRow = rPos.Row(); 2637 2638 const ScDPDataDimension* pDataChild = GetChildDimension(); 2639 const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension(); 2640 if ( pDataChild && pRefChild ) 2641 pDataChild->DumpState( pRefChild, pDoc, rPos ); 2642 2643 lcl_Indent( pDoc, nStartRow, rPos ); 2644 } 2645 2646 // ----------------------------------------------------------------------- 2647 2648 // Helper class to select the members to include in 2649 // ScDPResultDimension::InitFrom or LateInitFrom if groups are used 2650 2651 class ScDPGroupCompare 2652 { 2653 private: 2654 const ScDPResultData* pResultData; 2655 const ScDPInitState& rInitState; 2656 long nDimSource; 2657 sal_Bool bIncludeAll; 2658 sal_Bool bIsBase; 2659 long nGroupBase; 2660 // Wang Xu Ming -- 2009-8-6 2661 // DataPilot Migration - Cache&&Performance 2662 SCROW nBaseDataId; 2663 // End Comments 2664 public: 2665 ScDPGroupCompare( const ScDPResultData* pData, const ScDPInitState& rState, long nDimension ); 2666 ~ScDPGroupCompare() {} 2667 2668 sal_Bool IsIncluded( const ScDPMember& rMember ) { return bIncludeAll || TestIncluded( rMember ); } 2669 sal_Bool TestIncluded( const ScDPMember& rMember ); 2670 }; 2671 2672 ScDPGroupCompare::ScDPGroupCompare( const ScDPResultData* pData, const ScDPInitState& rState, long nDimension ) : 2673 pResultData( pData ), 2674 rInitState( rState ), 2675 nDimSource( nDimension ), 2676 nBaseDataId( -1 ) 2677 { 2678 bIsBase = pResultData->IsBaseForGroup( nDimSource ); 2679 nGroupBase = pResultData->GetGroupBase( nDimSource ); //! get together in one call? 2680 if ( nGroupBase >= 0 ) 2681 nBaseDataId = rInitState.GetNameIdForIndex( nGroupBase ); 2682 2683 // if bIncludeAll is set, TestIncluded doesn't need to be called 2684 bIncludeAll = !( bIsBase || nGroupBase >= 0 ); 2685 } 2686 2687 sal_Bool ScDPGroupCompare::TestIncluded( const ScDPMember& rMember ) 2688 { 2689 sal_Bool bInclude = sal_True; 2690 if ( nBaseDataId >=0 ) 2691 { 2692 ScDPItemData aMemberData; 2693 rMember.FillItemData( aMemberData ); 2694 bInclude = pResultData->IsInGroup( aMemberData, nDimSource, nBaseDataId, nGroupBase ); 2695 } 2696 else if ( bIsBase ) 2697 { 2698 // need to check all previous groups 2699 //! get array of groups (or indexes) before loop? 2700 ScDPItemData aMemberData; 2701 rMember.FillItemData( aMemberData ); 2702 long nInitCount = rInitState.GetCount(); 2703 const long* pInitSource = rInitState.GetSource(); 2704 /*const ScDPItemData* pInitNames = rInitState.GetNames();*/ 2705 const SCROW* pInitNames = rInitState.GetNameIds(); 2706 for (long nInitPos=0; nInitPos<nInitCount && bInclude; nInitPos++) 2707 if ( pResultData->GetGroupBase( pInitSource[nInitPos] ) == nDimSource ) 2708 { 2709 bInclude = pResultData->IsInGroup( pInitNames[nInitPos], pInitSource[nInitPos], 2710 aMemberData, nDimSource ); 2711 } 2712 } 2713 else if ( nGroupBase >= 0 ) 2714 { 2715 // base isn't used in preceding fields 2716 // -> look for other groups using the same base 2717 2718 //! get array of groups (or indexes) before loop? 2719 ScDPItemData aMemberData; 2720 rMember.FillItemData( aMemberData ); 2721 long nInitCount = rInitState.GetCount(); 2722 const long* pInitSource = rInitState.GetSource(); 2723 /*const ScDPItemData* pInitNames = rInitState.GetNames();*/ 2724 const SCROW* pInitNames = rInitState.GetNameIds(); 2725 for (long nInitPos=0; nInitPos<nInitCount && bInclude; nInitPos++) 2726 if ( pResultData->GetGroupBase( pInitSource[nInitPos] ) == nGroupBase ) 2727 { 2728 // same base (hierarchy between the two groups is irrelevant) 2729 bInclude = pResultData->HasCommonElement( pInitNames[nInitPos], pInitSource[nInitPos], 2730 aMemberData, nDimSource ); 2731 } 2732 } 2733 2734 return bInclude; 2735 } 2736 2737 // ----------------------------------------------------------------------- 2738 2739 ScDPResultDimension::ScDPResultDimension( const ScDPResultData* pData ) : 2740 pResultData( pData ), 2741 bInitialized( sal_False ), 2742 bIsDataLayout( sal_False ), 2743 bSortByData( sal_False ), 2744 bSortAscending( sal_False ), 2745 nSortMeasure( 0 ), 2746 bAutoShow( sal_False ), 2747 bAutoTopItems( sal_False ), 2748 nAutoMeasure( 0 ), 2749 nAutoCount( 0 ) 2750 { 2751 } 2752 2753 ScDPResultDimension::~ScDPResultDimension() 2754 { 2755 for( int i = maMemberArray.size () ; i-- > 0 ; ) 2756 delete maMemberArray[i]; 2757 } 2758 2759 ScDPResultMember *ScDPResultDimension::FindMember( SCROW iData ) const 2760 { 2761 if( bIsDataLayout ) 2762 return maMemberArray[0]; 2763 2764 MemberHash::const_iterator aRes = maMemberHash.find( iData ); 2765 if( aRes != maMemberHash.end()) { 2766 if ( aRes->second->IsNamedItem( iData ) ) 2767 return aRes->second; 2768 DBG_ERROR("problem! hash result is not the same as IsNamedItem"); 2769 } 2770 2771 unsigned int i; 2772 unsigned int nCount = maMemberArray.size(); 2773 ScDPResultMember* pResultMember; 2774 for( i = 0; i < nCount ; i++ ) 2775 { 2776 pResultMember = maMemberArray[i]; 2777 if ( pResultMember->IsNamedItem( iData ) ) 2778 return pResultMember; 2779 } 2780 return NULL; 2781 } 2782 2783 void ScDPResultDimension::InitFrom( const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev, 2784 size_t nPos, ScDPInitState& rInitState, sal_Bool bInitChild /*= sal_True */ ) 2785 { 2786 if (nPos >= ppDim.size() || nPos >= ppLev.size()) 2787 { 2788 bInitialized = true; 2789 return; 2790 } 2791 2792 ScDPDimension* pThisDim = ppDim[nPos]; 2793 ScDPLevel* pThisLevel = ppLev[nPos]; 2794 2795 if (!pThisDim || !pThisLevel) 2796 { 2797 bInitialized = true; 2798 return; 2799 } 2800 2801 bIsDataLayout = pThisDim->getIsDataLayoutDimension(); // member 2802 aDimensionName = pThisDim->getName(); // member 2803 2804 // Check the autoshow setting. If it's enabled, store the settings. 2805 const sheet::DataPilotFieldAutoShowInfo& rAutoInfo = pThisLevel->GetAutoShow(); 2806 if ( rAutoInfo.IsEnabled ) 2807 { 2808 bAutoShow = sal_True; 2809 bAutoTopItems = ( rAutoInfo.ShowItemsMode == sheet::DataPilotFieldShowItemsMode::FROM_TOP ); 2810 nAutoMeasure = pThisLevel->GetAutoMeasure(); 2811 nAutoCount = rAutoInfo.ItemCount; 2812 } 2813 2814 // Check the sort info, and store the settings if appropriate. 2815 const sheet::DataPilotFieldSortInfo& rSortInfo = pThisLevel->GetSortInfo(); 2816 if ( rSortInfo.Mode == sheet::DataPilotFieldSortMode::DATA ) 2817 { 2818 bSortByData = sal_True; 2819 bSortAscending = rSortInfo.IsAscending; 2820 nSortMeasure = pThisLevel->GetSortMeasure(); 2821 } 2822 2823 // global order is used to initialize aMembers, so it doesn't have to be looked at later 2824 const ScMemberSortOrder& rGlobalOrder = pThisLevel->GetGlobalOrder(); 2825 2826 long nDimSource = pThisDim->GetDimension(); //! check GetSourceDim? 2827 ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource ); 2828 2829 // Now, go through all members and initialize them. 2830 ScDPMembers* pMembers = pThisLevel->GetMembersObject(); 2831 long nMembCount = pMembers->getCount(); 2832 for ( long i=0; i<nMembCount; i++ ) 2833 { 2834 long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i]; 2835 2836 ScDPMember* pMember = pMembers->getByIndex(nSorted); 2837 if ( aCompare.IsIncluded( *pMember ) ) 2838 { 2839 ScDPParentDimData aData( i, pThisDim, pThisLevel, pMember); 2840 ScDPResultMember* pNew = AddMember( aData ); 2841 2842 rInitState.AddMember( nDimSource, /*aMemberData*/pNew->GetDataId() ); 2843 pNew->InitFrom( ppDim, ppLev, nPos+1, rInitState, bInitChild ); 2844 rInitState.RemoveMember(); 2845 } 2846 } 2847 bInitialized = sal_True; 2848 } 2849 2850 void ScDPResultDimension::LateInitFrom( LateInitParams& rParams/* const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev*/, 2851 const vector<SCROW>& pItemData, size_t nPos, 2852 ScDPInitState& rInitState ) 2853 // End Comments 2854 { 2855 if ( rParams.IsEnd( nPos ) ) 2856 return; 2857 #ifdef DBG_UTIL 2858 DBG_ASSERT( nPos <= pItemData.size(), ByteString::CreateFromInt32( pItemData.size()).GetBuffer() ); 2859 #endif 2860 ScDPDimension* pThisDim = rParams.GetDim( nPos ); 2861 ScDPLevel* pThisLevel = rParams.GetLevel( nPos ); 2862 SCROW rThisData = pItemData[nPos]; 2863 2864 if (!pThisDim || !pThisLevel) 2865 return; 2866 2867 long nDimSource = pThisDim->GetDimension(); //! check GetSourceDim? 2868 2869 sal_Bool bShowEmpty = pThisLevel->getShowEmpty(); 2870 2871 if ( !bInitialized ) 2872 { // init some values 2873 // create all members at the first call (preserve order) 2874 bIsDataLayout = pThisDim->getIsDataLayoutDimension(); 2875 aDimensionName = pThisDim->getName(); 2876 2877 const sheet::DataPilotFieldAutoShowInfo& rAutoInfo = pThisLevel->GetAutoShow(); 2878 if ( rAutoInfo.IsEnabled ) 2879 { 2880 bAutoShow = sal_True; 2881 bAutoTopItems = ( rAutoInfo.ShowItemsMode == sheet::DataPilotFieldShowItemsMode::FROM_TOP ); 2882 nAutoMeasure = pThisLevel->GetAutoMeasure(); 2883 nAutoCount = rAutoInfo.ItemCount; 2884 } 2885 2886 const sheet::DataPilotFieldSortInfo& rSortInfo = pThisLevel->GetSortInfo(); 2887 if ( rSortInfo.Mode == sheet::DataPilotFieldSortMode::DATA ) 2888 { 2889 bSortByData = sal_True; 2890 bSortAscending = rSortInfo.IsAscending; 2891 nSortMeasure = pThisLevel->GetSortMeasure(); 2892 } 2893 } 2894 2895 bool bLateInitAllMembers= bIsDataLayout || rParams.GetInitAllChild() || bShowEmpty; 2896 2897 if ( !bLateInitAllMembers ) 2898 { 2899 ResultMembers* pMembers = pResultData->GetDimResultMembers(nDimSource, pThisDim, pThisLevel); 2900 bLateInitAllMembers = pMembers->IsHasHideDetailsMembers(); 2901 #ifdef DBG_UTIL 2902 DBG_TRACESTR( aDimensionName ) 2903 if ( pMembers->IsHasHideDetailsMembers() ) 2904 DBG_TRACE ( "HasHideDetailsMembers" ); 2905 #endif 2906 pMembers->SetHasHideDetailsMembers( sal_False ); 2907 } 2908 2909 bool bNewAllMembers =(!rParams.IsRow()) || nPos == 0 || bLateInitAllMembers ; 2910 2911 if (bNewAllMembers ) 2912 { 2913 // global order is used to initialize aMembers, so it doesn't have to be looked at later 2914 if ( !bInitialized ) 2915 { //init all members 2916 const ScMemberSortOrder& rGlobalOrder = pThisLevel->GetGlobalOrder(); 2917 2918 ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource ); 2919 ScDPMembers* pMembers = pThisLevel->GetMembersObject(); 2920 long nMembCount = pMembers->getCount(); 2921 for ( long i=0; i<nMembCount; i++ ) 2922 { 2923 long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i]; 2924 2925 ScDPMember* pMember = pMembers->getByIndex(nSorted); 2926 if ( aCompare.IsIncluded( *pMember ) ) 2927 { // add all members 2928 ScDPParentDimData aData( i, pThisDim, pThisLevel, pMember ); 2929 AddMember( aData ); 2930 } 2931 } 2932 bInitialized = sal_True; // don't call again, even if no members were included 2933 } 2934 // initialize only specific member (or all if "show empty" flag is set) 2935 if ( bLateInitAllMembers ) 2936 { 2937 long nCount = maMemberArray.size(); 2938 for (long i=0; i<nCount; i++) 2939 { 2940 ScDPResultMember* pResultMember = maMemberArray[i]; 2941 2942 // check show empty 2943 sal_Bool bAllChildren = sal_False; 2944 if( bShowEmpty ) 2945 { 2946 if ( pResultMember->IsNamedItem( rThisData ) ) 2947 bAllChildren = sal_False; 2948 else 2949 bAllChildren = sal_True; 2950 } 2951 rParams.SetInitAllChildren( bAllChildren ); 2952 rInitState.AddMember( nDimSource, pResultMember->GetDataId() ); 2953 pResultMember->LateInitFrom( rParams, pItemData, nPos+1, rInitState ); 2954 rInitState.RemoveMember(); 2955 } 2956 } 2957 else 2958 { 2959 ScDPResultMember* pResultMember = FindMember( rThisData ); 2960 if( NULL != pResultMember ) 2961 { 2962 //DBG_TRACE( "ScDPResultDimension::LateInitFrom"); 2963 // DBG_TRACESTR( pResultMember->GetDPMember()->GetNameStr()); 2964 2965 rInitState.AddMember( nDimSource, pResultMember->GetDataId() ); 2966 pResultMember->LateInitFrom( rParams, pItemData, nPos+1, rInitState ); 2967 rInitState.RemoveMember(); 2968 } 2969 } 2970 } 2971 else 2972 InitWithMembers( rParams, pItemData, nPos, rInitState ); 2973 } 2974 2975 long ScDPResultDimension::GetSize(long nMeasure) const 2976 { 2977 long nTotal = 0; 2978 long nMemberCount = maMemberArray.size(); 2979 if (bIsDataLayout) 2980 { 2981 DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1, 2982 "DataLayout dimension twice?"); 2983 // repeat first member... 2984 nTotal = nMemberCount * maMemberArray[0]->GetSize(0); // all measures have equal size 2985 } 2986 else 2987 { 2988 // add all members 2989 for (long nMem=0; nMem<nMemberCount; nMem++) 2990 nTotal += maMemberArray[nMem]->GetSize(nMeasure); 2991 } 2992 return nTotal; 2993 } 2994 2995 bool ScDPResultDimension::IsValidEntry( const vector< SCROW >& aMembers ) const 2996 { 2997 if (aMembers.empty()) 2998 return false; 2999 3000 const ScDPResultMember* pMember = FindMember( aMembers[0] ); 3001 if ( NULL != pMember ) 3002 return pMember->IsValidEntry( aMembers ); 3003 #ifdef DBG_UTIL 3004 ByteString strTemp ("IsValidEntry: Member not found, DimName = " ); 3005 strTemp += ByteString( GetName(), RTL_TEXTENCODING_UTF8 ); 3006 DBG_TRACE( strTemp.GetBuffer() ); 3007 // DBG_ERROR("IsValidEntry: Member not found"); 3008 #endif 3009 return false; 3010 } 3011 3012 void ScDPResultDimension::ProcessData( const vector< SCROW >& aMembers, 3013 const ScDPResultDimension* pDataDim, 3014 const vector< SCROW >& aDataMembers, 3015 const vector<ScDPValueData>& aValues ) const 3016 { 3017 if (aMembers.empty()) 3018 return; 3019 3020 ScDPResultMember* pMember = FindMember( aMembers[0] ); 3021 if ( NULL != pMember ) 3022 { 3023 vector</*ScDPItemData*/SCROW > aChildMembers; 3024 if (aMembers.size() > 1) 3025 { 3026 vector</*ScDPItemData*/SCROW >::const_iterator itr = aMembers.begin(); 3027 aChildMembers.insert(aChildMembers.begin(), ++itr, aMembers.end()); 3028 } 3029 pMember->ProcessData( aChildMembers, pDataDim, aDataMembers, aValues ); 3030 return; 3031 } 3032 3033 DBG_ERROR("ProcessData: Member not found"); 3034 } 3035 3036 void ScDPResultDimension::FillMemberResults( uno::Sequence<sheet::MemberResult>* pSequences, 3037 long nStart, long nMeasure ) 3038 { 3039 long nPos = nStart; 3040 long nCount = maMemberArray.size(); 3041 3042 for (long i=0; i<nCount; i++) 3043 { 3044 long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i]; 3045 3046 ScDPResultMember* pMember = maMemberArray[nSorted]; 3047 // in data layout dimension, use first member with different measures/names 3048 if ( bIsDataLayout ) 3049 { 3050 bool bTotalResult = false; 3051 String aMbrName = pResultData->GetMeasureDimensionName( nSorted ); 3052 String aMbrCapt = pResultData->GetMeasureString( nSorted, sal_False, SUBTOTAL_FUNC_NONE, bTotalResult ); 3053 maMemberArray[0]->FillMemberResults( pSequences, nPos, nSorted, sal_False, &aMbrName, &aMbrCapt ); 3054 } 3055 else if ( pMember->IsVisible() ) 3056 pMember->FillMemberResults( pSequences, nPos, nMeasure, sal_False, NULL, NULL ); 3057 // nPos is modified 3058 } 3059 } 3060 3061 void ScDPResultDimension::FillDataResults( const ScDPResultMember* pRefMember, 3062 uno::Sequence< uno::Sequence<sheet::DataResult> >& rSequence, 3063 long nRow, long nMeasure ) const 3064 { 3065 long nMemberRow = nRow; 3066 long nMemberMeasure = nMeasure; 3067 long nCount = maMemberArray.size(); 3068 for (long i=0; i<nCount; i++) 3069 { 3070 long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i]; 3071 3072 const ScDPResultMember* pMember; 3073 if (bIsDataLayout) 3074 { 3075 DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1, 3076 "DataLayout dimension twice?"); 3077 pMember = maMemberArray[0]; 3078 nMemberMeasure = nSorted; 3079 } 3080 else 3081 pMember = maMemberArray[nSorted]; 3082 3083 if ( pMember->IsVisible() ) 3084 pMember->FillDataResults( pRefMember, rSequence, nMemberRow, nMemberMeasure ); 3085 // nMemberRow is modified 3086 } 3087 } 3088 3089 void ScDPResultDimension::UpdateDataResults( const ScDPResultMember* pRefMember, long nMeasure ) const 3090 { 3091 long nMemberMeasure = nMeasure; 3092 long nCount = maMemberArray.size(); 3093 for (long i=0; i<nCount; i++) 3094 { 3095 const ScDPResultMember* pMember; 3096 if (bIsDataLayout) 3097 { 3098 DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1, 3099 "DataLayout dimension twice?"); 3100 pMember = maMemberArray[0]; 3101 nMemberMeasure = i; 3102 } 3103 else 3104 pMember = maMemberArray[i]; 3105 3106 if ( pMember->IsVisible() ) 3107 pMember->UpdateDataResults( pRefMember, nMemberMeasure ); 3108 } 3109 } 3110 3111 void ScDPResultDimension::SortMembers( ScDPResultMember* pRefMember ) 3112 { 3113 long nCount = maMemberArray.size(); 3114 3115 if ( bSortByData ) 3116 { 3117 // sort members 3118 3119 DBG_ASSERT( aMemberOrder.empty(), "sort twice?" ); 3120 aMemberOrder.resize( nCount ); 3121 for (long nPos=0; nPos<nCount; nPos++) 3122 aMemberOrder[nPos] = nPos; 3123 3124 ScDPRowMembersOrder aComp( *this, nSortMeasure, bSortAscending ); 3125 ::std::sort( aMemberOrder.begin(), aMemberOrder.end(), aComp ); 3126 } 3127 3128 // handle children 3129 3130 // for data layout, call only once - sorting measure is always taken from settings 3131 long nLoopCount = bIsDataLayout ? 1 : nCount; 3132 for (long i=0; i<nLoopCount; i++) 3133 { 3134 ScDPResultMember* pMember = maMemberArray[i]; 3135 if ( pMember->IsVisible() ) 3136 pMember->SortMembers( pRefMember ); 3137 } 3138 } 3139 3140 void ScDPResultDimension::DoAutoShow( ScDPResultMember* pRefMember ) 3141 { 3142 long nCount = maMemberArray.size(); 3143 3144 // handle children first, before changing the visible state 3145 3146 // for data layout, call only once - sorting measure is always taken from settings 3147 long nLoopCount = bIsDataLayout ? 1 : nCount; 3148 for (long i=0; i<nLoopCount; i++) 3149 { 3150 ScDPResultMember* pMember = maMemberArray[i]; 3151 if ( pMember->IsVisible() ) 3152 pMember->DoAutoShow( pRefMember ); 3153 } 3154 3155 if ( bAutoShow && nAutoCount > 0 && nAutoCount < nCount ) 3156 { 3157 // establish temporary order, hide remaining members 3158 3159 ScMemberSortOrder aAutoOrder; 3160 aAutoOrder.resize( nCount ); 3161 long nPos; 3162 for (nPos=0; nPos<nCount; nPos++) 3163 aAutoOrder[nPos] = nPos; 3164 3165 ScDPRowMembersOrder aComp( *this, nAutoMeasure, !bAutoTopItems ); 3166 ::std::sort( aAutoOrder.begin(), aAutoOrder.end(), aComp ); 3167 3168 // look for equal values to the last included one 3169 3170 long nIncluded = nAutoCount; 3171 const ScDPResultMember* pMember1 = maMemberArray[aAutoOrder[nIncluded - 1]]; 3172 const ScDPDataMember* pDataMember1 = pMember1->IsVisible() ? pMember1->GetDataRoot() : NULL; 3173 sal_Bool bContinue = sal_True; 3174 while ( bContinue ) 3175 { 3176 bContinue = sal_False; 3177 if ( nIncluded < nCount ) 3178 { 3179 const ScDPResultMember* pMember2 = maMemberArray[aAutoOrder[nIncluded]]; 3180 const ScDPDataMember* pDataMember2 = pMember2->IsVisible() ? pMember2->GetDataRoot() : NULL; 3181 3182 if ( lcl_IsEqual( pDataMember1, pDataMember2, nAutoMeasure ) ) 3183 { 3184 ++nIncluded; // include more members if values are equal 3185 bContinue = sal_True; 3186 } 3187 } 3188 } 3189 3190 // hide the remaining members 3191 3192 for (nPos = nIncluded; nPos < nCount; nPos++) 3193 { 3194 ScDPResultMember* pMember = maMemberArray[aAutoOrder[nPos]]; 3195 pMember->SetAutoHidden(); 3196 } 3197 } 3198 } 3199 3200 void ScDPResultDimension::ResetResults() 3201 { 3202 long nCount = maMemberArray.size(); 3203 for (long i=0; i<nCount; i++) 3204 { 3205 // sort order doesn't matter 3206 ScDPResultMember* pMember = maMemberArray[bIsDataLayout ? 0 : i]; 3207 pMember->ResetResults( sal_False ); 3208 } 3209 } 3210 3211 long ScDPResultDimension::GetSortedIndex( long nUnsorted ) const 3212 { 3213 return aMemberOrder.empty() ? nUnsorted : aMemberOrder[nUnsorted]; 3214 } 3215 3216 void ScDPResultDimension::UpdateRunningTotals( const ScDPResultMember* pRefMember, long nMeasure, 3217 ScDPRunningTotalState& rRunning, ScDPRowTotals& rTotals ) const 3218 { 3219 const ScDPResultMember* pMember; 3220 long nMemberMeasure = nMeasure; 3221 long nCount = maMemberArray.size(); 3222 for (long i=0; i<nCount; i++) 3223 { 3224 long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i]; 3225 3226 if (bIsDataLayout) 3227 { 3228 DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1, 3229 "DataLayout dimension twice?"); 3230 pMember = maMemberArray[0]; 3231 nMemberMeasure = nSorted; 3232 } 3233 else 3234 pMember = maMemberArray[nSorted]; 3235 3236 if ( pMember->IsVisible() ) 3237 { 3238 if ( bIsDataLayout ) 3239 rRunning.AddRowIndex( 0, 0 ); 3240 else 3241 rRunning.AddRowIndex( i, nSorted ); 3242 pMember->UpdateRunningTotals( pRefMember, nMemberMeasure, rRunning, rTotals ); 3243 rRunning.RemoveRowIndex(); 3244 } 3245 } 3246 } 3247 3248 ScDPDataMember* ScDPResultDimension::GetRowReferenceMember( const ScDPRelativePos* pRelativePos, const String* pName, 3249 const long* pRowIndexes, const long* pColIndexes ) const 3250 { 3251 // get named, previous/next, or first member of this dimension (first existing if pRelativePos and pName are NULL) 3252 3253 DBG_ASSERT( pRelativePos == NULL || pName == NULL, "can't use position and name" ); 3254 3255 ScDPDataMember* pColMember = NULL; 3256 3257 sal_Bool bFirstExisting = ( pRelativePos == NULL && pName == NULL ); 3258 long nMemberCount = maMemberArray.size(); 3259 long nMemberIndex = 0; // unsorted 3260 long nDirection = 1; // forward if no relative position is used 3261 if ( pRelativePos ) 3262 { 3263 nDirection = pRelativePos->nDirection; 3264 nMemberIndex = pRelativePos->nBasePos + nDirection; // bounds are handled below 3265 3266 DBG_ASSERT( nDirection == 1 || nDirection == -1, "Direction must be 1 or -1" ); 3267 } 3268 else if ( pName ) 3269 { 3270 // search for named member 3271 3272 const ScDPResultMember* pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)]; 3273 3274 //! use ScDPItemData, as in ScDPDimension::IsValidPage? 3275 while ( pRowMember && pRowMember->GetName() != *pName ) 3276 { 3277 ++nMemberIndex; 3278 if ( nMemberIndex < nMemberCount ) 3279 pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)]; 3280 else 3281 pRowMember = NULL; 3282 } 3283 } 3284 3285 sal_Bool bContinue = sal_True; 3286 while ( bContinue && nMemberIndex >= 0 && nMemberIndex < nMemberCount ) 3287 { 3288 const ScDPResultMember* pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)]; 3289 3290 // get child members by given indexes 3291 3292 const long* pNextRowIndex = pRowIndexes; 3293 while ( *pNextRowIndex >= 0 && pRowMember ) 3294 { 3295 const ScDPResultDimension* pRowChild = pRowMember->GetChildDimension(); 3296 if ( pRowChild && *pNextRowIndex < pRowChild->GetMemberCount() ) 3297 pRowMember = pRowChild->GetMember( *pNextRowIndex ); 3298 else 3299 pRowMember = NULL; 3300 ++pNextRowIndex; 3301 } 3302 3303 if ( pRowMember && pRelativePos ) 3304 { 3305 // Skip the member if it has hidden details 3306 // (because when looking for the details, it is skipped, too). 3307 // Also skip if the member is invisible because it has no data, 3308 // for consistent ordering. 3309 if ( pRowMember->HasHiddenDetails() || !pRowMember->IsVisible() ) 3310 pRowMember = NULL; 3311 } 3312 3313 if ( pRowMember ) 3314 { 3315 pColMember = pRowMember->GetDataRoot(); 3316 3317 const long* pNextColIndex = pColIndexes; 3318 while ( *pNextColIndex >= 0 && pColMember ) 3319 { 3320 const ScDPDataDimension* pColChild = pColMember->GetChildDimension(); 3321 if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() ) 3322 pColMember = pColChild->GetMember( *pNextColIndex ); 3323 else 3324 pColMember = NULL; 3325 ++pNextColIndex; 3326 } 3327 } 3328 3329 // continue searching only if looking for first existing or relative position 3330 bContinue = ( pColMember == NULL && ( bFirstExisting || pRelativePos ) ); 3331 nMemberIndex += nDirection; 3332 } 3333 3334 return pColMember; 3335 } 3336 3337 // static 3338 ScDPDataMember* ScDPResultDimension::GetColReferenceMember( const ScDPRelativePos* pRelativePos, const String* pName, 3339 long nRefDimPos, const ScDPRunningTotalState& rRunning ) 3340 { 3341 DBG_ASSERT( pRelativePos == NULL || pName == NULL, "can't use position and name" ); 3342 3343 const long* pColIndexes = rRunning.GetColIndexes(); 3344 const long* pRowIndexes = rRunning.GetRowIndexes(); 3345 3346 // get own row member using all indexes 3347 3348 const ScDPResultMember* pRowMember = rRunning.GetRowResRoot(); 3349 ScDPDataMember* pColMember = NULL; 3350 3351 const long* pNextRowIndex = pRowIndexes; 3352 while ( *pNextRowIndex >= 0 && pRowMember ) 3353 { 3354 const ScDPResultDimension* pRowChild = pRowMember->GetChildDimension(); 3355 if ( pRowChild && *pNextRowIndex < pRowChild->GetMemberCount() ) 3356 pRowMember = pRowChild->GetMember( *pNextRowIndex ); 3357 else 3358 pRowMember = NULL; 3359 ++pNextRowIndex; 3360 } 3361 3362 // get column (data) members before the reference field 3363 //! pass rRowParent from ScDPDataMember::UpdateRunningTotals instead 3364 3365 if ( pRowMember ) 3366 { 3367 pColMember = pRowMember->GetDataRoot(); 3368 3369 const long* pNextColIndex = pColIndexes; 3370 long nColSkipped = 0; 3371 while ( *pNextColIndex >= 0 && pColMember && nColSkipped < nRefDimPos ) 3372 { 3373 const ScDPDataDimension* pColChild = pColMember->GetChildDimension(); 3374 if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() ) 3375 pColMember = pColChild->GetMember( *pNextColIndex ); 3376 else 3377 pColMember = NULL; 3378 ++pNextColIndex; 3379 ++nColSkipped; 3380 } 3381 } 3382 3383 // get column member for the reference field 3384 3385 if ( pColMember ) 3386 { 3387 const ScDPDataDimension* pReferenceDim = pColMember->GetChildDimension(); 3388 if ( pReferenceDim ) 3389 { 3390 long nReferenceCount = pReferenceDim->GetMemberCount(); 3391 3392 sal_Bool bFirstExisting = ( pRelativePos == NULL && pName == NULL ); 3393 long nMemberIndex = 0; // unsorted 3394 long nDirection = 1; // forward if no relative position is used 3395 pColMember = NULL; // don't use parent dimension's member if none found 3396 if ( pRelativePos ) 3397 { 3398 nDirection = pRelativePos->nDirection; 3399 nMemberIndex = pRelativePos->nBasePos + nDirection; // bounds are handled below 3400 } 3401 else if ( pName ) 3402 { 3403 // search for named member 3404 3405 pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) ); 3406 3407 //! use ScDPItemData, as in ScDPDimension::IsValidPage? 3408 while ( pColMember && pColMember->GetName() != *pName ) 3409 { 3410 ++nMemberIndex; 3411 if ( nMemberIndex < nReferenceCount ) 3412 pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) ); 3413 else 3414 pColMember = NULL; 3415 } 3416 } 3417 3418 sal_Bool bContinue = sal_True; 3419 while ( bContinue && nMemberIndex >= 0 && nMemberIndex < nReferenceCount ) 3420 { 3421 pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) ); 3422 3423 // get column members below the reference field 3424 3425 const long* pNextColIndex = pColIndexes + nRefDimPos + 1; 3426 while ( *pNextColIndex >= 0 && pColMember ) 3427 { 3428 const ScDPDataDimension* pColChild = pColMember->GetChildDimension(); 3429 if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() ) 3430 pColMember = pColChild->GetMember( *pNextColIndex ); 3431 else 3432 pColMember = NULL; 3433 ++pNextColIndex; 3434 } 3435 3436 if ( pColMember && pRelativePos ) 3437 { 3438 // Skip the member if it has hidden details 3439 // (because when looking for the details, it is skipped, too). 3440 // Also skip if the member is invisible because it has no data, 3441 // for consistent ordering. 3442 if ( pColMember->HasHiddenDetails() || !pColMember->IsVisible() ) 3443 pColMember = NULL; 3444 } 3445 3446 // continue searching only if looking for first existing or relative position 3447 bContinue = ( pColMember == NULL && ( bFirstExisting || pRelativePos ) ); 3448 nMemberIndex += nDirection; 3449 } 3450 } 3451 else 3452 pColMember = NULL; 3453 } 3454 3455 return pColMember; 3456 } 3457 3458 void ScDPResultDimension::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const 3459 { 3460 String aDimName = bIsDataLayout ? String::CreateFromAscii("(data layout)") : GetName(); 3461 lcl_DumpRow( String::CreateFromAscii("ScDPResultDimension"), aDimName, NULL, pDoc, rPos ); 3462 3463 SCROW nStartRow = rPos.Row(); 3464 3465 long nCount = bIsDataLayout ? 1 : maMemberArray.size(); 3466 for (long i=0; i<nCount; i++) 3467 { 3468 const ScDPResultMember* pMember = maMemberArray[i]; 3469 pMember->DumpState( pRefMember, pDoc, rPos ); 3470 } 3471 3472 lcl_Indent( pDoc, nStartRow, rPos ); 3473 } 3474 3475 long ScDPResultDimension::GetMemberCount() const 3476 { 3477 return maMemberArray.size(); 3478 } 3479 3480 const ScDPResultMember* ScDPResultDimension::GetMember(long n) const 3481 { 3482 return maMemberArray[n]; 3483 } 3484 ScDPResultMember* ScDPResultDimension::GetMember(long n) 3485 { 3486 return maMemberArray[n]; 3487 } 3488 3489 ScDPResultDimension* ScDPResultDimension::GetFirstChildDimension() const 3490 { 3491 if ( maMemberArray.size() > 0 ) 3492 return maMemberArray[0]->GetChildDimension(); 3493 else 3494 return NULL; 3495 } 3496 3497 void ScDPResultDimension::FillVisibilityData(ScDPResultVisibilityData& rData) const 3498 { 3499 if (IsDataLayout()) 3500 return; 3501 3502 MemberArray::const_iterator itr = maMemberArray.begin(), itrEnd = maMemberArray.end(); 3503 3504 for (;itr != itrEnd; ++itr) 3505 { 3506 ScDPResultMember* pMember = *itr; 3507 if (pMember->IsValid()) 3508 { 3509 ScDPItemData aItem; 3510 pMember->FillItemData(aItem); 3511 rData.addVisibleMember(GetName(), aItem); 3512 pMember->FillVisibilityData(rData); 3513 } 3514 } 3515 } 3516 3517 // ----------------------------------------------------------------------- 3518 3519 ScDPDataDimension::ScDPDataDimension( const ScDPResultData* pData ) : 3520 pResultData( pData ), 3521 pResultDimension( NULL ), 3522 bIsDataLayout( sal_False ) 3523 { 3524 } 3525 3526 ScDPDataDimension::~ScDPDataDimension() 3527 { 3528 } 3529 3530 void ScDPDataDimension::InitFrom( const ScDPResultDimension* pDim ) 3531 { 3532 if (!pDim) 3533 return; 3534 3535 pResultDimension = pDim; 3536 bIsDataLayout = pDim->IsDataLayout(); 3537 3538 // Go through all result members under the given result dimension, and 3539 // create a new data member instance for each result member. 3540 long nCount = pDim->GetMemberCount(); 3541 for (long i=0; i<nCount; i++) 3542 { 3543 const ScDPResultMember* pResMem = pDim->GetMember(i); 3544 3545 ScDPDataMember* pNew = new ScDPDataMember( pResultData, pResMem ); 3546 aMembers.Insert( pNew, aMembers.Count() ); 3547 3548 if ( !pResultData->IsLateInit() ) 3549 { 3550 // with LateInit, pResMem hasn't necessarily been initialized yet, 3551 // so InitFrom for the new result member is called from its ProcessData method 3552 3553 const ScDPResultDimension* pChildDim = pResMem->GetChildDimension(); 3554 if ( pChildDim ) 3555 pNew->InitFrom( pChildDim ); 3556 } 3557 } 3558 } 3559 3560 void ScDPDataDimension::ProcessData( const vector< SCROW >& aDataMembers, const vector<ScDPValueData>& aValues, 3561 const ScDPSubTotalState& rSubState ) 3562 { 3563 // the ScDPItemData array must contain enough entries for all dimensions - this isn't checked 3564 3565 long nCount = aMembers.Count(); 3566 for (long i=0; i<nCount; i++) 3567 { 3568 ScDPDataMember* pMember = aMembers[(sal_uInt16)i]; 3569 3570 // always first member for data layout dim 3571 if ( bIsDataLayout || ( !aDataMembers.empty() && pMember->IsNamedItem(aDataMembers[0]) ) ) 3572 { 3573 vector</*ScDPItemData*/SCROW> aChildDataMembers; 3574 if (aDataMembers.size() > 1) 3575 { 3576 vector</*ScDPItemData*/SCROW >::const_iterator itr = aDataMembers.begin(); 3577 aChildDataMembers.insert(aChildDataMembers.begin(), ++itr, aDataMembers.end()); 3578 } 3579 pMember->ProcessData( aChildDataMembers, aValues, rSubState ); 3580 return; 3581 } 3582 } 3583 3584 DBG_ERROR("ProcessData: Member not found"); 3585 } 3586 3587 void ScDPDataDimension::FillDataRow( const ScDPResultDimension* pRefDim, 3588 uno::Sequence<sheet::DataResult>& rSequence, 3589 long nCol, long nMeasure, sal_Bool bIsSubTotalRow, 3590 const ScDPSubTotalState& rSubState ) const 3591 { 3592 DBG_ASSERT( pRefDim && pRefDim->GetMemberCount() == aMembers.Count(), "dimensions don't match" ); 3593 DBG_ASSERT( pRefDim == pResultDimension, "wrong dim" ); 3594 3595 const ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder(); 3596 3597 long nMemberMeasure = nMeasure; 3598 long nMemberCol = nCol; 3599 long nCount = aMembers.Count(); 3600 for (long i=0; i<nCount; i++) 3601 { 3602 long nSorted = rMemberOrder.empty() ? i : rMemberOrder[i]; 3603 3604 long nMemberPos = nSorted; 3605 if (bIsDataLayout) 3606 { 3607 DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1, 3608 "DataLayout dimension twice?"); 3609 nMemberPos = 0; 3610 nMemberMeasure = nSorted; 3611 } 3612 3613 const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos); 3614 if ( pRefMember->IsVisible() ) //! here or in ScDPDataMember::FillDataRow ??? 3615 { 3616 const ScDPDataMember* pDataMember = aMembers[(sal_uInt16)nMemberPos]; 3617 pDataMember->FillDataRow( pRefMember, rSequence, nMemberCol, nMemberMeasure, bIsSubTotalRow, rSubState ); 3618 // nMemberCol is modified 3619 } 3620 } 3621 } 3622 3623 void ScDPDataDimension::UpdateDataRow( const ScDPResultDimension* pRefDim, 3624 long nMeasure, sal_Bool bIsSubTotalRow, 3625 const ScDPSubTotalState& rSubState ) const 3626 { 3627 DBG_ASSERT( pRefDim && pRefDim->GetMemberCount() == aMembers.Count(), "dimensions don't match" ); 3628 DBG_ASSERT( pRefDim == pResultDimension, "wrong dim" ); 3629 3630 long nMemberMeasure = nMeasure; 3631 long nCount = aMembers.Count(); 3632 for (long i=0; i<nCount; i++) 3633 { 3634 long nMemberPos = i; 3635 if (bIsDataLayout) 3636 { 3637 DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1, 3638 "DataLayout dimension twice?"); 3639 nMemberPos = 0; 3640 nMemberMeasure = i; 3641 } 3642 3643 // Calculate must be called even if the member is not visible (for use as reference value) 3644 const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos); 3645 ScDPDataMember* pDataMember = aMembers[(sal_uInt16)nMemberPos]; 3646 pDataMember->UpdateDataRow( pRefMember, nMemberMeasure, bIsSubTotalRow, rSubState ); 3647 } 3648 } 3649 3650 void ScDPDataDimension::SortMembers( ScDPResultDimension* pRefDim ) 3651 { 3652 long nCount = aMembers.Count(); 3653 3654 if ( pRefDim->IsSortByData() ) 3655 { 3656 // sort members 3657 3658 ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder(); 3659 DBG_ASSERT( rMemberOrder.empty(), "sort twice?" ); 3660 rMemberOrder.resize( nCount ); 3661 for (long nPos=0; nPos<nCount; nPos++) 3662 rMemberOrder[nPos] = nPos; 3663 3664 ScDPColMembersOrder aComp( *this, pRefDim->GetSortMeasure(), pRefDim->IsSortAscending() ); 3665 ::std::sort( rMemberOrder.begin(), rMemberOrder.end(), aComp ); 3666 } 3667 3668 // handle children 3669 3670 DBG_ASSERT( pRefDim && pRefDim->GetMemberCount() == aMembers.Count(), "dimensions don't match" ); 3671 DBG_ASSERT( pRefDim == pResultDimension, "wrong dim" ); 3672 3673 // for data layout, call only once - sorting measure is always taken from settings 3674 long nLoopCount = bIsDataLayout ? 1 : nCount; 3675 for (long i=0; i<nLoopCount; i++) 3676 { 3677 ScDPResultMember* pRefMember = pRefDim->GetMember(i); 3678 if ( pRefMember->IsVisible() ) //! here or in ScDPDataMember ??? 3679 { 3680 ScDPDataMember* pDataMember = aMembers[(sal_uInt16)i]; 3681 pDataMember->SortMembers( pRefMember ); 3682 } 3683 } 3684 } 3685 3686 void ScDPDataDimension::DoAutoShow( ScDPResultDimension* pRefDim ) 3687 { 3688 long nCount = aMembers.Count(); 3689 3690 // handle children first, before changing the visible state 3691 3692 DBG_ASSERT( pRefDim && pRefDim->GetMemberCount() == aMembers.Count(), "dimensions don't match" ); 3693 DBG_ASSERT( pRefDim == pResultDimension, "wrong dim" ); 3694 3695 // for data layout, call only once - sorting measure is always taken from settings 3696 long nLoopCount = bIsDataLayout ? 1 : nCount; 3697 for (long i=0; i<nLoopCount; i++) 3698 { 3699 ScDPResultMember* pRefMember = pRefDim->GetMember(i); 3700 if ( pRefMember->IsVisible() ) //! here or in ScDPDataMember ??? 3701 { 3702 ScDPDataMember* pDataMember = aMembers[(sal_uInt16)i]; 3703 pDataMember->DoAutoShow( pRefMember ); 3704 } 3705 } 3706 3707 if ( pRefDim->IsAutoShow() && pRefDim->GetAutoCount() > 0 && pRefDim->GetAutoCount() < nCount ) 3708 { 3709 // establish temporary order, hide remaining members 3710 3711 ScMemberSortOrder aAutoOrder; 3712 aAutoOrder.resize( nCount ); 3713 long nPos; 3714 for (nPos=0; nPos<nCount; nPos++) 3715 aAutoOrder[nPos] = nPos; 3716 3717 ScDPColMembersOrder aComp( *this, pRefDim->GetAutoMeasure(), !pRefDim->IsAutoTopItems() ); 3718 ::std::sort( aAutoOrder.begin(), aAutoOrder.end(), aComp ); 3719 3720 // look for equal values to the last included one 3721 3722 long nIncluded = pRefDim->GetAutoCount(); 3723 ScDPDataMember* pDataMember1 = aMembers[(sal_uInt16)aAutoOrder[nIncluded - 1]]; 3724 if ( !pDataMember1->IsVisible() ) 3725 pDataMember1 = NULL; 3726 sal_Bool bContinue = sal_True; 3727 while ( bContinue ) 3728 { 3729 bContinue = sal_False; 3730 if ( nIncluded < nCount ) 3731 { 3732 ScDPDataMember* pDataMember2 = aMembers[(sal_uInt16)aAutoOrder[nIncluded]]; 3733 if ( !pDataMember2->IsVisible() ) 3734 pDataMember2 = NULL; 3735 3736 if ( lcl_IsEqual( pDataMember1, pDataMember2, pRefDim->GetAutoMeasure() ) ) 3737 { 3738 ++nIncluded; // include more members if values are equal 3739 bContinue = sal_True; 3740 } 3741 } 3742 } 3743 3744 // hide the remaining members 3745 3746 for (nPos = nIncluded; nPos < nCount; nPos++) 3747 { 3748 ScDPResultMember* pMember = pRefDim->GetMember(aAutoOrder[nPos]); 3749 pMember->SetAutoHidden(); 3750 } 3751 } 3752 } 3753 3754 void ScDPDataDimension::ResetResults() 3755 { 3756 long nCount = aMembers.Count(); 3757 for (long i=0; i<nCount; i++) 3758 { 3759 // sort order doesn't matter 3760 3761 long nMemberPos = bIsDataLayout ? 0 : i; 3762 ScDPDataMember* pDataMember = aMembers[(sal_uInt16)nMemberPos]; 3763 pDataMember->ResetResults(); 3764 } 3765 } 3766 3767 long ScDPDataDimension::GetSortedIndex( long nUnsorted ) const 3768 { 3769 if (!pResultDimension) 3770 return nUnsorted; 3771 3772 const ScMemberSortOrder& rMemberOrder = pResultDimension->GetMemberOrder(); 3773 return rMemberOrder.empty() ? nUnsorted : rMemberOrder[nUnsorted]; 3774 } 3775 3776 void ScDPDataDimension::UpdateRunningTotals( const ScDPResultDimension* pRefDim, 3777 long nMeasure, sal_Bool bIsSubTotalRow, 3778 const ScDPSubTotalState& rSubState, ScDPRunningTotalState& rRunning, 3779 ScDPRowTotals& rTotals, const ScDPResultMember& rRowParent ) const 3780 { 3781 DBG_ASSERT( pRefDim && pRefDim->GetMemberCount() == aMembers.Count(), "dimensions don't match" ); 3782 DBG_ASSERT( pRefDim == pResultDimension, "wrong dim" ); 3783 3784 long nMemberMeasure = nMeasure; 3785 long nCount = aMembers.Count(); 3786 for (long i=0; i<nCount; i++) 3787 { 3788 const ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder(); 3789 long nSorted = rMemberOrder.empty() ? i : rMemberOrder[i]; 3790 3791 long nMemberPos = nSorted; 3792 if (bIsDataLayout) 3793 { 3794 DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1, 3795 "DataLayout dimension twice?"); 3796 nMemberPos = 0; 3797 nMemberMeasure = nSorted; 3798 } 3799 3800 const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos); 3801 if ( pRefMember->IsVisible() ) //! here or in ScDPDataMember::UpdateRunningTotals ??? 3802 { 3803 if ( bIsDataLayout ) 3804 rRunning.AddColIndex( 0, 0 ); 3805 else 3806 rRunning.AddColIndex( i, nSorted ); 3807 3808 ScDPDataMember* pDataMember = aMembers[(sal_uInt16)nMemberPos]; 3809 pDataMember->UpdateRunningTotals( pRefMember, nMemberMeasure, 3810 bIsSubTotalRow, rSubState, rRunning, rTotals, rRowParent ); 3811 3812 rRunning.RemoveColIndex(); 3813 } 3814 } 3815 } 3816 3817 void ScDPDataDimension::DumpState( const ScDPResultDimension* pRefDim, ScDocument* pDoc, ScAddress& rPos ) const 3818 { 3819 String aDimName = String::CreateFromAscii( bIsDataLayout ? "(data layout)" : "(unknown)" ); 3820 lcl_DumpRow( String::CreateFromAscii("ScDPDataDimension"), aDimName, NULL, pDoc, rPos ); 3821 3822 SCROW nStartRow = rPos.Row(); 3823 3824 long nCount = bIsDataLayout ? 1 : aMembers.Count(); 3825 for (long i=0; i<nCount; i++) 3826 { 3827 const ScDPResultMember* pRefMember = pRefDim->GetMember(i); 3828 const ScDPDataMember* pDataMember = aMembers[(sal_uInt16)i]; 3829 pDataMember->DumpState( pRefMember, pDoc, rPos ); 3830 } 3831 3832 lcl_Indent( pDoc, nStartRow, rPos ); 3833 } 3834 3835 long ScDPDataDimension::GetMemberCount() const 3836 { 3837 return aMembers.Count(); 3838 } 3839 3840 ScDPDataMember* ScDPDataDimension::GetMember(long n) const 3841 { 3842 return aMembers[(sal_uInt16)n]; 3843 } 3844 3845 // ---------------------------------------------------------------------------- 3846 3847 ScDPResultVisibilityData::ScDPResultVisibilityData( 3848 ScDPSource* pSource) : 3849 mpSource(pSource) 3850 { 3851 } 3852 3853 ScDPResultVisibilityData::~ScDPResultVisibilityData() 3854 { 3855 } 3856 3857 void ScDPResultVisibilityData::addVisibleMember(const String& rDimName, const ScDPItemData& rMemberItem) 3858 { 3859 DimMemberType::iterator itr = maDimensions.find(rDimName); 3860 if (itr == maDimensions.end()) 3861 { 3862 pair<DimMemberType::iterator, bool> r = maDimensions.insert( 3863 DimMemberType::value_type(rDimName, VisibleMemberType())); 3864 3865 if (!r.second) 3866 // insertion failed. 3867 return; 3868 3869 itr = r.first; 3870 } 3871 VisibleMemberType& rMem = itr->second; 3872 VisibleMemberType::iterator itrMem = rMem.find(rMemberItem); 3873 if (itrMem == rMem.end()) 3874 rMem.insert(rMemberItem); 3875 } 3876 3877 void ScDPResultVisibilityData::fillFieldFilters(vector<ScDPCacheTable::Criterion>& rFilters) const 3878 { 3879 typedef hash_map<String, long, ScStringHashCode> FieldNameMapType; 3880 FieldNameMapType aFieldNames; 3881 ScDPTableData* pData = mpSource->GetData(); 3882 long nColumnCount = pData->GetColumnCount(); 3883 for (long i = 0; i < nColumnCount; ++i) 3884 { 3885 aFieldNames.insert( 3886 FieldNameMapType::value_type(pData->getDimensionName(i), i)); 3887 } 3888 3889 const ScDPDimensions* pDims = mpSource->GetDimensionsObject(); 3890 for (DimMemberType::const_iterator itr = maDimensions.begin(), itrEnd = maDimensions.end(); 3891 itr != itrEnd; ++itr) 3892 { 3893 const String& rDimName = itr->first; 3894 ScDPCacheTable::Criterion aCri; 3895 FieldNameMapType::const_iterator itrField = aFieldNames.find(rDimName); 3896 if (itrField == aFieldNames.end()) 3897 // This should never happen! 3898 continue; 3899 3900 long nDimIndex = itrField->second; 3901 aCri.mnFieldIndex = static_cast<sal_Int32>(nDimIndex); 3902 aCri.mpFilter.reset(new ScDPCacheTable::GroupFilter(/*mrSharedString*/)); 3903 3904 ScDPCacheTable::GroupFilter* pGrpFilter = 3905 static_cast<ScDPCacheTable::GroupFilter*>(aCri.mpFilter.get()); 3906 3907 const VisibleMemberType& rMem = itr->second; 3908 for (VisibleMemberType::const_iterator itrMem = rMem.begin(), itrMemEnd = rMem.end(); 3909 itrMem != itrMemEnd; ++itrMem) 3910 { 3911 const ScDPItemData& rMemItem = *itrMem; 3912 pGrpFilter->addMatchItem(rMemItem.GetString(), rMemItem.GetValue(), rMemItem.IsValue()); 3913 } 3914 3915 ScDPDimension* pDim = pDims->getByIndex(nDimIndex); 3916 ScDPMembers* pMembers = pDim->GetHierarchiesObject()->getByIndex(0)-> 3917 GetLevelsObject()->getByIndex(0)->GetMembersObject(); 3918 if (pGrpFilter->getMatchItemCount() < static_cast<size_t>(pMembers->getCount())) 3919 rFilters.push_back(aCri); 3920 } 3921 } 3922 3923 size_t ScDPResultVisibilityData::MemberHash::operator() (const ScDPItemData& r) const 3924 { 3925 if (r.IsValue()) 3926 return static_cast<size_t>(::rtl::math::approxFloor(r.GetValue())); 3927 else 3928 return rtl_ustr_hashCode_WithLength(r.GetString().GetBuffer(), r.GetString().Len()); 3929 } 3930 // Wang Xu Ming -- 2009-6-10 3931 // DataPilot Migration 3932 SCROW ScDPResultMember::GetDataId( ) const 3933 { 3934 const ScDPMember* pMemberDesc = GetDPMember(); 3935 if (pMemberDesc) 3936 return pMemberDesc->GetItemDataId(); 3937 return -1; 3938 } 3939 3940 ScDPResultMember* ScDPResultDimension::AddMember(const ScDPParentDimData &aData ) 3941 { 3942 ScDPResultMember* pMember = new ScDPResultMember( pResultData, aData, sal_False ); 3943 SCROW nDataIndex = pMember->GetDataId(); 3944 maMemberArray.push_back( pMember ); 3945 3946 if ( maMemberHash.end() == maMemberHash.find( nDataIndex ) ) 3947 maMemberHash.insert( std::pair< SCROW, ScDPResultMember *>( nDataIndex, pMember ) ); 3948 return pMember; 3949 } 3950 3951 ResultMembers* ScDPResultDimension::GetResultMember( ScDPDimension* pThisDim, ScDPLevel* pThisLevel ) 3952 { 3953 ResultMembers* pResultMembers = new ResultMembers(); 3954 // global order is used to initialize aMembers, so it doesn't have to be looked at later 3955 const ScMemberSortOrder& rGlobalOrder = pThisLevel->GetGlobalOrder(); 3956 3957 ScDPMembers* pMembers = pThisLevel->GetMembersObject(); 3958 long nMembCount = pMembers->getCount(); 3959 for ( long i=0; i<nMembCount; i++ ) 3960 { 3961 long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i]; 3962 ScDPMember* pMember = pMembers->getByIndex(nSorted); 3963 if ( NULL == pResultMembers->FindMember( pMember->GetItemDataId() ) ) 3964 { 3965 ScDPParentDimData* pNew = new ScDPParentDimData( i, pThisDim, pThisLevel, pMember ); 3966 pResultMembers->InsertMember( pNew ); 3967 } 3968 } 3969 return pResultMembers; 3970 } 3971 3972 ScDPResultMember* ScDPResultDimension::InsertMember(ScDPParentDimData *pMemberData) 3973 { 3974 SCROW nInsert = 0; 3975 if ( !lcl_SearchMember( maMemberArray, pMemberData->mnOrder , nInsert ) ) 3976 { //Member not exist 3977 ScDPResultMember* pNew = new ScDPResultMember( pResultData, *pMemberData, sal_False ); 3978 maMemberArray.insert( maMemberArray.begin()+nInsert, pNew ); 3979 3980 SCROW nDataIndex = pMemberData->mpMemberDesc->GetItemDataId(); 3981 if ( maMemberHash.end() == maMemberHash.find( nDataIndex ) ) 3982 maMemberHash.insert( std::pair< SCROW, ScDPResultMember *>( nDataIndex, pNew ) ); 3983 return pNew; 3984 } 3985 return maMemberArray[ nInsert ]; 3986 } 3987 3988 void ScDPResultDimension:: InitWithMembers( LateInitParams& rParams, 3989 const ::std::vector< SCROW >& pItemData, 3990 size_t nPos, 3991 ScDPInitState& rInitState ) 3992 { 3993 if ( rParams.IsEnd( nPos ) ) 3994 return; 3995 ScDPDimension* pThisDim = rParams.GetDim( nPos ); 3996 ScDPLevel* pThisLevel = rParams.GetLevel( nPos ); 3997 SCROW nDataID = pItemData[nPos]; 3998 3999 if (pThisDim && pThisLevel) 4000 { 4001 long nDimSource = pThisDim->GetDimension(); //! check GetSourceDim? 4002 4003 // create all members at the first call (preserve order) 4004 ResultMembers* pMembers = pResultData->GetDimResultMembers(nDimSource, pThisDim, pThisLevel); 4005 ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource ); 4006 // initialize only specific member (or all if "show empty" flag is set) 4007 ScDPResultMember* pResultMember = NULL; 4008 if ( bInitialized ) 4009 pResultMember = FindMember( nDataID ); 4010 else 4011 bInitialized = sal_True; 4012 4013 if ( pResultMember == NULL ) 4014 { //only insert found item 4015 ScDPParentDimData* pMemberData = pMembers->FindMember( nDataID ); 4016 if ( pMemberData && aCompare.IsIncluded( *( pMemberData->mpMemberDesc ) ) ) 4017 pResultMember = InsertMember( pMemberData ); 4018 } 4019 if ( pResultMember ) 4020 { 4021 // DBG_TRACE( "ScDPResultDimension::InitWithMembers"); 4022 // DBG_TRACESTR( pResultMember->GetDPMember()->GetNameStr()); 4023 rInitState.AddMember( nDimSource, pResultMember->GetDataId() ); 4024 pResultMember->LateInitFrom( rParams /*ppDim, ppLev*/, pItemData, nPos+1 , rInitState ); 4025 rInitState.RemoveMember(); 4026 } 4027 } 4028 } 4029 4030 ScDPParentDimData* ResultMembers::FindMember( const SCROW& nIndex ) const 4031 { 4032 DimMemberHash::const_iterator aRes = maMemberHash.find( nIndex ); 4033 if( aRes != maMemberHash.end()) { 4034 if ( aRes->second->mpMemberDesc && aRes->second->mpMemberDesc->GetItemDataId()==nIndex ) 4035 return aRes->second; 4036 } 4037 return NULL; 4038 } 4039 void ResultMembers::InsertMember( ScDPParentDimData* pNew ) 4040 { 4041 if ( !pNew->mpMemberDesc->getShowDetails() ) 4042 mbHasHideDetailsMember = sal_True; 4043 maMemberHash.insert( std::pair< const SCROW, ScDPParentDimData *>( pNew->mpMemberDesc->GetItemDataId(), pNew ) ); 4044 } 4045 4046 ResultMembers::ResultMembers(): 4047 mbHasHideDetailsMember( sal_False ) 4048 { 4049 } 4050 ResultMembers::~ResultMembers() 4051 { 4052 for ( DimMemberHash::const_iterator iter = maMemberHash.begin(); iter != maMemberHash.end(); iter++ ) 4053 delete iter->second; 4054 } 4055 // ----------------------------------------------------------------------- 4056 LateInitParams::LateInitParams( const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev, sal_Bool bRow, sal_Bool bInitChild, sal_Bool bAllChildren ): 4057 mppDim( ppDim ), 4058 mppLev( ppLev ), 4059 mbRow( bRow ), 4060 mbInitChild( bInitChild ), 4061 mbAllChildren( bAllChildren ) 4062 { 4063 } 4064 4065 LateInitParams::~LateInitParams() 4066 { 4067 } 4068 4069 sal_Bool LateInitParams::IsEnd( size_t nPos ) const 4070 { 4071 return nPos >= mppDim.size(); 4072 } 4073 4074 // End Comments 4075 // Wang Xu Ming -- 2009-8-4 4076 // DataPilot Migration - old defects merge 4077 void ScDPResultDimension::CheckShowEmpty( sal_Bool bShow ) 4078 { 4079 long nCount = maMemberArray.size(); 4080 4081 ScDPResultMember* pMember = NULL; 4082 for (long i=0; i<nCount; i++) 4083 { 4084 pMember = maMemberArray.at(i); 4085 pMember->CheckShowEmpty( bShow ); 4086 } 4087 4088 } 4089 4090 void ScDPResultMember::CheckShowEmpty( sal_Bool bShow ) 4091 { 4092 if ( bHasElements ) 4093 { 4094 ScDPResultDimension* pChildDim = GetChildDimension(); 4095 if (pChildDim ) 4096 pChildDim->CheckShowEmpty(); 4097 } 4098 else if ( IsValid() && bInitialized ) 4099 { 4100 bShow = bShow || ( GetParentLevel() && GetParentLevel()->getShowEmpty() ); 4101 if ( bShow ) 4102 { 4103 SetHasElements(); 4104 ScDPResultDimension* pChildDim = GetChildDimension(); 4105 if (pChildDim ) 4106 pChildDim->CheckShowEmpty( sal_True ); 4107 } 4108 } 4109 }// End Comments 4110