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_sw.hxx" 26 27 #include <memory> 28 #include <algorithm> 29 30 #include <com/sun/star/chart/ChartDataRowSource.hpp> 31 #include <com/sun/star/chart2/data/LabelOrigin.hpp> 32 #include <cppuhelper/interfacecontainer.hxx> 33 #include <vos/mutex.hxx> 34 #include <osl/mutex.hxx> 35 #include <vcl/svapp.hxx> 36 #include <svl/zforlist.hxx> // SvNumberFormatter 37 #include <svx/charthelper.hxx> 38 39 #include <tools/link.hxx> 40 41 #include <XMLRangeHelper.hxx> 42 #include <unochart.hxx> 43 #include <swtable.hxx> 44 #include <unoprnms.hxx> 45 #include <unomap.hxx> 46 #include <unomid.h> 47 #include <unocrsr.hxx> 48 #include <unotbl.hxx> 49 #include <doc.hxx> 50 #include <frmfmt.hxx> 51 #include <docsh.hxx> 52 #include <ndole.hxx> 53 #include <swtable.hxx> 54 #include <swtypes.hxx> 55 #ifndef _UNOCORE_HRC 56 #include <unocore.hrc> 57 #endif 58 59 #include <docary.hxx> 60 61 #define SN_DATA_PROVIDER "com.sun.star.chart2.data.DataProvider" 62 #define SN_DATA_SOURCE "com.sun.star.chart2.data.DataSource" 63 #define SN_DATA_SEQUENCE "com.sun.star.chart2.data.DataSequence" 64 #define SN_LABELED_DATA_SEQUENCE "com.sun.star.chart2.data.LabeledDataSequence" 65 66 #define DIRECTION_DONT_KNOW -1 67 #define DIRECTION_HAS_ERROR -2 68 #define DIRECTION_COLS 0 69 #define DIRECTION_ROWS 1 70 71 using namespace ::com::sun::star; 72 using ::rtl::OUString; 73 74 // from unotbl.cxx 75 extern void lcl_GetCellPosition( const String &rCellName, sal_Int32 &rColumn, sal_Int32 &rRow); 76 extern String lcl_GetCellName( sal_Int32 nColumn, sal_Int32 nRow ); 77 extern int lcl_CompareCellsByColFirst( const String &rCellName1, const String &rCellName2 ); 78 extern int lcl_CompareCellsByRowFirst( const String &rCellName1, const String &rCellName2 ); 79 extern int lcl_CompareCellRanges( 80 const String &rRange1StartCell, const String &rRange1EndCell, 81 const String &rRange2StartCell, const String &rRange2EndCell, 82 sal_Bool bCmpColsFirst ); 83 extern void lcl_NormalizeRange( String &rCell1, String &rCell2 ); 84 85 ////////////////////////////////////////////////////////////////////// 86 87 //static 88 void SwChartHelper::DoUpdateAllCharts( SwDoc* pDoc ) 89 { 90 if (!pDoc) 91 return; 92 93 uno::Reference< frame::XModel > xRes; 94 95 SwOLENode *pONd; 96 SwStartNode *pStNd; 97 SwNodeIndex aIdx( *pDoc->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 ); 98 while( 0 != (pStNd = aIdx.GetNode().GetStartNode()) ) 99 { 100 aIdx++; 101 if (0 != ( pONd = aIdx.GetNode().GetOLENode() ) && 102 ChartHelper::IsChart( pONd->GetOLEObj().GetObject() ) ) 103 { 104 // Load the object and set modified 105 106 uno::Reference < embed::XEmbeddedObject > xIP = pONd->GetOLEObj().GetOleRef(); 107 if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) ) 108 { 109 try 110 { 111 uno::Reference< util::XModifiable > xModif( xIP->getComponent(), uno::UNO_QUERY_THROW ); 112 xModif->setModified( sal_True ); 113 } 114 catch ( uno::Exception& ) 115 { 116 } 117 118 } 119 } 120 aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 ); 121 } 122 } 123 124 ////////////////////////////////////////////////////////////////////// 125 126 SwChartLockController_Helper::SwChartLockController_Helper( SwDoc *pDocument ) : 127 pDoc( pDocument ) 128 { 129 aUnlockTimer.SetTimeout( 1500 ); 130 aUnlockTimer.SetTimeoutHdl( LINK( this, SwChartLockController_Helper, DoUnlockAllCharts )); 131 } 132 133 134 SwChartLockController_Helper::~SwChartLockController_Helper() 135 { 136 if (pDoc) // still connected? 137 Disconnect(); 138 } 139 140 141 void SwChartLockController_Helper::StartOrContinueLocking() 142 { 143 if (!bIsLocked) 144 LockAllCharts(); 145 aUnlockTimer.Start(); // start or continue time of locking 146 } 147 148 149 void SwChartLockController_Helper::Disconnect() 150 { 151 aUnlockTimer.Stop(); 152 UnlockAllCharts(); 153 pDoc = 0; 154 } 155 156 157 void SwChartLockController_Helper::LockUnlockAllCharts( sal_Bool bLock ) 158 { 159 if (!pDoc) 160 return; 161 162 const SwFrmFmts& rTblFmts = *pDoc->GetTblFrmFmts(); 163 for( sal_uInt16 n = 0; n < rTblFmts.Count(); ++n ) 164 { 165 SwTable* pTmpTbl; 166 const SwTableNode* pTblNd; 167 SwFrmFmt* pFmt = rTblFmts[ n ]; 168 169 if( 0 != ( pTmpTbl = SwTable::FindTable( pFmt ) ) && 170 0 != ( pTblNd = pTmpTbl->GetTableNode() ) && 171 pTblNd->GetNodes().IsDocNodes() ) 172 { 173 uno::Reference< frame::XModel > xRes; 174 175 String aName( pTmpTbl->GetFrmFmt()->GetName() ); 176 SwOLENode *pONd; 177 SwStartNode *pStNd; 178 SwNodeIndex aIdx( *pDoc->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 ); 179 while( 0 != (pStNd = aIdx.GetNode().GetStartNode()) ) 180 { 181 aIdx++; 182 if (0 != ( pONd = aIdx.GetNode().GetOLENode() ) && 183 pONd->GetChartTblName().Len() > 0 /* is chart object? */) 184 { 185 uno::Reference < embed::XEmbeddedObject > xIP = pONd->GetOLEObj().GetOleRef(); 186 if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) ) 187 { 188 xRes = uno::Reference < frame::XModel >( xIP->getComponent(), uno::UNO_QUERY ); 189 if (xRes.is()) 190 { 191 if (bLock) 192 xRes->lockControllers(); 193 else 194 xRes->unlockControllers(); 195 } 196 } 197 } 198 aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 ); 199 } 200 } 201 } 202 203 bIsLocked = bLock; 204 } 205 206 207 IMPL_LINK( SwChartLockController_Helper, DoUnlockAllCharts, Timer *, /*pTimer*/ ) 208 { 209 UnlockAllCharts(); 210 return 0; 211 } 212 213 214 ////////////////////////////////////////////////////////////////////// 215 216 static osl::Mutex & GetChartMutex() 217 { 218 static osl::Mutex aMutex; 219 return aMutex; 220 } 221 222 223 static void LaunchModifiedEvent( 224 ::cppu::OInterfaceContainerHelper &rICH, 225 const uno::Reference< uno::XInterface > &rxI ) 226 { 227 lang::EventObject aEvtObj( rxI ); 228 cppu::OInterfaceIteratorHelper aIt( rICH ); 229 while (aIt.hasMoreElements()) 230 { 231 uno::Reference< util::XModifyListener > xRef( aIt.next(), uno::UNO_QUERY ); 232 if (xRef.is()) 233 xRef->modified( aEvtObj ); 234 } 235 } 236 237 ////////////////////////////////////////////////////////////////////// 238 239 // rCellRangeName needs to be of one of the following formats: 240 // - e.g. "A2:E5" or 241 // - e.g. "Table1.A2:E5" 242 sal_Bool FillRangeDescriptor( 243 SwRangeDescriptor &rDesc, 244 const String &rCellRangeName ) 245 { 246 xub_StrLen nToken = STRING_NOTFOUND == rCellRangeName.Search('.') ? 0 : 1; 247 String aCellRangeNoTableName( rCellRangeName.GetToken( nToken, '.' ) ); 248 String aTLName( aCellRangeNoTableName.GetToken(0, ':') ); // name of top left cell 249 String aBRName( aCellRangeNoTableName.GetToken(1, ':') ); // name of bottom right cell 250 if(!aTLName.Len() || !aBRName.Len()) 251 return sal_False; 252 253 rDesc.nTop = rDesc.nLeft = rDesc.nBottom = rDesc.nRight = -1; 254 lcl_GetCellPosition( aTLName, rDesc.nLeft, rDesc.nTop ); 255 lcl_GetCellPosition( aBRName, rDesc.nRight, rDesc.nBottom ); 256 rDesc.Normalize(); 257 DBG_ASSERT( rDesc.nTop != -1 && 258 rDesc.nLeft != -1 && 259 rDesc.nBottom != -1 && 260 rDesc.nRight != -1, 261 "failed to get range descriptor" ); 262 DBG_ASSERT( rDesc.nTop <= rDesc.nBottom && rDesc.nLeft <= rDesc.nRight, 263 "invalid range descriptor"); 264 return sal_True; 265 } 266 267 268 static String GetCellRangeName( SwFrmFmt &rTblFmt, SwUnoCrsr &rTblCrsr ) 269 { 270 String aRes; 271 272 //!! see also SwXTextTableCursor::getRangeName 273 274 SwUnoTableCrsr* pUnoTblCrsr = dynamic_cast<SwUnoTableCrsr*>(&rTblCrsr); 275 if (!pUnoTblCrsr) 276 return String(); 277 pUnoTblCrsr->MakeBoxSels(); 278 279 const SwStartNode* pStart; 280 const SwTableBox* pStartBox = 0; 281 const SwTableBox* pEndBox = 0; 282 283 pStart = pUnoTblCrsr->GetPoint()->nNode.GetNode().FindTableBoxStartNode(); 284 if (pStart) 285 { 286 const SwTable* pTable = SwTable::FindTable( &rTblFmt ); 287 pEndBox = pTable->GetTblBox( pStart->GetIndex()); 288 aRes = pEndBox->GetName(); 289 290 if(pUnoTblCrsr->HasMark()) 291 { 292 pStart = pUnoTblCrsr->GetMark()->nNode.GetNode().FindTableBoxStartNode(); 293 pStartBox = pTable->GetTblBox( pStart->GetIndex()); 294 } 295 DBG_ASSERT( pStartBox, "start box not found" ); 296 DBG_ASSERT( pEndBox, "end box not found" ); 297 // need to switch start and end? 298 if (*pUnoTblCrsr->GetPoint() < *pUnoTblCrsr->GetMark()) 299 { 300 const SwTableBox* pTmpBox = pStartBox; 301 pStartBox = pEndBox; 302 pEndBox = pTmpBox; 303 } 304 305 aRes = pStartBox->GetName(); 306 aRes += (sal_Unicode)':'; 307 if (pEndBox) 308 aRes += pEndBox->GetName(); 309 else 310 aRes += pStartBox->GetName(); 311 } 312 313 return aRes; 314 } 315 316 317 static String GetRangeRepFromTableAndCells( const String &rTableName, 318 const String &rStartCell, const String &rEndCell, 319 sal_Bool bForceEndCellName ) 320 { 321 DBG_ASSERT( rTableName.Len(), "table name missing" ); 322 DBG_ASSERT( rStartCell.Len(), "cell name missing" ); 323 String aRes( rTableName ); 324 aRes += (sal_Unicode) '.'; 325 aRes += rStartCell; 326 327 if (rEndCell.Len()) 328 { 329 aRes += (sal_Unicode) ':'; 330 aRes += rEndCell; 331 } 332 else if (bForceEndCellName) 333 { 334 aRes += (sal_Unicode) ':'; 335 aRes += rStartCell; 336 } 337 338 return aRes; 339 } 340 341 342 static sal_Bool GetTableAndCellsFromRangeRep( 343 const OUString &rRangeRepresentation, 344 String &rTblName, 345 String &rStartCell, 346 String &rEndCell, 347 sal_Bool bSortStartEndCells = sal_True ) 348 { 349 // parse range representation for table name and cell/range names 350 // accepted format sth like: "Table1.A2:C5" , "Table2.A2.1:B3.2" 351 String aTblName; // table name 352 OUString aRange; // cell range 353 String aStartCell; // name of top left cell 354 String aEndCell; // name of bottom right cell 355 sal_Int32 nIdx = rRangeRepresentation.indexOf( '.' ); 356 if (nIdx >= 0) 357 { 358 aTblName = rRangeRepresentation.copy( 0, nIdx ); 359 aRange = rRangeRepresentation.copy( nIdx + 1 ); 360 sal_Int32 nPos = aRange.indexOf( ':' ); 361 if (nPos >= 0) // a cell-range like "Table1.A2:D4" 362 { 363 aStartCell = aRange.copy( 0, nPos ); 364 aEndCell = aRange.copy( nPos + 1 ); 365 366 // need to switch start and end cell ? 367 // (does not check for normalization here) 368 if (bSortStartEndCells && 1 == lcl_CompareCellsByColFirst( aStartCell, aEndCell )) 369 { 370 String aTmp( aStartCell ); 371 aStartCell = aEndCell; 372 aEndCell = aTmp; 373 } 374 } 375 else // a single cell like in "Table1.B3" 376 { 377 aStartCell = aEndCell = aRange; 378 } 379 } 380 381 sal_Bool bSuccess = aTblName.Len() != 0 && 382 aStartCell.Len() != 0 && aEndCell.Len() != 0; 383 if (bSuccess) 384 { 385 rTblName = aTblName; 386 rStartCell = aStartCell; 387 rEndCell = aEndCell; 388 } 389 return bSuccess; 390 } 391 392 393 static void GetTableByName( const SwDoc &rDoc, const String &rTableName, 394 SwFrmFmt **ppTblFmt, SwTable **ppTable) 395 { 396 SwFrmFmt *pTblFmt = NULL; 397 398 // find frame format of table 399 //! see SwXTextTables::getByName 400 sal_uInt16 nCount = rDoc.GetTblFrmFmtCount(sal_True); 401 for (sal_uInt16 i = 0; i < nCount && !pTblFmt; ++i) 402 { 403 SwFrmFmt& rTblFmt = rDoc.GetTblFrmFmt(i, sal_True); 404 if(rTableName == rTblFmt.GetName()) 405 pTblFmt = &rTblFmt; 406 } 407 408 if (ppTblFmt) 409 *ppTblFmt = pTblFmt; 410 411 if (ppTable) 412 *ppTable = pTblFmt ? SwTable::FindTable( pTblFmt ) : 0; 413 } 414 415 416 static void GetFormatAndCreateCursorFromRangeRep( 417 const SwDoc *pDoc, 418 const OUString &rRangeRepresentation, // must be a single range (i.e. so called sub-range) 419 SwFrmFmt **ppTblFmt, // will be set to the table format of the table used in the range representation 420 SwUnoCrsr **ppUnoCrsr ) // will be set to cursor spanning the cell range 421 // (cursor will be created!) 422 { 423 String aTblName; // table name 424 String aStartCell; // name of top left cell 425 String aEndCell; // name of bottom right cell 426 sal_Bool bNamesFound = GetTableAndCellsFromRangeRep( rRangeRepresentation, 427 aTblName, aStartCell, aEndCell ); 428 429 if (!bNamesFound) 430 { 431 if (ppTblFmt) 432 *ppTblFmt = NULL; 433 if (ppUnoCrsr) 434 *ppUnoCrsr = NULL; 435 } 436 else 437 { 438 SwFrmFmt *pTblFmt = NULL; 439 440 // is the correct table format already provided? 441 if (*ppTblFmt != NULL && (*ppTblFmt)->GetName() == aTblName) 442 pTblFmt = *ppTblFmt; 443 else if (ppTblFmt) 444 GetTableByName( *pDoc, aTblName, &pTblFmt, NULL ); 445 446 if (ppTblFmt) 447 *ppTblFmt = pTblFmt; 448 449 if (ppUnoCrsr != NULL) 450 { 451 *ppUnoCrsr = NULL; // default result in case of failure 452 453 SwTable *pTable = pTblFmt ? SwTable::FindTable( pTblFmt ) : 0; 454 // create new SwUnoCrsr spanning the specified range 455 //! see also SwXTextTable::GetRangeByName 456 // --> OD 2007-08-03 #i80314# 457 // perform validation check. Thus, pass <true> as 2nd parameter to <SwTable::GetTblBox(..)> 458 const SwTableBox* pTLBox = 459 pTable ? pTable->GetTblBox( aStartCell, true ) : 0; 460 // <-- 461 if(pTLBox) 462 { 463 // hier muessen die Actions aufgehoben werden 464 UnoActionRemoveContext aRemoveContext(pTblFmt->GetDoc()); 465 const SwStartNode* pSttNd = pTLBox->GetSttNd(); 466 SwPosition aPos(*pSttNd); 467 // set cursor to top left box of range 468 SwUnoCrsr* pUnoCrsr = pTblFmt->GetDoc()->CreateUnoCrsr(aPos, sal_True); 469 pUnoCrsr->Move( fnMoveForward, fnGoNode ); 470 pUnoCrsr->SetRemainInSection( sal_False ); 471 // --> OD 2007-08-03 #i80314# 472 // perform validation check. Thus, pass <true> as 2nd parameter to <SwTable::GetTblBox(..)> 473 const SwTableBox* pBRBox = pTable->GetTblBox( aEndCell, true ); 474 // <-- 475 if(pBRBox) 476 { 477 pUnoCrsr->SetMark(); 478 pUnoCrsr->GetPoint()->nNode = *pBRBox->GetSttNd(); 479 pUnoCrsr->Move( fnMoveForward, fnGoNode ); 480 SwUnoTableCrsr* pCrsr = 481 dynamic_cast<SwUnoTableCrsr*>(pUnoCrsr); 482 pCrsr->MakeBoxSels(); 483 484 if (ppUnoCrsr) 485 *ppUnoCrsr = pCrsr; 486 } 487 else 488 { 489 delete pUnoCrsr; 490 } 491 } 492 } 493 } 494 } 495 496 497 static sal_Bool GetSubranges( const OUString &rRangeRepresentation, 498 uno::Sequence< OUString > &rSubRanges, sal_Bool bNormalize ) 499 { 500 sal_Bool bRes = sal_True; 501 String aRangesStr( rRangeRepresentation ); 502 xub_StrLen nLen = aRangesStr.GetTokenCount( ';' ); 503 uno::Sequence< OUString > aRanges( nLen ); 504 505 sal_Int32 nCnt = 0; 506 if (nLen != 0) 507 { 508 OUString *pRanges = aRanges.getArray(); 509 String aFirstTable; 510 for ( xub_StrLen i = 0; i < nLen && bRes; ++i) 511 { 512 String aRange( aRangesStr.GetToken( i, ';' ) ); 513 if (aRange.Len()) 514 { 515 pRanges[nCnt] = aRange; 516 517 String aTableName, aStartCell, aEndCell; 518 bRes &= GetTableAndCellsFromRangeRep( aRange, 519 aTableName, aStartCell, aEndCell ); 520 521 if (bNormalize) 522 { 523 lcl_NormalizeRange( aStartCell, aEndCell ); 524 pRanges[nCnt] = GetRangeRepFromTableAndCells( aTableName, 525 aStartCell, aEndCell, sal_True ); 526 } 527 528 // make sure to use only a single table 529 if (nCnt == 0) 530 aFirstTable = aTableName; 531 else 532 bRes &= aFirstTable == aTableName; 533 534 ++nCnt; 535 } 536 } 537 } 538 aRanges.realloc( nCnt ); 539 540 rSubRanges = aRanges; 541 return bRes; 542 } 543 544 545 static void SortSubranges( uno::Sequence< OUString > &rSubRanges, sal_Bool bCmpByColumn ) 546 { 547 sal_Int32 nLen = rSubRanges.getLength(); 548 OUString *pSubRanges = rSubRanges.getArray(); 549 550 String aSmallestTblName; 551 String aSmallestStartCell; 552 String aSmallestEndCell; 553 554 for (sal_Int32 i = 0; i < nLen; ++i) 555 { 556 sal_Int32 nIdxOfSmallest = i; 557 GetTableAndCellsFromRangeRep( pSubRanges[nIdxOfSmallest], 558 aSmallestTblName, aSmallestStartCell, aSmallestEndCell ); 559 if (aSmallestEndCell.Len() == 0) 560 aSmallestEndCell = aSmallestStartCell; 561 562 for (sal_Int32 k = i+1; k < nLen; ++k) 563 { 564 // get cell names for sub range 565 String aTblName; 566 String aStartCell; 567 String aEndCell; 568 GetTableAndCellsFromRangeRep( pSubRanges[k], 569 aTblName, aStartCell, aEndCell ); 570 if (aEndCell.Len() == 0) 571 aEndCell = aStartCell; 572 573 // compare cell ranges ( is the new one smaller? ) 574 if (-1 == lcl_CompareCellRanges( aStartCell, aEndCell, 575 aSmallestStartCell, aSmallestEndCell, bCmpByColumn )) 576 { 577 nIdxOfSmallest = k; 578 aSmallestTblName = aTblName; 579 aSmallestStartCell = aStartCell; 580 aSmallestEndCell = aEndCell; 581 } 582 } 583 584 // move smallest element to the start of the not sorted area 585 OUString aTmp( pSubRanges[ nIdxOfSmallest ] ); 586 pSubRanges[ nIdxOfSmallest ] = pSubRanges[ i ]; 587 pSubRanges[ i ] = aTmp; 588 } 589 } 590 591 ////////////////////////////////////////////////////////////////////// 592 593 SwChartDataProvider::SwChartDataProvider( const SwDoc* pSwDoc ) : 594 aEvtListeners( GetChartMutex() ), 595 pDoc( pSwDoc ) 596 { 597 bDisposed = sal_False; 598 } 599 600 601 SwChartDataProvider::~SwChartDataProvider() 602 { 603 } 604 605 uno::Reference< chart2::data::XDataSource > SwChartDataProvider::Impl_createDataSource( 606 const uno::Sequence< beans::PropertyValue >& rArguments, sal_Bool bTestOnly ) 607 { 608 vos::OGuard aGuard( Application::GetSolarMutex() ); 609 if (bDisposed) 610 throw lang::DisposedException(); 611 612 uno::Reference< chart2::data::XDataSource > xRes; 613 614 if (!pDoc) 615 throw uno::RuntimeException(); 616 617 // get arguments 618 OUString aRangeRepresentation; 619 uno::Sequence< sal_Int32 > aSequenceMapping; 620 sal_Bool bFirstIsLabel = sal_False; 621 sal_Bool bDtaSrcIsColumns = sal_True; // true : DataSource will be sequence of columns 622 // false: DataSource will be sequence of rows 623 OUString aChartOleObjectName;//work around wrong writer ranges ( see Issue 58464 ) 624 sal_Int32 nArgs = rArguments.getLength(); 625 DBG_ASSERT( nArgs != 0, "no properties provided" ); 626 if (nArgs == 0) 627 return xRes; 628 const beans::PropertyValue *pArg = rArguments.getConstArray(); 629 for (sal_Int32 i = 0; i < nArgs; ++i) 630 { 631 if (pArg[i].Name.equalsAscii( "DataRowSource" )) 632 { 633 chart::ChartDataRowSource eSource; 634 if (!(pArg[i].Value >>= eSource)) 635 { 636 sal_Int32 nTmp = 0; 637 if (!(pArg[i].Value >>= nTmp)) 638 throw lang::IllegalArgumentException(); 639 eSource = static_cast< chart::ChartDataRowSource >( nTmp ); 640 } 641 bDtaSrcIsColumns = eSource == chart::ChartDataRowSource_COLUMNS; 642 } 643 else if (pArg[i].Name.equalsAscii( "FirstCellAsLabel" )) 644 { 645 if (!(pArg[i].Value >>= bFirstIsLabel)) 646 throw lang::IllegalArgumentException(); 647 } 648 else if (pArg[i].Name.equalsAscii( "CellRangeRepresentation" )) 649 { 650 if (!(pArg[i].Value >>= aRangeRepresentation)) 651 throw lang::IllegalArgumentException(); 652 } 653 else if (pArg[i].Name.equalsAscii( "SequenceMapping" )) 654 { 655 if (!(pArg[i].Value >>= aSequenceMapping)) 656 throw lang::IllegalArgumentException(); 657 } 658 else if (pArg[i].Name.equalsAscii( "ChartOleObjectName" )) 659 { 660 if (!(pArg[i].Value >>= aChartOleObjectName)) 661 throw lang::IllegalArgumentException(); 662 } 663 } 664 665 uno::Sequence< OUString > aSubRanges; 666 // get sub-ranges and check that they all are from the very same table 667 sal_Bool bOk = GetSubranges( aRangeRepresentation, aSubRanges, sal_True ); 668 669 if (!bOk && pDoc && aChartOleObjectName.getLength() ) 670 { 671 //try to correct the range here 672 //work around wrong writer ranges ( see Issue 58464 ) 673 String aChartTableName; 674 675 const SwNodes& rNodes = pDoc->GetNodes(); 676 for( sal_uLong nN = rNodes.Count(); nN--; ) 677 { 678 SwNodePtr pNode = rNodes[nN]; 679 if( !pNode ) 680 continue; 681 const SwOLENode* pOleNode = pNode->GetOLENode(); 682 if( !pOleNode ) 683 continue; 684 const SwOLEObj& rOObj = pOleNode->GetOLEObj(); 685 if( aChartOleObjectName.equals( rOObj.GetCurrentPersistName() ) ) 686 { 687 aChartTableName = pOleNode->GetChartTblName(); 688 break; 689 } 690 } 691 692 if( aChartTableName.Len() ) 693 { 694 //the wrong range is still shifted one row down 695 //thus the first row is missing and an invalid row at the end is added. 696 //Therefore we need to shift the range one row up 697 SwRangeDescriptor aDesc; 698 if (aRangeRepresentation.getLength() == 0) 699 return xRes; // we can't handle this thus returning an empty references 700 aRangeRepresentation = aRangeRepresentation.copy( 1 ); // get rid of '.' to have only the cell range left 701 FillRangeDescriptor( aDesc, aRangeRepresentation ); 702 aDesc.Normalize(); 703 if (aDesc.nTop <= 0) // no chance to shift the range one row up? 704 return xRes; // we can't handle this thus returning an empty references 705 aDesc.nTop -= 1; 706 aDesc.nBottom -= 1; 707 708 String aNewStartCell( lcl_GetCellName( aDesc.nLeft, aDesc.nTop ) ); 709 String aNewEndCell( lcl_GetCellName( aDesc.nRight, aDesc.nBottom ) ); 710 aRangeRepresentation = GetRangeRepFromTableAndCells( 711 aChartTableName, aNewStartCell, aNewEndCell, sal_True ); 712 bOk = GetSubranges( aRangeRepresentation, aSubRanges, sal_True ); 713 } 714 } 715 if (!bOk) // different tables used, or incorrect range specifiers 716 throw lang::IllegalArgumentException(); 717 718 SortSubranges( aSubRanges, bDtaSrcIsColumns ); 719 const OUString *pSubRanges = aSubRanges.getConstArray(); 720 #if OSL_DEBUG_LEVEL > 1 721 { 722 sal_Int32 nSR = aSubRanges.getLength(); 723 OUString *pSR = aSubRanges.getArray(); 724 OUString aRg; 725 for (sal_Int32 i = 0; i < nSR; ++i) 726 { 727 aRg = pSR[i]; 728 } 729 } 730 #endif 731 732 // get table format for that single table from above 733 SwFrmFmt *pTblFmt = 0; // pointer to table format 734 SwUnoCrsr *pUnoCrsr = 0; // here required to check if the cells in the range do actually exist 735 std::auto_ptr< SwUnoCrsr > pAuto( pUnoCrsr ); // to end lifetime of object pointed to by pUnoCrsr 736 if (aSubRanges.getLength() > 0) 737 GetFormatAndCreateCursorFromRangeRep( pDoc, pSubRanges[0], &pTblFmt, &pUnoCrsr ); 738 if (!pTblFmt || !pUnoCrsr) 739 throw lang::IllegalArgumentException(); 740 741 if(pTblFmt) 742 { 743 SwTable* pTable = SwTable::FindTable( pTblFmt ); 744 if(pTable->IsTblComplex()) 745 return xRes; // we can't handle this thus returning an empty references 746 else 747 { 748 // get a character map in the size of the table to mark 749 // all the ranges to use in 750 sal_Int32 nRows = pTable->GetTabLines().Count(); 751 sal_Int32 nCols = pTable->GetTabLines().GetObject(0)->GetTabBoxes().Count(); 752 std::vector< std::vector< sal_Char > > aMap( nRows ); 753 for (sal_Int32 i = 0; i < nRows; ++i) 754 aMap[i].resize( nCols ); 755 756 // iterate over subranges and mark used cells in above map 757 //!! by proceeding this way we automatically get rid of 758 //!! multiple listed or overlapping cell ranges which should 759 //!! just be ignored silently 760 sal_Int32 nSubRanges = aSubRanges.getLength(); 761 for (sal_Int32 i = 0; i < nSubRanges; ++i) 762 { 763 String aTblName, aStartCell, aEndCell; 764 sal_Bool bOk2 = GetTableAndCellsFromRangeRep( 765 pSubRanges[i], aTblName, aStartCell, aEndCell ); 766 (void) bOk2; 767 DBG_ASSERT( bOk2, "failed to get table and start/end cells" ); 768 769 sal_Int32 nStartRow, nStartCol, nEndRow, nEndCol; 770 lcl_GetCellPosition( aStartCell, nStartCol, nStartRow ); 771 lcl_GetCellPosition( aEndCell, nEndCol, nEndRow ); 772 DBG_ASSERT( nStartRow <= nEndRow && nStartCol <= nEndCol, 773 "cell range not normalized"); 774 775 // test if the ranges span more than the available cells 776 if( nStartRow < 0 || nEndRow >= nRows || 777 nStartCol < 0 || nEndCol >= nCols ) 778 { 779 throw lang::IllegalArgumentException(); 780 } 781 for (sal_Int32 k1 = nStartRow; k1 <= nEndRow; ++k1) 782 { 783 for (sal_Int32 k2 = nStartCol; k2 <= nEndCol; ++k2) 784 aMap[k1][k2] = 'x'; 785 } 786 } 787 788 // 789 // find label and data sequences to use 790 // 791 sal_Int32 oi; // outer index (slower changing index) 792 sal_Int32 ii; // inner index (faster changing index) 793 sal_Int32 oiEnd = bDtaSrcIsColumns ? nCols : nRows; 794 sal_Int32 iiEnd = bDtaSrcIsColumns ? nRows : nCols; 795 std::vector< sal_Int32 > aLabelIdx( oiEnd ); 796 std::vector< sal_Int32 > aDataStartIdx( oiEnd ); 797 std::vector< sal_Int32 > aDataLen( oiEnd ); 798 for (oi = 0; oi < oiEnd; ++oi) 799 { 800 aLabelIdx[oi] = -1; 801 aDataStartIdx[oi] = -1; 802 aDataLen[oi] = 0; 803 } 804 // 805 for (oi = 0; oi < oiEnd; ++oi) 806 { 807 ii = 0; 808 while (ii < iiEnd) 809 { 810 sal_Char &rChar = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii]; 811 812 // label should be used but is not yet found? 813 if (rChar == 'x' && bFirstIsLabel && aLabelIdx[oi] == -1) 814 { 815 aLabelIdx[oi] = ii; 816 rChar = 'L'; // setting a different char for labels here 817 // makes the test for the data sequence below 818 // easier 819 } 820 821 // find data sequence 822 if (rChar == 'x' && aDataStartIdx[oi] == -1) 823 { 824 aDataStartIdx[oi] = ii; 825 826 // get length of data sequence 827 sal_Int32 nL = 0; 828 sal_Char c; 829 while (ii< iiEnd && 'x' == (c = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii])) 830 { 831 ++nL; ++ii; 832 } 833 aDataLen[oi] = nL; 834 835 // check that there is no other separate sequence of data 836 // to be found because that is not supported 837 while (ii < iiEnd) 838 { 839 if ('x' == (c = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii])) 840 throw lang::IllegalArgumentException(); 841 ++ii; 842 } 843 } 844 else 845 ++ii; 846 } 847 } 848 849 // make some other consistency checks while calculating 850 // the number of XLabeledDataSequence to build: 851 // - labels should always be used or not at all 852 // - the data sequences should have equal non-zero length 853 sal_Int32 nNumLDS = 0; 854 if (oiEnd > 0) 855 { 856 sal_Int32 nFirstSeqLen = 0; 857 sal_Int32 nFirstSeqLabelIdx = -1; 858 for (oi = 0; oi < oiEnd; ++oi) 859 { 860 sal_Bool bFirstFound = sal_False; 861 // row/col used at all? 862 if (aDataStartIdx[oi] != -1 && 863 (!bFirstIsLabel || aLabelIdx[oi] != -1)) 864 { 865 ++nNumLDS; 866 if (!bFirstFound) 867 { 868 nFirstSeqLen = aDataLen[oi]; 869 nFirstSeqLabelIdx = aLabelIdx[oi]; 870 bFirstFound = sal_True; 871 } 872 else 873 { 874 if (nFirstSeqLen != aDataLen[oi] || 875 nFirstSeqLabelIdx != aLabelIdx[oi]) 876 throw lang::IllegalArgumentException(); 877 } 878 } 879 } 880 } 881 if (nNumLDS == 0) 882 throw lang::IllegalArgumentException(); 883 884 // now we should have all necessary data to build a proper DataSource 885 // thus if we came this far there should be no further problem 886 if (bTestOnly) 887 return xRes; // have createDataSourcePossible return true 888 889 // create data source from found label and data sequences 890 uno::Sequence< uno::Reference< chart2::data::XDataSequence > > aLabelSeqs( nNumLDS ); 891 uno::Reference< chart2::data::XDataSequence > *pLabelSeqs = aLabelSeqs.getArray(); 892 uno::Sequence< uno::Reference< chart2::data::XDataSequence > > aDataSeqs( nNumLDS ); 893 uno::Reference< chart2::data::XDataSequence > *pDataSeqs = aDataSeqs.getArray(); 894 sal_Int32 nSeqsIdx = 0; 895 for (oi = 0; oi < oiEnd; ++oi) 896 { 897 // row/col not used? (see if-statement above where nNumLDS was counted) 898 if (!(aDataStartIdx[oi] != -1 && 899 (!bFirstIsLabel || aLabelIdx[oi] != -1))) 900 continue; 901 902 // get cell ranges for label and data 903 // 904 SwRangeDescriptor aLabelDesc; 905 SwRangeDescriptor aDataDesc; 906 if (bDtaSrcIsColumns) // use columns 907 { 908 aLabelDesc.nTop = aLabelIdx[oi]; 909 aLabelDesc.nLeft = oi; 910 aLabelDesc.nBottom = aLabelDesc.nTop; 911 aLabelDesc.nRight = oi; 912 913 aDataDesc.nTop = aDataStartIdx[oi]; 914 aDataDesc.nLeft = oi; 915 aDataDesc.nBottom = aDataDesc.nTop + aDataLen[oi] - 1; 916 aDataDesc.nRight = oi; 917 } 918 else // use rows 919 { 920 aLabelDesc.nTop = oi; 921 aLabelDesc.nLeft = aLabelIdx[oi]; 922 aLabelDesc.nBottom = oi; 923 aLabelDesc.nRight = aLabelDesc.nLeft; 924 925 aDataDesc.nTop = oi; 926 aDataDesc.nLeft = aDataStartIdx[oi]; 927 aDataDesc.nBottom = oi; 928 aDataDesc.nRight = aDataDesc.nLeft + aDataLen[oi] - 1; 929 } 930 String aBaseName( pTblFmt->GetName() ); 931 aBaseName += '.'; 932 // 933 String aLabelRange; 934 if (aLabelIdx[oi] != -1) 935 { 936 aLabelRange += aBaseName; 937 aLabelRange += lcl_GetCellName( aLabelDesc.nLeft, aLabelDesc.nTop ); 938 aLabelRange += ':'; 939 aLabelRange += lcl_GetCellName( aLabelDesc.nRight, aLabelDesc.nBottom ); 940 } 941 // 942 String aDataRange; 943 if (aDataStartIdx[oi] != -1) 944 { 945 aDataRange += aBaseName; 946 aDataRange += lcl_GetCellName( aDataDesc.nLeft, aDataDesc.nTop ); 947 aDataRange += ':'; 948 aDataRange += lcl_GetCellName( aDataDesc.nRight, aDataDesc.nBottom ); 949 } 950 951 // get cursors spanning the cell ranges for label and data 952 SwUnoCrsr *pLabelUnoCrsr = 0; 953 SwUnoCrsr *pDataUnoCrsr = 0; 954 GetFormatAndCreateCursorFromRangeRep( pDoc, aLabelRange, &pTblFmt, &pLabelUnoCrsr); 955 GetFormatAndCreateCursorFromRangeRep( pDoc, aDataRange, &pTblFmt, &pDataUnoCrsr); 956 957 // create XDataSequence's from cursors 958 if (pLabelUnoCrsr) 959 pLabelSeqs[ nSeqsIdx ] = new SwChartDataSequence( *this, *pTblFmt, pLabelUnoCrsr ); 960 DBG_ASSERT( pDataUnoCrsr, "pointer to data sequence missing" ); 961 if (pDataUnoCrsr) 962 pDataSeqs [ nSeqsIdx ] = new SwChartDataSequence( *this, *pTblFmt, pDataUnoCrsr ); 963 if (pLabelUnoCrsr || pDataUnoCrsr) 964 ++nSeqsIdx; 965 } 966 DBG_ASSERT( nSeqsIdx == nNumLDS, 967 "mismatch between sequence size and num,ber of entries" ); 968 969 // build data source from data and label sequences 970 uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aLDS( nNumLDS ); 971 uno::Reference< chart2::data::XLabeledDataSequence > *pLDS = aLDS.getArray(); 972 for (sal_Int32 i = 0; i < nNumLDS; ++i) 973 { 974 SwChartLabeledDataSequence *pLabeledDtaSeq = new SwChartLabeledDataSequence; 975 pLabeledDtaSeq->setLabel( pLabelSeqs[i] ); 976 pLabeledDtaSeq->setValues( pDataSeqs[i] ); 977 pLDS[i] = pLabeledDtaSeq; 978 } 979 980 // apply 'SequenceMapping' if it was provided 981 sal_Int32 nSequenceMappingLen = aSequenceMapping.getLength(); 982 if (nSequenceMappingLen) 983 { 984 sal_Int32 *pSequenceMapping = aSequenceMapping.getArray(); 985 uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aOld_LDS( aLDS ); 986 uno::Reference< chart2::data::XLabeledDataSequence > *pOld_LDS = aOld_LDS.getArray(); 987 988 sal_Int32 nNewCnt = 0; 989 for (sal_Int32 i = 0; i < nSequenceMappingLen; ++i) 990 { 991 // check that index to be used is valid 992 // and has not yet been used 993 sal_Int32 nIdx = pSequenceMapping[i]; 994 if (0 <= nIdx && nIdx < nNumLDS && pOld_LDS[nIdx].is()) 995 { 996 pLDS[nNewCnt++] = pOld_LDS[nIdx]; 997 998 // mark index as being used already (avoids duplicate entries) 999 pOld_LDS[nIdx].clear(); 1000 } 1001 } 1002 // add not yet used 'old' sequences to new one 1003 for (sal_Int32 i = 0; i < nNumLDS; ++i) 1004 { 1005 #if OSL_DEBUG_LEVEL > 1 1006 if (!pOld_LDS[i].is()) 1007 i = i; 1008 #endif 1009 if (pOld_LDS[i].is()) 1010 pLDS[nNewCnt++] = pOld_LDS[i]; 1011 } 1012 DBG_ASSERT( nNewCnt == nNumLDS, "unexpected size of resulting sequence" ); 1013 } 1014 1015 xRes = new SwChartDataSource( aLDS ); 1016 } 1017 } 1018 1019 return xRes; 1020 } 1021 1022 sal_Bool SAL_CALL SwChartDataProvider::createDataSourcePossible( 1023 const uno::Sequence< beans::PropertyValue >& rArguments ) 1024 { 1025 vos::OGuard aGuard( Application::GetSolarMutex() ); 1026 1027 sal_Bool bPossible = sal_True; 1028 try 1029 { 1030 Impl_createDataSource( rArguments, sal_True ); 1031 } 1032 catch (lang::IllegalArgumentException &) 1033 { 1034 bPossible = sal_False; 1035 } 1036 1037 return bPossible; 1038 } 1039 1040 uno::Reference< chart2::data::XDataSource > SAL_CALL SwChartDataProvider::createDataSource( 1041 const uno::Sequence< beans::PropertyValue >& rArguments ) 1042 { 1043 vos::OGuard aGuard( Application::GetSolarMutex() ); 1044 return Impl_createDataSource( rArguments ); 1045 } 1046 1047 //////////////////////////////////////////////////////////// 1048 // SwChartDataProvider::GetBrokenCellRangeForExport 1049 // 1050 // fix for #i79009 1051 // we need to return a property that has the same value as the property 1052 // 'CellRangeRepresentation' but for all rows which are increased by one. 1053 // E.g. Table1:A1:D5 -> Table1:A2:D6 1054 // Since the problem is only for old charts which did not support multiple 1055 // we do not need to provide that property/string if the 'CellRangeRepresentation' 1056 // contains multiple ranges. 1057 OUString SwChartDataProvider::GetBrokenCellRangeForExport( 1058 const OUString &rCellRangeRepresentation ) 1059 { 1060 OUString aRes; 1061 1062 // check that we do not have multiple ranges 1063 if (-1 == rCellRangeRepresentation.indexOf( ';' )) 1064 { 1065 // get current cell and table names 1066 String aTblName, aStartCell, aEndCell; 1067 GetTableAndCellsFromRangeRep( rCellRangeRepresentation, 1068 aTblName, aStartCell, aEndCell, sal_False ); 1069 sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1; 1070 lcl_GetCellPosition( aStartCell, nStartCol, nStartRow ); 1071 lcl_GetCellPosition( aEndCell, nEndCol, nEndRow ); 1072 1073 // get new cell names 1074 ++nStartRow; 1075 ++nEndRow; 1076 aStartCell = lcl_GetCellName( nStartCol, nStartRow ); 1077 aEndCell = lcl_GetCellName( nEndCol, nEndRow ); 1078 1079 aRes = GetRangeRepFromTableAndCells( aTblName, 1080 aStartCell, aEndCell, sal_False ); 1081 } 1082 1083 return aRes; 1084 } 1085 1086 uno::Sequence< beans::PropertyValue > SAL_CALL SwChartDataProvider::detectArguments( 1087 const uno::Reference< chart2::data::XDataSource >& xDataSource ) 1088 { 1089 vos::OGuard aGuard( Application::GetSolarMutex() ); 1090 if (bDisposed) 1091 throw lang::DisposedException(); 1092 1093 uno::Sequence< beans::PropertyValue > aResult; 1094 if (!xDataSource.is()) 1095 return aResult; 1096 1097 const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aDS_LDS( xDataSource->getDataSequences() ); 1098 const uno::Reference< chart2::data::XLabeledDataSequence > *pDS_LDS = aDS_LDS.getConstArray(); 1099 sal_Int32 nNumDS_LDS = aDS_LDS.getLength(); 1100 1101 if (nNumDS_LDS == 0) 1102 { 1103 DBG_WARNING( "XLabeledDataSequence in data source contains 0 entries" ); 1104 return aResult; 1105 } 1106 1107 SwFrmFmt *pTableFmt = 0; 1108 SwTable *pTable = 0; 1109 String aTableName; 1110 sal_Int32 nTableRows = 0; 1111 sal_Int32 nTableCols = 0; 1112 1113 // data used to build 'CellRangeRepresentation' from later on 1114 std::vector< std::vector< sal_Char > > aMap; 1115 1116 uno::Sequence< sal_Int32 > aSequenceMapping( nNumDS_LDS ); 1117 sal_Int32 *pSequenceMapping = aSequenceMapping.getArray(); 1118 1119 String aCellRanges; 1120 sal_Int16 nDtaSrcIsColumns = -1;// -1: don't know yet, 0: false, 1: true -2: neither 1121 sal_Int32 nLabelSeqLen = -1; // used to see if labels are always used or not and have 1122 // the expected size of 1 (i.e. if FirstCellAsLabel can 1123 // be determined) 1124 // -1: don't know yet, 0: not used, 1: always a single label cell, ... 1125 // -2: neither/failed 1126 // sal_Int32 nValuesSeqLen = -1; // used to see if all value sequences have the same size 1127 for (sal_Int32 nDS1 = 0; nDS1 < nNumDS_LDS; ++nDS1) 1128 { 1129 uno::Reference< chart2::data::XLabeledDataSequence > xLabeledDataSequence( pDS_LDS[nDS1] ); 1130 if( !xLabeledDataSequence.is() ) 1131 { 1132 DBG_ERROR("got NULL for XLabeledDataSequence from Data source"); 1133 continue; 1134 } 1135 const uno::Reference< chart2::data::XDataSequence > xCurLabel( xLabeledDataSequence->getLabel(), uno::UNO_QUERY ); 1136 const uno::Reference< chart2::data::XDataSequence > xCurValues( xLabeledDataSequence->getValues(), uno::UNO_QUERY ); 1137 1138 // get sequence lengths for label and values. 1139 // (0 length is Ok) 1140 sal_Int32 nCurLabelSeqLen = -1; 1141 sal_Int32 nCurValuesSeqLen = -1; 1142 if (xCurLabel.is()) 1143 nCurLabelSeqLen = xCurLabel->getData().getLength(); 1144 if (xCurValues.is()) 1145 nCurValuesSeqLen = xCurValues->getData().getLength(); 1146 1147 // check for consistent use of 'first cell as label' 1148 if (nLabelSeqLen == -1) // set initial value to compare with below further on 1149 nLabelSeqLen = nCurLabelSeqLen; 1150 if (nLabelSeqLen != nCurLabelSeqLen) 1151 nLabelSeqLen = -2; // failed / no consistent use of label cells 1152 1153 // get table and cell names for label and values data sequences 1154 // (start and end cell will be sorted, i.e. start cell <= end cell) 1155 String aLabelTblName, aLabelStartCell, aLabelEndCell; 1156 String aValuesTblName, aValuesStartCell, aValuesEndCell; 1157 String aLabelRange, aValuesRange; 1158 if (xCurLabel.is()) 1159 aLabelRange = xCurLabel->getSourceRangeRepresentation(); 1160 if (xCurValues.is()) 1161 aValuesRange = xCurValues->getSourceRangeRepresentation(); 1162 if ((aLabelRange.Len() && !GetTableAndCellsFromRangeRep( aLabelRange, 1163 aLabelTblName, aLabelStartCell, aLabelEndCell )) || 1164 !GetTableAndCellsFromRangeRep( aValuesRange, 1165 aValuesTblName, aValuesStartCell, aValuesEndCell )) 1166 { 1167 return aResult; // failed -> return empty property sequence 1168 } 1169 1170 // make sure all sequences use the same table 1171 if (!aTableName.Len()) 1172 aTableName = aValuesTblName; // get initial value to compare with 1173 if (!aTableName.Len() || 1174 aTableName != aValuesTblName || 1175 (aLabelTblName.Len() && aTableName != aLabelTblName)) 1176 { 1177 return aResult; // failed -> return empty property sequence 1178 } 1179 1180 1181 // try to get 'DataRowSource' value (ROWS or COLUMNS) from inspecting 1182 // first and last cell used in both sequences 1183 // 1184 sal_Int32 nFirstCol = -1, nFirstRow = -1, nLastCol = -1, nLastRow = -1; 1185 String aCell( aLabelStartCell.Len() ? aLabelStartCell : aValuesStartCell ); 1186 DBG_ASSERT( aCell.Len() , "start cell missing?" ); 1187 lcl_GetCellPosition( aCell, nFirstCol, nFirstRow); 1188 lcl_GetCellPosition( aValuesEndCell, nLastCol, nLastRow); 1189 // 1190 sal_Int16 nDirection = -1; // -1: not yet set, 0: columns, 1: rows, -2: failed 1191 if (nFirstCol == nLastCol && nFirstRow == nLastRow) // a single cell... 1192 { 1193 DBG_ASSERT( nCurLabelSeqLen == 0 && nCurValuesSeqLen == 1, 1194 "trying to determine 'DataRowSource': something's fishy... should have been a single cell"); 1195 nDirection = 0; // default direction for a single cell should be 'columns' 1196 } 1197 else // more than one cell is available (in values and label together!) 1198 { 1199 if (nFirstCol == nLastCol && nFirstRow != nLastRow) 1200 nDirection = 1; 1201 else if (nFirstCol != nLastCol && nFirstRow == nLastRow) 1202 nDirection = 0; 1203 else 1204 { 1205 DBG_ERROR( "trying to determine 'DataRowSource': unexpected case found" ); 1206 nDirection = -2; 1207 } 1208 } 1209 // check for consistent direction of data source 1210 if (nDtaSrcIsColumns == -1) // set initial value to compare with below 1211 nDtaSrcIsColumns = nDirection; 1212 if (nDtaSrcIsColumns != nDirection) 1213 { 1214 nDtaSrcIsColumns = -2; // failed 1215 } 1216 1217 1218 if (nDtaSrcIsColumns == 0 || nDtaSrcIsColumns == 1) 1219 { 1220 // build data to obtain 'SequenceMapping' later on 1221 // 1222 DBG_ASSERT( nDtaSrcIsColumns == 0 || /* rows */ 1223 nDtaSrcIsColumns == 1, /* columns */ 1224 "unexpected value for 'nDtaSrcIsColumns'" ); 1225 pSequenceMapping[nDS1] = nDtaSrcIsColumns ? nFirstCol : nFirstRow; 1226 1227 1228 // build data used to determine 'CellRangeRepresentation' later on 1229 // 1230 GetTableByName( *pDoc, aTableName, &pTableFmt, &pTable ); 1231 if (!pTable || pTable->IsTblComplex()) 1232 return aResult; // failed -> return empty property sequence 1233 nTableRows = pTable->GetTabLines().Count(); 1234 nTableCols = pTable->GetTabLines().GetObject(0)->GetTabBoxes().Count(); 1235 aMap.resize( nTableRows ); 1236 for (sal_Int32 i = 0; i < nTableRows; ++i) 1237 aMap[i].resize( nTableCols ); 1238 // 1239 if (aLabelStartCell.Len() && aLabelEndCell.Len()) 1240 { 1241 sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1; 1242 lcl_GetCellPosition( aLabelStartCell, nStartCol, nStartRow ); 1243 lcl_GetCellPosition( aLabelEndCell, nEndCol, nEndRow ); 1244 if (nStartRow < 0 || nEndRow >= nTableRows || 1245 nStartCol < 0 || nEndCol >= nTableCols) 1246 { 1247 return aResult; // failed -> return empty property sequence 1248 } 1249 for (sal_Int32 i = nStartRow; i <= nEndRow; ++i) 1250 { 1251 for (sal_Int32 k = nStartCol; k <= nEndCol; ++k) 1252 { 1253 sal_Char &rChar = aMap[i][k]; 1254 if (rChar == '\0') // check for overlapping values and/or labels 1255 rChar = 'L'; 1256 else 1257 return aResult; // failed -> return empty property sequence 1258 } 1259 } 1260 } 1261 if (aValuesStartCell.Len() && aValuesEndCell.Len()) 1262 { 1263 sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1; 1264 lcl_GetCellPosition( aValuesStartCell, nStartCol, nStartRow ); 1265 lcl_GetCellPosition( aValuesEndCell, nEndCol, nEndRow ); 1266 if (nStartRow < 0 || nEndRow >= nTableRows || 1267 nStartCol < 0 || nEndCol >= nTableCols) 1268 { 1269 return aResult; // failed -> return empty property sequence 1270 } 1271 for (sal_Int32 i = nStartRow; i <= nEndRow; ++i) 1272 { 1273 for (sal_Int32 k = nStartCol; k <= nEndCol; ++k) 1274 { 1275 sal_Char &rChar = aMap[i][k]; 1276 if (rChar == '\0') // check for overlapping values and/or labels 1277 rChar = 'x'; 1278 else 1279 return aResult; // failed -> return empty property sequence 1280 } 1281 } 1282 } 1283 } 1284 1285 #if OSL_DEBUG_LEVEL > 1 1286 // do some extra sanity checking that the length of the sequences 1287 // matches their range representation 1288 { 1289 sal_Int32 nStartRow = -1, nStartCol = -1, nEndRow = -1, nEndCol = -1; 1290 if (xCurLabel.is()) 1291 { 1292 lcl_GetCellPosition( aLabelStartCell, nStartCol, nStartRow); 1293 lcl_GetCellPosition( aLabelEndCell, nEndCol, nEndRow); 1294 DBG_ASSERT( (nStartCol == nEndCol && (nEndRow - nStartRow + 1) == xCurLabel->getData().getLength()) || 1295 (nStartRow == nEndRow && (nEndCol - nStartCol + 1) == xCurLabel->getData().getLength()), 1296 "label sequence length does not match range representation!" ); 1297 } 1298 if (xCurValues.is()) 1299 { 1300 lcl_GetCellPosition( aValuesStartCell, nStartCol, nStartRow); 1301 lcl_GetCellPosition( aValuesEndCell, nEndCol, nEndRow); 1302 DBG_ASSERT( (nStartCol == nEndCol && (nEndRow - nStartRow + 1) == xCurValues->getData().getLength()) || 1303 (nStartRow == nEndRow && (nEndCol - nStartCol + 1) == xCurValues->getData().getLength()), 1304 "value sequence length does not match range representation!" ); 1305 } 1306 } 1307 #endif 1308 } // for 1309 1310 1311 // build value for 'CellRangeRepresentation' 1312 // 1313 String aCellRangeBase( aTableName ); 1314 aCellRangeBase += '.'; 1315 String aCurRange; 1316 for (sal_Int32 i = 0; i < nTableRows; ++i) 1317 { 1318 for (sal_Int32 k = 0; k < nTableCols; ++k) 1319 { 1320 if (aMap[i][k] != '\0') // top-left cell of a sub-range found 1321 { 1322 // find rectangular sub-range to use 1323 sal_Int32 nRowIndex1 = i; // row index 1324 sal_Int32 nColIndex1 = k; // column index 1325 sal_Int32 nRowSubLen = 0; 1326 sal_Int32 nColSubLen = 0; 1327 while (nRowIndex1 < nTableRows && aMap[nRowIndex1++][k] != '\0') 1328 ++nRowSubLen; 1329 // be aware of shifted sequences! 1330 // (according to the checks done prior the length should be ok) 1331 while (nColIndex1 < nTableCols && aMap[i][nColIndex1] != '\0' 1332 && aMap[i + nRowSubLen-1][nColIndex1] != '\0') 1333 { 1334 ++nColIndex1; 1335 ++nColSubLen; 1336 } 1337 String aStartCell( lcl_GetCellName( k, i ) ); 1338 String aEndCell( lcl_GetCellName( k + nColSubLen - 1, i + nRowSubLen - 1) ); 1339 aCurRange = aCellRangeBase; 1340 aCurRange += aStartCell; 1341 aCurRange += ':'; 1342 aCurRange += aEndCell; 1343 if (aCellRanges.Len()) 1344 aCellRanges += ';'; 1345 aCellRanges += aCurRange; 1346 1347 // clear already found sub-range from map 1348 for (sal_Int32 nRowIndex2 = 0; nRowIndex2 < nRowSubLen; ++nRowIndex2) 1349 for (sal_Int32 nColumnIndex2 = 0; nColumnIndex2 < nColSubLen; ++nColumnIndex2) 1350 aMap[i + nRowIndex2][k + nColumnIndex2] = '\0'; 1351 } 1352 } 1353 } 1354 // to be nice to the user we now sort the cell ranges according to 1355 // rows or columns depending on the direction used in the data source 1356 uno::Sequence< OUString > aSortedRanges; 1357 GetSubranges( aCellRanges, aSortedRanges, sal_False /*sub ranges should already be normalized*/ ); 1358 SortSubranges( aSortedRanges, (nDtaSrcIsColumns == 1) ); 1359 sal_Int32 nSortedRanges = aSortedRanges.getLength(); 1360 const OUString *pSortedRanges = aSortedRanges.getConstArray(); 1361 OUString aSortedCellRanges; 1362 for (sal_Int32 i = 0; i < nSortedRanges; ++i) 1363 { 1364 if (aSortedCellRanges.getLength()) 1365 aSortedCellRanges += OUString::valueOf( (sal_Unicode) ';'); 1366 aSortedCellRanges += pSortedRanges[i]; 1367 } 1368 1369 1370 // build value for 'SequenceMapping' 1371 // 1372 uno::Sequence< sal_Int32 > aSortedMapping( aSequenceMapping ); 1373 sal_Int32 *pSortedMapping = aSortedMapping.getArray(); 1374 std::sort( pSortedMapping, pSortedMapping + aSortedMapping.getLength() ); 1375 DBG_ASSERT( aSortedMapping.getLength() == nNumDS_LDS, "unexpected size of sequence" ); 1376 sal_Bool bNeedSequenceMapping = sal_False; 1377 for (sal_Int32 i = 0; i < nNumDS_LDS; ++i) 1378 { 1379 sal_Int32 *pIt = std::find( pSortedMapping, pSortedMapping + nNumDS_LDS, 1380 pSequenceMapping[i] ); 1381 DBG_ASSERT( pIt, "index not found" ); 1382 if (!pIt) 1383 return aResult; // failed -> return empty property sequence 1384 pSequenceMapping[i] = pIt - pSortedMapping; 1385 1386 if (i != pSequenceMapping[i]) 1387 bNeedSequenceMapping = sal_True; 1388 } 1389 1390 // check if 'SequenceMapping' is actually not required... 1391 // (don't write unnecessary properties to the XML file) 1392 if (!bNeedSequenceMapping) 1393 aSequenceMapping.realloc(0); 1394 1395 1396 #ifdef TL_NOT_USED // in the end chart2 did not want to have the sequence minimized 1397 // try to shorten the 'SequenceMapping' as much as possible 1398 sal_Int32 k; 1399 for (k = nNumDS_LDS - 1; k >= 0; --k) 1400 { 1401 if (pSequenceMapping[k] != k) 1402 break; 1403 } 1404 aSequenceMapping.realloc( k + 1 ); 1405 #endif 1406 1407 1408 // 1409 // build resulting properties 1410 // 1411 DBG_ASSERT(nLabelSeqLen >= 0 || nLabelSeqLen == -2 /*not used*/, 1412 "unexpected value for 'nLabelSeqLen'" ); 1413 sal_Bool bFirstCellIsLabel = sal_False; // default value if 'nLabelSeqLen' could not properly determined 1414 if (nLabelSeqLen > 0) // == 0 means no label sequence in use 1415 bFirstCellIsLabel = sal_True; 1416 // 1417 DBG_ASSERT( aSortedCellRanges.getLength(), "CellRangeRepresentation missing" ); 1418 OUString aBrokenCellRangeForExport( GetBrokenCellRangeForExport( aSortedCellRanges ) ); 1419 // 1420 aResult.realloc(5); 1421 sal_Int32 nProps = 0; 1422 aResult[nProps ].Name = C2U("FirstCellAsLabel"); 1423 aResult[nProps++].Value <<= bFirstCellIsLabel; 1424 aResult[nProps ].Name = C2U("CellRangeRepresentation"); 1425 aResult[nProps++].Value <<= aSortedCellRanges; 1426 if (0 != aBrokenCellRangeForExport.getLength()) 1427 { 1428 aResult[nProps ].Name = C2U("BrokenCellRangeForExport"); 1429 aResult[nProps++].Value <<= aBrokenCellRangeForExport; 1430 } 1431 if (nDtaSrcIsColumns == 0 || nDtaSrcIsColumns == 1) 1432 { 1433 chart::ChartDataRowSource eDataRowSource = (nDtaSrcIsColumns == 1) ? 1434 chart::ChartDataRowSource_COLUMNS : chart::ChartDataRowSource_ROWS; 1435 aResult[nProps ].Name = C2U("DataRowSource"); 1436 aResult[nProps++].Value <<= eDataRowSource; 1437 1438 if (aSequenceMapping.getLength() != 0) 1439 { 1440 aResult[nProps ].Name = C2U("SequenceMapping"); 1441 aResult[nProps++].Value <<= aSequenceMapping; 1442 } 1443 } 1444 aResult.realloc( nProps ); 1445 1446 return aResult; 1447 } 1448 1449 uno::Reference< chart2::data::XDataSequence > SwChartDataProvider::Impl_createDataSequenceByRangeRepresentation( 1450 const OUString& rRangeRepresentation, sal_Bool bTestOnly ) 1451 { 1452 if (bDisposed) 1453 throw lang::DisposedException(); 1454 1455 SwFrmFmt *pTblFmt = 0; // pointer to table format 1456 SwUnoCrsr *pUnoCrsr = 0; // pointer to new created cursor spanning the cell range 1457 GetFormatAndCreateCursorFromRangeRep( pDoc, rRangeRepresentation, 1458 &pTblFmt, &pUnoCrsr ); 1459 if (!pTblFmt || !pUnoCrsr) 1460 throw lang::IllegalArgumentException(); 1461 1462 // check that cursors point and mark are in a single row or column. 1463 String aCellRange( GetCellRangeName( *pTblFmt, *pUnoCrsr ) ); 1464 SwRangeDescriptor aDesc; 1465 FillRangeDescriptor( aDesc, aCellRange ); 1466 if (aDesc.nTop != aDesc.nBottom && aDesc.nLeft != aDesc.nRight) 1467 throw lang::IllegalArgumentException(); 1468 1469 DBG_ASSERT( pTblFmt && pUnoCrsr, "table format or cursor missing" ); 1470 uno::Reference< chart2::data::XDataSequence > xDataSeq; 1471 if (!bTestOnly) 1472 xDataSeq = new SwChartDataSequence( *this, *pTblFmt, pUnoCrsr ); 1473 1474 return xDataSeq; 1475 } 1476 1477 sal_Bool SAL_CALL SwChartDataProvider::createDataSequenceByRangeRepresentationPossible( 1478 const OUString& rRangeRepresentation ) 1479 { 1480 vos::OGuard aGuard( Application::GetSolarMutex() ); 1481 1482 sal_Bool bPossible = sal_True; 1483 try 1484 { 1485 Impl_createDataSequenceByRangeRepresentation( rRangeRepresentation, sal_True ); 1486 } 1487 catch (lang::IllegalArgumentException &) 1488 { 1489 bPossible = sal_False; 1490 } 1491 1492 return bPossible; 1493 } 1494 1495 uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartDataProvider::createDataSequenceByRangeRepresentation( 1496 const OUString& rRangeRepresentation ) 1497 { 1498 vos::OGuard aGuard( Application::GetSolarMutex() ); 1499 return Impl_createDataSequenceByRangeRepresentation( rRangeRepresentation ); 1500 } 1501 1502 1503 uno::Reference< sheet::XRangeSelection > SAL_CALL SwChartDataProvider::getRangeSelection( ) 1504 { 1505 // note: it is no error to return nothing here 1506 return uno::Reference< sheet::XRangeSelection >(); 1507 } 1508 1509 1510 void SAL_CALL SwChartDataProvider::dispose( ) 1511 { 1512 sal_Bool bMustDispose( sal_False ); 1513 { 1514 osl::MutexGuard aGuard( GetChartMutex() ); 1515 bMustDispose = !bDisposed; 1516 if (!bDisposed) 1517 bDisposed = sal_True; 1518 } 1519 if (bMustDispose) 1520 { 1521 // dispose all data-sequences 1522 Map_Set_DataSequenceRef_t::iterator aIt( aDataSequences.begin() ); 1523 while (aIt != aDataSequences.end()) 1524 { 1525 DisposeAllDataSequences( (*aIt).first ); 1526 ++aIt; 1527 } 1528 // release all references to data-sequences 1529 aDataSequences.clear(); 1530 1531 // require listeners to release references to this object 1532 lang::EventObject aEvtObj( dynamic_cast< chart2::data::XDataSequence * >(this) ); 1533 aEvtListeners.disposeAndClear( aEvtObj ); 1534 } 1535 } 1536 1537 1538 void SAL_CALL SwChartDataProvider::addEventListener( 1539 const uno::Reference< lang::XEventListener >& rxListener ) 1540 { 1541 osl::MutexGuard aGuard( GetChartMutex() ); 1542 if (!bDisposed && rxListener.is()) 1543 aEvtListeners.addInterface( rxListener ); 1544 } 1545 1546 1547 void SAL_CALL SwChartDataProvider::removeEventListener( 1548 const uno::Reference< lang::XEventListener >& rxListener ) 1549 { 1550 osl::MutexGuard aGuard( GetChartMutex() ); 1551 if (!bDisposed && rxListener.is()) 1552 aEvtListeners.removeInterface( rxListener ); 1553 } 1554 1555 1556 1557 OUString SAL_CALL SwChartDataProvider::getImplementationName( ) 1558 { 1559 return C2U("SwChartDataProvider"); 1560 } 1561 1562 1563 sal_Bool SAL_CALL SwChartDataProvider::supportsService( 1564 const OUString& rServiceName ) 1565 { 1566 vos::OGuard aGuard( Application::GetSolarMutex() ); 1567 return rServiceName.equalsAscii( SN_DATA_PROVIDER ); 1568 } 1569 1570 1571 uno::Sequence< OUString > SAL_CALL SwChartDataProvider::getSupportedServiceNames( ) 1572 { 1573 vos::OGuard aGuard( Application::GetSolarMutex() ); 1574 uno::Sequence< OUString > aRes(1); 1575 aRes.getArray()[0] = C2U( SN_DATA_PROVIDER ); 1576 return aRes; 1577 } 1578 1579 1580 void SwChartDataProvider::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew) 1581 { 1582 // actually this function should be superfluous (need to check later) 1583 ClientModify(this, pOld, pNew ); 1584 } 1585 1586 1587 void SwChartDataProvider::AddDataSequence( const SwTable &rTable, uno::Reference< chart2::data::XDataSequence > &rxDataSequence ) 1588 { 1589 aDataSequences[ &rTable ].insert( rxDataSequence ); 1590 } 1591 1592 1593 void SwChartDataProvider::RemoveDataSequence( const SwTable &rTable, uno::Reference< chart2::data::XDataSequence > &rxDataSequence ) 1594 { 1595 aDataSequences[ &rTable ].erase( rxDataSequence ); 1596 } 1597 1598 1599 void SwChartDataProvider::InvalidateTable( const SwTable *pTable ) 1600 { 1601 DBG_ASSERT( pTable, "table pointer is NULL" ); 1602 if (pTable) 1603 { 1604 if (!bDisposed) 1605 pTable->GetFrmFmt()->GetDoc()->GetChartControllerHelper().StartOrContinueLocking(); 1606 1607 const Set_DataSequenceRef_t &rSet = aDataSequences[ pTable ]; 1608 Set_DataSequenceRef_t::const_iterator aIt( rSet.begin() ); 1609 while (aIt != rSet.end()) 1610 { 1611 // uno::Reference< util::XModifiable > xRef( uno::Reference< chart2::data::XDataSequence >(*aIt), uno::UNO_QUERY ); 1612 uno::Reference< chart2::data::XDataSequence > xTemp(*aIt); // temporary needed for g++ 3.3.5 1613 uno::Reference< util::XModifiable > xRef( xTemp, uno::UNO_QUERY ); 1614 if (xRef.is()) 1615 { 1616 // mark the sequence as 'dirty' and notify listeners 1617 xRef->setModified( sal_True ); 1618 } 1619 ++aIt; 1620 } 1621 } 1622 } 1623 1624 1625 sal_Bool SwChartDataProvider::DeleteBox( const SwTable *pTable, const SwTableBox &rBox ) 1626 { 1627 sal_Bool bRes = sal_False; 1628 DBG_ASSERT( pTable, "table pointer is NULL" ); 1629 if (pTable) 1630 { 1631 if (!bDisposed) 1632 pTable->GetFrmFmt()->GetDoc()->GetChartControllerHelper().StartOrContinueLocking(); 1633 1634 Set_DataSequenceRef_t &rSet = aDataSequences[ pTable ]; 1635 1636 // iterate over all data-sequences for that table... 1637 Set_DataSequenceRef_t::iterator aIt( rSet.begin() ); 1638 Set_DataSequenceRef_t::iterator aEndIt( rSet.end() ); 1639 Set_DataSequenceRef_t::iterator aDelIt; // iterator used for deletion when appropriate 1640 while (aIt != aEndIt) 1641 { 1642 SwChartDataSequence *pDataSeq = 0; 1643 sal_Bool bNowEmpty = sal_False; 1644 sal_Bool bSeqDisposed = sal_False; 1645 1646 // check if weak reference is still valid... 1647 // uno::Reference< chart2::data::XDataSequence > xRef( uno::Reference< chart2::data::XDataSequence>(*aIt), uno::UNO_QUERY ); 1648 uno::Reference< chart2::data::XDataSequence > xTemp(*aIt); // temporary needed for g++ 3.3.5 1649 uno::Reference< chart2::data::XDataSequence > xRef( xTemp, uno::UNO_QUERY ); 1650 if (xRef.is()) 1651 { 1652 // then delete that table box (check if implementation cursor needs to be adjusted) 1653 pDataSeq = static_cast< SwChartDataSequence * >( xRef.get() ); 1654 if (pDataSeq) 1655 { 1656 try 1657 { 1658 #if OSL_DEBUG_LEVEL > 1 1659 OUString aRangeStr( pDataSeq->getSourceRangeRepresentation() ); 1660 #endif 1661 bNowEmpty = pDataSeq->DeleteBox( rBox ); 1662 } 1663 catch (lang::DisposedException&) 1664 { 1665 bNowEmpty = sal_True; 1666 bSeqDisposed = sal_True; 1667 } 1668 1669 if (bNowEmpty) 1670 aDelIt = aIt; 1671 } 1672 } 1673 ++aIt; 1674 1675 if (bNowEmpty) 1676 { 1677 rSet.erase( aDelIt ); 1678 if (pDataSeq && !bSeqDisposed) 1679 pDataSeq->dispose(); // the current way to tell chart that sth. got removed 1680 } 1681 } 1682 } 1683 return bRes; 1684 } 1685 1686 1687 void SwChartDataProvider::DisposeAllDataSequences( const SwTable *pTable ) 1688 { 1689 DBG_ASSERT( pTable, "table pointer is NULL" ); 1690 if (pTable) 1691 { 1692 if (!bDisposed) 1693 pTable->GetFrmFmt()->GetDoc()->GetChartControllerHelper().StartOrContinueLocking(); 1694 1695 //! make a copy of the STL container! 1696 //! This is necessary since calling 'dispose' will implicitly remove an element 1697 //! of the original container, and thus any iterator in the original container 1698 //! would become invalid. 1699 const Set_DataSequenceRef_t aSet( aDataSequences[ pTable ] ); 1700 1701 Set_DataSequenceRef_t::const_iterator aIt( aSet.begin() ); 1702 Set_DataSequenceRef_t::const_iterator aEndIt( aSet.end() ); 1703 while (aIt != aEndIt) 1704 { 1705 // uno::Reference< lang::XComponent > xRef( uno::Reference< chart2::data::XDataSequence >(*aIt), uno::UNO_QUERY ); 1706 uno::Reference< chart2::data::XDataSequence > xTemp(*aIt); // temporary needed for g++ 3.3.5 1707 uno::Reference< lang::XComponent > xRef( xTemp, uno::UNO_QUERY ); 1708 if (xRef.is()) 1709 { 1710 xRef->dispose(); 1711 } 1712 ++aIt; 1713 } 1714 } 1715 } 1716 1717 1718 //////////////////////////////////////// 1719 // SwChartDataProvider::AddRowCols tries to notify charts of added columns 1720 // or rows and extends the value sequence respectively (if possible). 1721 // If those can be added to the end of existing value data-sequences those 1722 // sequences get modified accordingly and will send a modification 1723 // notification (calling 'setModified'). 1724 // 1725 // Since this function is a work-around for non existent Writer core functionality 1726 // (no arbitrary multi-selection in tables that can be used to define a 1727 // data-sequence) this function will be somewhat unreliable. 1728 // For example we will only try to adapt value sequences. For this we assume 1729 // that a sequence of length 1 is a label sequence and those with length >= 2 1730 // we presume to be value sequences. Also new cells can only be added in the 1731 // direction the value sequence is already pointing (rows / cols) and at the 1732 // start or end of the values data-sequence. 1733 // Nothing needs to be done if the new cells are in between the table cursors 1734 // point and mark since data-sequence are considered to consist of all cells 1735 // between those. 1736 // New rows/cols need to be added already to the table before calling 1737 // this function. 1738 // 1739 void SwChartDataProvider::AddRowCols( 1740 const SwTable &rTable, 1741 const SwSelBoxes& rBoxes, 1742 sal_uInt16 nLines, sal_Bool bBehind ) 1743 { 1744 if (rTable.IsTblComplex()) 1745 return; 1746 1747 const sal_uInt16 nBoxes = rBoxes.Count(); 1748 if (nBoxes < 1 || nLines < 1) 1749 return; 1750 1751 SwTableBox* pFirstBox = *( rBoxes.GetData() + 0 ); 1752 SwTableBox* pLastBox = *( rBoxes.GetData() + nBoxes - 1 ); 1753 1754 sal_Int32 nFirstCol = -1, nFirstRow = -1, nLastCol = -1, nLastRow = -1; 1755 if (pFirstBox && pLastBox) 1756 { 1757 lcl_GetCellPosition( pFirstBox->GetName(), nFirstCol, nFirstRow ); 1758 lcl_GetCellPosition( pLastBox->GetName(), nLastCol, nLastRow ); 1759 1760 bool bAddCols = false; // default; also to be used if nBoxes == 1 :-/ 1761 if (nFirstCol == nLastCol && nFirstRow != nLastRow) 1762 bAddCols = true; 1763 if (nFirstCol == nLastCol || nFirstRow == nLastRow) 1764 { 1765 //get range of indices in col/rows for new cells 1766 sal_Int32 nFirstNewCol = nFirstCol; 1767 sal_Int32 nLastNewCol = nLastCol; 1768 sal_Int32 nFirstNewRow = bBehind ? nFirstRow + 1 : nFirstRow - nLines; 1769 sal_Int32 nLastNewRow = nFirstNewRow - 1 + nLines; 1770 if (bAddCols) 1771 { 1772 DBG_ASSERT( nFirstCol == nLastCol, "column indices seem broken" ); 1773 nFirstNewCol = bBehind ? nFirstCol + 1 : nFirstCol - nLines; 1774 nLastNewCol = nFirstNewCol - 1 + nLines; 1775 nFirstNewRow = nFirstRow; 1776 nLastNewRow = nLastRow; 1777 } 1778 1779 // iterate over all data-sequences for the table 1780 const Set_DataSequenceRef_t &rSet = aDataSequences[ &rTable ]; 1781 Set_DataSequenceRef_t::const_iterator aIt( rSet.begin() ); 1782 while (aIt != rSet.end()) 1783 { 1784 // uno::Reference< chart2::data::XTextualDataSequence > xRef( uno::Reference< chart2::data::XDataSequence >(*aIt), uno::UNO_QUERY ); 1785 uno::Reference< chart2::data::XDataSequence > xTemp(*aIt); // temporary needed for g++ 3.3.5 1786 uno::Reference< chart2::data::XTextualDataSequence > xRef( xTemp, uno::UNO_QUERY ); 1787 if (xRef.is()) 1788 { 1789 const sal_Int32 nLen = xRef->getTextualData().getLength(); 1790 if (nLen > 1) // value data-sequence ? 1791 { 1792 SwChartDataSequence *pDataSeq = 0; 1793 uno::Reference< lang::XUnoTunnel > xTunnel( xRef, uno::UNO_QUERY ); 1794 if(xTunnel.is()) 1795 { 1796 pDataSeq = reinterpret_cast< SwChartDataSequence * >( 1797 sal::static_int_cast< sal_IntPtr >( xTunnel->getSomething( SwChartDataSequence::getUnoTunnelId() ))); 1798 1799 if (pDataSeq) 1800 { 1801 SwRangeDescriptor aDesc; 1802 pDataSeq->FillRangeDesc( aDesc ); 1803 1804 chart::ChartDataRowSource eDRSource = chart::ChartDataRowSource_COLUMNS; 1805 if (aDesc.nTop == aDesc.nBottom && aDesc.nLeft != aDesc.nRight) 1806 eDRSource = chart::ChartDataRowSource_ROWS; 1807 1808 if (!bAddCols && eDRSource == chart::ChartDataRowSource_COLUMNS) 1809 { 1810 // add rows: extend affected columns by newly added row cells 1811 pDataSeq->ExtendTo( true, nFirstNewRow, nLines ); 1812 } 1813 else if (bAddCols && eDRSource == chart::ChartDataRowSource_ROWS) 1814 { 1815 // add cols: extend affected rows by newly added column cells 1816 pDataSeq->ExtendTo( false, nFirstNewCol, nLines ); 1817 } 1818 } 1819 } 1820 } 1821 } 1822 ++aIt; 1823 } 1824 1825 } 1826 } 1827 } 1828 1829 1830 // XRangeXMLConversion --------------------------------------------------- 1831 1832 rtl::OUString SAL_CALL SwChartDataProvider::convertRangeToXML( const rtl::OUString& rRangeRepresentation ) 1833 { 1834 vos::OGuard aGuard( Application::GetSolarMutex() ); 1835 if (bDisposed) 1836 throw lang::DisposedException(); 1837 1838 String aRes; 1839 String aRangeRepresentation( rRangeRepresentation ); 1840 1841 // multiple ranges are delimited by a ';' like in 1842 // "Table1.A1:A4;Table1.C2:C5" the same table must be used in all ranges! 1843 xub_StrLen nNumRanges = aRangeRepresentation.GetTokenCount( ';' ); 1844 SwTable* pFirstFoundTable = 0; // to check that only one table will be used 1845 for (sal_uInt16 i = 0; i < nNumRanges; ++i) 1846 { 1847 String aRange( aRangeRepresentation.GetToken(i, ';') ); 1848 SwFrmFmt *pTblFmt = 0; // pointer to table format 1849 // BM: For what should the check be necessary? for #i79009# it is required that NO check is done 1850 // SwUnoCrsr *pUnoCrsr = 0; // here required to check if the cells in the range do actually exist 1851 // std::auto_ptr< SwUnoCrsr > pAuto( pUnoCrsr ); // to end lifetime of object pointed to by pUnoCrsr 1852 GetFormatAndCreateCursorFromRangeRep( pDoc, aRange, &pTblFmt, NULL ); 1853 if (!pTblFmt) 1854 throw lang::IllegalArgumentException(); 1855 // if (!pUnoCrsr) 1856 // throw uno::RuntimeException(); 1857 SwTable* pTable = SwTable::FindTable( pTblFmt ); 1858 if (pTable->IsTblComplex()) 1859 throw uno::RuntimeException(); 1860 1861 // check that there is only one table used in all ranges 1862 if (!pFirstFoundTable) 1863 pFirstFoundTable = pTable; 1864 if (pTable != pFirstFoundTable) 1865 throw lang::IllegalArgumentException(); 1866 1867 String aTblName; 1868 String aStartCell; 1869 String aEndCell; 1870 if (!GetTableAndCellsFromRangeRep( aRange, aTblName, aStartCell, aEndCell )) 1871 throw lang::IllegalArgumentException(); 1872 1873 sal_Int32 nCol, nRow; 1874 lcl_GetCellPosition( aStartCell, nCol, nRow ); 1875 if (nCol < 0 || nRow < 0) 1876 throw uno::RuntimeException(); 1877 1878 //!! following objects/functions are implemented in XMLRangeHelper.?xx 1879 //!! which is a copy of the respective file from chart2 !! 1880 XMLRangeHelper::CellRange aCellRange; 1881 aCellRange.aTableName = aTblName; 1882 aCellRange.aUpperLeft.nColumn = nCol; 1883 aCellRange.aUpperLeft.nRow = nRow; 1884 aCellRange.aUpperLeft.bIsEmpty = false; 1885 if (aStartCell != aEndCell && aEndCell.Len() != 0) 1886 { 1887 lcl_GetCellPosition( aEndCell, nCol, nRow ); 1888 if (nCol < 0 || nRow < 0) 1889 throw uno::RuntimeException(); 1890 1891 aCellRange.aLowerRight.nColumn = nCol; 1892 aCellRange.aLowerRight.nRow = nRow; 1893 aCellRange.aLowerRight.bIsEmpty = false; 1894 } 1895 String aTmp( XMLRangeHelper::getXMLStringFromCellRange( aCellRange ) ); 1896 if (aRes.Len()) // in case of multiple ranges add delimiter 1897 aRes.AppendAscii( " " ); 1898 aRes += aTmp; 1899 } 1900 1901 return aRes; 1902 } 1903 1904 rtl::OUString SAL_CALL SwChartDataProvider::convertRangeFromXML( const rtl::OUString& rXMLRange ) 1905 { 1906 vos::OGuard aGuard( Application::GetSolarMutex() ); 1907 if (bDisposed) 1908 throw lang::DisposedException(); 1909 1910 String aRes; 1911 String aXMLRange( rXMLRange ); 1912 1913 // multiple ranges are delimited by a ' ' like in 1914 // "Table1.$A$1:.$A$4 Table1.$C$2:.$C$5" the same table must be used in all ranges! 1915 xub_StrLen nNumRanges = aXMLRange.GetTokenCount( ' ' ); 1916 rtl::OUString aFirstFoundTable; // to check that only one table will be used 1917 for (sal_uInt16 i = 0; i < nNumRanges; ++i) 1918 { 1919 String aRange( aXMLRange.GetToken(i, ' ') ); 1920 1921 //!! following objects and function are implemented in XMLRangeHelper.?xx 1922 //!! which is a copy of the respective file from chart2 !! 1923 XMLRangeHelper::CellRange aCellRange( XMLRangeHelper::getCellRangeFromXMLString( aRange )); 1924 1925 // check that there is only one table used in all ranges 1926 if (aFirstFoundTable.getLength() == 0) 1927 aFirstFoundTable = aCellRange.aTableName; 1928 if (aCellRange.aTableName != aFirstFoundTable) 1929 throw lang::IllegalArgumentException(); 1930 1931 OUString aTmp( aCellRange.aTableName ); 1932 aTmp += OUString::valueOf((sal_Unicode) '.'); 1933 aTmp += lcl_GetCellName( aCellRange.aUpperLeft.nColumn, 1934 aCellRange.aUpperLeft.nRow ); 1935 // does cell range consist of more than a single cell? 1936 if (!aCellRange.aLowerRight.bIsEmpty) 1937 { 1938 aTmp += OUString::valueOf((sal_Unicode) ':'); 1939 aTmp += lcl_GetCellName( aCellRange.aLowerRight.nColumn, 1940 aCellRange.aLowerRight.nRow ); 1941 } 1942 1943 if (aRes.Len()) // in case of multiple ranges add delimiter 1944 aRes.AppendAscii( ";" ); 1945 aRes += String(aTmp); 1946 } 1947 1948 return aRes; 1949 } 1950 1951 1952 ////////////////////////////////////////////////////////////////////// 1953 1954 SwChartDataSource::SwChartDataSource( 1955 const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > &rLDS ) : 1956 aLDS( rLDS ) 1957 { 1958 } 1959 1960 1961 SwChartDataSource::~SwChartDataSource() 1962 { 1963 // delete pTblCrsr; 1964 } 1965 1966 1967 uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > SAL_CALL SwChartDataSource::getDataSequences( ) 1968 { 1969 vos::OGuard aGuard( Application::GetSolarMutex() ); 1970 return aLDS; 1971 } 1972 1973 1974 OUString SAL_CALL SwChartDataSource::getImplementationName( ) 1975 { 1976 vos::OGuard aGuard( Application::GetSolarMutex() ); 1977 return C2U("SwChartDataSource"); 1978 } 1979 1980 1981 sal_Bool SAL_CALL SwChartDataSource::supportsService( 1982 const OUString& rServiceName ) 1983 { 1984 vos::OGuard aGuard( Application::GetSolarMutex() ); 1985 return rServiceName.equalsAscii( SN_DATA_SOURCE ); 1986 } 1987 1988 1989 uno::Sequence< OUString > SAL_CALL SwChartDataSource::getSupportedServiceNames( ) 1990 { 1991 vos::OGuard aGuard( Application::GetSolarMutex() ); 1992 uno::Sequence< OUString > aRes(1); 1993 aRes.getArray()[0] = C2U( SN_DATA_SOURCE ); 1994 return aRes; 1995 } 1996 1997 ////////////////////////////////////////////////////////////////////// 1998 1999 SwChartDataSequence::SwChartDataSequence( 2000 SwChartDataProvider &rProvider, 2001 SwFrmFmt &rTblFmt, 2002 SwUnoCrsr *pTableCursor ) : 2003 SwClient( &rTblFmt ), 2004 aEvtListeners( GetChartMutex() ), 2005 aModifyListeners( GetChartMutex() ), 2006 aRowLabelText( SW_RES( STR_CHART2_ROW_LABEL_TEXT ) ), 2007 aColLabelText( SW_RES( STR_CHART2_COL_LABEL_TEXT ) ), 2008 xDataProvider( &rProvider ), 2009 pDataProvider( &rProvider ), 2010 pTblCrsr( pTableCursor ), 2011 aCursorDepend( this, pTableCursor ), 2012 _pPropSet( aSwMapProvider.GetPropertySet( PROPERTY_MAP_CHART2_DATA_SEQUENCE ) ) 2013 { 2014 bDisposed = sal_False; 2015 2016 acquire(); 2017 try 2018 { 2019 const SwTable* pTable = SwTable::FindTable( &rTblFmt ); 2020 if (pTable) 2021 { 2022 uno::Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY ); 2023 pDataProvider->AddDataSequence( *pTable, xRef ); 2024 pDataProvider->addEventListener( dynamic_cast< lang::XEventListener * >(this) ); 2025 } 2026 else { 2027 DBG_ERROR( "table missing" ); 2028 } 2029 } 2030 catch (uno::RuntimeException &) 2031 { 2032 throw; 2033 } 2034 catch (uno::Exception &) 2035 { 2036 } 2037 release(); 2038 2039 #if OSL_DEBUG_LEVEL > 1 2040 OUString aRangeStr( getSourceRangeRepresentation() ); 2041 2042 // check if it can properly convert into a SwUnoTableCrsr 2043 // which is required for some functions 2044 SwUnoTableCrsr* pUnoTblCrsr = dynamic_cast<SwUnoTableCrsr*>(pTblCrsr); 2045 DBG_ASSERT(pUnoTblCrsr, "SwChartDataSequence: cursor not SwUnoTableCrsr"); 2046 (void) pUnoTblCrsr; 2047 #endif 2048 } 2049 2050 2051 SwChartDataSequence::SwChartDataSequence( const SwChartDataSequence &rObj ) : 2052 SwChartDataSequenceBaseClass(), 2053 SwClient( rObj.GetFrmFmt() ), 2054 aEvtListeners( GetChartMutex() ), 2055 aModifyListeners( GetChartMutex() ), 2056 aRole( rObj.aRole ), 2057 aRowLabelText( SW_RES(STR_CHART2_ROW_LABEL_TEXT) ), 2058 aColLabelText( SW_RES(STR_CHART2_COL_LABEL_TEXT) ), 2059 xDataProvider( rObj.pDataProvider ), 2060 pDataProvider( rObj.pDataProvider ), 2061 pTblCrsr( rObj.pTblCrsr->Clone() ), 2062 aCursorDepend( this, pTblCrsr ), 2063 _pPropSet( rObj._pPropSet ) 2064 { 2065 bDisposed = sal_False; 2066 2067 acquire(); 2068 try 2069 { 2070 const SwTable* pTable = SwTable::FindTable( GetFrmFmt() ); 2071 if (pTable) 2072 { 2073 uno::Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY ); 2074 pDataProvider->AddDataSequence( *pTable, xRef ); 2075 pDataProvider->addEventListener( dynamic_cast< lang::XEventListener * >(this) ); 2076 } 2077 else { 2078 DBG_ERROR( "table missing" ); 2079 } 2080 } 2081 catch (uno::RuntimeException &) 2082 { 2083 throw; 2084 } 2085 catch (uno::Exception &) 2086 { 2087 } 2088 release(); 2089 2090 #if OSL_DEBUG_LEVEL > 1 2091 OUString aRangeStr( getSourceRangeRepresentation() ); 2092 2093 // check if it can properly convert into a SwUnoTableCrsr 2094 // which is required for some functions 2095 SwUnoTableCrsr* pUnoTblCrsr = dynamic_cast<SwUnoTableCrsr*>(pTblCrsr); 2096 DBG_ASSERT(pUnoTblCrsr, "SwChartDataSequence: cursor not SwUnoTableCrsr"); 2097 (void) pUnoTblCrsr; 2098 #endif 2099 } 2100 2101 2102 SwChartDataSequence::~SwChartDataSequence() 2103 { 2104 // since the data-provider holds only weak references to the data-sequence 2105 // there should be no need here to release them explicitly... 2106 2107 delete pTblCrsr; 2108 } 2109 2110 2111 const uno::Sequence< sal_Int8 > & SwChartDataSequence::getUnoTunnelId() 2112 { 2113 static uno::Sequence< sal_Int8 > aSeq = ::CreateUnoTunnelId(); 2114 return aSeq; 2115 } 2116 2117 2118 sal_Int64 SAL_CALL SwChartDataSequence::getSomething( const uno::Sequence< sal_Int8 > &rId ) 2119 { 2120 if( rId.getLength() == 16 2121 && 0 == rtl_compareMemory( getUnoTunnelId().getConstArray(), 2122 rId.getConstArray(), 16 ) ) 2123 { 2124 return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) ); 2125 } 2126 return 0; 2127 } 2128 2129 2130 uno::Sequence< uno::Any > SAL_CALL SwChartDataSequence::getData( ) 2131 { 2132 vos::OGuard aGuard( Application::GetSolarMutex() ); 2133 if (bDisposed) 2134 throw lang::DisposedException(); 2135 2136 uno::Sequence< uno::Any > aRes; 2137 SwFrmFmt* pTblFmt = GetFrmFmt(); 2138 if(pTblFmt) 2139 { 2140 SwTable* pTable = SwTable::FindTable( pTblFmt ); 2141 if(!pTable->IsTblComplex()) 2142 { 2143 SwRangeDescriptor aDesc; 2144 if (FillRangeDescriptor( aDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) )) 2145 { 2146 //!! make copy of pTblCrsr (SwUnoCrsr ) 2147 // keep original cursor and make copy of it that gets handed 2148 // over to the SwXCellRange object which takes ownership and 2149 // thus will destroy the copy later. 2150 SwXCellRange aRange( pTblCrsr->Clone(), *pTblFmt, aDesc ); 2151 aRange.GetDataSequence( &aRes, 0, 0 ); 2152 } 2153 } 2154 } 2155 return aRes; 2156 } 2157 2158 2159 OUString SAL_CALL SwChartDataSequence::getSourceRangeRepresentation( ) 2160 { 2161 vos::OGuard aGuard( Application::GetSolarMutex() ); 2162 if (bDisposed) 2163 throw lang::DisposedException(); 2164 2165 String aRes; 2166 SwFrmFmt* pTblFmt = GetFrmFmt(); 2167 if (pTblFmt) 2168 { 2169 aRes = pTblFmt->GetName(); 2170 String aCellRange( GetCellRangeName( *pTblFmt, *pTblCrsr ) ); 2171 DBG_ASSERT( aCellRange.Len() != 0, "failed to get cell range" ); 2172 aRes += (sal_Unicode) '.'; 2173 aRes += aCellRange; 2174 } 2175 return aRes; 2176 } 2177 2178 uno::Sequence< OUString > SAL_CALL SwChartDataSequence::generateLabel( 2179 chart2::data::LabelOrigin eLabelOrigin ) 2180 { 2181 vos::OGuard aGuard( Application::GetSolarMutex() ); 2182 if (bDisposed) 2183 throw lang::DisposedException(); 2184 2185 uno::Sequence< OUString > aLabels; 2186 2187 { 2188 SwRangeDescriptor aDesc; 2189 sal_Bool bOk sal_False; 2190 SwFrmFmt* pTblFmt = GetFrmFmt(); 2191 SwTable* pTable = pTblFmt ? SwTable::FindTable( pTblFmt ) : 0; 2192 if (!pTblFmt || !pTable || pTable->IsTblComplex()) 2193 throw uno::RuntimeException(); 2194 else 2195 { 2196 String aCellRange( GetCellRangeName( *pTblFmt, *pTblCrsr ) ); 2197 DBG_ASSERT( aCellRange.Len() != 0, "failed to get cell range" ); 2198 bOk = FillRangeDescriptor( aDesc, aCellRange ); 2199 DBG_ASSERT( bOk, "failed to get SwRangeDescriptor" ); 2200 } 2201 if (bOk) 2202 { 2203 aDesc.Normalize(); 2204 sal_Int32 nColSpan = aDesc.nRight - aDesc.nLeft + 1; 2205 sal_Int32 nRowSpan = aDesc.nBottom - aDesc.nTop + 1; 2206 DBG_ASSERT( nColSpan == 1 || nRowSpan == 1, 2207 "unexpected range of selected cells" ); 2208 2209 String aTxt; // label text to be returned 2210 sal_Bool bReturnEmptyTxt = sal_False; 2211 sal_Bool bUseCol = sal_True; 2212 if (eLabelOrigin == chart2::data::LabelOrigin_COLUMN) 2213 bUseCol = sal_True; 2214 else if (eLabelOrigin == chart2::data::LabelOrigin_ROW) 2215 bUseCol = sal_False; 2216 else if (eLabelOrigin == chart2::data::LabelOrigin_SHORT_SIDE) 2217 { 2218 bUseCol = nColSpan < nRowSpan; 2219 bReturnEmptyTxt = nColSpan == nRowSpan; 2220 } 2221 else if (eLabelOrigin == chart2::data::LabelOrigin_LONG_SIDE) 2222 { 2223 bUseCol = nColSpan > nRowSpan; 2224 bReturnEmptyTxt = nColSpan == nRowSpan; 2225 } 2226 else { 2227 DBG_ERROR( "unexpected case" ); 2228 } 2229 2230 // build label sequence 2231 // 2232 sal_Int32 nSeqLen = bUseCol ? nColSpan : nRowSpan; 2233 aLabels.realloc( nSeqLen ); 2234 OUString *pLabels = aLabels.getArray(); 2235 for (sal_Int32 i = 0; i < nSeqLen; ++i) 2236 { 2237 if (!bReturnEmptyTxt) 2238 { 2239 aTxt = bUseCol ? aColLabelText : aRowLabelText; 2240 sal_Int32 nCol = aDesc.nLeft; 2241 sal_Int32 nRow = aDesc.nTop; 2242 if (bUseCol) 2243 nCol = nCol + i; 2244 else 2245 nRow = nRow + i; 2246 String aCellName( lcl_GetCellName( nCol, nRow ) ); 2247 2248 xub_StrLen nLen = aCellName.Len(); 2249 if (nLen) 2250 { 2251 const sal_Unicode *pBuf = aCellName.GetBuffer(); 2252 const sal_Unicode *pEnd = pBuf + nLen; 2253 while (pBuf < pEnd && !('0' <= *pBuf && *pBuf <= '9')) 2254 ++pBuf; 2255 // start of number found? 2256 if (pBuf < pEnd && ('0' <= *pBuf && *pBuf <= '9')) 2257 { 2258 String aRplc; 2259 String aNew; 2260 if (bUseCol) 2261 { 2262 aRplc = String::CreateFromAscii( "%COLUMNLETTER" ); 2263 aNew = String( aCellName.GetBuffer(), static_cast<xub_StrLen>(pBuf - aCellName.GetBuffer()) ); 2264 } 2265 else 2266 { 2267 aRplc = String::CreateFromAscii( "%ROWNUMBER" ); 2268 aNew = String( pBuf, static_cast<xub_StrLen>((aCellName.GetBuffer() + nLen) - pBuf) ); 2269 } 2270 xub_StrLen nPos = aTxt.Search( aRplc ); 2271 if (nPos != STRING_NOTFOUND) 2272 aTxt = aTxt.Replace( nPos, aRplc.Len(), aNew ); 2273 } 2274 } 2275 } 2276 pLabels[i] = aTxt; 2277 } 2278 } 2279 } 2280 2281 return aLabels; 2282 } 2283 2284 ::sal_Int32 SAL_CALL SwChartDataSequence::getNumberFormatKeyByIndex( 2285 ::sal_Int32 /*nIndex*/ ) 2286 { 2287 return 0; 2288 } 2289 2290 2291 2292 uno::Sequence< OUString > SAL_CALL SwChartDataSequence::getTextualData( ) 2293 { 2294 vos::OGuard aGuard( Application::GetSolarMutex() ); 2295 if (bDisposed) 2296 throw lang::DisposedException(); 2297 2298 uno::Sequence< OUString > aRes; 2299 SwFrmFmt* pTblFmt = GetFrmFmt(); 2300 if(pTblFmt) 2301 { 2302 SwTable* pTable = SwTable::FindTable( pTblFmt ); 2303 if(!pTable->IsTblComplex()) 2304 { 2305 SwRangeDescriptor aDesc; 2306 if (FillRangeDescriptor( aDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) )) 2307 { 2308 //!! make copy of pTblCrsr (SwUnoCrsr ) 2309 // keep original cursor and make copy of it that gets handed 2310 // over to the SwXCellRange object which takes ownership and 2311 // thus will destroy the copy later. 2312 SwXCellRange aRange( pTblCrsr->Clone(), *pTblFmt, aDesc ); 2313 aRange.GetDataSequence( 0, &aRes, 0 ); 2314 } 2315 } 2316 } 2317 return aRes; 2318 } 2319 2320 2321 uno::Sequence< double > SAL_CALL SwChartDataSequence::getNumericalData( ) 2322 { 2323 vos::OGuard aGuard( Application::GetSolarMutex() ); 2324 if (bDisposed) 2325 throw lang::DisposedException(); 2326 2327 uno::Sequence< double > aRes; 2328 SwFrmFmt* pTblFmt = GetFrmFmt(); 2329 if(pTblFmt) 2330 { 2331 SwTable* pTable = SwTable::FindTable( pTblFmt ); 2332 if(!pTable->IsTblComplex()) 2333 { 2334 SwRangeDescriptor aDesc; 2335 if (FillRangeDescriptor( aDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) )) 2336 { 2337 //!! make copy of pTblCrsr (SwUnoCrsr ) 2338 // keep original cursor and make copy of it that gets handed 2339 // over to the SwXCellRange object which takes ownership and 2340 // thus will destroy the copy later. 2341 SwXCellRange aRange( pTblCrsr->Clone(), *pTblFmt, aDesc ); 2342 2343 // get numerical values and make an effort to return the 2344 // numerical value for text formatted cells 2345 aRange.GetDataSequence( 0, 0, &aRes, sal_True ); 2346 } 2347 } 2348 } 2349 return aRes; 2350 } 2351 2352 2353 uno::Reference< util::XCloneable > SAL_CALL SwChartDataSequence::createClone( ) 2354 { 2355 vos::OGuard aGuard( Application::GetSolarMutex() ); 2356 if (bDisposed) 2357 throw lang::DisposedException(); 2358 return new SwChartDataSequence( *this ); 2359 } 2360 2361 2362 uno::Reference< beans::XPropertySetInfo > SAL_CALL SwChartDataSequence::getPropertySetInfo( ) 2363 { 2364 vos::OGuard aGuard( Application::GetSolarMutex() ); 2365 if (bDisposed) 2366 throw lang::DisposedException(); 2367 2368 static uno::Reference< beans::XPropertySetInfo > xRes = _pPropSet->getPropertySetInfo(); 2369 return xRes; 2370 } 2371 2372 2373 void SAL_CALL SwChartDataSequence::setPropertyValue( 2374 const OUString& rPropertyName, 2375 const uno::Any& rValue ) 2376 { 2377 vos::OGuard aGuard( Application::GetSolarMutex() ); 2378 if (bDisposed) 2379 throw lang::DisposedException(); 2380 2381 if (rPropertyName.equalsAscii( SW_PROP_NAME_STR( UNO_NAME_ROLE ))) 2382 { 2383 if ( !(rValue >>= aRole) ) 2384 throw lang::IllegalArgumentException(); 2385 } 2386 else 2387 throw beans::UnknownPropertyException(); 2388 } 2389 2390 2391 uno::Any SAL_CALL SwChartDataSequence::getPropertyValue( 2392 const OUString& rPropertyName ) 2393 { 2394 vos::OGuard aGuard( Application::GetSolarMutex() ); 2395 if (bDisposed) 2396 throw lang::DisposedException(); 2397 2398 uno::Any aRes; 2399 if (rPropertyName.equalsAscii( SW_PROP_NAME_STR( UNO_NAME_ROLE ))) 2400 aRes <<= aRole; 2401 else 2402 throw beans::UnknownPropertyException(); 2403 2404 return aRes; 2405 } 2406 2407 2408 void SAL_CALL SwChartDataSequence::addPropertyChangeListener( 2409 const OUString& /*rPropertyName*/, 2410 const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ ) 2411 { 2412 //vos::OGuard aGuard( Application::GetSolarMutex() ); 2413 DBG_ERROR( "not implemented" ); 2414 } 2415 2416 2417 void SAL_CALL SwChartDataSequence::removePropertyChangeListener( 2418 const OUString& /*rPropertyName*/, 2419 const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ ) 2420 { 2421 //vos::OGuard aGuard( Application::GetSolarMutex() ); 2422 DBG_ERROR( "not implemented" ); 2423 } 2424 2425 2426 void SAL_CALL SwChartDataSequence::addVetoableChangeListener( 2427 const OUString& /*rPropertyName*/, 2428 const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/ ) 2429 { 2430 //vos::OGuard aGuard( Application::GetSolarMutex() ); 2431 DBG_ERROR( "not implemented" ); 2432 } 2433 2434 2435 void SAL_CALL SwChartDataSequence::removeVetoableChangeListener( 2436 const OUString& /*rPropertyName*/, 2437 const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/ ) 2438 { 2439 //vos::OGuard aGuard( Application::GetSolarMutex() ); 2440 DBG_ERROR( "not implemented" ); 2441 } 2442 2443 2444 OUString SAL_CALL SwChartDataSequence::getImplementationName( ) 2445 { 2446 return C2U("SwChartDataSequence"); 2447 } 2448 2449 2450 sal_Bool SAL_CALL SwChartDataSequence::supportsService( 2451 const OUString& rServiceName ) 2452 { 2453 return rServiceName.equalsAscii( SN_DATA_SEQUENCE ); 2454 } 2455 2456 2457 uno::Sequence< OUString > SAL_CALL SwChartDataSequence::getSupportedServiceNames( ) 2458 { 2459 vos::OGuard aGuard( Application::GetSolarMutex() ); 2460 uno::Sequence< OUString > aRes(1); 2461 aRes.getArray()[0] = C2U( SN_DATA_SEQUENCE ); 2462 return aRes; 2463 } 2464 2465 2466 void SwChartDataSequence::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew) 2467 { 2468 ClientModify(this, pOld, pNew ); 2469 2470 // table was deleted or cursor was deleted 2471 if(!GetRegisteredIn() || !aCursorDepend.GetRegisteredIn()) 2472 { 2473 pTblCrsr = 0; 2474 dispose(); 2475 } 2476 else 2477 { 2478 setModified( sal_True ); 2479 } 2480 } 2481 2482 2483 sal_Bool SAL_CALL SwChartDataSequence::isModified( ) 2484 { 2485 vos::OGuard aGuard( Application::GetSolarMutex() ); 2486 if (bDisposed) 2487 throw lang::DisposedException(); 2488 2489 return sal_True; 2490 } 2491 2492 2493 void SAL_CALL SwChartDataSequence::setModified( 2494 ::sal_Bool bModified ) 2495 { 2496 vos::OGuard aGuard( Application::GetSolarMutex() ); 2497 if (bDisposed) 2498 throw lang::DisposedException(); 2499 2500 if (bModified) 2501 LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) ); 2502 } 2503 2504 2505 void SAL_CALL SwChartDataSequence::addModifyListener( 2506 const uno::Reference< util::XModifyListener >& rxListener ) 2507 { 2508 osl::MutexGuard aGuard( GetChartMutex() ); 2509 if (!bDisposed && rxListener.is()) 2510 aModifyListeners.addInterface( rxListener ); 2511 } 2512 2513 2514 void SAL_CALL SwChartDataSequence::removeModifyListener( 2515 const uno::Reference< util::XModifyListener >& rxListener ) 2516 { 2517 osl::MutexGuard aGuard( GetChartMutex() ); 2518 if (!bDisposed && rxListener.is()) 2519 aModifyListeners.removeInterface( rxListener ); 2520 } 2521 2522 2523 void SAL_CALL SwChartDataSequence::disposing( const lang::EventObject& rSource ) 2524 { 2525 if (bDisposed) 2526 throw lang::DisposedException(); 2527 if (rSource.Source == xDataProvider) 2528 { 2529 pDataProvider = 0; 2530 xDataProvider.clear(); 2531 } 2532 } 2533 2534 2535 void SAL_CALL SwChartDataSequence::dispose( ) 2536 { 2537 sal_Bool bMustDispose( sal_False ); 2538 { 2539 osl::MutexGuard aGuard( GetChartMutex() ); 2540 bMustDispose = !bDisposed; 2541 if (!bDisposed) 2542 bDisposed = sal_True; 2543 } 2544 if (bMustDispose) 2545 { 2546 bDisposed = sal_True; 2547 if (pDataProvider) 2548 { 2549 const SwTable* pTable = SwTable::FindTable( GetFrmFmt() ); 2550 if (pTable) 2551 { 2552 uno::Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY ); 2553 pDataProvider->RemoveDataSequence( *pTable, xRef ); 2554 } 2555 else { 2556 DBG_ERROR( "table missing" ); 2557 } 2558 2559 //Comment: The bug is crashed for an exception threw out in SwCharDataSequence::setModified(), just because 2560 //the SwCharDataSequence object has been disposed. Actually, the former design of SwClient will disband 2561 //itself from the notification list in its destruction. But the SwCharDataSeqence won't be destructed but disposed 2562 //in code (the data member SwChartDataSequence::bDisposed will be set to TRUE), the relationship between client 2563 //and modification are not released. So any notification from modify object will lead said exception threw out. 2564 //Recorrect the logic of code in SwChartDataSequence::Dispose(), release the relationship inside... 2565 SwModify* pRegisteredIn = GetRegisteredInNonConst(); 2566 if (pRegisteredIn && pRegisteredIn->GetDepends()) 2567 { 2568 pRegisteredIn->Remove(this); 2569 pTblCrsr = NULL; 2570 } 2571 2572 } 2573 2574 // require listeners to release references to this object 2575 lang::EventObject aEvtObj( dynamic_cast< chart2::data::XDataSequence * >(this) ); 2576 aModifyListeners.disposeAndClear( aEvtObj ); 2577 aEvtListeners.disposeAndClear( aEvtObj ); 2578 } 2579 } 2580 2581 2582 void SAL_CALL SwChartDataSequence::addEventListener( 2583 const uno::Reference< lang::XEventListener >& rxListener ) 2584 { 2585 osl::MutexGuard aGuard( GetChartMutex() ); 2586 if (!bDisposed && rxListener.is()) 2587 aEvtListeners.addInterface( rxListener ); 2588 } 2589 2590 2591 void SAL_CALL SwChartDataSequence::removeEventListener( 2592 const uno::Reference< lang::XEventListener >& rxListener ) 2593 { 2594 osl::MutexGuard aGuard( GetChartMutex() ); 2595 if (!bDisposed && rxListener.is()) 2596 aEvtListeners.removeInterface( rxListener ); 2597 } 2598 2599 2600 sal_Bool SwChartDataSequence::DeleteBox( const SwTableBox &rBox ) 2601 { 2602 if (bDisposed) 2603 throw lang::DisposedException(); 2604 2605 #if OSL_DEBUG_LEVEL > 1 2606 String aBoxName( rBox.GetName() ); 2607 #endif 2608 2609 // to be set if the last box of the data-sequence was removed here 2610 sal_Bool bNowEmpty = sal_False; 2611 2612 // if the implementation cursor gets affected (i.e. thew box where it is located 2613 // in gets removed) we need to move it before that... (otherwise it does not need to change) 2614 // 2615 const SwStartNode* pPointStartNode = pTblCrsr->GetPoint()->nNode.GetNode().FindTableBoxStartNode(); 2616 const SwStartNode* pMarkStartNode = pTblCrsr->GetMark()->nNode.GetNode().FindTableBoxStartNode(); 2617 // 2618 if (!pTblCrsr->HasMark() || (pPointStartNode == rBox.GetSttNd() && pMarkStartNode == rBox.GetSttNd())) 2619 { 2620 bNowEmpty = sal_True; 2621 } 2622 else if (pPointStartNode == rBox.GetSttNd() || pMarkStartNode == rBox.GetSttNd()) 2623 { 2624 sal_Int32 nPointRow = -1, nPointCol = -1; 2625 sal_Int32 nMarkRow = -1, nMarkCol = -1; 2626 const SwTable* pTable = SwTable::FindTable( GetFrmFmt() ); 2627 String aPointCellName( pTable->GetTblBox( pPointStartNode->GetIndex() )->GetName() ); 2628 String aMarkCellName( pTable->GetTblBox( pMarkStartNode->GetIndex() )->GetName() ); 2629 2630 lcl_GetCellPosition( aPointCellName, nPointCol, nPointRow ); 2631 lcl_GetCellPosition( aMarkCellName, nMarkCol, nMarkRow ); 2632 DBG_ASSERT( nPointRow >= 0 && nPointCol >= 0, "invalid row and col" ); 2633 DBG_ASSERT( nMarkRow >= 0 && nMarkCol >= 0, "invalid row and col" ); 2634 2635 // move vertical or horizontal? 2636 DBG_ASSERT( nPointRow == nMarkRow || nPointCol == nMarkCol, 2637 "row/col indices not matching" ); 2638 DBG_ASSERT( nPointRow != nMarkRow || nPointCol != nMarkCol, 2639 "point and mark are identical" ); 2640 sal_Bool bMoveVertical = (nPointCol == nMarkCol); 2641 sal_Bool bMoveHorizontal = (nPointRow == nMarkRow); 2642 2643 // get movement direction 2644 sal_Bool bMoveLeft = sal_False; // move left or right? 2645 sal_Bool bMoveUp = sal_False; // move up or down? 2646 if (bMoveVertical) 2647 { 2648 if (pPointStartNode == rBox.GetSttNd()) // move point? 2649 bMoveUp = nPointRow > nMarkRow; 2650 else // move mark 2651 bMoveUp = nMarkRow > nPointRow; 2652 } 2653 else if (bMoveHorizontal) 2654 { 2655 if (pPointStartNode == rBox.GetSttNd()) // move point? 2656 bMoveLeft = nPointCol > nMarkCol; 2657 else // move mark 2658 bMoveLeft = nMarkCol > nPointCol; 2659 } 2660 else { 2661 DBG_ERROR( "neither vertical nor horizontal movement" ); 2662 } 2663 2664 // get new box (position) to use... 2665 sal_Int32 nRow = (pPointStartNode == rBox.GetSttNd()) ? nPointRow : nMarkRow; 2666 sal_Int32 nCol = (pPointStartNode == rBox.GetSttNd()) ? nPointCol : nMarkCol; 2667 if (bMoveVertical) 2668 nRow += bMoveUp ? -1 : +1; 2669 if (bMoveHorizontal) 2670 nCol += bMoveLeft ? -1 : +1; 2671 String aNewCellName = lcl_GetCellName( nCol, nRow ); 2672 SwTableBox* pNewBox = (SwTableBox*) pTable->GetTblBox( aNewCellName ); 2673 2674 if (pNewBox) // set new position (cell range) to use 2675 { 2676 // So erhält man den ersten Inhaltsnode in einer gegebenen Zelle: 2677 // Zunächst einen SwNodeIndex auf den Node hinter dem SwStartNode der Box... 2678 SwNodeIndex aIdx( *pNewBox->GetSttNd(), +1 ); 2679 // Dies kann ein SwCntntNode sein, kann aber auch ein Tabellen oder Sectionnode sein, 2680 // deshalb das GoNext; 2681 SwCntntNode *pCNd = aIdx.GetNode().GetCntntNode(); 2682 if (!pCNd) 2683 pCNd = GetFrmFmt()->GetDoc()->GetNodes().GoNext( &aIdx ); 2684 //und damit kann man z.B. eine SwPosition erzeugen: 2685 SwPosition aNewPos( *pCNd ); // new position to beused with cursor 2686 2687 // if the mark is to be changed make sure there is one... 2688 if (pMarkStartNode == rBox.GetSttNd() && !pTblCrsr->HasMark()) 2689 pTblCrsr->SetMark(); 2690 2691 // set cursor to new position... 2692 SwPosition *pPos = (pPointStartNode == rBox.GetSttNd()) ? 2693 pTblCrsr->GetPoint() : pTblCrsr->GetMark(); 2694 if (pPos) 2695 { 2696 pPos->nNode = aNewPos.nNode; 2697 pPos->nContent = aNewPos.nContent; 2698 } 2699 else { 2700 DBG_ERROR( "neither point nor mark available for change" ); 2701 } 2702 } 2703 else { 2704 DBG_ERROR( "failed to get position" ); 2705 } 2706 } 2707 2708 return bNowEmpty; 2709 } 2710 2711 2712 void SwChartDataSequence::FillRangeDesc( SwRangeDescriptor &rRangeDesc ) const 2713 { 2714 SwFrmFmt* pTblFmt = GetFrmFmt(); 2715 if(pTblFmt) 2716 { 2717 SwTable* pTable = SwTable::FindTable( pTblFmt ); 2718 if(!pTable->IsTblComplex()) 2719 { 2720 FillRangeDescriptor( rRangeDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) ); 2721 } 2722 } 2723 } 2724 2725 /** 2726 SwChartDataSequence::ExtendTo 2727 2728 extends the data-sequence by new cells added at the end of the direction 2729 the data-sequence points to. 2730 If the cells are already within the range of the sequence nothing needs 2731 to be done. 2732 If the cells are beyond the end of the sequence (are not adjacent to the 2733 current last cell) nothing can be done. Only if the cells are adjacent to 2734 the last cell they can be added. 2735 2736 @returns true if the data-sequence was changed. 2737 @param bExtendCols 2738 specifies if columns or rows are to be extended 2739 @param nFirstNew 2740 index of first new row/col to be included in data-sequence 2741 @param nLastNew 2742 index of last new row/col to be included in data-sequence 2743 */ 2744 bool SwChartDataSequence::ExtendTo( bool bExtendCol, 2745 sal_Int32 nFirstNew, sal_Int32 nCount ) 2746 { 2747 bool bChanged = false; 2748 2749 SwUnoTableCrsr* pUnoTblCrsr = dynamic_cast<SwUnoTableCrsr*>(pTblCrsr); 2750 //pUnoTblCrsr->MakeBoxSels(); 2751 2752 const SwStartNode *pStartNd = 0; 2753 const SwTableBox *pStartBox = 0; 2754 const SwTableBox *pEndBox = 0; 2755 2756 const SwTable* pTable = SwTable::FindTable( GetFrmFmt() ); 2757 DBG_ASSERT( !pTable->IsTblComplex(), "table too complex" ); 2758 if (nCount < 1 || nFirstNew < 0 || pTable->IsTblComplex()) 2759 return false; 2760 2761 // 2762 // get range descriptor (cell range) for current data-sequence 2763 // 2764 pStartNd = pUnoTblCrsr->GetPoint()->nNode.GetNode().FindTableBoxStartNode(); 2765 pEndBox = pTable->GetTblBox( pStartNd->GetIndex() ); 2766 const String aEndBox( pEndBox->GetName() ); 2767 // 2768 pStartNd = pUnoTblCrsr->GetMark()->nNode.GetNode().FindTableBoxStartNode(); 2769 pStartBox = pTable->GetTblBox( pStartNd->GetIndex() ); 2770 const String aStartBox( pStartBox->GetName() ); 2771 // 2772 String aCellRange( aStartBox ); // note that cell range here takes the newly added rows/cols already into account 2773 aCellRange.AppendAscii( ":" ); 2774 aCellRange += aEndBox; 2775 SwRangeDescriptor aDesc; 2776 FillRangeDescriptor( aDesc, aCellRange ); 2777 2778 String aNewStartCell; 2779 String aNewEndCell; 2780 if (bExtendCol && aDesc.nBottom + 1 == nFirstNew) 2781 { 2782 // new column cells adjacent to the bottom of the 2783 // current data-sequence to be added... 2784 DBG_ASSERT( aDesc.nLeft == aDesc.nRight, "data-sequence is not a column" ); 2785 aNewStartCell = lcl_GetCellName(aDesc.nLeft, aDesc.nTop); 2786 aNewEndCell = lcl_GetCellName(aDesc.nRight, aDesc.nBottom + nCount); 2787 bChanged = true; 2788 } 2789 else if (bExtendCol && aDesc.nTop - nCount == nFirstNew) 2790 { 2791 // new column cells adjacent to the top of the 2792 // current data-sequence to be added... 2793 DBG_ASSERT( aDesc.nLeft == aDesc.nRight, "data-sequence is not a column" ); 2794 aNewStartCell = lcl_GetCellName(aDesc.nLeft, aDesc.nTop - nCount); 2795 aNewEndCell = lcl_GetCellName(aDesc.nRight, aDesc.nBottom); 2796 bChanged = true; 2797 } 2798 else if (!bExtendCol && aDesc.nRight + 1 == nFirstNew) 2799 { 2800 // new row cells adjacent to the right of the 2801 // current data-sequence to be added... 2802 DBG_ASSERT( aDesc.nTop == aDesc.nBottom, "data-sequence is not a row" ); 2803 aNewStartCell = lcl_GetCellName(aDesc.nLeft, aDesc.nTop); 2804 aNewEndCell = lcl_GetCellName(aDesc.nRight + nCount, aDesc.nBottom); 2805 bChanged = true; 2806 } 2807 else if (!bExtendCol && aDesc.nLeft - nCount == nFirstNew) 2808 { 2809 // new row cells adjacent to the left of the 2810 // current data-sequence to be added... 2811 DBG_ASSERT( aDesc.nTop == aDesc.nBottom, "data-sequence is not a row" ); 2812 aNewStartCell = lcl_GetCellName(aDesc.nLeft - nCount, aDesc.nTop); 2813 aNewEndCell = lcl_GetCellName(aDesc.nRight, aDesc.nBottom); 2814 bChanged = true; 2815 } 2816 2817 if (bChanged) 2818 { 2819 // move table cursor to new start and end of data-sequence 2820 const SwTableBox *pNewStartBox = pTable->GetTblBox( aNewStartCell ); 2821 const SwTableBox *pNewEndBox = pTable->GetTblBox( aNewEndCell ); 2822 pUnoTblCrsr->SetMark(); 2823 pUnoTblCrsr->GetPoint()->nNode = *pNewEndBox->GetSttNd(); 2824 pUnoTblCrsr->GetMark()->nNode = *pNewStartBox->GetSttNd(); 2825 pUnoTblCrsr->Move( fnMoveForward, fnGoNode ); 2826 pUnoTblCrsr->MakeBoxSels(); 2827 } 2828 2829 return bChanged; 2830 } 2831 2832 ////////////////////////////////////////////////////////////////////// 2833 2834 SwChartLabeledDataSequence::SwChartLabeledDataSequence() : 2835 aEvtListeners( GetChartMutex() ), 2836 aModifyListeners( GetChartMutex() ) 2837 { 2838 bDisposed = sal_False; 2839 } 2840 2841 2842 SwChartLabeledDataSequence::~SwChartLabeledDataSequence() 2843 { 2844 } 2845 2846 2847 uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartLabeledDataSequence::getValues( ) 2848 { 2849 vos::OGuard aGuard( Application::GetSolarMutex() ); 2850 if (bDisposed) 2851 throw lang::DisposedException(); 2852 return xData; 2853 } 2854 2855 2856 void SwChartLabeledDataSequence::SetDataSequence( 2857 uno::Reference< chart2::data::XDataSequence >& rxDest, 2858 const uno::Reference< chart2::data::XDataSequence >& rxSource) 2859 { 2860 uno::Reference< util::XModifyListener > xML( dynamic_cast< util::XModifyListener* >(this), uno::UNO_QUERY ); 2861 uno::Reference< lang::XEventListener > xEL( dynamic_cast< lang::XEventListener* >(this), uno::UNO_QUERY ); 2862 2863 // stop listening to old data-sequence 2864 uno::Reference< util::XModifyBroadcaster > xMB( rxDest, uno::UNO_QUERY ); 2865 if (xMB.is()) 2866 xMB->removeModifyListener( xML ); 2867 uno::Reference< lang::XComponent > xC( rxDest, uno::UNO_QUERY ); 2868 if (xC.is()) 2869 xC->removeEventListener( xEL ); 2870 2871 rxDest = rxSource; 2872 2873 // start listening to new data-sequence 2874 xC = uno::Reference< lang::XComponent >( rxDest, uno::UNO_QUERY ); 2875 if (xC.is()) 2876 xC->addEventListener( xEL ); 2877 xMB = uno::Reference< util::XModifyBroadcaster >( rxDest, uno::UNO_QUERY ); 2878 if (xMB.is()) 2879 xMB->addModifyListener( xML ); 2880 } 2881 2882 2883 void SAL_CALL SwChartLabeledDataSequence::setValues( 2884 const uno::Reference< chart2::data::XDataSequence >& rxSequence ) 2885 { 2886 vos::OGuard aGuard( Application::GetSolarMutex() ); 2887 if (bDisposed) 2888 throw lang::DisposedException(); 2889 2890 if (xData != rxSequence) 2891 { 2892 SetDataSequence( xData, rxSequence ); 2893 // inform listeners of changes 2894 LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) ); 2895 } 2896 } 2897 2898 2899 uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartLabeledDataSequence::getLabel( ) 2900 { 2901 vos::OGuard aGuard( Application::GetSolarMutex() ); 2902 if (bDisposed) 2903 throw lang::DisposedException(); 2904 return xLabels; 2905 } 2906 2907 2908 void SAL_CALL SwChartLabeledDataSequence::setLabel( 2909 const uno::Reference< chart2::data::XDataSequence >& rxSequence ) 2910 { 2911 vos::OGuard aGuard( Application::GetSolarMutex() ); 2912 if (bDisposed) 2913 throw lang::DisposedException(); 2914 2915 if (xLabels != rxSequence) 2916 { 2917 SetDataSequence( xLabels, rxSequence ); 2918 // inform listeners of changes 2919 LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) ); 2920 } 2921 } 2922 2923 2924 uno::Reference< util::XCloneable > SAL_CALL SwChartLabeledDataSequence::createClone( ) 2925 { 2926 vos::OGuard aGuard( Application::GetSolarMutex() ); 2927 if (bDisposed) 2928 throw lang::DisposedException(); 2929 2930 uno::Reference< util::XCloneable > xRes; 2931 2932 uno::Reference< util::XCloneable > xDataCloneable( xData, uno::UNO_QUERY ); 2933 uno::Reference< util::XCloneable > xLabelsCloneable( xLabels, uno::UNO_QUERY ); 2934 SwChartLabeledDataSequence *pRes = new SwChartLabeledDataSequence(); 2935 if (xDataCloneable.is()) 2936 { 2937 uno::Reference< chart2::data::XDataSequence > xDataClone( xDataCloneable->createClone(), uno::UNO_QUERY ); 2938 pRes->setValues( xDataClone ); 2939 } 2940 2941 if (xLabelsCloneable.is()) 2942 { 2943 uno::Reference< chart2::data::XDataSequence > xLabelsClone( xLabelsCloneable->createClone(), uno::UNO_QUERY ); 2944 pRes->setLabel( xLabelsClone ); 2945 } 2946 xRes = pRes; 2947 return xRes; 2948 } 2949 2950 2951 OUString SAL_CALL SwChartLabeledDataSequence::getImplementationName( ) 2952 { 2953 return C2U("SwChartLabeledDataSequence"); 2954 } 2955 2956 2957 sal_Bool SAL_CALL SwChartLabeledDataSequence::supportsService( 2958 const OUString& rServiceName ) 2959 { 2960 return rServiceName.equalsAscii( SN_LABELED_DATA_SEQUENCE ); 2961 } 2962 2963 2964 uno::Sequence< OUString > SAL_CALL SwChartLabeledDataSequence::getSupportedServiceNames( ) 2965 { 2966 vos::OGuard aGuard( Application::GetSolarMutex() ); 2967 uno::Sequence< OUString > aRes(1); 2968 aRes.getArray()[0] = C2U( SN_LABELED_DATA_SEQUENCE ); 2969 return aRes; 2970 } 2971 2972 2973 void SAL_CALL SwChartLabeledDataSequence::disposing( 2974 const lang::EventObject& rSource ) 2975 { 2976 osl::MutexGuard aGuard( GetChartMutex() ); 2977 uno::Reference< uno::XInterface > xRef( rSource.Source ); 2978 if (xRef == xData) 2979 xData.clear(); 2980 if (xRef == xLabels) 2981 xLabels.clear(); 2982 if (!xData.is() && !xLabels.is()) 2983 dispose(); 2984 } 2985 2986 2987 void SAL_CALL SwChartLabeledDataSequence::modified( 2988 const lang::EventObject& rEvent ) 2989 { 2990 if (rEvent.Source == xData || rEvent.Source == xLabels) 2991 { 2992 LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) ); 2993 } 2994 } 2995 2996 2997 void SAL_CALL SwChartLabeledDataSequence::addModifyListener( 2998 const uno::Reference< util::XModifyListener >& rxListener ) 2999 { 3000 osl::MutexGuard aGuard( GetChartMutex() ); 3001 if (!bDisposed && rxListener.is()) 3002 aModifyListeners.addInterface( rxListener ); 3003 } 3004 3005 3006 void SAL_CALL SwChartLabeledDataSequence::removeModifyListener( 3007 const uno::Reference< util::XModifyListener >& rxListener ) 3008 { 3009 osl::MutexGuard aGuard( GetChartMutex() ); 3010 if (!bDisposed && rxListener.is()) 3011 aModifyListeners.removeInterface( rxListener ); 3012 } 3013 3014 3015 void SAL_CALL SwChartLabeledDataSequence::dispose( ) 3016 { 3017 sal_Bool bMustDispose( sal_False ); 3018 { 3019 osl::MutexGuard aGuard( GetChartMutex() ); 3020 bMustDispose = !bDisposed; 3021 if (!bDisposed) 3022 bDisposed = sal_True; 3023 } 3024 if (bMustDispose) 3025 { 3026 bDisposed = sal_True; 3027 3028 // require listeners to release references to this object 3029 lang::EventObject aEvtObj( dynamic_cast< chart2::data::XLabeledDataSequence * >(this) ); 3030 aModifyListeners.disposeAndClear( aEvtObj ); 3031 aEvtListeners.disposeAndClear( aEvtObj ); 3032 } 3033 } 3034 3035 3036 void SAL_CALL SwChartLabeledDataSequence::addEventListener( 3037 const uno::Reference< lang::XEventListener >& rxListener ) 3038 { 3039 osl::MutexGuard aGuard( GetChartMutex() ); 3040 if (!bDisposed && rxListener.is()) 3041 aEvtListeners.addInterface( rxListener ); 3042 } 3043 3044 3045 void SAL_CALL SwChartLabeledDataSequence::removeEventListener( 3046 const uno::Reference< lang::XEventListener >& rxListener ) 3047 { 3048 osl::MutexGuard aGuard( GetChartMutex() ); 3049 if (!bDisposed && rxListener.is()) 3050 aEvtListeners.removeInterface( rxListener ); 3051 } 3052 3053 ////////////////////////////////////////////////////////////////////// 3054