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_sd.hxx" 30 31 #include "ViewShellManager.hxx" 32 #include "ViewShell.hxx" 33 #include "ViewShellBase.hxx" 34 #include "Window.hxx" 35 #include "DrawDocShell.hxx" 36 #include "FormShellManager.hxx" 37 38 #include <sfx2/dispatch.hxx> 39 #include <svx/svxids.hrc> 40 #include <svx/fmshell.hxx> 41 42 #include <hash_map> 43 44 #undef VERBOSE 45 //#define VERBOSE 2 46 47 namespace sd { 48 49 namespace { 50 51 /** The ShellDescriptor class is used to shells together with their ids and 52 the factory that was used to create the shell. 53 54 The shell pointer may be NULL. In that case the shell is created on 55 demand by a factory. 56 57 The factory pointer may be NULL. In that case the shell pointer is 58 given to the ViewShellManager. 59 60 Shell pointer and factory pointer can but should not be NULL at the same 61 time. 62 */ 63 class ShellDescriptor { 64 public: 65 SfxShell* mpShell; 66 ShellId mnId; 67 ViewShellManager::SharedShellFactory mpFactory; 68 ShellDescriptor (); 69 ShellDescriptor (SfxShell* pShell, ShellId nId); 70 ShellDescriptor (const ShellDescriptor& rDescriptor); 71 ShellDescriptor& operator= (const ShellDescriptor& rDescriptor); 72 bool IsMainViewShell (void) const; 73 ::Window* GetWindow (void) const; 74 }; 75 76 77 78 79 /** This functor can be used to search for a shell in an STL container when the 80 shell pointer is given. 81 */ 82 class IsShell : public ::std::unary_function<ShellDescriptor,bool> 83 { 84 public: 85 IsShell (const SfxShell* pShell) : mpShell(pShell) {} 86 bool operator() (const ShellDescriptor& rDescriptor) 87 { return rDescriptor.mpShell == mpShell; } 88 private: 89 const SfxShell* mpShell; 90 }; 91 92 93 94 95 /** This functor can be used to search for a shell in an STL container when the 96 id of the shell is given. 97 */ 98 class IsId : public ::std::unary_function<ShellDescriptor,bool> 99 { 100 public: 101 IsId (ShellId nId) : mnId(nId) {} 102 bool operator() (const ShellDescriptor& rDescriptor) 103 { return rDescriptor.mnId == mnId; } 104 private: 105 ShellId mnId; 106 }; 107 108 } // end of anonymous namespace 109 110 111 112 113 class ViewShellManager::Implementation 114 { 115 public: 116 Implementation ( 117 ViewShellManager& rManager, 118 ViewShellBase& rBase); 119 ~Implementation (void); 120 121 void AddShellFactory ( 122 const SfxShell* pViewShell, 123 const SharedShellFactory& rpFactory); 124 void RemoveShellFactory ( 125 const SfxShell* pViewShell, 126 const SharedShellFactory& rpFactory); 127 void ActivateViewShell ( 128 ViewShell* pViewShell); 129 void DeactivateViewShell (const ViewShell& rShell); 130 void ActivateShell (SfxShell& rShell); 131 void DeactivateShell (const SfxShell& rShell); 132 void ActivateShell (const ShellDescriptor& rDescriptor); 133 void SetFormShell (const ViewShell* pViewShell, FmFormShell* pFormShell, bool bAbove); 134 void ActivateSubShell (const SfxShell& rParentShell, ShellId nId); 135 void DeactivateSubShell (const SfxShell& rParentShell, ShellId nId); 136 void MoveSubShellToTop (const SfxShell& rParentShell, ShellId nId); 137 void MoveToTop (const SfxShell& rParentShell); 138 SfxShell* GetShell (ShellId nId) const; 139 SfxShell* GetTopShell (void) const; 140 void Shutdown (void); 141 void InvalidateAllSubShells (const SfxShell* pParentShell); 142 143 /** Remove all shells from the SFX stack above and including the given 144 shell. 145 */ 146 void TakeShellsFromStack (const SfxShell* pShell); 147 148 class UpdateLock 149 { 150 public: 151 UpdateLock (Implementation& rImpl) : mrImpl(rImpl) {mrImpl.LockUpdate();} 152 ~UpdateLock (void) {mrImpl.UnlockUpdate();}; 153 private: 154 Implementation& mrImpl; 155 }; 156 157 158 159 /** Prevent updates of the shell stack. While the sub shell manager is 160 locked it will update its internal data structures but not alter the 161 shell stack. Use this method when there are several modifications 162 to the shell stack to prevent multiple rebuilds of the shell stack 163 and resulting broadcasts. 164 */ 165 void LockUpdate (void); 166 167 /** Allow updates of the shell stack. This method has to be called the 168 same number of times as LockUpdate() to really allow a rebuild of 169 the shell stack. 170 */ 171 void UnlockUpdate (void); 172 173 private: 174 ViewShellBase& mrBase; 175 mutable ::osl::Mutex maMutex; 176 177 class ShellHash{public: size_t operator()(const SfxShell* p) const { return (size_t)p;} }; 178 typedef ::std::hash_multimap<const SfxShell*,SharedShellFactory,ShellHash> 179 FactoryList; 180 FactoryList maShellFactories; 181 182 /** List of the active view shells. In order to create gather all shells 183 to put on the shell stack each view shell in this list is asked for 184 its sub-shells (typically toolbars). 185 */ 186 typedef ::std::list<ShellDescriptor> ActiveShellList; 187 ActiveShellList maActiveViewShells; 188 189 typedef ::std::list<ShellDescriptor> SubShellSubList; 190 typedef ::std::hash_map<const SfxShell*,SubShellSubList,ShellHash> SubShellList; 191 SubShellList maActiveSubShells; 192 193 /** In this member we remember what shells we have pushed on the shell 194 stack. 195 */ 196 typedef ::std::vector<SfxShell*> ShellStack; 197 198 int mnUpdateLockCount; 199 200 /** When this flag is set then the main view shell is always kept at the 201 top of the shell stack. 202 */ 203 bool mbKeepMainViewShellOnTop; 204 205 /** The UpdateShellStack() method can be called recursively. This flag 206 is used to communicate between different levels of invocation: if 207 the stack has been updated in an inner call the outer call can (has 208 to) stop and return immediately. 209 */ 210 bool mbShellStackIsUpToDate; 211 212 SfxShell* mpFormShell; 213 const ViewShell* mpFormShellParent; 214 bool mbFormShellAboveParent; 215 216 SfxShell* mpTopShell; 217 218 void GatherActiveShells (ShellStack& rShellList); 219 220 void UpdateShellStack (void); 221 222 void CreateShells (void); 223 224 /** This method rebuilds the stack of shells that are stacked upon the 225 view shell base. 226 */ 227 void CreateTargetStack (ShellStack& rStack) const; 228 229 DECL_LINK(WindowEventHandler, VclWindowEvent*); 230 231 #ifdef VERBOSE 232 void DumpShellStack (const ShellStack& rStack); 233 void DumpSfxShellStack (void); 234 #endif 235 236 /** To be called before a shell is taken fom the SFX shell stack. This 237 method deactivates an active text editing to avoid problems with 238 undo managers. 239 Afterwards the Deactivate() of the shell is called. 240 */ 241 void Deactivate (SfxShell* pShell); 242 243 ShellDescriptor CreateSubShell ( 244 SfxShell* pShell, 245 ShellId nShellId, 246 ::Window* pParentWindow, 247 FrameView* pFrameView); 248 void DestroyViewShell (const ShellDescriptor& rDescriptor); 249 void DestroySubShell ( 250 const SfxShell& rViewShell, 251 const ShellDescriptor& rDescriptor); 252 }; 253 254 255 256 257 //===== ViewShellManager ====================================================== 258 259 ViewShellManager::ViewShellManager (ViewShellBase& rBase) 260 : mpImpl(new Implementation(*this,rBase)), 261 mbValid(true) 262 { 263 } 264 265 266 267 268 ViewShellManager::~ViewShellManager (void) 269 { 270 } 271 272 273 274 275 void ViewShellManager::AddSubShellFactory ( 276 ViewShell* pViewShell, 277 const SharedShellFactory& rpFactory) 278 { 279 if (mbValid) 280 mpImpl->AddShellFactory(pViewShell, rpFactory); 281 } 282 283 284 285 286 void ViewShellManager::RemoveSubShellFactory ( 287 ViewShell* pViewShell, 288 const SharedShellFactory& rpFactory) 289 { 290 if (mbValid) 291 mpImpl->RemoveShellFactory(pViewShell, rpFactory); 292 } 293 294 295 296 297 void ViewShellManager::ActivateViewShell (ViewShell* pViewShell) 298 { 299 if (mbValid) 300 return mpImpl->ActivateViewShell(pViewShell); 301 } 302 303 304 305 306 void ViewShellManager::DeactivateViewShell (const ViewShell* pShell) 307 { 308 if (mbValid && pShell!=NULL) 309 mpImpl->DeactivateViewShell(*pShell); 310 } 311 312 313 314 315 void ViewShellManager::MoveSubShellToTop ( 316 const ViewShell& rParentShell, 317 ShellId nId) 318 { 319 if (mbValid) 320 mpImpl->MoveSubShellToTop(rParentShell, nId); 321 } 322 323 324 325 326 void ViewShellManager::SetFormShell ( 327 const ViewShell* pParentShell, 328 FmFormShell* pFormShell, 329 bool bAbove) 330 { 331 if (mbValid) 332 mpImpl->SetFormShell(pParentShell,pFormShell,bAbove); 333 } 334 335 336 337 338 void ViewShellManager::ActivateSubShell (const ViewShell& rViewShell, ShellId nId) 339 { 340 if (mbValid) 341 mpImpl->ActivateSubShell(rViewShell,nId); 342 } 343 344 345 346 347 void ViewShellManager::DeactivateSubShell (const ViewShell& rViewShell, ShellId nId) 348 { 349 if (mbValid) 350 mpImpl->DeactivateSubShell(rViewShell,nId); 351 } 352 353 354 355 356 void ViewShellManager::InvalidateAllSubShells (ViewShell* pViewShell) 357 { 358 if (mbValid) 359 mpImpl->InvalidateAllSubShells(pViewShell); 360 } 361 362 363 364 365 void ViewShellManager::ActivateShell (SfxShell* pShell) 366 { 367 if (mbValid && pShell!=NULL) 368 mpImpl->ActivateShell(*pShell); 369 } 370 371 372 373 374 void ViewShellManager::DeactivateShell (const SfxShell* pShell) 375 { 376 if (mbValid && pShell!=NULL) 377 mpImpl->DeactivateShell(*pShell); 378 } 379 380 381 382 383 void ViewShellManager::MoveToTop (const ViewShell& rParentShell) 384 { 385 if (mbValid) 386 mpImpl->MoveToTop(rParentShell); 387 } 388 389 390 391 392 SfxShell* ViewShellManager::GetShell (ShellId nId) const 393 { 394 if (mbValid) 395 return mpImpl->GetShell(nId); 396 else 397 return NULL; 398 } 399 400 401 402 403 SfxShell* ViewShellManager::GetTopShell (void) const 404 { 405 if (mbValid) 406 return mpImpl->GetTopShell(); 407 else 408 return NULL; 409 } 410 411 412 413 414 void ViewShellManager::Shutdown (void) 415 { 416 if (mbValid) 417 { 418 mpImpl->Shutdown(); 419 mbValid = false; 420 } 421 } 422 423 424 425 void ViewShellManager::LockUpdate (void) 426 { 427 mpImpl->LockUpdate(); 428 } 429 430 431 432 433 void ViewShellManager::UnlockUpdate (void) 434 { 435 mpImpl->UnlockUpdate(); 436 } 437 438 439 440 441 //===== ViewShellManager::Implementation ====================================== 442 443 ViewShellManager::Implementation::Implementation ( 444 ViewShellManager& rManager, 445 ViewShellBase& rBase) 446 : mrBase(rBase), 447 maMutex(), 448 maShellFactories(), 449 maActiveViewShells(), 450 mnUpdateLockCount(0), 451 mbKeepMainViewShellOnTop(false), 452 mbShellStackIsUpToDate(true), 453 mpFormShell(NULL), 454 mpFormShellParent(NULL), 455 mbFormShellAboveParent(true), 456 mpTopShell(NULL) 457 { 458 (void)rManager; 459 } 460 461 462 463 464 ViewShellManager::Implementation::~Implementation (void) 465 { 466 Shutdown(); 467 } 468 469 470 471 472 void ViewShellManager::Implementation::AddShellFactory ( 473 const SfxShell* pViewShell, 474 const SharedShellFactory& rpFactory) 475 { 476 bool bAlreadyAdded (false); 477 478 // Check that the given factory has not already been added. 479 ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange( 480 maShellFactories.equal_range(pViewShell)); 481 for (FactoryList::const_iterator iFactory=aRange.first; iFactory!=aRange.second; ++iFactory) 482 if (iFactory->second == rpFactory) 483 { 484 bAlreadyAdded = true; 485 break; 486 } 487 488 // Add the factory if it is not already present. 489 if ( ! bAlreadyAdded) 490 maShellFactories.insert(FactoryList::value_type(pViewShell, rpFactory)); 491 } 492 493 494 495 496 void ViewShellManager::Implementation::RemoveShellFactory ( 497 const SfxShell* pViewShell, 498 const SharedShellFactory& rpFactory) 499 { 500 ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange( 501 maShellFactories.equal_range(pViewShell)); 502 for (FactoryList::iterator iFactory=aRange.first; iFactory!=aRange.second; ++iFactory) 503 if (iFactory->second == rpFactory) 504 { 505 maShellFactories.erase(iFactory); 506 break; 507 } 508 } 509 510 511 512 513 void ViewShellManager::Implementation::ActivateViewShell (ViewShell* pViewShell) 514 { 515 ::osl::MutexGuard aGuard (maMutex); 516 517 ShellDescriptor aResult; 518 aResult.mpShell = pViewShell; 519 520 // Register as window listener so that the shells of the current 521 // window can be moved to the top of the shell stack. 522 if (aResult.mpShell != NULL) 523 { 524 ::Window* pWindow = aResult.GetWindow(); 525 if (pWindow != NULL) 526 pWindow->AddEventListener( 527 LINK(this, ViewShellManager::Implementation, WindowEventHandler)); 528 else 529 { 530 DBG_ASSERT(false, 531 "ViewShellManager::ActivateViewShell: " 532 "new view shell has no active window"); 533 } 534 } 535 536 ActivateShell(aResult); 537 } 538 539 540 541 542 void ViewShellManager::Implementation::DeactivateViewShell (const ViewShell& rShell) 543 { 544 ::osl::MutexGuard aGuard (maMutex); 545 546 ActiveShellList::iterator iShell (::std::find_if ( 547 maActiveViewShells.begin(), 548 maActiveViewShells.end(), 549 IsShell(&rShell))); 550 if (iShell != maActiveViewShells.end()) 551 { 552 UpdateLock aLocker (*this); 553 554 ShellDescriptor aDescriptor(*iShell); 555 mrBase.GetDocShell()->Disconnect(dynamic_cast<ViewShell*>(aDescriptor.mpShell)); 556 maActiveViewShells.erase(iShell); 557 TakeShellsFromStack(aDescriptor.mpShell); 558 559 // Deactivate sub shells. 560 SubShellList::iterator iList (maActiveSubShells.find(&rShell)); 561 if (iList != maActiveSubShells.end()) 562 { 563 SubShellSubList& rList (iList->second); 564 while ( ! rList.empty()) 565 DeactivateSubShell(rShell, rList.front().mnId); 566 } 567 568 DestroyViewShell(aDescriptor); 569 } 570 } 571 572 573 574 575 void ViewShellManager::Implementation::ActivateShell (SfxShell& rShell) 576 { 577 ::osl::MutexGuard aGuard (maMutex); 578 579 // Create a new shell or recycle on in the cache. 580 ShellDescriptor aDescriptor; 581 aDescriptor.mpShell = &rShell; 582 583 ActivateShell(aDescriptor); 584 } 585 586 587 588 589 void ViewShellManager::Implementation::ActivateShell (const ShellDescriptor& rDescriptor) 590 { 591 // Put shell on top of the active view shells. 592 if (rDescriptor.mpShell != NULL) 593 { 594 // Determine where to put the view shell on the stack. By default 595 // it is put on top of the stack. When the view shell of the center 596 // pane is to be kept top most and the new view shell is not 597 // displayed in the center pane then it is inserted at the position 598 // one below the top. 599 ActiveShellList::iterator iInsertPosition (maActiveViewShells.begin()); 600 if (iInsertPosition != maActiveViewShells.end() 601 && mbKeepMainViewShellOnTop 602 && ! rDescriptor.IsMainViewShell() 603 && iInsertPosition->IsMainViewShell()) 604 { 605 ++iInsertPosition; 606 } 607 maActiveViewShells.insert( 608 iInsertPosition, 609 rDescriptor); 610 } 611 } 612 613 614 615 616 void ViewShellManager::Implementation::DeactivateShell (const SfxShell& rShell) 617 { 618 ::osl::MutexGuard aGuard (maMutex); 619 620 ActiveShellList::iterator iShell (::std::find_if ( 621 maActiveViewShells.begin(), 622 maActiveViewShells.end(), 623 IsShell(&rShell))); 624 if (iShell != maActiveViewShells.end()) 625 { 626 UpdateLock aLocker (*this); 627 628 ShellDescriptor aDescriptor(*iShell); 629 mrBase.GetDocShell()->Disconnect(dynamic_cast<ViewShell*>(aDescriptor.mpShell)); 630 maActiveViewShells.erase(iShell); 631 TakeShellsFromStack(aDescriptor.mpShell); 632 633 // Deactivate sub shells. 634 SubShellList::iterator iList (maActiveSubShells.find(&rShell)); 635 if (iList != maActiveSubShells.end()) 636 { 637 SubShellSubList& rList (iList->second); 638 while ( ! rList.empty()) 639 DeactivateSubShell(rShell, rList.front().mnId); 640 } 641 642 DestroyViewShell(aDescriptor); 643 } 644 } 645 646 647 648 649 void ViewShellManager::Implementation::ActivateSubShell ( 650 const SfxShell& rParentShell, 651 ShellId nId) 652 { 653 ::osl::MutexGuard aGuard (maMutex); 654 655 do 656 { 657 // Check that the given view shell is active. 658 ActiveShellList::iterator iShell (::std::find_if ( 659 maActiveViewShells.begin(), 660 maActiveViewShells.end(), 661 IsShell(&rParentShell))); 662 if (iShell == maActiveViewShells.end()) 663 break; 664 665 // Create the sub shell list if it does not yet exist. 666 SubShellList::iterator iList (maActiveSubShells.find(&rParentShell)); 667 if (iList == maActiveSubShells.end()) 668 iList = maActiveSubShells.insert( 669 SubShellList::value_type(&rParentShell,SubShellSubList())).first; 670 671 // Do not activate an object bar that is already active. Requesting 672 // this is not exactly an error but may be an indication of one. 673 SubShellSubList& rList (iList->second); 674 if (::std::find_if(rList.begin(),rList.end(), IsId(nId)) != rList.end()) 675 break; 676 677 // Add just the id of the sub shell. The actual shell is created 678 // later in CreateShells(). 679 UpdateLock aLock (*this); 680 rList.push_back(ShellDescriptor(NULL, nId)); 681 } 682 while (false); 683 } 684 685 686 687 688 void ViewShellManager::Implementation::DeactivateSubShell ( 689 const SfxShell& rParentShell, 690 ShellId nId) 691 { 692 ::osl::MutexGuard aGuard (maMutex); 693 694 do 695 { 696 // Check that the given view shell is active. 697 SubShellList::iterator iList (maActiveSubShells.find(&rParentShell)); 698 if (iList == maActiveSubShells.end()) 699 break; 700 701 // Look up the sub shell. 702 SubShellSubList& rList (iList->second); 703 SubShellSubList::iterator iShell ( 704 ::std::find_if(rList.begin(),rList.end(), IsId(nId))); 705 if (iShell == rList.end()) 706 break; 707 SfxShell* pShell = iShell->mpShell; 708 if (pShell == NULL) 709 break; 710 711 UpdateLock aLock (*this); 712 713 ShellDescriptor aDescriptor(*iShell); 714 // Remove the sub shell from both the internal structure as well as the 715 // SFX shell stack above and including the sub shell. 716 rList.erase(iShell); 717 TakeShellsFromStack(pShell); 718 719 DestroySubShell(rParentShell, aDescriptor); 720 } 721 while (false); 722 } 723 724 725 726 727 void ViewShellManager::Implementation::MoveSubShellToTop ( 728 const SfxShell& rParentShell, 729 ShellId nId) 730 { 731 SubShellList::iterator iList (maActiveSubShells.find(&rParentShell)); 732 if (iList != maActiveSubShells.end()) 733 { 734 // Look up the sub shell. 735 SubShellSubList& rList (iList->second); 736 SubShellSubList::iterator iShell ( 737 ::std::find_if(rList.begin(),rList.end(), IsId(nId))); 738 if (iShell!=rList.end() && iShell!=rList.begin()) 739 { 740 SubShellSubList::value_type aEntry (*iShell); 741 rList.erase(iShell); 742 rList.push_front(aEntry); 743 } 744 } 745 else 746 { 747 // Ignore this call when there are no sub shells for the given 748 // parent shell. We could remember the sub shell to move to the top 749 // but we do not. Do call this method at a later time instead. 750 } 751 } 752 753 754 755 void ViewShellManager::Implementation::MoveToTop (const SfxShell& rShell) 756 { 757 ::osl::MutexGuard aGuard (maMutex); 758 759 // Check that we have access to a dispatcher. If not, then we are 760 // (probably) called while the view shell is still being created or 761 // initialized. Without dispatcher we can not rebuild the shell stack 762 // to move the requested shell to the top. So return right away instead 763 // of making a mess without being able to clean up afterwards. 764 if (mrBase.GetDispatcher() == NULL) 765 return; 766 767 ActiveShellList::iterator iShell (::std::find_if ( 768 maActiveViewShells.begin(), 769 maActiveViewShells.end(), 770 IsShell(&rShell))); 771 bool bMove = true; 772 if (iShell != maActiveViewShells.end()) 773 { 774 // Is the shell already at the top of the stack? We have to keep 775 // the case in mind that mbKeepMainViewShellOnTop is true. Shells 776 // that are not the main view shell are placed on the second-to-top 777 // position in this case. 778 if (iShell == maActiveViewShells.begin() 779 && (iShell->IsMainViewShell() || ! mbKeepMainViewShellOnTop)) 780 { 781 // The shell is at the top position and is either a) the main 782 // view shell or b) another shell but the main view shell is not 783 // kept at the top position. We do not have to move the shell. 784 bMove = false; 785 } 786 else if (iShell == ++maActiveViewShells.begin() 787 && ! iShell->IsMainViewShell() 788 && mbKeepMainViewShellOnTop) 789 { 790 // The shell is a the second-to-top position, not the main view 791 // shell and the main view shell is kept at the top position. 792 // Therefore we do not have to move the shell. 793 bMove = false; 794 } 795 } 796 else 797 { 798 // The shell is not on the stack. Therefore it can not be moved. 799 // We could insert it but we don't. Use ActivateViewShell() for 800 // that. 801 bMove = false; 802 } 803 804 // When the shell is not at the right position it is removed from the 805 // internal list of shells and inserted at the correct position. 806 if (bMove) 807 { 808 UpdateLock aLock (*this); 809 810 ShellDescriptor aDescriptor(*iShell); 811 812 TakeShellsFromStack(&rShell); 813 maActiveViewShells.erase(iShell); 814 815 // Find out whether to insert at the top or one below. 816 ActiveShellList::iterator aInsertPosition (maActiveViewShells.begin()); 817 if (mbKeepMainViewShellOnTop && ! aDescriptor.IsMainViewShell()) 818 { 819 if (maActiveViewShells.back().IsMainViewShell()) 820 aInsertPosition++; 821 } 822 823 maActiveViewShells.insert(aInsertPosition, aDescriptor); 824 } 825 } 826 827 828 829 830 SfxShell* ViewShellManager::Implementation::GetShell (ShellId nId) const 831 { 832 ::osl::MutexGuard aGuard (maMutex); 833 834 SfxShell* pShell = NULL; 835 836 // First search the active view shells. 837 ActiveShellList::const_iterator iShell ( 838 ::std::find_if ( 839 maActiveViewShells.begin(), 840 maActiveViewShells.end(), 841 IsId(nId))); 842 if (iShell != maActiveViewShells.end()) 843 pShell = iShell->mpShell; 844 else 845 { 846 // Now search the active sub shells of every active view shell. 847 SubShellList::const_iterator iList; 848 for (iList=maActiveSubShells.begin(); iList!=maActiveSubShells.end(); ++iList) 849 { 850 const SubShellSubList& rList (iList->second); 851 SubShellSubList::const_iterator iSubShell( 852 ::std::find_if(rList.begin(),rList.end(), IsId(nId))); 853 if (iSubShell != rList.end()) 854 { 855 pShell = iSubShell->mpShell; 856 break; 857 } 858 } 859 } 860 861 return pShell; 862 } 863 864 865 866 867 SfxShell* ViewShellManager::Implementation::GetTopShell (void) const 868 { 869 OSL_ASSERT(mpTopShell == mrBase.GetSubShell(0)); 870 return mpTopShell; 871 } 872 873 874 875 876 void ViewShellManager::Implementation::LockUpdate (void) 877 { 878 mnUpdateLockCount++; 879 } 880 881 882 883 884 void ViewShellManager::Implementation::UnlockUpdate (void) 885 { 886 ::osl::MutexGuard aGuard (maMutex); 887 888 mnUpdateLockCount--; 889 if (mnUpdateLockCount < 0) 890 { 891 // This should not happen. 892 OSL_ASSERT (mnUpdateLockCount>=0); 893 mnUpdateLockCount = 0; 894 } 895 if (mnUpdateLockCount == 0) 896 UpdateShellStack(); 897 } 898 899 900 901 902 /** Update the SFX shell stack (the portion that is visible to us) so that 903 it matches the internal shell stack. This is done in six steps: 904 1. Create the missing view shells and sub shells. 905 2. Set up the internal shell stack. 906 3. Get the SFX shell stack. 907 4. Find the lowest shell in which the two stacks differ. 908 5. Remove all shells above and including that shell from the SFX stack. 909 6. Push all shells of the internal stack on the SFX shell stack that are 910 not already present on the later. 911 */ 912 void ViewShellManager::Implementation::UpdateShellStack (void) 913 { 914 ::osl::MutexGuard aGuard (maMutex); 915 916 // Remember the undo manager from the top-most shell on the stack. 917 SfxShell* pTopMostShell = mrBase.GetSubShell(0); 918 ::svl::IUndoManager* pUndoManager = (pTopMostShell!=NULL) 919 ? pTopMostShell->GetUndoManager() 920 : NULL; 921 922 // 1. Create the missing shells. 923 CreateShells(); 924 925 926 // 2. Create the internal target stack. 927 ShellStack aTargetStack; 928 CreateTargetStack(aTargetStack); 929 930 931 // 3. Get SFX shell stack. 932 ShellStack aSfxShellStack; 933 sal_uInt16 nIndex (0); 934 while (mrBase.GetSubShell(nIndex)!=NULL) 935 ++nIndex; 936 aSfxShellStack.reserve(nIndex); 937 while (nIndex-- > 0) 938 aSfxShellStack.push_back(mrBase.GetSubShell(nIndex)); 939 940 941 #ifdef VERBOSE 942 OSL_TRACE("Current SFX Stack\r"); 943 DumpShellStack(aSfxShellStack); 944 OSL_TRACE("Target Stack\r"); 945 DumpShellStack(aTargetStack); 946 #endif 947 948 949 // 4. Find the lowest shell in which the two stacks differ. 950 ShellStack::const_iterator iSfxShell (aSfxShellStack.begin()); 951 ShellStack::iterator iTargetShell (aTargetStack.begin()); 952 while (iSfxShell != aSfxShellStack.end() 953 && iTargetShell!=aTargetStack.end() 954 && (*iSfxShell)==(*iTargetShell)) 955 { 956 ++iSfxShell; 957 ++iTargetShell; 958 } 959 960 961 // 5. Remove all shells above and including the differing shell from the 962 // SFX stack starting with the shell on top of the stack. 963 while (iSfxShell != aSfxShellStack.end()) 964 { 965 SfxShell* pShell = aSfxShellStack.back(); 966 aSfxShellStack.pop_back(); 967 #ifdef VERBOSE 968 OSL_TRACE("removing shell %p from stack\r", pShell); 969 #endif 970 mrBase.RemoveSubShell(pShell); 971 } 972 973 974 // 6. Push shells from the given stack onto the SFX stack. 975 mbShellStackIsUpToDate = false; 976 while (iTargetShell != aTargetStack.end()) 977 { 978 #ifdef VERBOSE 979 OSL_TRACE("pushing shell %p on stack\r", *iTargetShell); 980 #endif 981 mrBase.AddSubShell(**iTargetShell); 982 ++iTargetShell; 983 984 // The pushing of the shell on to the shell stack may have lead to 985 // another invocation of this method. In this case we have to abort 986 // pushing shells on the stack and return immediately. 987 if (mbShellStackIsUpToDate) 988 break; 989 } 990 if (mrBase.GetDispatcher() != NULL) 991 mrBase.GetDispatcher()->Flush(); 992 993 // Update the pointer to the top-most shell and set its undo manager 994 // to the one of the previous top-most shell. 995 mpTopShell = mrBase.GetSubShell(0); 996 if (mpTopShell!=NULL && pUndoManager!=NULL && mpTopShell->GetUndoManager()==NULL) 997 mpTopShell->SetUndoManager(pUndoManager); 998 999 // Finally tell an invocation of this method on a higher level that it can (has 1000 // to) abort and return immediately. 1001 mbShellStackIsUpToDate = true; 1002 1003 #ifdef VERBOSE 1004 OSL_TRACE("New current stack\r"); 1005 DumpSfxShellStack(); 1006 #endif 1007 } 1008 1009 1010 1011 1012 void ViewShellManager::Implementation::TakeShellsFromStack (const SfxShell* pShell) 1013 { 1014 ::osl::MutexGuard aGuard (maMutex); 1015 1016 // Remember the undo manager from the top-most shell on the stack. 1017 SfxShell* pTopMostShell = mrBase.GetSubShell(0); 1018 ::svl::IUndoManager* pUndoManager = (pTopMostShell!=NULL) 1019 ? pTopMostShell->GetUndoManager() 1020 : NULL; 1021 1022 #ifdef VERBOSE 1023 OSL_TRACE("TakeShellsFromStack(%p)\r", pShell); 1024 DumpSfxShellStack(); 1025 #endif 1026 1027 // 0.Make sure that the given shell is on the stack. This is a 1028 // preparation for the following assertion. 1029 for (sal_uInt16 nIndex=0; true; nIndex++) 1030 { 1031 SfxShell* pShellOnStack = mrBase.GetSubShell(nIndex); 1032 if (pShellOnStack == NULL) 1033 { 1034 // Set pShell to NULL to indicate the following code that the 1035 // shell is not on the stack. 1036 pShell = NULL; 1037 break; 1038 } 1039 else if (pShellOnStack == pShell) 1040 break; 1041 } 1042 1043 if (pShell != NULL) 1044 { 1045 // 1. Deactivate our shells on the stack before they are removed so 1046 // that during the Deactivation() calls the stack is still intact. 1047 for (sal_uInt16 nIndex=0; true; nIndex++) 1048 { 1049 SfxShell* pShellOnStack = mrBase.GetSubShell(nIndex); 1050 Deactivate(pShellOnStack); 1051 if (pShellOnStack == pShell) 1052 break; 1053 } 1054 1055 // 2. Remove the shells from the stack. 1056 while (true) 1057 { 1058 SfxShell* pShellOnStack = mrBase.GetSubShell(0); 1059 #ifdef VERBOSE 1060 OSL_TRACE("removing shell %p from stack\r", pShellOnStack); 1061 #endif 1062 mrBase.RemoveSubShell(pShellOnStack); 1063 if (pShellOnStack == pShell) 1064 break; 1065 } 1066 1067 // 3. Update the stack. 1068 if (mrBase.GetDispatcher() != NULL) 1069 mrBase.GetDispatcher()->Flush(); 1070 1071 // Update the pointer to the top-most shell and set its undo manager 1072 // to the one of the previous top-most shell. 1073 mpTopShell = mrBase.GetSubShell(0); 1074 if (mpTopShell!=NULL && pUndoManager!=NULL && mpTopShell->GetUndoManager()==NULL) 1075 mpTopShell->SetUndoManager(pUndoManager); 1076 } 1077 1078 #ifdef VERBOSE 1079 OSL_TRACE("Sfx shell stack is:\r"); 1080 DumpSfxShellStack(); 1081 #endif 1082 } 1083 1084 1085 1086 1087 void ViewShellManager::Implementation::CreateShells (void) 1088 { 1089 ::osl::MutexGuard aGuard (maMutex); 1090 1091 // Iterate over all view shells. 1092 ShellStack aShellStack; 1093 ActiveShellList::reverse_iterator iShell; 1094 for (iShell=maActiveViewShells.rbegin(); iShell!=maActiveViewShells.rend(); ++iShell) 1095 { 1096 // Get the list of associated sub shells. 1097 SubShellList::iterator iList (maActiveSubShells.find(iShell->mpShell)); 1098 if (iList != maActiveSubShells.end()) 1099 { 1100 SubShellSubList& rList (iList->second); 1101 1102 // Iterate over all sub shells of the current view shell. 1103 SubShellSubList::iterator iSubShell; 1104 for (iSubShell=rList.begin(); iSubShell!=rList.end(); ++iSubShell) 1105 { 1106 if (iSubShell->mpShell == NULL) 1107 { 1108 *iSubShell = CreateSubShell(iShell->mpShell,iSubShell->mnId,NULL,NULL); 1109 } 1110 } 1111 } 1112 } 1113 } 1114 1115 1116 1117 1118 void ViewShellManager::Implementation::CreateTargetStack (ShellStack& rStack) const 1119 { 1120 // Create a local stack of the shells that are to push on the shell 1121 // stack. We can thus safly create the required shells wile still 1122 // having a valid shell stack. 1123 for (ActiveShellList::const_reverse_iterator iViewShell (maActiveViewShells.rbegin()); 1124 iViewShell != maActiveViewShells.rend(); 1125 ++iViewShell) 1126 { 1127 // Possibly place the form shell below the current view shell. 1128 if ( ! mbFormShellAboveParent 1129 && mpFormShell!=NULL 1130 && iViewShell->mpShell==mpFormShellParent) 1131 { 1132 rStack.push_back(mpFormShell); 1133 } 1134 1135 // Put the view shell itself on the local stack. 1136 rStack.push_back (iViewShell->mpShell); 1137 1138 // Possibly place the form shell above the current view shell. 1139 if (mbFormShellAboveParent 1140 && mpFormShell!=NULL 1141 && iViewShell->mpShell==mpFormShellParent) 1142 { 1143 rStack.push_back(mpFormShell); 1144 } 1145 1146 // Add all other sub shells. 1147 SubShellList::const_iterator iList (maActiveSubShells.find(iViewShell->mpShell)); 1148 if (iList != maActiveSubShells.end()) 1149 { 1150 const SubShellSubList& rList (iList->second); 1151 SubShellSubList::const_reverse_iterator iSubShell; 1152 for (iSubShell=rList.rbegin(); iSubShell!=rList.rend(); ++iSubShell) 1153 if (iSubShell->mpShell != mpFormShell) 1154 rStack.push_back(iSubShell->mpShell); 1155 } 1156 } 1157 } 1158 1159 1160 1161 1162 IMPL_LINK(ViewShellManager::Implementation, WindowEventHandler, VclWindowEvent*, pEvent) 1163 { 1164 if (pEvent != NULL) 1165 { 1166 ::Window* pEventWindow 1167 = static_cast<VclWindowEvent*>(pEvent)->GetWindow(); 1168 1169 switch (pEvent->GetId()) 1170 { 1171 case VCLEVENT_WINDOW_GETFOCUS: 1172 { 1173 for (ActiveShellList::iterator aI(maActiveViewShells.begin()); 1174 aI!=maActiveViewShells.end(); 1175 aI++) 1176 { 1177 if (pEventWindow == static_cast< ::Window*>(aI->GetWindow())) 1178 { 1179 MoveToTop(*aI->mpShell); 1180 break; 1181 } 1182 } 1183 } 1184 break; 1185 1186 case VCLEVENT_WINDOW_LOSEFOCUS: 1187 break; 1188 } 1189 } 1190 return sal_True; 1191 } 1192 1193 1194 1195 1196 ShellDescriptor ViewShellManager::Implementation::CreateSubShell ( 1197 SfxShell* pParentShell, 1198 ShellId nShellId, 1199 ::Window* pParentWindow, 1200 FrameView* pFrameView) 1201 { 1202 ::osl::MutexGuard aGuard (maMutex); 1203 ShellDescriptor aResult; 1204 1205 // Look up the factories for the parent shell. 1206 ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange( 1207 maShellFactories.equal_range(pParentShell)); 1208 1209 // Try all factories to create the shell. 1210 for (FactoryList::const_iterator iFactory=aRange.first; iFactory!=aRange.second; ++iFactory) 1211 { 1212 SharedShellFactory pFactory = iFactory->second; 1213 if (pFactory != NULL) 1214 aResult.mpShell = pFactory->CreateShell(nShellId, pParentWindow, pFrameView); 1215 1216 // Exit the loop when the shell has been successfully created. 1217 if (aResult.mpShell != NULL) 1218 { 1219 aResult.mpFactory = pFactory; 1220 aResult.mnId = nShellId; 1221 break; 1222 } 1223 } 1224 1225 return aResult; 1226 } 1227 1228 1229 1230 1231 void ViewShellManager::Implementation::DestroyViewShell ( 1232 const ShellDescriptor& rDescriptor) 1233 { 1234 OSL_ASSERT(rDescriptor.mpShell != NULL); 1235 1236 ::Window* pWindow = rDescriptor.GetWindow(); 1237 if (pWindow != NULL) 1238 { 1239 pWindow->RemoveEventListener( 1240 LINK(this, ViewShellManager::Implementation, WindowEventHandler)); 1241 } 1242 1243 // Destroy the sub shell factories. 1244 ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange( 1245 maShellFactories.equal_range(rDescriptor.mpShell)); 1246 if (aRange.first != maShellFactories.end()) 1247 maShellFactories.erase(aRange.first, aRange.second); 1248 1249 // Release the shell. 1250 if (rDescriptor.mpFactory.get() != NULL) 1251 rDescriptor.mpFactory->ReleaseShell(rDescriptor.mpShell); 1252 } 1253 1254 1255 1256 1257 void ViewShellManager::Implementation::DestroySubShell ( 1258 const SfxShell& rParentShell, 1259 const ShellDescriptor& rDescriptor) 1260 { 1261 (void)rParentShell; 1262 OSL_ASSERT(rDescriptor.mpFactory.get() != NULL); 1263 rDescriptor.mpFactory->ReleaseShell(rDescriptor.mpShell); 1264 } 1265 1266 1267 1268 1269 void ViewShellManager::Implementation::InvalidateAllSubShells (const SfxShell* pParentShell) 1270 { 1271 ::osl::MutexGuard aGuard (maMutex); 1272 1273 SubShellList::iterator iList (maActiveSubShells.find(pParentShell)); 1274 if (iList != maActiveSubShells.end()) 1275 { 1276 SubShellSubList& rList (iList->second); 1277 SubShellSubList::iterator iShell; 1278 for (iShell=rList.begin(); iShell!=rList.end(); ++iShell) 1279 if (iShell->mpShell != NULL) 1280 iShell->mpShell->Invalidate(); 1281 } 1282 } 1283 1284 1285 1286 1287 void ViewShellManager::Implementation::Shutdown (void) 1288 { 1289 ::osl::MutexGuard aGuard (maMutex); 1290 1291 // Take stacked shells from stack. 1292 if ( ! maActiveViewShells.empty()) 1293 { 1294 UpdateLock aLock (*this); 1295 1296 1297 while ( ! maActiveViewShells.empty()) 1298 { 1299 SfxShell* pShell = maActiveViewShells.front().mpShell; 1300 if (pShell != NULL) 1301 { 1302 ViewShell* pViewShell = dynamic_cast<ViewShell*>(pShell); 1303 if (pViewShell != NULL) 1304 DeactivateViewShell(*pViewShell); 1305 else 1306 DeactivateShell(*pShell); 1307 } 1308 else 1309 { 1310 DBG_ASSERT(false, 1311 "ViewShellManager::Implementation::Shutdown(): empty active shell descriptor"); 1312 maActiveViewShells.pop_front(); 1313 } 1314 } 1315 } 1316 mrBase.RemoveSubShell (NULL); 1317 1318 maShellFactories.clear(); 1319 } 1320 1321 1322 1323 1324 #ifdef VERBOSE 1325 void ViewShellManager::Implementation::DumpShellStack (const ShellStack& rStack) 1326 { 1327 ShellStack::const_reverse_iterator iEntry; 1328 for (iEntry=rStack.rbegin(); iEntry!=rStack.rend(); ++iEntry) 1329 if (*iEntry != NULL) 1330 OSL_TRACE (" %p : %s\r", 1331 *iEntry, 1332 ::rtl::OUStringToOString((*iEntry)->GetName(),RTL_TEXTENCODING_UTF8).getStr()); 1333 else 1334 OSL_TRACE(" null\r"); 1335 } 1336 1337 1338 1339 1340 void ViewShellManager::Implementation::DumpSfxShellStack (void) 1341 { 1342 ShellStack aSfxShellStack; 1343 sal_uInt16 nIndex (0); 1344 while (mrBase.GetSubShell(nIndex)!=NULL) 1345 ++nIndex; 1346 aSfxShellStack.reserve(nIndex); 1347 while (nIndex-- > 0) 1348 aSfxShellStack.push_back(mrBase.GetSubShell(nIndex)); 1349 DumpShellStack(aSfxShellStack); 1350 } 1351 #endif 1352 1353 1354 1355 void ViewShellManager::Implementation::Deactivate (SfxShell* pShell) 1356 { 1357 OSL_ASSERT(pShell!=NULL); 1358 1359 // We have to end a text edit for view shells that are to be taken from 1360 // the shell stack. 1361 ViewShell* pViewShell = dynamic_cast<ViewShell*>(pShell); 1362 if (pViewShell != NULL) 1363 { 1364 sd::View* pView = pViewShell->GetView(); 1365 if (pView!=NULL && pView->IsTextEdit()) 1366 { 1367 pView->SdrEndTextEdit(); 1368 pView->UnmarkAll(); 1369 pViewShell->GetViewFrame()->GetDispatcher()->Execute( 1370 SID_OBJECT_SELECT, 1371 SFX_CALLMODE_ASYNCHRON); 1372 } 1373 } 1374 1375 // Now we can deactivate the shell. 1376 pShell->Deactivate(sal_True); 1377 } 1378 1379 1380 1381 1382 void ViewShellManager::Implementation::SetFormShell ( 1383 const ViewShell* pFormShellParent, 1384 FmFormShell* pFormShell, 1385 bool bFormShellAboveParent) 1386 { 1387 ::osl::MutexGuard aGuard (maMutex); 1388 1389 mpFormShellParent = pFormShellParent; 1390 mpFormShell = pFormShell; 1391 mbFormShellAboveParent = bFormShellAboveParent; 1392 } 1393 1394 1395 1396 1397 namespace { 1398 1399 ShellDescriptor::ShellDescriptor (void) 1400 : mpShell(NULL), 1401 mnId(0), 1402 mpFactory() 1403 { 1404 } 1405 1406 1407 1408 1409 ShellDescriptor::ShellDescriptor ( 1410 SfxShell* pShell, 1411 ShellId nId) 1412 : mpShell(pShell), 1413 mnId(nId), 1414 mpFactory() 1415 { 1416 } 1417 1418 1419 1420 1421 ShellDescriptor::ShellDescriptor (const ShellDescriptor& rDescriptor) 1422 : mpShell(rDescriptor.mpShell), 1423 mnId(rDescriptor.mnId), 1424 mpFactory(rDescriptor.mpFactory) 1425 { 1426 } 1427 1428 1429 1430 1431 ShellDescriptor& ShellDescriptor::operator= (const ShellDescriptor& rDescriptor) 1432 { 1433 mpShell = rDescriptor.mpShell; 1434 mnId = rDescriptor.mnId; 1435 mpFactory = rDescriptor.mpFactory; 1436 return *this; 1437 } 1438 1439 1440 1441 1442 bool ShellDescriptor::IsMainViewShell (void) const 1443 { 1444 ViewShell* pViewShell = dynamic_cast<ViewShell*>(mpShell); 1445 if (pViewShell != NULL) 1446 return pViewShell->IsMainViewShell(); 1447 else 1448 return false; 1449 } 1450 1451 1452 1453 1454 ::Window* ShellDescriptor::GetWindow (void) const 1455 { 1456 ViewShell* pViewShell = dynamic_cast<ViewShell*>(mpShell); 1457 if (pViewShell != NULL) 1458 return pViewShell->GetActiveWindow(); 1459 else 1460 return NULL; 1461 } 1462 1463 1464 1465 } // end of anonymous namespace 1466 1467 } // end of namespace sd 1468