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