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