diff options
Diffstat (limited to 'sd/source/ui/view/ViewShellManager.cxx')
-rwxr-xr-x | sd/source/ui/view/ViewShellManager.cxx | 1464 |
1 files changed, 1464 insertions, 0 deletions
diff --git a/sd/source/ui/view/ViewShellManager.cxx b/sd/source/ui/view/ViewShellManager.cxx new file mode 100755 index 000000000000..c25c0f11270a --- /dev/null +++ b/sd/source/ui/view/ViewShellManager.cxx @@ -0,0 +1,1464 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sd.hxx" + +#include "ViewShellManager.hxx" +#include "ViewShell.hxx" +#include "ViewShellBase.hxx" +#include "Window.hxx" +#include "DrawDocShell.hxx" +#include "FormShellManager.hxx" + +#include <sfx2/dispatch.hxx> +#include <svx/svxids.hrc> +#include <svx/fmshell.hxx> + +#include <hash_map> + +#undef VERBOSE +//#define VERBOSE 2 + +namespace sd { + +namespace { + +/** The ShellDescriptor class is used to shells together with their ids and + the factory that was used to create the shell. + + The shell pointer may be NULL. In that case the shell is created on + demand by a factory. + + The factory pointer may be NULL. In that case the shell pointer is + given to the ViewShellManager. + + Shell pointer and factory pointer can but should not be NULL at the same + time. +*/ +class ShellDescriptor { +public: + SfxShell* mpShell; + ShellId mnId; + ViewShellManager::SharedShellFactory mpFactory; + ShellDescriptor (); + ShellDescriptor (SfxShell* pShell, ShellId nId); + ShellDescriptor (const ShellDescriptor& rDescriptor); + ShellDescriptor& operator= (const ShellDescriptor& rDescriptor); + bool IsMainViewShell (void) const; + ::Window* GetWindow (void) const; +}; + + + + +/** This functor can be used to search for a shell in an STL container when the + shell pointer is given. +*/ +class IsShell : public ::std::unary_function<ShellDescriptor,bool> +{ +public: + IsShell (const SfxShell* pShell) : mpShell(pShell) {} + bool operator() (const ShellDescriptor& rDescriptor) + { return rDescriptor.mpShell == mpShell; } +private: + const SfxShell* mpShell; +}; + + + + +/** This functor can be used to search for a shell in an STL container when the + id of the shell is given. +*/ +class IsId : public ::std::unary_function<ShellDescriptor,bool> +{ +public: + IsId (ShellId nId) : mnId(nId) {} + bool operator() (const ShellDescriptor& rDescriptor) + { return rDescriptor.mnId == mnId; } +private: + ShellId mnId; +}; + +} // end of anonymous namespace + + + + +class ViewShellManager::Implementation +{ +public: + Implementation ( + ViewShellManager& rManager, + ViewShellBase& rBase); + ~Implementation (void); + + void AddShellFactory ( + const SfxShell* pViewShell, + const SharedShellFactory& rpFactory); + void RemoveShellFactory ( + const SfxShell* pViewShell, + const SharedShellFactory& rpFactory); + void ActivateViewShell ( + ViewShell* pViewShell); + void DeactivateViewShell (const ViewShell& rShell); + void ActivateShell (SfxShell& rShell); + void DeactivateShell (const SfxShell& rShell); + void ActivateShell (const ShellDescriptor& rDescriptor); + void SetFormShell (const ViewShell* pViewShell, FmFormShell* pFormShell, bool bAbove); + void ActivateSubShell (const SfxShell& rParentShell, ShellId nId); + void DeactivateSubShell (const SfxShell& rParentShell, ShellId nId); + void MoveSubShellToTop (const SfxShell& rParentShell, ShellId nId); + void MoveToTop (const SfxShell& rParentShell); + SfxShell* GetShell (ShellId nId) const; + SfxShell* GetTopShell (void) const; + void Shutdown (void); + void InvalidateAllSubShells (const SfxShell* pParentShell); + + /** Remove all shells from the SFX stack above and including the given + shell. + */ + void TakeShellsFromStack (const SfxShell* pShell); + + class UpdateLock + { + public: + UpdateLock (Implementation& rImpl) : mrImpl(rImpl) {mrImpl.LockUpdate();} + ~UpdateLock (void) {mrImpl.UnlockUpdate();}; + private: + Implementation& mrImpl; + }; + + + + /** Prevent updates of the shell stack. While the sub shell manager is + locked it will update its internal data structures but not alter the + shell stack. Use this method when there are several modifications + to the shell stack to prevent multiple rebuilds of the shell stack + and resulting broadcasts. + */ + void LockUpdate (void); + + /** Allow updates of the shell stack. This method has to be called the + same number of times as LockUpdate() to really allow a rebuild of + the shell stack. + */ + void UnlockUpdate (void); + +private: + ViewShellBase& mrBase; + mutable ::osl::Mutex maMutex; + + class ShellHash{public: size_t operator()(const SfxShell* p) const { return (size_t)p;} }; + typedef ::std::hash_multimap<const SfxShell*,SharedShellFactory,ShellHash> + FactoryList; + FactoryList maShellFactories; + + /** List of the active view shells. In order to create gather all shells + to put on the shell stack each view shell in this list is asked for + its sub-shells (typically toolbars). + */ + typedef ::std::list<ShellDescriptor> ActiveShellList; + ActiveShellList maActiveViewShells; + + typedef ::std::list<ShellDescriptor> SubShellSubList; + typedef ::std::hash_map<const SfxShell*,SubShellSubList,ShellHash> SubShellList; + SubShellList maActiveSubShells; + + /** In this member we remember what shells we have pushed on the shell + stack. + */ + typedef ::std::vector<SfxShell*> ShellStack; + + int mnUpdateLockCount; + + /** When this flag is set then the main view shell is always kept at the + top of the shell stack. + */ + bool mbKeepMainViewShellOnTop; + + /** The UpdateShellStack() method can be called recursively. This flag + is used to communicate between different levels of invocation: if + the stack has been updated in an inner call the outer call can (has + to) stop and return immediately. + */ + bool mbShellStackIsUpToDate; + + SfxShell* mpFormShell; + const ViewShell* mpFormShellParent; + bool mbFormShellAboveParent; + + SfxShell* mpTopShell; + + void GatherActiveShells (ShellStack& rShellList); + + void UpdateShellStack (void); + + void CreateShells (void); + + /** This method rebuilds the stack of shells that are stacked upon the + view shell base. + */ + void CreateTargetStack (ShellStack& rStack) const; + + DECL_LINK(WindowEventHandler, VclWindowEvent*); + + void DumpShellStack (const ShellStack& rStack); + void DumpSfxShellStack (void); + + /** To be called before a shell is taken fom the SFX shell stack. This + method deactivates an active text editing to avoid problems with + undo managers. + Afterwards the Deactivate() of the shell is called. + */ + void Deactivate (SfxShell* pShell); + + ShellDescriptor CreateSubShell ( + SfxShell* pShell, + ShellId nShellId, + ::Window* pParentWindow, + FrameView* pFrameView); + void DestroyViewShell (const ShellDescriptor& rDescriptor); + void DestroySubShell ( + const SfxShell& rViewShell, + const ShellDescriptor& rDescriptor); +}; + + + + +//===== ViewShellManager ====================================================== + +ViewShellManager::ViewShellManager (ViewShellBase& rBase) + : mpImpl(new Implementation(*this,rBase)), + mbValid(true) +{ +} + + + + +ViewShellManager::~ViewShellManager (void) +{ +} + + + + +void ViewShellManager::AddSubShellFactory ( + ViewShell* pViewShell, + const SharedShellFactory& rpFactory) +{ + if (mbValid) + mpImpl->AddShellFactory(pViewShell, rpFactory); +} + + + + +void ViewShellManager::RemoveSubShellFactory ( + ViewShell* pViewShell, + const SharedShellFactory& rpFactory) +{ + if (mbValid) + mpImpl->RemoveShellFactory(pViewShell, rpFactory); +} + + + + +void ViewShellManager::ActivateViewShell (ViewShell* pViewShell) +{ + if (mbValid) + return mpImpl->ActivateViewShell(pViewShell); +} + + + + +void ViewShellManager::DeactivateViewShell (const ViewShell* pShell) +{ + if (mbValid && pShell!=NULL) + mpImpl->DeactivateViewShell(*pShell); +} + + + + +void ViewShellManager::MoveSubShellToTop ( + const ViewShell& rParentShell, + ShellId nId) +{ + if (mbValid) + mpImpl->MoveSubShellToTop(rParentShell, nId); +} + + + + +void ViewShellManager::SetFormShell ( + const ViewShell* pParentShell, + FmFormShell* pFormShell, + bool bAbove) +{ + if (mbValid) + mpImpl->SetFormShell(pParentShell,pFormShell,bAbove); +} + + + + +void ViewShellManager::ActivateSubShell (const ViewShell& rViewShell, ShellId nId) +{ + if (mbValid) + mpImpl->ActivateSubShell(rViewShell,nId); +} + + + + +void ViewShellManager::DeactivateSubShell (const ViewShell& rViewShell, ShellId nId) +{ + if (mbValid) + mpImpl->DeactivateSubShell(rViewShell,nId); +} + + + + +void ViewShellManager::InvalidateAllSubShells (ViewShell* pViewShell) +{ + if (mbValid) + mpImpl->InvalidateAllSubShells(pViewShell); +} + + + + +void ViewShellManager::ActivateShell (SfxShell* pShell) +{ + if (mbValid && pShell!=NULL) + mpImpl->ActivateShell(*pShell); +} + + + + +void ViewShellManager::DeactivateShell (const SfxShell* pShell) +{ + if (mbValid && pShell!=NULL) + mpImpl->DeactivateShell(*pShell); +} + + + + +void ViewShellManager::MoveToTop (const ViewShell& rParentShell) +{ + if (mbValid) + mpImpl->MoveToTop(rParentShell); +} + + + + +SfxShell* ViewShellManager::GetShell (ShellId nId) const +{ + if (mbValid) + return mpImpl->GetShell(nId); + else + return NULL; +} + + + + +SfxShell* ViewShellManager::GetTopShell (void) const +{ + if (mbValid) + return mpImpl->GetTopShell(); + else + return NULL; +} + + + + +void ViewShellManager::Shutdown (void) +{ + if (mbValid) + { + mpImpl->Shutdown(); + mbValid = false; + } +} + + + +void ViewShellManager::LockUpdate (void) +{ + mpImpl->LockUpdate(); +} + + + + +void ViewShellManager::UnlockUpdate (void) +{ + mpImpl->UnlockUpdate(); +} + + + + +//===== ViewShellManager::Implementation ====================================== + +ViewShellManager::Implementation::Implementation ( + ViewShellManager& rManager, + ViewShellBase& rBase) + : mrBase(rBase), + maMutex(), + maShellFactories(), + maActiveViewShells(), + mnUpdateLockCount(0), + mbKeepMainViewShellOnTop(false), + mbShellStackIsUpToDate(true), + mpFormShell(NULL), + mpFormShellParent(NULL), + mbFormShellAboveParent(true), + mpTopShell(NULL) +{ + (void)rManager; +} + + + + +ViewShellManager::Implementation::~Implementation (void) +{ + Shutdown(); +} + + + + +void ViewShellManager::Implementation::AddShellFactory ( + const SfxShell* pViewShell, + const SharedShellFactory& rpFactory) +{ + bool bAlreadyAdded (false); + + // Check that the given factory has not already been added. + ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange( + maShellFactories.equal_range(pViewShell)); + for (FactoryList::const_iterator iFactory=aRange.first; iFactory!=aRange.second; ++iFactory) + if (iFactory->second == rpFactory) + { + bAlreadyAdded = true; + break; + } + + // Add the factory if it is not already present. + if ( ! bAlreadyAdded) + maShellFactories.insert(FactoryList::value_type(pViewShell, rpFactory)); +} + + + + +void ViewShellManager::Implementation::RemoveShellFactory ( + const SfxShell* pViewShell, + const SharedShellFactory& rpFactory) +{ + ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange( + maShellFactories.equal_range(pViewShell)); + for (FactoryList::iterator iFactory=aRange.first; iFactory!=aRange.second; ++iFactory) + if (iFactory->second == rpFactory) + { + maShellFactories.erase(iFactory); + break; + } +} + + + + +void ViewShellManager::Implementation::ActivateViewShell (ViewShell* pViewShell) +{ + ::osl::MutexGuard aGuard (maMutex); + + ShellDescriptor aResult; + aResult.mpShell = pViewShell; + + // Register as window listener so that the shells of the current + // window can be moved to the top of the shell stack. + if (aResult.mpShell != NULL) + { + ::Window* pWindow = aResult.GetWindow(); + if (pWindow != NULL) + pWindow->AddEventListener( + LINK(this, ViewShellManager::Implementation, WindowEventHandler)); + else + { + DBG_ASSERT(false, + "ViewShellManager::ActivateViewShell: " + "new view shell has no active window"); + } + } + + ActivateShell(aResult); +} + + + + +void ViewShellManager::Implementation::DeactivateViewShell (const ViewShell& rShell) +{ + ::osl::MutexGuard aGuard (maMutex); + + ActiveShellList::iterator iShell (::std::find_if ( + maActiveViewShells.begin(), + maActiveViewShells.end(), + IsShell(&rShell))); + if (iShell != maActiveViewShells.end()) + { + UpdateLock aLocker (*this); + + ShellDescriptor aDescriptor(*iShell); + mrBase.GetDocShell()->Disconnect(dynamic_cast<ViewShell*>(aDescriptor.mpShell)); + maActiveViewShells.erase(iShell); + TakeShellsFromStack(aDescriptor.mpShell); + + // Deactivate sub shells. + SubShellList::iterator iList (maActiveSubShells.find(&rShell)); + if (iList != maActiveSubShells.end()) + { + SubShellSubList& rList (iList->second); + while ( ! rList.empty()) + DeactivateSubShell(rShell, rList.front().mnId); + } + + DestroyViewShell(aDescriptor); + } +} + + + + +void ViewShellManager::Implementation::ActivateShell (SfxShell& rShell) +{ + ::osl::MutexGuard aGuard (maMutex); + + // Create a new shell or recycle on in the cache. + ShellDescriptor aDescriptor; + aDescriptor.mpShell = &rShell; + + ActivateShell(aDescriptor); +} + + + + +void ViewShellManager::Implementation::ActivateShell (const ShellDescriptor& rDescriptor) +{ + // Put shell on top of the active view shells. + if (rDescriptor.mpShell != NULL) + { + // Determine where to put the view shell on the stack. By default + // it is put on top of the stack. When the view shell of the center + // pane is to be kept top most and the new view shell is not + // displayed in the center pane then it is inserted at the position + // one below the top. + ActiveShellList::iterator iInsertPosition (maActiveViewShells.begin()); + if (iInsertPosition != maActiveViewShells.end() + && mbKeepMainViewShellOnTop + && ! rDescriptor.IsMainViewShell() + && iInsertPosition->IsMainViewShell()) + { + ++iInsertPosition; + } + maActiveViewShells.insert( + iInsertPosition, + rDescriptor); + } +} + + + + +void ViewShellManager::Implementation::DeactivateShell (const SfxShell& rShell) +{ + ::osl::MutexGuard aGuard (maMutex); + + ActiveShellList::iterator iShell (::std::find_if ( + maActiveViewShells.begin(), + maActiveViewShells.end(), + IsShell(&rShell))); + if (iShell != maActiveViewShells.end()) + { + UpdateLock aLocker (*this); + + ShellDescriptor aDescriptor(*iShell); + mrBase.GetDocShell()->Disconnect(dynamic_cast<ViewShell*>(aDescriptor.mpShell)); + maActiveViewShells.erase(iShell); + TakeShellsFromStack(aDescriptor.mpShell); + + // Deactivate sub shells. + SubShellList::iterator iList (maActiveSubShells.find(&rShell)); + if (iList != maActiveSubShells.end()) + { + SubShellSubList& rList (iList->second); + while ( ! rList.empty()) + DeactivateSubShell(rShell, rList.front().mnId); + } + + DestroyViewShell(aDescriptor); + } +} + + + + +void ViewShellManager::Implementation::ActivateSubShell ( + const SfxShell& rParentShell, + ShellId nId) +{ + ::osl::MutexGuard aGuard (maMutex); + + do + { + // Check that the given view shell is active. + ActiveShellList::iterator iShell (::std::find_if ( + maActiveViewShells.begin(), + maActiveViewShells.end(), + IsShell(&rParentShell))); + if (iShell == maActiveViewShells.end()) + break; + + // Create the sub shell list if it does not yet exist. + SubShellList::iterator iList (maActiveSubShells.find(&rParentShell)); + if (iList == maActiveSubShells.end()) + iList = maActiveSubShells.insert( + SubShellList::value_type(&rParentShell,SubShellSubList())).first; + + // Do not activate an object bar that is already active. Requesting + // this is not exactly an error but may be an indication of one. + SubShellSubList& rList (iList->second); + if (::std::find_if(rList.begin(),rList.end(), IsId(nId)) != rList.end()) + break; + + // Add just the id of the sub shell. The actual shell is created + // later in CreateShells(). + UpdateLock aLock (*this); + rList.push_back(ShellDescriptor(NULL, nId)); + } + while (false); +} + + + + +void ViewShellManager::Implementation::DeactivateSubShell ( + const SfxShell& rParentShell, + ShellId nId) +{ + ::osl::MutexGuard aGuard (maMutex); + + do + { + // Check that the given view shell is active. + SubShellList::iterator iList (maActiveSubShells.find(&rParentShell)); + if (iList == maActiveSubShells.end()) + break; + + // Look up the sub shell. + SubShellSubList& rList (iList->second); + SubShellSubList::iterator iShell ( + ::std::find_if(rList.begin(),rList.end(), IsId(nId))); + if (iShell == rList.end()) + break; + SfxShell* pShell = iShell->mpShell; + if (pShell == NULL) + break; + + UpdateLock aLock (*this); + + ShellDescriptor aDescriptor(*iShell); + // Remove the sub shell from both the internal structure as well as the + // SFX shell stack above and including the sub shell. + rList.erase(iShell); + TakeShellsFromStack(pShell); + + DestroySubShell(rParentShell, aDescriptor); + } + while (false); +} + + + + +void ViewShellManager::Implementation::MoveSubShellToTop ( + const SfxShell& rParentShell, + ShellId nId) +{ + SubShellList::iterator iList (maActiveSubShells.find(&rParentShell)); + if (iList != maActiveSubShells.end()) + { + // Look up the sub shell. + SubShellSubList& rList (iList->second); + SubShellSubList::iterator iShell ( + ::std::find_if(rList.begin(),rList.end(), IsId(nId))); + if (iShell!=rList.end() && iShell!=rList.begin()) + { + SubShellSubList::value_type aEntry (*iShell); + rList.erase(iShell); + rList.push_front(aEntry); + } + } + else + { + // Ignore this call when there are no sub shells for the given + // parent shell. We could remember the sub shell to move to the top + // but we do not. Do call this method at a later time instead. + } +} + + + +void ViewShellManager::Implementation::MoveToTop (const SfxShell& rShell) +{ + ::osl::MutexGuard aGuard (maMutex); + + // Check that we have access to a dispatcher. If not, then we are + // (probably) called while the view shell is still being created or + // initialized. Without dispatcher we can not rebuild the shell stack + // to move the requested shell to the top. So return right away instead + // of making a mess without being able to clean up afterwards. + if (mrBase.GetDispatcher() == NULL) + return; + + ActiveShellList::iterator iShell (::std::find_if ( + maActiveViewShells.begin(), + maActiveViewShells.end(), + IsShell(&rShell))); + bool bMove = true; + if (iShell != maActiveViewShells.end()) + { + // Is the shell already at the top of the stack? We have to keep + // the case in mind that mbKeepMainViewShellOnTop is true. Shells + // that are not the main view shell are placed on the second-to-top + // position in this case. + if (iShell == maActiveViewShells.begin() + && (iShell->IsMainViewShell() || ! mbKeepMainViewShellOnTop)) + { + // The shell is at the top position and is either a) the main + // view shell or b) another shell but the main view shell is not + // kept at the top position. We do not have to move the shell. + bMove = false; + } + else if (iShell == ++maActiveViewShells.begin() + && ! iShell->IsMainViewShell() + && mbKeepMainViewShellOnTop) + { + // The shell is a the second-to-top position, not the main view + // shell and the main view shell is kept at the top position. + // Therefore we do not have to move the shell. + bMove = false; + } + } + else + { + // The shell is not on the stack. Therefore it can not be moved. + // We could insert it but we don't. Use ActivateViewShell() for + // that. + bMove = false; + } + + // When the shell is not at the right position it is removed from the + // internal list of shells and inserted at the correct position. + if (bMove) + { + UpdateLock aLock (*this); + + ShellDescriptor aDescriptor(*iShell); + + TakeShellsFromStack(&rShell); + maActiveViewShells.erase(iShell); + + // Find out whether to insert at the top or one below. + ActiveShellList::iterator aInsertPosition (maActiveViewShells.begin()); + if (mbKeepMainViewShellOnTop && ! aDescriptor.IsMainViewShell()) + { + if (maActiveViewShells.back().IsMainViewShell()) + aInsertPosition++; + } + + maActiveViewShells.insert(aInsertPosition, aDescriptor); + } +} + + + + +SfxShell* ViewShellManager::Implementation::GetShell (ShellId nId) const +{ + ::osl::MutexGuard aGuard (maMutex); + + SfxShell* pShell = NULL; + + // First search the active view shells. + ActiveShellList::const_iterator iShell ( + ::std::find_if ( + maActiveViewShells.begin(), + maActiveViewShells.end(), + IsId(nId))); + if (iShell != maActiveViewShells.end()) + pShell = iShell->mpShell; + else + { + // Now search the active sub shells of every active view shell. + SubShellList::const_iterator iList; + for (iList=maActiveSubShells.begin(); iList!=maActiveSubShells.end(); ++iList) + { + const SubShellSubList& rList (iList->second); + SubShellSubList::const_iterator iSubShell( + ::std::find_if(rList.begin(),rList.end(), IsId(nId))); + if (iSubShell != rList.end()) + { + pShell = iSubShell->mpShell; + break; + } + } + } + + return pShell; +} + + + + +SfxShell* ViewShellManager::Implementation::GetTopShell (void) const +{ + OSL_ASSERT(mpTopShell == mrBase.GetSubShell(0)); + return mpTopShell; +} + + + + +void ViewShellManager::Implementation::LockUpdate (void) +{ + mnUpdateLockCount++; +} + + + + +void ViewShellManager::Implementation::UnlockUpdate (void) +{ + ::osl::MutexGuard aGuard (maMutex); + + mnUpdateLockCount--; + if (mnUpdateLockCount < 0) + { + // This should not happen. + OSL_ASSERT (mnUpdateLockCount>=0); + mnUpdateLockCount = 0; + } + if (mnUpdateLockCount == 0) + UpdateShellStack(); +} + + + + +/** Update the SFX shell stack (the portion that is visible to us) so that + it matches the internal shell stack. This is done in six steps: + 1. Create the missing view shells and sub shells. + 2. Set up the internal shell stack. + 3. Get the SFX shell stack. + 4. Find the lowest shell in which the two stacks differ. + 5. Remove all shells above and including that shell from the SFX stack. + 6. Push all shells of the internal stack on the SFX shell stack that are + not already present on the later. +*/ +void ViewShellManager::Implementation::UpdateShellStack (void) +{ + ::osl::MutexGuard aGuard (maMutex); + + // Remember the undo manager from the top-most shell on the stack. + SfxShell* pTopMostShell = mrBase.GetSubShell(0); + SfxUndoManager* pUndoManager = (pTopMostShell!=NULL) + ? pTopMostShell->GetUndoManager() + : NULL; + + // 1. Create the missing shells. + CreateShells(); + + + // 2. Create the internal target stack. + ShellStack aTargetStack; + CreateTargetStack(aTargetStack); + + + // 3. Get SFX shell stack. + ShellStack aSfxShellStack; + USHORT nIndex (0); + while (mrBase.GetSubShell(nIndex)!=NULL) + ++nIndex; + aSfxShellStack.reserve(nIndex); + while (nIndex-- > 0) + aSfxShellStack.push_back(mrBase.GetSubShell(nIndex)); + + +#ifdef VERBOSE + OSL_TRACE("Current SFX Stack\r"); + DumpShellStack(aSfxShellStack); + OSL_TRACE("Target Stack\r"); + DumpShellStack(aTargetStack); +#endif + + + // 4. Find the lowest shell in which the two stacks differ. + ShellStack::const_iterator iSfxShell (aSfxShellStack.begin()); + ShellStack::iterator iTargetShell (aTargetStack.begin()); + while (iSfxShell != aSfxShellStack.end() + && iTargetShell!=aTargetStack.end() + && (*iSfxShell)==(*iTargetShell)) + { + ++iSfxShell; + ++iTargetShell; + } + + + // 5. Remove all shells above and including the differing shell from the + // SFX stack starting with the shell on top of the stack. + while (iSfxShell != aSfxShellStack.end()) + { + SfxShell* pShell = aSfxShellStack.back(); + aSfxShellStack.pop_back(); +#ifdef VERBOSE + OSL_TRACE("removing shell %p from stack\r", pShell); +#endif + mrBase.RemoveSubShell(pShell); + } + + + // 6. Push shells from the given stack onto the SFX stack. + mbShellStackIsUpToDate = false; + while (iTargetShell != aTargetStack.end()) + { +#ifdef VERBOSE + OSL_TRACE("pushing shell %p on stack\r", *iTargetShell); +#endif + mrBase.AddSubShell(**iTargetShell); + ++iTargetShell; + + // The pushing of the shell on to the shell stack may have lead to + // another invocation of this method. In this case we have to abort + // pushing shells on the stack and return immediately. + if (mbShellStackIsUpToDate) + break; + } + if (mrBase.GetDispatcher() != NULL) + mrBase.GetDispatcher()->Flush(); + + // Update the pointer to the top-most shell and set its undo manager + // to the one of the previous top-most shell. + mpTopShell = mrBase.GetSubShell(0); + if (mpTopShell!=NULL && pUndoManager!=NULL && mpTopShell->GetUndoManager()==NULL) + mpTopShell->SetUndoManager(pUndoManager); + + // Finally tell an invocation of this method on a higher level that it can (has + // to) abort and return immediately. + mbShellStackIsUpToDate = true; + +#ifdef VERBOSE + OSL_TRACE("New current stack\r"); + DumpSfxShellStack(); +#endif +} + + + + +void ViewShellManager::Implementation::TakeShellsFromStack (const SfxShell* pShell) +{ + ::osl::MutexGuard aGuard (maMutex); + + // Remember the undo manager from the top-most shell on the stack. + SfxShell* pTopMostShell = mrBase.GetSubShell(0); + SfxUndoManager* pUndoManager = (pTopMostShell!=NULL) + ? pTopMostShell->GetUndoManager() + : NULL; + +#ifdef VERBOSE + OSL_TRACE("TakeShellsFromStack(%p)\r", pShell); + DumpSfxShellStack(); +#endif + + // 0.Make sure that the given shell is on the stack. This is a + // preparation for the following assertion. + for (USHORT nIndex=0; true; nIndex++) + { + SfxShell* pShellOnStack = mrBase.GetSubShell(nIndex); + if (pShellOnStack == NULL) + { + // Set pShell to NULL to indicate the following code that the + // shell is not on the stack. + pShell = NULL; + break; + } + else if (pShellOnStack == pShell) + break; + } + + if (pShell != NULL) + { + // 1. Deactivate our shells on the stack before they are removed so + // that during the Deactivation() calls the stack is still intact. + for (USHORT nIndex=0; true; nIndex++) + { + SfxShell* pShellOnStack = mrBase.GetSubShell(nIndex); + Deactivate(pShellOnStack); + if (pShellOnStack == pShell) + break; + } + + // 2. Remove the shells from the stack. + while (true) + { + SfxShell* pShellOnStack = mrBase.GetSubShell(0); +#ifdef VERBOSE + OSL_TRACE("removing shell %p from stack\r", pShellOnStack); +#endif + mrBase.RemoveSubShell(pShellOnStack); + if (pShellOnStack == pShell) + break; + } + + // 3. Update the stack. + if (mrBase.GetDispatcher() != NULL) + mrBase.GetDispatcher()->Flush(); + + // Update the pointer to the top-most shell and set its undo manager + // to the one of the previous top-most shell. + mpTopShell = mrBase.GetSubShell(0); + if (mpTopShell!=NULL && pUndoManager!=NULL && mpTopShell->GetUndoManager()==NULL) + mpTopShell->SetUndoManager(pUndoManager); + } + +#ifdef VERBOSE + OSL_TRACE("Sfx shell stack is:\r"); + DumpSfxShellStack(); +#endif +} + + + + +void ViewShellManager::Implementation::CreateShells (void) +{ + ::osl::MutexGuard aGuard (maMutex); + + // Iterate over all view shells. + ShellStack aShellStack; + ActiveShellList::reverse_iterator iShell; + for (iShell=maActiveViewShells.rbegin(); iShell!=maActiveViewShells.rend(); ++iShell) + { + // Get the list of associated sub shells. + SubShellList::iterator iList (maActiveSubShells.find(iShell->mpShell)); + if (iList != maActiveSubShells.end()) + { + SubShellSubList& rList (iList->second); + + // Iterate over all sub shells of the current view shell. + SubShellSubList::iterator iSubShell; + for (iSubShell=rList.begin(); iSubShell!=rList.end(); ++iSubShell) + { + if (iSubShell->mpShell == NULL) + { + *iSubShell = CreateSubShell(iShell->mpShell,iSubShell->mnId,NULL,NULL); + } + } + } + } +} + + + + +void ViewShellManager::Implementation::CreateTargetStack (ShellStack& rStack) const +{ + // Create a local stack of the shells that are to push on the shell + // stack. We can thus safly create the required shells wile still + // having a valid shell stack. + for (ActiveShellList::const_reverse_iterator iViewShell (maActiveViewShells.rbegin()); + iViewShell != maActiveViewShells.rend(); + ++iViewShell) + { + // Possibly place the form shell below the current view shell. + if ( ! mbFormShellAboveParent + && mpFormShell!=NULL + && iViewShell->mpShell==mpFormShellParent) + { + rStack.push_back(mpFormShell); + } + + // Put the view shell itself on the local stack. + rStack.push_back (iViewShell->mpShell); + + // Possibly place the form shell above the current view shell. + if (mbFormShellAboveParent + && mpFormShell!=NULL + && iViewShell->mpShell==mpFormShellParent) + { + rStack.push_back(mpFormShell); + } + + // Add all other sub shells. + SubShellList::const_iterator iList (maActiveSubShells.find(iViewShell->mpShell)); + if (iList != maActiveSubShells.end()) + { + const SubShellSubList& rList (iList->second); + SubShellSubList::const_reverse_iterator iSubShell; + for (iSubShell=rList.rbegin(); iSubShell!=rList.rend(); ++iSubShell) + if (iSubShell->mpShell != mpFormShell) + rStack.push_back(iSubShell->mpShell); + } + } +} + + + + +IMPL_LINK(ViewShellManager::Implementation, WindowEventHandler, VclWindowEvent*, pEvent) +{ + if (pEvent != NULL) + { + ::Window* pEventWindow + = static_cast<VclWindowEvent*>(pEvent)->GetWindow(); + + switch (pEvent->GetId()) + { + case VCLEVENT_WINDOW_GETFOCUS: + { + for (ActiveShellList::iterator aI(maActiveViewShells.begin()); + aI!=maActiveViewShells.end(); + aI++) + { + if (pEventWindow == static_cast< ::Window*>(aI->GetWindow())) + { + MoveToTop(*aI->mpShell); + break; + } + } + } + break; + + case VCLEVENT_WINDOW_LOSEFOCUS: + break; + } + } + return TRUE; +} + + + + +ShellDescriptor ViewShellManager::Implementation::CreateSubShell ( + SfxShell* pParentShell, + ShellId nShellId, + ::Window* pParentWindow, + FrameView* pFrameView) +{ + ::osl::MutexGuard aGuard (maMutex); + ShellDescriptor aResult; + + // Look up the factories for the parent shell. + ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange( + maShellFactories.equal_range(pParentShell)); + + // Try all factories to create the shell. + for (FactoryList::const_iterator iFactory=aRange.first; iFactory!=aRange.second; ++iFactory) + { + SharedShellFactory pFactory = iFactory->second; + if (pFactory != NULL) + aResult.mpShell = pFactory->CreateShell(nShellId, pParentWindow, pFrameView); + + // Exit the loop when the shell has been successfully created. + if (aResult.mpShell != NULL) + { + aResult.mpFactory = pFactory; + aResult.mnId = nShellId; + break; + } + } + + return aResult; +} + + + + +void ViewShellManager::Implementation::DestroyViewShell ( + const ShellDescriptor& rDescriptor) +{ + OSL_ASSERT(rDescriptor.mpShell != NULL); + + ::Window* pWindow = rDescriptor.GetWindow(); + if (pWindow != NULL) + { + pWindow->RemoveEventListener( + LINK(this, ViewShellManager::Implementation, WindowEventHandler)); + } + + // Destroy the sub shell factories. + ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange( + maShellFactories.equal_range(rDescriptor.mpShell)); + if (aRange.first != maShellFactories.end()) + maShellFactories.erase(aRange.first, aRange.second); + + // Release the shell. + if (rDescriptor.mpFactory.get() != NULL) + rDescriptor.mpFactory->ReleaseShell(rDescriptor.mpShell); +} + + + + +void ViewShellManager::Implementation::DestroySubShell ( + const SfxShell& rParentShell, + const ShellDescriptor& rDescriptor) +{ + (void)rParentShell; + OSL_ASSERT(rDescriptor.mpFactory.get() != NULL); + rDescriptor.mpFactory->ReleaseShell(rDescriptor.mpShell); +} + + + + +void ViewShellManager::Implementation::InvalidateAllSubShells (const SfxShell* pParentShell) +{ + ::osl::MutexGuard aGuard (maMutex); + + SubShellList::iterator iList (maActiveSubShells.find(pParentShell)); + if (iList != maActiveSubShells.end()) + { + SubShellSubList& rList (iList->second); + SubShellSubList::iterator iShell; + for (iShell=rList.begin(); iShell!=rList.end(); ++iShell) + if (iShell->mpShell != NULL) + iShell->mpShell->Invalidate(); + } +} + + + + +void ViewShellManager::Implementation::Shutdown (void) +{ + ::osl::MutexGuard aGuard (maMutex); + + // Take stacked shells from stack. + if ( ! maActiveViewShells.empty()) + { + UpdateLock aLock (*this); + + + while ( ! maActiveViewShells.empty()) + { + SfxShell* pShell = maActiveViewShells.front().mpShell; + if (pShell != NULL) + { + ViewShell* pViewShell = dynamic_cast<ViewShell*>(pShell); + if (pViewShell != NULL) + DeactivateViewShell(*pViewShell); + else + DeactivateShell(*pShell); + } + else + { + DBG_ASSERT(false, + "ViewShellManager::Implementation::Shutdown(): empty active shell descriptor"); + maActiveViewShells.pop_front(); + } + } + } + mrBase.RemoveSubShell (NULL); + + maShellFactories.clear(); +} + + + + +void ViewShellManager::Implementation::DumpShellStack (const ShellStack& rStack) +{ + ShellStack::const_reverse_iterator iEntry; + for (iEntry=rStack.rbegin(); iEntry!=rStack.rend(); ++iEntry) + if (*iEntry != NULL) + OSL_TRACE (" %p : %s\r", + *iEntry, + ::rtl::OUStringToOString((*iEntry)->GetName(),RTL_TEXTENCODING_UTF8).getStr()); + else + OSL_TRACE(" null\r"); +} + + + + +void ViewShellManager::Implementation::DumpSfxShellStack (void) +{ + ShellStack aSfxShellStack; + USHORT nIndex (0); + while (mrBase.GetSubShell(nIndex)!=NULL) + ++nIndex; + aSfxShellStack.reserve(nIndex); + while (nIndex-- > 0) + aSfxShellStack.push_back(mrBase.GetSubShell(nIndex)); + DumpShellStack(aSfxShellStack); +} + + + + +void ViewShellManager::Implementation::Deactivate (SfxShell* pShell) +{ + OSL_ASSERT(pShell!=NULL); + + // We have to end a text edit for view shells that are to be taken from + // the shell stack. + ViewShell* pViewShell = dynamic_cast<ViewShell*>(pShell); + if (pViewShell != NULL) + { + sd::View* pView = pViewShell->GetView(); + if (pView!=NULL && pView->IsTextEdit()) + { + pView->SdrEndTextEdit(); + pView->UnmarkAll(); + pViewShell->GetViewFrame()->GetDispatcher()->Execute( + SID_OBJECT_SELECT, + SFX_CALLMODE_ASYNCHRON); + } + } + + // Now we can deactivate the shell. + pShell->Deactivate(TRUE); +} + + + + +void ViewShellManager::Implementation::SetFormShell ( + const ViewShell* pFormShellParent, + FmFormShell* pFormShell, + bool bFormShellAboveParent) +{ + ::osl::MutexGuard aGuard (maMutex); + + mpFormShellParent = pFormShellParent; + mpFormShell = pFormShell; + mbFormShellAboveParent = bFormShellAboveParent; +} + + + + +namespace { + +ShellDescriptor::ShellDescriptor (void) + : mpShell(NULL), + mnId(0), + mpFactory() +{ +} + + + + +ShellDescriptor::ShellDescriptor ( + SfxShell* pShell, + ShellId nId) + : mpShell(pShell), + mnId(nId), + mpFactory() +{ +} + + + + +ShellDescriptor::ShellDescriptor (const ShellDescriptor& rDescriptor) + : mpShell(rDescriptor.mpShell), + mnId(rDescriptor.mnId), + mpFactory(rDescriptor.mpFactory) +{ +} + + + + +ShellDescriptor& ShellDescriptor::operator= (const ShellDescriptor& rDescriptor) +{ + mpShell = rDescriptor.mpShell; + mnId = rDescriptor.mnId; + mpFactory = rDescriptor.mpFactory; + return *this; +} + + + + +bool ShellDescriptor::IsMainViewShell (void) const +{ + ViewShell* pViewShell = dynamic_cast<ViewShell*>(mpShell); + if (pViewShell != NULL) + return pViewShell->IsMainViewShell(); + else + return false; +} + + + + +::Window* ShellDescriptor::GetWindow (void) const +{ + ViewShell* pViewShell = dynamic_cast<ViewShell*>(mpShell); + if (pViewShell != NULL) + return pViewShell->GetActiveWindow(); + else + return NULL; +} + + + +} // end of anonymous namespace + +} // end of namespace sd |