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>
35 #include <unordered_map>
41 /** The ShellDescriptor class is used to shells together with their ids and
42 the factory that was used to create the shell.
44 The shell pointer may be NULL. In that case the shell is created on
47 The factory pointer may be NULL. In that case the shell pointer is
48 given to the ViewShellManager.
50 Shell pointer and factory pointer can but should not be NULL at the same
53 class ShellDescriptor
{
57 ViewShellManager::SharedShellFactory mpFactory
;
58 bool mbIsListenerAddedToWindow
;
61 explicit ShellDescriptor (ShellId nId
);
62 vcl::Window
* GetWindow() const;
65 /** This functor can be used to search for a shell in an STL container when the
66 shell pointer is given.
71 explicit IsShell (const SfxShell
* pShell
) : mpShell(pShell
) {}
72 bool operator() (const ShellDescriptor
& rDescriptor
)
73 { return rDescriptor
.mpShell
== mpShell
; }
75 const SfxShell
* mpShell
;
78 /** This functor can be used to search for a shell in an STL container when the
79 id of the shell is given.
84 explicit IsId (ShellId nId
) : mnId(nId
) {}
85 bool operator() (const ShellDescriptor
& rDescriptor
)
86 { return rDescriptor
.mnId
== mnId
; }
91 } // end of anonymous namespace
93 class ViewShellManager::Implementation
97 ViewShellBase
& rBase
);
98 ~Implementation() COVERITY_NOEXCEPT_FALSE
;
100 void AddShellFactory (
101 const SfxShell
* pViewShell
,
102 const SharedShellFactory
& rpFactory
);
103 void RemoveShellFactory (
104 const SfxShell
* pViewShell
,
105 const SharedShellFactory
& rpFactory
);
106 void ActivateViewShell (
107 ViewShell
* pViewShell
);
108 void DeactivateViewShell (const ViewShell
& rShell
);
109 void ActivateShell (SfxShell
& rShell
);
110 void DeactivateShell (const SfxShell
& rShell
);
111 void ActivateShell (const ShellDescriptor
& rDescriptor
);
112 void SetFormShell (const ViewShell
* pViewShell
, FmFormShell
* pFormShell
, bool bAbove
);
113 void ActivateSubShell (const SfxShell
& rParentShell
, ShellId nId
);
114 void DeactivateSubShell (const SfxShell
& rParentShell
, ShellId nId
);
115 void MoveToTop (const SfxShell
& rParentShell
);
116 SfxShell
* GetShell (ShellId nId
) const;
117 SfxShell
* GetTopShell() const;
118 SfxShell
* GetTopViewShell() const;
120 void InvalidateAllSubShells (const SfxShell
* pParentShell
);
122 /** Remove all shells from the SFX stack above and including the given
125 void TakeShellsFromStack (const SfxShell
* pShell
);
130 explicit UpdateLock (Implementation
& rImpl
) : mrImpl(rImpl
) {mrImpl
.LockUpdate();}
131 ~UpdateLock() COVERITY_NOEXCEPT_FALSE
{mrImpl
.UnlockUpdate();}
133 Implementation
& mrImpl
;
136 /** Prevent updates of the shell stack. While the sub shell manager is
137 locked it will update its internal data structures but not alter the
138 shell stack. Use this method when there are several modifications
139 to the shell stack to prevent multiple rebuilds of the shell stack
140 and resulting broadcasts.
144 /** Allow updates of the shell stack. This method has to be called the
145 same number of times as LockUpdate() to really allow a rebuild of
151 ViewShellBase
& mrBase
;
152 mutable ::osl::Mutex maMutex
;
154 class ShellHash
{ public: size_t operator()(const SfxShell
* p
) const { return reinterpret_cast<size_t>(p
);} };
155 typedef std::unordered_multimap
<const SfxShell
*,SharedShellFactory
,ShellHash
>
157 FactoryList maShellFactories
;
159 /** List of the active view shells. In order to create gather all shells
160 to put on the shell stack each view shell in this list is asked for
161 its sub-shells (typically toolbars).
163 typedef std::list
<ShellDescriptor
> ActiveShellList
;
164 ActiveShellList maActiveViewShells
;
166 typedef std::list
<ShellDescriptor
> SubShellSubList
;
167 typedef std::unordered_map
<const SfxShell
*,SubShellSubList
,ShellHash
> SubShellList
;
168 SubShellList maActiveSubShells
;
170 /** In this member we remember what shells we have pushed on the shell
173 typedef ::std::vector
<SfxShell
*> ShellStack
;
175 int mnUpdateLockCount
;
177 /** The UpdateShellStack() method can be called recursively. This flag
178 is used to communicate between different levels of invocation: if
179 the stack has been updated in an inner call the outer call can (has
180 to) stop and return immediately.
182 bool mbShellStackIsUpToDate
;
184 SfxShell
* mpFormShell
;
185 const ViewShell
* mpFormShellParent
;
186 bool mbFormShellAboveParent
;
188 SfxShell
* mpTopShell
;
189 SfxShell
* mpTopViewShell
;
192 void UpdateShellStack();
196 /** This method rebuilds the stack of shells that are stacked upon the
199 void CreateTargetStack (ShellStack
& rStack
) const;
201 DECL_LINK(WindowEventHandler
, VclWindowEvent
&, void);
203 #if OSL_DEBUG_LEVEL >= 2
204 void DumpShellStack (const ShellStack
& rStack
);
205 void DumpSfxShellStack();
208 /** To be called before a shell is taken from the SFX shell stack. This
209 method deactivates an active text editing to avoid problems with
211 Afterwards the Deactivate() of the shell is called.
213 static void Deactivate (SfxShell
* pShell
);
215 ShellDescriptor
CreateSubShell (
216 SfxShell
const * pShell
,
218 void DestroyViewShell (ShellDescriptor
& rDescriptor
);
219 static void DestroySubShell (const ShellDescriptor
& rDescriptor
);
222 //===== ViewShellManager ======================================================
224 ViewShellManager::ViewShellManager (ViewShellBase
& rBase
)
225 : mpImpl(new Implementation(rBase
)),
230 ViewShellManager::~ViewShellManager()
234 void ViewShellManager::AddSubShellFactory (
235 ViewShell
const * pViewShell
,
236 const SharedShellFactory
& rpFactory
)
239 mpImpl
->AddShellFactory(pViewShell
, rpFactory
);
242 void ViewShellManager::RemoveSubShellFactory (
243 ViewShell
const * pViewShell
,
244 const SharedShellFactory
& rpFactory
)
247 mpImpl
->RemoveShellFactory(pViewShell
, rpFactory
);
250 void ViewShellManager::ActivateViewShell (ViewShell
* pViewShell
)
253 return mpImpl
->ActivateViewShell(pViewShell
);
256 void ViewShellManager::DeactivateViewShell (const ViewShell
* pShell
)
258 if (mbValid
&& pShell
!=nullptr)
259 mpImpl
->DeactivateViewShell(*pShell
);
262 void ViewShellManager::SetFormShell (
263 const ViewShell
* pParentShell
,
264 FmFormShell
* pFormShell
,
268 mpImpl
->SetFormShell(pParentShell
,pFormShell
,bAbove
);
271 void ViewShellManager::ActivateSubShell (const ViewShell
& rViewShell
, ShellId nId
)
274 mpImpl
->ActivateSubShell(rViewShell
,nId
);
277 void ViewShellManager::DeactivateSubShell (const ViewShell
& rViewShell
, ShellId nId
)
280 mpImpl
->DeactivateSubShell(rViewShell
,nId
);
283 void ViewShellManager::InvalidateAllSubShells (ViewShell
const * pViewShell
)
286 mpImpl
->InvalidateAllSubShells(pViewShell
);
289 void ViewShellManager::ActivateShell (SfxShell
* pShell
)
291 if (mbValid
&& pShell
!=nullptr)
292 mpImpl
->ActivateShell(*pShell
);
295 void ViewShellManager::DeactivateShell (const SfxShell
* pShell
)
297 if (mbValid
&& pShell
!=nullptr)
298 mpImpl
->DeactivateShell(*pShell
);
301 void ViewShellManager::MoveToTop (const ViewShell
& rParentShell
)
304 mpImpl
->MoveToTop(rParentShell
);
307 SfxShell
* ViewShellManager::GetShell (ShellId nId
) const
310 return mpImpl
->GetShell(nId
);
315 SfxShell
* ViewShellManager::GetTopShell() const
318 return mpImpl
->GetTopShell();
323 SfxShell
* ViewShellManager::GetTopViewShell() const
326 return mpImpl
->GetTopViewShell();
331 void ViewShellManager::Shutdown()
340 void ViewShellManager::LockUpdate()
342 mpImpl
->LockUpdate();
345 void ViewShellManager::UnlockUpdate()
347 mpImpl
->UnlockUpdate();
350 //===== ViewShellManager::Implementation ======================================
352 ViewShellManager::Implementation::Implementation (
353 ViewShellBase
& rBase
)
357 maActiveViewShells(),
358 mnUpdateLockCount(0),
359 mbShellStackIsUpToDate(true),
360 mpFormShell(nullptr),
361 mpFormShellParent(nullptr),
362 mbFormShellAboveParent(true),
364 mpTopViewShell(nullptr)
367 ViewShellManager::Implementation::~Implementation() COVERITY_NOEXCEPT_FALSE
372 void ViewShellManager::Implementation::AddShellFactory (
373 const SfxShell
* pViewShell
,
374 const SharedShellFactory
& rpFactory
)
376 bool bAlreadyAdded (false);
378 // Check that the given factory has not already been added.
379 ::std::pair
<FactoryList::iterator
,FactoryList::iterator
> aRange(
380 maShellFactories
.equal_range(pViewShell
));
381 for (FactoryList::const_iterator iFactory
=aRange
.first
; iFactory
!=aRange
.second
; ++iFactory
)
382 if (iFactory
->second
== rpFactory
)
384 bAlreadyAdded
= true;
388 // Add the factory if it is not already present.
389 if ( ! bAlreadyAdded
)
390 maShellFactories
.emplace(pViewShell
, rpFactory
);
393 void ViewShellManager::Implementation::RemoveShellFactory (
394 const SfxShell
* pViewShell
,
395 const SharedShellFactory
& rpFactory
)
397 ::std::pair
<FactoryList::iterator
,FactoryList::iterator
> aRange(
398 maShellFactories
.equal_range(pViewShell
));
399 for (FactoryList::iterator iFactory
=aRange
.first
; iFactory
!=aRange
.second
; ++iFactory
)
400 if (iFactory
->second
== rpFactory
)
402 maShellFactories
.erase(iFactory
);
407 void ViewShellManager::Implementation::ActivateViewShell (ViewShell
* pViewShell
)
409 ::osl::MutexGuard
aGuard (maMutex
);
411 ShellDescriptor aResult
;
412 aResult
.mpShell
= pViewShell
;
414 // Register as window listener so that the shells of the current
415 // window can be moved to the top of the shell stack.
416 if (aResult
.mpShell
!= nullptr)
418 vcl::Window
* pWindow
= aResult
.GetWindow();
419 if (pWindow
!= nullptr)
421 pWindow
->AddEventListener(
422 LINK(this, ViewShellManager::Implementation
, WindowEventHandler
));
423 aResult
.mbIsListenerAddedToWindow
= true;
428 "ViewShellManager::ActivateViewShell: "
429 "new view shell has no active window");
433 ActivateShell(aResult
);
436 void ViewShellManager::Implementation::DeactivateViewShell (const ViewShell
& rShell
)
438 ::osl::MutexGuard
aGuard (maMutex
);
440 ActiveShellList::iterator
iShell (::std::find_if (
441 maActiveViewShells
.begin(),
442 maActiveViewShells
.end(),
444 if (iShell
== maActiveViewShells
.end())
447 UpdateLock
aLocker (*this);
449 ShellDescriptor
aDescriptor(*iShell
);
450 mrBase
.GetDocShell()->Disconnect(dynamic_cast<ViewShell
*>(aDescriptor
.mpShell
));
451 maActiveViewShells
.erase(iShell
);
452 TakeShellsFromStack(aDescriptor
.mpShell
);
454 // Deactivate sub shells.
455 SubShellList::iterator
iList (maActiveSubShells
.find(&rShell
));
456 if (iList
!= maActiveSubShells
.end())
458 SubShellSubList
& rList (iList
->second
);
459 while ( ! rList
.empty())
460 DeactivateSubShell(rShell
, rList
.front().mnId
);
463 DestroyViewShell(aDescriptor
);
466 void ViewShellManager::Implementation::ActivateShell (SfxShell
& rShell
)
468 ::osl::MutexGuard
aGuard (maMutex
);
470 // Create a new shell or recycle on in the cache.
471 ShellDescriptor aDescriptor
;
472 aDescriptor
.mpShell
= &rShell
;
474 ActivateShell(aDescriptor
);
477 void ViewShellManager::Implementation::ActivateShell (const ShellDescriptor
& rDescriptor
)
479 // Put shell on top of the active view shells.
480 if (rDescriptor
.mpShell
!= nullptr)
482 maActiveViewShells
.insert( maActiveViewShells
.begin(), rDescriptor
);
486 void ViewShellManager::Implementation::DeactivateShell (const SfxShell
& rShell
)
488 ::osl::MutexGuard
aGuard (maMutex
);
490 ActiveShellList::iterator
iShell (::std::find_if (
491 maActiveViewShells
.begin(),
492 maActiveViewShells
.end(),
494 if (iShell
== maActiveViewShells
.end())
497 UpdateLock
aLocker (*this);
499 ShellDescriptor
aDescriptor(*iShell
);
500 mrBase
.GetDocShell()->Disconnect(dynamic_cast<ViewShell
*>(aDescriptor
.mpShell
));
501 maActiveViewShells
.erase(iShell
);
502 TakeShellsFromStack(aDescriptor
.mpShell
);
504 // Deactivate sub shells.
505 SubShellList::iterator
iList (maActiveSubShells
.find(&rShell
));
506 if (iList
!= maActiveSubShells
.end())
508 SubShellSubList
& rList (iList
->second
);
509 while ( ! rList
.empty())
510 DeactivateSubShell(rShell
, rList
.front().mnId
);
513 DestroyViewShell(aDescriptor
);
516 void ViewShellManager::Implementation::ActivateSubShell (
517 const SfxShell
& rParentShell
,
520 ::osl::MutexGuard
aGuard (maMutex
);
522 // Check that the given view shell is active.
523 if (std::none_of (maActiveViewShells
.begin(), maActiveViewShells
.end(), IsShell(&rParentShell
)))
526 // Create the sub shell list if it does not yet exist.
527 SubShellList::iterator
iList (maActiveSubShells
.find(&rParentShell
));
528 if (iList
== maActiveSubShells
.end())
529 iList
= maActiveSubShells
.emplace(&rParentShell
,SubShellSubList()).first
;
531 // Do not activate an object bar that is already active. Requesting
532 // this is not exactly an error but may be an indication of one.
533 SubShellSubList
& rList (iList
->second
);
534 if (std::any_of(rList
.begin(),rList
.end(), IsId(nId
)))
537 // Add just the id of the sub shell. The actual shell is created
538 // later in CreateShells().
539 UpdateLock
aLock (*this);
540 rList
.emplace_back(nId
);
543 void ViewShellManager::Implementation::DeactivateSubShell (
544 const SfxShell
& rParentShell
,
547 ::osl::MutexGuard
aGuard (maMutex
);
549 // Check that the given view shell is active.
550 SubShellList::iterator
iList (maActiveSubShells
.find(&rParentShell
));
551 if (iList
== maActiveSubShells
.end())
554 // Look up the sub shell.
555 SubShellSubList
& rList (iList
->second
);
556 SubShellSubList::iterator
iShell (
557 ::std::find_if(rList
.begin(),rList
.end(), IsId(nId
)));
558 if (iShell
== rList
.end())
560 SfxShell
* pShell
= iShell
->mpShell
;
561 if (pShell
== nullptr)
564 UpdateLock
aLock (*this);
566 ShellDescriptor
aDescriptor(*iShell
);
567 // Remove the sub shell from both the internal structure as well as the
568 // SFX shell stack above and including the sub shell.
570 TakeShellsFromStack(pShell
);
572 DestroySubShell(aDescriptor
);
575 void ViewShellManager::Implementation::MoveToTop (const SfxShell
& rShell
)
577 ::osl::MutexGuard
aGuard (maMutex
);
579 // Check that we have access to a dispatcher. If not, then we are
580 // (probably) called while the view shell is still being created or
581 // initialized. Without dispatcher we can not rebuild the shell stack
582 // to move the requested shell to the top. So return right away instead
583 // of making a mess without being able to clean up afterwards.
584 if (mrBase
.GetDispatcher() == nullptr)
587 ActiveShellList::iterator
iShell (::std::find_if (
588 maActiveViewShells
.begin(),
589 maActiveViewShells
.end(),
592 if (iShell
!= maActiveViewShells
.end())
594 // Is the shell already at the top of the stack? We have to keep
595 // the case in mind that mbKeepMainViewShellOnTop is true. Shells
596 // that are not the main view shell are placed on the second-to-top
597 // position in this case.
598 if (iShell
== maActiveViewShells
.begin())
600 // The shell is at the top position and is either a) the main
601 // view shell or b) another shell but the main view shell is not
602 // kept at the top position. We do not have to move the shell.
608 // The shell is not on the stack. Therefore it can not be moved.
609 // We could insert it but we don't. Use ActivateViewShell() for
614 // When the shell is not at the right position it is removed from the
615 // internal list of shells and inserted at the correct position.
618 UpdateLock
aLock (*this);
620 ShellDescriptor
aDescriptor(*iShell
);
622 TakeShellsFromStack(&rShell
);
623 maActiveViewShells
.erase(iShell
);
625 maActiveViewShells
.insert(maActiveViewShells
.begin(), aDescriptor
);
629 SfxShell
* ViewShellManager::Implementation::GetShell (ShellId nId
) const
631 ::osl::MutexGuard
aGuard (maMutex
);
633 SfxShell
* pShell
= nullptr;
635 // First search the active view shells.
636 ActiveShellList::const_iterator
iShell (
638 maActiveViewShells
.begin(),
639 maActiveViewShells
.end(),
641 if (iShell
!= maActiveViewShells
.end())
642 pShell
= iShell
->mpShell
;
645 // Now search the active sub shells of every active view shell.
646 for (auto const& activeSubShell
: maActiveSubShells
)
648 const SubShellSubList
& rList (activeSubShell
.second
);
649 SubShellSubList::const_iterator
iSubShell(
650 ::std::find_if(rList
.begin(),rList
.end(), IsId(nId
)));
651 if (iSubShell
!= rList
.end())
653 pShell
= iSubShell
->mpShell
;
662 SfxShell
* ViewShellManager::Implementation::GetTopShell() const
664 OSL_ASSERT(mpTopShell
== mrBase
.GetSubShell(0));
668 SfxShell
* ViewShellManager::Implementation::GetTopViewShell() const
670 return mpTopViewShell
;
673 void ViewShellManager::Implementation::LockUpdate()
678 void ViewShellManager::Implementation::UnlockUpdate()
680 ::osl::MutexGuard
aGuard (maMutex
);
683 if (mnUpdateLockCount
< 0)
685 // This should not happen.
686 OSL_ASSERT (mnUpdateLockCount
>=0);
687 mnUpdateLockCount
= 0;
689 if (mnUpdateLockCount
== 0)
693 /** Update the SFX shell stack (the portion that is visible to us) so that
694 it matches the internal shell stack. This is done in six steps:
695 1. Create the missing view shells and sub shells.
696 2. Set up the internal shell stack.
697 3. Get the SFX shell stack.
698 4. Find the lowest shell in which the two stacks differ.
699 5. Remove all shells above and including that shell from the SFX stack.
700 6. Push all shells of the internal stack on the SFX shell stack that are
701 not already present on the later.
703 void ViewShellManager::Implementation::UpdateShellStack()
705 ::osl::MutexGuard
aGuard (maMutex
);
707 // Remember the undo manager from the top-most shell on the stack.
708 SfxShell
* pTopMostShell
= mrBase
.GetSubShell(0);
709 SfxUndoManager
* pUndoManager
= (pTopMostShell
!=nullptr)
710 ? pTopMostShell
->GetUndoManager()
713 // 1. Create the missing shells.
716 // Update the pointer to the top-most active view shell.
717 mpTopViewShell
= (maActiveViewShells
.empty())
718 ? nullptr : maActiveViewShells
.begin()->mpShell
;
721 // 2. Create the internal target stack.
722 ShellStack aTargetStack
;
723 CreateTargetStack(aTargetStack
);
725 // 3. Get SFX shell stack.
726 ShellStack aSfxShellStack
;
727 sal_uInt16
nIndex (0);
728 while (mrBase
.GetSubShell(nIndex
)!=nullptr)
730 aSfxShellStack
.reserve(nIndex
);
732 aSfxShellStack
.push_back(mrBase
.GetSubShell(nIndex
));
734 #if OSL_DEBUG_LEVEL >= 2
735 SAL_INFO("sd.view", __func__
<< ": Current SFX Stack");
736 DumpShellStack(aSfxShellStack
);
737 SAL_INFO("sd.view", __func__
<< ": Target Stack");
738 DumpShellStack(aTargetStack
);
741 // 4. Find the lowest shell in which the two stacks differ.
742 auto mismatchIters
= std::mismatch(aSfxShellStack
.begin(), aSfxShellStack
.end(),
743 aTargetStack
.begin(), aTargetStack
.end());
744 ShellStack::iterator
iSfxShell (mismatchIters
.first
);
745 ShellStack::iterator
iTargetShell (mismatchIters
.second
);
747 // 5. Remove all shells above and including the differing shell from the
748 // SFX stack starting with the shell on top of the stack.
749 for (std::reverse_iterator
<ShellStack::const_iterator
> i(aSfxShellStack
.end()), iLast(iSfxShell
);
752 SfxShell
* const pShell
= *i
;
753 SAL_INFO("sd.view", __func__
<< ": removing shell " << pShell
<< " from stack");
754 mrBase
.RemoveSubShell(pShell
);
756 aSfxShellStack
.erase(iSfxShell
, aSfxShellStack
.end());
758 // 6. Push shells from the given stack onto the SFX stack.
759 mbShellStackIsUpToDate
= false;
760 while (iTargetShell
!= aTargetStack
.end())
762 SAL_INFO("sd.view", __func__
<< ": pushing shell " << *iTargetShell
<< " on stack");
763 mrBase
.AddSubShell(**iTargetShell
);
766 // The pushing of the shell on to the shell stack may have lead to
767 // another invocation of this method. In this case we have to abort
768 // pushing shells on the stack and return immediately.
769 if (mbShellStackIsUpToDate
)
772 if (mrBase
.GetDispatcher() != nullptr)
773 mrBase
.GetDispatcher()->Flush();
775 // Update the pointer to the top-most shell and set its undo manager
776 // to the one of the previous top-most shell.
777 mpTopShell
= mrBase
.GetSubShell(0);
778 if (mpTopShell
!=nullptr && pUndoManager
!=nullptr && mpTopShell
->GetUndoManager()==nullptr)
779 mpTopShell
->SetUndoManager(pUndoManager
);
781 // Finally tell an invocation of this method on a higher level that it can (has
782 // to) abort and return immediately.
783 mbShellStackIsUpToDate
= true;
785 #if OSL_DEBUG_LEVEL >= 2
786 SAL_INFO("sd.view", __func__
<< ": New current stack");
791 void ViewShellManager::Implementation::TakeShellsFromStack (const SfxShell
* pShell
)
793 ::osl::MutexGuard
aGuard (maMutex
);
795 // Remember the undo manager from the top-most shell on the stack.
796 SfxShell
* pTopMostShell
= mrBase
.GetSubShell(0);
797 SfxUndoManager
* pUndoManager
= (pTopMostShell
!=nullptr)
798 ? pTopMostShell
->GetUndoManager()
801 #if OSL_DEBUG_LEVEL >= 2
802 SAL_INFO("sd.view", __func__
<< "TakeShellsFromStack( " << pShell
<< ")");
806 // 0.Make sure that the given shell is on the stack. This is a
807 // preparation for the following assertion.
808 for (sal_uInt16 nIndex
=0; true; nIndex
++)
810 SfxShell
* pShellOnStack
= mrBase
.GetSubShell(nIndex
);
811 if (pShellOnStack
== nullptr)
813 // Set pShell to NULL to indicate the following code that the
814 // shell is not on the stack.
818 else if (pShellOnStack
== pShell
)
822 if (pShell
== nullptr)
825 // 1. Deactivate our shells on the stack before they are removed so
826 // that during the Deactivation() calls the stack is still intact.
827 for (sal_uInt16 nIndex
=0; true; nIndex
++)
829 SfxShell
* pShellOnStack
= mrBase
.GetSubShell(nIndex
);
830 Deactivate(pShellOnStack
);
831 if (pShellOnStack
== pShell
)
835 // 2. Remove the shells from the stack.
838 SfxShell
* pShellOnStack
= mrBase
.GetSubShell(0);
839 SAL_INFO("sd.view", __func__
<< "removing shell " << pShellOnStack
<< " from stack");
840 mrBase
.RemoveSubShell(pShellOnStack
);
841 if (pShellOnStack
== pShell
)
845 // 3. Update the stack.
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 #if OSL_DEBUG_LEVEL >= 2
856 SAL_INFO("sd.view", __func__
<< "Sfx shell stack is:");
861 void ViewShellManager::Implementation::CreateShells()
863 ::osl::MutexGuard
aGuard (maMutex
);
865 // Iterate over all view shells.
866 ActiveShellList::reverse_iterator iShell
;
867 for (iShell
=maActiveViewShells
.rbegin(); iShell
!=maActiveViewShells
.rend(); ++iShell
)
869 // Get the list of associated sub shells.
870 SubShellList::iterator
iList (maActiveSubShells
.find(iShell
->mpShell
));
871 if (iList
!= maActiveSubShells
.end())
873 SubShellSubList
& rList (iList
->second
);
875 // Iterate over all sub shells of the current view shell.
876 for (auto & subShell
: rList
)
878 if (subShell
.mpShell
== nullptr)
880 subShell
= CreateSubShell(iShell
->mpShell
,subShell
.mnId
);
887 void ViewShellManager::Implementation::CreateTargetStack (ShellStack
& rStack
) const
889 // Create a local stack of the shells that are to push on the shell
890 // stack. We can thus safely create the required shells while still
891 // having a valid shell stack.
892 for (ActiveShellList::const_reverse_iterator
iViewShell (maActiveViewShells
.rbegin());
893 iViewShell
!= maActiveViewShells
.rend();
896 // Possibly place the form shell below the current view shell.
897 if ( ! mbFormShellAboveParent
898 && mpFormShell
!=nullptr
899 && iViewShell
->mpShell
==mpFormShellParent
)
901 rStack
.push_back(mpFormShell
);
904 // Put the view shell itself on the local stack.
905 rStack
.push_back (iViewShell
->mpShell
);
907 // Possibly place the form shell above the current view shell.
908 if (mbFormShellAboveParent
909 && mpFormShell
!=nullptr
910 && iViewShell
->mpShell
==mpFormShellParent
)
912 rStack
.push_back(mpFormShell
);
915 // Add all other sub shells.
916 SubShellList::const_iterator
iList (maActiveSubShells
.find(iViewShell
->mpShell
));
917 if (iList
!= maActiveSubShells
.end())
919 const SubShellSubList
& rList (iList
->second
);
920 SubShellSubList::const_reverse_iterator iSubShell
;
921 for (iSubShell
=rList
.rbegin(); iSubShell
!=rList
.rend(); ++iSubShell
)
922 if (iSubShell
->mpShell
!= mpFormShell
)
923 rStack
.push_back(iSubShell
->mpShell
);
928 IMPL_LINK(ViewShellManager::Implementation
, WindowEventHandler
, VclWindowEvent
&, rEvent
, void)
930 vcl::Window
* pEventWindow
= rEvent
.GetWindow();
932 switch (rEvent
.GetId())
934 case VclEventId::WindowGetFocus
:
936 for (auto const& activeShell
: maActiveViewShells
)
938 if (pEventWindow
== activeShell
.GetWindow())
940 MoveToTop(*activeShell
.mpShell
);
947 case VclEventId::WindowLoseFocus
:
950 case VclEventId::ObjectDying
:
951 // Remember that we do not have to remove the window
952 // listener for this window.
953 for (auto & activeViewShell
: maActiveViewShells
)
955 if (activeViewShell
.GetWindow() == pEventWindow
)
957 activeViewShell
.mbIsListenerAddedToWindow
= false;
967 ShellDescriptor
ViewShellManager::Implementation::CreateSubShell (
968 SfxShell
const * pParentShell
,
971 ::osl::MutexGuard
aGuard (maMutex
);
972 ShellDescriptor aResult
;
974 // Look up the factories for the parent shell.
975 ::std::pair
<FactoryList::iterator
,FactoryList::iterator
> aRange(
976 maShellFactories
.equal_range(pParentShell
));
978 // Try all factories to create the shell.
979 for (FactoryList::const_iterator iFactory
=aRange
.first
; iFactory
!=aRange
.second
; ++iFactory
)
981 SharedShellFactory pFactory
= iFactory
->second
;
982 if (pFactory
!= nullptr)
983 aResult
.mpShell
= pFactory
->CreateShell(nShellId
);
985 // Exit the loop when the shell has been successfully created.
986 if (aResult
.mpShell
!= nullptr)
988 aResult
.mpFactory
= pFactory
;
989 aResult
.mnId
= nShellId
;
997 void ViewShellManager::Implementation::DestroyViewShell (
998 ShellDescriptor
& rDescriptor
)
1000 OSL_ASSERT(rDescriptor
.mpShell
!= nullptr);
1002 if (rDescriptor
.mbIsListenerAddedToWindow
)
1004 rDescriptor
.mbIsListenerAddedToWindow
= false;
1005 vcl::Window
* pWindow
= rDescriptor
.GetWindow();
1006 if (pWindow
!= nullptr)
1008 pWindow
->RemoveEventListener(
1009 LINK(this, ViewShellManager::Implementation
, WindowEventHandler
));
1013 // Destroy the sub shell factories.
1014 ::std::pair
<FactoryList::iterator
,FactoryList::iterator
> aRange(
1015 maShellFactories
.equal_range(rDescriptor
.mpShell
));
1016 if (aRange
.first
!= maShellFactories
.end())
1017 maShellFactories
.erase(aRange
.first
, aRange
.second
);
1019 // Release the shell.
1020 if (rDescriptor
.mpFactory
)
1021 rDescriptor
.mpFactory
->ReleaseShell(rDescriptor
.mpShell
);
1024 void ViewShellManager::Implementation::DestroySubShell (
1025 const ShellDescriptor
& rDescriptor
)
1027 OSL_ASSERT(rDescriptor
.mpFactory
);
1028 rDescriptor
.mpFactory
->ReleaseShell(rDescriptor
.mpShell
);
1031 void ViewShellManager::Implementation::InvalidateAllSubShells (const SfxShell
* pParentShell
)
1033 ::osl::MutexGuard
aGuard (maMutex
);
1035 SubShellList::iterator
iList (maActiveSubShells
.find(pParentShell
));
1036 if (iList
!= maActiveSubShells
.end())
1038 SubShellSubList
& rList (iList
->second
);
1039 for (auto const& shell
: rList
)
1040 if (shell
.mpShell
!= nullptr)
1041 shell
.mpShell
->Invalidate();
1045 void ViewShellManager::Implementation::Shutdown()
1047 ::osl::MutexGuard
aGuard (maMutex
);
1049 // Take stacked shells from stack.
1050 if ( ! maActiveViewShells
.empty())
1052 UpdateLock
aLock (*this);
1054 while ( ! maActiveViewShells
.empty())
1056 SfxShell
* pShell
= maActiveViewShells
.front().mpShell
;
1057 if (pShell
!= nullptr)
1059 ViewShell
* pViewShell
= dynamic_cast<ViewShell
*>(pShell
);
1060 if (pViewShell
!= nullptr)
1061 DeactivateViewShell(*pViewShell
);
1063 DeactivateShell(*pShell
);
1068 "ViewShellManager::Implementation::Shutdown(): empty active shell descriptor");
1069 maActiveViewShells
.pop_front();
1073 mrBase
.RemoveSubShell ();
1075 maShellFactories
.clear();
1078 #if OSL_DEBUG_LEVEL >= 2
1079 void ViewShellManager::Implementation::DumpShellStack (const ShellStack
& rStack
)
1081 ShellStack::const_reverse_iterator iEntry
;
1082 for (iEntry
=rStack
.rbegin(); iEntry
!=rStack
.rend(); ++iEntry
)
1083 if (*iEntry
!= NULL
)
1084 SAL_INFO("sd.view", __func__
<< ": " <<
1086 (*iEntry
)->GetName());
1088 SAL_INFO("sd.view", __func__
<< " null");
1091 void ViewShellManager::Implementation::DumpSfxShellStack()
1093 ShellStack aSfxShellStack
;
1094 sal_uInt16
nIndex (0);
1095 while (mrBase
.GetSubShell(nIndex
)!=NULL
)
1097 aSfxShellStack
.reserve(nIndex
);
1098 while (nIndex
-- > 0)
1099 aSfxShellStack
.push_back(mrBase
.GetSubShell(nIndex
));
1100 DumpShellStack(aSfxShellStack
);
1104 void ViewShellManager::Implementation::Deactivate (SfxShell
* pShell
)
1106 OSL_ASSERT(pShell
!=nullptr);
1108 // We have to end a text edit for view shells that are to be taken from
1110 ViewShell
* pViewShell
= dynamic_cast<ViewShell
*>(pShell
);
1111 if (pViewShell
!= nullptr)
1113 sd::View
* pView
= pViewShell
->GetView();
1114 if (pView
!=nullptr && pView
->IsTextEdit())
1116 pView
->SdrEndTextEdit();
1118 pViewShell
->GetViewFrame()->GetDispatcher()->Execute(
1120 SfxCallMode::ASYNCHRON
);
1124 // Now we can deactivate the shell.
1125 pShell
->Deactivate(true);
1128 void ViewShellManager::Implementation::SetFormShell (
1129 const ViewShell
* pFormShellParent
,
1130 FmFormShell
* pFormShell
,
1131 bool bFormShellAboveParent
)
1133 ::osl::MutexGuard
aGuard (maMutex
);
1135 mpFormShellParent
= pFormShellParent
;
1136 mpFormShell
= pFormShell
;
1137 mbFormShellAboveParent
= bFormShellAboveParent
;
1142 ShellDescriptor::ShellDescriptor()
1144 mnId(ToolbarId::None
),
1146 mbIsListenerAddedToWindow(false)
1150 ShellDescriptor::ShellDescriptor (
1155 mbIsListenerAddedToWindow(false)
1159 vcl::Window
* ShellDescriptor::GetWindow() const
1161 ViewShell
* pViewShell
= dynamic_cast<ViewShell
*>(mpShell
);
1162 if (pViewShell
!= nullptr)
1163 return pViewShell
->GetActiveWindow();
1168 } // end of anonymous namespace
1170 } // end of namespace sd
1172 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */