1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_sc.hxx" 30 31 32 33 #include <vcl/svapp.hxx> 34 35 #include "chartlis.hxx" 36 #include "brdcst.hxx" 37 #include "document.hxx" 38 #include "reftokenhelper.hxx" 39 40 using namespace com::sun::star; 41 using ::std::vector; 42 using ::std::list; 43 using ::std::hash_set; 44 using ::std::auto_ptr; 45 using ::std::unary_function; 46 using ::std::for_each; 47 48 //2do: DocOption TimeOut? 49 //#define SC_CHARTTIMEOUT 1000 // eine Sekunde keine Aenderung/KeyEvent 50 51 // Update chart listeners quickly, to get a similar behavior to loaded charts 52 // which register UNO listeners. 53 #define SC_CHARTTIMEOUT 10 54 55 56 // ==================================================================== 57 58 class ScChartUnoData 59 { 60 uno::Reference< chart::XChartDataChangeEventListener > xListener; 61 uno::Reference< chart::XChartData > xSource; 62 63 public: 64 ScChartUnoData( const uno::Reference< chart::XChartDataChangeEventListener >& rL, 65 const uno::Reference< chart::XChartData >& rS ) : 66 xListener( rL ), xSource( rS ) {} 67 ~ScChartUnoData() {} 68 69 const uno::Reference< chart::XChartDataChangeEventListener >& GetListener() const { return xListener; } 70 const uno::Reference< chart::XChartData >& GetSource() const { return xSource; } 71 }; 72 73 74 // === ScChartListener ================================================ 75 76 ScChartListener::ExternalRefListener::ExternalRefListener(ScChartListener& rParent, ScDocument* pDoc) : 77 mrParent(rParent), mpDoc(pDoc) 78 { 79 } 80 81 ScChartListener::ExternalRefListener::~ExternalRefListener() 82 { 83 if (!mpDoc || mpDoc->IsInDtorClear()) 84 // The document is being destroyed. Do nothing. 85 return; 86 87 // Make sure to remove all pointers to this object. 88 mpDoc->GetExternalRefManager()->removeLinkListener(this); 89 } 90 91 void ScChartListener::ExternalRefListener::notify(sal_uInt16 nFileId, ScExternalRefManager::LinkUpdateType eType) 92 { 93 switch (eType) 94 { 95 case ScExternalRefManager::LINK_MODIFIED: 96 { 97 if (maFileIds.count(nFileId)) 98 // We are listening to this external document. Send an update 99 // requst to the chart. 100 mrParent.SetUpdateQueue(); 101 } 102 break; 103 case ScExternalRefManager::LINK_BROKEN: 104 removeFileId(nFileId); 105 break; 106 } 107 } 108 109 void ScChartListener::ExternalRefListener::addFileId(sal_uInt16 nFileId) 110 { 111 maFileIds.insert(nFileId); 112 } 113 114 void ScChartListener::ExternalRefListener::removeFileId(sal_uInt16 nFileId) 115 { 116 maFileIds.erase(nFileId); 117 } 118 119 hash_set<sal_uInt16>& ScChartListener::ExternalRefListener::getAllFileIds() 120 { 121 return maFileIds; 122 } 123 124 // ---------------------------------------------------------------------------- 125 126 ScChartListener::ScChartListener( const String& rName, ScDocument* pDocP, 127 const ScRange& rRange ) : 128 StrData( rName ), 129 SvtListener(), 130 mpExtRefListener(NULL), 131 mpTokens(new vector<ScSharedTokenRef>), 132 pUnoData( NULL ), 133 pDoc( pDocP ), 134 bUsed( sal_False ), 135 bDirty( sal_False ), 136 bSeriesRangesScheduled( sal_False ) 137 { 138 SetRangeList( rRange ); 139 } 140 141 ScChartListener::ScChartListener( const String& rName, ScDocument* pDocP, 142 const ScRangeListRef& rRangeList ) : 143 StrData( rName ), 144 SvtListener(), 145 mpExtRefListener(NULL), 146 mpTokens(new vector<ScSharedTokenRef>), 147 pUnoData( NULL ), 148 pDoc( pDocP ), 149 bUsed( sal_False ), 150 bDirty( sal_False ), 151 bSeriesRangesScheduled( sal_False ) 152 { 153 ScRefTokenHelper::getTokensFromRangeList(*mpTokens, *rRangeList); 154 } 155 156 ScChartListener::ScChartListener( const String& rName, ScDocument* pDocP, vector<ScSharedTokenRef>* pTokens ) : 157 StrData( rName ), 158 SvtListener(), 159 mpExtRefListener(NULL), 160 mpTokens(pTokens), 161 pUnoData( NULL ), 162 pDoc( pDocP ), 163 bUsed( sal_False ), 164 bDirty( sal_False ), 165 bSeriesRangesScheduled( sal_False ) 166 { 167 } 168 169 ScChartListener::ScChartListener( const ScChartListener& r ) : 170 StrData( r ), 171 SvtListener(), 172 mpExtRefListener(NULL), 173 mpTokens(new vector<ScSharedTokenRef>(*r.mpTokens)), 174 pUnoData( NULL ), 175 pDoc( r.pDoc ), 176 bUsed( sal_False ), 177 bDirty( r.bDirty ), 178 bSeriesRangesScheduled( r.bSeriesRangesScheduled ) 179 { 180 if ( r.pUnoData ) 181 pUnoData = new ScChartUnoData( *r.pUnoData ); 182 183 if (r.mpExtRefListener.get()) 184 { 185 // Re-register this new listener for the files that the old listener 186 // was listening to. 187 188 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); 189 const hash_set<sal_uInt16>& rFileIds = r.mpExtRefListener->getAllFileIds(); 190 mpExtRefListener.reset(new ExternalRefListener(*this, pDoc)); 191 hash_set<sal_uInt16>::const_iterator itr = rFileIds.begin(), itrEnd = rFileIds.end(); 192 for (; itr != itrEnd; ++itr) 193 { 194 pRefMgr->addLinkListener(*itr, mpExtRefListener.get()); 195 mpExtRefListener->addFileId(*itr); 196 } 197 } 198 } 199 200 ScChartListener::~ScChartListener() 201 { 202 if ( HasBroadcaster() ) 203 EndListeningTo(); 204 delete pUnoData; 205 206 if (mpExtRefListener.get()) 207 { 208 // Stop listening to all external files. 209 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); 210 const hash_set<sal_uInt16>& rFileIds = mpExtRefListener->getAllFileIds(); 211 hash_set<sal_uInt16>::const_iterator itr = rFileIds.begin(), itrEnd = rFileIds.end(); 212 for (; itr != itrEnd; ++itr) 213 pRefMgr->removeLinkListener(*itr, mpExtRefListener.get()); 214 } 215 } 216 217 ScDataObject* ScChartListener::Clone() const 218 { 219 return new ScChartListener( *this ); 220 } 221 222 void ScChartListener::SetUno( 223 const uno::Reference< chart::XChartDataChangeEventListener >& rListener, 224 const uno::Reference< chart::XChartData >& rSource ) 225 { 226 // DBG_ASSERT( rListener.is() && rSource.is(), "Nullpointer bei SetUno" ); 227 delete pUnoData; 228 pUnoData = new ScChartUnoData( rListener, rSource ); 229 } 230 231 uno::Reference< chart::XChartDataChangeEventListener > ScChartListener::GetUnoListener() const 232 { 233 if ( pUnoData ) 234 return pUnoData->GetListener(); 235 return uno::Reference< chart::XChartDataChangeEventListener >(); 236 } 237 238 uno::Reference< chart::XChartData > ScChartListener::GetUnoSource() const 239 { 240 if ( pUnoData ) 241 return pUnoData->GetSource(); 242 return uno::Reference< chart::XChartData >(); 243 } 244 245 void ScChartListener::Notify( SvtBroadcaster&, const SfxHint& rHint ) 246 { 247 const ScHint* p = dynamic_cast<const ScHint*>(&rHint); 248 if (p && (p->GetId() & (SC_HINT_DATACHANGED | SC_HINT_DYING))) 249 SetUpdateQueue(); 250 } 251 252 void ScChartListener::Update() 253 { 254 if ( pDoc->IsInInterpreter() ) 255 { // #73482# If interpreting do nothing and restart timer so we don't 256 // interfere with interpreter and don't produce an Err522 or similar. 257 // This may happen if we are rescheduled via Basic function. 258 pDoc->GetChartListenerCollection()->StartTimer(); 259 return ; 260 } 261 if ( pUnoData ) 262 { 263 bDirty = sal_False; 264 //! irgendwann mal erkennen, was sich innerhalb des Charts geaendert hat 265 chart::ChartDataChangeEvent aEvent( pUnoData->GetSource(), 266 chart::ChartDataChangeType_ALL, 267 0, 0, 0, 0 ); 268 pUnoData->GetListener()->chartDataChanged( aEvent ); 269 } 270 else if ( pDoc->GetAutoCalc() ) 271 { 272 bDirty = sal_False; 273 pDoc->UpdateChart( GetString()); 274 } 275 } 276 277 ScRangeListRef ScChartListener::GetRangeList() const 278 { 279 ScRangeListRef aRLRef(new ScRangeList); 280 ScRefTokenHelper::getRangeListFromTokens(*aRLRef, *mpTokens); 281 return aRLRef; 282 } 283 284 void ScChartListener::SetRangeList( const ScRangeListRef& rNew ) 285 { 286 vector<ScSharedTokenRef> aTokens; 287 ScRefTokenHelper::getTokensFromRangeList(aTokens, *rNew); 288 mpTokens->swap(aTokens); 289 } 290 291 void ScChartListener::SetRangeList( const ScRange& rRange ) 292 { 293 ScSharedTokenRef pToken; 294 ScRefTokenHelper::getTokenFromRange(pToken, rRange); 295 mpTokens->push_back(pToken); 296 } 297 298 namespace { 299 300 class StartEndListening : public unary_function<ScSharedTokenRef, void> 301 { 302 public: 303 StartEndListening(ScDocument* pDoc, ScChartListener& rParent, bool bStart) : 304 mpDoc(pDoc), mrParent(rParent), mbStart(bStart) {} 305 306 void operator() (const ScSharedTokenRef& pToken) 307 { 308 if (!ScRefTokenHelper::isRef(pToken)) 309 return; 310 311 bool bExternal = ScRefTokenHelper::isExternalRef(pToken); 312 if (bExternal) 313 { 314 sal_uInt16 nFileId = pToken->GetIndex(); 315 ScExternalRefManager* pRefMgr = mpDoc->GetExternalRefManager(); 316 ScChartListener::ExternalRefListener* pExtRefListener = mrParent.GetExtRefListener(); 317 if (mbStart) 318 { 319 pRefMgr->addLinkListener(nFileId, pExtRefListener); 320 pExtRefListener->addFileId(nFileId); 321 } 322 else 323 { 324 pRefMgr->removeLinkListener(nFileId, pExtRefListener); 325 pExtRefListener->removeFileId(nFileId); 326 } 327 } 328 else 329 { 330 ScRange aRange; 331 ScRefTokenHelper::getRangeFromToken(aRange, pToken, bExternal); 332 if (mbStart) 333 startListening(aRange); 334 else 335 endListening(aRange); 336 } 337 } 338 339 private: 340 void startListening(const ScRange& rRange) 341 { 342 if (rRange.aStart == rRange.aEnd) 343 mpDoc->StartListeningCell(rRange.aStart, &mrParent); 344 else 345 mpDoc->StartListeningArea(rRange, &mrParent); 346 } 347 348 void endListening(const ScRange& rRange) 349 { 350 if (rRange.aStart == rRange.aEnd) 351 mpDoc->EndListeningCell(rRange.aStart, &mrParent); 352 else 353 mpDoc->EndListeningArea(rRange, &mrParent); 354 } 355 356 private: 357 ScDocument* mpDoc; 358 ScChartListener& mrParent; 359 bool mbStart; 360 }; 361 362 } 363 364 void ScChartListener::StartListeningTo() 365 { 366 if (!mpTokens.get() || mpTokens->empty()) 367 // no references to listen to. 368 return; 369 370 for_each(mpTokens->begin(), mpTokens->end(), StartEndListening(pDoc, *this, true)); 371 } 372 373 void ScChartListener::EndListeningTo() 374 { 375 if (!mpTokens.get() || mpTokens->empty()) 376 // no references to listen to. 377 return; 378 379 for_each(mpTokens->begin(), mpTokens->end(), StartEndListening(pDoc, *this, false)); 380 } 381 382 383 void ScChartListener::ChangeListening( const ScRangeListRef& rRangeListRef, 384 sal_Bool bDirtyP ) 385 { 386 EndListeningTo(); 387 SetRangeList( rRangeListRef ); 388 StartListeningTo(); 389 if ( bDirtyP ) 390 SetDirty( sal_True ); 391 } 392 393 394 void ScChartListener::UpdateScheduledSeriesRanges() 395 { 396 if ( bSeriesRangesScheduled ) 397 { 398 bSeriesRangesScheduled = sal_False; 399 UpdateSeriesRanges(); 400 } 401 } 402 403 404 void ScChartListener::UpdateChartIntersecting( const ScRange& rRange ) 405 { 406 ScSharedTokenRef pToken; 407 ScRefTokenHelper::getTokenFromRange(pToken, rRange); 408 409 if (ScRefTokenHelper::intersects(*mpTokens, pToken)) 410 { 411 // force update (chart has to be loaded), don't use ScChartListener::Update 412 pDoc->UpdateChart( GetString()); 413 } 414 } 415 416 417 void ScChartListener::UpdateSeriesRanges() 418 { 419 ScRangeListRef pRangeList(new ScRangeList); 420 ScRefTokenHelper::getRangeListFromTokens(*pRangeList, *mpTokens); 421 pDoc->SetChartRangeList(GetString(), pRangeList); 422 } 423 424 ScChartListener::ExternalRefListener* ScChartListener::GetExtRefListener() 425 { 426 if (!mpExtRefListener.get()) 427 mpExtRefListener.reset(new ExternalRefListener(*this, pDoc)); 428 429 return mpExtRefListener.get(); 430 } 431 432 void ScChartListener::SetUpdateQueue() 433 { 434 bDirty = true; 435 pDoc->GetChartListenerCollection()->StartTimer(); 436 } 437 438 sal_Bool ScChartListener::operator==( const ScChartListener& r ) 439 { 440 bool b1 = (mpTokens.get() && !mpTokens->empty()); 441 bool b2 = (r.mpTokens.get() && !r.mpTokens->empty()); 442 443 if (pDoc != r.pDoc || bUsed != r.bUsed || bDirty != r.bDirty || 444 bSeriesRangesScheduled != r.bSeriesRangesScheduled || 445 GetString() != r.GetString() || b1 != b2) 446 return false; 447 448 if (!b1 && !b2) 449 // both token list instances are empty. 450 return true; 451 452 return *mpTokens == *r.mpTokens; 453 } 454 455 // ============================================================================ 456 457 ScChartHiddenRangeListener::ScChartHiddenRangeListener() 458 { 459 } 460 461 ScChartHiddenRangeListener::~ScChartHiddenRangeListener() 462 { 463 // empty d'tor 464 } 465 466 // === ScChartListenerCollection ====================================== 467 468 ScChartListenerCollection::RangeListenerItem::RangeListenerItem(const ScRange& rRange, ScChartHiddenRangeListener* p) : 469 maRange(rRange), mpListener(p) 470 { 471 } 472 473 ScChartListenerCollection::ScChartListenerCollection( ScDocument* pDocP ) : 474 ScStrCollection( 4, 4, sal_False ), 475 pDoc( pDocP ) 476 { 477 aTimer.SetTimeoutHdl( LINK( this, ScChartListenerCollection, TimerHdl ) ); 478 } 479 480 ScChartListenerCollection::ScChartListenerCollection( 481 const ScChartListenerCollection& rColl ) : 482 ScStrCollection( rColl ), 483 pDoc( rColl.pDoc ) 484 { 485 aTimer.SetTimeoutHdl( LINK( this, ScChartListenerCollection, TimerHdl ) ); 486 } 487 488 ScChartListenerCollection::~ScChartListenerCollection() 489 { 490 // #96783# remove ChartListener objects before aTimer dtor is called, because 491 // ScChartListener::EndListeningTo may cause ScChartListenerCollection::StartTimer 492 // to be called if an empty ScNoteCell is deleted 493 494 if (GetCount()) 495 FreeAll(); 496 } 497 498 ScDataObject* ScChartListenerCollection::Clone() const 499 { 500 return new ScChartListenerCollection( *this ); 501 } 502 503 void ScChartListenerCollection::StartAllListeners() 504 { 505 for ( sal_uInt16 nIndex = 0; nIndex < nCount; nIndex++ ) 506 { 507 ((ScChartListener*) pItems[ nIndex ])->StartListeningTo(); 508 } 509 } 510 511 void ScChartListenerCollection::ChangeListening( const String& rName, 512 const ScRangeListRef& rRangeListRef, sal_Bool bDirty ) 513 { 514 ScChartListener aCLSearcher( rName, pDoc, rRangeListRef ); 515 ScChartListener* pCL; 516 sal_uInt16 nIndex; 517 if ( Search( &aCLSearcher, nIndex ) ) 518 { 519 pCL = (ScChartListener*) pItems[ nIndex ]; 520 pCL->EndListeningTo(); 521 pCL->SetRangeList( rRangeListRef ); 522 } 523 else 524 { 525 pCL = new ScChartListener( aCLSearcher ); 526 Insert( pCL ); 527 } 528 pCL->StartListeningTo(); 529 if ( bDirty ) 530 pCL->SetDirty( sal_True ); 531 } 532 533 void ScChartListenerCollection::FreeUnused() 534 { 535 // rueckwaerts wg. Pointer-Aufrueckerei im Array 536 for ( sal_uInt16 nIndex = nCount; nIndex-- >0; ) 537 { 538 ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ]; 539 // Uno-Charts nicht rauskicken 540 // (werden per FreeUno von aussen geloescht) 541 if ( !pCL->IsUno() ) 542 { 543 if ( pCL->IsUsed() ) 544 pCL->SetUsed( sal_False ); 545 else 546 Free( pCL ); 547 } 548 } 549 } 550 551 void ScChartListenerCollection::FreeUno( const uno::Reference< chart::XChartDataChangeEventListener >& rListener, 552 const uno::Reference< chart::XChartData >& rSource ) 553 { 554 // rueckwaerts wg. Pointer-Aufrueckerei im Array 555 for ( sal_uInt16 nIndex = nCount; nIndex-- >0; ) 556 { 557 ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ]; 558 if ( pCL->IsUno() && 559 pCL->GetUnoListener() == rListener && 560 pCL->GetUnoSource() == rSource ) 561 { 562 Free( pCL ); 563 } 564 //! sollte nur einmal vorkommen? 565 } 566 } 567 568 void ScChartListenerCollection::StartTimer() 569 { 570 aTimer.SetTimeout( SC_CHARTTIMEOUT ); 571 aTimer.Start(); 572 } 573 574 IMPL_LINK( ScChartListenerCollection, TimerHdl, Timer*, EMPTYARG ) 575 { 576 if ( Application::AnyInput( INPUT_KEYBOARD ) ) 577 { 578 aTimer.Start(); 579 return 0; 580 } 581 UpdateDirtyCharts(); 582 return 0; 583 } 584 585 void ScChartListenerCollection::UpdateDirtyCharts() 586 { 587 for ( sal_uInt16 nIndex = 0; nIndex < nCount; nIndex++ ) 588 { 589 ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ]; 590 if ( pCL->IsDirty() ) 591 pCL->Update(); 592 if ( aTimer.IsActive() && !pDoc->IsImportingXML()) 593 break; // da kam einer dazwischen 594 } 595 } 596 597 598 void ScChartListenerCollection::SetDirty() 599 { 600 for ( sal_uInt16 nIndex = 0; nIndex < nCount; nIndex++ ) 601 { 602 ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ]; 603 pCL->SetDirty( sal_True ); 604 } 605 StartTimer(); 606 } 607 608 609 void ScChartListenerCollection::SetDiffDirty( 610 const ScChartListenerCollection& rCmp, sal_Bool bSetChartRangeLists ) 611 { 612 sal_Bool bDirty = sal_False; 613 for ( sal_uInt16 nIndex = 0; nIndex < nCount; nIndex++ ) 614 { 615 ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ]; 616 sal_uInt16 nFound; 617 sal_Bool bFound = rCmp.Search( pCL, nFound ); 618 if ( !bFound || (*pCL != *((const ScChartListener*) rCmp.pItems[ nFound ])) ) 619 { 620 if ( bSetChartRangeLists ) 621 { 622 if ( bFound ) 623 { 624 const ScRangeListRef& rList1 = pCL->GetRangeList(); 625 const ScRangeListRef& rList2 = 626 ((const ScChartListener*) rCmp.pItems[ nFound ])->GetRangeList(); 627 sal_Bool b1 = rList1.Is(); 628 sal_Bool b2 = rList2.Is(); 629 if ( b1 != b2 || (b1 && b2 && (*rList1 != *rList2)) ) 630 pDoc->SetChartRangeList( pCL->GetString(), rList1 ); 631 } 632 else 633 pDoc->SetChartRangeList( pCL->GetString(), pCL->GetRangeList() ); 634 } 635 bDirty = sal_True; 636 pCL->SetDirty( sal_True ); 637 } 638 } 639 if ( bDirty ) 640 StartTimer(); 641 } 642 643 644 void ScChartListenerCollection::SetRangeDirty( const ScRange& rRange ) 645 { 646 sal_Bool bDirty = sal_False; 647 for ( sal_uInt16 nIndex = 0; nIndex < nCount; nIndex++ ) 648 { 649 ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ]; 650 const ScRangeListRef& rList = pCL->GetRangeList(); 651 if ( rList.Is() && rList->Intersects( rRange ) ) 652 { 653 bDirty = sal_True; 654 pCL->SetDirty( sal_True ); 655 } 656 } 657 if ( bDirty ) 658 StartTimer(); 659 660 // New hidden range listener implementation 661 for (list<RangeListenerItem>::iterator itr = maHiddenListeners.begin(), itrEnd = maHiddenListeners.end(); 662 itr != itrEnd; ++itr) 663 { 664 if (itr->maRange.Intersects(rRange)) 665 itr->mpListener->notify(); 666 } 667 } 668 669 670 void ScChartListenerCollection::UpdateScheduledSeriesRanges() 671 { 672 for ( sal_uInt16 nIndex = 0; nIndex < nCount; nIndex++ ) 673 { 674 ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ]; 675 pCL->UpdateScheduledSeriesRanges(); 676 } 677 } 678 679 680 void ScChartListenerCollection::UpdateChartsContainingTab( SCTAB nTab ) 681 { 682 ScRange aRange( 0, 0, nTab, MAXCOL, MAXROW, nTab ); 683 for ( sal_uInt16 nIndex = 0; nIndex < nCount; nIndex++ ) 684 { 685 ScChartListener* pCL = (ScChartListener*) pItems[ nIndex ]; 686 pCL->UpdateChartIntersecting( aRange ); 687 } 688 } 689 690 691 sal_Bool ScChartListenerCollection::operator==( const ScChartListenerCollection& r ) 692 { 693 // hier nicht ScStrCollection::operator==() verwenden, der umstaendlich via 694 // IsEqual und Compare laeuft, stattdessen ScChartListener::operator==() 695 if ( pDoc != r.pDoc || nCount != r.nCount ) 696 return sal_False; 697 for ( sal_uInt16 nIndex = 0; nIndex < nCount; nIndex++ ) 698 { 699 if ( *((ScChartListener*) pItems[ nIndex ]) != 700 *((ScChartListener*) r.pItems[ nIndex ]) ) 701 return sal_False; 702 } 703 return sal_True; 704 } 705 706 void ScChartListenerCollection::StartListeningHiddenRange( const ScRange& rRange, ScChartHiddenRangeListener* pListener ) 707 { 708 RangeListenerItem aItem(rRange, pListener); 709 maHiddenListeners.push_back(aItem); 710 } 711 712 namespace { 713 714 struct MatchListener : public ::std::unary_function< 715 ScChartListenerCollection::RangeListenerItem, bool> 716 { 717 MatchListener(const ScChartHiddenRangeListener* pMatch) : 718 mpMatch(pMatch) 719 { 720 } 721 722 bool operator() (const ScChartListenerCollection::RangeListenerItem& rItem) const 723 { 724 return mpMatch == rItem.mpListener; 725 } 726 727 private: 728 const ScChartHiddenRangeListener* mpMatch; 729 }; 730 731 } 732 void ScChartListenerCollection::EndListeningHiddenRange( ScChartHiddenRangeListener* pListener ) 733 { 734 maHiddenListeners.remove_if(MatchListener(pListener)); 735 } 736 737