/** This functor can be used to search for a shell in an STL container when the shellpointerisgiven.
*/ class IsShell
{ public: explicit IsShell (const SfxShell* pShell) : mpShell(pShell) {} booloperator() (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 idoftheshellisgiven.
*/ class IsId
{ public: explicit IsId (ShellId nId) : mnId(nId) {} booloperator() (const ShellDescriptor& rDescriptor)
{ return rDescriptor.mnId == mnId; } private:
ShellId mnId;
};
} // end of anonymous namespace
class ViewShellManager::Implementation
{ public:
Implementation (
ViewShellBase& rBase);
~Implementation() COVERITY_NOEXCEPT_FALSE;
/** Prevent updates of the shell stack. While the sub shell manager is lockeditwillupdateitsinternaldatastructuresbutnotalterthe shellstack.Usethismethodwhenthereareseveralmodifications totheshellstacktopreventmultiplerebuildsoftheshellstack andresultingbroadcasts.
*/ void LockUpdate();
/** Allow updates of the shell stack. This method has to be called the samenumberoftimesasLockUpdate()toreallyallowarebuildof theshellstack.
*/ void UnlockUpdate();
/** List of the active view shells. In order to create gather all shells toputontheshellstackeachviewshellinthislistisaskedfor itssub-shells(typicallytoolbars).
*/ typedef std::list<ShellDescriptor> ActiveShellList;
ActiveShellList maActiveViewShells;
/** In this member we remember what shells we have pushed on the shell stack.
*/ typedef ::std::vector<SfxShell*> ShellStack;
int mnUpdateLockCount;
/** The UpdateShellStack() method can be called recursively. This flag isusedtocommunicatebetweendifferentlevelsofinvocation:if thestackhasbeenupdatedinaninnercalltheoutercallcan(has to)stopandreturnimmediately.
*/ bool mbShellStackIsUpToDate;
/** To be called before a shell is taken from the SFX shell stack. This methoddeactivatesanactivetexteditingtoavoidproblemswith undomanagers. AfterwardstheDeactivate()oftheshelliscalled.
*/ staticvoid Deactivate (SfxShell* pShell);
// 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.emplace(pViewShell, rpFactory);
}
// 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 != nullptr)
{
vcl::Window* pWindow = aResult.GetWindow(); if (pWindow != nullptr)
{
pWindow->AddEventListener(
LINK(this, ViewShellManager::Implementation, WindowEventHandler));
aResult.mbIsListenerAddedToWindow = true;
} else
{
SAL_WARN("sd.view", "ViewShellManager::ActivateViewShell: " "new view shell has no active window");
}
}
// Create a new shell or recycle on in the cache.
ShellDescriptor aDescriptor;
aDescriptor.mpShell = &rShell;
ActivateLowPriorityShell(aDescriptor);
}
void ViewShellManager::Implementation::ActivateShell (const ShellDescriptor& rDescriptor)
{ // Put shell on top of the active view shells. if (rDescriptor.mpShell != nullptr)
{
maActiveViewShells.insert( maActiveViewShells.begin(), rDescriptor);
}
}
void ViewShellManager::Implementation::ActivateLowPriorityShell (const ShellDescriptor& rDescriptor)
{ // Put shell on bottom of the active view shells. if (rDescriptor.mpShell != nullptr)
{
maActiveViewShells.push_back( rDescriptor );
}
}
// Check that the given view shell is active. if (std::none_of (maActiveViewShells.begin(), maActiveViewShells.end(), IsShell(&rParentShell))) return;
// Create the sub shell list if it does not yet exist.
SubShellList::iterator iList (maActiveSubShells.find(&rParentShell)); if (iList == maActiveSubShells.end())
iList = maActiveSubShells.emplace(&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::any_of(rList.begin(),rList.end(), IsId(nId))) return;
// Add just the id of the sub shell. The actual shell is created // later in CreateShells().
UpdateLock aLock (*this);
rList.emplace_back(nId);
}
// Check that the given view shell is active.
SubShellList::iterator iList (maActiveSubShells.find(&rParentShell)); if (iList == maActiveSubShells.end()) return;
// 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()) return;
SfxShell* pShell = iShell->mpShell; if (pShell == nullptr) return;
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);
// 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() == nullptr) 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())
{ // 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
{ // 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);
// 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. for (autoconst& activeSubShell : maActiveSubShells)
{ const SubShellSubList& rList (activeSubShell.second);
SubShellSubList::const_iterator iSubShell(
::std::find_if(rList.begin(),rList.end(), IsId(nId))); if (iSubShell != rList.end())
{
pShell = iSubShell->mpShell; break;
}
}
}
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 itmatchestheinternalshellstack.Thisisdoneinsixsteps: 1.Createthemissingviewshellsandsubshells. 2.Setuptheinternalshellstack. 3.GettheSFXshellstack. 4.Findthelowestshellinwhichthetwostacksdiffer. 5.RemoveallshellsaboveandincludingthatshellfromtheSFXstack. 6.PushallshellsoftheinternalstackontheSFXshellstackthatare notalreadypresentonthelater.
*/ void ViewShellManager::Implementation::UpdateShellStack()
{
::osl::MutexGuard aGuard (maMutex);
// Remember the undo manager from the top-most shell on the stack.
SfxShell* pTopMostShell = mrBase.GetSubShell(0);
SfxUndoManager* pUndoManager = (pTopMostShell!=nullptr)
? pTopMostShell->GetUndoManager()
: nullptr;
// 1. Create the missing shells.
CreateShells();
SfxShell* pPreviousTopViewShell = mpTopViewShell; // Update the pointer to the top-most active view shell.
mpTopViewShell = (maActiveViewShells.empty() || mbFormShellAboveParent)
? nullptr : maActiveViewShells.begin()->mpShell;
// 4. Find the lowest shell in which the two stacks differ. auto mismatchIters = std::mismatch(aSfxShellStack.begin(), aSfxShellStack.end(),
aTargetStack.begin(), aTargetStack.end());
ShellStack::iterator iSfxShell (mismatchIters.first);
ShellStack::iterator iTargetShell (mismatchIters.second);
// 5. Remove all shells above and including the differing shell from the // SFX stack starting with the shell on top of the stack. for (std::reverse_iterator<ShellStack::const_iterator> i(aSfxShellStack.end()), iLast(iSfxShell);
i != iLast; ++i)
{
SfxShell* const pShell = *i;
SAL_INFO("sd.view", __func__ << ": removing shell " << pShell << " from stack");
mrBase.RemoveSubShell(pShell);
}
aSfxShellStack.erase(iSfxShell, aSfxShellStack.end());
// 6. Push shells from the given stack onto the SFX stack.
mbShellStackIsUpToDate = false; while (iTargetShell != aTargetStack.end())
{
SAL_INFO("sd.view", __func__ << ": pushing shell " << *iTargetShell << " on stack");
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() != nullptr)
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!=nullptr && pUndoManager!=nullptr && mpTopShell->GetUndoManager()==nullptr)
mpTopShell->SetUndoManager(pUndoManager);
// Only broadcast context for activation on the top-most ViewShell if (mpTopViewShell && bTopViewShellChanged)
mpTopViewShell->BroadcastContextForActivation(true);
// Finally tell an invocation of this method on a higher level that it can (has // to) abort and return immediately.
mbShellStackIsUpToDate = true;
#if OSL_DEBUG_LEVEL >= 2
SAL_INFO("sd.view", __func__ << ": New current stack");
DumpSfxShellStack(); #endif
}
// Remember the undo manager from the top-most shell on the stack.
SfxShell* pTopMostShell = mrBase.GetSubShell(0);
SfxUndoManager* pUndoManager = (pTopMostShell!=nullptr)
? pTopMostShell->GetUndoManager()
: nullptr;
// 0.Make sure that the given shell is on the stack. This is a // preparation for the following assertion. for (sal_uInt16 nIndex=0; true; nIndex++)
{
SfxShell* pShellOnStack = mrBase.GetSubShell(nIndex); if (pShellOnStack == nullptr)
{ // the shell is not on the stack. return;
} elseif (pShellOnStack == &rShell) break;
}
// 1. Deactivate our shells on the stack before they are removed so // that during the Deactivation() calls the stack is still intact. for (sal_uInt16 nIndex=0; true; nIndex++)
{
SfxShell* pShellOnStack = mrBase.GetSubShell(nIndex);
Deactivate(pShellOnStack); if (pShellOnStack == &rShell) break;
}
// 2. Remove the shells from the stack. while (true)
{
SfxShell* pShellOnStack = mrBase.GetSubShell(0);
SAL_INFO("sd.view", __func__ << "removing shell " << pShellOnStack << " from stack");
mrBase.RemoveSubShell(pShellOnStack); if (pShellOnStack == &rShell) break;
}
// 3. Update the stack. if (mrBase.GetDispatcher() != nullptr)
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!=nullptr && pUndoManager!=nullptr && mpTopShell->GetUndoManager()==nullptr)
mpTopShell->SetUndoManager(pUndoManager);
// Iterate over all view shells.
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. for (auto & subShell : rList)
{ if (subShell.mpShell == nullptr)
{
subShell = CreateSubShell(iShell->mpShell,subShell.mnId);
}
}
}
}
}
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 safely create the required shells while 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!=nullptr
&& 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!=nullptr
&& 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);
}
}
}
switch (rEvent.GetId())
{ case VclEventId::WindowGetFocus:
{ for (autoconst& activeShell : maActiveViewShells)
{ if (pEventWindow == activeShell.GetWindow())
{
MoveToTop(*activeShell.mpShell); break;
}
}
} break;
case VclEventId::WindowLoseFocus: break;
case VclEventId::ObjectDying: // Remember that we do not have to remove the window // listener for this window. for (auto & activeViewShell : maActiveViewShells)
{ if (activeViewShell.GetWindow() == pEventWindow)
{
activeViewShell.mbIsListenerAddedToWindow = false; break;
}
} break;
// 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 != nullptr)
aResult.mpShell = pFactory->CreateShell(nShellId);
// Exit the loop when the shell has been successfully created. if (aResult.mpShell != nullptr)
{
aResult.mpFactory = std::move(pFactory);
aResult.mnId = nShellId; break;
}
}
// 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)
rDescriptor.mpFactory->ReleaseShell(rDescriptor.mpShell);
}
// 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 != nullptr)
{
sd::View* pView = pViewShell->GetView(); if (pView!=nullptr && pView->IsTextEdit())
{
pView->SdrEndTextEdit();
pView->UnmarkAll();
// dispatch synchronously, otherwise it might execute while another // ViewShell is active!
pViewShell->GetViewFrame()->GetDispatcher()->Execute(
SID_OBJECT_SELECT,
SfxCallMode::SYNCHRON);
}
}
// Now we can deactivate the shell.
pShell->Deactivate(true);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.