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