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 DeactivateShell (const SfxShell
& rShell
);
112 void ActivateShell (const ShellDescriptor
& rDescriptor
);
113 void SetFormShell (const ViewShell
* pViewShell
, FmFormShell
* pFormShell
, bool bAbove
);
114 void ActivateSubShell (const SfxShell
& rParentShell
, ShellId nId
);
115 void DeactivateSubShell (const SfxShell
& rParentShell
, ShellId nId
);
116 void MoveToTop (const SfxShell
& rParentShell
);
117 SfxShell
* GetShell (ShellId nId
) const;
118 SfxShell
* GetTopShell() const;
119 SfxShell
* GetTopViewShell() const;
121 void InvalidateAllSubShells (const SfxShell
* pParentShell
);
123 /** Remove all shells from the SFX stack above and including the given
126 void TakeShellsFromStack (const SfxShell
* pShell
);
131 explicit UpdateLock (Implementation
& rImpl
) : mrImpl(rImpl
) {mrImpl
.LockUpdate();}
132 ~UpdateLock() COVERITY_NOEXCEPT_FALSE
{mrImpl
.UnlockUpdate();}
134 Implementation
& mrImpl
;
137 /** Prevent updates of the shell stack. While the sub shell manager is
138 locked it will update its internal data structures but not alter the
139 shell stack. Use this method when there are several modifications
140 to the shell stack to prevent multiple rebuilds of the shell stack
141 and resulting broadcasts.
145 /** Allow updates of the shell stack. This method has to be called the
146 same number of times as LockUpdate() to really allow a rebuild of
152 ViewShellBase
& mrBase
;
153 mutable ::osl::Mutex maMutex
;
155 class ShellHash
{ public: size_t operator()(const SfxShell
* p
) const { return reinterpret_cast<size_t>(p
);} };
156 typedef std::unordered_multimap
<const SfxShell
*,SharedShellFactory
,ShellHash
>
158 FactoryList maShellFactories
;
160 /** List of the active view shells. In order to create gather all shells
161 to put on the shell stack each view shell in this list is asked for
162 its sub-shells (typically toolbars).
164 typedef std::list
<ShellDescriptor
> ActiveShellList
;
165 ActiveShellList maActiveViewShells
;
167 typedef std::list
<ShellDescriptor
> SubShellSubList
;
168 typedef std::unordered_map
<const SfxShell
*,SubShellSubList
,ShellHash
> SubShellList
;
169 SubShellList maActiveSubShells
;
171 /** In this member we remember what shells we have pushed on the shell
174 typedef ::std::vector
<SfxShell
*> ShellStack
;
176 int mnUpdateLockCount
;
178 /** The UpdateShellStack() method can be called recursively. This flag
179 is used to communicate between different levels of invocation: if
180 the stack has been updated in an inner call the outer call can (has
181 to) stop and return immediately.
183 bool mbShellStackIsUpToDate
;
185 SfxShell
* mpFormShell
;
186 const ViewShell
* mpFormShellParent
;
187 bool mbFormShellAboveParent
;
189 SfxShell
* mpTopShell
;
190 SfxShell
* mpTopViewShell
;
193 void UpdateShellStack();
197 /** This method rebuilds the stack of shells that are stacked upon the
200 void CreateTargetStack (ShellStack
& rStack
) const;
202 DECL_LINK(WindowEventHandler
, VclWindowEvent
&, void);
204 #if OSL_DEBUG_LEVEL >= 2
205 void DumpShellStack (const ShellStack
& rStack
);
206 void DumpSfxShellStack();
209 /** To be called before a shell is taken from the SFX shell stack. This
210 method deactivates an active text editing to avoid problems with
212 Afterwards the Deactivate() of the shell is called.
214 static void Deactivate (SfxShell
* pShell
);
216 ShellDescriptor
CreateSubShell (
217 SfxShell
const * pShell
,
219 void DestroyViewShell (ShellDescriptor
& rDescriptor
);
220 static void DestroySubShell (const ShellDescriptor
& rDescriptor
);
223 //===== ViewShellManager ======================================================
225 ViewShellManager::ViewShellManager (ViewShellBase
& rBase
)
226 : mpImpl(new Implementation(rBase
)),
231 ViewShellManager::~ViewShellManager()
235 void ViewShellManager::AddSubShellFactory (
236 ViewShell
const * pViewShell
,
237 const SharedShellFactory
& rpFactory
)
240 mpImpl
->AddShellFactory(pViewShell
, rpFactory
);
243 void ViewShellManager::RemoveSubShellFactory (
244 ViewShell
const * pViewShell
,
245 const SharedShellFactory
& rpFactory
)
248 mpImpl
->RemoveShellFactory(pViewShell
, rpFactory
);
251 void ViewShellManager::ActivateViewShell (ViewShell
* pViewShell
)
254 return mpImpl
->ActivateViewShell(pViewShell
);
257 void ViewShellManager::DeactivateViewShell (const ViewShell
* pShell
)
259 if (mbValid
&& pShell
!=nullptr)
260 mpImpl
->DeactivateViewShell(*pShell
);
263 void ViewShellManager::SetFormShell (
264 const ViewShell
* pParentShell
,
265 FmFormShell
* pFormShell
,
269 mpImpl
->SetFormShell(pParentShell
,pFormShell
,bAbove
);
272 void ViewShellManager::ActivateSubShell (const ViewShell
& rViewShell
, ShellId nId
)
275 mpImpl
->ActivateSubShell(rViewShell
,nId
);
278 void ViewShellManager::DeactivateSubShell (const ViewShell
& rViewShell
, ShellId nId
)
281 mpImpl
->DeactivateSubShell(rViewShell
,nId
);
284 void ViewShellManager::InvalidateAllSubShells (ViewShell
const * pViewShell
)
287 mpImpl
->InvalidateAllSubShells(pViewShell
);
290 void ViewShellManager::ActivateShell (SfxShell
* pShell
)
292 if (mbValid
&& pShell
!=nullptr)
293 mpImpl
->ActivateShell(*pShell
);
296 void ViewShellManager::DeactivateShell (const SfxShell
* pShell
)
298 if (mbValid
&& pShell
!=nullptr)
299 mpImpl
->DeactivateShell(*pShell
);
302 void ViewShellManager::MoveToTop (const ViewShell
& rParentShell
)
305 mpImpl
->MoveToTop(rParentShell
);
308 SfxShell
* ViewShellManager::GetShell (ShellId nId
) const
311 return mpImpl
->GetShell(nId
);
316 SfxShell
* ViewShellManager::GetTopShell() const
319 return mpImpl
->GetTopShell();
324 SfxShell
* ViewShellManager::GetTopViewShell() const
327 return mpImpl
->GetTopViewShell();
332 void ViewShellManager::Shutdown()
341 void ViewShellManager::LockUpdate()
343 mpImpl
->LockUpdate();
346 void ViewShellManager::UnlockUpdate()
348 mpImpl
->UnlockUpdate();
351 //===== ViewShellManager::Implementation ======================================
353 ViewShellManager::Implementation::Implementation (
354 ViewShellBase
& rBase
)
356 mnUpdateLockCount(0),
357 mbShellStackIsUpToDate(true),
358 mpFormShell(nullptr),
359 mpFormShellParent(nullptr),
360 mbFormShellAboveParent(true),
362 mpTopViewShell(nullptr)
365 ViewShellManager::Implementation::~Implementation() COVERITY_NOEXCEPT_FALSE
370 void ViewShellManager::Implementation::AddShellFactory (
371 const SfxShell
* pViewShell
,
372 const SharedShellFactory
& rpFactory
)
374 bool bAlreadyAdded (false);
376 // Check that the given factory has not already been added.
377 ::std::pair
<FactoryList::iterator
,FactoryList::iterator
> aRange(
378 maShellFactories
.equal_range(pViewShell
));
379 for (FactoryList::const_iterator iFactory
=aRange
.first
; iFactory
!=aRange
.second
; ++iFactory
)
380 if (iFactory
->second
== rpFactory
)
382 bAlreadyAdded
= true;
386 // Add the factory if it is not already present.
387 if ( ! bAlreadyAdded
)
388 maShellFactories
.emplace(pViewShell
, rpFactory
);
391 void ViewShellManager::Implementation::RemoveShellFactory (
392 const SfxShell
* pViewShell
,
393 const SharedShellFactory
& rpFactory
)
395 ::std::pair
<FactoryList::iterator
,FactoryList::iterator
> aRange(
396 maShellFactories
.equal_range(pViewShell
));
397 for (FactoryList::iterator iFactory
=aRange
.first
; iFactory
!=aRange
.second
; ++iFactory
)
398 if (iFactory
->second
== rpFactory
)
400 maShellFactories
.erase(iFactory
);
405 void ViewShellManager::Implementation::ActivateViewShell (ViewShell
* pViewShell
)
407 ::osl::MutexGuard
aGuard (maMutex
);
409 ShellDescriptor aResult
;
410 aResult
.mpShell
= pViewShell
;
412 // Register as window listener so that the shells of the current
413 // window can be moved to the top of the shell stack.
414 if (aResult
.mpShell
!= nullptr)
416 vcl::Window
* pWindow
= aResult
.GetWindow();
417 if (pWindow
!= nullptr)
419 pWindow
->AddEventListener(
420 LINK(this, ViewShellManager::Implementation
, WindowEventHandler
));
421 aResult
.mbIsListenerAddedToWindow
= true;
426 "ViewShellManager::ActivateViewShell: "
427 "new view shell has no active window");
431 ActivateShell(aResult
);
434 void ViewShellManager::Implementation::DeactivateViewShell (const ViewShell
& rShell
)
436 ::osl::MutexGuard
aGuard (maMutex
);
438 ActiveShellList::iterator
iShell (::std::find_if (
439 maActiveViewShells
.begin(),
440 maActiveViewShells
.end(),
442 if (iShell
== maActiveViewShells
.end())
445 UpdateLock
aLocker (*this);
447 ShellDescriptor
aDescriptor(*iShell
);
448 mrBase
.GetDocShell()->Disconnect(dynamic_cast<ViewShell
*>(aDescriptor
.mpShell
));
449 maActiveViewShells
.erase(iShell
);
450 TakeShellsFromStack(aDescriptor
.mpShell
);
452 // Deactivate sub shells.
453 SubShellList::iterator
iList (maActiveSubShells
.find(&rShell
));
454 if (iList
!= maActiveSubShells
.end())
456 SubShellSubList
& rList (iList
->second
);
457 while ( ! rList
.empty())
458 DeactivateSubShell(rShell
, rList
.front().mnId
);
461 DestroyViewShell(aDescriptor
);
464 void ViewShellManager::Implementation::ActivateShell (SfxShell
& rShell
)
466 ::osl::MutexGuard
aGuard (maMutex
);
468 // Create a new shell or recycle on in the cache.
469 ShellDescriptor aDescriptor
;
470 aDescriptor
.mpShell
= &rShell
;
472 ActivateShell(aDescriptor
);
475 void ViewShellManager::Implementation::ActivateShell (const ShellDescriptor
& rDescriptor
)
477 // Put shell on top of the active view shells.
478 if (rDescriptor
.mpShell
!= nullptr)
480 maActiveViewShells
.insert( maActiveViewShells
.begin(), rDescriptor
);
484 void ViewShellManager::Implementation::DeactivateShell (const SfxShell
& rShell
)
486 ::osl::MutexGuard
aGuard (maMutex
);
488 ActiveShellList::iterator
iShell (::std::find_if (
489 maActiveViewShells
.begin(),
490 maActiveViewShells
.end(),
492 if (iShell
== maActiveViewShells
.end())
495 UpdateLock
aLocker (*this);
497 ShellDescriptor
aDescriptor(*iShell
);
498 mrBase
.GetDocShell()->Disconnect(dynamic_cast<ViewShell
*>(aDescriptor
.mpShell
));
499 maActiveViewShells
.erase(iShell
);
500 TakeShellsFromStack(aDescriptor
.mpShell
);
502 // Deactivate sub shells.
503 SubShellList::iterator
iList (maActiveSubShells
.find(&rShell
));
504 if (iList
!= maActiveSubShells
.end())
506 SubShellSubList
& rList (iList
->second
);
507 while ( ! rList
.empty())
508 DeactivateSubShell(rShell
, rList
.front().mnId
);
511 DestroyViewShell(aDescriptor
);
514 void ViewShellManager::Implementation::ActivateSubShell (
515 const SfxShell
& rParentShell
,
518 ::osl::MutexGuard
aGuard (maMutex
);
520 // Check that the given view shell is active.
521 if (std::none_of (maActiveViewShells
.begin(), maActiveViewShells
.end(), IsShell(&rParentShell
)))
524 // Create the sub shell list if it does not yet exist.
525 SubShellList::iterator
iList (maActiveSubShells
.find(&rParentShell
));
526 if (iList
== maActiveSubShells
.end())
527 iList
= maActiveSubShells
.emplace(&rParentShell
,SubShellSubList()).first
;
529 // Do not activate an object bar that is already active. Requesting
530 // this is not exactly an error but may be an indication of one.
531 SubShellSubList
& rList (iList
->second
);
532 if (std::any_of(rList
.begin(),rList
.end(), IsId(nId
)))
535 // Add just the id of the sub shell. The actual shell is created
536 // later in CreateShells().
537 UpdateLock
aLock (*this);
538 rList
.emplace_back(nId
);
541 void ViewShellManager::Implementation::DeactivateSubShell (
542 const SfxShell
& rParentShell
,
545 ::osl::MutexGuard
aGuard (maMutex
);
547 // Check that the given view shell is active.
548 SubShellList::iterator
iList (maActiveSubShells
.find(&rParentShell
));
549 if (iList
== maActiveSubShells
.end())
552 // Look up the sub shell.
553 SubShellSubList
& rList (iList
->second
);
554 SubShellSubList::iterator
iShell (
555 ::std::find_if(rList
.begin(),rList
.end(), IsId(nId
)));
556 if (iShell
== rList
.end())
558 SfxShell
* pShell
= iShell
->mpShell
;
559 if (pShell
== nullptr)
562 UpdateLock
aLock (*this);
564 ShellDescriptor
aDescriptor(*iShell
);
565 // Remove the sub shell from both the internal structure as well as the
566 // SFX shell stack above and including the sub shell.
568 TakeShellsFromStack(pShell
);
570 DestroySubShell(aDescriptor
);
573 void ViewShellManager::Implementation::MoveToTop (const SfxShell
& rShell
)
575 ::osl::MutexGuard
aGuard (maMutex
);
577 // Check that we have access to a dispatcher. If not, then we are
578 // (probably) called while the view shell is still being created or
579 // initialized. Without dispatcher we can not rebuild the shell stack
580 // to move the requested shell to the top. So return right away instead
581 // of making a mess without being able to clean up afterwards.
582 if (mrBase
.GetDispatcher() == nullptr)
585 ActiveShellList::iterator
iShell (::std::find_if (
586 maActiveViewShells
.begin(),
587 maActiveViewShells
.end(),
590 if (iShell
!= maActiveViewShells
.end())
592 // Is the shell already at the top of the stack? We have to keep
593 // the case in mind that mbKeepMainViewShellOnTop is true. Shells
594 // that are not the main view shell are placed on the second-to-top
595 // position in this case.
596 if (iShell
== maActiveViewShells
.begin())
598 // The shell is at the top position and is either a) the main
599 // view shell or b) another shell but the main view shell is not
600 // kept at the top position. We do not have to move the shell.
606 // The shell is not on the stack. Therefore it can not be moved.
607 // We could insert it but we don't. Use ActivateViewShell() for
612 // When the shell is not at the right position it is removed from the
613 // internal list of shells and inserted at the correct position.
616 UpdateLock
aLock (*this);
618 ShellDescriptor
aDescriptor(*iShell
);
620 TakeShellsFromStack(&rShell
);
621 maActiveViewShells
.erase(iShell
);
623 maActiveViewShells
.insert(maActiveViewShells
.begin(), aDescriptor
);
627 SfxShell
* ViewShellManager::Implementation::GetShell (ShellId nId
) const
629 ::osl::MutexGuard
aGuard (maMutex
);
631 SfxShell
* pShell
= nullptr;
633 // First search the active view shells.
634 ActiveShellList::const_iterator
iShell (
636 maActiveViewShells
.begin(),
637 maActiveViewShells
.end(),
639 if (iShell
!= maActiveViewShells
.end())
640 pShell
= iShell
->mpShell
;
643 // Now search the active sub shells of every active view shell.
644 for (auto const& activeSubShell
: maActiveSubShells
)
646 const SubShellSubList
& rList (activeSubShell
.second
);
647 SubShellSubList::const_iterator
iSubShell(
648 ::std::find_if(rList
.begin(),rList
.end(), IsId(nId
)));
649 if (iSubShell
!= rList
.end())
651 pShell
= iSubShell
->mpShell
;
660 SfxShell
* ViewShellManager::Implementation::GetTopShell() const
662 OSL_ASSERT(mpTopShell
== mrBase
.GetSubShell(0));
666 SfxShell
* ViewShellManager::Implementation::GetTopViewShell() const
668 return mpTopViewShell
;
671 void ViewShellManager::Implementation::LockUpdate()
676 void ViewShellManager::Implementation::UnlockUpdate()
678 ::osl::MutexGuard
aGuard (maMutex
);
681 if (mnUpdateLockCount
< 0)
683 // This should not happen.
684 OSL_ASSERT (mnUpdateLockCount
>=0);
685 mnUpdateLockCount
= 0;
687 if (mnUpdateLockCount
== 0)
691 /** Update the SFX shell stack (the portion that is visible to us) so that
692 it matches the internal shell stack. This is done in six steps:
693 1. Create the missing view shells and sub shells.
694 2. Set up the internal shell stack.
695 3. Get the SFX shell stack.
696 4. Find the lowest shell in which the two stacks differ.
697 5. Remove all shells above and including that shell from the SFX stack.
698 6. Push all shells of the internal stack on the SFX shell stack that are
699 not already present on the later.
701 void ViewShellManager::Implementation::UpdateShellStack()
703 ::osl::MutexGuard
aGuard (maMutex
);
705 // Remember the undo manager from the top-most shell on the stack.
706 SfxShell
* pTopMostShell
= mrBase
.GetSubShell(0);
707 SfxUndoManager
* pUndoManager
= (pTopMostShell
!=nullptr)
708 ? pTopMostShell
->GetUndoManager()
711 // 1. Create the missing shells.
714 // Update the pointer to the top-most active view shell.
715 mpTopViewShell
= (maActiveViewShells
.empty())
716 ? nullptr : maActiveViewShells
.begin()->mpShell
;
719 // 2. Create the internal target stack.
720 ShellStack aTargetStack
;
721 CreateTargetStack(aTargetStack
);
723 // 3. Get SFX shell stack.
724 ShellStack aSfxShellStack
;
725 sal_uInt16
nIndex (0);
726 while (mrBase
.GetSubShell(nIndex
)!=nullptr)
728 aSfxShellStack
.reserve(nIndex
);
730 aSfxShellStack
.push_back(mrBase
.GetSubShell(nIndex
));
732 #if OSL_DEBUG_LEVEL >= 2
733 SAL_INFO("sd.view", __func__
<< ": Current SFX Stack");
734 DumpShellStack(aSfxShellStack
);
735 SAL_INFO("sd.view", __func__
<< ": Target Stack");
736 DumpShellStack(aTargetStack
);
739 // 4. Find the lowest shell in which the two stacks differ.
740 auto mismatchIters
= std::mismatch(aSfxShellStack
.begin(), aSfxShellStack
.end(),
741 aTargetStack
.begin(), aTargetStack
.end());
742 ShellStack::iterator
iSfxShell (mismatchIters
.first
);
743 ShellStack::iterator
iTargetShell (mismatchIters
.second
);
745 // 5. Remove all shells above and including the differing shell from the
746 // SFX stack starting with the shell on top of the stack.
747 for (std::reverse_iterator
<ShellStack::const_iterator
> i(aSfxShellStack
.end()), iLast(iSfxShell
);
750 SfxShell
* const pShell
= *i
;
751 SAL_INFO("sd.view", __func__
<< ": removing shell " << pShell
<< " from stack");
752 mrBase
.RemoveSubShell(pShell
);
754 aSfxShellStack
.erase(iSfxShell
, aSfxShellStack
.end());
756 // 6. Push shells from the given stack onto the SFX stack.
757 mbShellStackIsUpToDate
= false;
758 while (iTargetShell
!= aTargetStack
.end())
760 SAL_INFO("sd.view", __func__
<< ": pushing shell " << *iTargetShell
<< " on stack");
761 mrBase
.AddSubShell(**iTargetShell
);
764 // The pushing of the shell on to the shell stack may have lead to
765 // another invocation of this method. In this case we have to abort
766 // pushing shells on the stack and return immediately.
767 if (mbShellStackIsUpToDate
)
770 if (mrBase
.GetDispatcher() != nullptr)
771 mrBase
.GetDispatcher()->Flush();
773 // Update the pointer to the top-most shell and set its undo manager
774 // to the one of the previous top-most shell.
775 mpTopShell
= mrBase
.GetSubShell(0);
776 if (mpTopShell
!=nullptr && pUndoManager
!=nullptr && mpTopShell
->GetUndoManager()==nullptr)
777 mpTopShell
->SetUndoManager(pUndoManager
);
779 // Finally tell an invocation of this method on a higher level that it can (has
780 // to) abort and return immediately.
781 mbShellStackIsUpToDate
= true;
783 #if OSL_DEBUG_LEVEL >= 2
784 SAL_INFO("sd.view", __func__
<< ": New current stack");
789 void ViewShellManager::Implementation::TakeShellsFromStack (const SfxShell
* pShell
)
791 ::osl::MutexGuard
aGuard (maMutex
);
793 // Remember the undo manager from the top-most shell on the stack.
794 SfxShell
* pTopMostShell
= mrBase
.GetSubShell(0);
795 SfxUndoManager
* pUndoManager
= (pTopMostShell
!=nullptr)
796 ? pTopMostShell
->GetUndoManager()
799 #if OSL_DEBUG_LEVEL >= 2
800 SAL_INFO("sd.view", __func__
<< "TakeShellsFromStack( " << pShell
<< ")");
804 // 0.Make sure that the given shell is on the stack. This is a
805 // preparation for the following assertion.
806 for (sal_uInt16 nIndex
=0; true; nIndex
++)
808 SfxShell
* pShellOnStack
= mrBase
.GetSubShell(nIndex
);
809 if (pShellOnStack
== nullptr)
811 // Set pShell to NULL to indicate the following code that the
812 // shell is not on the stack.
816 else if (pShellOnStack
== pShell
)
820 if (pShell
== nullptr)
823 // 1. Deactivate our shells on the stack before they are removed so
824 // that during the Deactivation() calls the stack is still intact.
825 for (sal_uInt16 nIndex
=0; true; nIndex
++)
827 SfxShell
* pShellOnStack
= mrBase
.GetSubShell(nIndex
);
828 Deactivate(pShellOnStack
);
829 if (pShellOnStack
== pShell
)
833 // 2. Remove the shells from the stack.
836 SfxShell
* pShellOnStack
= mrBase
.GetSubShell(0);
837 SAL_INFO("sd.view", __func__
<< "removing shell " << pShellOnStack
<< " from stack");
838 mrBase
.RemoveSubShell(pShellOnStack
);
839 if (pShellOnStack
== pShell
)
843 // 3. Update the stack.
844 if (mrBase
.GetDispatcher() != nullptr)
845 mrBase
.GetDispatcher()->Flush();
847 // Update the pointer to the top-most shell and set its undo manager
848 // to the one of the previous top-most shell.
849 mpTopShell
= mrBase
.GetSubShell(0);
850 if (mpTopShell
!=nullptr && pUndoManager
!=nullptr && mpTopShell
->GetUndoManager()==nullptr)
851 mpTopShell
->SetUndoManager(pUndoManager
);
853 #if OSL_DEBUG_LEVEL >= 2
854 SAL_INFO("sd.view", __func__
<< "Sfx shell stack is:");
859 void ViewShellManager::Implementation::CreateShells()
861 ::osl::MutexGuard
aGuard (maMutex
);
863 // Iterate over all view shells.
864 ActiveShellList::reverse_iterator iShell
;
865 for (iShell
=maActiveViewShells
.rbegin(); iShell
!=maActiveViewShells
.rend(); ++iShell
)
867 // Get the list of associated sub shells.
868 SubShellList::iterator
iList (maActiveSubShells
.find(iShell
->mpShell
));
869 if (iList
!= maActiveSubShells
.end())
871 SubShellSubList
& rList (iList
->second
);
873 // Iterate over all sub shells of the current view shell.
874 for (auto & subShell
: rList
)
876 if (subShell
.mpShell
== nullptr)
878 subShell
= CreateSubShell(iShell
->mpShell
,subShell
.mnId
);
885 void ViewShellManager::Implementation::CreateTargetStack (ShellStack
& rStack
) const
887 // Create a local stack of the shells that are to push on the shell
888 // stack. We can thus safely create the required shells while still
889 // having a valid shell stack.
890 for (ActiveShellList::const_reverse_iterator
iViewShell (maActiveViewShells
.rbegin());
891 iViewShell
!= maActiveViewShells
.rend();
894 // Possibly place the form shell below the current view shell.
895 if ( ! mbFormShellAboveParent
896 && mpFormShell
!=nullptr
897 && iViewShell
->mpShell
==mpFormShellParent
)
899 rStack
.push_back(mpFormShell
);
902 // Put the view shell itself on the local stack.
903 rStack
.push_back (iViewShell
->mpShell
);
905 // Possibly place the form shell above the current view shell.
906 if (mbFormShellAboveParent
907 && mpFormShell
!=nullptr
908 && iViewShell
->mpShell
==mpFormShellParent
)
910 rStack
.push_back(mpFormShell
);
913 // Add all other sub shells.
914 SubShellList::const_iterator
iList (maActiveSubShells
.find(iViewShell
->mpShell
));
915 if (iList
!= maActiveSubShells
.end())
917 const SubShellSubList
& rList (iList
->second
);
918 SubShellSubList::const_reverse_iterator iSubShell
;
919 for (iSubShell
=rList
.rbegin(); iSubShell
!=rList
.rend(); ++iSubShell
)
920 if (iSubShell
->mpShell
!= mpFormShell
)
921 rStack
.push_back(iSubShell
->mpShell
);
926 IMPL_LINK(ViewShellManager::Implementation
, WindowEventHandler
, VclWindowEvent
&, rEvent
, void)
928 vcl::Window
* pEventWindow
= rEvent
.GetWindow();
930 switch (rEvent
.GetId())
932 case VclEventId::WindowGetFocus
:
934 for (auto const& activeShell
: maActiveViewShells
)
936 if (pEventWindow
== activeShell
.GetWindow())
938 MoveToTop(*activeShell
.mpShell
);
945 case VclEventId::WindowLoseFocus
:
948 case VclEventId::ObjectDying
:
949 // Remember that we do not have to remove the window
950 // listener for this window.
951 for (auto & activeViewShell
: maActiveViewShells
)
953 if (activeViewShell
.GetWindow() == pEventWindow
)
955 activeViewShell
.mbIsListenerAddedToWindow
= false;
965 ShellDescriptor
ViewShellManager::Implementation::CreateSubShell (
966 SfxShell
const * pParentShell
,
969 ::osl::MutexGuard
aGuard (maMutex
);
970 ShellDescriptor aResult
;
972 // Look up the factories for the parent shell.
973 ::std::pair
<FactoryList::iterator
,FactoryList::iterator
> aRange(
974 maShellFactories
.equal_range(pParentShell
));
976 // Try all factories to create the shell.
977 for (FactoryList::const_iterator iFactory
=aRange
.first
; iFactory
!=aRange
.second
; ++iFactory
)
979 SharedShellFactory pFactory
= iFactory
->second
;
980 if (pFactory
!= nullptr)
981 aResult
.mpShell
= pFactory
->CreateShell(nShellId
);
983 // Exit the loop when the shell has been successfully created.
984 if (aResult
.mpShell
!= nullptr)
986 aResult
.mpFactory
= pFactory
;
987 aResult
.mnId
= nShellId
;
995 void ViewShellManager::Implementation::DestroyViewShell (
996 ShellDescriptor
& rDescriptor
)
998 OSL_ASSERT(rDescriptor
.mpShell
!= nullptr);
1000 if (rDescriptor
.mbIsListenerAddedToWindow
)
1002 rDescriptor
.mbIsListenerAddedToWindow
= false;
1003 vcl::Window
* pWindow
= rDescriptor
.GetWindow();
1004 if (pWindow
!= nullptr)
1006 pWindow
->RemoveEventListener(
1007 LINK(this, ViewShellManager::Implementation
, WindowEventHandler
));
1011 // Destroy the sub shell factories.
1012 ::std::pair
<FactoryList::iterator
,FactoryList::iterator
> aRange(
1013 maShellFactories
.equal_range(rDescriptor
.mpShell
));
1014 if (aRange
.first
!= maShellFactories
.end())
1015 maShellFactories
.erase(aRange
.first
, aRange
.second
);
1017 // Release the shell.
1018 if (rDescriptor
.mpFactory
)
1019 rDescriptor
.mpFactory
->ReleaseShell(rDescriptor
.mpShell
);
1022 void ViewShellManager::Implementation::DestroySubShell (
1023 const ShellDescriptor
& rDescriptor
)
1025 OSL_ASSERT(rDescriptor
.mpFactory
);
1026 rDescriptor
.mpFactory
->ReleaseShell(rDescriptor
.mpShell
);
1029 void ViewShellManager::Implementation::InvalidateAllSubShells (const SfxShell
* pParentShell
)
1031 ::osl::MutexGuard
aGuard (maMutex
);
1033 SubShellList::iterator
iList (maActiveSubShells
.find(pParentShell
));
1034 if (iList
!= maActiveSubShells
.end())
1036 SubShellSubList
& rList (iList
->second
);
1037 for (auto const& shell
: rList
)
1038 if (shell
.mpShell
!= nullptr)
1039 shell
.mpShell
->Invalidate();
1043 void ViewShellManager::Implementation::Shutdown()
1045 ::osl::MutexGuard
aGuard (maMutex
);
1047 // Take stacked shells from stack.
1048 if ( ! maActiveViewShells
.empty())
1050 UpdateLock
aLock (*this);
1052 while ( ! maActiveViewShells
.empty())
1054 SfxShell
* pShell
= maActiveViewShells
.front().mpShell
;
1055 if (pShell
!= nullptr)
1057 ViewShell
* pViewShell
= dynamic_cast<ViewShell
*>(pShell
);
1058 if (pViewShell
!= nullptr)
1059 DeactivateViewShell(*pViewShell
);
1061 DeactivateShell(*pShell
);
1066 "ViewShellManager::Implementation::Shutdown(): empty active shell descriptor");
1067 maActiveViewShells
.pop_front();
1071 mrBase
.RemoveSubShell ();
1073 maShellFactories
.clear();
1076 #if OSL_DEBUG_LEVEL >= 2
1077 void ViewShellManager::Implementation::DumpShellStack (const ShellStack
& rStack
)
1079 ShellStack::const_reverse_iterator iEntry
;
1080 for (iEntry
=rStack
.rbegin(); iEntry
!=rStack
.rend(); ++iEntry
)
1081 if (*iEntry
!= NULL
)
1082 SAL_INFO("sd.view", __func__
<< ": " <<
1084 (*iEntry
)->GetName());
1086 SAL_INFO("sd.view", __func__
<< " null");
1089 void ViewShellManager::Implementation::DumpSfxShellStack()
1091 ShellStack aSfxShellStack
;
1092 sal_uInt16
nIndex (0);
1093 while (mrBase
.GetSubShell(nIndex
)!=NULL
)
1095 aSfxShellStack
.reserve(nIndex
);
1096 while (nIndex
-- > 0)
1097 aSfxShellStack
.push_back(mrBase
.GetSubShell(nIndex
));
1098 DumpShellStack(aSfxShellStack
);
1102 void ViewShellManager::Implementation::Deactivate (SfxShell
* pShell
)
1104 OSL_ASSERT(pShell
!=nullptr);
1106 // We have to end a text edit for view shells that are to be taken from
1108 ViewShell
* pViewShell
= dynamic_cast<ViewShell
*>(pShell
);
1109 if (pViewShell
!= nullptr)
1111 sd::View
* pView
= pViewShell
->GetView();
1112 if (pView
!=nullptr && pView
->IsTextEdit())
1114 pView
->SdrEndTextEdit();
1116 pViewShell
->GetViewFrame()->GetDispatcher()->Execute(
1118 SfxCallMode::ASYNCHRON
);
1122 // Now we can deactivate the shell.
1123 pShell
->Deactivate(true);
1126 void ViewShellManager::Implementation::SetFormShell (
1127 const ViewShell
* pFormShellParent
,
1128 FmFormShell
* pFormShell
,
1129 bool bFormShellAboveParent
)
1131 ::osl::MutexGuard
aGuard (maMutex
);
1133 mpFormShellParent
= pFormShellParent
;
1134 mpFormShell
= pFormShell
;
1135 mbFormShellAboveParent
= bFormShellAboveParent
;
1140 ShellDescriptor::ShellDescriptor()
1142 mnId(ToolbarId::None
),
1143 mbIsListenerAddedToWindow(false)
1147 ShellDescriptor::ShellDescriptor (
1151 mbIsListenerAddedToWindow(false)
1155 vcl::Window
* ShellDescriptor::GetWindow() const
1157 ViewShell
* pViewShell
= dynamic_cast<ViewShell
*>(mpShell
);
1158 if (pViewShell
!= nullptr)
1159 return pViewShell
->GetActiveWindow();
1164 } // end of anonymous namespace
1166 } // end of namespace sd
1168 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */