1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <ViewShellManager.hxx>
21 #include <ViewShell.hxx>
22 #include <ViewShellBase.hxx>
24 #include <DrawDocShell.hxx>
26 #include <sal/log.hxx>
27 #include <sfx2/dispatch.hxx>
28 #include <sfx2/viewfrm.hxx>
29 #include <svx/svxids.hrc>
30 #include <svx/fmshell.hxx>
31 #include <vcl/vclevent.hxx>
32 #include <osl/diagnose.h>
36 #include <unordered_map>
42 /** The ShellDescriptor class is used to shells together with their ids and
43 the factory that was used to create the shell.
45 The shell pointer may be NULL. In that case the shell is created on
48 The factory pointer may be NULL. In that case the shell pointer is
49 given to the ViewShellManager.
51 Shell pointer and factory pointer can but should not be NULL at the same
54 class ShellDescriptor
{
58 ViewShellManager::SharedShellFactory mpFactory
;
59 bool mbIsListenerAddedToWindow
;
62 explicit ShellDescriptor (ShellId nId
);
63 vcl::Window
* GetWindow() const;
66 /** This functor can be used to search for a shell in an STL container when the
67 shell pointer is given.
72 explicit IsShell (const SfxShell
* pShell
) : mpShell(pShell
) {}
73 bool operator() (const ShellDescriptor
& rDescriptor
)
74 { return rDescriptor
.mpShell
== mpShell
; }
76 const SfxShell
* mpShell
;
79 /** This functor can be used to search for a shell in an STL container when the
80 id of the shell is given.
85 explicit IsId (ShellId nId
) : mnId(nId
) {}
86 bool operator() (const ShellDescriptor
& rDescriptor
)
87 { return rDescriptor
.mnId
== mnId
; }
92 } // end of anonymous namespace
94 class ViewShellManager::Implementation
98 ViewShellBase
& rBase
);
99 ~Implementation() COVERITY_NOEXCEPT_FALSE
;
101 void AddShellFactory (
102 const SfxShell
* pViewShell
,
103 const SharedShellFactory
& rpFactory
);
104 void RemoveShellFactory (
105 const SfxShell
* pViewShell
,
106 const SharedShellFactory
& rpFactory
);
107 void ActivateViewShell (
108 ViewShell
* pViewShell
);
109 void DeactivateViewShell (const ViewShell
& rShell
);
110 void ActivateShell (SfxShell
& rShell
);
111 void ActivateLowPriorityShell (SfxShell
& rShell
);
112 void DeactivateShell (const SfxShell
& rShell
);
113 void ActivateShell (const ShellDescriptor
& rDescriptor
);
114 void ActivateLowPriorityShell (const ShellDescriptor
& rDescriptor
);
115 void SetFormShell (const ViewShell
* pViewShell
, FmFormShell
* pFormShell
, bool bAbove
);
116 void ActivateSubShell (const SfxShell
& rParentShell
, ShellId nId
);
117 void DeactivateSubShell (const SfxShell
& rParentShell
, ShellId nId
);
118 void RemoveOverridingMainShell();
119 void SetOverridingShell(const std::shared_ptr
<ViewShell
>& pViewShell
);
120 std::shared_ptr
<ViewShell
> GetOverridingShell();
121 void MoveToTop (const SfxShell
& rParentShell
);
122 SfxShell
* GetShell (ShellId nId
) const;
123 SfxShell
* GetTopShell() const;
124 SfxShell
* GetTopViewShell() const;
126 void InvalidateAllSubShells (const SfxShell
* pParentShell
);
128 /** Remove all shells from the SFX stack above and including the given
131 void TakeShellsFromStack (const SfxShell
& rShell
);
136 explicit UpdateLock (Implementation
& rImpl
) : mrImpl(rImpl
) {mrImpl
.LockUpdate();}
137 ~UpdateLock() COVERITY_NOEXCEPT_FALSE
{mrImpl
.UnlockUpdate();}
139 Implementation
& mrImpl
;
142 /** Prevent updates of the shell stack. While the sub shell manager is
143 locked it will update its internal data structures but not alter the
144 shell stack. Use this method when there are several modifications
145 to the shell stack to prevent multiple rebuilds of the shell stack
146 and resulting broadcasts.
150 /** Allow updates of the shell stack. This method has to be called the
151 same number of times as LockUpdate() to really allow a rebuild of
157 ViewShellBase
& mrBase
;
158 mutable ::osl::Mutex maMutex
;
160 class ShellHash
{ public: size_t operator()(const SfxShell
* p
) const { return reinterpret_cast<size_t>(p
);} };
161 typedef std::unordered_multimap
<const SfxShell
*,SharedShellFactory
,ShellHash
>
163 FactoryList maShellFactories
;
165 /** List of the active view shells. In order to create gather all shells
166 to put on the shell stack each view shell in this list is asked for
167 its sub-shells (typically toolbars).
169 typedef std::list
<ShellDescriptor
> ActiveShellList
;
170 ActiveShellList maActiveViewShells
;
172 typedef std::list
<ShellDescriptor
> SubShellSubList
;
173 typedef std::unordered_map
<const SfxShell
*,SubShellSubList
,ShellHash
> SubShellList
;
174 SubShellList maActiveSubShells
;
176 /** In this member we remember what shells we have pushed on the shell
179 typedef ::std::vector
<SfxShell
*> ShellStack
;
181 int mnUpdateLockCount
;
183 /** The UpdateShellStack() method can be called recursively. This flag
184 is used to communicate between different levels of invocation: if
185 the stack has been updated in an inner call the outer call can (has
186 to) stop and return immediately.
188 bool mbShellStackIsUpToDate
;
190 SfxShell
* mpFormShell
;
191 const ViewShell
* mpFormShellParent
;
192 bool mbFormShellAboveParent
;
194 SfxShell
* mpTopShell
;
195 SfxShell
* mpTopViewShell
;
197 std::weak_ptr
<ViewShell
> mpOverridingShell
;
199 void UpdateShellStack();
203 /** This method rebuilds the stack of shells that are stacked upon the
206 void CreateTargetStack (ShellStack
& rStack
) const;
208 DECL_LINK(WindowEventHandler
, VclWindowEvent
&, void);
210 #if OSL_DEBUG_LEVEL >= 2
211 void DumpShellStack (const ShellStack
& rStack
);
212 void DumpSfxShellStack();
215 /** To be called before a shell is taken from the SFX shell stack. This
216 method deactivates an active text editing to avoid problems with
218 Afterwards the Deactivate() of the shell is called.
220 static void Deactivate (SfxShell
* pShell
);
222 ShellDescriptor
CreateSubShell (
223 SfxShell
const * pShell
,
225 void DestroyViewShell (ShellDescriptor
& rDescriptor
);
226 static void DestroySubShell (const ShellDescriptor
& rDescriptor
);
229 //===== ViewShellManager ======================================================
231 ViewShellManager::ViewShellManager (ViewShellBase
& rBase
)
232 : mpImpl(new Implementation(rBase
)),
237 ViewShellManager::~ViewShellManager()
241 void ViewShellManager::AddSubShellFactory (
242 ViewShell
const * pViewShell
,
243 const SharedShellFactory
& rpFactory
)
246 mpImpl
->AddShellFactory(pViewShell
, rpFactory
);
249 void ViewShellManager::RemoveSubShellFactory (
250 ViewShell
const * pViewShell
,
251 const SharedShellFactory
& rpFactory
)
254 mpImpl
->RemoveShellFactory(pViewShell
, rpFactory
);
257 void ViewShellManager::ActivateViewShell (ViewShell
* pViewShell
)
260 return mpImpl
->ActivateViewShell(pViewShell
);
263 void ViewShellManager::DeactivateViewShell (const ViewShell
* pShell
)
265 if (mbValid
&& pShell
!=nullptr)
266 mpImpl
->DeactivateViewShell(*pShell
);
270 void ViewShellManager::RemoveOverridingMainShell()
273 mpImpl
->RemoveOverridingMainShell();
276 void ViewShellManager::SetOverridingMainShell(const std::shared_ptr
<ViewShell
>& pViewShell
)
279 mpImpl
->SetOverridingShell(pViewShell
);
282 std::shared_ptr
<ViewShell
> ViewShellManager::GetOverridingMainShell()
285 return mpImpl
->GetOverridingShell();
289 void ViewShellManager::SetFormShell (
290 const ViewShell
* pParentShell
,
291 FmFormShell
* pFormShell
,
295 mpImpl
->SetFormShell(pParentShell
,pFormShell
,bAbove
);
298 void ViewShellManager::ActivateSubShell (const ViewShell
& rViewShell
, ShellId nId
)
301 mpImpl
->ActivateSubShell(rViewShell
,nId
);
304 void ViewShellManager::DeactivateSubShell (const ViewShell
& rViewShell
, ShellId nId
)
307 mpImpl
->DeactivateSubShell(rViewShell
,nId
);
310 void ViewShellManager::InvalidateAllSubShells (ViewShell
const * pViewShell
)
313 mpImpl
->InvalidateAllSubShells(pViewShell
);
316 void ViewShellManager::ActivateShell (SfxShell
* pShell
)
318 if (mbValid
&& pShell
!=nullptr)
319 mpImpl
->ActivateShell(*pShell
);
322 void ViewShellManager::ActivateLowPriorityShell (SfxShell
* pShell
)
324 if (mbValid
&& pShell
!=nullptr)
325 mpImpl
->ActivateLowPriorityShell(*pShell
);
328 void ViewShellManager::DeactivateShell (const SfxShell
* pShell
)
330 if (mbValid
&& pShell
!=nullptr)
331 mpImpl
->DeactivateShell(*pShell
);
334 void ViewShellManager::MoveToTop (const ViewShell
& rParentShell
)
337 mpImpl
->MoveToTop(rParentShell
);
340 SfxShell
* ViewShellManager::GetShell (ShellId nId
) const
343 return mpImpl
->GetShell(nId
);
348 SfxShell
* ViewShellManager::GetTopShell() const
351 return mpImpl
->GetTopShell();
356 SfxShell
* ViewShellManager::GetTopViewShell() const
359 return mpImpl
->GetTopViewShell();
364 void ViewShellManager::Shutdown()
373 void ViewShellManager::LockUpdate()
375 mpImpl
->LockUpdate();
378 void ViewShellManager::UnlockUpdate()
380 mpImpl
->UnlockUpdate();
383 //===== ViewShellManager::Implementation ======================================
385 ViewShellManager::Implementation::Implementation (
386 ViewShellBase
& rBase
)
388 mnUpdateLockCount(0),
389 mbShellStackIsUpToDate(true),
390 mpFormShell(nullptr),
391 mpFormShellParent(nullptr),
392 mbFormShellAboveParent(true),
394 mpTopViewShell(nullptr)
397 ViewShellManager::Implementation::~Implementation() COVERITY_NOEXCEPT_FALSE
402 void ViewShellManager::Implementation::AddShellFactory (
403 const SfxShell
* pViewShell
,
404 const SharedShellFactory
& rpFactory
)
406 bool bAlreadyAdded (false);
408 // Check that the given factory has not already been added.
409 ::std::pair
<FactoryList::iterator
,FactoryList::iterator
> aRange(
410 maShellFactories
.equal_range(pViewShell
));
411 for (FactoryList::const_iterator iFactory
=aRange
.first
; iFactory
!=aRange
.second
; ++iFactory
)
412 if (iFactory
->second
== rpFactory
)
414 bAlreadyAdded
= true;
418 // Add the factory if it is not already present.
419 if ( ! bAlreadyAdded
)
420 maShellFactories
.emplace(pViewShell
, rpFactory
);
423 void ViewShellManager::Implementation::RemoveShellFactory (
424 const SfxShell
* pViewShell
,
425 const SharedShellFactory
& rpFactory
)
427 ::std::pair
<FactoryList::iterator
,FactoryList::iterator
> aRange(
428 maShellFactories
.equal_range(pViewShell
));
429 for (FactoryList::iterator iFactory
=aRange
.first
; iFactory
!=aRange
.second
; ++iFactory
)
430 if (iFactory
->second
== rpFactory
)
432 maShellFactories
.erase(iFactory
);
437 void ViewShellManager::Implementation::ActivateViewShell (ViewShell
* pViewShell
)
439 ::osl::MutexGuard
aGuard (maMutex
);
441 ShellDescriptor aResult
;
442 aResult
.mpShell
= pViewShell
;
444 // Register as window listener so that the shells of the current
445 // window can be moved to the top of the shell stack.
446 if (aResult
.mpShell
!= nullptr)
448 vcl::Window
* pWindow
= aResult
.GetWindow();
449 if (pWindow
!= nullptr)
451 pWindow
->AddEventListener(
452 LINK(this, ViewShellManager::Implementation
, WindowEventHandler
));
453 aResult
.mbIsListenerAddedToWindow
= true;
458 "ViewShellManager::ActivateViewShell: "
459 "new view shell has no active window");
463 ActivateShell(aResult
);
466 void ViewShellManager::Implementation::DeactivateViewShell (const ViewShell
& rShell
)
468 ::osl::MutexGuard
aGuard (maMutex
);
470 ActiveShellList::iterator
iShell (::std::find_if (
471 maActiveViewShells
.begin(),
472 maActiveViewShells
.end(),
474 if (iShell
== maActiveViewShells
.end())
477 // iShell points to a ShellDescriptor with mpShell pointing to rShell
479 UpdateLock
aLocker (*this);
481 ShellDescriptor
aDescriptor(*iShell
);
482 mrBase
.GetDocShell()->Disconnect(&rShell
);
483 maActiveViewShells
.erase(iShell
);
484 TakeShellsFromStack(rShell
);
486 // Deactivate sub shells.
487 SubShellList::iterator
iList (maActiveSubShells
.find(&rShell
));
488 if (iList
!= maActiveSubShells
.end())
490 SubShellSubList
& rList (iList
->second
);
491 while ( ! rList
.empty())
492 DeactivateSubShell(rShell
, rList
.front().mnId
);
495 DestroyViewShell(aDescriptor
);
498 void ViewShellManager::Implementation::ActivateShell (SfxShell
& rShell
)
500 ::osl::MutexGuard
aGuard (maMutex
);
502 // Create a new shell or recycle on in the cache.
503 ShellDescriptor aDescriptor
;
504 aDescriptor
.mpShell
= &rShell
;
506 ActivateShell(aDescriptor
);
509 void ViewShellManager::Implementation::ActivateLowPriorityShell (SfxShell
& rShell
)
511 ::osl::MutexGuard
aGuard (maMutex
);
513 // Create a new shell or recycle on in the cache.
514 ShellDescriptor aDescriptor
;
515 aDescriptor
.mpShell
= &rShell
;
517 ActivateLowPriorityShell(aDescriptor
);
520 void ViewShellManager::Implementation::ActivateShell (const ShellDescriptor
& rDescriptor
)
522 // Put shell on top of the active view shells.
523 if (rDescriptor
.mpShell
!= nullptr)
525 maActiveViewShells
.insert( maActiveViewShells
.begin(), rDescriptor
);
529 void ViewShellManager::Implementation::ActivateLowPriorityShell (const ShellDescriptor
& rDescriptor
)
531 // Put shell on bottom of the active view shells.
532 if (rDescriptor
.mpShell
!= nullptr)
534 maActiveViewShells
.push_back( rDescriptor
);
538 void ViewShellManager::Implementation::DeactivateShell (const SfxShell
& rShell
)
540 ::osl::MutexGuard
aGuard (maMutex
);
542 ActiveShellList::iterator
iShell (::std::find_if (
543 maActiveViewShells
.begin(),
544 maActiveViewShells
.end(),
546 if (iShell
== maActiveViewShells
.end())
549 // iShell points to a ShellDescriptor with mpShell pointing to rShell
551 UpdateLock
aLocker (*this);
553 ShellDescriptor
aDescriptor(*iShell
);
554 mrBase
.GetDocShell()->Disconnect(dynamic_cast<const ViewShell
*>(&rShell
));
555 maActiveViewShells
.erase(iShell
);
556 TakeShellsFromStack(rShell
);
558 // Deactivate sub shells.
559 SubShellList::iterator
iList (maActiveSubShells
.find(&rShell
));
560 if (iList
!= maActiveSubShells
.end())
562 SubShellSubList
& rList (iList
->second
);
563 while ( ! rList
.empty())
564 DeactivateSubShell(rShell
, rList
.front().mnId
);
567 DestroyViewShell(aDescriptor
);
570 void ViewShellManager::Implementation::ActivateSubShell (
571 const SfxShell
& rParentShell
,
574 ::osl::MutexGuard
aGuard (maMutex
);
576 // Check that the given view shell is active.
577 if (std::none_of (maActiveViewShells
.begin(), maActiveViewShells
.end(), IsShell(&rParentShell
)))
580 // Create the sub shell list if it does not yet exist.
581 SubShellList::iterator
iList (maActiveSubShells
.find(&rParentShell
));
582 if (iList
== maActiveSubShells
.end())
583 iList
= maActiveSubShells
.emplace(&rParentShell
,SubShellSubList()).first
;
585 // Do not activate an object bar that is already active. Requesting
586 // this is not exactly an error but may be an indication of one.
587 SubShellSubList
& rList (iList
->second
);
588 if (std::any_of(rList
.begin(),rList
.end(), IsId(nId
)))
591 // Add just the id of the sub shell. The actual shell is created
592 // later in CreateShells().
593 UpdateLock
aLock (*this);
594 rList
.emplace_back(nId
);
597 void ViewShellManager::Implementation::DeactivateSubShell (
598 const SfxShell
& rParentShell
,
601 ::osl::MutexGuard
aGuard (maMutex
);
603 // Check that the given view shell is active.
604 SubShellList::iterator
iList (maActiveSubShells
.find(&rParentShell
));
605 if (iList
== maActiveSubShells
.end())
608 // Look up the sub shell.
609 SubShellSubList
& rList (iList
->second
);
610 SubShellSubList::iterator
iShell (
611 ::std::find_if(rList
.begin(),rList
.end(), IsId(nId
)));
612 if (iShell
== rList
.end())
614 SfxShell
* pShell
= iShell
->mpShell
;
615 if (pShell
== nullptr)
618 UpdateLock
aLock (*this);
620 ShellDescriptor
aDescriptor(*iShell
);
621 // Remove the sub shell from both the internal structure as well as the
622 // SFX shell stack above and including the sub shell.
624 TakeShellsFromStack(*pShell
);
626 DestroySubShell(aDescriptor
);
629 std::shared_ptr
<ViewShell
> ViewShellManager::Implementation::GetOverridingShell()
631 return mpOverridingShell
.lock();
634 void ViewShellManager::Implementation::RemoveOverridingMainShell()
636 mpOverridingShell
.reset();
639 void ViewShellManager::Implementation::SetOverridingShell(const std::shared_ptr
<ViewShell
>& pViewShell
)
641 mpOverridingShell
= pViewShell
;
644 void ViewShellManager::Implementation::MoveToTop (const SfxShell
& rShell
)
646 ::osl::MutexGuard
aGuard (maMutex
);
648 // Check that we have access to a dispatcher. If not, then we are
649 // (probably) called while the view shell is still being created or
650 // initialized. Without dispatcher we can not rebuild the shell stack
651 // to move the requested shell to the top. So return right away instead
652 // of making a mess without being able to clean up afterwards.
653 if (mrBase
.GetDispatcher() == nullptr)
656 ActiveShellList::iterator
iShell (::std::find_if (
657 maActiveViewShells
.begin(),
658 maActiveViewShells
.end(),
661 if (iShell
!= maActiveViewShells
.end())
663 // Is the shell already at the top of the stack? We have to keep
664 // the case in mind that mbKeepMainViewShellOnTop is true. Shells
665 // that are not the main view shell are placed on the second-to-top
666 // position in this case.
667 if (iShell
== maActiveViewShells
.begin())
669 // The shell is at the top position and is either a) the main
670 // view shell or b) another shell but the main view shell is not
671 // kept at the top position. We do not have to move the shell.
677 // The shell is not on the stack. Therefore it can not be moved.
678 // We could insert it but we don't. Use ActivateViewShell() for
683 // When the shell is not at the right position it is removed from the
684 // internal list of shells and inserted at the correct position.
687 UpdateLock
aLock (*this);
689 ShellDescriptor
aDescriptor(*iShell
);
691 TakeShellsFromStack(rShell
);
692 maActiveViewShells
.erase(iShell
);
694 maActiveViewShells
.insert(maActiveViewShells
.begin(), aDescriptor
);
698 SfxShell
* ViewShellManager::Implementation::GetShell (ShellId nId
) const
700 ::osl::MutexGuard
aGuard (maMutex
);
702 SfxShell
* pShell
= nullptr;
704 // First search the active view shells.
705 ActiveShellList::const_iterator
iShell (
707 maActiveViewShells
.begin(),
708 maActiveViewShells
.end(),
710 if (iShell
!= maActiveViewShells
.end())
711 pShell
= iShell
->mpShell
;
714 // Now search the active sub shells of every active view shell.
715 for (auto const& activeSubShell
: maActiveSubShells
)
717 const SubShellSubList
& rList (activeSubShell
.second
);
718 SubShellSubList::const_iterator
iSubShell(
719 ::std::find_if(rList
.begin(),rList
.end(), IsId(nId
)));
720 if (iSubShell
!= rList
.end())
722 pShell
= iSubShell
->mpShell
;
731 SfxShell
* ViewShellManager::Implementation::GetTopShell() const
733 OSL_ASSERT(mpTopShell
== mrBase
.GetSubShell(0));
737 SfxShell
* ViewShellManager::Implementation::GetTopViewShell() const
739 return mpTopViewShell
;
742 void ViewShellManager::Implementation::LockUpdate()
747 void ViewShellManager::Implementation::UnlockUpdate()
749 ::osl::MutexGuard
aGuard (maMutex
);
752 if (mnUpdateLockCount
< 0)
754 // This should not happen.
755 OSL_ASSERT (mnUpdateLockCount
>=0);
756 mnUpdateLockCount
= 0;
758 if (mnUpdateLockCount
== 0)
762 /** Update the SFX shell stack (the portion that is visible to us) so that
763 it matches the internal shell stack. This is done in six steps:
764 1. Create the missing view shells and sub shells.
765 2. Set up the internal shell stack.
766 3. Get the SFX shell stack.
767 4. Find the lowest shell in which the two stacks differ.
768 5. Remove all shells above and including that shell from the SFX stack.
769 6. Push all shells of the internal stack on the SFX shell stack that are
770 not already present on the later.
772 void ViewShellManager::Implementation::UpdateShellStack()
774 ::osl::MutexGuard
aGuard (maMutex
);
776 // Remember the undo manager from the top-most shell on the stack.
777 SfxShell
* pTopMostShell
= mrBase
.GetSubShell(0);
778 SfxUndoManager
* pUndoManager
= (pTopMostShell
!=nullptr)
779 ? pTopMostShell
->GetUndoManager()
782 // 1. Create the missing shells.
785 SfxShell
* pPreviousTopViewShell
= mpTopViewShell
;
786 // Update the pointer to the top-most active view shell.
787 mpTopViewShell
= (maActiveViewShells
.empty() || mbFormShellAboveParent
)
788 ? nullptr : maActiveViewShells
.begin()->mpShell
;
790 bool bTopViewShellChanged
= mpTopViewShell
!= pPreviousTopViewShell
;
792 // 2. Create the internal target stack.
793 ShellStack aTargetStack
;
794 CreateTargetStack(aTargetStack
);
796 // 3. Get SFX shell stack.
797 ShellStack aSfxShellStack
;
798 sal_uInt16
nIndex (0);
799 while (mrBase
.GetSubShell(nIndex
)!=nullptr)
801 aSfxShellStack
.reserve(nIndex
);
805 aSfxShellStack
.push_back(mrBase
.GetSubShell(nIndex
));
808 #if OSL_DEBUG_LEVEL >= 2
809 SAL_INFO("sd.view", __func__
<< ": Current SFX Stack");
810 DumpShellStack(aSfxShellStack
);
811 SAL_INFO("sd.view", __func__
<< ": Target Stack");
812 DumpShellStack(aTargetStack
);
815 // 4. Find the lowest shell in which the two stacks differ.
816 auto mismatchIters
= std::mismatch(aSfxShellStack
.begin(), aSfxShellStack
.end(),
817 aTargetStack
.begin(), aTargetStack
.end());
818 ShellStack::iterator
iSfxShell (mismatchIters
.first
);
819 ShellStack::iterator
iTargetShell (mismatchIters
.second
);
821 // 5. Remove all shells above and including the differing shell from the
822 // SFX stack starting with the shell on top of the stack.
823 for (std::reverse_iterator
<ShellStack::const_iterator
> i(aSfxShellStack
.end()), iLast(iSfxShell
);
826 SfxShell
* const pShell
= *i
;
827 SAL_INFO("sd.view", __func__
<< ": removing shell " << pShell
<< " from stack");
828 mrBase
.RemoveSubShell(pShell
);
830 aSfxShellStack
.erase(iSfxShell
, aSfxShellStack
.end());
832 // 6. Push shells from the given stack onto the SFX stack.
833 mbShellStackIsUpToDate
= false;
834 while (iTargetShell
!= aTargetStack
.end())
836 SAL_INFO("sd.view", __func__
<< ": pushing shell " << *iTargetShell
<< " on stack");
837 mrBase
.AddSubShell(**iTargetShell
);
840 // The pushing of the shell on to the shell stack may have lead to
841 // another invocation of this method. In this case we have to abort
842 // pushing shells on the stack and return immediately.
843 if (mbShellStackIsUpToDate
)
846 if (mrBase
.GetDispatcher() != nullptr)
847 mrBase
.GetDispatcher()->Flush();
849 // Update the pointer to the top-most shell and set its undo manager
850 // to the one of the previous top-most shell.
851 mpTopShell
= mrBase
.GetSubShell(0);
852 if (mpTopShell
!=nullptr && pUndoManager
!=nullptr && mpTopShell
->GetUndoManager()==nullptr)
853 mpTopShell
->SetUndoManager(pUndoManager
);
855 // Only broadcast context for activation on the top-most ViewShell
856 if (mpTopViewShell
&& bTopViewShellChanged
)
857 mpTopViewShell
->BroadcastContextForActivation(true);
859 // Finally tell an invocation of this method on a higher level that it can (has
860 // to) abort and return immediately.
861 mbShellStackIsUpToDate
= true;
863 #if OSL_DEBUG_LEVEL >= 2
864 SAL_INFO("sd.view", __func__
<< ": New current stack");
869 void ViewShellManager::Implementation::TakeShellsFromStack (const SfxShell
& rShell
)
871 ::osl::MutexGuard
aGuard (maMutex
);
873 // Remember the undo manager from the top-most shell on the stack.
874 SfxShell
* pTopMostShell
= mrBase
.GetSubShell(0);
875 SfxUndoManager
* pUndoManager
= (pTopMostShell
!=nullptr)
876 ? pTopMostShell
->GetUndoManager()
879 #if OSL_DEBUG_LEVEL >= 2
880 SAL_INFO("sd.view", __func__
<< "TakeShellsFromStack( " << pShell
<< ")");
884 // 0.Make sure that the given shell is on the stack. This is a
885 // preparation for the following assertion.
886 for (sal_uInt16 nIndex
=0; true; nIndex
++)
888 SfxShell
* pShellOnStack
= mrBase
.GetSubShell(nIndex
);
889 if (pShellOnStack
== nullptr)
891 // the shell is not on the stack.
894 else if (pShellOnStack
== &rShell
)
898 // 1. Deactivate our shells on the stack before they are removed so
899 // that during the Deactivation() calls the stack is still intact.
900 for (sal_uInt16 nIndex
=0; true; nIndex
++)
902 SfxShell
* pShellOnStack
= mrBase
.GetSubShell(nIndex
);
903 Deactivate(pShellOnStack
);
904 if (pShellOnStack
== &rShell
)
908 // 2. Remove the shells from the stack.
911 SfxShell
* pShellOnStack
= mrBase
.GetSubShell(0);
912 SAL_INFO("sd.view", __func__
<< "removing shell " << pShellOnStack
<< " from stack");
913 mrBase
.RemoveSubShell(pShellOnStack
);
914 if (pShellOnStack
== &rShell
)
918 // 3. Update the stack.
919 if (mrBase
.GetDispatcher() != nullptr)
920 mrBase
.GetDispatcher()->Flush();
922 // Update the pointer to the top-most shell and set its undo manager
923 // to the one of the previous top-most shell.
924 mpTopShell
= mrBase
.GetSubShell(0);
925 if (mpTopShell
!=nullptr && pUndoManager
!=nullptr && mpTopShell
->GetUndoManager()==nullptr)
926 mpTopShell
->SetUndoManager(pUndoManager
);
928 #if OSL_DEBUG_LEVEL >= 2
929 SAL_INFO("sd.view", __func__
<< "Sfx shell stack is:");
934 void ViewShellManager::Implementation::CreateShells()
936 ::osl::MutexGuard
aGuard (maMutex
);
938 // Iterate over all view shells.
939 ActiveShellList::reverse_iterator iShell
;
940 for (iShell
=maActiveViewShells
.rbegin(); iShell
!=maActiveViewShells
.rend(); ++iShell
)
942 // Get the list of associated sub shells.
943 SubShellList::iterator
iList (maActiveSubShells
.find(iShell
->mpShell
));
944 if (iList
!= maActiveSubShells
.end())
946 SubShellSubList
& rList (iList
->second
);
948 // Iterate over all sub shells of the current view shell.
949 for (auto & subShell
: rList
)
951 if (subShell
.mpShell
== nullptr)
953 subShell
= CreateSubShell(iShell
->mpShell
,subShell
.mnId
);
960 void ViewShellManager::Implementation::CreateTargetStack (ShellStack
& rStack
) const
962 // Create a local stack of the shells that are to push on the shell
963 // stack. We can thus safely create the required shells while still
964 // having a valid shell stack.
965 for (ActiveShellList::const_reverse_iterator
iViewShell (maActiveViewShells
.rbegin());
966 iViewShell
!= maActiveViewShells
.rend();
969 // Possibly place the form shell below the current view shell.
970 if ( ! mbFormShellAboveParent
971 && mpFormShell
!=nullptr
972 && iViewShell
->mpShell
==mpFormShellParent
)
974 rStack
.push_back(mpFormShell
);
977 // Put the view shell itself on the local stack.
978 rStack
.push_back (iViewShell
->mpShell
);
980 // Possibly place the form shell above the current view shell.
981 if (mbFormShellAboveParent
982 && mpFormShell
!=nullptr
983 && iViewShell
->mpShell
==mpFormShellParent
)
985 rStack
.push_back(mpFormShell
);
988 // Add all other sub shells.
989 SubShellList::const_iterator
iList (maActiveSubShells
.find(iViewShell
->mpShell
));
990 if (iList
!= maActiveSubShells
.end())
992 const SubShellSubList
& rList (iList
->second
);
993 SubShellSubList::const_reverse_iterator iSubShell
;
994 for (iSubShell
=rList
.rbegin(); iSubShell
!=rList
.rend(); ++iSubShell
)
995 if (iSubShell
->mpShell
!= mpFormShell
)
996 rStack
.push_back(iSubShell
->mpShell
);
1001 IMPL_LINK(ViewShellManager::Implementation
, WindowEventHandler
, VclWindowEvent
&, rEvent
, void)
1003 vcl::Window
* pEventWindow
= rEvent
.GetWindow();
1005 switch (rEvent
.GetId())
1007 case VclEventId::WindowGetFocus
:
1009 for (auto const& activeShell
: maActiveViewShells
)
1011 if (pEventWindow
== activeShell
.GetWindow())
1013 MoveToTop(*activeShell
.mpShell
);
1020 case VclEventId::WindowLoseFocus
:
1023 case VclEventId::ObjectDying
:
1024 // Remember that we do not have to remove the window
1025 // listener for this window.
1026 for (auto & activeViewShell
: maActiveViewShells
)
1028 if (activeViewShell
.GetWindow() == pEventWindow
)
1030 activeViewShell
.mbIsListenerAddedToWindow
= false;
1040 ShellDescriptor
ViewShellManager::Implementation::CreateSubShell (
1041 SfxShell
const * pParentShell
,
1044 ::osl::MutexGuard
aGuard (maMutex
);
1045 ShellDescriptor aResult
;
1047 // Look up the factories for the parent shell.
1048 ::std::pair
<FactoryList::iterator
,FactoryList::iterator
> aRange(
1049 maShellFactories
.equal_range(pParentShell
));
1051 // Try all factories to create the shell.
1052 for (FactoryList::const_iterator iFactory
=aRange
.first
; iFactory
!=aRange
.second
; ++iFactory
)
1054 SharedShellFactory pFactory
= iFactory
->second
;
1055 if (pFactory
!= nullptr)
1056 aResult
.mpShell
= pFactory
->CreateShell(nShellId
);
1058 // Exit the loop when the shell has been successfully created.
1059 if (aResult
.mpShell
!= nullptr)
1061 aResult
.mpFactory
= std::move(pFactory
);
1062 aResult
.mnId
= nShellId
;
1070 void ViewShellManager::Implementation::DestroyViewShell (
1071 ShellDescriptor
& rDescriptor
)
1073 OSL_ASSERT(rDescriptor
.mpShell
!= nullptr);
1075 if (rDescriptor
.mbIsListenerAddedToWindow
)
1077 rDescriptor
.mbIsListenerAddedToWindow
= false;
1078 vcl::Window
* pWindow
= rDescriptor
.GetWindow();
1079 if (pWindow
!= nullptr)
1081 pWindow
->RemoveEventListener(
1082 LINK(this, ViewShellManager::Implementation
, WindowEventHandler
));
1086 // Destroy the sub shell factories.
1087 ::std::pair
<FactoryList::iterator
,FactoryList::iterator
> aRange(
1088 maShellFactories
.equal_range(rDescriptor
.mpShell
));
1089 if (aRange
.first
!= maShellFactories
.end())
1090 maShellFactories
.erase(aRange
.first
, aRange
.second
);
1092 // Release the shell.
1093 if (rDescriptor
.mpFactory
)
1094 rDescriptor
.mpFactory
->ReleaseShell(rDescriptor
.mpShell
);
1097 void ViewShellManager::Implementation::DestroySubShell (
1098 const ShellDescriptor
& rDescriptor
)
1100 OSL_ASSERT(rDescriptor
.mpFactory
);
1101 rDescriptor
.mpFactory
->ReleaseShell(rDescriptor
.mpShell
);
1104 void ViewShellManager::Implementation::InvalidateAllSubShells (const SfxShell
* pParentShell
)
1106 ::osl::MutexGuard
aGuard (maMutex
);
1108 SubShellList::iterator
iList (maActiveSubShells
.find(pParentShell
));
1109 if (iList
!= maActiveSubShells
.end())
1111 SubShellSubList
& rList (iList
->second
);
1112 for (auto const& shell
: rList
)
1113 if (shell
.mpShell
!= nullptr)
1114 shell
.mpShell
->Invalidate();
1118 void ViewShellManager::Implementation::Shutdown()
1120 ::osl::MutexGuard
aGuard (maMutex
);
1122 // Take stacked shells from stack.
1123 if ( ! maActiveViewShells
.empty())
1125 UpdateLock
aLock (*this);
1127 while ( ! maActiveViewShells
.empty())
1129 SfxShell
* pShell
= maActiveViewShells
.front().mpShell
;
1130 if (pShell
!= nullptr)
1132 ViewShell
* pViewShell
= dynamic_cast<ViewShell
*>(pShell
);
1133 if (pViewShell
!= nullptr)
1134 DeactivateViewShell(*pViewShell
);
1136 DeactivateShell(*pShell
);
1141 "ViewShellManager::Implementation::Shutdown(): empty active shell descriptor");
1142 maActiveViewShells
.pop_front();
1146 mrBase
.RemoveSubShell ();
1148 maShellFactories
.clear();
1149 mpOverridingShell
.reset();
1152 #if OSL_DEBUG_LEVEL >= 2
1153 void ViewShellManager::Implementation::DumpShellStack (const ShellStack
& rStack
)
1155 ShellStack::const_reverse_iterator iEntry
;
1156 for (iEntry
=rStack
.rbegin(); iEntry
!=rStack
.rend(); ++iEntry
)
1157 if (*iEntry
!= NULL
)
1158 SAL_INFO("sd.view", __func__
<< ": " <<
1160 (*iEntry
)->GetName());
1162 SAL_INFO("sd.view", __func__
<< " null");
1165 void ViewShellManager::Implementation::DumpSfxShellStack()
1167 ShellStack aSfxShellStack
;
1168 sal_uInt16
nIndex (0);
1169 while (mrBase
.GetSubShell(nIndex
)!=NULL
)
1171 aSfxShellStack
.reserve(nIndex
);
1172 while (nIndex
-- > 0)
1173 aSfxShellStack
.push_back(mrBase
.GetSubShell(nIndex
));
1174 DumpShellStack(aSfxShellStack
);
1178 void ViewShellManager::Implementation::Deactivate (SfxShell
* pShell
)
1180 OSL_ASSERT(pShell
!=nullptr);
1182 // We have to end a text edit for view shells that are to be taken from
1184 ViewShell
* pViewShell
= dynamic_cast<ViewShell
*>(pShell
);
1185 if (pViewShell
!= nullptr)
1187 sd::View
* pView
= pViewShell
->GetView();
1188 if (pView
!=nullptr && pView
->IsTextEdit())
1190 pView
->SdrEndTextEdit();
1193 // dispatch synchronously, otherwise it might execute while another
1194 // ViewShell is active!
1195 pViewShell
->GetViewFrame()->GetDispatcher()->Execute(
1197 SfxCallMode::SYNCHRON
);
1201 // Now we can deactivate the shell.
1202 pShell
->Deactivate(true);
1205 void ViewShellManager::Implementation::SetFormShell (
1206 const ViewShell
* pFormShellParent
,
1207 FmFormShell
* pFormShell
,
1208 bool bFormShellAboveParent
)
1210 ::osl::MutexGuard
aGuard (maMutex
);
1212 mpFormShellParent
= pFormShellParent
;
1213 mpFormShell
= pFormShell
;
1214 mbFormShellAboveParent
= bFormShellAboveParent
;
1219 ShellDescriptor::ShellDescriptor()
1221 mnId(ToolbarId::None
),
1222 mbIsListenerAddedToWindow(false)
1226 ShellDescriptor::ShellDescriptor (
1230 mbIsListenerAddedToWindow(false)
1234 vcl::Window
* ShellDescriptor::GetWindow() const
1236 ViewShell
* pViewShell
= dynamic_cast<ViewShell
*>(mpShell
);
1237 if (pViewShell
!= nullptr)
1238 return pViewShell
->GetActiveWindow();
1243 } // end of anonymous namespace
1245 } // end of namespace sd
1247 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */